Skip to content

Commit 572086a

Browse files
authored
GH-515 Improve Cooldown API (#521)
* cooldown * wip * Add cooldown api tests. Improve publisher
1 parent 30070f8 commit 572086a

File tree

15 files changed

+328
-90
lines changed

15 files changed

+328
-90
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package dev.rollczi.litecommands.annotations.cooldown;
2+
3+
import dev.rollczi.litecommands.annotations.argument.Arg;
4+
import dev.rollczi.litecommands.annotations.command.Command;
5+
import dev.rollczi.litecommands.annotations.execute.Execute;
6+
import dev.rollczi.litecommands.cooldown.event.CooldownEvent;
7+
import dev.rollczi.litecommands.unit.annotations.LiteTestSpec;
8+
import java.time.temporal.ChronoUnit;
9+
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
10+
import org.junit.jupiter.api.Test;
11+
12+
class CooldownAPITest extends LiteTestSpec {
13+
14+
static CooldownEvent registeredEvent;
15+
16+
static LiteTestConfig config = builder -> builder
17+
.listener(CooldownEvent.class, event -> registeredEvent = event);
18+
19+
@Command(name = "test")
20+
@Cooldown(key = "test-cooldown", count = 400, unit = ChronoUnit.MILLIS)
21+
static class TestCommand {
22+
23+
@Execute
24+
void execute() {}
25+
26+
@Execute(name = "with-args")
27+
@Cooldown(key = "test-cooldown-with-args", count = 600, unit = ChronoUnit.MILLIS)
28+
void execute(@Arg Integer arg) {}
29+
30+
}
31+
32+
@Command(name = "bypass-test")
33+
@Cooldown(key = "bypass-test-cooldown", count = 400, unit = ChronoUnit.MILLIS, bypass = "test.bypass")
34+
static class TestWithBypassCommand {
35+
36+
@Execute
37+
void execute() {}
38+
39+
}
40+
41+
@Test
42+
void test() {
43+
platform.execute("test");
44+
assertThat(registeredEvent)
45+
.isNotNull();
46+
}
47+
48+
}

litecommands-annotations/test/dev/rollczi/litecommands/annotations/cooldown/CooldownAnnotationTest.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ void test() {
4747
CooldownState cooldownState = platform.execute("test")
4848
.assertFailedAs(CooldownState.class);
4949

50-
assertEquals("test-cooldown", cooldownState.getCooldownContext().getKey());
51-
assertEquals(Duration.ofMillis(400), cooldownState.getCooldownContext().getDuration());
50+
assertEquals("test-cooldown", cooldownState.getKey());
51+
assertEquals(Duration.ofMillis(400), cooldownState.getDuration());
5252
assertFalse(cooldownState.getRemainingDuration().isZero());
5353

5454
Awaitility.await()
@@ -73,8 +73,8 @@ void testInvalidUsage() {
7373
CooldownState cooldownState = platform.execute("test with-args 10")
7474
.assertFailedAs(CooldownState.class);
7575

76-
assertEquals("test-cooldown-with-args", cooldownState.getCooldownContext().getKey());
77-
assertEquals(Duration.ofMillis(600), cooldownState.getCooldownContext().getDuration());
76+
assertEquals("test-cooldown-with-args", cooldownState.getKey());
77+
assertEquals(Duration.ofMillis(600), cooldownState.getDuration());
7878
assertFalse(cooldownState.getRemainingDuration().isZero());
7979

8080
Awaitility.await()
@@ -93,8 +93,8 @@ void testCooldownBypass() {
9393
CooldownState cooldownState = platform.execute("bypass-test")
9494
.assertFailedAs(CooldownState.class);
9595

96-
assertEquals("bypass-test-cooldown", cooldownState.getCooldownContext().getKey());
97-
assertEquals(Duration.ofMillis(400), cooldownState.getCooldownContext().getDuration());
96+
assertEquals("bypass-test-cooldown", cooldownState.getKey());
97+
assertEquals(Duration.ofMillis(400), cooldownState.getDuration());
9898
assertFalse(cooldownState.getRemainingDuration().isZero());
9999

100100
Awaitility.await()
Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,92 @@
11
package dev.rollczi.litecommands.cooldown;
22

33
import dev.rollczi.litecommands.command.executor.CommandExecutor;
4+
import dev.rollczi.litecommands.cooldown.event.CooldownApiEvent;
5+
import dev.rollczi.litecommands.cooldown.event.CooldownCommandEvent;
6+
import dev.rollczi.litecommands.cooldown.event.CooldownEvent;
7+
import dev.rollczi.litecommands.event.EventPublisher;
48
import dev.rollczi.litecommands.identifier.Identifier;
9+
import dev.rollczi.litecommands.invocation.Invocation;
510
import dev.rollczi.litecommands.meta.Meta;
611
import dev.rollczi.litecommands.platform.PlatformSender;
712
import dev.rollczi.litecommands.scheduler.Scheduler;
813
import dev.rollczi.litecommands.scheduler.SchedulerPoll;
9-
import java.time.Instant;
14+
import java.time.Duration;
1015
import java.util.HashMap;
1116
import java.util.Map;
1217
import java.util.Optional;
1318
import org.jetbrains.annotations.ApiStatus;
14-
import org.jetbrains.annotations.Nullable;
1519

1620
@ApiStatus.Experimental
1721
public class CooldownService {
1822

1923
private final Scheduler scheduler;
24+
private final EventPublisher publisher;
2025
private final Map<CooldownCompositeKey, CooldownState> cooldowns = new HashMap<>();
2126

22-
public CooldownService(Scheduler scheduler) {
27+
public CooldownService(Scheduler scheduler, EventPublisher publisher) {
2328
this.scheduler = scheduler;
29+
this.publisher = publisher;
2430
}
2531

26-
Optional<CooldownState> getState(CommandExecutor<?> executor, PlatformSender sender) {
27-
CooldownContext cooldownContext = getOperativeContext(executor, sender);
28-
29-
if (cooldownContext == null) {
30-
return Optional.empty();
31-
}
32-
33-
return getState(cooldownContext.getKey(), sender.getIdentifier());
32+
public Optional<CooldownState> getCooldown(CommandExecutor<?> executor, PlatformSender sender) {
33+
return this.getOperativeContext(executor, sender)
34+
.flatMap(cooldownContext -> this.getCooldown(cooldownContext.getKey(), sender.getIdentifier()));
3435
}
3536

36-
public Optional<CooldownState> getState(String key, Identifier senderIdentifier) {
37+
public Optional<CooldownState> getCooldown(String key, Identifier senderIdentifier) {
3738
CooldownCompositeKey compositeKey = new CooldownCompositeKey(senderIdentifier, key);
3839
CooldownState cooldownState = cooldowns.get(compositeKey);
39-
4040
if (cooldownState != null && !cooldownState.isExpired()) {
4141
return Optional.of(cooldownState);
4242
}
4343

4444
return Optional.empty();
4545
}
4646

47-
boolean updateState(CommandExecutor<?> executor, PlatformSender sender) {
48-
CooldownContext cooldownContext = getOperativeContext(executor, sender);
49-
50-
if (cooldownContext == null) {
51-
return false;
52-
}
47+
public Optional<CooldownState> markCooldown(Invocation<?> invocation, CommandExecutor<?> executor) {
48+
return this.getOperativeContext(executor, invocation.platformSender())
49+
.map(context -> new CooldownCommandEvent(invocation, context.getKey(), context.getDuration()))
50+
.flatMap(event -> this.processChange(event));
51+
}
5352

54-
return updateState(cooldownContext, sender.getIdentifier());
53+
public Optional<CooldownState> markCooldown(CooldownContext context, Identifier senderIdentifier) {
54+
return this.processChange(new CooldownApiEvent(senderIdentifier, context.getKey(), context.getDuration()));
5555
}
5656

57-
public boolean updateState(CooldownContext context, Identifier senderIdentifier) {
58-
CooldownCompositeKey compositeKey = new CooldownCompositeKey(senderIdentifier, context.getKey());
57+
private Optional<CooldownState> processChange(CooldownEvent event) {
58+
CooldownEvent publishedEvent = this.publisher.publish(event);
59+
if (publishedEvent.isCancelled()) {
60+
return Optional.empty();
61+
}
62+
63+
CooldownCompositeKey compositeKey = new CooldownCompositeKey(publishedEvent.getSender(), publishedEvent.getKey());
64+
Duration duration = publishedEvent.getDuration();
65+
CooldownState state = new CooldownState(publishedEvent.getKey(), duration);
5966

60-
Instant now = Instant.now();
61-
Instant expirationTime = now.plus(context.getDuration());
62-
cooldowns.put(compositeKey, new CooldownState(context, expirationTime));
63-
scheduler.supplyLater(SchedulerPoll.MAIN, context.getDuration(), () -> cooldowns.remove(compositeKey));
64-
return true;
67+
cooldowns.put(compositeKey, state);
68+
scheduler.supplyLater(SchedulerPoll.MAIN, duration, () -> cooldowns.remove(compositeKey));
69+
return Optional.of(state);
6570
}
6671

67-
public boolean clearState(String key, Identifier senderIdentifier) {
72+
public boolean removeCooldown(String key, Identifier senderIdentifier) {
6873
CooldownCompositeKey compositeKey = new CooldownCompositeKey(senderIdentifier, key);
6974

7075
return cooldowns.remove(compositeKey) != null;
7176
}
7277

73-
@Nullable
74-
private CooldownContext getOperativeContext(CommandExecutor<?> executor, PlatformSender sender) {
78+
private Optional<CooldownContext> getOperativeContext(CommandExecutor<?> executor, PlatformSender sender) {
7579
CooldownContext cooldownContext = executor.metaCollector().findFirst(Meta.COOLDOWN, null);
76-
7780
if (cooldownContext == null) {
78-
return null;
81+
return Optional.empty();
7982
}
8083

8184
String bypassPermission = cooldownContext.getBypassPermission();
82-
8385
if (!bypassPermission.isEmpty() && sender.hasPermission(bypassPermission)) {
84-
return null;
86+
return Optional.empty();
8587
}
8688

87-
return cooldownContext;
89+
return Optional.of(cooldownContext);
8890
}
8991

9092
}

litecommands-core/src/dev/rollczi/litecommands/cooldown/CooldownState.java

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,22 @@
55

66
public class CooldownState {
77

8-
private final CooldownContext cooldownContext;
8+
private final String key;
9+
private final Duration duration;
910
private final Instant expirationTime;
1011

11-
public CooldownState(CooldownContext cooldownContext, Instant expirationTime) {
12-
this.cooldownContext = cooldownContext;
13-
this.expirationTime = expirationTime;
12+
public CooldownState(String key, Duration duration) {
13+
this.key = key;
14+
this.duration = duration;
15+
this.expirationTime = Instant.now().plus(duration);
1416
}
1517

16-
public CooldownContext getCooldownContext() {
17-
return cooldownContext;
18+
public String getKey() {
19+
return key;
20+
}
21+
22+
public Duration getDuration() {
23+
return duration;
1824
}
1925

2026
public Duration getRemainingDuration() {
@@ -29,11 +35,9 @@ public boolean isExpired() {
2935
return Instant.now().isAfter(expirationTime);
3036
}
3137

32-
@Override
33-
public String toString() {
34-
return "CooldownState{" +
35-
"cooldownContext=" + cooldownContext.getKey() +
36-
", expirationTime=" + expirationTime +
37-
'}';
38+
@Deprecated
39+
public CooldownContext getCooldownContext() {
40+
return new CooldownContext(key, duration);
3841
}
42+
3943
}

litecommands-core/src/dev/rollczi/litecommands/cooldown/CooldownStateController.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public CooldownStateController(CooldownService cooldownService) {
2222
public void onEvent(CommandPreExecutionEvent<SENDER> event) {
2323
Invocation<SENDER> invocation = event.getInvocation();
2424
PlatformSender sender = invocation.platformSender();
25-
Optional<CooldownState> currentState = this.cooldownService.getState(event.getExecutor(), sender);
25+
Optional<CooldownState> currentState = this.cooldownService.getCooldown(event.getExecutor(), sender);
2626

2727
if (currentState.isPresent()) {
2828
event.stopFlow(FailedReason.of(currentState.get()));
@@ -37,9 +37,7 @@ public void onEvent(CommandPostExecutionEvent<SENDER> event) {
3737
return;
3838
}
3939

40-
Invocation<SENDER> invocation = event.getInvocation();
41-
PlatformSender sender = invocation.platformSender();
42-
cooldownService.updateState(event.getExecutor(), sender);
40+
cooldownService.markCooldown(event.getInvocation(), event.getExecutor());
4341
}
4442

4543
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package dev.rollczi.litecommands.cooldown.event;
2+
3+
import dev.rollczi.litecommands.identifier.Identifier;
4+
import java.time.Duration;
5+
6+
public class CooldownApiEvent implements CooldownEvent {
7+
8+
private final Identifier identifier;
9+
private String key;
10+
private Duration duration;
11+
private boolean cancelled;
12+
13+
public CooldownApiEvent(Identifier identifier, String key, Duration duration) {
14+
this.identifier = identifier;
15+
this.key = key;
16+
this.duration = duration;
17+
}
18+
19+
@Override
20+
public Identifier getSender() {
21+
return identifier;
22+
}
23+
24+
@Override
25+
public String getKey() {
26+
return key;
27+
}
28+
29+
@Override
30+
public void setKey(String key) {
31+
this.key = key;
32+
}
33+
34+
@Override
35+
public Duration getDuration() {
36+
return duration;
37+
}
38+
39+
@Override
40+
public void setDuration(Duration duration) {
41+
this.duration = duration;
42+
}
43+
44+
@Override
45+
public boolean isCancelled() {
46+
return cancelled;
47+
}
48+
49+
@Override
50+
public void setCancelled(boolean cancelled) {
51+
this.cancelled = cancelled;
52+
}
53+
54+
@Override
55+
public Cause getCause() {
56+
return Cause.API;
57+
}
58+
59+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package dev.rollczi.litecommands.cooldown.event;
2+
3+
import dev.rollczi.litecommands.identifier.Identifier;
4+
import dev.rollczi.litecommands.invocation.Invocation;
5+
import java.time.Duration;
6+
7+
public class CooldownCommandEvent implements CooldownEvent {
8+
9+
private final Invocation<?> invocation;
10+
private String key;
11+
private Duration duration;
12+
private boolean cancelled;
13+
14+
public CooldownCommandEvent(Invocation<?> invocation, String key, Duration duration) {
15+
this.invocation = invocation;
16+
this.key = key;
17+
this.duration = duration;
18+
}
19+
20+
public Invocation<?> getInvocation() {
21+
return invocation;
22+
}
23+
24+
@Override
25+
public Identifier getSender() {
26+
return invocation.platformSender().getIdentifier();
27+
}
28+
29+
@Override
30+
public String getKey() {
31+
return key;
32+
}
33+
34+
@Override
35+
public void setKey(String key) {
36+
this.key = key;
37+
}
38+
39+
@Override
40+
public Duration getDuration() {
41+
return duration;
42+
}
43+
44+
@Override
45+
public void setDuration(Duration duration) {
46+
this.duration = duration;
47+
}
48+
49+
@Override
50+
public boolean isCancelled() {
51+
return cancelled;
52+
}
53+
54+
@Override
55+
public void setCancelled(boolean cancelled) {
56+
this.cancelled = cancelled;
57+
}
58+
59+
@Override
60+
public Cause getCause() {
61+
return Cause.COMMAND;
62+
}
63+
64+
}

0 commit comments

Comments
 (0)