From ad60b909870ccc93b1ff999620a248201c60f37a Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Wed, 24 Jan 2018 19:57:08 +1100 Subject: [PATCH] Various minor Functioning nukkit gui for most commands Fixes #876 --- .../com/boydti/fawe/bukkit/FaweBukkit.java | 16 +- .../boydti/fawe/bukkit/v0/ChunkListener.java | 19 +- .../fawe/bukkit/v1_12/BukkitChunk_1_12.java | 4 +- .../fawe/bukkit/v1_12/BukkitQueue_1_12.java | 5 +- bukkit/src/main/resources/plugin.yml | 3 + .../main/java/com/boydti/fawe/config/BBC.java | 3 +- .../java/com/boydti/fawe/config/Settings.java | 2 +- .../com/boydti/fawe/object/FawePlayer.java | 2 +- .../fawe/object/extent/FaweRegionExtent.java | 26 +- .../general/plot/PlotSquaredFeature.java | 1 + .../java/com/boydti/fawe/util/WEManager.java | 2 +- .../boydti/fawe/util/chat/UsageMessage.java | 2 +- .../com/boydti/fawe/util/gui/FormBuilder.java | 6 +- .../sk89q/minecraft/util/commands/Step.java | 11 + .../util/commands/SuggestedRange.java | 25 + .../java/com/sk89q/worldedit/EditSession.java | 8 +- .../worldedit/command/BrushCommands.java | 82 +-- .../sk89q/worldedit/command/HelpBuilder.java | 244 ++++++++ .../worldedit/command/UtilityCommands.java | 592 ++++++++++-------- .../nukkit/core/gui/NukkitFormBuilder.java | 17 +- .../nukkit/core/gui/ResponseFormWindow.java | 10 +- .../fawe/nukkit/optimization/FaweNukkit.java | 44 ++ .../nukkit/optimization/FaweNukkitPlayer.java | 2 +- nukkit/src/main/resources/plugin.yml | 3 + 24 files changed, 735 insertions(+), 394 deletions(-) create mode 100644 core/src/main/java/com/sk89q/minecraft/util/commands/Step.java create mode 100644 core/src/main/java/com/sk89q/minecraft/util/commands/SuggestedRange.java create mode 100644 core/src/main/java/com/sk89q/worldedit/command/HelpBuilder.java diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java index 598286a1..ee02dbc0 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java @@ -7,16 +7,7 @@ import com.boydti.fawe.bukkit.listener.BrushListener; import com.boydti.fawe.bukkit.listener.BukkitImageListener; import com.boydti.fawe.bukkit.listener.CFIPacketListener; import com.boydti.fawe.bukkit.listener.RenderListener; -import com.boydti.fawe.bukkit.regions.ASkyBlockHook; -import com.boydti.fawe.bukkit.regions.FactionsFeature; -import com.boydti.fawe.bukkit.regions.FactionsOneFeature; -import com.boydti.fawe.bukkit.regions.FactionsUUIDFeature; -import com.boydti.fawe.bukkit.regions.GriefPreventionFeature; -import com.boydti.fawe.bukkit.regions.PlotMeFeature; -import com.boydti.fawe.bukkit.regions.PreciousStonesFeature; -import com.boydti.fawe.bukkit.regions.ResidenceFeature; -import com.boydti.fawe.bukkit.regions.TownyFeature; -import com.boydti.fawe.bukkit.regions.Worldguard; +import com.boydti.fawe.bukkit.regions.*; import com.boydti.fawe.bukkit.util.BukkitReflectionUtils; import com.boydti.fawe.bukkit.util.BukkitTaskMan; import com.boydti.fawe.bukkit.util.ItemUtil; @@ -41,10 +32,7 @@ import com.boydti.fawe.object.FaweCommand; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.regions.FaweMaskManager; -import com.boydti.fawe.util.Jars; -import com.boydti.fawe.util.MainUtil; -import com.boydti.fawe.util.ReflectionUtils; -import com.boydti.fawe.util.TaskManager; +import com.boydti.fawe.util.*; import com.boydti.fawe.util.cui.CUI; import com.boydti.fawe.util.image.ImageViewer; import com.boydti.fawe.util.metrics.BStats; diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/ChunkListener.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/ChunkListener.java index eedde166..c6aee342 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/ChunkListener.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/ChunkListener.java @@ -31,14 +31,11 @@ import org.bukkit.event.block.BlockIgniteEvent; import org.bukkit.event.block.BlockPhysicsEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockRedstoneEvent; -import org.bukkit.event.block.CauldronLevelChangeEvent; import org.bukkit.event.block.LeavesDecayEvent; import org.bukkit.event.block.NotePlayEvent; import org.bukkit.event.block.SignChangeEvent; import org.bukkit.event.entity.EntityChangeBlockEvent; import org.bukkit.event.entity.ItemSpawnEvent; -import org.bukkit.event.inventory.BrewEvent; -import org.bukkit.event.inventory.BrewingStandFuelEvent; import org.bukkit.event.inventory.FurnaceBurnEvent; import org.bukkit.event.inventory.FurnaceSmeltEvent; import org.bukkit.event.world.ChunkLoadEvent; @@ -156,14 +153,14 @@ public abstract class ChunkListener implements Listener { @EventHandler(priority = EventPriority.LOWEST) public void event(BlockPlaceEvent event) { reset(); } - @EventHandler(priority = EventPriority.LOWEST) - public void event(BrewEvent event) { reset(); } - - @EventHandler(priority = EventPriority.LOWEST) - public void event(BrewingStandFuelEvent event) { reset(); } - - @EventHandler(priority = EventPriority.LOWEST) - public void event(CauldronLevelChangeEvent event) { reset(); } +// @EventHandler(priority = EventPriority.LOWEST) +// public void event(BrewEvent event) { reset(); } +// +// @EventHandler(priority = EventPriority.LOWEST) +// public void event(BrewingStandFuelEvent event) { reset(); } +// +// @EventHandler(priority = EventPriority.LOWEST) +// public void event(CauldronLevelChangeEvent event) { reset(); } @EventHandler(priority = EventPriority.LOWEST) public void event(FurnaceBurnEvent event) { reset(); } diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/BukkitChunk_1_12.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/BukkitChunk_1_12.java index 335f5acb..2e8e2f69 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/BukkitChunk_1_12.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/BukkitChunk_1_12.java @@ -302,7 +302,7 @@ public class BukkitChunk_1_12 extends CharFaweChunk { } // Set entities Set entitiesToSpawn = this.getEntities(); - Set createdEntities = new HashSet<>(); +// Set createdEntities = new HashSet<>(); if (!entitiesToSpawn.isEmpty()) { synchronized (BukkitQueue_0.class) { for (CompoundTag nativeTag : entitiesToSpawn) { @@ -347,7 +347,7 @@ public class BukkitChunk_1_12 extends CharFaweChunk { synchronized (BukkitQueue_0.class) { nmsWorld.addEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM); } - createdEntities.add(entity.getUniqueID()); +// createdEntities.add(entity.getUniqueID()); } } } diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/BukkitQueue_1_12.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/BukkitQueue_1_12.java index 10951d8d..c35cdba4 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/BukkitQueue_1_12.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/BukkitQueue_1_12.java @@ -734,7 +734,10 @@ public class BukkitQueue_1_12 extends BukkitQueue_0 extends Metadatable { if (allowed.length == 0) { throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_NO_REGION); } else if (!WEManager.IMP.regionContains(wrappedSelection, allowedSet)) { - throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS); + throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_OUTSIDE_REGION); } } diff --git a/core/src/main/java/com/boydti/fawe/object/extent/FaweRegionExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/FaweRegionExtent.java index 7d28d07e..192494d6 100644 --- a/core/src/main/java/com/boydti/fawe/object/extent/FaweRegionExtent.java +++ b/core/src/main/java/com/boydti/fawe/object/extent/FaweRegionExtent.java @@ -57,7 +57,7 @@ public abstract class FaweRegionExtent extends ResettableExtent { public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException { if (!contains(location)) { if (!limit.MAX_FAILS()) { - WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS); + WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_OUTSIDE_REGION); } return false; } @@ -68,7 +68,7 @@ public abstract class FaweRegionExtent extends ResettableExtent { public boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException { if (!contains(x, y, z)) { if (!limit.MAX_FAILS()) { - WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS); + WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_OUTSIDE_REGION); } return false; } @@ -79,7 +79,7 @@ public abstract class FaweRegionExtent extends ResettableExtent { public boolean setBiome(Vector2D position, BaseBiome biome) { if (!contains(position)) { if (!limit.MAX_FAILS()) { - WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS); + WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_OUTSIDE_REGION); } return false; } @@ -90,7 +90,7 @@ public abstract class FaweRegionExtent extends ResettableExtent { public BaseBiome getBiome(Vector2D position) { if (!contains(position)) { if (!limit.MAX_FAILS()) { - WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS); + WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_OUTSIDE_REGION); } return EditSession.nullBiome; } @@ -101,7 +101,7 @@ public abstract class FaweRegionExtent extends ResettableExtent { public BaseBlock getBlock(Vector position) { if (!contains(position)) { if (!limit.MAX_FAILS()) { - WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS); + WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_OUTSIDE_REGION); } return EditSession.nullBlock; } @@ -112,7 +112,7 @@ public abstract class FaweRegionExtent extends ResettableExtent { public BaseBlock getLazyBlock(Vector position) { if (!contains(position)) { if (!limit.MAX_FAILS()) { - WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS); + WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_OUTSIDE_REGION); } return EditSession.nullBlock; } @@ -123,7 +123,7 @@ public abstract class FaweRegionExtent extends ResettableExtent { public BaseBlock getLazyBlock(int x, int y, int z) { if (!contains(x, y, z)) { if (!limit.MAX_FAILS()) { - WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS); + WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_OUTSIDE_REGION); } return EditSession.nullBlock; } @@ -134,7 +134,7 @@ public abstract class FaweRegionExtent extends ResettableExtent { public int getBlockLight(int x, int y, int z) { if (!contains(x, y, z)) { if (!limit.MAX_FAILS()) { - WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS); + WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_OUTSIDE_REGION); } return 0; } @@ -145,7 +145,7 @@ public abstract class FaweRegionExtent extends ResettableExtent { public int getBrightness(int x, int y, int z) { if (!contains(x, y, z)) { if (!limit.MAX_FAILS()) { - WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS); + WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_OUTSIDE_REGION); } return 0; } @@ -156,7 +156,7 @@ public abstract class FaweRegionExtent extends ResettableExtent { public int getLight(int x, int y, int z) { if (!contains(x, y, z)) { if (!limit.MAX_FAILS()) { - WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS); + WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_OUTSIDE_REGION); } return 0; } @@ -167,7 +167,7 @@ public abstract class FaweRegionExtent extends ResettableExtent { public int getOpacity(int x, int y, int z) { if (!contains(x, y, z)) { if (!limit.MAX_FAILS()) { - WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS); + WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_OUTSIDE_REGION); } return 0; } @@ -178,7 +178,7 @@ public abstract class FaweRegionExtent extends ResettableExtent { public int getSkyLight(int x, int y, int z) { if (!contains(x, y, z)) { if (!limit.MAX_FAILS()) { - WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS); + WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_OUTSIDE_REGION); } return 0; } @@ -190,7 +190,7 @@ public abstract class FaweRegionExtent extends ResettableExtent { public Entity createEntity(Location location, BaseEntity entity) { if (!contains(location.getBlockX(), location.getBlockY(), location.getBlockZ())) { if (!limit.MAX_FAILS()) { - WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS); + WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_OUTSIDE_REGION); } return null; } diff --git a/core/src/main/java/com/boydti/fawe/regions/general/plot/PlotSquaredFeature.java b/core/src/main/java/com/boydti/fawe/regions/general/plot/PlotSquaredFeature.java index 1e45751d..f3f04ebf 100644 --- a/core/src/main/java/com/boydti/fawe/regions/general/plot/PlotSquaredFeature.java +++ b/core/src/main/java/com/boydti/fawe/regions/general/plot/PlotSquaredFeature.java @@ -121,6 +121,7 @@ public class PlotSquaredFeature extends FaweMaskManager { if (Settings.Done.RESTRICT_BUILDING && Flags.DONE.isSet(finalPlot)) { return null; } + return new FaweMask(pos1, pos2) { @Override public String getName() { diff --git a/core/src/main/java/com/boydti/fawe/util/WEManager.java b/core/src/main/java/com/boydti/fawe/util/WEManager.java index d4aa8b07..8ac722d6 100644 --- a/core/src/main/java/com/boydti/fawe/util/WEManager.java +++ b/core/src/main/java/com/boydti/fawe/util/WEManager.java @@ -88,7 +88,7 @@ public class WEManager { * @return */ public RegionWrapper[] getMask(final FawePlayer player, FaweMaskManager.MaskType type) { - if (player.hasPermission("fawe.bypass") || !Settings.IMP.REGION_RESTRICTIONS) { + if (!Settings.IMP.REGION_RESTRICTIONS || player.hasPermission("fawe.bypass") || player.hasPermission("fawe.bypass.regions")) { return new RegionWrapper[]{RegionWrapper.GLOBAL()}; } FaweLocation loc = player.getLocation(); diff --git a/core/src/main/java/com/boydti/fawe/util/chat/UsageMessage.java b/core/src/main/java/com/boydti/fawe/util/chat/UsageMessage.java index 43653efc..ea33a753 100644 --- a/core/src/main/java/com/boydti/fawe/util/chat/UsageMessage.java +++ b/core/src/main/java/com/boydti/fawe/util/chat/UsageMessage.java @@ -69,7 +69,7 @@ public class UsageMessage extends Message { } } - public String separateArg(String arg) { + protected String separateArg(String arg) { return " " + arg; } diff --git a/core/src/main/java/com/boydti/fawe/util/gui/FormBuilder.java b/core/src/main/java/com/boydti/fawe/util/gui/FormBuilder.java index 7710fb36..8897d558 100644 --- a/core/src/main/java/com/boydti/fawe/util/gui/FormBuilder.java +++ b/core/src/main/java/com/boydti/fawe/util/gui/FormBuilder.java @@ -2,7 +2,7 @@ package com.boydti.fawe.util.gui; import com.boydti.fawe.object.FawePlayer; import java.net.URL; -import java.util.List; +import java.util.Map; import java.util.function.Consumer; import javax.annotation.Nullable; @@ -25,5 +25,7 @@ public interface FormBuilder { FormBuilder addToggle(String text, boolean def); - void display(FawePlayer fp, Consumer> response); + FormBuilder setResponder(Consumer> handler); + + void display(FawePlayer fp); } diff --git a/core/src/main/java/com/sk89q/minecraft/util/commands/Step.java b/core/src/main/java/com/sk89q/minecraft/util/commands/Step.java new file mode 100644 index 00000000..9a5243a1 --- /dev/null +++ b/core/src/main/java/com/sk89q/minecraft/util/commands/Step.java @@ -0,0 +1,11 @@ +package com.sk89q.minecraft.util.commands; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Step { + Class clazz() default Link.class; + + double value() default 1; +} diff --git a/core/src/main/java/com/sk89q/minecraft/util/commands/SuggestedRange.java b/core/src/main/java/com/sk89q/minecraft/util/commands/SuggestedRange.java new file mode 100644 index 00000000..3eeb191e --- /dev/null +++ b/core/src/main/java/com/sk89q/minecraft/util/commands/SuggestedRange.java @@ -0,0 +1,25 @@ +package com.sk89q.minecraft.util.commands; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface SuggestedRange { + Class clazz() default Link.class; + String value(); + + + /** + * The minimum value that the number can be at, inclusive. + * + * @return the minimum value + */ + double min() default Double.MIN_VALUE; + + /** + * The maximum value that the number can be at, inclusive. + * + * @return the maximum value + */ + double max() default Double.MAX_VALUE; +} diff --git a/core/src/main/java/com/sk89q/worldedit/EditSession.java b/core/src/main/java/com/sk89q/worldedit/EditSession.java index 718b64f6..f9d035d9 100644 --- a/core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -344,7 +344,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, } } if (allowedRegions == null) { - if (player != null && !player.hasWorldEditBypass() && !(queue instanceof HeightMapMCAGenerator)) { + if (player != null && !player.hasPermission("fawe.bypass") && !player.hasPermission("fawe.bypass.regions") && !(queue instanceof HeightMapMCAGenerator)) { allowedRegions = player.getCurrentRegions(); } } @@ -1376,8 +1376,10 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, if (used.MAX_FAILS > 0) { if (used.MAX_CHANGES > 0 || used.MAX_ENTITIES > 0) { BBC.WORLDEDIT_SOME_FAILS.send(player, used.MAX_FAILS); + } else if (new ExtentTraverser(this).findAndGet(FaweRegionExtent.class) != null){ + BBC.WORLDEDIT_CANCEL_REASON_OUTSIDE_REGION.send(player); } else { - BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS.send(player); + BBC.WORLDEDIT_CANCEL_REASON_OUTSIDE_WORLD.send(player); } } // Reset limit @@ -3452,7 +3454,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, Vector max = region.getMaximumPoint(); Vector min = region.getMinimumPoint(); if (!fe.contains(max.getBlockX(), max.getBlockY(), max.getBlockZ()) && !fe.contains(min.getBlockX(), min.getBlockY(), min.getBlockZ())) { - throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS); + throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_OUTSIDE_REGION); } } final Set chunks = region.getChunks(); 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 a15c55d7..4f801fba 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -25,59 +25,19 @@ import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FaweLimit; import com.boydti.fawe.object.FawePlayer; -import com.boydti.fawe.object.brush.BlendBall; -import com.boydti.fawe.object.brush.BlobBrush; -import com.boydti.fawe.object.brush.BrushSettings; -import com.boydti.fawe.object.brush.CatenaryBrush; -import com.boydti.fawe.object.brush.CircleBrush; -import com.boydti.fawe.object.brush.CommandBrush; -import com.boydti.fawe.object.brush.CopyPastaBrush; -import com.boydti.fawe.object.brush.ErodeBrush; -import com.boydti.fawe.object.brush.FlattenBrush; -import com.boydti.fawe.object.brush.HeightBrush; -import com.boydti.fawe.object.brush.LayerBrush; -import com.boydti.fawe.object.brush.LineBrush; -import com.boydti.fawe.object.brush.PopulateSchem; -import com.boydti.fawe.object.brush.RaiseBrush; -import com.boydti.fawe.object.brush.RecurseBrush; -import com.boydti.fawe.object.brush.ScatterBrush; -import com.boydti.fawe.object.brush.ScatterCommand; -import com.boydti.fawe.object.brush.ScatterOverlayBrush; -import com.boydti.fawe.object.brush.ShatterBrush; -import com.boydti.fawe.object.brush.SplatterBrush; -import com.boydti.fawe.object.brush.SplineBrush; -import com.boydti.fawe.object.brush.StencilBrush; -import com.boydti.fawe.object.brush.SurfaceSphereBrush; -import com.boydti.fawe.object.brush.SurfaceSpline; +import com.boydti.fawe.object.brush.*; import com.boydti.fawe.object.brush.heightmap.ScalableHeightMap; import com.boydti.fawe.object.brush.sweep.SweepBrush; import com.boydti.fawe.object.clipboard.MultiClipboardHolder; import com.boydti.fawe.object.mask.IdMask; import com.boydti.fawe.util.ColorUtil; import com.boydti.fawe.util.MathMan; -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandLocals; -import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.EmptyClipboardException; -import com.sk89q.worldedit.LocalConfiguration; -import com.sk89q.worldedit.LocalSession; -import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.WorldEditException; +import com.sk89q.minecraft.util.commands.*; +import com.sk89q.worldedit.*; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldedit.command.tool.InvalidToolBindException; -import com.sk89q.worldedit.command.tool.brush.Brush; -import com.sk89q.worldedit.command.tool.brush.ButcherBrush; -import com.sk89q.worldedit.command.tool.brush.ClipboardBrush; -import com.sk89q.worldedit.command.tool.brush.CylinderBrush; -import com.sk89q.worldedit.command.tool.brush.GravityBrush; -import com.sk89q.worldedit.command.tool.brush.HollowCylinderBrush; -import com.sk89q.worldedit.command.tool.brush.HollowSphereBrush; -import com.sk89q.worldedit.command.tool.brush.SmoothBrush; -import com.sk89q.worldedit.command.tool.brush.SphereBrush; +import com.sk89q.worldedit.command.tool.brush.*; import com.sk89q.worldedit.command.util.CreatureButcher; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.input.ParserContext; @@ -93,11 +53,7 @@ import com.sk89q.worldedit.util.command.binding.Range; import com.sk89q.worldedit.util.command.binding.Switch; import com.sk89q.worldedit.util.command.parametric.Optional; import java.awt.Color; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; import java.net.URL; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; @@ -337,7 +293,7 @@ public class BrushCommands extends MethodCommands { max = 2 ) @CommandPermissions("worldedit.brush.sphere") - public BrushSettings sphereBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("2") double radius, @Switch('h') boolean hollow, CommandContext context) throws WorldEditException { + public BrushSettings sphereBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("2") @Range(min=0) double radius, @Switch('h') boolean hollow, CommandContext context) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); Brush brush; @@ -393,12 +349,12 @@ public class BrushCommands extends MethodCommands { max = -1 ) @CommandPermissions("worldedit.brush.stencil") - public BrushSettings stencilBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("5") double radius, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale, @Switch('w') boolean onlyWhite, @Switch('r') boolean randomRotate, CommandContext context) throws WorldEditException { + public BrushSettings stencilBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("5") double radius, @Optional("") final String image, @Optional("0") @Step(90) @Range(min=0, max=360) final int rotation, @Optional("1") final double yscale, @Switch('w') boolean onlyWhite, @Switch('r') boolean randomRotate, CommandContext context) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); - InputStream stream = getHeightmapStream(filename); + InputStream stream = getHeightmapStream(image); HeightBrush brush; try { - brush = new StencilBrush(stream, rotation, yscale, onlyWhite, filename.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null); + brush = new StencilBrush(stream, rotation, yscale, onlyWhite, image.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null); } catch (EmptyClipboardException ignore) { brush = new StencilBrush(stream, rotation, yscale, onlyWhite, null); } @@ -705,8 +661,8 @@ public class BrushCommands extends MethodCommands { max = 4 ) @CommandPermissions("worldedit.brush.height") - public BrushSettings heightBrush(Player player, LocalSession session, @Optional("5") double radius, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale, @Switch('r') boolean randomRotate, @Switch('l') boolean layers, @Switch('s') boolean dontSmooth, CommandContext context) throws WorldEditException { - return terrainBrush(player, session, radius, filename, rotation, yscale, false, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CONE, context); + public BrushSettings heightBrush(Player player, LocalSession session, @Optional("5") double radius, @Optional("") final String image, @Optional("0") @Step(90) @Range(min=0, max=360) final int rotation, @Optional("1") final double yscale, @Switch('r') boolean randomRotate, @Switch('l') boolean layers, @Switch('s') boolean dontSmooth, CommandContext context) throws WorldEditException { + return terrainBrush(player, session, radius, image, rotation, yscale, false, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CONE, context); } @Command( @@ -723,8 +679,8 @@ public class BrushCommands extends MethodCommands { max = 4 ) @CommandPermissions("worldedit.brush.height") - public BrushSettings cliffBrush(Player player, LocalSession session, @Optional("5") double radius, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale, @Switch('r') boolean randomRotate, @Switch('l') boolean layers, @Switch('s') boolean dontSmooth, CommandContext context) throws WorldEditException { - return terrainBrush(player, session, radius, filename, rotation, yscale, true, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CYLINDER, context); + public BrushSettings cliffBrush(Player player, LocalSession session, @Optional("5") double radius, @Optional("") final String image, @Optional("0") @Step(90) @Range(min=0, max=360) final int rotation, @Optional("1") final double yscale, @Switch('r') boolean randomRotate, @Switch('l') boolean layers, @Switch('s') boolean dontSmooth, CommandContext context) throws WorldEditException { + return terrainBrush(player, session, radius, image, rotation, yscale, true, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CYLINDER, context); } @Command( @@ -740,8 +696,8 @@ public class BrushCommands extends MethodCommands { max = 4 ) @CommandPermissions("worldedit.brush.height") - public BrushSettings flattenBrush(Player player, LocalSession session, @Optional("5") double radius, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale, @Switch('r') boolean randomRotate, @Switch('l') boolean layers, @Switch('s') boolean dontSmooth, CommandContext context) throws WorldEditException { - return terrainBrush(player, session, radius, filename, rotation, yscale, true, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CONE, context); + public BrushSettings flattenBrush(Player player, LocalSession session, @Optional("5") double radius, @Optional("") final String image, @Optional("0") @Step(90) @Range(min=0, max=360) final int rotation, @Optional("1") final double yscale, @Switch('r') boolean randomRotate, @Switch('l') boolean layers, @Switch('s') boolean dontSmooth, CommandContext context) throws WorldEditException { + return terrainBrush(player, session, radius, image, rotation, yscale, true, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CONE, context); } private InputStream getHeightmapStream(String filename) { @@ -775,19 +731,19 @@ public class BrushCommands extends MethodCommands { return null; } - private BrushSettings terrainBrush(Player player, LocalSession session, double radius, String filename, int rotation, double yscale, boolean flat, boolean randomRotate, boolean layers, boolean smooth, ScalableHeightMap.Shape shape, CommandContext context) throws WorldEditException { + private BrushSettings terrainBrush(Player player, LocalSession session, double radius, String image, int rotation, double yscale, boolean flat, boolean randomRotate, boolean layers, boolean smooth, ScalableHeightMap.Shape shape, CommandContext context) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); - InputStream stream = getHeightmapStream(filename); + InputStream stream = getHeightmapStream(image); HeightBrush brush; if (flat) { try { - brush = new FlattenBrush(stream, rotation, yscale, layers, smooth, filename.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null, shape); + brush = new FlattenBrush(stream, rotation, yscale, layers, smooth, image.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null, shape); } catch (EmptyClipboardException ignore) { brush = new FlattenBrush(stream, rotation, yscale, layers, smooth, null, shape); } } else { try { - brush = new HeightBrush(stream, rotation, yscale, layers, smooth, filename.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null); + brush = new HeightBrush(stream, rotation, yscale, layers, smooth, image.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null); } catch (EmptyClipboardException ignore) { brush = new HeightBrush(stream, rotation, yscale, layers, smooth, null); } diff --git a/core/src/main/java/com/sk89q/worldedit/command/HelpBuilder.java b/core/src/main/java/com/sk89q/worldedit/command/HelpBuilder.java new file mode 100644 index 00000000..a0ad5430 --- /dev/null +++ b/core/src/main/java/com/sk89q/worldedit/command/HelpBuilder.java @@ -0,0 +1,244 @@ +package com.sk89q.worldedit.command; + +import com.boydti.fawe.config.BBC; +import com.boydti.fawe.util.StringMan; +import com.google.common.base.Joiner; +import com.sk89q.minecraft.util.commands.Command; +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.platform.CommandManager; +import com.sk89q.worldedit.util.command.CommandCallable; +import com.sk89q.worldedit.util.command.CommandMapping; +import com.sk89q.worldedit.util.command.DelegateCallable; +import com.sk89q.worldedit.util.command.Dispatcher; +import com.sk89q.worldedit.util.command.PrimaryAliasComparator; +import com.sk89q.worldedit.util.command.parametric.ParametricCallable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +public abstract class HelpBuilder implements Runnable { + private final CommandCallable callable; + private final CommandContext args; + private final String prefix; + private final int perPage; + + public HelpBuilder(CommandCallable callable, CommandContext args, final String prefix, int perPage) { + if (callable == null) { + callable = WorldEdit.getInstance().getPlatformManager().getCommandManager().getDispatcher(); + } + this.callable = callable; + this.args = args; + this.prefix = prefix; + this.perPage = perPage; + } + + @Override + public void run() { + try { + CommandCallable callable = this.callable; + int page = -1; + String category = null; + int effectiveLength = args.argsLength(); + + // Detect page from args + try { + if (effectiveLength > 0) { + page = args.getInteger(args.argsLength() - 1); + if (page <= 0) { + page = 1; + } else { + page--; + } + effectiveLength--; + } + } catch (NumberFormatException ignored) { + } + + boolean isRootLevel = true; + List visited = new ArrayList(); + + // Create the message + if (callable instanceof Dispatcher) { + Dispatcher dispatcher = (Dispatcher) callable; + + // Get a list of aliases + List aliases = new ArrayList(dispatcher.getCommands()); + List prefixes = Collections.nCopies(aliases.size(), ""); + // Group by callable + + if (page == -1 || effectiveLength > 0) { + Map> grouped = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + for (CommandMapping mapping : aliases) { + CommandCallable c = mapping.getCallable(); + String group; + if (c instanceof DelegateCallable) { + c = ((DelegateCallable) c).getParent(); + } + if (c instanceof ParametricCallable) { + Object obj = ((ParametricCallable) c).getObject(); + Command command = obj.getClass().getAnnotation(Command.class); + if (command != null && command.aliases().length != 0) { + group = command.aliases()[0]; + } else { + group = obj.getClass().getSimpleName().replaceAll("Commands", "").replaceAll("Util$", ""); + } + } else if (c instanceof Dispatcher) { + group = mapping.getPrimaryAlias(); + } else { + group = "Unsorted"; + } + group = group.replace("/", ""); + group = StringMan.toProperCase(group); + Map queue = grouped.get(group); + if (queue == null) { + queue = new LinkedHashMap<>(); + grouped.put(group, queue); + } + if (c instanceof Dispatcher) { + for (CommandMapping m : ((Dispatcher) c).getCommands()) { + queue.put(m, mapping.getPrimaryAlias() + " "); + } + } else { + // Sub commands get priority + queue.putIfAbsent(mapping, ""); + } + } + if (effectiveLength > 0) { + String cat = args.getString(0); + Map mappings = effectiveLength == 1 ? grouped.get(cat) : null; + if (mappings == null) { + // Drill down to the command + for (int i = 0; i < effectiveLength; i++) { + String command = args.getString(i); + + if (callable instanceof Dispatcher) { + // Chop off the beginning / if we're are the root level + if (isRootLevel && command.length() > 1 && command.charAt(0) == '/') { + command = command.substring(1); + } + + CommandMapping mapping = UtilityCommands.detectCommand((Dispatcher) callable, command, isRootLevel); + if (mapping != null) { + callable = mapping.getCallable(); + } else { + if (isRootLevel) { + Set found = new HashSet<>(); + String arg = args.getString(i).toLowerCase(); + String closest = null; + int distance = Integer.MAX_VALUE; + for (CommandMapping map : aliases) { + String desc = map.getDescription().getDescription(); + if (desc == null) desc = map.getDescription().getHelp(); + if (desc == null) desc = ""; + String[] descSplit = desc.replaceAll("[^A-Za-z0-9]", "").toLowerCase().split(" "); + for (String alias : map.getAllAliases()) { + if (alias.equals(arg)) { + closest = map.getPrimaryAlias(); + distance = 0; + found.add(map.getPrimaryAlias()); + } else if (alias.contains(arg)) { + closest = map.getPrimaryAlias(); + distance = 1; + found.add(map.getPrimaryAlias()); + } else if (StringMan.isEqualIgnoreCaseToAny(arg, descSplit)) { + closest = map.getPrimaryAlias(); + distance = 1; + found.add(map.getPrimaryAlias()); + } else { + int currentDist = StringMan.getLevenshteinDistance(alias, arg); + if (currentDist < distance) { + distance = currentDist; + closest = map.getPrimaryAlias(); + } + } + } + } + found.add(closest); + displayFailure(BBC.HELP_SUGGEST.f(arg, StringMan.join(found, ", "))); + return; + } else { + String msg = String.format("The sub-command '%s' under '%s' could not be found.", + command, Joiner.on(" ").join(visited)); + displayFailure(msg); + return; + } + } + visited.add(args.getString(i)); + isRootLevel = false; + } else { + String msg = String.format("'%s' has no sub-commands. (Maybe '%s' is for a parameter?)", + Joiner.on(" ").join(visited), command); + displayFailure(msg); + return; + } + } + if (!(callable instanceof Dispatcher)) { + // TODO interactive box + String cmd = (WorldEdit.getInstance().getConfiguration().noDoubleSlash ? "" : "/") + Joiner.on(" ").join(visited); + displayUsage(callable, cmd); + return; + } + dispatcher = (Dispatcher) callable; + aliases = new ArrayList(dispatcher.getCommands()); + prefixes = Collections.nCopies(aliases.size(), ""); + } else { + aliases = new ArrayList<>(); + prefixes = new ArrayList<>(); + for (Map.Entry entry : mappings.entrySet()) { + aliases.add(entry.getKey()); + prefixes.add(entry.getValue()); + } + } + page = Math.max(0, page); + } else if (grouped.size() > 1) { + displayCategories(grouped); + return; + } + } +// else + { + Collections.sort(aliases, new PrimaryAliasComparator(CommandManager.COMMAND_CLEAN_PATTERN)); + + // Calculate pagination + int offset = perPage * Math.max(0, page); + int pageTotal = (int) Math.ceil(aliases.size() / (double) perPage); + + // Box + if (offset >= aliases.size()) { + displayFailure(String.format("There is no page %d (total number of pages is %d).", page + 1, pageTotal)); + } else { + int end = Math.min(offset + perPage, aliases.size()); + List subAliases = aliases.subList(offset, end); + List subPrefixes = prefixes.subList(offset, end); + Map commandMap = new LinkedHashMap<>(); + for (int i = 0; i < subAliases.size(); i++) { + commandMap.put(subAliases.get(i), subPrefixes.get(i)); + } + String visitedString = Joiner.on(" ").join(visited); + displayCommands(commandMap, visitedString, page, pageTotal, effectiveLength); + } + } + } else { + String cmd = (WorldEdit.getInstance().getConfiguration().noDoubleSlash ? "" : "/") + Joiner.on(" ").join(visited); + displayUsage(callable, cmd); + } + } catch (Throwable e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + public abstract void displayFailure(String message); + + public abstract void displayUsage(CommandCallable callable, String command); + + public abstract void displayCategories(Map> categories); + + public abstract void displayCommands(Map commandMap, String visited, int page, int pageTotal, int effectiveLength); +} diff --git a/core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java b/core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java index 486c7754..59821d37 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java @@ -27,29 +27,21 @@ import com.boydti.fawe.config.Commands; import com.boydti.fawe.object.FaweLimit; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.RunnableVal3; +import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.StringMan; import com.boydti.fawe.util.chat.Message; import com.boydti.fawe.util.chat.UsageMessage; import com.boydti.fawe.util.gui.FormBuilder; -import com.google.common.base.Joiner; -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.CommandLocals; -import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.minecraft.util.commands.Logging; -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.LocalConfiguration; -import com.sk89q.worldedit.LocalSession; +import com.sk89q.minecraft.util.commands.*; +import com.sk89q.worldedit.*; import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.command.util.CreatureButcher; import com.sk89q.worldedit.command.util.EntityRemover; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.event.platform.CommandEvent; import com.sk89q.worldedit.extension.factory.DefaultMaskParser; import com.sk89q.worldedit.extension.factory.DefaultTransformParser; import com.sk89q.worldedit.extension.factory.HashTagPatternParser; @@ -70,37 +62,18 @@ import com.sk89q.worldedit.internal.expression.runtime.EvaluationException; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.CylinderRegion; import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.util.command.CommandCallable; -import com.sk89q.worldedit.util.command.CommandMapping; -import com.sk89q.worldedit.util.command.DelegateCallable; -import com.sk89q.worldedit.util.command.Dispatcher; -import com.sk89q.worldedit.util.command.PrimaryAliasComparator; +import com.sk89q.worldedit.util.command.*; import com.sk89q.worldedit.util.command.binding.Range; import com.sk89q.worldedit.util.command.binding.Text; import com.sk89q.worldedit.util.command.parametric.Optional; -import com.sk89q.worldedit.util.command.parametric.ParametricCallable; +import com.sk89q.worldedit.util.command.parametric.ParameterData; import com.sk89q.worldedit.world.World; import java.io.File; import java.io.FileFilter; +import java.lang.reflect.Type; import java.net.URI; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.UUID; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; +import java.util.*; +import java.util.concurrent.*; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -617,7 +590,7 @@ public class UtilityCommands extends MethodCommands { help(args, worldEdit, actor); } - private static CommandMapping detectCommand(Dispatcher dispatcher, String command, boolean isRootLevel) { + protected static CommandMapping detectCommand(Dispatcher dispatcher, String command, boolean isRootLevel) { CommandMapping mapping; // First try the command as entered @@ -896,262 +869,341 @@ public class UtilityCommands extends MethodCommands { desc = "Open the GUI" ) @Logging(PLACEMENT) - public void gui(FawePlayer fp, LocalSession session, CommandContext args) throws WorldEditException { + public void gui(Actor actor, FawePlayer fp, LocalSession session, CommandContext args) throws WorldEditException { FormBuilder gui = Fawe.imp().getFormBuilder(); if (gui == null) throw new UnsupportedOperationException("Not implemented"); Dispatcher callable = worldEdit.getPlatformManager().getCommandManager().getDispatcher(); CommandLocals locals = args.getLocals(); - // TODO build form + String prefix = Commands.getAlias(UtilityCommands.class, "/gui"); + // TODO sort commands by most used - gui.display(fp, new Consumer>() { + new HelpBuilder(callable, args, prefix, Integer.MAX_VALUE) { @Override - public void accept(List strings) { - + public void displayFailure(String message) { + gui.setTitle("Error"); + gui.addLabel(message); } - }); - } - private void help(CommandCallable callable) { + @Override + public void displayUsage(CommandCallable callable, String commandString) { + gui.setTitle(commandString); + if (callable instanceof Dispatcher) { + Dispatcher dispathcer = (Dispatcher) callable; + dispathcer.getCommands(); + gui.addLabel("Dispatcher not implemented for " + commandString); + } else { + Description cmdDesc = callable.getDescription(); + + + + List params = cmdDesc.getParameters(); + String[] suggested = new String[params.size()]; + if (cmdDesc.getUsage() != null) { + String[] usageArgs = cmdDesc.getUsage().split(" ", params.size()); + for (int i = 0; i < usageArgs.length; i++) { + String arg = usageArgs[i]; + String[] splitSug = arg.split("="); + if (splitSug.length == 2) { + suggested[i] = splitSug[1]; + } + } + } + for (int i = 0 ; i < params.size(); i++) { + String[] def = params.get(i).getDefaultValue(); + if (def != null && def.length != 0) { + suggested[i] = def[0]; + } + } + + String help = cmdDesc.getHelp(); + if (help == null || help.isEmpty()) help = cmdDesc.getDescription(); + + gui.addLabel(BBC.color("&2" + help + "\n")); + + List flags = new ArrayList<>(); + + for (int i = 0; i < params.size(); i++) { + Parameter param = params.get(i); + String name = param.getName(); + boolean optional = param.isValueFlag() || param.isOptional(); + String[] def = param.getDefaultValue(); + + if (param.getFlag() != null) { + flags.add("-" + param.getFlag() + " "); + } else { + flags.add(""); + } + + if (param instanceof ParameterData) { + ParameterData pd = (ParameterData) param; + Type type = pd.getType(); + String suggestion = suggested[i]; + + String color = optional ? "3" : "c"; + StringBuilder label = new StringBuilder(BBC.color("&" + color + name + ": ")); +// if (suggested[i] != null) label.append(" e.g. " + suggestion); + Range range = MainUtil.getOf(pd.getModifiers(), Range.class); + double min = 0; + double max = 100; + if (range != null) { + min = range.min(); + max = range.max(); + } else { + SuggestedRange suggestedRange = MainUtil.getOf(pd.getModifiers(), SuggestedRange.class); + if (suggestedRange != null) { + min = suggestedRange.min(); + max = suggestedRange.max(); + } else if (name.equalsIgnoreCase("radius") || name.equalsIgnoreCase("size")) { + max = WorldEdit.getInstance().getConfiguration().maxBrushRadius; + } + } + int step = 1; + Step stepSizeAnn = MainUtil.getOf(pd.getModifiers(), Step.class); + if (stepSizeAnn != null) { + double stepVal = stepSizeAnn.value(); + step = Math.max(1, (int) stepVal); + } + /* + BaseBiome + Vector + Vector2D + */ + + switch (type.getTypeName()) { + case "double": + case "java.lang.Double": { + double value = suggestion != null ? Double.parseDouble(suggestion) : min; + gui.addSlider("\n" + label.toString(), min, max, 1, value); + break; + } + case "int": + case "java.lang.Integer": { + int value = suggestion != null ? Integer.parseInt(suggestion) : (int) min; + gui.addSlider("\n" + label.toString(), min, max, 1, value); + break; + } + case "boolean": + case "java.lang.Boolean": { + boolean value = suggestion != null ? Boolean.parseBoolean(suggestion) : false; + gui.addToggle(label.toString(), value); + break; + } + case "com.sk89q.worldedit.patterns.Pattern": { + gui.addInput("\n" + label.toString(), "stone", "wood"); + break; + } + case "com.sk89q.worldedit.blocks.BaseBlock": { + gui.addInput("\n" + label.toString(), "stone", "wood"); + break; + } + case "com.sk89q.worldedit.function.mask.Mask": { + gui.addInput("\n" + label.toString(), "stone", "wood"); + break; + } + default: + case "java.lang.String": { + // TODO + // clipboard + // schematic + // image + if (suggestion == null) suggestion = ""; + gui.addInput("\n" + label.toString(), suggestion, suggestion); + break; + } + } + } else { + throw new UnsupportedOperationException("Unsupported callable: " + callable.getClass() + " | " + param.getClass()); + } + } + + gui.setResponder(new Consumer>() { + @Override + public void accept(Map response) { + int index = 0; + StringBuilder command = new StringBuilder(commandString); + for (Map.Entry arg : response.entrySet()) { + String argValue = arg.getValue().toString(); + String flag = flags.get(index); + if (!flag.isEmpty()) { + if (argValue.equalsIgnoreCase("false")) continue; + if (argValue.equalsIgnoreCase("true")) argValue = ""; + } + command.append(" " + flag + argValue); + index++; + } + CommandEvent event = new CommandEvent(actor, command.toString()); + CommandManager.getInstance().handleCommand(event); + } + }); + } + } + + @Override + public void displayCategories(Map> categories) { + gui.setTitle(BBC.HELP_HEADER_CATEGORIES.s()); + List categoryList = new ArrayList<>(); + for (Map.Entry> categoryEntry : categories.entrySet()) { + String category = categoryEntry.getKey(); + categoryList.add(category); + Map commandMap = categoryEntry.getValue(); + int size = commandMap.size(); + + String plural = size == 1 ? "command" : "commands"; + gui.addButton(BBC.HELP_ITEM_ALLOWED.f(category, "(" + size + " " + plural + ")"), null); + } + + gui.setResponder(new Consumer>() { + @Override + public void accept(Map response) { + if (response.isEmpty()) { + // ?? + throw new IllegalArgumentException("No response for categories"); + } else { + Map.Entry clicked = response.entrySet().iterator().next(); + String category = categoryList.get(clicked.getKey()); + String arguments = prefix + " " + category; + CommandEvent event = new CommandEvent(actor, arguments); + CommandManager.getInstance().handleCommand(event); + } + } + }); + } + + @Override + public void displayCommands(Map commandMap, String visited, int page, int pageTotal, int effectiveLength) { + gui.setTitle(BBC.HELP_HEADER_SUBCOMMANDS.s()); + + String baseCommand = prefix; + if (effectiveLength > 0) baseCommand += " " + args.getString(0, effectiveLength - 1); + + CommandLocals locals = args.getLocals(); + if (!visited.isEmpty()) { + visited = visited + " "; + } + + List commands = new ArrayList<>(); + + for (Map.Entry cmdEntry : commandMap.entrySet()) { + CommandMapping mapping = cmdEntry.getKey(); + String subPrefix = cmdEntry.getValue(); + + StringBuilder helpCmd = new StringBuilder(); + helpCmd.append(prefix); + helpCmd.append(" "); + helpCmd.append(subPrefix); + CommandCallable c = mapping.getCallable(); + helpCmd.append(visited); + helpCmd.append(mapping.getPrimaryAlias()); + String s2 = mapping.getDescription().getDescription(); + if (c.testPermission(locals)) { +// gui.addLabel(s2); + gui.addButton(helpCmd.toString(), null); + commands.add(helpCmd.toString()); + } + } + + gui.setResponder(new Consumer>() { + @Override + public void accept(Map response) { + if (response.isEmpty()) { + // ?? + throw new IllegalArgumentException("No response for command list: " + prefix); + } else { + Map.Entry clicked = response.entrySet().iterator().next(); + int index = clicked.getKey(); + String cmd = commands.get(index); + CommandEvent event = new CommandEvent(actor, cmd); + CommandManager.getInstance().handleCommand(event); + } + } + }); + } + }.run(); + + gui.display(fp); } public static void help(CommandContext args, WorldEdit we, Actor actor, String prefix, CommandCallable callable) { - try { - if (callable == null) { - callable = we.getPlatformManager().getCommandManager().getDispatcher(); + final int perPage = actor instanceof Player ? 12 : 20; // More pages for console + + HelpBuilder builder = new HelpBuilder(callable, args, prefix, perPage) { + @Override + public void displayFailure(String message) { + actor.printError(message); } - CommandLocals locals = args.getLocals(); - int page = -1; - String category = null; - final int perPage = actor instanceof Player ? 12 : 20; // More pages for console - int effectiveLength = args.argsLength(); + @Override + public void displayUsage(CommandCallable callable, String command) { + new UsageMessage(callable, command).send(actor); + } - // Detect page from args - try { - if (effectiveLength > 0) { - page = args.getInteger(args.argsLength() - 1); - if (page <= 0) { - page = 1; + @Override + public void displayCategories(Map> categories) { + Message msg = new Message(); + msg.prefix().text(BBC.HELP_HEADER_CATEGORIES).newline(); + boolean first = true; + for (Map.Entry> entry : categories.entrySet()) { + String s1 = Commands.getAlias(UtilityCommands.class, "/help") + " " + entry.getKey(); + String s2 = entry.getValue().size() + ""; + msg.text(BBC.HELP_ITEM_ALLOWED, "&a" + s1, s2); + msg.tooltip(StringMan.join(entry.getValue().keySet(), ", ", cm -> cm.getPrimaryAlias())); + msg.command(s1); + msg.newline(); + } + msg.text(BBC.HELP_FOOTER).link("https://git.io/vSKE5").newline(); + msg.paginate((prefix.equals("/") ? Commands.getAlias(UtilityCommands.class, "/help") : prefix), 0, 1); + msg.send(actor); + } + + @Override + public void displayCommands(Map commandMap, String visited, int page, int pageTotal, int effectiveLength) { + Message msg = new Message(); + msg.prefix().text(BBC.HELP_HEADER, page + 1, pageTotal).newline(); + + CommandLocals locals = args.getLocals(); + + if (!visited.isEmpty()) { + visited = visited + " "; + } + + // Add each command + for (Map.Entry cmdEntry : commandMap.entrySet()) { + CommandMapping mapping = cmdEntry.getKey(); + String subPrefix = cmdEntry.getValue(); + + StringBuilder s1 = new StringBuilder(); + s1.append(prefix); + s1.append(subPrefix); + CommandCallable c = mapping.getCallable(); + s1.append(visited); + s1.append(mapping.getPrimaryAlias()); + String s2 = mapping.getDescription().getDescription(); + if (c.testPermission(locals)) { + msg.text(BBC.HELP_ITEM_ALLOWED, s1, s2); + String helpCmd = (prefix.equals("/") ? Commands.getAlias(UtilityCommands.class, "/help") + " " : "") + s1; + msg.cmdTip(helpCmd); + msg.newline(); } else { - page--; + msg.text(BBC.HELP_ITEM_DENIED, s1, s2).newline(); } - effectiveLength--; } - } catch (NumberFormatException ignored) { + + if (args.argsLength() == 0) { + msg.text(BBC.HELP_FOOTER).newline(); + } + String baseCommand = (prefix.equals("/") ? Commands.getAlias(UtilityCommands.class, "/help") : prefix); + if (effectiveLength > 0) baseCommand += " " + args.getString(0, effectiveLength - 1); + msg.paginate(baseCommand, page + 1, pageTotal); + + msg.send(actor); } + }; - boolean isRootLevel = true; - List visited = new ArrayList(); - - // Create the message - if (callable instanceof Dispatcher) { - Dispatcher dispatcher = (Dispatcher) callable; - - // Get a list of aliases - List aliases = new ArrayList(dispatcher.getCommands()); - List prefixes = Collections.nCopies(aliases.size(), ""); - // Group by callable - - if (page == -1 || effectiveLength > 0) { - Map> grouped = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - for (CommandMapping mapping : aliases) { - CommandCallable c = mapping.getCallable(); - String group; - if (c instanceof DelegateCallable) { - c = ((DelegateCallable) c).getParent(); - } - if (c instanceof ParametricCallable) { - Object obj = ((ParametricCallable) c).getObject(); - Command command = obj.getClass().getAnnotation(Command.class); - if (command != null && command.aliases().length != 0) { - group = command.aliases()[0]; - } else { - group = obj.getClass().getSimpleName().replaceAll("Commands", "").replaceAll("Util$", ""); - } - } else if (c instanceof Dispatcher) { - group = mapping.getPrimaryAlias(); - } else { - group = "Unsorted"; - } - group = group.replace("/", ""); - group = StringMan.toProperCase(group); - Map queue = grouped.get(group); - if (queue == null) { - queue = new LinkedHashMap<>(); - grouped.put(group, queue); - } - if (c instanceof Dispatcher) { - for (CommandMapping m : ((Dispatcher) c).getCommands()) { - queue.put(m, mapping.getPrimaryAlias() + " "); - } - } else { - // Sub commands get priority - queue.putIfAbsent(mapping, ""); - } - } - if (effectiveLength > 0) { - String cat = args.getString(0); - Map mappings = effectiveLength == 1 ? grouped.get(cat) : null; - if (mappings == null) { - // Drill down to the command - for (int i = 0; i < effectiveLength; i++) { - String command = args.getString(i); - - if (callable instanceof Dispatcher) { - // Chop off the beginning / if we're are the root level - if (isRootLevel && command.length() > 1 && command.charAt(0) == '/') { - command = command.substring(1); - } - - CommandMapping mapping = detectCommand((Dispatcher) callable, command, isRootLevel); - if (mapping != null) { - callable = mapping.getCallable(); - } else { - if (isRootLevel) { - Set found = new HashSet<>(); - String arg = args.getString(i).toLowerCase(); - String closest = null; - int distance = Integer.MAX_VALUE; - for (CommandMapping map : aliases) { - String desc = map.getDescription().getDescription(); - if (desc == null) desc = map.getDescription().getHelp(); - if (desc == null) desc = ""; - String[] descSplit = desc.replaceAll("[^A-Za-z0-9]", "").toLowerCase().split(" "); - for (String alias : map.getAllAliases()) { - if (alias.equals(arg)) { - closest = map.getPrimaryAlias(); - distance = 0; - found.add(map.getPrimaryAlias()); - } else if (alias.contains(arg)) { - closest = map.getPrimaryAlias(); - distance = 1; - found.add(map.getPrimaryAlias()); - } else if (StringMan.isEqualIgnoreCaseToAny(arg, descSplit)) { - closest = map.getPrimaryAlias(); - distance = 1; - found.add(map.getPrimaryAlias()); - } else { - int currentDist = StringMan.getLevenshteinDistance(alias, arg); - if (currentDist < distance) { - distance = currentDist; - closest = map.getPrimaryAlias(); - } - } - } - } - found.add(closest); - BBC.HELP_SUGGEST.send(actor, arg, StringMan.join(found, ", ")); - return; - } else { - actor.printError(String.format("The sub-command '%s' under '%s' could not be found.", - command, Joiner.on(" ").join(visited))); - return; - } - } - visited.add(args.getString(i)); - isRootLevel = false; - } else { - actor.printError(String.format("'%s' has no sub-commands. (Maybe '%s' is for a parameter?)", - Joiner.on(" ").join(visited), command)); - return; - } - } - if (!(callable instanceof Dispatcher)) { - // TODO interactive box - new UsageMessage(callable, (WorldEdit.getInstance().getConfiguration().noDoubleSlash ? "" : "/") + Joiner.on(" ").join(visited)).send(actor); - return; - } - dispatcher = (Dispatcher) callable; - aliases = new ArrayList(dispatcher.getCommands()); - prefixes = Collections.nCopies(aliases.size(), ""); - } else { - aliases = new ArrayList<>(); - prefixes = new ArrayList<>(); - for (Map.Entry entry : mappings.entrySet()) { - aliases.add(entry.getKey()); - prefixes.add(entry.getValue()); - } - } - page = Math.max(0, page); - } else if (grouped.size() > 1) { - Message msg = new Message(); - msg.prefix().text(BBC.HELP_HEADER_CATEGORIES).newline(); - boolean first = true; - for (Map.Entry> entry : grouped.entrySet()) { - String s1 = Commands.getAlias(UtilityCommands.class, "/help") + " " + entry.getKey(); - String s2 = entry.getValue().size() + ""; - msg.text(BBC.HELP_ITEM_ALLOWED, "&a" + s1, s2); - msg.tooltip(StringMan.join(entry.getValue().keySet(), ", ", cm -> cm.getPrimaryAlias())); - msg.command(s1); - msg.newline(); - } - msg.text(BBC.HELP_FOOTER).link("https://git.io/vSKE5").newline(); - msg.paginate((prefix.equals("/") ? Commands.getAlias(UtilityCommands.class, "/help") : prefix), 0, 1); - msg.send(actor); - return; - } - } -// else - { - Collections.sort(aliases, new PrimaryAliasComparator(CommandManager.COMMAND_CLEAN_PATTERN)); - - // Calculate pagination - int offset = perPage * Math.max(0, page); - int pageTotal = (int) Math.ceil(aliases.size() / (double) perPage); - - // Box - Message msg = new Message(); - - if (offset >= aliases.size()) { - msg.text("&c").text(String.format("There is no page %d (total number of pages is %d).", page + 1, pageTotal)); - } else { - msg.prefix().text(BBC.HELP_HEADER, page + 1, pageTotal).newline(); - int end = Math.min(offset + perPage, aliases.size()); - List subAliases = aliases.subList(offset, end); - List subPrefixes = prefixes.subList(offset, end); - boolean first = true; - // Add each command - for (int i = 0; i < subAliases.size(); i++) { - StringBuilder s1 = new StringBuilder(); - s1.append(prefix); - s1.append(subPrefixes.get(i)); - CommandMapping mapping = subAliases.get(i); - CommandCallable c = mapping.getCallable(); - if (!visited.isEmpty()) { - s1.append(Joiner.on(" ").join(visited)); - s1.append(" "); - } - s1.append(mapping.getPrimaryAlias()); - String s2 = mapping.getDescription().getDescription(); - if (c.testPermission(locals)) { - msg.text(BBC.HELP_ITEM_ALLOWED, s1, s2); - String helpCmd = (prefix.equals("/") ? Commands.getAlias(UtilityCommands.class, "/help") + " " : "") + s1; - msg.cmdTip(helpCmd); - msg.newline(); - } else { - msg.text(BBC.HELP_ITEM_DENIED, s1, s2).newline(); - } - } - if (args.argsLength() == 0) { - msg.text(BBC.HELP_FOOTER).newline(); - } - String baseCommand = (prefix.equals("/") ? Commands.getAlias(UtilityCommands.class, "/help") : prefix); - if (effectiveLength > 0) baseCommand += " " + args.getString(0, effectiveLength - 1); - msg.paginate(baseCommand, page + 1, pageTotal); - } - msg.send(actor); - } - } else { - new UsageMessage(callable, (WorldEdit.getInstance().getConfiguration().noDoubleSlash ? "" : "/") + Joiner.on(" ").join(visited)).send(actor); - } - } catch (Throwable e) { - e.printStackTrace(); - throw new RuntimeException(e); - } + builder.run(); } public static Class inject() { diff --git a/nukkit/src/main/java/com/boydti/fawe/nukkit/core/gui/NukkitFormBuilder.java b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/gui/NukkitFormBuilder.java index e8e2d7ec..3f5c3fe7 100644 --- a/nukkit/src/main/java/com/boydti/fawe/nukkit/core/gui/NukkitFormBuilder.java +++ b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/gui/NukkitFormBuilder.java @@ -19,12 +19,14 @@ import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.function.Consumer; import static com.google.common.base.Preconditions.checkNotNull; public class NukkitFormBuilder implements FormBuilder { + private Consumer> response; private final List elements; private final List buttons; private String title = ""; @@ -125,22 +127,29 @@ public class NukkitFormBuilder implements FormBuilder { } @Override - public void display(FawePlayer fp, Consumer> response) { + public FormBuilder setResponder(Consumer> handler) { + this.response = handler; + return this; + } + + @Override + public void display(FawePlayer fp) { FormWindow window; if (buttons.isEmpty()) { if (icon == null) { - window = new FormWindowCustom("Title", elements); + window = new FormWindowCustom(title, elements); } else { - window = new FormWindowCustom("Title", elements, icon); + window = new FormWindowCustom(title, elements, icon); } } else { - window = new FormWindowSimple("Title", "", buttons); + window = new FormWindowSimple(title, "", buttons); } if (response != null) { window = new ResponseFormWindow(window, response); } + Player player = fp.parent; player.showFormWindow(window); } diff --git a/nukkit/src/main/java/com/boydti/fawe/nukkit/core/gui/ResponseFormWindow.java b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/gui/ResponseFormWindow.java index 1d67237b..b8b2f7be 100644 --- a/nukkit/src/main/java/com/boydti/fawe/nukkit/core/gui/ResponseFormWindow.java +++ b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/gui/ResponseFormWindow.java @@ -1,22 +1,22 @@ package com.boydti.fawe.nukkit.core.gui; import cn.nukkit.form.window.FormWindow; -import java.util.List; +import java.util.Map; import java.util.function.Consumer; import static com.google.common.base.Preconditions.checkNotNull; public class ResponseFormWindow extends DelegateFormWindow { - private final Consumer> task; + private final Consumer> task; - public ResponseFormWindow(FormWindow parent, Consumer> onResponse) { + public ResponseFormWindow(FormWindow parent, Consumer> onResponse) { super(parent); checkNotNull(onResponse); this.task = onResponse; } - public void respond(List response) { - task.accept(response); + public void respond(Map response) { + if (task != null) task.accept(response); } } \ No newline at end of file diff --git a/nukkit/src/main/java/com/boydti/fawe/nukkit/optimization/FaweNukkit.java b/nukkit/src/main/java/com/boydti/fawe/nukkit/optimization/FaweNukkit.java index 6d2a49da..87bd1b75 100644 --- a/nukkit/src/main/java/com/boydti/fawe/nukkit/optimization/FaweNukkit.java +++ b/nukkit/src/main/java/com/boydti/fawe/nukkit/optimization/FaweNukkit.java @@ -6,12 +6,19 @@ import cn.nukkit.event.EventHandler; import cn.nukkit.event.Listener; import cn.nukkit.event.player.PlayerFormRespondedEvent; import cn.nukkit.event.player.PlayerQuitEvent; +import cn.nukkit.form.element.ElementButton; +import cn.nukkit.form.response.FormResponse; +import cn.nukkit.form.response.FormResponseCustom; +import cn.nukkit.form.response.FormResponseData; +import cn.nukkit.form.response.FormResponseSimple; +import cn.nukkit.form.window.FormWindow; import com.boydti.fawe.Fawe; import com.boydti.fawe.IFawe; import com.boydti.fawe.config.Settings; import com.boydti.fawe.nukkit.core.NukkitTaskManager; import com.boydti.fawe.nukkit.core.NukkitWorldEdit; import com.boydti.fawe.nukkit.core.gui.NukkitFormBuilder; +import com.boydti.fawe.nukkit.core.gui.ResponseFormWindow; import com.boydti.fawe.nukkit.listener.BrushListener; import com.boydti.fawe.nukkit.optimization.queue.NukkitQueue; import com.boydti.fawe.object.FaweChunk; @@ -26,6 +33,9 @@ import com.sk89q.worldedit.world.World; import java.io.File; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.UUID; import java.util.logging.Level; @@ -72,6 +82,40 @@ public class FaweNukkit implements IFawe, Listener { @EventHandler public void onFormSubmit(PlayerFormRespondedEvent event) { + FormWindow window = event.getWindow(); + if (window instanceof ResponseFormWindow) { + ResponseFormWindow responseWindow = (ResponseFormWindow) window; + FormResponse response = event.getResponse(); + if (response instanceof FormResponseSimple) { + FormResponseSimple simple = (FormResponseSimple) response; + ElementButton button = simple.getClickedButton(); + int index = simple.getClickedButtonId(); + + System.out.println("Simple: " + index); + + responseWindow.respond(Collections.singletonMap(index, "true")); + + } else if (response instanceof FormResponseCustom) { + FormResponseCustom custom = (FormResponseCustom) response; + HashMap responses = custom.getResponses(); + + HashMap parsedResponses = new HashMap<>(); + for (Map.Entry responseEntry : responses.entrySet()) { + int index = responseEntry.getKey(); + Object value = responseEntry.getValue(); + if (value instanceof FormResponseData) { + value = ((FormResponseData) value).getElementContent(); + } else if (value instanceof Float) { + value = (double) (float) value; + } + parsedResponses.put(index, value); + } + + responseWindow.respond(parsedResponses); + + System.out.println("Custom: " + parsedResponses); + } + } // TODO } diff --git a/nukkit/src/main/java/com/boydti/fawe/nukkit/optimization/FaweNukkitPlayer.java b/nukkit/src/main/java/com/boydti/fawe/nukkit/optimization/FaweNukkitPlayer.java index c223b457..8131b7c3 100644 --- a/nukkit/src/main/java/com/boydti/fawe/nukkit/optimization/FaweNukkitPlayer.java +++ b/nukkit/src/main/java/com/boydti/fawe/nukkit/optimization/FaweNukkitPlayer.java @@ -36,7 +36,7 @@ public class FaweNukkitPlayer extends FawePlayer { @Override public void setPermission(final String perm, final boolean flag) { - this.parent.addAttachment(Fawe. imp().getPlugin()).setPermission("fawe.bypass", flag); + this.parent.addAttachment(Fawe. imp().getPlugin()).setPermission(perm, flag); } diff --git a/nukkit/src/main/resources/plugin.yml b/nukkit/src/main/resources/plugin.yml index be4f558c..e61b65d0 100644 --- a/nukkit/src/main/resources/plugin.yml +++ b/nukkit/src/main/resources/plugin.yml @@ -10,6 +10,9 @@ permissions: default: true fawe.bypass: default: false + children: + fawe.bypass.regions: true + fawe.limit.*: true fawe.tips: default: false fawe.admin: