Skip to content
Open
Show file tree
Hide file tree
Changes from all 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();
}
}