Skip to content

Commit 72a8b07

Browse files
authored
GH-496 Create and expose Cooldown API. Create PlatformSender factory (#496)
* Create and expose cooldown API * Create platform sender factory and expose platform API. * Improve cooldown context API * Code style fix
1 parent 2d25f16 commit 72a8b07

File tree

26 files changed

+187
-93
lines changed

26 files changed

+187
-93
lines changed

litecommands-bukkit/src/dev/rollczi/litecommands/bukkit/BukkitPlatform.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
class BukkitPlatform extends AbstractPlatform<CommandSender, LiteBukkitSettings> implements Platform<CommandSender, LiteBukkitSettings> {
1111

1212
BukkitPlatform(LiteBukkitSettings settings) {
13-
super(settings);
13+
super(settings, sender -> new BukkitPlatformSender(sender));
1414
}
1515

1616
@Override

litecommands-bungee/src/dev/rollczi/litecommands/bungee/BungeePlatform.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class BungeePlatform extends AbstractPlatform<CommandSender, LiteBungeeSettings>
1919
private final PluginManager pluginManager;
2020

2121
public BungeePlatform(Plugin plugin, LiteBungeeSettings liteBungeeSettings) {
22-
super(liteBungeeSettings);
22+
super(liteBungeeSettings, sender -> new BungeeSender(sender));
2323
this.plugin = plugin;
2424
this.pluginManager = plugin.getProxy().getPluginManager();
2525
}

litecommands-core/src/dev/rollczi/litecommands/LiteCommands.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dev.rollczi.litecommands;
22

33
import dev.rollczi.litecommands.command.CommandManager;
4+
import dev.rollczi.litecommands.platform.Platform;
45
import org.jetbrains.annotations.ApiStatus;
56

67
public interface LiteCommands<SENDER> {
@@ -10,6 +11,8 @@ public interface LiteCommands<SENDER> {
1011

1112
CommandManager<SENDER> getCommandManager();
1213

14+
Platform<SENDER, ?> getPlatform();
15+
1316
@ApiStatus.Experimental
1417
LiteCommandsInternal<SENDER, ?> getInternal();
1518

litecommands-core/src/dev/rollczi/litecommands/LiteCommandsInternal.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import dev.rollczi.litecommands.argument.parser.ParserRegistry;
44
import dev.rollczi.litecommands.bind.BindRegistry;
55
import dev.rollczi.litecommands.context.ContextRegistry;
6+
import dev.rollczi.litecommands.cooldown.CooldownService;
67
import dev.rollczi.litecommands.handler.result.ResultHandleService;
78
import dev.rollczi.litecommands.command.builder.CommandBuilderCollector;
89
import dev.rollczi.litecommands.editor.EditorService;
@@ -61,4 +62,7 @@ public interface LiteCommandsInternal<SENDER, C extends PlatformSettings> {
6162
@ApiStatus.Internal
6263
StrictService getStrictService();
6364

65+
@ApiStatus.Experimental
66+
CooldownService getCooldownService();
67+
6468
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dev.rollczi.litecommands.cooldown;
22

3+
import dev.rollczi.litecommands.util.StringUtil;
34
import java.time.Duration;
45

56
public class CooldownContext {
@@ -14,6 +15,10 @@ public CooldownContext(String key, Duration duration, String bypassPermission) {
1415
this.bypassPermission = bypassPermission;
1516
}
1617

18+
public CooldownContext(String key, Duration duration) {
19+
this(key, duration, StringUtil.EMPTY);
20+
}
21+
1722
public String getKey() {
1823
return key;
1924
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package dev.rollczi.litecommands.cooldown;
2+
3+
import dev.rollczi.litecommands.command.executor.CommandExecutor;
4+
import dev.rollczi.litecommands.identifier.Identifier;
5+
import dev.rollczi.litecommands.meta.Meta;
6+
import dev.rollczi.litecommands.platform.PlatformSender;
7+
import dev.rollczi.litecommands.scheduler.Scheduler;
8+
import dev.rollczi.litecommands.scheduler.SchedulerPoll;
9+
import java.time.Instant;
10+
import java.util.HashMap;
11+
import java.util.Map;
12+
import java.util.Optional;
13+
import org.jetbrains.annotations.ApiStatus;
14+
import org.jetbrains.annotations.Nullable;
15+
16+
@ApiStatus.Experimental
17+
public class CooldownService {
18+
19+
private final Scheduler scheduler;
20+
private final Map<CooldownCompositeKey, CooldownState> cooldowns = new HashMap<>();
21+
22+
public CooldownService(Scheduler scheduler) {
23+
this.scheduler = scheduler;
24+
}
25+
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());
34+
}
35+
36+
public Optional<CooldownState> getState(String key, Identifier senderIdentifier) {
37+
CooldownCompositeKey compositeKey = new CooldownCompositeKey(senderIdentifier, key);
38+
CooldownState cooldownState = cooldowns.get(compositeKey);
39+
40+
if (cooldownState != null && !cooldownState.isExpired()) {
41+
return Optional.of(cooldownState);
42+
}
43+
44+
return Optional.empty();
45+
}
46+
47+
boolean updateState(CommandExecutor<?> executor, PlatformSender sender) {
48+
CooldownContext cooldownContext = getOperativeContext(executor, sender);
49+
50+
if (cooldownContext == null) {
51+
return false;
52+
}
53+
54+
return updateState(cooldownContext, sender.getIdentifier());
55+
}
56+
57+
public boolean updateState(CooldownContext context, Identifier senderIdentifier) {
58+
CooldownCompositeKey compositeKey = new CooldownCompositeKey(senderIdentifier, context.getKey());
59+
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;
65+
}
66+
67+
public boolean clearState(String key, Identifier senderIdentifier) {
68+
CooldownCompositeKey compositeKey = new CooldownCompositeKey(senderIdentifier, key);
69+
70+
return cooldowns.remove(compositeKey) != null;
71+
}
72+
73+
@Nullable
74+
private CooldownContext getOperativeContext(CommandExecutor<?> executor, PlatformSender sender) {
75+
CooldownContext cooldownContext = executor.metaCollector().findFirst(Meta.COOLDOWN, null);
76+
77+
if (cooldownContext == null) {
78+
return null;
79+
}
80+
81+
String bypassPermission = cooldownContext.getBypassPermission();
82+
83+
if (!bypassPermission.isEmpty() && sender.hasPermission(bypassPermission)) {
84+
return null;
85+
}
86+
87+
return cooldownContext;
88+
}
89+
90+
}

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@
66
public class CooldownState {
77

88
private final CooldownContext cooldownContext;
9-
private final Duration remainingDuration;
109
private final Instant expirationTime;
1110

12-
public CooldownState(CooldownContext cooldownContext, Duration remainingDuration, Instant expirationTime) {
11+
public CooldownState(CooldownContext cooldownContext, Instant expirationTime) {
1312
this.cooldownContext = cooldownContext;
14-
this.remainingDuration = remainingDuration;
1513
this.expirationTime = expirationTime;
1614
}
1715

@@ -20,18 +18,21 @@ public CooldownContext getCooldownContext() {
2018
}
2119

2220
public Duration getRemainingDuration() {
23-
return remainingDuration;
21+
return Duration.between(Instant.now(), expirationTime);
2422
}
2523

2624
public Instant getExpirationTime() {
2725
return expirationTime;
2826
}
2927

28+
public boolean isExpired() {
29+
return Instant.now().isAfter(expirationTime);
30+
}
31+
3032
@Override
3133
public String toString() {
3234
return "CooldownState{" +
3335
"cooldownContext=" + cooldownContext.getKey() +
34-
", remainingDuration=" + remainingDuration +
3536
", expirationTime=" + expirationTime +
3637
'}';
3738
}
Lines changed: 8 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,31 @@
11
package dev.rollczi.litecommands.cooldown;
22

33
import dev.rollczi.litecommands.command.executor.CommandExecuteResult;
4-
import dev.rollczi.litecommands.command.executor.event.CommandExecutorEvent;
54
import dev.rollczi.litecommands.command.executor.event.CommandPostExecutionEvent;
65
import dev.rollczi.litecommands.command.executor.event.CommandPreExecutionEvent;
76
import dev.rollczi.litecommands.event.EventListener;
87
import dev.rollczi.litecommands.event.Subscriber;
98
import dev.rollczi.litecommands.invocation.Invocation;
10-
import dev.rollczi.litecommands.meta.Meta;
119
import dev.rollczi.litecommands.platform.PlatformSender;
12-
import dev.rollczi.litecommands.scheduler.Scheduler;
13-
import dev.rollczi.litecommands.scheduler.SchedulerPoll;
1410
import dev.rollczi.litecommands.shared.FailedReason;
15-
import java.time.Duration;
16-
import java.time.Instant;
17-
import java.util.HashMap;
18-
import java.util.List;
19-
import java.util.Map;
20-
import org.jetbrains.annotations.Nullable;
11+
import java.util.Optional;
2112

2213
public class CooldownStateController<SENDER> implements EventListener {
2314

24-
private final Scheduler scheduler;
25-
private final Map<CooldownCompositeKey, Instant> cooldowns = new HashMap<>();
15+
private final CooldownService cooldownService;
2616

27-
public CooldownStateController(Scheduler scheduler) {
28-
this.scheduler = scheduler;
17+
public CooldownStateController(CooldownService cooldownService) {
18+
this.cooldownService = cooldownService;
2919
}
3020

3121
@Subscriber
3222
public void onEvent(CommandPreExecutionEvent<SENDER> event) {
3323
Invocation<SENDER> invocation = event.getInvocation();
3424
PlatformSender sender = invocation.platformSender();
35-
CooldownContext cooldownContext = getOperativeContext(event, sender);
36-
37-
if (cooldownContext == null) {
38-
return;
39-
}
40-
41-
CooldownCompositeKey compositeKey = new CooldownCompositeKey(sender.getIdentifier(), cooldownContext.getKey());
25+
Optional<CooldownState> currentState = this.cooldownService.getState(event.getExecutor(), sender);
4226

43-
Instant now = Instant.now();
44-
Instant expirationTime = cooldowns.get(compositeKey);
45-
46-
if (expirationTime != null && expirationTime.isAfter(now)) {
47-
event.stopFlow(FailedReason.of(new CooldownState(cooldownContext, Duration.between(now, expirationTime), expirationTime)));
27+
if (currentState.isPresent()) {
28+
event.stopFlow(FailedReason.of(currentState.get()));
4829
}
4930
}
5031

@@ -58,35 +39,7 @@ public void onEvent(CommandPostExecutionEvent<SENDER> event) {
5839

5940
Invocation<SENDER> invocation = event.getInvocation();
6041
PlatformSender sender = invocation.platformSender();
61-
CooldownContext cooldownContext = getOperativeContext(event, sender);
62-
63-
if (cooldownContext == null) {
64-
return;
65-
}
66-
67-
CooldownCompositeKey compositeKey = new CooldownCompositeKey(sender.getIdentifier(), cooldownContext.getKey());
68-
69-
Instant now = Instant.now();
70-
cooldowns.put(compositeKey, now.plus(cooldownContext.getDuration()));
71-
scheduler.supplyLater(SchedulerPoll.MAIN, cooldownContext.getDuration(), () -> cooldowns.remove(compositeKey));
72-
}
73-
74-
@Nullable
75-
private CooldownContext getOperativeContext(CommandExecutorEvent event, PlatformSender sender) {
76-
List<CooldownContext> cooldownContexts = event.getExecutor().metaCollector().collect(Meta.COOLDOWN);
77-
78-
if (cooldownContexts.isEmpty()) {
79-
return null;
80-
}
81-
82-
CooldownContext cooldownContext = cooldownContexts.get(0);
83-
String bypassPermission = cooldownContext.getBypassPermission();
84-
85-
if (!bypassPermission.isEmpty() && sender.hasPermission(bypassPermission)) {
86-
return null;
87-
}
88-
89-
return cooldownContext;
42+
cooldownService.updateState(event.getExecutor(), sender);
9043
}
9144

9245
}

litecommands-core/src/dev/rollczi/litecommands/meta/MetaCollector.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import java.util.List;
44
import org.jetbrains.annotations.ApiStatus;
5+
import org.jetbrains.annotations.Contract;
6+
import org.jetbrains.annotations.Nullable;
57

68
public interface MetaCollector {
79

@@ -14,11 +16,15 @@ public interface MetaCollector {
1416

1517
<T> T findFirst(MetaKey<T> key);
1618

17-
<T> T findFirst(MetaKey<T> key, T defaultValue);
19+
@Nullable
20+
@Contract("_,null -> _")
21+
<T> T findFirst(MetaKey<T> key, @Nullable T defaultValue);
1822

1923
<T> T findLast(MetaKey<T> key);
2024

21-
<T> T findLast(MetaKey<T> key, T defaultValue);
25+
@Nullable
26+
@Contract("_,null -> _; _,!null -> !null")
27+
<T> T findLast(MetaKey<T> key, @Nullable T defaultValue);
2228

2329
static MetaCollector of(MetaHolder metaHolder) {
2430
return new MetaHolderCollectorImpl(metaHolder);

litecommands-core/src/dev/rollczi/litecommands/meta/MetaHolderCollectorImpl.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package dev.rollczi.litecommands.meta;
22

33
import java.util.ArrayList;
4-
import java.util.Collections;
54
import java.util.Iterator;
65
import java.util.List;
76
import java.util.NoSuchElementException;

litecommands-core/src/dev/rollczi/litecommands/platform/AbstractPlatform.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
public abstract class AbstractPlatform<SENDER, C extends PlatformSettings> implements Platform<SENDER, C> {
1010

1111
protected @NotNull C settings;
12+
protected final PlatformSenderFactory<SENDER> senderFactory;
1213
protected final Map<String, CommandRoute<SENDER>> commandRoutes = new HashMap<>();
1314

14-
protected AbstractPlatform(@NotNull C settings) {
15+
protected AbstractPlatform(@NotNull C settings, PlatformSenderFactory<SENDER> senderFactory) {
1516
this.settings = settings;
17+
this.senderFactory = senderFactory;
1618
}
1719

1820
@Override
@@ -26,6 +28,16 @@ public C getConfiguration() {
2628
return settings;
2729
}
2830

31+
@Override
32+
public PlatformSenderFactory<SENDER> getSenderFactory() {
33+
return senderFactory;
34+
}
35+
36+
@Override
37+
public PlatformSender createSender(SENDER nativeSender) {
38+
return this.getSenderFactory().create(nativeSender);
39+
}
40+
2941
@Override
3042
public final void register(CommandRoute<SENDER> commandRoute, PlatformInvocationListener<SENDER> invocationHook, PlatformSuggestionListener<SENDER> suggestionHook) {
3143
for (String name : commandRoute.names()) {

litecommands-core/src/dev/rollczi/litecommands/platform/Platform.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dev.rollczi.litecommands.platform;
22

33
import dev.rollczi.litecommands.command.CommandRoute;
4+
import org.jetbrains.annotations.ApiStatus;
45
import org.jetbrains.annotations.NotNull;
56

67
public interface Platform<SENDER, C extends PlatformSettings> {
@@ -10,6 +11,11 @@ public interface Platform<SENDER, C extends PlatformSettings> {
1011
@NotNull
1112
C getConfiguration();
1213

14+
@ApiStatus.Experimental
15+
PlatformSenderFactory<SENDER> getSenderFactory();
16+
17+
PlatformSender createSender(SENDER nativeSender);
18+
1319
default void start() {}
1420

1521
default void stop() {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package dev.rollczi.litecommands.platform;
2+
3+
/**
4+
* Factory for a {@link PlatformSender}
5+
* @param <SENDER> - Platform related sender e.g. CommandSender, User, CommandSource
6+
*/
7+
public interface PlatformSenderFactory<SENDER> {
8+
9+
PlatformSender create(SENDER sender);
10+
11+
}

0 commit comments

Comments
 (0)