diff --git a/Core/src/main/java/com/plotsquared/core/commands/PlotSquaredCommandBean.java b/Core/src/main/java/com/plotsquared/core/commands/PlotSquaredCommandBean.java index 6c604d61b..46da2f099 100644 --- a/Core/src/main/java/com/plotsquared/core/commands/PlotSquaredCommandBean.java +++ b/Core/src/main/java/com/plotsquared/core/commands/PlotSquaredCommandBean.java @@ -25,6 +25,19 @@ public abstract class PlotSquaredCommandBean extends CommandBean> */ public abstract @NonNull Set<@NonNull CommandRequirement> requirements(); + /** + * Prepares the given {@code builder}. + * + *

This should be implemented by abstract classes that extend {@link PlotSquaredCommandBean} to offer shared behavior + * for a subset of plot commands.

+ * + * @param builder the builder + * @return the prepared builder + */ + protected Command.@NonNull Builder> prepare(final Command.@NonNull Builder> builder) { + return builder; + } + @Override protected final @NonNull CommandProperties properties() { return CommandProperties.of("platsquared", "plat"); @@ -33,8 +46,7 @@ public abstract class PlotSquaredCommandBean extends CommandBean> @Override protected final Command.@NonNull Builder> configure(final Command.@NonNull Builder> builder) { Command.@NonNull Builder> intermediaryBuilder = - this.configurePlotCommand(builder.meta(PlotSquaredCommandMeta.META_CATEGORY, - this.category())); + this.configurePlotCommand(this.prepare(builder.meta(PlotSquaredCommandMeta.META_CATEGORY, this.category()))); for (final CommandRequirement requirement : this.requirements()) { intermediaryBuilder = intermediaryBuilder.meta(requirement.key(), true); } diff --git a/Core/src/main/java/com/plotsquared/core/commands/command/setting/flag/FlagCommandBean.java b/Core/src/main/java/com/plotsquared/core/commands/command/setting/flag/FlagCommandBean.java new file mode 100644 index 000000000..664efe794 --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/commands/command/setting/flag/FlagCommandBean.java @@ -0,0 +1,123 @@ +package com.plotsquared.core.commands.command.setting.flag; + +import cloud.commandframework.Command; +import com.plotsquared.core.PlotSquared; +import com.plotsquared.core.command.CommandCategory; +import com.plotsquared.core.commands.CommandRequirement; +import com.plotsquared.core.commands.CommonCommandRequirement; +import com.plotsquared.core.commands.PlotSquaredCommandBean; +import com.plotsquared.core.configuration.Settings; +import com.plotsquared.core.configuration.caption.TranslatableCaption; +import com.plotsquared.core.permissions.Permission; +import com.plotsquared.core.player.PlotPlayer; +import com.plotsquared.core.plot.flag.FlagParseException; +import com.plotsquared.core.plot.flag.PlotFlag; +import com.plotsquared.core.plot.flag.types.IntegerFlag; +import com.plotsquared.core.plot.flag.types.ListFlag; +import com.plotsquared.core.util.MathMan; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.tag.Tag; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.List; +import java.util.Set; + +public abstract class FlagCommandBean extends PlotSquaredCommandBean { + + protected static boolean checkPermValue( + final @NonNull PlotPlayer player, + final @NonNull PlotFlag flag, @NonNull String key, @NonNull String value + ) { + key = key.toLowerCase(); + value = value.toLowerCase(); + String perm = Permission.PERMISSION_SET_FLAG_KEY_VALUE.format(key.toLowerCase(), value.toLowerCase()); + if (flag instanceof IntegerFlag && MathMan.isInteger(value)) { + try { + int numeric = Integer.parseInt(value); + // Getting full permission without "." at the end + perm = perm.substring(0, perm.length() - value.length() - 1); + boolean result = false; + if (numeric >= 0) { + int checkRange = PlotSquared.get().getPlatform().equalsIgnoreCase("bukkit") ? + numeric : + Settings.Limit.MAX_PLOTS; + result = player.hasPermissionRange(perm, checkRange) >= numeric; + } + if (!result) { + player.sendMessage( + TranslatableCaption.of("permission.no_permission"), + TagResolver.resolver( + "node", + Tag.inserting(Component.text(perm + "." + numeric)) + ) + ); + } + return result; + } catch (NumberFormatException ignore) { + } + } else if (flag instanceof final ListFlag listFlag) { + try { + PlotFlag, ?> parsedFlag = listFlag.parse(value); + for (final Object entry : parsedFlag.getValue()) { + final String permission = Permission.PERMISSION_SET_FLAG_KEY_VALUE.format( + key.toLowerCase(), + entry.toString().toLowerCase() + ); + final boolean result = player.hasPermission(permission); + if (!result) { + player.sendMessage( + TranslatableCaption.of("permission.no_permission"), + TagResolver.resolver("node", Tag.inserting(Component.text(permission))) + ); + return false; + } + } + } catch (final FlagParseException e) { + player.sendMessage( + TranslatableCaption.of("flag.flag_parse_error"), + TagResolver.builder() + .tag("flag_name", Tag.inserting(Component.text(flag.getName()))) + .tag("flag_value", Tag.inserting(Component.text(e.getValue()))) + .tag("error", Tag.inserting(e.getErrorMessage().toComponent(player))) + .build() + ); + return false; + } catch (final Exception e) { + return false; + } + return true; + } + boolean result; + String basePerm = Permission.PERMISSION_SET_FLAG_KEY.format(key.toLowerCase()); + if (flag.isValuedPermission()) { + result = player.hasKeyedPermission(basePerm, value); + } else { + result = player.hasPermission(basePerm); + perm = basePerm; + } + if (!result) { + player.sendMessage( + TranslatableCaption.of("permission.no_permission"), + TagResolver.resolver("node", Tag.inserting(Component.text(perm))) + ); + } + return result; + } + + @Override + public final @NonNull CommandCategory category() { + return CommandCategory.SETTINGS; + } + + @Override + public @NonNull Set<@NonNull CommandRequirement> requirements() { + // TODO: Figure out how to handle the override permission check :) + return Set.of(CommonCommandRequirement.REQUIRES_PLOT, CommonCommandRequirement.REQUIRES_OWNER); + } + + @Override + protected final Command.@NonNull Builder> prepare(final Command.@NonNull Builder> builder) { + return builder.literal("flag"); + } +} diff --git a/Core/src/main/java/com/plotsquared/core/commands/command/setting/flag/FlagRemoveCommand.java b/Core/src/main/java/com/plotsquared/core/commands/command/setting/flag/FlagRemoveCommand.java new file mode 100644 index 000000000..173aabf37 --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/commands/command/setting/flag/FlagRemoveCommand.java @@ -0,0 +1,153 @@ +package com.plotsquared.core.commands.command.setting.flag; + +import cloud.commandframework.Command; +import cloud.commandframework.context.CommandContext; +import cloud.commandframework.keys.CloudKey; +import com.google.inject.Inject; +import com.plotsquared.core.commands.parser.PlotFlagParser; +import com.plotsquared.core.commands.suggestions.FlagValueSuggestionProvider; +import com.plotsquared.core.configuration.caption.TranslatableCaption; +import com.plotsquared.core.events.PlotFlagAddEvent; +import com.plotsquared.core.events.PlotFlagRemoveEvent; +import com.plotsquared.core.events.Result; +import com.plotsquared.core.permissions.Permission; +import com.plotsquared.core.player.PlotPlayer; +import com.plotsquared.core.plot.Plot; +import com.plotsquared.core.plot.flag.FlagParseException; +import com.plotsquared.core.plot.flag.PlotFlag; +import com.plotsquared.core.plot.flag.types.ListFlag; +import com.plotsquared.core.util.EventDispatcher; +import io.leangen.geantyref.TypeToken; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.tag.Tag; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import static cloud.commandframework.arguments.standard.StringParser.greedyStringParser; +import static com.plotsquared.core.commands.parser.PlotFlagParser.plotFlagParser; + +public final class FlagRemoveCommand extends FlagCommandBean { + + private static final CloudKey> COMPONENT_FLAG = CloudKey.of("flag", new TypeToken>() {}); + private static final CloudKey COMPONENT_VALUE = CloudKey.of("value", String.class); + + private final EventDispatcher eventDispatcher; + + @Inject + public FlagRemoveCommand(final @NonNull EventDispatcher eventDispatcher) { + this.eventDispatcher = eventDispatcher; + } + + @Override + protected Command.@NonNull Builder> configurePlotCommand( + final Command.@NonNull Builder> builder + ) { + return builder.literal("remove") + .required(COMPONENT_FLAG, plotFlagParser(PlotFlagParser.FlagSource.PLOT)) + .optional(COMPONENT_VALUE, greedyStringParser(), new FlagValueSuggestionProvider(COMPONENT_FLAG)); + } + + @Override + public void execute(final @NonNull CommandContext> commandContext) { + final PlotPlayer player = commandContext.sender(); + final Plot plot = commandContext.inject(Plot.class).orElseThrow(); + final PlotFlag flag = commandContext.get(COMPONENT_FLAG); + final String flagValue = commandContext.getOrDefault(COMPONENT_VALUE, null); + + final PlotFlagRemoveEvent event = this.eventDispatcher.callFlagRemove(flag, plot); + if (event.getEventResult() == Result.DENY) { + player.sendMessage( + TranslatableCaption.of("events.event_denied"), + TagResolver.resolver("value", Tag.inserting(Component.text("Flag set"))) + ); + return; + } + final String flagKey = flag.getName().toLowerCase(Locale.ENGLISH); + if (event.getEventResult() != Result.FORCE + && !player.hasPermission(Permission.PERMISSION_SET_FLAG_KEY.format(flagKey))) { + if (flagValue == null) { + player.sendMessage( + TranslatableCaption.of("permission.no_permission"), + TagResolver.resolver( + "node", + Tag.inserting(Component.text(Permission.PERMISSION_SET_FLAG_KEY.format(flagKey))) + ) + ); + return; + } + } + + if (flagValue != null && flag instanceof ListFlag listFlag) { + final List list = new ArrayList<>(plot.getFlag(listFlag)); + final PlotFlag parsedFlag; + try { + parsedFlag = listFlag.parse(flagValue); + } catch (final FlagParseException e) { + player.sendMessage( + TranslatableCaption.of("flag.flag_parse_error"), + TagResolver.builder() + .tag("flag_name", Tag.inserting(Component.text(flag.getName()))) + .tag("flag_value", Tag.inserting(Component.text(e.getValue()))) + .tag("error", Tag.inserting(e.getErrorMessage().toComponent(player))) + .build() + ); + return; + } + if (((List) parsedFlag.getValue()).isEmpty()) { + player.sendMessage(TranslatableCaption.of("flag.flag_not_removed")); + return; + } + if (list.removeAll((List) parsedFlag.getValue())) { + if (list.isEmpty()) { + if (plot.removeFlag(flag)) { + player.sendMessage( + TranslatableCaption.of("flag.flag_removed"), + TagResolver.builder() + .tag("flag", Tag.inserting(Component.text(flagKey))) + .tag("value", Tag.inserting(Component.text(flag.toString()))) + .build() + ); + return; + } else { + player.sendMessage(TranslatableCaption.of("flag.flag_not_removed")); + return; + } + } else { + PlotFlag plotFlag = parsedFlag.createFlagInstance(list); + PlotFlagAddEvent addEvent = eventDispatcher.callFlagAdd(plotFlag, plot); + if (addEvent.getEventResult() == Result.DENY) { + player.sendMessage( + TranslatableCaption.of("events.event_denied"), + TagResolver.resolver( + "value", + Tag.inserting(Component.text("Re-addition of " + plotFlag.getName())) + ) + ); + return; + } + if (plot.setFlag(addEvent.getFlag())) { + player.sendMessage(TranslatableCaption.of("flag.flag_partially_removed")); + return; + } else { + player.sendMessage(TranslatableCaption.of("flag.flag_not_removed")); + return; + } + } + } + } else if (!plot.removeFlag(flag)) { + player.sendMessage(TranslatableCaption.of("flag.flag_not_removed")); + return; + } + player.sendMessage( + TranslatableCaption.of("flag.flag_removed"), + TagResolver.builder() + .tag("flag", Tag.inserting(Component.text(flagKey))) + .tag("value", Tag.inserting(Component.text(flag.toString()))) + .build() + ); + } +} diff --git a/Core/src/main/java/com/plotsquared/core/commands/command/setting/flag/FlagSetCommand.java b/Core/src/main/java/com/plotsquared/core/commands/command/setting/flag/FlagSetCommand.java index 2a4d243ab..6e02ad0d9 100644 --- a/Core/src/main/java/com/plotsquared/core/commands/command/setting/flag/FlagSetCommand.java +++ b/Core/src/main/java/com/plotsquared/core/commands/command/setting/flag/FlagSetCommand.java @@ -4,123 +4,31 @@ import cloud.commandframework.Command; import cloud.commandframework.context.CommandContext; import cloud.commandframework.keys.CloudKey; import com.google.inject.Inject; -import com.plotsquared.core.PlotSquared; -import com.plotsquared.core.command.CommandCategory; -import com.plotsquared.core.commands.CommandRequirement; -import com.plotsquared.core.commands.CommonCommandRequirement; -import com.plotsquared.core.commands.PlotSquaredCommandBean; +import com.plotsquared.core.commands.parser.PlotFlagParser; import com.plotsquared.core.commands.suggestions.FlagValueSuggestionProvider; -import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.configuration.caption.CaptionUtility; import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.events.PlotFlagAddEvent; import com.plotsquared.core.events.Result; -import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.flag.FlagParseException; import com.plotsquared.core.plot.flag.PlotFlag; -import com.plotsquared.core.plot.flag.types.IntegerFlag; -import com.plotsquared.core.plot.flag.types.ListFlag; import com.plotsquared.core.util.EventDispatcher; -import com.plotsquared.core.util.MathMan; import io.leangen.geantyref.TypeToken; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.tag.Tag; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import org.checkerframework.checker.nullness.qual.NonNull; -import java.util.List; -import java.util.Set; - import static cloud.commandframework.arguments.standard.StringParser.greedyStringParser; import static com.plotsquared.core.commands.parser.PlotFlagParser.plotFlagParser; -public final class FlagSetCommand extends PlotSquaredCommandBean { +public final class FlagSetCommand extends FlagCommandBean { private static final CloudKey> COMPONENT_FLAG = CloudKey.of("flag", new TypeToken>() {}); private static final CloudKey COMPONENT_VALUE = CloudKey.of("value", String.class); - private static boolean checkPermValue( - final @NonNull PlotPlayer player, - final @NonNull PlotFlag flag, @NonNull String key, @NonNull String value - ) { - key = key.toLowerCase(); - value = value.toLowerCase(); - String perm = Permission.PERMISSION_SET_FLAG_KEY_VALUE.format(key.toLowerCase(), value.toLowerCase()); - if (flag instanceof IntegerFlag && MathMan.isInteger(value)) { - try { - int numeric = Integer.parseInt(value); - // Getting full permission without "." at the end - perm = perm.substring(0, perm.length() - value.length() - 1); - boolean result = false; - if (numeric >= 0) { - int checkRange = PlotSquared.get().getPlatform().equalsIgnoreCase("bukkit") ? - numeric : - Settings.Limit.MAX_PLOTS; - result = player.hasPermissionRange(perm, checkRange) >= numeric; - } - if (!result) { - player.sendMessage( - TranslatableCaption.of("permission.no_permission"), - TagResolver.resolver( - "node", - Tag.inserting(Component.text(perm + "." + numeric)) - ) - ); - } - return result; - } catch (NumberFormatException ignore) { - } - } else if (flag instanceof final ListFlag listFlag) { - try { - PlotFlag, ?> parsedFlag = listFlag.parse(value); - for (final Object entry : parsedFlag.getValue()) { - final String permission = Permission.PERMISSION_SET_FLAG_KEY_VALUE.format( - key.toLowerCase(), - entry.toString().toLowerCase() - ); - final boolean result = player.hasPermission(permission); - if (!result) { - player.sendMessage( - TranslatableCaption.of("permission.no_permission"), - TagResolver.resolver("node", Tag.inserting(Component.text(permission))) - ); - return false; - } - } - } catch (final FlagParseException e) { - player.sendMessage( - TranslatableCaption.of("flag.flag_parse_error"), - TagResolver.builder() - .tag("flag_name", Tag.inserting(Component.text(flag.getName()))) - .tag("flag_value", Tag.inserting(Component.text(e.getValue()))) - .tag("error", Tag.inserting(e.getErrorMessage().toComponent(player))) - .build() - ); - return false; - } catch (final Exception e) { - return false; - } - return true; - } - boolean result; - String basePerm = Permission.PERMISSION_SET_FLAG_KEY.format(key.toLowerCase()); - if (flag.isValuedPermission()) { - result = player.hasKeyedPermission(basePerm, value); - } else { - result = player.hasPermission(basePerm); - perm = basePerm; - } - if (!result) { - player.sendMessage( - TranslatableCaption.of("permission.no_permission"), - TagResolver.resolver("node", Tag.inserting(Component.text(perm))) - ); - } - return result; - } - private final EventDispatcher eventDispatcher; @Inject @@ -128,24 +36,12 @@ public final class FlagSetCommand extends PlotSquaredCommandBean { this.eventDispatcher = eventDispatcher; } - @Override - public @NonNull CommandCategory category() { - return CommandCategory.SETTINGS; - } - - @Override - public @NonNull Set<@NonNull CommandRequirement> requirements() { - // TODO: Figure out how to handle the override permission check :) - return Set.of(CommonCommandRequirement.REQUIRES_PLOT, CommonCommandRequirement.REQUIRES_OWNER); - } - @Override protected Command.@NonNull Builder> configurePlotCommand( final Command.@NonNull Builder> builder ) { - return builder.literal("flag") - .literal("set") - .required(COMPONENT_FLAG, plotFlagParser()) + return builder.literal("set") + .required(COMPONENT_FLAG, plotFlagParser(PlotFlagParser.FlagSource.GLOBAL)) .required(COMPONENT_VALUE, greedyStringParser(), new FlagValueSuggestionProvider(COMPONENT_FLAG)); } diff --git a/Core/src/main/java/com/plotsquared/core/commands/parser/PlotFlagParser.java b/Core/src/main/java/com/plotsquared/core/commands/parser/PlotFlagParser.java index b477f8df0..8d90aa32d 100644 --- a/Core/src/main/java/com/plotsquared/core/commands/parser/PlotFlagParser.java +++ b/Core/src/main/java/com/plotsquared/core/commands/parser/PlotFlagParser.java @@ -11,6 +11,8 @@ import cloud.commandframework.exceptions.parsing.ParserException; import com.plotsquared.core.configuration.caption.LocaleHolder; import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.player.PlotPlayer; +import com.plotsquared.core.plot.Plot; +import com.plotsquared.core.plot.flag.FlagContainer; import com.plotsquared.core.plot.flag.GlobalFlagContainer; import com.plotsquared.core.plot.flag.InternalFlag; import com.plotsquared.core.plot.flag.PlotFlag; @@ -20,6 +22,9 @@ import net.kyori.adventure.util.ComponentMessageThrowable; import org.checkerframework.checker.nullness.qual.NonNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; +import java.util.function.Function; + /** * Parser that parses and suggests {@link PlotFlag plot flags}. */ @@ -29,13 +34,20 @@ public final class PlotFlagParser implements ArgumentParser, PlotF /** * Returns a new parser that parses {@link PlotFlag plot flags}. * + * @param source the source of available flag values * @return the parser */ - public static @NonNull ParserDescriptor, PlotFlag> plotFlagParser() { - return ParserDescriptor.of(new PlotFlagParser(), new TypeToken>() { + public static @NonNull ParserDescriptor, PlotFlag> plotFlagParser(final @NonNull FlagSource source) { + return ParserDescriptor.of(new PlotFlagParser(source), new TypeToken>() { }); } + private final FlagSource flagSource; + + private PlotFlagParser(final @NonNull FlagSource flagSource) { + this.flagSource = flagSource; + } + @Override public @NonNull ArgumentParseResult<@NonNull PlotFlag> parse( final @NonNull CommandContext<@NonNull PlotPlayer> commandContext, @@ -54,8 +66,7 @@ public final class PlotFlagParser implements ArgumentParser, PlotF final @NonNull CommandContext> context, final @NonNull CommandInput input ) { - return GlobalFlagContainer.getInstance() - .getRecognizedPlotFlags() + return this.flagSource.flags(context.sender()) .stream() .filter(flag -> (!(flag instanceof InternalFlag))) .map(PlotFlag::getName) @@ -63,6 +74,55 @@ public final class PlotFlagParser implements ArgumentParser, PlotF .toList(); } + public enum FlagSource { + /** + * All recognized flags. + */ + GLOBAL(player -> GlobalFlagContainer.getInstance(), false), + /** + * All flags that have been configured in the current plot. + */ + PLOT(player -> { + final Plot plot = player.getCurrentPlot(); + if (plot == null) { + return GlobalFlagContainer.getInstance(); + } + return plot.getFlagContainer(); + }, true); + + private final Function, FlagContainer> containerFunction; + private final boolean storedOnly; + + FlagSource(final @NonNull Function, FlagContainer> containerFunction, final boolean storedOnly) { + this.containerFunction = containerFunction; + this.storedOnly = storedOnly; + } + + /** + * Returns the flag container. + * + * @param player the player to get the container for + * @return the container + */ + public @NonNull FlagContainer flagContainer(final @NonNull PlotPlayer player) { + return this.containerFunction.apply(player); + } + + /** + * Returns the flags from this source. + * + * @param player the player to get the flags for + * @return the flags + */ + public @NonNull Collection<@NonNull PlotFlag> flags(final @NonNull PlotPlayer player) { + final FlagContainer container = this.flagContainer(player); + if (this.storedOnly) { + return container.getFlagMap().values(); + } + return container.getRecognizedPlotFlags(); + } + } + /** * Exception thrown when an invalid flag name is supplied. */ diff --git a/Core/src/main/java/com/plotsquared/core/inject/modules/CommandModule.java b/Core/src/main/java/com/plotsquared/core/inject/modules/CommandModule.java index 0b72a9724..80125380c 100644 --- a/Core/src/main/java/com/plotsquared/core/inject/modules/CommandModule.java +++ b/Core/src/main/java/com/plotsquared/core/inject/modules/CommandModule.java @@ -4,6 +4,7 @@ import com.google.inject.AbstractModule; import com.google.inject.Scopes; import com.google.inject.multibindings.Multibinder; import com.plotsquared.core.commands.PlotSquaredCommandBean; +import com.plotsquared.core.commands.command.setting.flag.FlagRemoveCommand; import com.plotsquared.core.commands.command.setting.flag.FlagSetCommand; public final class CommandModule extends AbstractModule { @@ -16,5 +17,6 @@ public final class CommandModule extends AbstractModule { ); commands.addBinding().to(FlagSetCommand.class).in(Scopes.SINGLETON); + commands.addBinding().to(FlagRemoveCommand.class).in(Scopes.SINGLETON); } }