Skip to content

Commit

Permalink
feat: implement cooldowns
Browse files Browse the repository at this point in the history
  • Loading branch information
Citymonstret committed Jan 5, 2024
1 parent fb9be78 commit 36cc12d
Show file tree
Hide file tree
Showing 18 changed files with 1,205 additions and 2 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ Command pre- & post-processors for [Cloud v2](https://github.com/incendo/cloud).

## postprocessors

- [cloud-processors-confirmation](./cloud-processors-confirmation)
- [cloud-processors-confirmation](./cloud-processors-confirmation)
- [cloud-processors-cooldown](./cloud-processors-cooldown)
13 changes: 13 additions & 0 deletions cloud-processors-cooldown/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# cloud-processors-cooldown

Postprocessor that adds command cooldowns.

## Installation

cloud-processors-cooldown is not yet available on Maven Central.

## Usage

### Builders

### Annotations
10 changes: 10 additions & 0 deletions cloud-processors-cooldown/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
plugins {
id("cloud-processors.base-conventions")
id("cloud-processors.publishing-conventions")
}

dependencies {
api(projects.cloudProcessorsCommon)

compileOnly(libs.cloud.annotations)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// 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.cooldown;

import java.time.Duration;
import java.time.Instant;
import org.apiguardian.api.API;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.immutables.value.Value;
import org.incendo.cloud.processors.immutables.StagedImmutableBuilder;

/**
* An instance of a cooldown for a {@link CooldownGroup} belonging to a command sender.
*
* @since 1.0.0
*/
@StagedImmutableBuilder
@Value.Immutable
@API(status = API.Status.STABLE, since = "1.0.0")
public interface Cooldown {

/**
* Returns a new cooldown builder.
*
* @return the builder
*/
static ImmutableCooldown.@NonNull GroupBuildStage builder() {
return ImmutableCooldown.builder();
}

/**
* Returns the group that this instance belongs to.
*
* @return the cooldown group
*/
@NonNull CooldownGroup group();

/**
* Returns the cooldown duration.
*
* @return the duration
*/
@NonNull Duration duration();

/**
* Returns the time that the cooldown was created.
*
* @return the creation time
*/
@NonNull Instant creationTime();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//
// 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.cooldown;

import cloud.commandframework.context.CommandContext;
import java.time.Clock;
import java.util.function.Predicate;
import org.apiguardian.api.API;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.immutables.value.Value;
import org.incendo.cloud.processors.immutables.StagedImmutableBuilder;

/**
* Configuration for a {@link CooldownManager}.
*
* @param <C> command sender type
* @since 1.0.0
*/
@Value.Immutable
@StagedImmutableBuilder
@API(status = API.Status.STABLE, since = "1.0.0")
public interface CooldownConfiguration<C> {

/**
* Returns a new configuration builder.
*
* @param <C> command sender type
* @return the builder
*/
static <C> ImmutableCooldownConfiguration.@NonNull RepositoryBuildStage<C> builder() {
return ImmutableCooldownConfiguration.builder();
}

/**
* Returns the repository used to store cooldowns.
*
* @return the repository
*/
@NonNull CooldownRepository<C> repository();

/**
* Returns a consumer that gets invoked when a cooldown is active and is preventing the command
* from executing.
*
* @return notifier for active cooldowns
*/
@NonNull CooldownNotifier<C> cooldownNotifier();

/**
* Returns a predicate that determines whether the {@link CommandContext}
* should bypass the cooldown requirement.
*
* <p>The default implementation always returns {@code false}</p>
*
* @return predicate that determines whether cooldowns should be skipped
*/
default @NonNull Predicate<@NonNull CommandContext<C>> bypassCooldown() {
return context -> false;
}

/**
* Returns the clock used to calculate the current time.
*
* @return the clock
*/
default @NonNull Clock clock() {
return Clock.systemUTC();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//
// 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.cooldown;

import cloud.commandframework.Command;
import java.time.Duration;
import org.apiguardian.api.API;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
* Utility for decorating {@link Command.Builder command builders}.
*
* <p>The decorator can be applied using {@link Command.Builder#apply(Command.Builder.Applicable)}.</p>
*
* @param <C> command sender type
* @since 1.0.0
*/
@API(status = API.Status.STABLE, since = "1.0.0")
public final class CooldownDecorator<C> implements Command.Builder.Applicable<C> {

/**
* Returns a new decorator that uses the {@link CooldownGroup#command(Command)} group.
*
* @param <C> command sender type
* @param duration cooldown duration
* @return the decorator
*/
public static <C> @NonNull CooldownDecorator<C> of(final @NonNull Duration duration) {
return new CooldownDecorator<>(duration, null /* group */);
}

/**
* Returns a new decorator.
*
* @param <C> command sender type
* @param duration cooldown duration
* @param group cooldown group
* @return the decorator
*/
public static <C> @NonNull CooldownDecorator<C> of(final @NonNull Duration duration, final @NonNull CooldownGroup group) {
return new CooldownDecorator<>(duration, group);
}

private final Duration duration;
private final CooldownGroup group;

private CooldownDecorator(final @NonNull Duration duration, final @Nullable CooldownGroup group) {
this.duration = duration;
this.group = group;
}

@Override
public Command.@NonNull Builder<C> applyToCommandBuilder(final Command.@NonNull Builder<C> builder) {
if (this.group != null) {
return builder.meta(CooldownManager.META_COOLDOWN_DURATION, this.duration)
.meta(CooldownManager.META_COOLDOWN_GROUP, this.group);
}
return builder.meta(CooldownManager.META_COOLDOWN_DURATION, this.duration);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
//
// 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.cooldown;

import cloud.commandframework.Command;
import java.util.Objects;
import org.apiguardian.api.API;
import org.checkerframework.checker.nullness.qual.NonNull;

/**
* Groups cooldown together.
*
* <p>If two commands belong to the same group, the cooldown will be shared between those commands.</p>
*
* @since 1.0.0
*/
@API(status = API.Status.STABLE, since = "1.0.0")
public interface CooldownGroup {

/**
* Creates a new cooldown group that is unique to the given {@code command}.
*
* <p>This means that the cooldown will not be shared with any other commands.</p>
*
* @param command the command
* @return the group
*/
static CooldownGroup command(final @NonNull Command<?> command) {
return new CommandCooldownGroup(command);
}

/**
* Creates a new cooldown group identified by the given {@code name}.
*
* @param name group name
* @return the group
*/
static CooldownGroup named(final @NonNull String name) {
return new NamedCooldownGroup(name);
}

final class CommandCooldownGroup implements CooldownGroup {

private final Command<?> command;

private CommandCooldownGroup(final @NonNull Command<?> command) {
this.command = command;
}

@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || this.getClass() != o.getClass()) {
return false;
}
final CommandCooldownGroup that = (CommandCooldownGroup) o;
return Objects.equals(this.command, that.command);
}

@Override
public int hashCode() {
return Objects.hash(this.command);
}
}

final class NamedCooldownGroup implements CooldownGroup {

private final String name;

private NamedCooldownGroup(final @NonNull String name) {
this.name = name;
}

@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || this.getClass() != o.getClass()) {
return false;
}
final NamedCooldownGroup that = (NamedCooldownGroup) o;
return Objects.equals(this.name, that.name);
}

@Override
public int hashCode() {
return Objects.hash(this.name);
}
}
}
Loading

0 comments on commit 36cc12d

Please sign in to comment.