Merge branch 'master' into feature/mapping

Conflicts:
	src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java
	src/main/java/com/sk89q/worldedit/internal/LocalWorldAdapter.java
	src/main/java/com/sk89q/worldedit/util/TargetBlock.java
This commit is contained in:
sk89q
2014-06-28 22:31:13 -07:00
154 changed files with 8969 additions and 2116 deletions

View File

@ -42,15 +42,4 @@ public abstract class AbstractPlatform implements Platform {
return Collections.emptyList();
}
@Override
@Deprecated
public void onCommandRegistration(List<Command> commands) {
// Do nothing :)
}
@Override
public void onCommandRegistration(List<Command> commands, CommandsManager<LocalPlayer> manager) {
onCommandRegistration(commands);
}
}

View File

@ -31,8 +31,6 @@ import com.sk89q.worldedit.world.World;
import java.io.File;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* An abstract implementation of both a {@link Actor} and a {@link Player}
* that is intended for implementations of WorldEdit to use to wrap
@ -40,19 +38,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/
public abstract class AbstractPlayerActor implements Actor, Player {
private final Platform platform;
/**
* Create a new instance.
*
* @param platform the platform
*/
protected AbstractPlayerActor(Platform platform) {
checkNotNull(platform);
this.platform = platform;
}
/**
* Returns direction according to rotation. May return null.
*

View File

@ -37,13 +37,6 @@ public interface Actor {
*/
String getName();
/**
* Get the actor's world.
*
* @return the world
*/
World getWorld();
/**
* Print a message.
*

View File

@ -0,0 +1,81 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.extension.platform;
/**
* A collection of capabilities that a {@link Platform} may support.
*/
public enum Capability {
/**
* The capability of registering game hooks to catch events such as
* a player clicking a block.
*/
GAME_HOOKS {
@Override
void initialize(PlatformManager platformManager, Platform platform) {
platform.registerGameHooks();
}
@Override
void unload(PlatformManager platformManager, Platform platform) {
}
},
/**
* The capability of providing configuration.
*/
CONFIGURATION,
/**
* The capability of handling user commands entered in chat or console.
*/
USER_COMMANDS {
@Override
void initialize(PlatformManager platformManager, Platform platform) {
platformManager.getCommandManager().register(platform);
}
@Override
void unload(PlatformManager platformManager, Platform platform) {
platformManager.getCommandManager().unregister();
}
},
/**
* The capability of a platform to assess whether a given
* {@link Actor} has sufficient authorization to perform a task.
*/
PERMISSIONS,
/**
* The capability of a platform to perform modifications to a world.
*/
WORLD_EDITING;
void initialize(PlatformManager platformManager, Platform platform) {
}
void unload(PlatformManager platformManager, Platform platform) {
}
}

View File

@ -19,22 +19,37 @@
package com.sk89q.worldedit.extension.platform;
import com.sk89q.minecraft.util.commands.*;
import com.sk89q.util.StringUtil;
import com.sk89q.worldedit.*;
import com.sk89q.worldedit.blocks.ItemType;
import com.google.common.base.Joiner;
import com.sk89q.minecraft.util.commands.CommandException;
import com.sk89q.minecraft.util.commands.CommandLocals;
import com.sk89q.minecraft.util.commands.CommandPermissionsException;
import com.sk89q.minecraft.util.commands.WrappedCommandException;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.LocalConfiguration;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.command.*;
import com.sk89q.worldedit.event.platform.CommandEvent;
import com.sk89q.worldedit.event.platform.CommandSuggestionEvent;
import com.sk89q.worldedit.internal.command.ActorAuthorizer;
import com.sk89q.worldedit.internal.command.CommandLoggingHandler;
import com.sk89q.worldedit.internal.command.WorldEditBinding;
import com.sk89q.worldedit.internal.command.WorldEditExceptionConverter;
import com.sk89q.worldedit.session.request.Request;
import com.sk89q.worldedit.util.logging.LogFormat;
import com.sk89q.worldedit.util.command.Dispatcher;
import com.sk89q.worldedit.util.command.InvalidUsageException;
import com.sk89q.worldedit.util.command.fluent.CommandGraph;
import com.sk89q.worldedit.util.command.parametric.LegacyCommandsHandler;
import com.sk89q.worldedit.util.command.parametric.ParametricBuilder;
import com.sk89q.worldedit.util.eventbus.Subscribe;
import com.sk89q.worldedit.util.logging.DynamicStreamHandler;
import com.sk89q.worldedit.util.logging.LogFormat;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.logging.*;
import java.util.regex.Matcher;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.google.common.base.Preconditions.checkNotNull;
@ -49,7 +64,8 @@ public final class CommandManager {
private static final java.util.regex.Pattern numberFormatExceptionPattern = java.util.regex.Pattern.compile("^For input string: \"(.*)\"$");
private final WorldEdit worldEdit;
private final CommandsManager<LocalPlayer> commands;
private final PlatformManager platformManager;
private final Dispatcher dispatcher;
private final DynamicStreamHandler dynamicHandler = new DynamicStreamHandler();
/**
@ -57,9 +73,11 @@ public final class CommandManager {
*
* @param worldEdit the WorldEdit instance
*/
CommandManager(final WorldEdit worldEdit) {
CommandManager(final WorldEdit worldEdit, PlatformManager platformManager) {
checkNotNull(worldEdit);
checkNotNull(platformManager);
this.worldEdit = worldEdit;
this.platformManager = platformManager;
// Register this instance for command events
worldEdit.getEventBus().register(this);
@ -69,8 +87,56 @@ public final class CommandManager {
dynamicHandler.setFormatter(new LogFormat());
// Set up the commands manager
commands = new CommandsManagerImpl();
commands.setInjector(new SimpleInjector(worldEdit));
ParametricBuilder builder = new ParametricBuilder();
builder.setAuthorizer(new ActorAuthorizer());
builder.addBinding(new WorldEditBinding(worldEdit));
builder.addExceptionConverter(new WorldEditExceptionConverter(worldEdit));
builder.addInvokeListener(new LegacyCommandsHandler());
builder.addInvokeListener(new CommandLoggingHandler(worldEdit, logger));
dispatcher = new CommandGraph()
.builder(builder)
.commands()
.registerMethods(new BiomeCommands(worldEdit))
.registerMethods(new ChunkCommands(worldEdit))
.registerMethods(new ClipboardCommands(worldEdit))
.registerMethods(new GeneralCommands(worldEdit))
.registerMethods(new GenerationCommands(worldEdit))
.registerMethods(new HistoryCommands(worldEdit))
.registerMethods(new NavigationCommands(worldEdit))
.registerMethods(new RegionCommands(worldEdit))
.registerMethods(new ScriptingCommands(worldEdit))
.registerMethods(new SelectionCommands(worldEdit))
.registerMethods(new SnapshotUtilCommands(worldEdit))
.registerMethods(new ToolUtilCommands(worldEdit))
.registerMethods(new ToolCommands(worldEdit))
.registerMethods(new UtilityCommands(worldEdit))
.group("worldedit", "we")
.describeAs("WorldEdit commands")
.registerMethods(new WorldEditCommands(worldEdit))
.parent()
.group("schematic", "schem", "/schematic", "/schem")
.describeAs("Schematic commands for saving/loading areas")
.registerMethods(new SchematicCommands(worldEdit))
.parent()
.group("snapshot", "snap")
.describeAs("Schematic commands for saving/loading areas")
.registerMethods(new SnapshotCommands(worldEdit))
.parent()
.group("brush", "br")
.describeAs("Brushing commands")
.registerMethods(new BrushCommands(worldEdit))
.parent()
.group("superpickaxe", "pickaxe", "sp")
.describeAs("Super-pickaxe commands")
.registerMethods(new SuperPickaxeCommands(worldEdit))
.parent()
.group("tool")
.describeAs("Bind functions to held items")
.registerMethods(new ToolCommands(worldEdit))
.parent()
.graph()
.getDispatcher();
}
void register(Platform platform) {
@ -83,9 +149,12 @@ public final class CommandManager {
// Register log
if (!logging || path.isEmpty()) {
dynamicHandler.setHandler(null);
logger.setLevel(Level.OFF);
} else {
File file = new File(config.getWorkingDirectory(), path);
logger.setLevel(Level.ALL);
logger.log(Level.INFO, "Logging WorldEdit commands to " + file.getAbsolutePath());
try {
@ -95,39 +164,14 @@ public final class CommandManager {
}
}
register(platform, BiomeCommands.class);
register(platform, ChunkCommands.class);
register(platform, ClipboardCommands.class);
register(platform, GeneralCommands.class);
register(platform, GenerationCommands.class);
register(platform, HistoryCommands.class);
register(platform, NavigationCommands.class);
register(platform, RegionCommands.class);
register(platform, ScriptingCommands.class);
register(platform, SelectionCommands.class);
register(platform, SnapshotUtilCommands.class);
register(platform, ToolUtilCommands.class);
register(platform, ToolCommands.class);
register(platform, UtilityCommands.class);
platform.registerCommands(dispatcher);
}
void unregister() {
dynamicHandler.setHandler(null);
}
private void register(Platform platform, Class<?> clazz) {
platform.onCommandRegistration(commands.registerAndReturn(clazz), commands);
}
public CommandsManager<LocalPlayer> getCommands() {
return commands;
}
public String[] commandDetection(String[] split) {
Request.reset();
split[0] = split[0].substring(1);
// Quick script shortcut
if (split[0].matches("^[^/].*\\.js$")) {
String[] newSplit = new String[split.length + 1];
@ -140,12 +184,12 @@ public final class CommandManager {
String searchCmd = split[0].toLowerCase();
// Try to detect the command
if (commands.hasCommand(searchCmd)) {
} else if (worldEdit.getConfiguration().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);
if (!dispatcher.contains(searchCmd)) {
if (worldEdit.getConfiguration().noDoubleSlash && dispatcher.contains("/" + searchCmd)) {
split[0] = "/" + split[0];
} else if (searchCmd.length() >= 2 && searchCmd.charAt(0) == '/' && dispatcher.contains(searchCmd.substring(1))) {
split[0] = split[0].substring(1);
}
}
return split;
@ -155,185 +199,80 @@ public final class CommandManager {
public void handleCommand(CommandEvent event) {
Request.reset();
LocalPlayer player = event.getPlayer();
String[] split = event.getArguments();
Actor actor = platformManager.createProxyActor(event.getActor());
String split[] = commandDetection(event.getArguments().split(" "));
// No command found!
if (!dispatcher.contains(split[0])) {
return;
}
LocalSession session = worldEdit.getSessionManager().get(actor);
LocalConfiguration config = worldEdit.getConfiguration();
CommandLocals locals = new CommandLocals();
locals.put(Actor.class, actor);
long start = System.currentTimeMillis();
try {
split = commandDetection(split);
dispatcher.call(Joiner.on(" ").join(split), locals, new String[0]);
} catch (CommandPermissionsException e) {
actor.printError("You don't have permission to do this.");
} catch (InvalidUsageException e) {
actor.printError(e.getMessage() + "\nUsage: " + e.getUsage("/"));
} catch (WrappedCommandException e) {
Throwable t = e.getCause();
actor.printError("Please report this error: [See console]");
actor.printRaw(t.getClass().getName() + ": " + t.getMessage());
t.printStackTrace();
} catch (CommandException e) {
actor.printError(e.getMessage());
} finally {
EditSession editSession = locals.get(EditSession.class);
// No command found!
if (!commands.hasCommand(split[0])) {
return;
}
LocalSession session = worldEdit.getSession(player);
EditSession editSession = session.createEditSession(player);
editSession.enableQueue();
session.tellVersion(player);
long start = System.currentTimeMillis();
try {
commands.execute(split, player, 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 (PlayerNeededException e) {
player.printError(e.getMessage());
} catch (WrappedCommandException e) {
throw e.getCause();
} catch (UnhandledCommandException e) {
player.printError("Command could not be handled; invalid sender!");
event.setCancelled(true);
return;
} finally {
if (editSession != null) {
session.remember(editSession);
editSession.flushQueue();
if (worldEdit.getConfiguration().profile) {
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: "
actor.printDebug((time / 1000.0) + "s elapsed (history: "
+ changed + " changed; "
+ Math.round(throughput) + " blocks/sec).");
} else {
player.printDebug((time / 1000.0) + "s elapsed.");
actor.printDebug((time / 1000.0) + "s elapsed.");
}
}
worldEdit.flushBlockBag(player, editSession);
worldEdit.flushBlockBag(actor, editSession);
}
} catch (NumberFormatException e) {
final Matcher matcher = numberFormatExceptionPattern.matcher(e.getMessage());
if (matcher.matches()) {
player.printError("Number expected; string \"" + matcher.group(1) + "\" given.");
} else {
player.printError("Number expected; string given.");
}
} catch (IncompleteRegionException e) {
player.printError("Make a region selection first.");
} 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) {
player.printError("Max blocks changed in an operation reached ("
+ e.getBlockLimit() + ").");
} catch (MaxBrushRadiusException e) {
player.printError("Maximum allowed brush size: " + worldEdit.getConfiguration().maxBrushRadius);
} catch (MaxRadiusException e) {
player.printError("Maximum allowed size: " + worldEdit.getConfiguration().maxRadius);
} catch (UnknownDirectionException e) {
player.printError("Unknown direction: " + e.getDirection());
} catch (InsufficientArgumentsException e) {
player.printError(e.getMessage());
} catch (EmptyClipboardException e) {
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());
} catch (InvalidToolBindException e) {
player.printError("Can't bind tool to "
+ ItemType.toHeldName(e.getItemId()) + ": " + e.getMessage());
} catch (FileSelectionAbortedException e) {
player.printError("File selection aborted.");
} catch (WorldEditException e) {
player.printError(e.getMessage());
} catch (Throwable excp) {
player.printError("Please report this error: [See console]");
player.printRaw(excp.getClass().getName() + ": " + excp.getMessage());
excp.printStackTrace();
}
event.setCancelled(true);
}
private class CommandsManagerImpl extends CommandsManager<LocalPlayer> {
@Override
protected void checkPermission(LocalPlayer player, Method method) throws CommandException {
if (!player.isPlayer() && !method.isAnnotationPresent(Console.class)) {
throw new UnhandledCommandException();
}
super.checkPermission(player, method);
@Subscribe
public void handleCommandSuggestion(CommandSuggestionEvent event) {
try {
CommandLocals locals = new CommandLocals();
locals.put(Actor.class, event.getActor());
event.setSuggestions(dispatcher.getSuggestions(event.getArguments(), locals));
} catch (CommandException e) {
event.getActor().printError(e.getMessage());
}
}
@Override
public boolean hasPermission(LocalPlayer player, String perm) {
return player.hasPermission(perm);
}
@Override
public void invokeMethod(Method parent, String[] args,
LocalPlayer player, Method method, Object instance,
Object[] methodArgs, int level) throws CommandException {
if (worldEdit.getConfiguration().logCommands) {
final Logging loggingAnnotation = method.getAnnotation(Logging.class);
final Logging.LogMode logMode;
if (loggingAnnotation == null) {
logMode = null;
} else {
logMode = loggingAnnotation.value();
}
String msg = "WorldEdit: " + player.getName();
if (player.isPlayer()) {
msg += " (in \"" + player.getWorld().getName() + "\")";
}
msg += ": " + StringUtil.joinString(args, " ");
if (logMode != null && player.isPlayer()) {
Vector position = player.getPosition();
final LocalSession session = worldEdit.getSessionManager().get(player);
switch (logMode) {
case PLACEMENT:
try {
position = session.getPlacementPosition(player);
} catch (IncompleteRegionException e) {
break;
}
/* FALL-THROUGH */
case POSITION:
msg += " - Position: " + position;
break;
case ALL:
msg += " - Position: " + position;
/* FALL-THROUGH */
case ORIENTATION_REGION:
msg += " - Orientation: " + player.getCardinalDirection().name();
/* FALL-THROUGH */
case REGION:
try {
msg += " - Region: " + session.getSelection(player.getWorld());
} catch (IncompleteRegionException e) {
break;
}
break;
}
}
getLogger().info(msg);
}
super.invokeMethod(parent, args, player, method, instance, methodArgs, level);
}
/**
* Get the command dispatcher instance.
*
* @return the command dispatcher
*/
public Dispatcher getDispatcher() {
return dispatcher;
}
public static Logger getLogger() {

View File

@ -19,31 +19,24 @@
package com.sk89q.worldedit.extension.platform;
import com.sk89q.worldedit.WorldEditException;
/**
* Thrown when a platform registration request is rejected, which may
* be because another platform is already registered.
* Thrown when no capable platform is found.
*/
public class PlatformRejectionException extends WorldEditException {
public class NoCapablePlatformException extends RuntimeException {
/**
* Create with a message.
*
* @param message the message
*/
public PlatformRejectionException(String message) {
public NoCapablePlatformException() {
}
public NoCapablePlatformException(String message) {
super(message);
}
/**
* Create with a message and a cause.
*
* @param message the message
* @param cause the cause
*/
public PlatformRejectionException(String message, Throwable cause) {
public NoCapablePlatformException(String message, Throwable cause) {
super(message, cause);
}
public NoCapablePlatformException(Throwable cause) {
super(cause);
}
}

View File

@ -19,14 +19,15 @@
package com.sk89q.worldedit.extension.platform;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandsManager;
import com.sk89q.worldedit.BiomeTypes;
import com.sk89q.worldedit.LocalConfiguration;
import com.sk89q.worldedit.LocalPlayer;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.util.command.Dispatcher;
import com.sk89q.worldedit.world.World;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
/**
* Represents a platform that WorldEdit has been implemented for.
@ -77,10 +78,37 @@ public interface Platform {
List<? extends World> getWorlds();
@Deprecated
void onCommandRegistration(List<Command> commands);
/**
* Create a duplicate of the given player.
* </p>
* The given player may have been provided by a different platform.
*
* @param player the player to match
* @return a matched player, otherwise null
*/
@Nullable Player matchPlayer(Player player);
void onCommandRegistration(List<Command> commands, CommandsManager<LocalPlayer> manager);
/**
* Create a duplicate of the given world.
* </p>
* The given world may have been provided by a different platform.
*
* @param world the world to match
* @return a matched world, otherwise null
*/
@Nullable World matchWorld(World world);
/**
* Register the commands contained within the given command dispatcher.
*
* @param dispatcher the dispatcher
*/
void registerCommands(Dispatcher dispatcher);
/**
* Register game hooks.
*/
void registerGameHooks();
/**
* Get the configuration from this platform.
@ -116,4 +144,13 @@ public interface Platform {
*/
String getPlatformVersion();
/**
* Get a map of advertised capabilities of this platform, where each key
* in the given map is a supported capability and the respective value
* indicates the preference for this platform for that given capability.
*
* @return a map of capabilities
*/
Map<Capability, Preference> getCapabilities();
}

View File

@ -19,13 +19,23 @@
package com.sk89q.worldedit.extension.platform;
import com.sk89q.worldedit.LocalConfiguration;
import com.sk89q.worldedit.ServerInterface;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.*;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.command.tool.*;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.event.platform.BlockInteractEvent;
import com.sk89q.worldedit.event.platform.Interaction;
import com.sk89q.worldedit.event.platform.PlatformReadyEvent;
import com.sk89q.worldedit.event.platform.PlayerInputEvent;
import com.sk89q.worldedit.internal.ServerInterfaceAdapter;
import com.sk89q.worldedit.regions.RegionSelector;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.eventbus.Subscribe;
import com.sk89q.worldedit.world.World;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -41,10 +51,11 @@ public class PlatformManager {
private static final Logger logger = Logger.getLogger(PlatformManager.class.getCanonicalName());
private final LocalConfiguration defaultConfig = new DefaultConfiguration();
private final List<Platform> platforms = new ArrayList<Platform>();
private final WorldEdit worldEdit;
private final CommandManager commandManager;
private @Nullable Platform primary = null;
private final List<Platform> platforms = new ArrayList<Platform>();
private final Map<Capability, Platform> preferences = new EnumMap<Capability, Platform>(Capability.class);
private @Nullable String firstSeenVersion;
/**
* Create a new platform manager.
@ -53,63 +64,139 @@ public class PlatformManager {
*/
public PlatformManager(WorldEdit worldEdit) {
checkNotNull(worldEdit);
this.commandManager = new CommandManager(worldEdit);
this.worldEdit = worldEdit;
this.commandManager = new CommandManager(worldEdit, this);
// Register this instance for events
worldEdit.getEventBus().register(this);
}
/**
* Register a platform with WorldEdit.
*
* @param platform the platform
* @throws PlatformRejectionException thrown if the registration is rejected
*/
public synchronized void register(Platform platform) throws PlatformRejectionException {
public synchronized void register(Platform platform) {
checkNotNull(platform);
logger.log(Level.FINE, "Got request to register " + platform.getClass() + " with WorldEdit [" + super.toString() + "]");
// Just add the platform to the list of platforms: we'll pick favorites
// once all the platforms have been loaded
platforms.add(platform);
// Register primary platform
if (this.primary == null) {
commandManager.register(platform);
this.primary = platform;
} else {
// Make sure that versions are in sync
if (!primary.getVersion().equals(platform.getVersion())) {
// Make sure that versions are in sync
if (firstSeenVersion != null) {
if (!firstSeenVersion.equals(platform.getVersion())) {
logger.log(Level.WARNING,
"\n**********************************************\n" +
"** There is a mismatch in available WorldEdit platforms!\n" +
"** You have WorldEdit installed for multiple platforms in the same \n" +
"** game/program. This is OK except that you have different WorldEdit versions\n" +
"** installed (i.e. {0} and {1}).\n" +
"**\n" +
"** {0} v{1} is trying to register WE version v{2}\n" +
"** but the primary platform, {3} v{4}, uses WE version v{5}\n" +
"** WorldEdit has seen both versions {0} and {1}.\n" +
"**\n" +
"** Things may break! Please make sure that your WE versions are in sync.\n" +
"**********************************************\n",
new Object[]{
platform.getClass(), platform.getPlatformVersion(), platform.getVersion(),
primary.getClass(), primary.getPlatformVersion(), primary.getVersion()
firstSeenVersion, platform.getVersion()
});
}
} else {
firstSeenVersion = platform.getVersion();
}
}
/**
* Unregister a platform from WorldEdit.
* </p>
* If the platform has been chosen for any capabilities, then a new
* platform will be found.
*
* @param platform the platform
*/
public synchronized boolean unregister(Platform platform) {
checkNotNull(platform);
boolean removed = platforms.remove(platform);
if (removed) {
logger.log(Level.FINE, "Unregistering " + platform.getClass().getCanonicalName() + " from WorldEdit");
if (platform == primary) {
primary = null;
commandManager.unregister();
boolean choosePreferred = false;
// Check whether this platform was chosen to be the preferred one
// for any capability and be sure to remove it
Iterator<Entry<Capability, Platform>> it = preferences.entrySet().iterator();
while (it.hasNext()) {
Entry<Capability, Platform> entry = it.next();
if (entry.getValue().equals(platform)) {
entry.getKey().unload(this, entry.getValue());
it.remove();
choosePreferred = true; // Have to choose new favorites
}
}
if (choosePreferred) {
choosePreferred();
}
}
return removed;
}
/**
* Get the preferred platform for handling a certain capability. Returns
* null if none is available.
*
* @param capability the capability
* @return the platform
* @throws NoCapablePlatformException thrown if no platform is capable
*/
public synchronized Platform queryCapability(Capability capability) throws NoCapablePlatformException {
Platform platform = preferences.get(checkNotNull(capability));
if (platform != null) {
return platform;
} else {
throw new NoCapablePlatformException("No platform was found supporting " + capability.name());
}
}
/**
* Choose preferred platforms and perform necessary initialization.
*/
private synchronized void choosePreferred() {
for (Capability capability : Capability.values()) {
Platform preferred = findMostPreferred(capability);
if (preferred != null) {
preferences.put(capability, preferred);
capability.initialize(this, preferred);
}
}
}
/**
* Find the most preferred platform for a given capability from the list of
* platforms. This does not use the map of preferred platforms.
*
* @param capability the capability
* @return the most preferred platform, or null if no platform was found
*/
private synchronized @Nullable Platform findMostPreferred(Capability capability) {
Platform preferred = null;
Preference highest = null;
for (Platform platform : platforms) {
Preference preference = platform.getCapabilities().get(capability);
if (preference != null && (highest == null || preference.isPreferredOver(highest))) {
preferred = platform;
highest = preference;
}
}
return preferred;
}
/**
* Get a list of loaded platforms.
* </p>
@ -122,12 +209,41 @@ public class PlatformManager {
}
/**
* Get the primary platform.
* Given a world, possibly return the same world but using a different
* platform preferred for world editing operations.
*
* @return the primary platform (may be null)
* @param base the world to match
* @return the preferred world, if one was found, otherwise the given world
*/
public @Nullable Platform getPrimaryPlatform() {
return primary;
public World getWorldForEditing(World base) {
checkNotNull(base);
World match = queryCapability(Capability.WORLD_EDITING).matchWorld(base);
return match != null ? match : base;
}
/**
* Given an actor, return a new one that may use a different platform
* for permissions and world editing.
*
* @param base the base actor to match
* @return a new delegate actor
*/
@SuppressWarnings("unchecked")
public <T extends Actor> T createProxyActor(T base) {
checkNotNull(base);
if (base instanceof Player) {
Player player = (Player) base;
Player permActor = queryCapability(Capability.PERMISSIONS).matchPlayer(player);
if (permActor == null) {
permActor = player;
}
return (T) new PlayerProxy(player, permActor, getWorldForEditing(player.getWorld()));
} else {
return base;
}
}
/**
@ -148,26 +264,7 @@ public class PlatformManager {
* @return the configuration
*/
public LocalConfiguration getConfiguration() {
Platform platform = primary;
if (platform != null) {
return platform.getConfiguration();
} else {
return defaultConfig;
}
}
/**
* Return a {@link Platform}.
*
* @return a {@link Platform}
* @throws IllegalStateException if no platform has been registered
*/
public Platform getPlatform() throws IllegalStateException {
Platform platform = primary;
if (platform != null) {
return platform;
} else {
throw new IllegalStateException("No platform has been registered");
}
return queryCapability(Capability.CONFIGURATION).getConfiguration();
}
/**
@ -176,26 +273,174 @@ public class PlatformManager {
* @return a {@link ServerInterface}
* @throws IllegalStateException if no platform has been registered
*/
@SuppressWarnings("deprecation")
public ServerInterface getServerInterface() throws IllegalStateException {
Platform platform = primary;
if (platform != null) {
if (platform instanceof ServerInterface) {
return (ServerInterface) platform;
} else {
return new ServerInterfaceAdapter(platform);
return ServerInterfaceAdapter.adapt(queryCapability(Capability.USER_COMMANDS));
}
@Subscribe
public void handlePlatformReady(PlatformReadyEvent event) {
choosePreferred();
}
@SuppressWarnings("deprecation")
@Subscribe
public void handleBlockInteract(BlockInteractEvent event) {
// Create a proxy actor with a potentially different world for
// making changes to the world
Actor actor = createProxyActor(event.getCause());
Location location = event.getLocation();
Vector vector = location.toVector();
// At this time, only handle interaction from players
if (actor instanceof Player) {
Player player = (Player) actor;
LocalSession session = worldEdit.getSessionManager().get(actor);
if (event.getType() == Interaction.HIT) {
if (player.getItemInHand() == getConfiguration().wandItem) {
if (!session.isToolControlEnabled()) {
return;
}
if (!actor.hasPermission("worldedit.selection.pos")) {
return;
}
RegionSelector selector = session.getRegionSelector(player.getWorld());
if (selector.selectPrimary(location.toVector())) {
selector.explainPrimarySelection(actor, session, vector);
}
event.setCancelled(true);
return;
}
if (player.isHoldingPickAxe() && session.hasSuperPickAxe()) {
final BlockTool superPickaxe = session.getSuperPickaxe();
if (superPickaxe != null && superPickaxe.canUse(player)) {
event.setCancelled(superPickaxe.actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location));
return;
}
}
Tool tool = session.getTool(player.getItemInHand());
if (tool != null && tool instanceof DoubleActionBlockTool) {
if (tool.canUse(player)) {
((DoubleActionBlockTool) tool).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location);
event.setCancelled(true);
}
}
} else if (event.getType() == Interaction.OPEN) {
if (player.getItemInHand() == getConfiguration().wandItem) {
if (!session.isToolControlEnabled()) {
return;
}
if (!actor.hasPermission("worldedit.selection.pos")) {
return;
}
RegionSelector selector = session.getRegionSelector(player.getWorld());
if (selector.selectSecondary(vector)) {
selector.explainSecondarySelection(actor, session, vector);
}
event.setCancelled(true);
return;
}
Tool tool = session.getTool(player.getItemInHand());
if (tool != null && tool instanceof BlockTool) {
if (tool.canUse(player)) {
((BlockTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location);
event.setCancelled(true);
}
}
}
} else {
throw new IllegalStateException("No platform has been registered");
}
}
/**
* A default configuration for when none is set.
*/
private static class DefaultConfiguration extends LocalConfiguration {
@Override
public void load() {
@SuppressWarnings("deprecation")
@Subscribe
public void handlePlayerInput(PlayerInputEvent event) {
// Create a proxy actor with a potentially different world for
// making changes to the world
Player player = createProxyActor(event.getPlayer());
World world = player.getWorld();
switch (event.getInputType()) {
case PRIMARY: {
if (player.getItemInHand() == getConfiguration().navigationWand) {
if (getConfiguration().navigationWandMaxDistance <= 0) {
return;
}
if (!player.hasPermission("worldedit.navigation.jumpto.tool")) {
return;
}
WorldVector pos = player.getSolidBlockTrace(getConfiguration().navigationWandMaxDistance);
if (pos != null) {
player.findFreePosition(pos);
} else {
player.printError("No block in sight (or too far)!");
}
event.setCancelled(true);
return;
}
LocalSession session = worldEdit.getSessionManager().get(player);
Tool tool = session.getTool(player.getItemInHand());
if (tool != null && tool instanceof DoubleActionTraceTool) {
if (tool.canUse(player)) {
((DoubleActionTraceTool) tool).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session);
event.setCancelled(true);
return;
}
}
break;
}
case SECONDARY: {
if (player.getItemInHand() == getConfiguration().navigationWand) {
if (getConfiguration().navigationWandMaxDistance <= 0) {
return;
}
if (!player.hasPermission("worldedit.navigation.thru.tool")) {
return;
}
if (!player.passThroughForwardWall(40)) {
player.printError("Nothing to pass through!");
}
event.setCancelled(true);
return;
}
LocalSession session = worldEdit.getSessionManager().get(player);
Tool tool = session.getTool(player.getItemInHand());
if (tool != null && tool instanceof TraceTool) {
if (tool.canUse(player)) {
((TraceTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session);
event.setCancelled(true);
return;
}
}
break;
}
}
}
}

View File

@ -0,0 +1,131 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.extension.platform;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldVector;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extent.inventory.BlockBag;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.World;
import static com.google.common.base.Preconditions.checkNotNull;
class PlayerProxy extends AbstractPlayerActor {
private final Player basePlayer;
private final Actor permActor;
private final World world;
PlayerProxy(Player basePlayer, Actor permActor, World world) {
checkNotNull(basePlayer);
checkNotNull(permActor);
checkNotNull(world);
this.basePlayer = basePlayer;
this.permActor = permActor;
this.world = world;
}
@Override
public int getItemInHand() {
return basePlayer.getItemInHand();
}
@Override
public void giveItem(int type, int amount) {
basePlayer.giveItem(type, amount);
}
@Override
public BlockBag getInventoryBlockBag() {
return basePlayer.getInventoryBlockBag();
}
@Override
public String getName() {
return basePlayer.getName();
}
@Override
public BaseEntity getState() {
throw new UnsupportedOperationException("Can't getState() on a player");
}
@Override
public Location getLocation() {
return basePlayer.getLocation();
}
@Override
public WorldVector getPosition() {
return basePlayer.getPosition();
}
@Override
public double getPitch() {
return basePlayer.getPitch();
}
@Override
public double getYaw() {
return basePlayer.getYaw();
}
@Override
public void setPosition(Vector pos, float pitch, float yaw) {
basePlayer.setPosition(pos, pitch, yaw);
}
@Override
public World getWorld() {
return world;
}
@Override
public void printRaw(String msg) {
basePlayer.printRaw(msg);
}
@Override
public void printDebug(String msg) {
basePlayer.printDebug(msg);
}
@Override
public void print(String msg) {
basePlayer.print(msg);
}
@Override
public void printError(String msg) {
basePlayer.printError(msg);
}
@Override
public String[] getGroups() {
return permActor.getGroups();
}
@Override
public boolean hasPermission(String perm) {
return permActor.hasPermission(perm);
}
}

View File

@ -0,0 +1,59 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.extension.platform;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Indicates the preference of a platform for a particular
* {@link Capability}.
*/
public enum Preference {
/**
* Indicates that the platform should be preferred for a given capability.
*/
PREFERRED,
/**
* Indicates that preference for a platform is neutral for a given
* capability.
*/
NORMAL,
/**
* Indicates that there should not be a preference for the platform for
* a given capability.
*/
PREFER_OTHERS;
/**
* Returns whether this given preference is preferred over the given
* other preference.
*
* @param other the other preference
* @return true if this preference is greater
*/
public boolean isPreferredOver(Preference other) {
checkNotNull(other);
return ordinal() < other.ordinal();
}
}

View File

@ -1,109 +0,0 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.extension.platform;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandsManager;
import com.sk89q.worldedit.*;
import com.sk89q.worldedit.world.World;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Adapts {@link Platform}s into the legacy {@link ServerInterface}.
*/
class ServerInterfaceAdapter extends ServerInterface {
private final Platform platform;
/**
* Create a new adapter.
*
* @param platform the platform
*/
ServerInterfaceAdapter(Platform platform) {
checkNotNull(platform);
this.platform = platform;
}
@Override
public int resolveItem(String name) {
return platform.resolveItem(name);
}
@Override
public boolean isValidMobType(String type) {
return platform.isValidMobType(type);
}
@Override
public void reload() {
platform.reload();
}
@Override
public BiomeTypes getBiomes() {
return platform.getBiomes();
}
@Override
public int schedule(long delay, long period, Runnable task) {
return platform.schedule(delay, period, task);
}
@Override
public List<? extends World> getWorlds() {
return platform.getWorlds();
}
@Override
@Deprecated
public void onCommandRegistration(List<Command> commands) {
platform.onCommandRegistration(commands);
}
@Override
public void onCommandRegistration(List<Command> commands, CommandsManager<LocalPlayer> manager) {
platform.onCommandRegistration(commands, manager);
}
@Override
public LocalConfiguration getConfiguration() {
return platform.getConfiguration();
}
@Override
public String getVersion() {
return platform.getVersion();
}
@Override
public String getPlatformName() {
return platform.getPlatformName();
}
@Override
public String getPlatformVersion() {
return platform.getPlatformVersion();
}
}