Skip to content

Commit

Permalink
GH-496 Create and expose Cooldown API. Create PlatformSender factory (#…
Browse files Browse the repository at this point in the history
…496)

* Create and expose cooldown API

* Create platform sender factory and expose platform API.

* Improve cooldown context API

* Code style fix
  • Loading branch information
Rollczi authored Dec 11, 2024
1 parent 2d25f16 commit 72a8b07
Show file tree
Hide file tree
Showing 26 changed files with 187 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
class BukkitPlatform extends AbstractPlatform<CommandSender, LiteBukkitSettings> implements Platform<CommandSender, LiteBukkitSettings> {

BukkitPlatform(LiteBukkitSettings settings) {
super(settings);
super(settings, sender -> new BukkitPlatformSender(sender));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class BungeePlatform extends AbstractPlatform<CommandSender, LiteBungeeSettings>
private final PluginManager pluginManager;

public BungeePlatform(Plugin plugin, LiteBungeeSettings liteBungeeSettings) {
super(liteBungeeSettings);
super(liteBungeeSettings, sender -> new BungeeSender(sender));
this.plugin = plugin;
this.pluginManager = plugin.getProxy().getPluginManager();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dev.rollczi.litecommands;

import dev.rollczi.litecommands.command.CommandManager;
import dev.rollczi.litecommands.platform.Platform;
import org.jetbrains.annotations.ApiStatus;

public interface LiteCommands<SENDER> {
Expand All @@ -10,6 +11,8 @@ public interface LiteCommands<SENDER> {

CommandManager<SENDER> getCommandManager();

Platform<SENDER, ?> getPlatform();

@ApiStatus.Experimental
LiteCommandsInternal<SENDER, ?> getInternal();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import dev.rollczi.litecommands.argument.parser.ParserRegistry;
import dev.rollczi.litecommands.bind.BindRegistry;
import dev.rollczi.litecommands.context.ContextRegistry;
import dev.rollczi.litecommands.cooldown.CooldownService;
import dev.rollczi.litecommands.handler.result.ResultHandleService;
import dev.rollczi.litecommands.command.builder.CommandBuilderCollector;
import dev.rollczi.litecommands.editor.EditorService;
Expand Down Expand Up @@ -61,4 +62,7 @@ public interface LiteCommandsInternal<SENDER, C extends PlatformSettings> {
@ApiStatus.Internal
StrictService getStrictService();

@ApiStatus.Experimental
CooldownService getCooldownService();

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.rollczi.litecommands.cooldown;

import dev.rollczi.litecommands.util.StringUtil;
import java.time.Duration;

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

public CooldownContext(String key, Duration duration) {
this(key, duration, StringUtil.EMPTY);
}

public String getKey() {
return key;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package dev.rollczi.litecommands.cooldown;

import dev.rollczi.litecommands.command.executor.CommandExecutor;
import dev.rollczi.litecommands.identifier.Identifier;
import dev.rollczi.litecommands.meta.Meta;
import dev.rollczi.litecommands.platform.PlatformSender;
import dev.rollczi.litecommands.scheduler.Scheduler;
import dev.rollczi.litecommands.scheduler.SchedulerPoll;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;

@ApiStatus.Experimental
public class CooldownService {

private final Scheduler scheduler;
private final Map<CooldownCompositeKey, CooldownState> cooldowns = new HashMap<>();

public CooldownService(Scheduler scheduler) {
this.scheduler = scheduler;
}

Optional<CooldownState> getState(CommandExecutor<?> executor, PlatformSender sender) {
CooldownContext cooldownContext = getOperativeContext(executor, sender);

if (cooldownContext == null) {
return Optional.empty();
}

return getState(cooldownContext.getKey(), sender.getIdentifier());
}

public Optional<CooldownState> getState(String key, Identifier senderIdentifier) {
CooldownCompositeKey compositeKey = new CooldownCompositeKey(senderIdentifier, key);
CooldownState cooldownState = cooldowns.get(compositeKey);

if (cooldownState != null && !cooldownState.isExpired()) {
return Optional.of(cooldownState);
}

return Optional.empty();
}

boolean updateState(CommandExecutor<?> executor, PlatformSender sender) {
CooldownContext cooldownContext = getOperativeContext(executor, sender);

if (cooldownContext == null) {
return false;
}

return updateState(cooldownContext, sender.getIdentifier());
}

public boolean updateState(CooldownContext context, Identifier senderIdentifier) {
CooldownCompositeKey compositeKey = new CooldownCompositeKey(senderIdentifier, context.getKey());

Instant now = Instant.now();
Instant expirationTime = now.plus(context.getDuration());
cooldowns.put(compositeKey, new CooldownState(context, expirationTime));
scheduler.supplyLater(SchedulerPoll.MAIN, context.getDuration(), () -> cooldowns.remove(compositeKey));
return true;
}

public boolean clearState(String key, Identifier senderIdentifier) {
CooldownCompositeKey compositeKey = new CooldownCompositeKey(senderIdentifier, key);

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

@Nullable
private CooldownContext getOperativeContext(CommandExecutor<?> executor, PlatformSender sender) {
CooldownContext cooldownContext = executor.metaCollector().findFirst(Meta.COOLDOWN, null);

if (cooldownContext == null) {
return null;
}

String bypassPermission = cooldownContext.getBypassPermission();

if (!bypassPermission.isEmpty() && sender.hasPermission(bypassPermission)) {
return null;
}

return cooldownContext;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@
public class CooldownState {

private final CooldownContext cooldownContext;
private final Duration remainingDuration;
private final Instant expirationTime;

public CooldownState(CooldownContext cooldownContext, Duration remainingDuration, Instant expirationTime) {
public CooldownState(CooldownContext cooldownContext, Instant expirationTime) {
this.cooldownContext = cooldownContext;
this.remainingDuration = remainingDuration;
this.expirationTime = expirationTime;
}

Expand All @@ -20,18 +18,21 @@ public CooldownContext getCooldownContext() {
}

public Duration getRemainingDuration() {
return remainingDuration;
return Duration.between(Instant.now(), expirationTime);
}

public Instant getExpirationTime() {
return expirationTime;
}

public boolean isExpired() {
return Instant.now().isAfter(expirationTime);
}

@Override
public String toString() {
return "CooldownState{" +
"cooldownContext=" + cooldownContext.getKey() +
", remainingDuration=" + remainingDuration +
", expirationTime=" + expirationTime +
'}';
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,50 +1,31 @@
package dev.rollczi.litecommands.cooldown;

import dev.rollczi.litecommands.command.executor.CommandExecuteResult;
import dev.rollczi.litecommands.command.executor.event.CommandExecutorEvent;
import dev.rollczi.litecommands.command.executor.event.CommandPostExecutionEvent;
import dev.rollczi.litecommands.command.executor.event.CommandPreExecutionEvent;
import dev.rollczi.litecommands.event.EventListener;
import dev.rollczi.litecommands.event.Subscriber;
import dev.rollczi.litecommands.invocation.Invocation;
import dev.rollczi.litecommands.meta.Meta;
import dev.rollczi.litecommands.platform.PlatformSender;
import dev.rollczi.litecommands.scheduler.Scheduler;
import dev.rollczi.litecommands.scheduler.SchedulerPoll;
import dev.rollczi.litecommands.shared.FailedReason;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;

public class CooldownStateController<SENDER> implements EventListener {

private final Scheduler scheduler;
private final Map<CooldownCompositeKey, Instant> cooldowns = new HashMap<>();
private final CooldownService cooldownService;

public CooldownStateController(Scheduler scheduler) {
this.scheduler = scheduler;
public CooldownStateController(CooldownService cooldownService) {
this.cooldownService = cooldownService;
}

@Subscriber
public void onEvent(CommandPreExecutionEvent<SENDER> event) {
Invocation<SENDER> invocation = event.getInvocation();
PlatformSender sender = invocation.platformSender();
CooldownContext cooldownContext = getOperativeContext(event, sender);

if (cooldownContext == null) {
return;
}

CooldownCompositeKey compositeKey = new CooldownCompositeKey(sender.getIdentifier(), cooldownContext.getKey());
Optional<CooldownState> currentState = this.cooldownService.getState(event.getExecutor(), sender);

Instant now = Instant.now();
Instant expirationTime = cooldowns.get(compositeKey);

if (expirationTime != null && expirationTime.isAfter(now)) {
event.stopFlow(FailedReason.of(new CooldownState(cooldownContext, Duration.between(now, expirationTime), expirationTime)));
if (currentState.isPresent()) {
event.stopFlow(FailedReason.of(currentState.get()));
}
}

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

Invocation<SENDER> invocation = event.getInvocation();
PlatformSender sender = invocation.platformSender();
CooldownContext cooldownContext = getOperativeContext(event, sender);

if (cooldownContext == null) {
return;
}

CooldownCompositeKey compositeKey = new CooldownCompositeKey(sender.getIdentifier(), cooldownContext.getKey());

Instant now = Instant.now();
cooldowns.put(compositeKey, now.plus(cooldownContext.getDuration()));
scheduler.supplyLater(SchedulerPoll.MAIN, cooldownContext.getDuration(), () -> cooldowns.remove(compositeKey));
}

@Nullable
private CooldownContext getOperativeContext(CommandExecutorEvent event, PlatformSender sender) {
List<CooldownContext> cooldownContexts = event.getExecutor().metaCollector().collect(Meta.COOLDOWN);

if (cooldownContexts.isEmpty()) {
return null;
}

CooldownContext cooldownContext = cooldownContexts.get(0);
String bypassPermission = cooldownContext.getBypassPermission();

if (!bypassPermission.isEmpty() && sender.hasPermission(bypassPermission)) {
return null;
}

return cooldownContext;
cooldownService.updateState(event.getExecutor(), sender);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.util.List;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;

public interface MetaCollector {

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

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

<T> T findFirst(MetaKey<T> key, T defaultValue);
@Nullable
@Contract("_,null -> _")
<T> T findFirst(MetaKey<T> key, @Nullable T defaultValue);

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

<T> T findLast(MetaKey<T> key, T defaultValue);
@Nullable
@Contract("_,null -> _; _,!null -> !null")
<T> T findLast(MetaKey<T> key, @Nullable T defaultValue);

static MetaCollector of(MetaHolder metaHolder) {
return new MetaHolderCollectorImpl(metaHolder);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package dev.rollczi.litecommands.meta;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
public abstract class AbstractPlatform<SENDER, C extends PlatformSettings> implements Platform<SENDER, C> {

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

protected AbstractPlatform(@NotNull C settings) {
protected AbstractPlatform(@NotNull C settings, PlatformSenderFactory<SENDER> senderFactory) {
this.settings = settings;
this.senderFactory = senderFactory;
}

@Override
Expand All @@ -26,6 +28,16 @@ public C getConfiguration() {
return settings;
}

@Override
public PlatformSenderFactory<SENDER> getSenderFactory() {
return senderFactory;
}

@Override
public PlatformSender createSender(SENDER nativeSender) {
return this.getSenderFactory().create(nativeSender);
}

@Override
public final void register(CommandRoute<SENDER> commandRoute, PlatformInvocationListener<SENDER> invocationHook, PlatformSuggestionListener<SENDER> suggestionHook) {
for (String name : commandRoute.names()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dev.rollczi.litecommands.platform;

import dev.rollczi.litecommands.command.CommandRoute;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;

public interface Platform<SENDER, C extends PlatformSettings> {
Expand All @@ -10,6 +11,11 @@ public interface Platform<SENDER, C extends PlatformSettings> {
@NotNull
C getConfiguration();

@ApiStatus.Experimental
PlatformSenderFactory<SENDER> getSenderFactory();

PlatformSender createSender(SENDER nativeSender);

default void start() {}

default void stop() {}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package dev.rollczi.litecommands.platform;

/**
* Factory for a {@link PlatformSender}
* @param <SENDER> - Platform related sender e.g. CommandSender, User, CommandSource
*/
public interface PlatformSenderFactory<SENDER> {

PlatformSender create(SENDER sender);

}
Loading

0 comments on commit 72a8b07

Please sign in to comment.