-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
23 changed files
with
1,330 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,9 @@ | ||
# cloud-processors | ||
|
||
> [!IMPORTANT] | ||
> Cloud Requirements requires Java 11+. | ||
Command pre- & post-processors for [Cloud v2](https://github.com/incendo/cloud). | ||
|
||
## postprocessors | ||
|
||
- [cloud-processors-confirmation](./cloud-processors-confirmation) | ||
- [cloud-processors-cooldown](./cloud-processors-cooldown) | ||
- [cloud-processors-requirements](./cloud-processors-requirements) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
# cloud-processor-requirements | ||
|
||
Command requirement system for [Cloud v2](https://github.com/incendo/cloud). | ||
|
||
The requirements are evaluated before | ||
the command is executed to determine whether the command sender should be able to execute the command. The requirements | ||
are defined on a per-command basis. | ||
|
||
## Installation | ||
|
||
Cloud Requirements is not yet available on Maven Central. | ||
|
||
## Usage | ||
|
||
You create requirements by implementing the `Requirement` interface. It is recommended to create an intermediary | ||
requirement interface that extends `Requirement` that can contain shared logic. This also reduces verbosity introduced | ||
by the generic types. Example: | ||
```java | ||
public interface YourRequirementInterface implements Requirement<YourSenderType, YourRequirementInterface> { | ||
|
||
// Example method | ||
@NonNull String errorMessage(); | ||
} | ||
``` | ||
you can then create a requirement: | ||
```java | ||
public final class YourRequirement implements YourRequirementInterface { | ||
|
||
@Override | ||
public @NonNull String errorMessage() { | ||
return "not cool enough"; | ||
} | ||
|
||
@Override | ||
public boolean evaluateRequirement(final @NonNull CommandContext<YourSenderType> context) { | ||
return false; // You should probably put some logic here :) | ||
} | ||
} | ||
``` | ||
|
||
You then need to create a `CloudKey<Requirements<YourSenderType, YourRequirementInterface>` which is used to store | ||
the requirements in the command meta and for the processor to access the stored requirements: | ||
```java | ||
public static final CloudKey<Requirements<YourSenderType, YourRequirementInterface>> REQUIREMENT_KEY = CloudKey.of( | ||
"requirements", | ||
new TypeToken<CloudKey<Requirements<YourSenderType, YourRequirementInterface>>>() {} | ||
); | ||
``` | ||
|
||
You then need to create an instance of the postprocessor and register it to your command manager: | ||
```java | ||
final RequirementPostprocessor<YourSenderType, YourRequirementInterface> postprocessor = RequirementPostprocessor.of( | ||
REQUIREMENTS_KEY, | ||
new YourFailureHandler() | ||
); | ||
commandManager.registerPostprocessor(postprocessor); | ||
``` | ||
the failure handler gets invoked when the command sender fails to meet a requirement: | ||
```java | ||
public final class YourFailureHandler implements RequirementFailureHandler<YourSenderType, YourRequirementInterface> { | ||
|
||
@Override | ||
public void handleFailure( | ||
final @NonNull CommandContext<YourSenderType> context, | ||
final YourRequirementInterface requirement | ||
) { | ||
context.sender().sendMessage("Requirement failed: " + requirement.errorMessage()); | ||
} | ||
} | ||
``` | ||
|
||
You then need to register the requirements to your command. This step depends on whether you use | ||
[builders](#builders) or [annotations](#annotations). | ||
|
||
### Builders | ||
|
||
You have two different options when it comes to registering requirements to commands using the command builders. | ||
You may store the requirements directly: | ||
```java | ||
commandBuilder.meta(REQUIREMENT_KEY, Requirements.of(requirement, requirement1, ...)); | ||
``` | ||
|
||
or by using the `RequirementApplicable` system: | ||
```java | ||
// Store this somewhere: | ||
RequirementApplicable.RequirementApplicableFactory<YourSenderType, | ||
YourRequirementInterface> factory = RequirementApplicable.factory(REQUIREMENT_KEY); | ||
|
||
// Then register the requirements: | ||
commandBuilder.apply(factory.create(requirement, requirement1, ...)); | ||
``` | ||
|
||
### Annotations | ||
|
||
When using `cloud-annotations` you may use the `RequirementBindings` system to register bindings between | ||
annotations and requirements: | ||
```java | ||
// Create some annotation: | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@Target(ElementType.METHOD) | ||
public @interface YourAnnotation { | ||
// ... | ||
} | ||
|
||
// Then register a binding for it: | ||
RequirementBindings.create(this.annotationParser, REQUIREMENT_KEY).register( | ||
YourAnnotation.class, | ||
annotation -> new YourRequirement() | ||
); | ||
|
||
// Then annotate a method with it: | ||
@YourAnnotation | ||
@CommandMethod("command") | ||
public void commandMethod() { | ||
// ... | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
plugins { | ||
id("cloud-processors.base-conventions") | ||
id("cloud-processors.publishing-conventions") | ||
} | ||
|
||
dependencies { | ||
implementation(libs.cloud.core) | ||
implementation(libs.cloud.annotations) | ||
} |
59 changes: 59 additions & 0 deletions
59
...ors-requirements/src/main/java/org/incendo/cloud/processors/requirements/Requirement.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// | ||
// MIT License | ||
// | ||
// Copyright (c) 2024 Incendo | ||
// | ||
// Permission is hereby granted, free of charge, to any person obtaining a copy | ||
// of this software and associated documentation files (the "Software"), to deal | ||
// in the Software without restriction, including without limitation the rights | ||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
// copies of the Software, and to permit persons to whom the Software is | ||
// furnished to do so, subject to the following conditions: | ||
// | ||
// The above copyright notice and this permission notice shall be included in all | ||
// copies or substantial portions of the Software. | ||
// | ||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
// SOFTWARE. | ||
// | ||
package org.incendo.cloud.processors.requirements; | ||
|
||
import cloud.commandframework.context.CommandContext; | ||
import java.util.List; | ||
import org.apiguardian.api.API; | ||
import org.checkerframework.checker.nullness.qual.NonNull; | ||
|
||
/** | ||
* A requirement for a command to be executed. | ||
* | ||
* @param <C> command sender type | ||
* @param <R> requirement type, used for inheritance | ||
* @since 1.0.0 | ||
*/ | ||
@API(status = API.Status.STABLE, since = "1.0.0") | ||
public interface Requirement<C, R extends Requirement<C, R>> { | ||
|
||
/** | ||
* Returns whether the given {@code context} meets the requirement. | ||
* | ||
* @param commandContext command context to evaluate | ||
* @return {@code true} if the context meets the requirement, {@code false} if not | ||
*/ | ||
boolean evaluateRequirement(@NonNull CommandContext<C> commandContext); | ||
|
||
/** | ||
* Returns the parents of the requirement. | ||
* | ||
* <p>The parents will always be evaluated before {@code this} requirement.</p> | ||
* | ||
* @return the parents | ||
*/ | ||
default @NonNull List<@NonNull R> parents() { | ||
return List.of(); | ||
} | ||
} |
126 changes: 126 additions & 0 deletions
126
...ements/src/main/java/org/incendo/cloud/processors/requirements/RequirementApplicable.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
// | ||
// MIT License | ||
// | ||
// Copyright (c) 2024 Incendo | ||
// | ||
// Permission is hereby granted, free of charge, to any person obtaining a copy | ||
// of this software and associated documentation files (the "Software"), to deal | ||
// in the Software without restriction, including without limitation the rights | ||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
// copies of the Software, and to permit persons to whom the Software is | ||
// furnished to do so, subject to the following conditions: | ||
// | ||
// The above copyright notice and this permission notice shall be included in all | ||
// copies or substantial portions of the Software. | ||
// | ||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
// SOFTWARE. | ||
// | ||
package org.incendo.cloud.processors.requirements; | ||
|
||
import cloud.commandframework.Command; | ||
import cloud.commandframework.keys.CloudKey; | ||
import java.util.List; | ||
import java.util.Objects; | ||
import org.apiguardian.api.API; | ||
import org.checkerframework.checker.nullness.qual.NonNull; | ||
|
||
/** | ||
* Utility for adding {@link Requirements} to a {@link Command.Builder}. | ||
* | ||
* <p>The requirements can be applied to the command builder by | ||
* using {@link Command.Builder#apply(Command.Builder.Applicable)}.</p> | ||
* | ||
* @param <C> command sender type | ||
* @param <R> requirement type | ||
* @since 1.0.0 | ||
*/ | ||
@API(status = API.Status.STABLE, since = "1.0.0") | ||
public final class RequirementApplicable<C, R extends Requirement<C, R>> implements Command.Builder.Applicable<C> { | ||
|
||
/** | ||
* Returns a new factory that creates {@link RequirementApplicable} instances. | ||
* | ||
* @param <C> command sender type | ||
* @param <R> requirement type | ||
* @param requirementKey key used to store the requirements in the command meta, should be the same as the key supplied to | ||
* {@link RequirementPostprocessor} | ||
* @return the factory | ||
*/ | ||
public static <C, R extends Requirement<C, R>> @NonNull RequirementApplicableFactory<C, R> factory( | ||
final @NonNull CloudKey<Requirements<C, R>> requirementKey | ||
) { | ||
return new RequirementApplicableFactory<>(requirementKey); | ||
} | ||
|
||
private final CloudKey<Requirements<C, R>> requirementKey; | ||
private final Requirements<C, R> requirements; | ||
|
||
private RequirementApplicable( | ||
final @NonNull CloudKey<Requirements<C, R>> requirementKey, | ||
final @NonNull Requirements<C, R> requirements | ||
) { | ||
this.requirementKey = Objects.requireNonNull(requirementKey, "requirementKey"); | ||
this.requirements = Objects.requireNonNull(requirements, "requirements"); | ||
} | ||
|
||
@Override | ||
public Command.@NonNull Builder<C> applyToCommandBuilder(final Command.@NonNull Builder<C> builder) { | ||
return builder.meta(this.requirementKey, this.requirements); | ||
} | ||
|
||
|
||
/** | ||
* Factory that produces {@link RequirementApplicable} instances. | ||
* | ||
* @param <C> command sender type | ||
* @param <R> requirement type | ||
* @since 1.0.0 | ||
*/ | ||
@API(status = API.Status.STABLE, since = "1.0.0") | ||
public static final class RequirementApplicableFactory<C, R extends Requirement<C, R>> { | ||
|
||
private final CloudKey<Requirements<C, R>> requirementKey; | ||
|
||
private RequirementApplicableFactory(final @NonNull CloudKey<Requirements<C, R>> requirementKey) { | ||
this.requirementKey = Objects.requireNonNull(requirementKey, "requirementKey"); | ||
} | ||
|
||
/** | ||
* Creates a new {@link RequirementApplicable} using the given {@code requirements}. | ||
* | ||
* @param requirements requirements to apply to the command builder | ||
* @return the {@link RequirementApplicable} instance | ||
*/ | ||
public @NonNull RequirementApplicable<C, R> create(final @NonNull Requirements<C, R> requirements) { | ||
return new RequirementApplicable<>(this.requirementKey, requirements); | ||
} | ||
|
||
/** | ||
* Creates a new {@link RequirementApplicable} using the given {@code requirements}. | ||
* | ||
* @param requirements requirements to apply to the command builder | ||
* @return the {@link RequirementApplicable} instance | ||
*/ | ||
public @NonNull RequirementApplicable<C, R> create(final @NonNull List<@NonNull R> requirements) { | ||
return new RequirementApplicable<>(this.requirementKey, Requirements.of(requirements)); | ||
} | ||
|
||
/** | ||
* Creates a new {@link RequirementApplicable} using the given {@code requirements}. | ||
* | ||
* @param requirements requirements to apply to the command builder | ||
* @return the {@link RequirementApplicable} instance | ||
*/ | ||
@SafeVarargs | ||
@SuppressWarnings("varargs") | ||
public final @NonNull RequirementApplicable<C, R> create(final @NonNull R @NonNull... requirements) { | ||
return new RequirementApplicable<>(this.requirementKey, Requirements.of(requirements)); | ||
} | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
...ts/src/main/java/org/incendo/cloud/processors/requirements/RequirementFailureHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// | ||
// MIT License | ||
// | ||
// Copyright (c) 2024 Incendo | ||
// | ||
// Permission is hereby granted, free of charge, to any person obtaining a copy | ||
// of this software and associated documentation files (the "Software"), to deal | ||
// in the Software without restriction, including without limitation the rights | ||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
// copies of the Software, and to permit persons to whom the Software is | ||
// furnished to do so, subject to the following conditions: | ||
// | ||
// The above copyright notice and this permission notice shall be included in all | ||
// copies or substantial portions of the Software. | ||
// | ||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
// SOFTWARE. | ||
// | ||
package org.incendo.cloud.processors.requirements; | ||
|
||
import cloud.commandframework.context.CommandContext; | ||
import org.apiguardian.api.API; | ||
import org.checkerframework.checker.nullness.qual.NonNull; | ||
|
||
/** | ||
* Handler that gets invoked when a {@link Requirement requirement} is not met. | ||
* | ||
* @param <C> command sender type | ||
* @param <R> requirement type | ||
* @since 1.0.0 | ||
*/ | ||
@FunctionalInterface | ||
@API(status = API.Status.STABLE, since = "1.0.0") | ||
public interface RequirementFailureHandler<C, R extends Requirement<C, R>> { | ||
|
||
/** | ||
* Returns a requirement failure handler that does nothing. | ||
* | ||
* @param <C> command sender type | ||
* @param <R> requirement type | ||
* @return the handler | ||
*/ | ||
static <C, R extends Requirement<C, R>> @NonNull RequirementFailureHandler<C, R> noOp() { | ||
return (requirement, context) -> {}; | ||
} | ||
|
||
/** | ||
* Handles the case where the given {@code context} does not meet the given {@code requirement}. | ||
* | ||
* @param context the context | ||
* @param requirement the unmet requirement | ||
*/ | ||
void handleFailure(@NonNull CommandContext<C> context, @NonNull R requirement); | ||
} |
Oops, something went wrong.