mirror of
https://github.com/IntellectualSites/PlotSquared.git
synced 2024-11-22 13:16:45 +01:00
implement a command and write a sample parser
This commit is contained in:
parent
f3f44b55c4
commit
f175c24523
@ -0,0 +1,86 @@
|
||||
package com.plotsquared.core.commands;
|
||||
|
||||
import cloud.commandframework.annotations.Argument;
|
||||
import cloud.commandframework.annotations.CommandMethod;
|
||||
import cloud.commandframework.annotations.CommandPermission;
|
||||
import com.google.inject.Inject;
|
||||
import com.plotsquared.core.commands.arguments.PlotMember;
|
||||
import com.plotsquared.core.commands.requirements.Requirement;
|
||||
import com.plotsquared.core.commands.requirements.RequirementType;
|
||||
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.Plot;
|
||||
import com.plotsquared.core.util.EventDispatcher;
|
||||
import com.plotsquared.core.util.PlayerManager;
|
||||
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;
|
||||
|
||||
class CommandAdd implements PlotSquaredCommandContainer {
|
||||
|
||||
private final EventDispatcher eventDispatcher;
|
||||
|
||||
@Inject
|
||||
CommandAdd(final @NonNull EventDispatcher eventDispatcher) {
|
||||
this.eventDispatcher = eventDispatcher;
|
||||
}
|
||||
|
||||
@Requirement(RequirementType.PLAYER)
|
||||
@Requirement(RequirementType.IS_OWNER)
|
||||
@CommandPermission("plots.add")
|
||||
@CommandMethod("plot add [target]")
|
||||
public void commandAdd(
|
||||
final @NonNull PlotPlayer<?> sender,
|
||||
@Argument("target") final PlotMember target,
|
||||
final @NonNull Plot plot
|
||||
) {
|
||||
if (target instanceof PlotMember.Everyone) {
|
||||
if (!sender.hasPermission(Permission.PERMISSION_TRUST_EVERYONE) && !sender.hasPermission(Permission.PERMISSION_ADMIN_COMMAND_TRUST)) {
|
||||
sender.sendMessage(
|
||||
TranslatableCaption.of("errors.invalid_player"),
|
||||
TagResolver.resolver("value", Tag.inserting(
|
||||
PlayerManager.resolveName(target.uuid()).toComponent(sender)
|
||||
))
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else if (plot.isOwner(target.uuid())) {
|
||||
sender.sendMessage(
|
||||
TranslatableCaption.of("member.already_added"),
|
||||
TagResolver.resolver("player", Tag.inserting(
|
||||
PlayerManager.resolveName(target.uuid()).toComponent(sender)
|
||||
))
|
||||
);
|
||||
return;
|
||||
} else if (plot.getMembers().contains(target.uuid())) {
|
||||
sender.sendMessage(
|
||||
TranslatableCaption.of("member.already_added"),
|
||||
TagResolver.resolver("player", Tag.inserting(
|
||||
PlayerManager.resolveName(target.uuid()).toComponent(sender)
|
||||
))
|
||||
);
|
||||
return;
|
||||
} else if (plot.getMembers().size() >= sender.hasPermissionRange(Permission.PERMISSION_ADD, Settings.Limit.MAX_PLOTS)) {
|
||||
sender.sendMessage(
|
||||
TranslatableCaption.of("members.plot_max_members_added"),
|
||||
TagResolver.resolver("amount", Tag.inserting(Component.text(plot.getMembers().size())))
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (target instanceof PlotMember.Player) {
|
||||
if (!plot.removeTrusted(target.uuid())) {
|
||||
if (plot.getDenied().contains(target.uuid())) {
|
||||
plot.removeDenied(target.uuid());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plot.addMember(target.uuid());
|
||||
this.eventDispatcher.callMember(sender, plot, target.uuid(), true);
|
||||
sender.sendMessage(TranslatableCaption.of("member.member_added"));
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package com.plotsquared.core.commands;
|
||||
|
||||
import cloud.commandframework.captions.Caption;
|
||||
|
||||
public final class PlotSquaredCaptionKeys {
|
||||
|
||||
public static final Caption ARGUMENT_PARSE_FAILURE_TARGET = Caption.of("argument.parse.failure.target");
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package com.plotsquared.core.commands;
|
||||
|
||||
/**
|
||||
* Indicates that a class contains commands.
|
||||
*/
|
||||
public interface PlotSquaredCommandContainer {
|
||||
|
||||
}
|
@ -23,6 +23,7 @@ import cloud.commandframework.annotations.AnnotationParser;
|
||||
import cloud.commandframework.meta.SimpleCommandMeta;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.plotsquared.core.commands.parsers.PlotMemberParser;
|
||||
import com.plotsquared.core.player.PlotPlayer;
|
||||
import io.leangen.geantyref.TypeToken;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
@ -63,8 +64,13 @@ public class PlotSquaredCommandManager {
|
||||
* Initializes all the known commands.
|
||||
*/
|
||||
public void initializeCommands() {
|
||||
final Stream<Class<?>> commandClasses = Stream.of(
|
||||
);
|
||||
commandClasses.map(injector::getInstance).forEach(this::scanClass);
|
||||
// We start by scanning the parsers.
|
||||
Stream.of(
|
||||
PlotMemberParser.class
|
||||
).map(this.injector::getInstance).forEach(this::scanClass);
|
||||
// Then we scan the commands.
|
||||
Stream.of(
|
||||
CommandAdd.class
|
||||
).map(this.injector::getInstance).forEach(this::scanClass);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,76 @@
|
||||
package com.plotsquared.core.commands.arguments;
|
||||
|
||||
import cloud.commandframework.context.CommandContext;
|
||||
import com.plotsquared.core.commands.parsers.PlotMemberParser;
|
||||
import com.plotsquared.core.database.DBFunc;
|
||||
import com.plotsquared.core.player.PlotPlayer;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public sealed interface PlotMember {
|
||||
|
||||
PlotMember EVERYONE = new Everyone();
|
||||
|
||||
default @NonNull UUID uuid(@NonNull CommandContext<PlotPlayer<?>> context) {
|
||||
return this.uuid();
|
||||
}
|
||||
|
||||
@NonNull UUID uuid();
|
||||
|
||||
sealed interface PlayerLike extends PlotMember {
|
||||
}
|
||||
|
||||
record Player(@NonNull UUID uuid) implements PlayerLike {
|
||||
}
|
||||
|
||||
final class LazyPlayer implements PlayerLike {
|
||||
|
||||
private final String candidate;
|
||||
private final UuidSupplier uuidSupplier;
|
||||
private @MonotonicNonNull UUID cachedUuid = null;
|
||||
|
||||
public LazyPlayer(
|
||||
final @NonNull String candidate,
|
||||
final @NonNull UuidSupplier uuidSupplier
|
||||
) {
|
||||
this.candidate = candidate;
|
||||
this.uuidSupplier = uuidSupplier;
|
||||
}
|
||||
|
||||
public @NonNull UUID uuid() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized @NonNull UUID uuid(final @NonNull CommandContext<PlotPlayer<?>> context) {
|
||||
if (this.cachedUuid == null) {
|
||||
try {
|
||||
this.cachedUuid = this.uuidSupplier.uuid();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
// The player didn't exist :-(
|
||||
if (this.cachedUuid == null) {
|
||||
throw new PlotMemberParser.TargetParseException(this.candidate, context);
|
||||
}
|
||||
}
|
||||
return this.cachedUuid;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface UuidSupplier {
|
||||
@Nullable UUID uuid() throws Exception;
|
||||
}
|
||||
}
|
||||
|
||||
final class Everyone implements PlotMember {
|
||||
|
||||
@Override
|
||||
public @NonNull UUID uuid() {
|
||||
return DBFunc.EVERYONE;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
package com.plotsquared.core.commands.parsers;
|
||||
|
||||
import cloud.commandframework.annotations.parsers.Parser;
|
||||
import cloud.commandframework.captions.CaptionVariable;
|
||||
import cloud.commandframework.context.CommandContext;
|
||||
import cloud.commandframework.exceptions.parsing.NoInputProvidedException;
|
||||
import cloud.commandframework.exceptions.parsing.ParserException;
|
||||
import com.google.inject.Inject;
|
||||
import com.plotsquared.core.commands.PlotSquaredCaptionKeys;
|
||||
import com.plotsquared.core.commands.arguments.PlotMember;
|
||||
import com.plotsquared.core.configuration.Settings;
|
||||
import com.plotsquared.core.inject.annotations.ImpromptuPipeline;
|
||||
import com.plotsquared.core.player.PlotPlayer;
|
||||
import com.plotsquared.core.uuid.UUIDMapping;
|
||||
import com.plotsquared.core.uuid.UUIDPipeline;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PlotMemberParser {
|
||||
|
||||
private final UUIDPipeline uuidPipeline;
|
||||
|
||||
@Inject
|
||||
public PlotMemberParser(@ImpromptuPipeline final @NonNull UUIDPipeline uuidPipeline) {
|
||||
this.uuidPipeline = uuidPipeline;
|
||||
}
|
||||
|
||||
@Parser
|
||||
public @NonNull PlotMember parse(
|
||||
final @NonNull CommandContext<PlotPlayer<?>> context,
|
||||
final @NonNull Queue<@NonNull String> input
|
||||
) {
|
||||
final var candidate = input.peek();
|
||||
if (candidate == null) {
|
||||
throw new NoInputProvidedException(this.getClass(), context);
|
||||
}
|
||||
|
||||
if ("*".equals(candidate)) {
|
||||
return PlotMember.EVERYONE;
|
||||
} else if (candidate.length() > 16) {
|
||||
try {
|
||||
return new PlotMember.Player(UUID.fromString(candidate));
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
throw new TargetParseException(candidate, context);
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings.Paper_Components.PAPER_LISTENERS) {
|
||||
try {
|
||||
return this.uuidPipeline.getUUID(candidate, Settings.UUID.NON_BLOCKING_TIMEOUT)
|
||||
.get()
|
||||
.map(UUIDMapping::getUuid)
|
||||
.map(PlotMember.Player::new)
|
||||
.orElseThrow();
|
||||
} catch (Exception e) {
|
||||
throw new TargetParseException(candidate, context);
|
||||
}
|
||||
} else {
|
||||
return new PlotMember.LazyPlayer(
|
||||
candidate,
|
||||
() -> this.uuidPipeline.getUUID(candidate, Settings.UUID.NON_BLOCKING_TIMEOUT)
|
||||
.get()
|
||||
.map(UUIDMapping::getUuid)
|
||||
.orElse(null)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class TargetParseException extends ParserException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 927476591631527552L;
|
||||
private final String input;
|
||||
|
||||
/**
|
||||
* Construct a new Player parse exception
|
||||
*
|
||||
* @param input String input
|
||||
* @param context Command context
|
||||
*/
|
||||
public TargetParseException(
|
||||
final @NonNull String input,
|
||||
final @NonNull CommandContext<?> context
|
||||
) {
|
||||
super(
|
||||
PlotMemberParser.class,
|
||||
context,
|
||||
PlotSquaredCaptionKeys.ARGUMENT_PARSE_FAILURE_TARGET,
|
||||
CaptionVariable.of("input", input)
|
||||
);
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the supplied input
|
||||
*
|
||||
* @return String value
|
||||
*/
|
||||
public @NonNull String getInput() {
|
||||
return this.input;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.plotsquared.core.commands.requirements;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Repeatable(Requirements.class)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Requirement {
|
||||
|
||||
@NonNull RequirementType value();
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package com.plotsquared.core.commands.requirements;
|
||||
|
||||
import com.plotsquared.core.configuration.caption.Caption;
|
||||
import com.plotsquared.core.configuration.caption.TranslatableCaption;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
public enum RequirementType {
|
||||
PLAYER(""),
|
||||
IN_PLOT("errors.not_in_plot"),
|
||||
PLOT_HAS_OWNER("info.plot_unowned", IN_PLOT),
|
||||
IS_OWNER("permission.no_plot_perms", PLOT_HAS_OWNER);
|
||||
|
||||
private final Caption caption;
|
||||
private @NonNull Set<@NonNull RequirementType> inheritedRequirements;
|
||||
|
||||
RequirementType(
|
||||
final String caption,
|
||||
final @NonNull RequirementType... inheritedRequirements
|
||||
) {
|
||||
this.caption = TranslatableCaption.of(caption);
|
||||
this.inheritedRequirements = EnumSet.copyOf(Arrays.asList(inheritedRequirements));
|
||||
}
|
||||
|
||||
public @NonNull Set<@NonNull RequirementType> inheritedRequirements() {
|
||||
return Collections.unmodifiableSet(this.inheritedRequirements);
|
||||
}
|
||||
|
||||
public @NonNull Caption caption() {
|
||||
return this.caption;
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.plotsquared.core.commands.requirements;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Requirements {
|
||||
|
||||
@NonNull Requirement @NonNull[] value();
|
||||
}
|
@ -36,6 +36,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@ -273,6 +274,25 @@ public class UUIDPipeline {
|
||||
return this.getUUIDs(requests).orTimeout(timeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously attempt to fetch the mapping from a name.
|
||||
* <p>
|
||||
* This will timeout after the specified time and throws a {@link TimeoutException}
|
||||
* if this happens
|
||||
*
|
||||
* @param username Name
|
||||
* @param timeout Timeout in milliseconds
|
||||
* @return Mapping
|
||||
*/
|
||||
public @NonNull CompletableFuture<Optional<UUIDMapping>> getUUID(
|
||||
final @NonNull String username,
|
||||
final long timeout
|
||||
) {
|
||||
return this.getUUIDs(List.of(username), timeout).thenApply(
|
||||
results -> results.stream().findFirst()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously attempt to fetch the mapping from a list of UUIDs
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user