From 24d7fe626da3786259b09d64dbf0dde74cc5bac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Mon, 1 Jan 2024 17:31:04 +0100 Subject: [PATCH] improve the requirement system --- .../bukkit/inject/CloudModule.java | 2 - .../core/commands/CommandRequirement.java | 44 ++++++++++++- .../core/commands/CommandRequirements.java | 61 +++++++++++++++++++ .../commands/CommonCommandRequirement.java | 15 ++--- .../core/commands/PlotSquaredCommandBean.java | 8 +-- .../command/setting/flag/FlagCommandBean.java | 7 ++- .../CommandRequirementPostprocessor.java | 49 +++------------ 7 files changed, 128 insertions(+), 58 deletions(-) create mode 100644 Core/src/main/java/com/plotsquared/core/commands/CommandRequirements.java diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/inject/CloudModule.java b/Bukkit/src/main/java/com/plotsquared/bukkit/inject/CloudModule.java index afc03eed2..9528ff3ae 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/inject/CloudModule.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/inject/CloudModule.java @@ -28,7 +28,6 @@ import com.google.inject.Key; import com.google.inject.TypeLiteral; import com.plotsquared.bukkit.BukkitPlatform; import com.plotsquared.bukkit.util.BukkitUtil; -import com.plotsquared.core.commands.CommonCommandRequirement; import com.plotsquared.core.commands.PlotSquaredCaptionProvider; import com.plotsquared.core.commands.processing.CommandRequirementPostprocessor; import com.plotsquared.core.configuration.caption.TranslatableCaption; @@ -83,7 +82,6 @@ public class CloudModule extends AbstractModule { } final CommandRequirementPostprocessor requirementPostprocessor = new CommandRequirementPostprocessor(); - requirementPostprocessor.registerRequirements(CommonCommandRequirement.values()); commandManager.registerCommandPostProcessor(requirementPostprocessor); // TODO(City): Override parsing errors using MM parsing. diff --git a/Core/src/main/java/com/plotsquared/core/commands/CommandRequirement.java b/Core/src/main/java/com/plotsquared/core/commands/CommandRequirement.java index 7bef53a1e..11bc1321b 100644 --- a/Core/src/main/java/com/plotsquared/core/commands/CommandRequirement.java +++ b/Core/src/main/java/com/plotsquared/core/commands/CommandRequirement.java @@ -19,15 +19,17 @@ package com.plotsquared.core.commands; import cloud.commandframework.context.CommandContext; -import cloud.commandframework.keys.CloudKeyHolder; import com.plotsquared.core.configuration.caption.TranslatableCaption; +import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.player.PlotPlayer; +import net.kyori.adventure.text.minimessage.tag.Tag; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import org.checkerframework.checker.nullness.qual.NonNull; /** * Something that is required for a command to be executed. */ -public interface CommandRequirement extends CloudKeyHolder { +public interface CommandRequirement { /** * Returns the caption sent when the requirement is not met. @@ -43,4 +45,42 @@ public interface CommandRequirement extends CloudKeyHolder { * @return {@code true} if the requirement is met, else {@code false} */ boolean evaluate(final @NonNull CommandContext> context); + + /** + * Returns the placeholder values. + * + * @return placeholder values + */ + default @NonNull TagResolver @NonNull[] tagResolvers() { + return new TagResolver[0]; + } + + /** + * Returns a requirement that evaluates to {@code true} if the sender has the given {@code permission} or if + * this requirement evaluates to {@code true}. + * + * @param permission the override permission + * @return the new requirement + */ + default @NonNull CommandRequirement withPermissionOverride(final @NonNull Permission permission) { + final CommandRequirement thisRequirement = this; + return new CommandRequirement() { + @Override + public @NonNull TranslatableCaption failureCaption() { + return TranslatableCaption.of("permission.no_permission"); + } + + @Override + public @NonNull TagResolver @NonNull [] tagResolvers() { + return new TagResolver[] { + TagResolver.resolver("node", Tag.inserting(Permission.PERMISSION_SET_FLAG_OTHER)) + }; + } + + @Override + public boolean evaluate(final @NonNull CommandContext> context) { + return context.sender().hasPermission(permission) || thisRequirement.evaluate(context); + } + }; + } } diff --git a/Core/src/main/java/com/plotsquared/core/commands/CommandRequirements.java b/Core/src/main/java/com/plotsquared/core/commands/CommandRequirements.java new file mode 100644 index 000000000..046c09b26 --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/commands/CommandRequirements.java @@ -0,0 +1,61 @@ +/* + * PlotSquared, a land and world management plugin for Minecraft. + * Copyright (C) IntellectualSites + * Copyright (C) IntellectualSites team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.plotsquared.core.commands; + +import cloud.commandframework.keys.CloudKey; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +/** + * Holder of {@link CommandRequirement} requirements. + */ +public final class CommandRequirements implements Iterable<@NonNull CommandRequirement> { + + /** + * The key used to store the requirements in the {@link cloud.commandframework.meta.CommandMeta}. + */ + public static final CloudKey REQUIREMENTS_KEY = CloudKey.of( + "requirements", + CommandRequirements.class + ); + + /** + * Creates a new instance. + * + * @param requirements the requirements + * @return the instance + */ + public static @NonNull CommandRequirements create(final @NonNull Collection<@NonNull CommandRequirement> requirements) { + return new CommandRequirements(requirements); + } + + private final List requirements; + + private CommandRequirements(final @NonNull Collection<@NonNull CommandRequirement> requirements) { + this.requirements = List.copyOf(requirements); + } + + @Override + public @NonNull Iterator<@NonNull CommandRequirement> iterator() { + return this.requirements.iterator(); + } +} diff --git a/Core/src/main/java/com/plotsquared/core/commands/CommonCommandRequirement.java b/Core/src/main/java/com/plotsquared/core/commands/CommonCommandRequirement.java index 2dd69814d..93f55c5f5 100644 --- a/Core/src/main/java/com/plotsquared/core/commands/CommonCommandRequirement.java +++ b/Core/src/main/java/com/plotsquared/core/commands/CommonCommandRequirement.java @@ -19,7 +19,6 @@ package com.plotsquared.core.commands; import cloud.commandframework.context.CommandContext; -import cloud.commandframework.keys.CloudKey; import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.player.PlotPlayer; import org.checkerframework.checker.nullness.qual.NonNull; @@ -39,7 +38,14 @@ public enum CommonCommandRequirement implements CommandRequirement { */ REQUIRES_OWNER(TranslatableCaption.of("working.plot_not_claimed"), ctx -> ctx.sender().getCurrentPlot() != null && ctx.sender().getCurrentPlot().hasOwner() - ); + ), + /** + * Requires that the command sender is the plot owner. + */ + IS_OWNER(TranslatableCaption.of("permission.no_plot_perms"), + ctx -> ctx.sender().getCurrentPlot() != null && ctx.sender().getCurrentPlot().isOwner(ctx.sender().getUUID()) + ) + ; private final TranslatableCaption failureCaption; private final Predicate>> predicate; @@ -60,9 +66,4 @@ public enum CommonCommandRequirement implements CommandRequirement { public boolean evaluate(final @NonNull CommandContext> context) { return this.predicate.test(context); } - - @Override - public @NonNull CloudKey key() { - return CloudKey.of(String.format("requirement_%s", this.name()), Boolean.class); - } } 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 f0726a147..60dddd855 100644 --- a/Core/src/main/java/com/plotsquared/core/commands/PlotSquaredCommandBean.java +++ b/Core/src/main/java/com/plotsquared/core/commands/PlotSquaredCommandBean.java @@ -63,12 +63,8 @@ public abstract class PlotSquaredCommandBean extends CommandBean> @Override protected final Command.@NonNull Builder> configure(final Command.@NonNull Builder> builder) { - Command.@NonNull Builder> intermediaryBuilder = - this.configurePlotCommand(this.prepare(builder.meta(PlotSquaredCommandMeta.META_CATEGORY, this.category()))); - for (final CommandRequirement requirement : this.requirements()) { - intermediaryBuilder = intermediaryBuilder.meta(requirement.key(), true); - } - return intermediaryBuilder; + return this.configurePlotCommand(this.prepare(builder.meta(PlotSquaredCommandMeta.META_CATEGORY, this.category()))) + .meta(CommandRequirements.REQUIREMENTS_KEY, CommandRequirements.create(this.requirements())); } protected abstract Command.@NonNull Builder> configurePlotCommand( 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 index a712d8c22..ae6445e43 100644 --- 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 @@ -130,8 +130,11 @@ public abstract class FlagCommandBean extends PlotSquaredCommandBean { @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); + return Set.of( + CommonCommandRequirement.REQUIRES_PLOT, + CommonCommandRequirement.REQUIRES_OWNER, + CommonCommandRequirement.IS_OWNER.withPermissionOverride(Permission.PERMISSION_SET_FLAG_OTHER) + ); } @Override diff --git a/Core/src/main/java/com/plotsquared/core/commands/processing/CommandRequirementPostprocessor.java b/Core/src/main/java/com/plotsquared/core/commands/processing/CommandRequirementPostprocessor.java index 57154294b..36fad14d1 100644 --- a/Core/src/main/java/com/plotsquared/core/commands/processing/CommandRequirementPostprocessor.java +++ b/Core/src/main/java/com/plotsquared/core/commands/processing/CommandRequirementPostprocessor.java @@ -22,58 +22,29 @@ import cloud.commandframework.execution.postprocessor.CommandPostprocessingConte import cloud.commandframework.execution.postprocessor.CommandPostprocessor; import cloud.commandframework.services.types.ConsumerService; import com.plotsquared.core.commands.CommandRequirement; +import com.plotsquared.core.commands.CommandRequirements; import com.plotsquared.core.player.PlotPlayer; import org.checkerframework.checker.nullness.qual.NonNull; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Objects; - /** * Processor that evaluates registered {@link CommandRequirement command requirements} before a command is executed. */ public final class CommandRequirementPostprocessor implements CommandPostprocessor> { - private final Collection<@NonNull CommandRequirement> requirements = new ArrayList<>(); - - /** - * Requires a single requirement. - * - * @param requirement the requirement - */ - public void registerRequirement(final @NonNull CommandRequirement requirement) { - this.requirements.add(Objects.requireNonNull(requirement, "requirement")); - } - - /** - * Registers the given {@code requirements}. - * - * @param requirements the requirements - */ - public void registerRequirements(final @NonNull Collection<@NonNull CommandRequirement> requirements) { - requirements.forEach(this::registerRequirement); - } - - /** - * Registers the given {@code requirements}. - * - * @param requirements the requirements - */ - public void registerRequirements(final @NonNull CommandRequirement @NonNull... requirements) { - this.registerRequirements(Arrays.asList(requirements)); - } - @Override public void accept(final @NonNull CommandPostprocessingContext> processingContext) { - for (final CommandRequirement requirement : this.requirements) { - if (!processingContext.command().commandMeta().getOrDefault(requirement.key(), false)) { - continue; - } + final CommandRequirements requirements = processingContext.command().commandMeta().getOrDefault( + CommandRequirements.REQUIREMENTS_KEY, + null + ); + if (requirements == null) { + return; + } + for (final CommandRequirement requirement : requirements) { if (requirement.evaluate(processingContext.commandContext())) { continue; } - processingContext.commandContext().sender().sendMessage(requirement.failureCaption()); + processingContext.commandContext().sender().sendMessage(requirement.failureCaption(), requirement.tagResolvers()); // Not allowed :( ConsumerService.interrupt(); }