From 5165c439fc5b3a9468bb72d99c700146a3260c7a Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Wed, 3 Jun 2020 01:50:10 +0200 Subject: [PATCH] Add tab completion and fix minor bugs --- .../bukkit/util/BukkitSetupUtils.java | 115 ++++++++++++++++++ .../plotsquared/bukkit/util/BukkitUtil.java | 2 +- .../com/plotsquared/core/command/Setup.java | 20 ++- .../core/setup/CommonSetupSteps.java | 41 ++----- .../core/setup/PlotAreaBuilder.java | 2 +- .../core/setup/SettingsNodeStep.java | 24 +++- .../plotsquared/core/setup/SetupProcess.java | 2 +- .../com/plotsquared/core/setup/SetupStep.java | 25 +++- .../plotsquared/core/util/TabCompletions.java | 20 +++ 9 files changed, 213 insertions(+), 38 deletions(-) diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitSetupUtils.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitSetupUtils.java index af529c251..ce893aeee 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitSetupUtils.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitSetupUtils.java @@ -33,6 +33,7 @@ import com.plotsquared.core.generator.GeneratorWrapper; import com.plotsquared.core.plot.PlotArea; import com.plotsquared.core.plot.PlotAreaType; import com.plotsquared.core.plot.SetupObject; +import com.plotsquared.core.setup.PlotAreaBuilder; import com.plotsquared.core.util.SetupUtils; import io.papermc.lib.PaperLib; import org.bukkit.Bukkit; @@ -209,6 +210,120 @@ public class BukkitSetupUtils extends SetupUtils { return object.world; } + @Override + public String setupWorld(PlotAreaBuilder builder) { + SetupUtils.manager.updateGenerators(); + ConfigurationNode[] steps = builder.settingsNodesWrapper() == null ? + new ConfigurationNode[0] : builder.settingsNodesWrapper().getSettingsNodes(); + String world = builder.worldName(); + PlotAreaType type = builder.plotAreaType(); + String worldPath = "worlds." + builder.worldName(); + switch (type) { + case PARTIAL: { + if (builder.areaName() != null) { + if (!PlotSquared.get().worlds.contains(worldPath)) { + PlotSquared.get().worlds.createSection(worldPath); + } + ConfigurationSection worldSection = + PlotSquared.get().worlds.getConfigurationSection(worldPath); + String areaName = builder.areaName() + "-" + builder.minimumId() + "-" + builder.maximumId(); + String areaPath = "areas." + areaName; + if (!worldSection.contains(areaPath)) { + worldSection.createSection(areaPath); + } + ConfigurationSection areaSection = + worldSection.getConfigurationSection(areaPath); + HashMap options = new HashMap<>(); + for (ConfigurationNode step : steps) { + options.put(step.getConstant(), step.getValue()); + } + options.put("generator.type", builder.plotAreaType().toString()); + options.put("generator.terrain", builder.terrainType().toString()); + options.put("generator.plugin", builder.plotManager()); + if (builder.generatorName() != null && !builder.generatorName() + .equals(builder.plotManager())) { + options.put("generator.init", builder.generatorName()); + } + for (Entry entry : options.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + if (worldSection.contains(key)) { + Object current = worldSection.get(key); + if (!Objects.equals(value, current)) { + areaSection.set(key, value); + } + } else { + worldSection.set(key, value); + } + } + } + GeneratorWrapper gen = SetupUtils.generators.get(builder.generatorName()); + if (gen != null && gen.isFull()) { + builder.generatorName(null); + } + break; + } + case AUGMENTED: { + if (!builder.plotManager().endsWith(":single")) { + if (!PlotSquared.get().worlds.contains(worldPath)) { + PlotSquared.get().worlds.createSection(worldPath); + } + if (steps.length != 0) { + ConfigurationSection worldSection = + PlotSquared.get().worlds.getConfigurationSection(worldPath); + for (ConfigurationNode step : steps) { + worldSection.set(step.getConstant(), step.getValue()); + } + } + PlotSquared.get().worlds + .set("worlds." + world + ".generator.type", builder.plotAreaType().toString()); + PlotSquared.get().worlds + .set("worlds." + world + ".generator.terrain", builder.terrainType().toString()); + PlotSquared.get().worlds + .set("worlds." + world + ".generator.plugin", builder.plotManager()); + if (builder.generatorName() != null && !builder.generatorName() + .equals(builder.plotManager())) { + PlotSquared.get().worlds + .set("worlds." + world + ".generator.init", builder.generatorName()); + } + } + GeneratorWrapper gen = SetupUtils.generators.get(builder.generatorName()); + if (gen != null && gen.isFull()) { + builder.generatorName(null); + } + break; + } + case NORMAL: { + if (steps.length != 0) { + if (!PlotSquared.get().worlds.contains(worldPath)) { + PlotSquared.get().worlds.createSection(worldPath); + } + ConfigurationSection worldSection = + PlotSquared.get().worlds.getConfigurationSection(worldPath); + for (ConfigurationNode step : steps) { + worldSection.set(step.getConstant(), step.getValue()); + } + } + break; + } + } + + try { + PlotSquared.get().worlds.save(PlotSquared.get().worldsFile); + } catch (IOException e) { + e.printStackTrace(); + } + + Objects.requireNonNull(PlotSquared.imp()).getWorldManager() + .handleWorldCreation(builder.worldName(), builder.generatorName()); + + if (Bukkit.getWorld(world) != null) { + return world; + } + + return builder.worldName(); + } + @Override public String getGenerator(PlotArea plotArea) { if (SetupUtils.generators.isEmpty()) { updateGenerators(); diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitUtil.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitUtil.java index b21301251..b771c5e45 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitUtil.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitUtil.java @@ -270,7 +270,7 @@ public class BukkitUtil extends WorldUtil { return lastPlotPlayer; } final PlayerManager playerManager = PlotSquared.imp().getPlayerManager(); - return ((BukkitPlayerManager) player).getPlayer(player); + return ((BukkitPlayerManager) playerManager).getPlayer(player); } public static Location getLocation(@NonNull final org.bukkit.Location location) { diff --git a/Core/src/main/java/com/plotsquared/core/command/Setup.java b/Core/src/main/java/com/plotsquared/core/command/Setup.java index 97aa5350e..db3d750bd 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Setup.java +++ b/Core/src/main/java/com/plotsquared/core/command/Setup.java @@ -57,6 +57,7 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -98,14 +99,13 @@ public class Setup extends SubCommand { if (process == null) { if (args.length > 0) { // TODO use old behaviour? - MainUtil.sendMessage(player, "Use /plot setup to start the setup"); + MainUtil.sendMessage(player, Captions.SETUP_NOT_STARTED); return true; } process = new SetupProcess(); player.setMeta("setup", process); SetupUtils.manager.updateGenerators(); com.plotsquared.core.setup.SetupStep step = process.getCurrentStep(); - // TODO generalize? step.announce(player); displayGenerators(player); return true; @@ -116,8 +116,12 @@ public class Setup extends SubCommand { process.getCurrentStep().announce(player); } else if ("cancel".equalsIgnoreCase(args[0])) { player.deleteMeta("setup"); + MainUtil.sendMessage(player, Captions.SETUP_CANCELLED); } else { process.handleInput(player, args[0]); + if (process.getCurrentStep() != null) { + process.getCurrentStep().announce(player); + } } return true; } else { @@ -392,6 +396,18 @@ public class Setup extends SubCommand { return false; } + @Override public Collection tab(PlotPlayer player, String[] args, boolean space) { + SetupProcess process = (SetupProcess) player.getMeta("setup"); // TODO use generics -> auto cast + if (process == null) { + return Collections.emptyList(); + } + // player already provided too many arguments + if (args.length > 1 || (args.length == 1 && space)) { + return Collections.emptyList(); + } + SetupStep setupStep = process.getCurrentStep(); + return setupStep.createSuggestions(player, space ? "" : args[0]); + } /*private static final class StepPickGenerator extends SetupStep { diff --git a/Core/src/main/java/com/plotsquared/core/setup/CommonSetupSteps.java b/Core/src/main/java/com/plotsquared/core/setup/CommonSetupSteps.java index 34869d15f..4b0a6c7a6 100644 --- a/Core/src/main/java/com/plotsquared/core/setup/CommonSetupSteps.java +++ b/Core/src/main/java/com/plotsquared/core/setup/CommonSetupSteps.java @@ -3,7 +3,6 @@ package com.plotsquared.core.setup; import com.plotsquared.core.PlotSquared; import com.plotsquared.core.configuration.Caption; import com.plotsquared.core.configuration.Captions; -import com.plotsquared.core.configuration.ConfigurationNode; import com.plotsquared.core.events.TeleportCause; import com.plotsquared.core.generator.GeneratorWrapper; import com.plotsquared.core.player.PlotPlayer; @@ -30,18 +29,16 @@ import static com.plotsquared.core.util.MainUtil.sendMessage; public enum CommonSetupSteps implements SetupStep { CHOOSE_GENERATOR(Captions.SETUP_INIT) { - @Override public SetupStep handleInput(PlotPlayer plotPlayer, PlotAreaBuilder builder, String arg) { + @Override public SetupStep handleInput(PlotPlayer plotPlayer, PlotAreaBuilder builder, String arg) { if (!SetupUtils.generators.containsKey(arg)) { String prefix = "\n&8 - &7"; sendMessage(plotPlayer, Captions.SETUP_WORLD_GENERATOR_ERROR + prefix + StringMan .join(SetupUtils.generators.keySet(), prefix) .replaceAll(PlotSquared.imp().getPluginName(), "&2" + PlotSquared.imp().getPluginName())); - sendMessage(plotPlayer, Captions.SETUP_INIT); return this; // invalid input -> same setup step } builder.generatorName(arg); - sendMessage(plotPlayer, Captions.SETUP_WORLD_TYPE); return CommonSetupSteps.CHOOSE_PLOT_AREA_TYPE; // proceed with next step } @@ -55,7 +52,7 @@ public enum CommonSetupSteps implements SetupStep { }, CHOOSE_PLOT_AREA_TYPE(PlotAreaType.class, Captions.SETUP_WORLD_TYPE) { - @Override public SetupStep handleInput(PlotPlayer plotPlayer, PlotAreaBuilder builder, String arg) { + @Override public SetupStep handleInput(PlotPlayer plotPlayer, PlotAreaBuilder builder, String arg) { boolean withNormal = SetupUtils.generators.get(builder.generatorName()).isFull(); Optional plotAreaType = PlotAreaType.fromString(arg); if (!plotAreaType.isPresent()) { @@ -71,7 +68,6 @@ public enum CommonSetupSteps implements SetupStep { return this; } builder.plotAreaType(plotAreaType.get()); - // object.type = plotAreaType.get(); GeneratorWrapper gen = SetupUtils.generators.get(builder.generatorName()); if (builder.plotAreaType() == PlotAreaType.NORMAL) { if (builder.settingsNodesWrapper() == null) { @@ -81,16 +77,10 @@ public enum CommonSetupSteps implements SetupStep { // .processSetup(process); } if (!builder.settingsNodesWrapper().hasNext()) { - // MainUtil.sendMessage(plotPlayer, Captions.SETUP_WORLD_NAME); // object.setup_index = 0; TODO what did that do? - return CHOOSE_WORLD_NAME; // skip + return builder.settingsNodesWrapper().getAfterwards(); // skip } - SettingsNodeStep next = builder.settingsNodesWrapper().next(); - ConfigurationNode step = next.getConfigurationNode(); - sendMessage(plotPlayer, Captions.SETUP_STEP, next.getId() + 1, - step.getDescription(), step.getType().getType(), - String.valueOf(step.getDefaultValue())); - return next; + return builder.settingsNodesWrapper().next(); } else { if (gen.isFull()) { builder.plotManager(builder.generatorName()); @@ -105,23 +95,20 @@ public enum CommonSetupSteps implements SetupStep { // TODO why is processSetup not called here? } if (builder.plotAreaType() == PlotAreaType.PARTIAL) { - // MainUtil.sendMessage(plotPlayer, Captions.SETUP_AREA_NAME); - // TODO return step area id - return null; + return CHOOSE_AREA_ID; } else { - // MainUtil.sendMessage(plotPlayer, Captions.SETUP_PARTIAL_AREA); return CHOOSE_TERRAIN_TYPE; } } } @Nullable @Override public String getDefaultValue() { - return PlotAreaType.NORMAL.toString(); // TODO toLowerCase here? + return PlotAreaType.NORMAL.toString(); } }, CHOOSE_AREA_ID(Captions.SETUP_AREA_NAME) { - @Override public SetupStep handleInput(PlotPlayer plotPlayer, PlotAreaBuilder builder, String argument) { + @Override public SetupStep handleInput(PlotPlayer plotPlayer, PlotAreaBuilder builder, String argument) { if (!StringMan.isAlphanumericUnd(argument)) { MainUtil.sendMessage(plotPlayer, Captions.SETUP_AREA_NON_ALPHANUMERICAL); return this; @@ -142,7 +129,7 @@ public enum CommonSetupSteps implements SetupStep { }, CHOOSE_MINIMUM_PLOT_ID(Captions.SETUP_AREA_MIN_PLOT_ID) { - @Override public SetupStep handleInput(PlotPlayer plotPlayer, PlotAreaBuilder builder, String argument) { + @Override public SetupStep handleInput(PlotPlayer plotPlayer, PlotAreaBuilder builder, String argument) { try { builder.minimumId(PlotId.fromString(argument)); } catch (IllegalArgumentException ignored) { @@ -161,7 +148,7 @@ public enum CommonSetupSteps implements SetupStep { }, CHOOSE_MAXIMUM_PLOT_ID(Captions.SETUP_AREA_MAX_PLOT_ID) { - @Override public SetupStep handleInput(PlotPlayer plotPlayer, PlotAreaBuilder builder, String argument) { + @Override public SetupStep handleInput(PlotPlayer plotPlayer, PlotAreaBuilder builder, String argument) { try { builder.maximumId(PlotId.fromString(argument)); } catch (IllegalArgumentException ignored) { @@ -180,7 +167,7 @@ public enum CommonSetupSteps implements SetupStep { }, CHOOSE_TERRAIN_TYPE(PlotAreaTerrainType.class, Captions.SETUP_PARTIAL_AREA) { - @Override public SetupStep handleInput(PlotPlayer plotPlayer, PlotAreaBuilder builder, String argument) { + @Override public SetupStep handleInput(PlotPlayer plotPlayer, PlotAreaBuilder builder, String argument) { Optional optTerrain; if (!(optTerrain = PlotAreaTerrainType.fromString(argument)) .isPresent()) { @@ -192,17 +179,16 @@ public enum CommonSetupSteps implements SetupStep { builder.settingsNodesWrapper(CommonSetupSteps.wrap(builder.plotManager())); } SettingsNodesWrapper wrapper = builder.settingsNodesWrapper(); - // TODO return CHOOSE_WORLD_NAME if !hasNext return wrapper.hasNext() ? wrapper.next() : wrapper.getAfterwards(); } @Nullable @Override public String getDefaultValue() { - return PlotAreaTerrainType.NONE.toString(); // TODO toLowerCase here? + return PlotAreaTerrainType.NONE.toString(); } }, CHOOSE_WORLD_NAME(Captions.SETUP_WORLD_NAME) { - @Override public SetupStep handleInput(PlotPlayer plotPlayer, PlotAreaBuilder builder, String argument) { + @Override public SetupStep handleInput(PlotPlayer plotPlayer, PlotAreaBuilder builder, String argument) { if (!isValidWorldName(argument)) { MainUtil.sendMessage(plotPlayer, Captions.SETUP_WORLD_NAME_FORMAT + argument); return this; @@ -259,8 +245,7 @@ public enum CommonSetupSteps implements SetupStep { this(enumToStrings(argumentType), description); } - @Override - public void announce(PlotPlayer plotPlayer) { + @Override public void announce(PlotPlayer plotPlayer) { MainUtil.sendMessage(plotPlayer, this.description); } diff --git a/Core/src/main/java/com/plotsquared/core/setup/PlotAreaBuilder.java b/Core/src/main/java/com/plotsquared/core/setup/PlotAreaBuilder.java index 956a5f847..c6c6ea494 100644 --- a/Core/src/main/java/com/plotsquared/core/setup/PlotAreaBuilder.java +++ b/Core/src/main/java/com/plotsquared/core/setup/PlotAreaBuilder.java @@ -32,7 +32,7 @@ public class PlotAreaBuilder { public void maximumId(PlotId maximumId) { if (this.minimumId != null - && (this.minimumId.getX() <= maximumId.getX() || this.minimumId.getY() <= maximumId.getY())) { + && (maximumId.getX() <= this.minimumId.getX() || maximumId.getY() <= this.minimumId.getY())) { throw new IllegalStateException("maxId <= minId"); } this.maximumId = maximumId; diff --git a/Core/src/main/java/com/plotsquared/core/setup/SettingsNodeStep.java b/Core/src/main/java/com/plotsquared/core/setup/SettingsNodeStep.java index b4f4a94c5..55d78722d 100644 --- a/Core/src/main/java/com/plotsquared/core/setup/SettingsNodeStep.java +++ b/Core/src/main/java/com/plotsquared/core/setup/SettingsNodeStep.java @@ -1,14 +1,18 @@ package com.plotsquared.core.setup; +import com.plotsquared.core.command.Command; +import com.plotsquared.core.command.RequiredType; import com.plotsquared.core.configuration.Captions; import com.plotsquared.core.configuration.ConfigurationNode; import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.util.MainUtil; +import com.plotsquared.core.util.TabCompletions; import lombok.Getter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; +import java.util.Collections; public class SettingsNodeStep implements SetupStep { @Getter private final ConfigurationNode configurationNode; @@ -21,7 +25,7 @@ public class SettingsNodeStep implements SetupStep { this.wrapper = wrapper; } - @Override public SetupStep handleInput(PlotPlayer plotPlayer, PlotAreaBuilder builder, String argument) { + @Override public SetupStep handleInput(PlotPlayer plotPlayer, PlotAreaBuilder builder, String argument) { if (this.configurationNode.isValid(argument)) { this.configurationNode.setValue(argument); } @@ -36,10 +40,24 @@ public class SettingsNodeStep implements SetupStep { return String.valueOf(this.configurationNode.getDefaultValue()); } - @Override - public void announce(PlotPlayer plotPlayer) { + @Override public void announce(PlotPlayer plotPlayer) { MainUtil.sendMessage(plotPlayer, Captions.SETUP_STEP, this.getId() + 1, this.configurationNode.getDescription(), this.configurationNode.getType().getType(), String.valueOf(this.configurationNode.getDefaultValue())); } + + @Override public Collection createSuggestions(PlotPlayer plotPlayer, String argument) { + switch (this.configurationNode.getType().getType()) { + case "BLOCK_BUCKET": + return TabCompletions.completePatterns(argument); + case "INTEGER": + if (getDefaultValue() != null && getDefaultValue().startsWith(argument)) { + return Collections.singletonList(new Command(null, false, + getDefaultValue(), "", RequiredType.NONE, null) {}); + } + case "BOOLEAN": + return TabCompletions.completeBoolean(argument); + } + return Collections.emptyList(); + } } diff --git a/Core/src/main/java/com/plotsquared/core/setup/SetupProcess.java b/Core/src/main/java/com/plotsquared/core/setup/SetupProcess.java index 0086189ce..93d7b9593 100644 --- a/Core/src/main/java/com/plotsquared/core/setup/SetupProcess.java +++ b/Core/src/main/java/com/plotsquared/core/setup/SetupProcess.java @@ -19,7 +19,7 @@ public class SetupProcess { return this.current; } - public void handleInput(PlotPlayer plotPlayer, String argument) { + public void handleInput(PlotPlayer plotPlayer, String argument) { SetupStep previous = this.current; // TODO null check? this.current = this.current.handleInput(plotPlayer, this.builder, argument); diff --git a/Core/src/main/java/com/plotsquared/core/setup/SetupStep.java b/Core/src/main/java/com/plotsquared/core/setup/SetupStep.java index 787c4ac03..8ea5db0a0 100644 --- a/Core/src/main/java/com/plotsquared/core/setup/SetupStep.java +++ b/Core/src/main/java/com/plotsquared/core/setup/SetupStep.java @@ -1,10 +1,14 @@ package com.plotsquared.core.setup; +import com.plotsquared.core.command.Command; +import com.plotsquared.core.command.RequiredType; import com.plotsquared.core.player.PlotPlayer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; public interface SetupStep { @@ -16,7 +20,7 @@ public interface SetupStep { * @param argument the argument given as input * @return the next step if input was valid, this setup step otherwise */ - SetupStep handleInput(final PlotPlayer plotPlayer, PlotAreaBuilder builder, String argument); + SetupStep handleInput(final PlotPlayer plotPlayer, PlotAreaBuilder builder, String argument); @NotNull Collection getSuggestions(); @@ -27,7 +31,24 @@ public interface SetupStep { * * @param plotPlayer the player to announce this step to. */ - void announce(PlotPlayer plotPlayer); + void announce(PlotPlayer plotPlayer); + + /** + * Creates a collection of suggestions for the current input. + * + * @param plotPlayer the player to receive the suggestions. + * @param argument the argument already typed. + * @return a collection of suggestions. + */ + default Collection createSuggestions(final PlotPlayer plotPlayer, String argument) { + List result = new ArrayList<>(getSuggestions().size()); + for (String suggestion : getSuggestions()) { + if (suggestion.startsWith(argument)) { + result.add(new Command(null, false, suggestion, "", RequiredType.NONE, null) {}); + } + } + return result; + } /** * This method is called when the SetupProcess reverts to a previous step. diff --git a/Core/src/main/java/com/plotsquared/core/util/TabCompletions.java b/Core/src/main/java/com/plotsquared/core/util/TabCompletions.java index 0369ad730..91454b3bb 100644 --- a/Core/src/main/java/com/plotsquared/core/util/TabCompletions.java +++ b/Core/src/main/java/com/plotsquared/core/util/TabCompletions.java @@ -38,7 +38,9 @@ import lombok.experimental.UtilityClass; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.concurrent.TimeUnit; @@ -53,6 +55,11 @@ public class TabCompletions { private final Cache> cachedCompletionValues = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build(); + private final Command booleanTrueCompletion = new Command(null, false, "true", "", + RequiredType.NONE, null) {}; + private final Command booleanFalseCompletion = new Command(null, false, "false", "", + RequiredType.NONE, null) {}; + /** * Get a list of tab completions corresponding to player names. This uses the UUID pipeline * cache, so it will complete will all names known to PlotSquared @@ -109,4 +116,17 @@ public class TabCompletions { }).collect(Collectors.toList()); } + @NotNull public List completeBoolean(@NotNull final String input) { + if (input.isEmpty()) { + return Arrays.asList(booleanTrueCompletion, booleanFalseCompletion); + } + if ("true".startsWith(input)) { + return Collections.singletonList(booleanTrueCompletion); + } + if ("false".startsWith(input)) { + return Collections.singletonList(booleanFalseCompletion); + } + return Collections.emptyList(); + } + }