From e5ebccd05324b18ef2be9dbce6c269e50fbcb20d Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Wed, 3 Aug 2016 19:23:31 +1000 Subject: [PATCH] All commands translatable + Start on inspect brush + Disable AWE if present as otherwise both don't load --- .../com/boydti/fawe/bukkit/ABukkitMain.java | 43 +++ bukkit110/src/main/resources/plugin.yml | 2 +- bukkit1710/src/main/resources/plugin.yml | 2 +- bukkit18/src/main/resources/plugin.yml | 2 +- bukkit19/src/main/resources/plugin.yml | 2 +- core/src/main/java/com/boydti/fawe/Fawe.java | 5 + .../main/java/com/boydti/fawe/config/BBC.java | 1 + .../java/com/boydti/fawe/config/Commands.java | 116 ++++++++ .../fawe/object/brush/InspectBrush.java | 24 ++ .../boydti/fawe/util/DelegateFaweQueue.java | 6 + .../worldedit/command/BrushCommands.java | 19 ++ .../command/parametric/ParametricBuilder.java | 270 ++++++++++++++++++ 12 files changed, 488 insertions(+), 4 deletions(-) create mode 100644 core/src/main/java/com/boydti/fawe/config/Commands.java create mode 100644 core/src/main/java/com/boydti/fawe/object/brush/InspectBrush.java create mode 100644 core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java diff --git a/bukkit0/src/main/java/com/boydti/fawe/bukkit/ABukkitMain.java b/bukkit0/src/main/java/com/boydti/fawe/bukkit/ABukkitMain.java index 1997b2ce..83a8c069 100644 --- a/bukkit0/src/main/java/com/boydti/fawe/bukkit/ABukkitMain.java +++ b/bukkit0/src/main/java/com/boydti/fawe/bukkit/ABukkitMain.java @@ -1,10 +1,53 @@ package com.boydti.fawe.bukkit; +import com.boydti.fawe.Fawe; import com.boydti.fawe.object.FaweQueue; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; public abstract class ABukkitMain extends JavaPlugin { + static { + { // Disable AWE as otherwise both fail to load + PluginManager manager = Bukkit.getPluginManager(); + try { + Field pluginsField = manager.getClass().getDeclaredField("plugins"); + Field lookupNamesField = manager.getClass().getDeclaredField("lookupNames"); + pluginsField.setAccessible(true); + lookupNamesField.setAccessible(true); + List plugins = (List) pluginsField.get(manager); + Map lookupNames = (Map) lookupNamesField.get(manager); + pluginsField.set(manager, new ArrayList(plugins) { + @Override + public boolean add(Plugin plugin) { + if (!plugin.getName().startsWith("AsyncWorldEdit")) { + return super.add(plugin); + } else { + Fawe.debug("[FAWE] Disabling `" + plugin.getName() + "` as it is incompatible"); + } + return false; + } + }); + lookupNamesField.set(manager, new ConcurrentHashMap(lookupNames) { + @Override + public Plugin put(String key, Plugin plugin) { + if (!plugin.getName().startsWith("AsyncWorldEdit")) { + return super.put(key, plugin); + } + return null; + } + }); + } catch (Throwable ignore) {} + } + } + @Override public void onEnable() { FaweBukkit imp = new FaweBukkit(this); diff --git a/bukkit110/src/main/resources/plugin.yml b/bukkit110/src/main/resources/plugin.yml index d2788b8c..97a1106a 100644 --- a/bukkit110/src/main/resources/plugin.yml +++ b/bukkit110/src/main/resources/plugin.yml @@ -3,7 +3,7 @@ main: com.boydti.fawe.bukkit.v1_10.BukkitMain_110 version: ${version} description: Fast Async WorldEdit plugin authors: [Empire92] -loadbefore: [WorldEdit,AsyncWorldEdit] +loadbefore: [WorldEdit,AsyncWorldEdit,AsyncWorldEditInjector] load: STARTUP database: false #softdepend: [WorldGuard, PlotSquared, MCore, Factions, GriefPrevention, Residence, Towny, PlotMe, PreciousStones] diff --git a/bukkit1710/src/main/resources/plugin.yml b/bukkit1710/src/main/resources/plugin.yml index a2cb9b59..79ad4207 100644 --- a/bukkit1710/src/main/resources/plugin.yml +++ b/bukkit1710/src/main/resources/plugin.yml @@ -3,7 +3,7 @@ main: com.boydti.fawe.bukkit.v1_7.BukkitMain_17 version: ${version} description: Fast Async WorldEdit plugin authors: [Empire92] -loadbefore: [WorldEdit,AsyncWorldEdit] +loadbefore: [WorldEdit,AsyncWorldEdit,AsyncWorldEditInjector] load: STARTUP database: false #softdepend: [WorldGuard, PlotSquared, MCore, Factions, GriefPrevention, Residence, Towny, PlotMe, PreciousStones] diff --git a/bukkit18/src/main/resources/plugin.yml b/bukkit18/src/main/resources/plugin.yml index d0bf7f26..475cd05b 100644 --- a/bukkit18/src/main/resources/plugin.yml +++ b/bukkit18/src/main/resources/plugin.yml @@ -3,7 +3,7 @@ main: com.boydti.fawe.bukkit.v1_8.BukkitMain_18 version: ${version} description: Fast Async WorldEdit plugin authors: [Empire92] -loadbefore: [WorldEdit,AsyncWorldEdit] +loadbefore: [WorldEdit,AsyncWorldEdit,AsyncWorldEditInjector] load: STARTUP database: false #softdepend: [WorldGuard, PlotSquared, MCore, Factions, GriefPrevention, Residence, Towny, PlotMe, PreciousStones] diff --git a/bukkit19/src/main/resources/plugin.yml b/bukkit19/src/main/resources/plugin.yml index 6970351d..78f59b58 100644 --- a/bukkit19/src/main/resources/plugin.yml +++ b/bukkit19/src/main/resources/plugin.yml @@ -3,7 +3,7 @@ main: com.boydti.fawe.bukkit.v1_9.BukkitMain_19 version: ${version} description: Fast Async WorldEdit plugin authors: [Empire92] -loadbefore: [WorldEdit,AsyncWorldEdit] +loadbefore: [WorldEdit,AsyncWorldEdit,AsyncWorldEditInjector] load: STARTUP database: false #softdepend: [WorldGuard, PlotSquared, MCore, Factions, GriefPrevention, Residence, Towny, PlotMe, PreciousStones] diff --git a/core/src/main/java/com/boydti/fawe/Fawe.java b/core/src/main/java/com/boydti/fawe/Fawe.java index 8b941f3a..b4996268 100644 --- a/core/src/main/java/com/boydti/fawe/Fawe.java +++ b/core/src/main/java/com/boydti/fawe/Fawe.java @@ -7,6 +7,7 @@ import com.boydti.fawe.command.Stream; import com.boydti.fawe.command.Wea; import com.boydti.fawe.command.WorldEditRegion; import com.boydti.fawe.config.BBC; +import com.boydti.fawe.config.Commands; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.regions.general.plot.PlotSquaredFeature; @@ -55,6 +56,7 @@ import com.sk89q.worldedit.history.change.EntityCreate; import com.sk89q.worldedit.history.change.EntityRemove; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; +import com.sk89q.worldedit.util.command.parametric.ParametricBuilder; import com.sk89q.worldedit.world.registry.BundledBlockData; import java.io.File; import java.io.IOException; @@ -240,6 +242,8 @@ public class Fawe { Settings.save(file); // Setting up message.yml BBC.load(new File(this.IMP.getDirectory(), "message.yml")); + // Setting up commands.yml + Commands.load(new File(this.IMP.getDirectory(), "commands.yml")); // Block rotation try { BundledBlockData.getInstance().loadFromResource(); @@ -280,6 +284,7 @@ public class Fawe { RegionCommands.inject(); // Translations HistoryCommands.inject(); // Translations NavigationCommands.inject(); // Translations + thru fix + ParametricBuilder.inject(); // Translations // Schematic SchematicReader.inject(); SchematicWriter.inject(); diff --git a/core/src/main/java/com/boydti/fawe/config/BBC.java b/core/src/main/java/com/boydti/fawe/config/BBC.java index 191c7084..4fb394a4 100644 --- a/core/src/main/java/com/boydti/fawe/config/BBC.java +++ b/core/src/main/java/com/boydti/fawe/config/BBC.java @@ -90,6 +90,7 @@ public enum BBC { BRUSH_HEIGHT_INVALID("Invalid height map file (%s0)", "WorldEdit.Brush"), BRUSH_SMOOTH("Smooth brush equipped (%s0 x %s1 using %s2).", "WorldEdit.Brush"), BRUSH_SPHERE("Sphere brush shape equipped (%s0).", "WorldEdit.Brush"), + BRUSH_INSPECT("Inspect brush shape equipped (%s0).", "WorldEdit.Brush"), SCHEMATIC_DELETE("%s0 has been deleted.", "Worldedit.Schematic"), SCHEMATIC_FORMAT("Available clipboard formats (Name: Lookup names)", "Worldedit.Schematic"), diff --git a/core/src/main/java/com/boydti/fawe/config/Commands.java b/core/src/main/java/com/boydti/fawe/config/Commands.java new file mode 100644 index 00000000..1058da7d --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/config/Commands.java @@ -0,0 +1,116 @@ +package com.boydti.fawe.config; + +import com.boydti.fawe.configuration.ConfigurationSection; +import com.boydti.fawe.configuration.file.YamlConfiguration; +import com.sk89q.minecraft.util.commands.Command; +import java.io.File; +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class Commands { + + private static YamlConfiguration cmdConfig; + private static File cmdFile; + + public static void load(File file) { + cmdFile = file; + try { + if (!file.exists()) { + file.getParentFile().mkdirs(); + file.createNewFile(); + } + cmdConfig = YamlConfiguration.loadConfiguration(file); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static Command translate(final Command command) { + if (cmdConfig == null) { + return command; + } + + String id = command.aliases()[0]; + ConfigurationSection commands = cmdConfig.getConfigurationSection(id); + boolean set = false; + if (commands == null) { + set = (commands = cmdConfig.createSection(id)) != null; + } + + HashMap options = new HashMap<>(); + options.put("aliases", new ArrayList(Arrays.asList(command.aliases()))); + options.put("usage", command.usage()); + options.put("desc", command.desc()); + options.put("help", command.help()); + + for (Map.Entry entry : options.entrySet()) { + String key = entry.getKey(); + if (!commands.contains(key)) { + commands.set(key, entry.getValue()); + set = true; + } + } + if (set) { + try { + cmdConfig.save(cmdFile); + } catch (IOException e) { + e.printStackTrace(); + } + } + final String[] aliases = commands.getStringList("aliases").toArray(new String[0]); + final String usage = commands.getString("usage"); + final String desc = commands.getString("desc"); + final String help = commands.getString("help"); + + return new Command() { + @Override + public Class annotationType() { + return command.annotationType(); + } + + @Override + public String[] aliases() { + return aliases; + } + + @Override + public String usage() { + return usage; + } + + @Override + public String desc() { + return desc; + } + + @Override + public int min() { + return command.min(); + } + + @Override + public int max() { + return command.max(); + } + + @Override + public String flags() { + return command.flags(); + } + + @Override + public String help() { + return help; + } + + @Override + public boolean anyFlags() { + return command.anyFlags(); + } + }; + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/brush/InspectBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/InspectBrush.java new file mode 100644 index 00000000..d3d1a2ce --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/brush/InspectBrush.java @@ -0,0 +1,24 @@ +package com.boydti.fawe.object.brush; + +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.command.tool.brush.Brush; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.function.pattern.Pattern; + +public class InspectBrush implements Brush { + + private final Player player; + private final int radius; + + public InspectBrush(Player player, double radius) { + this.player = player; + this.radius = (int) radius; + } + + @Override + public void build(EditSession editSession, Vector position, Pattern pattern, double size) throws MaxChangedBlocksException { + // TODO + } +} diff --git a/core/src/main/java/com/boydti/fawe/util/DelegateFaweQueue.java b/core/src/main/java/com/boydti/fawe/util/DelegateFaweQueue.java index 5df07e22..2646c88c 100644 --- a/core/src/main/java/com/boydti/fawe/util/DelegateFaweQueue.java +++ b/core/src/main/java/com/boydti/fawe/util/DelegateFaweQueue.java @@ -8,6 +8,7 @@ import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.world.biome.BaseBiome; import java.io.File; +import java.util.Collection; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentLinkedDeque; @@ -123,6 +124,11 @@ public class DelegateFaweQueue extends FaweQueue { return parent.getFaweChunk(x, z); } + @Override + public Collection getFaweChunks() { + return parent.getFaweChunks(); + } + @Override public void setChunk(FaweChunk chunk) { parent.setChunk(chunk); diff --git a/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index 66d84a2f..ce32cc9a 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -27,6 +27,7 @@ import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.brush.CommandBrush; import com.boydti.fawe.object.brush.CopyBrush; import com.boydti.fawe.object.brush.HeightBrush; +import com.boydti.fawe.object.brush.InspectBrush; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; @@ -79,6 +80,24 @@ public class BrushCommands { this.worldEdit = worldEdit; } + @Command( + aliases = { "inspect", "i" }, + usage = "", + desc = "Inspect edits within a radius", + help = + "Chooses the inspect brush", + min = 0, + max = 1 + ) + @CommandPermissions("worldedit.brush.inspect") + public void inspectBrush(Player player, LocalSession session, EditSession editSession, @Optional("1") double radius) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); + BrushTool tool = session.getBrushTool(player.getItemInHand()); + tool.setSize(radius); + tool.setBrush(new InspectBrush(player, radius), "worldedit.brush.inspect"); + BBC.BRUSH_INSPECT.send(player, radius); + } + @Command( aliases = { "sphere", "s" }, usage = " [radius]", diff --git a/core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java b/core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java new file mode 100644 index 00000000..c7bed330 --- /dev/null +++ b/core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java @@ -0,0 +1,270 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * 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 . + */ + +package com.sk89q.worldedit.util.command.parametric; + +import com.boydti.fawe.config.Commands; +import com.google.common.collect.ImmutableBiMap.Builder; +import com.sk89q.minecraft.util.commands.Command; +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.minecraft.util.commands.CommandPermissions; +import com.sk89q.worldedit.util.auth.Authorizer; +import com.sk89q.worldedit.util.auth.NullAuthorizer; +import com.sk89q.worldedit.util.command.CommandCallable; +import com.sk89q.worldedit.util.command.CommandCompleter; +import com.sk89q.worldedit.util.command.Dispatcher; +import com.sk89q.worldedit.util.command.NullCompleter; +import com.sk89q.worldedit.util.command.binding.PrimitiveBindings; +import com.sk89q.worldedit.util.command.binding.StandardBindings; +import com.sk89q.worldedit.util.command.binding.Switch; +import com.thoughtworks.paranamer.CachingParanamer; +import com.thoughtworks.paranamer.Paranamer; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Creates commands using annotations placed on methods and individual parameters of + * such methods. + * + * @see Command defines a command + * @see Switch defines a flag + */ +public class ParametricBuilder { + + private final Map bindings = new HashMap(); + private final Paranamer paranamer = new CachingParanamer(); + private final List invokeListeners = new ArrayList(); + private final List exceptionConverters = new ArrayList(); + private Authorizer authorizer = new NullAuthorizer(); + private CommandCompleter defaultCompleter = new NullCompleter(); + + /** + * Create a new builder. + * + *

This method will install {@link PrimitiveBindings} and + * {@link StandardBindings} and default bindings.

+ */ + public ParametricBuilder() { + addBinding(new PrimitiveBindings()); + addBinding(new StandardBindings()); + } + + /** + * Add a binding for a given type or classifier (annotation). + * + *

Whenever a method parameter is encountered, a binding must be found for it + * so that it can be called later to consume the stack of arguments provided by + * the user and return an object that is later passed to + * {@link Method#invoke(Object, Object...)}.

+ * + *

Normally, a {@link Type} is used to discern between different bindings, but + * if this is not specific enough, an annotation can be defined and used. This + * makes it a "classifier" and it will take precedence over the base type. For + * example, even if there is a binding that handles {@link String} parameters, + * a special {@code @MyArg} annotation can be assigned to a {@link String} + * parameter, which will cause the {@link Builder} to consult the {@link Binding} + * associated with {@code @MyArg} rather than with the binding for + * the {@link String} type.

+ * + * @param binding the binding + * @param type a list of types (if specified) to override the binding's types + */ + public void addBinding(Binding binding, Type... type) { + if (type == null || type.length == 0) { + type = binding.getTypes(); + } + + for (Type t : type) { + bindings.put(t, binding); + } + } + + /** + * Attach an invocation listener. + * + *

Invocation handlers are called in order that their listeners are + * registered with a {@link ParametricBuilder}. It is not guaranteed that + * a listener may be called, in the case of a {@link CommandException} being + * thrown at any time before the appropriate listener or handler is called. + * It is possible for a + * {@link com.sk89q.worldedit.util.command.parametric.InvokeHandler#preInvoke(Object, Method, com.sk89q.worldedit.util.command.parametric.ParameterData[], Object[], CommandContext)} to + * be called for a invocation handler, but not the associated + * {@link com.sk89q.worldedit.util.command.parametric.InvokeHandler#postInvoke(Object, Method, com.sk89q.worldedit.util.command.parametric.ParameterData[], Object[], CommandContext)}.

+ * + *

An example of an invocation listener is one to handle + * {@link CommandPermissions}, by first checking to see if permission is available + * in a {@link com.sk89q.worldedit.util.command.parametric.InvokeHandler#preInvoke(Object, Method, com.sk89q.worldedit.util.command.parametric.ParameterData[], Object[], CommandContext)} + * call. If permission is not found, then an appropriate {@link CommandException} + * can be thrown to cease invocation.

+ * + * @param listener the listener + * @see com.sk89q.worldedit.util.command.parametric.InvokeHandler the handler + */ + public void addInvokeListener(InvokeListener listener) { + invokeListeners.add(listener); + } + + /** + * Attach an exception converter to this builder in order to wrap unknown + * {@link Throwable}s into known {@link CommandException}s. + * + *

Exception converters are called in order that they are registered.

+ * + * @param converter the converter + * @see ExceptionConverter for an explanation + */ + public void addExceptionConverter(ExceptionConverter converter) { + exceptionConverters.add(converter); + } + + /** + * Build a list of commands from methods specially annotated with {@link Command} + * (and other relevant annotations) and register them all with the given + * {@link Dispatcher}. + * + * @param dispatcher the dispatcher to register commands with + * @param object the object contain the methods + * @throws com.sk89q.worldedit.util.command.parametric.ParametricException thrown if the commands cannot be registered + */ + public void registerMethodsAsCommands(Dispatcher dispatcher, Object object) throws ParametricException { + for (Method method : object.getClass().getDeclaredMethods()) { + Command definition = method.getAnnotation(Command.class); + if (definition != null) { + CommandCallable callable = build(object, method, definition); + dispatcher.registerCommand(callable, definition.aliases()); + } + } + } + + /** + * Build a {@link CommandCallable} for the given method. + * + * @param object the object to be invoked on + * @param method the method to invoke + * @param definition the command definition annotation + * @return the command executor + * @throws ParametricException thrown on an error + */ + private CommandCallable build(Object object, Method method, Command definition) + throws ParametricException { + try { + Class clazz = Class.forName("com.sk89q.worldedit.util.command.parametric.ParametricCallable"); + Constructor constructor = clazz.getDeclaredConstructors()[0]; + constructor.setAccessible(true); + return (CommandCallable) constructor.newInstance(this, object, method, Commands.translate(definition)); + } catch (Throwable e) { + if (e instanceof ParametricException) { + throw (ParametricException) e; + } + e.printStackTrace(); + return null; + } + } + + /** + * Get the object used to get method names on Java versions before 8 (assuming + * that Java 8 is given the ability to reliably reflect method names at runtime). + * + * @return the paranamer + */ + public Paranamer getParanamer() { + return paranamer; + } + + /** + * Get the map of bindings. + * + * @return the map of bindings + */ + public Map getBindings() { + return bindings; + } + + /** + * Get a list of invocation listeners. + * + * @return a list of invocation listeners + */ + public List getInvokeListeners() { + return invokeListeners; + } + + /** + * Get the list of exception converters. + * + * @return a list of exception converters + */ + public List getExceptionConverters() { + return exceptionConverters; + } + + /** + * Get the authorizer. + * + * @return the authorizer + */ + public Authorizer getAuthorizer() { + return authorizer; + } + + /** + * Set the authorizer. + * + * @param authorizer the authorizer + */ + public void setAuthorizer(Authorizer authorizer) { + checkNotNull(authorizer); + this.authorizer = authorizer; + } + + /** + * Get the default command suggestions provider that will be used if + * no suggestions are available. + * + * @return the default command completer + */ + public CommandCompleter getDefaultCompleter() { + return defaultCompleter; + } + + /** + * Set the default command suggestions provider that will be used if + * no suggestions are available. + * + * @param defaultCompleter the default command completer + */ + public void setDefaultCompleter(CommandCompleter defaultCompleter) { + checkNotNull(defaultCompleter); + this.defaultCompleter = defaultCompleter; + } + + public static Class inject() { + return ParametricBuilder.class; + } + +}