Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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"

}
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
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.eternalcode.core.delay;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;

import java.time.Duration;
import java.time.Instant;
Expand All @@ -13,11 +13,10 @@ public class Delay<T> {

private final Supplier<Duration> delaySettings;

public Delay(Supplier<Duration> delayProvider) {
private Delay(Supplier<Duration> delayProvider) {
this.delaySettings = delayProvider;

this.delays = CacheBuilder.newBuilder()
.expireAfterWrite(delayProvider.get())
this.delays = Caffeine.newBuilder()
.expireAfter(new InstantExpiry<T>())
.build();
}

Expand All @@ -34,17 +33,21 @@ public void unmarkDelay(T key) {
}

public boolean hasDelay(T key) {
Instant delayExpireMoment = this.getDelayExpireMoment(key);
Instant delayExpireMoment = this.getExpireAt(key);

return Instant.now().isBefore(delayExpireMoment);
}

public Duration getDurationToExpire(T key) {
return Duration.between(Instant.now(), this.getDelayExpireMoment(key));
public Duration getRemaining(T key) {
return Duration.between(Instant.now(), this.getExpireAt(key));
}

private Instant getDelayExpireMoment(T key) {
private Instant getExpireAt(T key) {
return this.delays.asMap().getOrDefault(key, Instant.MIN);
}

public static <T> Delay<T> withDefault(Supplier<Duration> defaultDelay) {
return new Delay<>(defaultDelay);
}

}
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(T key, Instant expireTime, long currentTime) {
return timeToExpire(expireTime);
}

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

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

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

long nanos = durationToExpire.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
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package com.eternalcode.core.delay;

import org.junit.jupiter.api.Test;

import java.time.Duration;
import java.util.UUID;

import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;

class DelayTest {

@Test
void shouldExpireAfterDefaultDelay() {
Delay<UUID> delay = Delay.withDefault(() -> Duration.ofMillis(500));
UUID key = UUID.randomUUID();

delay.markDelay(key);
assertThat(delay.hasDelay(key)).isTrue();

await()
.pollDelay(250, MILLISECONDS)
.atMost(500, MILLISECONDS)
.until(() -> delay.hasDelay(key));

await()
.atMost(Duration.ofMillis(350)) // After previously await (600 ms - 900 ms)
.until(() -> !delay.hasDelay(key));
}

@Test
void shouldDoNotExpireBeforeCustomDelay() {
Delay<UUID> delay = Delay.withDefault(() -> Duration.ofMillis(500));
UUID key = UUID.randomUUID();

delay.markDelay(key, Duration.ofMillis(1000));
assertThat(delay.hasDelay(key)).isTrue();

await()
.pollDelay(500, MILLISECONDS)
.atMost(1000, MILLISECONDS)
.until(() -> delay.hasDelay(key));

await()
.atMost(600, MILLISECONDS) // After previously await (1100 ms - 1600 ms)
.until(() -> !delay.hasDelay(key));
}

@Test
void shouldUnmarkDelay() {
Delay<UUID> delay = Delay.withDefault(() -> Duration.ofMillis(500));
UUID key = UUID.randomUUID();

delay.markDelay(key);
assertThat(delay.hasDelay(key)).isTrue();

delay.unmarkDelay(key);
assertThat(delay.hasDelay(key)).isFalse();
}

@Test
void shouldNotHaveDelayOnNonExistentKey() {
Delay<UUID> delay = Delay.withDefault(() -> Duration.ofMillis(500));
UUID key = UUID.randomUUID();

assertThat(delay.hasDelay(key)).isFalse();
}

@Test
void shouldReturnCorrectRemainingTime() {
Delay<UUID> delay = Delay.withDefault(() -> Duration.ofMillis(500));
UUID key = UUID.randomUUID();

delay.markDelay(key, Duration.ofMillis(1000));

// Immediately after marking, remaining time should be close to the full delay
assertThat(delay.getRemaining(key)).isCloseTo(Duration.ofMillis(1000), Duration.ofMillis(150));

// Wait for some time
await()
.pollDelay(400, MILLISECONDS)
.atMost(550, MILLISECONDS)
.untilAsserted(() -> {
// After 400ms, remaining time should be less than the original
assertThat(delay.getRemaining(key)).isLessThan(Duration.ofMillis(1000).minus(Duration.ofMillis(300)));
});

await()
.atMost(Duration.ofMillis(1000).plus(Duration.ofMillis(150)))
.until(() -> !delay.hasDelay(key));

// After expiration, remaining time should be negative
assertThat(delay.getRemaining(key)).isNegative();
}

@Test
void shouldHandleMultipleKeysIndependently() {
Delay<UUID> delay = Delay.withDefault(() -> Duration.ofMillis(500));
UUID shortTimeKey = UUID.randomUUID(); // 500ms
UUID longTimeKey = UUID.randomUUID(); // 1000ms

delay.markDelay(shortTimeKey);
delay.markDelay(longTimeKey, Duration.ofMillis(1000));

assertThat(delay.hasDelay(shortTimeKey)).isTrue();
assertThat(delay.hasDelay(longTimeKey)).isTrue();

// Wait for the first key to expire
await()
.atMost(Duration.ofMillis(500).plus(Duration.ofMillis(150)))
.until(() -> !delay.hasDelay(shortTimeKey));

// After first key expires, second should still be active
assertThat(delay.hasDelay(shortTimeKey)).isFalse();
assertThat(delay.hasDelay(longTimeKey)).isTrue();

// Wait for the second key to expire
await()
.atMost(Duration.ofMillis(1000))
.until(() -> !delay.hasDelay(longTimeKey));

assertThat(delay.hasDelay(longTimeKey)).isFalse();
}
}