diff --git a/README.md b/README.md index a0a1c89..7f14766 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Versions are available on maven in the format `net.william278.uniform:ARTIFACT:VERSION`. See below for a table of supported platforms. -Note that Uniform versions omit the `v` prefix. Fabric versions are suffixed with the target Minecraft version (e.g. `1.2+1.21`) and also require Fabric API installed on the server. Sponge versions are suffixed with the target Sponge API version (e.g. `1.2+11`). +Note that Uniform versions omit the `v` prefix. Fabric versions are suffixed with the target Minecraft version (e.g. `1.2.1+1.21`) and also require Fabric API installed on the server. Sponge versions are suffixed with the target Sponge API version (e.g. `1.2.1+11`). @@ -89,7 +89,7 @@ Note that Uniform versions omit the `v` prefix. Fabric versions are suffixed wit
-Example: To target Uniform on Bukkit, the artifact is `net.william278.uniform:uniform-bukkit:1.2` (check that this version is up-to-date – make sure you target the latest available!). +Example: To target Uniform on Bukkit, the artifact is `net.william278.uniform:uniform-bukkit:1.2.1` (check that this version is up-to-date – make sure you target the latest available!). ## Setup Uniform is available [on Maven](https://repo.william278.net/#/releases/net/william278/uniform/). You can browse the Javadocs [here](https://repo.william278.net/javadoc/releases/net/william278/uniform/latest). @@ -104,7 +104,7 @@ repositories { } ``` -Then, add the dependency itself. Replace `VERSION` with the latest release version. (e.g., `1.2`) and `PLATFORM` with the platform you are targeting (e.g., `paper`). If you want to target pre-release "snapshot" versions (not recommended), you should use the `/snapshots` repository instead. +Then, add the dependency itself. Replace `VERSION` with the latest release version. (e.g., `1.2.1`) and `PLATFORM` with the platform you are targeting (e.g., `paper`). If you want to target pre-release "snapshot" versions (not recommended), you should use the `/snapshots` repository instead. ```groovy dependencies { diff --git a/common/src/main/java/net/william278/uniform/BaseCommand.java b/common/src/main/java/net/william278/uniform/BaseCommand.java index 5ad9026..7c5a83c 100644 --- a/common/src/main/java/net/william278/uniform/BaseCommand.java +++ b/common/src/main/java/net/william278/uniform/BaseCommand.java @@ -55,6 +55,7 @@ public abstract class BaseCommand { this.name = command.getName(); this.aliases = command.getAliases(); this.description = command.getDescription(); + setExecutionScope(command.getScope()); command.getPermission().ifPresent(this::setPermission); command.provide(this); } @@ -79,8 +80,16 @@ public abstract class BaseCommand { this.condition = condition; } + public final void addCondition(@NotNull Predicate condition) { + if (this.condition == null) { + this.condition = condition; + } else { + this.condition = this.condition.and(condition); + } + } + public void setPermission(@NotNull Permission permission) { - this.setCondition(this.createPermission(permission)); + this.addCondition(this.createPermission(permission)); } public final void setPermission(@NotNull String permission) { @@ -91,6 +100,13 @@ public abstract class BaseCommand { this.setPermission(new Permission(permission, permissionDefault)); } + public final void setExecutionScope(@NotNull Command.ExecutionScope scope) { + final Predicate predicate = scope.toPredicate(this); + if (predicate != null) { + this.addCondition(predicate); + } + } + public final void setDefaultExecutor(@NotNull CommandExecutor executor) { this.defaultExecutor = executor; } @@ -130,6 +146,12 @@ public abstract class BaseCommand { this.addSubCommand(new Command.SubCommand(name, aliases, permission, provider).command()); } + public final void addSubCommand(@NotNull String name, @NotNull List aliases, + @NotNull Permission permission, @NotNull Command.ExecutionScope scope, + @NotNull CommandProvider provider) { + this.addSubCommand(new Command.SubCommand(name, aliases, permission, scope, provider).command()); + } + public abstract void addSubCommand(@NotNull Command command); public abstract Uniform getUniform(); diff --git a/common/src/main/java/net/william278/uniform/Command.java b/common/src/main/java/net/william278/uniform/Command.java index 15b1e6f..81e4243 100644 --- a/common/src/main/java/net/william278/uniform/Command.java +++ b/common/src/main/java/net/william278/uniform/Command.java @@ -55,6 +55,7 @@ public abstract class Command implements CommandProvider { private String description = ""; @Getter(AccessLevel.NONE) private @Nullable Permission permission = null; + private ExecutionScope scope = ExecutionScope.ALL; public Optional getPermission() { return Optional.ofNullable(permission); @@ -71,9 +72,29 @@ public abstract class Command implements CommandProvider { this.name = node.value(); this.aliases = List.of(node.aliases()); this.description = node.description(); + this.scope = node.scope(); Permission.annotated(node.permission()).ifPresent(this::setPermission); } + public enum ExecutionScope { + IN_GAME, + CONSOLE, + ALL; + + @Nullable + public Predicate toPredicate(@NotNull BaseCommand command) { + return this == ALL ? null : user -> this.contains(command.getUser(user)); + } + + public boolean contains(@NotNull CommandUser user) { + return switch (this) { + case IN_GAME -> !user.isConsole(); + case CONSOLE -> user.isConsole(); + case ALL -> true; + }; + } + } + static class AnnotatedCommand extends Command { private final Object annotated; @@ -102,11 +123,15 @@ public abstract class Command implements CommandProvider { continue; } - // Conditional & unconditional syntax + // Determine predicates final Optional perm = Permission.annotated(syntax.permission()).map(p -> p.toPredicate(cmd)); + final Optional scope = Optional.ofNullable(syntax.scope().toPredicate(cmd)); + final Optional combined = scope.map(sp -> perm.map(pp -> sp.and(pp)).orElse(sp)); + + // Conditional & unconditional syntax final CommandExecutor executor = methodToExecutor(method, annotated, cmd); - if (perm.isPresent()) { - cmd.addConditionalSyntax(perm.get(), executor, args); + if (combined.isPresent()) { + cmd.addConditionalSyntax(combined.get(), executor, args); continue; } cmd.addSyntax(executor, args); @@ -170,22 +195,28 @@ public abstract class Command implements CommandProvider { } public record SubCommand(@NotNull String name, @NotNull List aliases, @Nullable Permission permission, - @NotNull CommandProvider provider) { - public SubCommand(@NotNull String name, @NotNull List aliases, @NotNull CommandProvider provider) { - this(name, aliases, null, provider); + @NotNull ExecutionScope scope, @NotNull CommandProvider provider) { + public SubCommand(@NotNull String name, @NotNull List aliases, @Nullable Permission permission, + @NotNull CommandProvider provider) { + this(name, aliases, permission, ExecutionScope.ALL, provider); } - public SubCommand(@NotNull String name, @NotNull CommandProvider provider) { - this(name, List.of(), null, provider); + public SubCommand(@NotNull String name, @NotNull List aliases, @NotNull CommandProvider provider) { + this(name, aliases, null, ExecutionScope.ALL, provider); } public SubCommand(@NotNull String name, @Nullable Permission permission, @NotNull CommandProvider provider) { - this(name, List.of(), permission, provider); + this(name, List.of(), permission, ExecutionScope.ALL, provider); } + public SubCommand(@NotNull String name, @NotNull CommandProvider provider) { + this(name, List.of(), null, ExecutionScope.ALL, provider); + } + + @NotNull Command command() { - return new Command(name, aliases, "", permission) { + return new Command(name, aliases, "", permission, scope) { @Override public void provide(@NotNull BaseCommand command) { provider.provide(command); diff --git a/common/src/main/java/net/william278/uniform/annotations/CommandNode.java b/common/src/main/java/net/william278/uniform/annotations/CommandNode.java index 33e4516..4d58331 100644 --- a/common/src/main/java/net/william278/uniform/annotations/CommandNode.java +++ b/common/src/main/java/net/william278/uniform/annotations/CommandNode.java @@ -21,6 +21,8 @@ package net.william278.uniform.annotations; +import net.william278.uniform.Command; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -34,5 +36,6 @@ public @interface CommandNode { String[] aliases() default {}; String description() default ""; PermissionNode permission() default @PermissionNode(""); + Command.ExecutionScope scope() default Command.ExecutionScope.ALL; } \ No newline at end of file diff --git a/common/src/main/java/net/william278/uniform/annotations/Syntax.java b/common/src/main/java/net/william278/uniform/annotations/Syntax.java index ed1b92f..100d3d2 100644 --- a/common/src/main/java/net/william278/uniform/annotations/Syntax.java +++ b/common/src/main/java/net/william278/uniform/annotations/Syntax.java @@ -21,6 +21,8 @@ package net.william278.uniform.annotations; +import net.william278.uniform.Command; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -29,5 +31,8 @@ import java.lang.annotation.Target; @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Syntax { + PermissionNode permission() default @PermissionNode(""); + Command.ExecutionScope scope() default Command.ExecutionScope.ALL; + } diff --git a/gradle.properties b/gradle.properties index 21f3d78..90a8339 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,6 +3,6 @@ javaVersion=17 org.gradle.jvmargs='-Dfile.encoding=UTF-8' org.gradle.daemon=true -library_version=1.2 +library_version=1.2.1 library_archive=uniform library_description=Cross-platform wrapper for making Brigadier commands, based on BrigadierWrapper by Tofaa2, itself inspired by emortalmcs command system. \ No newline at end of file