2014-04-05 03:53:58 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2014-06-26 23:56:40 +00:00
|
|
|
import com.sk89q.worldedit.*;
|
2014-06-27 08:11:35 +00:00
|
|
|
import com.sk89q.worldedit.Vector;
|
2014-06-27 03:07:04 +00:00
|
|
|
import com.sk89q.worldedit.command.tool.*;
|
2014-06-26 23:56:40 +00:00
|
|
|
import com.sk89q.worldedit.entity.Player;
|
2014-06-27 03:07:04 +00:00
|
|
|
import com.sk89q.worldedit.event.platform.BlockInteractEvent;
|
|
|
|
import com.sk89q.worldedit.event.platform.Interaction;
|
2014-06-27 08:11:35 +00:00
|
|
|
import com.sk89q.worldedit.event.platform.PlatformReadyEvent;
|
2014-06-27 03:07:04 +00:00
|
|
|
import com.sk89q.worldedit.event.platform.PlayerInputEvent;
|
2014-06-26 23:56:40 +00:00
|
|
|
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;
|
2014-06-27 20:14:44 +00:00
|
|
|
import com.sk89q.worldedit.world.World;
|
2014-04-05 03:53:58 +00:00
|
|
|
|
|
|
|
import javax.annotation.Nullable;
|
2014-06-27 08:11:35 +00:00
|
|
|
import java.util.*;
|
|
|
|
import java.util.Map.Entry;
|
2014-04-05 03:53:58 +00:00
|
|
|
import java.util.logging.Level;
|
|
|
|
import java.util.logging.Logger;
|
|
|
|
|
|
|
|
import static com.google.common.base.Preconditions.checkNotNull;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Manages registered {@link Platform}s for WorldEdit. Platforms are
|
|
|
|
* implementations of WorldEdit.
|
|
|
|
* </p>
|
|
|
|
* This class is thread-safe.
|
|
|
|
*/
|
|
|
|
public class PlatformManager {
|
|
|
|
|
|
|
|
private static final Logger logger = Logger.getLogger(PlatformManager.class.getCanonicalName());
|
|
|
|
|
2014-06-26 23:56:40 +00:00
|
|
|
private final WorldEdit worldEdit;
|
2014-04-05 03:53:58 +00:00
|
|
|
private final CommandManager commandManager;
|
2014-06-27 08:11:35 +00:00
|
|
|
private final List<Platform> platforms = new ArrayList<Platform>();
|
|
|
|
private final Map<Capability, Platform> preferences = new EnumMap<Capability, Platform>(Capability.class);
|
|
|
|
private @Nullable String firstSeenVersion;
|
2014-04-05 03:53:58 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new platform manager.
|
|
|
|
*
|
|
|
|
* @param worldEdit the WorldEdit instance
|
|
|
|
*/
|
|
|
|
public PlatformManager(WorldEdit worldEdit) {
|
|
|
|
checkNotNull(worldEdit);
|
2014-06-26 23:56:40 +00:00
|
|
|
this.worldEdit = worldEdit;
|
2014-06-28 03:12:44 +00:00
|
|
|
this.commandManager = new CommandManager(worldEdit, this);
|
2014-06-26 23:56:40 +00:00
|
|
|
|
|
|
|
// Register this instance for events
|
|
|
|
worldEdit.getEventBus().register(this);
|
2014-04-05 03:53:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Register a platform with WorldEdit.
|
|
|
|
*
|
|
|
|
* @param platform the platform
|
|
|
|
*/
|
2014-06-27 08:11:35 +00:00
|
|
|
public synchronized void register(Platform platform) {
|
2014-04-05 03:53:58 +00:00
|
|
|
checkNotNull(platform);
|
2014-06-27 08:11:35 +00:00
|
|
|
|
2014-04-06 06:27:10 +00:00
|
|
|
logger.log(Level.FINE, "Got request to register " + platform.getClass() + " with WorldEdit [" + super.toString() + "]");
|
2014-06-27 08:11:35 +00:00
|
|
|
|
|
|
|
// Just add the platform to the list of platforms: we'll pick favorites
|
|
|
|
// once all the platforms have been loaded
|
2014-04-05 03:53:58 +00:00
|
|
|
platforms.add(platform);
|
2014-04-06 06:27:10 +00:00
|
|
|
|
2014-06-27 08:11:35 +00:00
|
|
|
// Make sure that versions are in sync
|
|
|
|
if (firstSeenVersion != null) {
|
|
|
|
if (!firstSeenVersion.equals(platform.getVersion())) {
|
2014-07-19 00:19:35 +00:00
|
|
|
logger.log(Level.WARNING, "Multiple ports of WorldEdit are installed but they report different versions ({0} and {1}). " +
|
|
|
|
"If these two versions are truly different, then you may run into unexpected crashes and errors.",
|
|
|
|
new Object[]{ firstSeenVersion, platform.getVersion() });
|
2014-04-06 06:27:10 +00:00
|
|
|
}
|
2014-06-27 08:11:35 +00:00
|
|
|
} else {
|
|
|
|
firstSeenVersion = platform.getVersion();
|
2014-04-05 03:53:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unregister a platform from WorldEdit.
|
2014-06-27 08:11:35 +00:00
|
|
|
* </p>
|
|
|
|
* If the platform has been chosen for any capabilities, then a new
|
|
|
|
* platform will be found.
|
2014-04-05 03:53:58 +00:00
|
|
|
*
|
|
|
|
* @param platform the platform
|
|
|
|
*/
|
|
|
|
public synchronized boolean unregister(Platform platform) {
|
|
|
|
checkNotNull(platform);
|
2014-06-27 08:11:35 +00:00
|
|
|
|
2014-04-05 03:53:58 +00:00
|
|
|
boolean removed = platforms.remove(platform);
|
2014-06-27 08:11:35 +00:00
|
|
|
|
2014-04-05 03:53:58 +00:00
|
|
|
if (removed) {
|
|
|
|
logger.log(Level.FINE, "Unregistering " + platform.getClass().getCanonicalName() + " from WorldEdit");
|
|
|
|
|
2014-06-27 08:11:35 +00:00
|
|
|
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();
|
2014-04-05 03:53:58 +00:00
|
|
|
}
|
|
|
|
}
|
2014-06-27 08:11:35 +00:00
|
|
|
|
2014-04-05 03:53:58 +00:00
|
|
|
return removed;
|
|
|
|
}
|
|
|
|
|
2014-06-27 08:11:35 +00:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2014-04-06 06:27:10 +00:00
|
|
|
/**
|
|
|
|
* Get a list of loaded platforms.
|
|
|
|
* </p>
|
|
|
|
* The returned list is a copy of the original and is mutable.
|
|
|
|
*
|
|
|
|
* @return a list of platforms
|
|
|
|
*/
|
|
|
|
public synchronized List<Platform> getPlatforms() {
|
|
|
|
return new ArrayList<Platform>(platforms);
|
|
|
|
}
|
|
|
|
|
2014-06-27 20:14:44 +00:00
|
|
|
/**
|
|
|
|
* Given a world, possibly return the same world but using a different
|
|
|
|
* platform preferred for world editing operations.
|
|
|
|
*
|
|
|
|
* @param base the world to match
|
|
|
|
* @return the preferred world, if one was found, otherwise the given world
|
|
|
|
*/
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-07-07 00:13:27 +00:00
|
|
|
Player cuiActor = queryCapability(Capability.WORLDEDIT_CUI).matchPlayer(player);
|
|
|
|
if (cuiActor == null) {
|
|
|
|
cuiActor = player;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (T) new PlayerProxy(player, permActor, cuiActor, getWorldForEditing(player.getWorld()));
|
2014-06-27 20:14:44 +00:00
|
|
|
} else {
|
|
|
|
return base;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-05 03:53:58 +00:00
|
|
|
/**
|
|
|
|
* Get the command manager.
|
|
|
|
*
|
|
|
|
* @return the command manager
|
|
|
|
*/
|
|
|
|
public CommandManager getCommandManager() {
|
|
|
|
return commandManager;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the current configuration.
|
|
|
|
* </p>
|
|
|
|
* If no platform has been registered yet, then a default configuration
|
|
|
|
* will be returned.
|
|
|
|
*
|
|
|
|
* @return the configuration
|
|
|
|
*/
|
|
|
|
public LocalConfiguration getConfiguration() {
|
2014-06-27 08:11:35 +00:00
|
|
|
return queryCapability(Capability.CONFIGURATION).getConfiguration();
|
2014-04-05 03:53:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return a legacy {@link ServerInterface}.
|
|
|
|
*
|
|
|
|
* @return a {@link ServerInterface}
|
|
|
|
* @throws IllegalStateException if no platform has been registered
|
|
|
|
*/
|
2014-06-27 08:11:35 +00:00
|
|
|
@SuppressWarnings("deprecation")
|
2014-04-05 03:53:58 +00:00
|
|
|
public ServerInterface getServerInterface() throws IllegalStateException {
|
2014-06-27 08:11:35 +00:00
|
|
|
return ServerInterfaceAdapter.adapt(queryCapability(Capability.USER_COMMANDS));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Subscribe
|
|
|
|
public void handlePlatformReady(PlatformReadyEvent event) {
|
|
|
|
choosePreferred();
|
2014-04-05 03:53:58 +00:00
|
|
|
}
|
|
|
|
|
2014-06-27 08:11:35 +00:00
|
|
|
@SuppressWarnings("deprecation")
|
2014-06-26 23:56:40 +00:00
|
|
|
@Subscribe
|
|
|
|
public void handleBlockInteract(BlockInteractEvent event) {
|
2014-06-27 20:14:44 +00:00
|
|
|
// Create a proxy actor with a potentially different world for
|
|
|
|
// making changes to the world
|
|
|
|
Actor actor = createProxyActor(event.getCause());
|
|
|
|
|
2014-06-26 23:56:40 +00:00
|
|
|
Location location = event.getLocation();
|
|
|
|
Vector vector = location.toVector();
|
|
|
|
|
|
|
|
// At this time, only handle interaction from players
|
|
|
|
if (actor instanceof Player) {
|
2014-06-27 00:07:08 +00:00
|
|
|
Player player = (Player) actor;
|
|
|
|
LocalSession session = worldEdit.getSessionManager().get(actor);
|
2014-06-26 23:56:40 +00:00
|
|
|
|
2014-06-27 03:07:04 +00:00
|
|
|
if (event.getType() == Interaction.HIT) {
|
2014-06-26 23:58:54 +00:00
|
|
|
if (player.getItemInHand() == getConfiguration().wandItem) {
|
|
|
|
if (!session.isToolControlEnabled()) {
|
|
|
|
return;
|
|
|
|
}
|
2014-06-26 23:56:40 +00:00
|
|
|
|
2014-06-26 23:58:54 +00:00
|
|
|
if (!actor.hasPermission("worldedit.selection.pos")) {
|
|
|
|
return;
|
|
|
|
}
|
2014-06-26 23:56:40 +00:00
|
|
|
|
2014-06-26 23:58:54 +00:00
|
|
|
RegionSelector selector = session.getRegionSelector(player.getWorld());
|
2014-06-26 23:56:40 +00:00
|
|
|
|
2014-06-26 23:58:54 +00:00
|
|
|
if (selector.selectPrimary(location.toVector())) {
|
|
|
|
selector.explainPrimarySelection(actor, session, vector);
|
|
|
|
}
|
2014-06-26 23:56:40 +00:00
|
|
|
|
2014-06-26 23:58:54 +00:00
|
|
|
event.setCancelled(true);
|
|
|
|
return;
|
|
|
|
}
|
2014-06-26 23:56:40 +00:00
|
|
|
|
2014-06-28 02:55:38 +00:00
|
|
|
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;
|
2014-06-26 23:56:40 +00:00
|
|
|
}
|
2014-06-28 02:55:38 +00:00
|
|
|
}
|
2014-06-26 23:56:40 +00:00
|
|
|
|
2014-06-28 02:55:38 +00:00
|
|
|
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);
|
2014-06-26 23:56:40 +00:00
|
|
|
}
|
|
|
|
}
|
2014-06-27 00:07:08 +00:00
|
|
|
|
2014-06-27 03:07:04 +00:00
|
|
|
} else if (event.getType() == Interaction.OPEN) {
|
2014-06-27 00:07:08 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-06-28 02:55:38 +00:00
|
|
|
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);
|
2014-06-27 00:07:08 +00:00
|
|
|
}
|
|
|
|
}
|
2014-06-26 23:56:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-27 08:11:35 +00:00
|
|
|
@SuppressWarnings("deprecation")
|
2014-06-27 03:07:04 +00:00
|
|
|
@Subscribe
|
|
|
|
public void handlePlayerInput(PlayerInputEvent event) {
|
2014-06-27 20:14:44 +00:00
|
|
|
// Create a proxy actor with a potentially different world for
|
|
|
|
// making changes to the world
|
|
|
|
Player player = createProxyActor(event.getPlayer());
|
2014-06-28 02:55:38 +00:00
|
|
|
World world = player.getWorld();
|
2014-06-27 03:07:04 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2014-06-28 02:55:38 +00:00
|
|
|
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;
|
2014-06-27 03:07:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2014-06-28 02:55:38 +00:00
|
|
|
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;
|
2014-06-27 03:07:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-05 03:53:58 +00:00
|
|
|
|
|
|
|
}
|