Skip to content

Commit

Permalink
improve the requirement system
Browse files Browse the repository at this point in the history
  • Loading branch information
Citymonstret committed Jan 1, 2024
1 parent b30b695 commit 982f38a
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
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;
Expand Down Expand Up @@ -83,7 +82,6 @@ protected void configure() {
}

final CommandRequirementPostprocessor requirementPostprocessor = new CommandRequirementPostprocessor();
requirementPostprocessor.registerRequirements(CommonCommandRequirement.values());
commandManager.registerCommandPostProcessor(requirementPostprocessor);

// TODO(City): Override parsing errors using MM parsing.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Boolean> {
public interface CommandRequirement {

/**
* Returns the caption sent when the requirement is not met.
Expand All @@ -43,4 +45,42 @@ public interface CommandRequirement extends CloudKeyHolder<Boolean> {
* @return {@code true} if the requirement is met, else {@code false}
*/
boolean evaluate(final @NonNull CommandContext<PlotPlayer<?>> 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<PlotPlayer<?>> context) {
return context.sender().hasPermission(permission) || thisRequirement.evaluate(context);
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* PlotSquared, a land and world management plugin for Minecraft.
* Copyright (C) IntellectualSites <https://intellectualsites.com>
* 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 <https://www.gnu.org/licenses/>.
*/
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<CommandRequirements> 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<CommandRequirement> 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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<CommandContext<PlotPlayer<?>>> predicate;
Expand All @@ -60,9 +66,4 @@ public enum CommonCommandRequirement implements CommandRequirement {
public boolean evaluate(final @NonNull CommandContext<PlotPlayer<?>> context) {
return this.predicate.test(context);
}

@Override
public @NonNull CloudKey<Boolean> key() {
return CloudKey.of(String.format("requirement_%s", this.name()), Boolean.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,8 @@ public abstract class PlotSquaredCommandBean extends CommandBean<PlotPlayer<?>>

@Override
protected final Command.@NonNull Builder<PlotPlayer<?>> configure(final Command.@NonNull Builder<PlotPlayer<?>> builder) {
Command.@NonNull Builder<PlotPlayer<?>> 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<PlotPlayer<?>> configurePlotCommand(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,11 @@ protected static boolean checkPermValue(

@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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,58 +22,29 @@
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<PlotPlayer<?>> {

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<PlotPlayer<?>> 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();
}
Expand Down

0 comments on commit 982f38a

Please sign in to comment.