Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,7 @@ object Versions {
// tests
const val JUNIT_BOM = "5.13.4"
const val MOCKITO_CORE = "5.20.0"
const val ASSERTJ_CORE = "3.26.3"
const val AWAITILITY = "4.2.1"

}

This file was deleted.

2 changes: 2 additions & 0 deletions eternalcore-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ dependencies {
annotationProcessor("org.projectlombok:lombok:${Versions.LOMBOK}")

testImplementation("com.eternalcode:eternalcode-commons-bukkit:${Versions.ETERNALCODE_COMMONS}")
testImplementation("org.assertj:assertj-core:${Versions.ASSERTJ_CORE}")
testImplementation("org.awaitility:awaitility:${Versions.AWAITILITY}")
}

eternalShadow {
Expand Down
136 changes: 136 additions & 0 deletions eternalcore-core/src/main/java/com/eternalcode/core/delay/Delay.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package com.eternalcode.core.delay;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;

import java.time.Duration;
import java.time.Instant;
import java.util.function.Supplier;

/**
* Provides per-entry delay management using Caffeine with wall-clock based expiration.
*
* @param <T> the key type
*/
public class Delay<T> {

private static final long DEFAULT_MAXIMUM_SIZE = 50_000L;

private final Cache<T, Instant> cache;
private final Supplier<Duration> defaultDelay;

/**
* Creates a new delay with a default delay supplier and cache size limit.
*
* @param defaultDelay supplier providing default delay durations
* @param maximumSize maximum number of cached entries
*/
private Delay(Supplier<Duration> defaultDelay, long maximumSize) {
if (defaultDelay == null) {
throw new IllegalArgumentException("defaultDelay cannot be null");
}

if (maximumSize <= 0) {
throw new IllegalArgumentException("maximumSize must be > 0");
}

this.defaultDelay = defaultDelay;
this.cache = Caffeine.newBuilder()
.maximumSize(maximumSize)
.expireAfter(new InstantExpiry<T>())
.build();
}

/**
* Creates a new delay manager with the default maximum cache size.
*
* @param defaultDelay supplier providing default delay durations
*/
private Delay(Supplier<Duration> defaultDelay) {
this(defaultDelay, DEFAULT_MAXIMUM_SIZE);
}

/**
* Marks a delay for the given key using a specific duration.
*
* @param key the key to delay
* @param delay the duration of the delay
*/
public void markDelay(T key, Duration delay) {
if (delay.isZero() || delay.isNegative()) {
this.cache.invalidate(key);
}

this.cache.put(key, Instant.now().plus(delay));
}

/**
* Marks a delay for the given key using the default duration.
*
* @param key the key to delay
*/
public void markDelay(T key) {
this.markDelay(key, this.defaultDelay.get());
}

/**
* Removes any existing delay for the given key.
*
* @param key the key to clear
*/
public void unmarkDelay(T key) {
this.cache.invalidate(key);
}

/**
* Checks whether the given key currently has an active delay.
*
* @param key the key to check
* @return true if the delay is active, false otherwise
*/
public boolean hasDelay(T key) {
Instant delayExpireMoment = this.getExpireAt(key);
return Instant.now().isBefore(delayExpireMoment);
}

/**
* Returns the remaining delay duration for the given key.
*
* @param key the key to check
* @return the remaining duration, or {@code Duration.ZERO} if expired
*/
public Duration getRemaining(T key) {
return Duration.between(Instant.now(), this.getExpireAt(key));
}

/**
* Returns the expiration instant for the given key.
*
* @param key the key to check
* @return the expiration instant, or {@code Instant.MIN} if none
*/
private Instant getExpireAt(T key) {
return this.cache.asMap().getOrDefault(key, Instant.MIN);
}

/**
* Creates a new {@link Delay} instance with a default delay supplier.
*
* @param defaultDelay supplier providing default delay durations
* @return a new Delay instance
*/
public static <T> Delay<T> withDefault(Supplier<Duration> defaultDelay) {
return new Delay<>(defaultDelay);
}

/**
* Creates a new {@link Delay} instance with a default delay supplier and cache size.
*
* @param defaultDelay supplier providing default delay durations
* @param maximumSize maximum number of cached entries
* @return a new Delay instance
*/
public static <T> Delay<T> withDefault(Supplier<Duration> defaultDelay, long maximumSize) {
return new Delay<>(defaultDelay, maximumSize);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.eternalcode.core.delay;

import com.github.benmanes.caffeine.cache.Expiry;
import java.time.Duration;
import java.time.Instant;
import org.jetbrains.annotations.NotNull;

class InstantExpiry<T> implements Expiry<@NotNull T, @NotNull Instant> {

@Override
public long expireAfterCreate(@NotNull T key, @NotNull Instant expireTime, long currentTime) {
return timeToExpire(expireTime);
}

@Override
public long expireAfterUpdate(@NotNull T key, @NotNull Instant newExpireTime, long currentTime, long currentDuration) {
return timeToExpire(newExpireTime);
}

@Override
public long expireAfterRead(@NotNull T key, @NotNull Instant value, long currentTime, long currentDuration) {
return currentDuration;
}

private static long timeToExpire(Instant expireTime) {
Duration toExpire = Duration.between(Instant.now(), expireTime);
if (toExpire.isNegative()) {
return 0;
}

long nanos = toExpire.toNanos();
if (nanos == 0) {
return 1;
}

return nanos;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class AfkCommand {
this.noticeService = noticeService;
this.afkSettings = afkSettings;
this.afkService = afkService;
this.delay = new Delay<>(() -> this.afkSettings.afkCommandDelay());
this.delay = Delay.withDefault(() -> this.afkSettings.afkCommandDelay());
}

@Execute
Expand All @@ -51,7 +51,7 @@ void execute(@Sender Player player) {
}

if (this.delay.hasDelay(uuid)) {
Duration time = this.delay.getDurationToExpire(uuid);
Duration time = this.delay.getRemaining(uuid);

this.noticeService
.create()
Expand All @@ -64,6 +64,6 @@ void execute(@Sender Player player) {
}

this.afkService.switchAfk(uuid, AfkReason.COMMAND);
this.delay.markDelay(uuid, this.afkSettings.afkCommandDelay());
this.delay.markDelay(uuid);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class HelpOpCommand {
this.helpOpSettings = helpOpSettings;
this.eventCaller = eventCaller;
this.server = server;
this.delay = new Delay<>(() -> this.helpOpSettings.helpOpDelay());
this.delay = Delay.withDefault(() -> this.helpOpSettings.helpOpDelay());
}

@Execute
Expand All @@ -58,7 +58,7 @@ void execute(@Sender Player player, @Join String message) {
}

if (this.delay.hasDelay(uuid)) {
Duration time = this.delay.getDurationToExpire(uuid);
Duration time = this.delay.getRemaining(uuid);

this.noticeService.create()
.notice(translation -> translation.helpOp().helpOpDelay())
Expand Down Expand Up @@ -91,7 +91,7 @@ void execute(@Sender Player player, @Join String message) {
.notice(translation -> translation.helpOp().send())
.send();

this.delay.markDelay(uuid, this.helpOpSettings.helpOpDelay());
this.delay.markDelay(uuid);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class RandomTeleportCommand {
this.randomTeleportService = randomTeleportService;
this.randomTeleportTaskService = randomTeleportTaskService;
this.randomTeleportSettings = randomTeleportSettings;
this.cooldown = new Delay<>(() -> this.randomTeleportSettings.cooldown());
this.cooldown = Delay.withDefault(() -> this.randomTeleportSettings.cooldown());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tutaj musi być provider () -> bo pobieramy wartość z configu a ona się reloaduje czasem

}

@Execute
Expand Down Expand Up @@ -68,7 +68,7 @@ void executeSelf(@Sender Player player) {
this.handleTeleportSuccess(player);
});

this.cooldown.markDelay(uuid, this.randomTeleportSettings.cooldown());
this.cooldown.markDelay(uuid);
}

@Execute
Expand Down Expand Up @@ -96,7 +96,7 @@ void executeOther(@Sender Viewer sender, @Arg Player player) {
this.handleAdminTeleport(sender, player);
});

this.cooldown.markDelay(uuid, this.randomTeleportSettings.cooldown());
this.cooldown.markDelay(uuid);
}

private void handleTeleportSuccess(Player player) {
Expand Down Expand Up @@ -129,7 +129,7 @@ private boolean hasRandomTeleportDelay(Player player) {
}

if (this.cooldown.hasDelay(uniqueId)) {
Duration time = this.cooldown.getDurationToExpire(uniqueId);
Duration time = this.cooldown.getRemaining(uniqueId);

this.noticeService.create()
.notice(translation -> translation.randomTeleport().randomTeleportDelay())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class RepairCommand {
RepairCommand(NoticeService noticeService, RepairSettings repairSettings) {
this.noticeService = noticeService;
this.repairSettings = repairSettings;
this.delay = new Delay<>(() -> this.repairSettings.repairDelay());
this.delay = Delay.withDefault(() -> this.repairSettings.repairDelay());
}

@Execute
Expand Down Expand Up @@ -73,7 +73,7 @@ void repair(@Sender Player player) {
.player(player.getUniqueId())
.send();

this.delay.markDelay(uuid, this.repairSettings.repairDelay());
this.delay.markDelay(uuid);
}

@Execute(name = "all")
Expand Down Expand Up @@ -117,7 +117,7 @@ void repairAll(@Sender Player player) {
.player(player.getUniqueId())
.send();

this.delay.markDelay(uuid, this.repairSettings.repairDelay());
this.delay.markDelay(uuid);
}

@Execute(name = "armor")
Expand Down Expand Up @@ -161,12 +161,12 @@ void repairArmor(@Sender Player player) {
.player(player.getUniqueId())
.send();

this.delay.markDelay(uuid, this.repairSettings.repairDelay());
this.delay.markDelay(uuid);
}

private boolean hasRepairDelay(UUID uuid) {
if (this.delay.hasDelay(uuid)) {
Duration time = this.delay.getDurationToExpire(uuid);
Duration time = this.delay.getRemaining(uuid);

this.noticeService
.create()
Expand Down
Loading