diff --git a/eternalcore-api/src/main/java/com/eternalcode/core/delay/DefaultDelay.java b/eternalcore-api/src/main/java/com/eternalcode/core/delay/DefaultDelay.java new file mode 100644 index 000000000..93c1afd3b --- /dev/null +++ b/eternalcore-api/src/main/java/com/eternalcode/core/delay/DefaultDelay.java @@ -0,0 +1,14 @@ +package com.eternalcode.core.delay; + +/** + * Delay with a predefined default duration, with an option to override per call. + * + * @param key type + */ +public interface DefaultDelay extends ExplicitDelay { + + /** + * Marks the key using the configured default duration. + */ + void markDelay(T key); +} diff --git a/eternalcore-api/src/main/java/com/eternalcode/core/delay/Delay.java b/eternalcore-api/src/main/java/com/eternalcode/core/delay/Delay.java index 8d30ad07c..22a8c789d 100644 --- a/eternalcore-api/src/main/java/com/eternalcode/core/delay/Delay.java +++ b/eternalcore-api/src/main/java/com/eternalcode/core/delay/Delay.java @@ -1,50 +1,61 @@ package com.eternalcode.core.delay; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; - import java.time.Duration; -import java.time.Instant; -import java.util.function.Supplier; - -public class Delay { - - private final Cache delays; - private final Supplier delaySettings; - - public Delay(Supplier delayProvider) { - this.delaySettings = delayProvider; - - this.delays = CacheBuilder.newBuilder() - .expireAfterWrite(delayProvider.get()) - .build(); +/** + * Factory class for creating delay instances. + *

+ * Naming convention: + * - withDefault(...) -> DefaultDelay (uses predefined Duration) + * - explicit(...) -> ExplicitDelay (requires explicit Duration each time) + */ +public final class Delay { + + private Delay() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); } - public void markDelay(T key, Duration delay) { - this.delays.put(key, Instant.now().plus(delay)); + /** + * Creates a DefaultDelay with the default cache size. + * + * @param defaultDelay the default duration used for delays + * @param the key type + * @return DefaultDelay instance + */ + public static DefaultDelay withDefault(Duration defaultDelay) { + return new GuavaDefaultDelay<>(defaultDelay); } - public void markDelay(T key) { - this.markDelay(key, this.delaySettings.get()); + /** + * Creates a DefaultDelay with a custom maximum cache size. + * + * @param defaultDelay the default duration used for delays + * @param maximumSize maximum number of entries in the cache + * @param the key type + * @return DefaultDelay instance + */ + public static DefaultDelay withDefault(Duration defaultDelay, long maximumSize) { + return new GuavaDefaultDelay<>(defaultDelay, maximumSize); } - public void unmarkDelay(T key) { - this.delays.invalidate(key); + /** + * Creates an ExplicitDelay with the default cache size. + * + * @param the key type + * @return ExplicitDelay instance + */ + public static ExplicitDelay explicit() { + return new GuavaExplicitDelay<>(); } - public boolean hasDelay(T key) { - Instant delayExpireMoment = this.getDelayExpireMoment(key); - - return Instant.now().isBefore(delayExpireMoment); + /** + * Creates an ExplicitDelay with a custom maximum cache size. + * + * @param maximumSize maximum number of entries in the cache + * @param the key type + * @return ExplicitDelay instance + */ + public static ExplicitDelay explicit(long maximumSize) { + return new GuavaExplicitDelay<>(maximumSize); } - - public Duration getDurationToExpire(T key) { - return Duration.between(Instant.now(), this.getDelayExpireMoment(key)); - } - - private Instant getDelayExpireMoment(T key) { - return this.delays.asMap().getOrDefault(key, Instant.MIN); - } - } diff --git a/eternalcore-api/src/main/java/com/eternalcode/core/delay/DelayActions.java b/eternalcore-api/src/main/java/com/eternalcode/core/delay/DelayActions.java new file mode 100644 index 000000000..07abc0c30 --- /dev/null +++ b/eternalcore-api/src/main/java/com/eternalcode/core/delay/DelayActions.java @@ -0,0 +1,39 @@ +package com.eternalcode.core.delay; + +import java.time.Duration; +import java.time.Instant; + +/** + * Common delay operations shared by all delay types. + * + * @param key type + */ +interface DelayActions { + + /** + * Removes any existing delay for the key. + * */ + void unmarkDelay(T key); + + /** + * @return true, if the key has an active delay; expired entries are cleaned on read. + */ + boolean hasDelay(T key); + + /** + * @return remaining duration or {@code Duration.ZERO} if none/expired. + */ + Duration getRemaining(T key); + + /** + * @return expiration instant or {@code null} if none/expired. + */ + Instant getExpireAt(T key); + + /** + * Extends current delay by {@code extra}; if none/expired, starts now. + * Non-positive durations are ignored. + */ + void extendDelay(T key, Duration extra); +} + diff --git a/eternalcore-api/src/main/java/com/eternalcode/core/delay/ExplicitDelay.java b/eternalcore-api/src/main/java/com/eternalcode/core/delay/ExplicitDelay.java new file mode 100644 index 000000000..651cf643a --- /dev/null +++ b/eternalcore-api/src/main/java/com/eternalcode/core/delay/ExplicitDelay.java @@ -0,0 +1,17 @@ +package com.eternalcode.core.delay; + +import java.time.Duration; + +/** + * Delay that requires an explicit duration on marking. + * + * @param key type + */ +public interface ExplicitDelay extends DelayActions { + + /** + * Marks the key with the given duration. + * Non-positive durations remove the entry. + */ + void markDelay(T key, Duration duration); +} diff --git a/eternalcore-api/src/main/java/com/eternalcode/core/delay/GuavaDefaultDelay.java b/eternalcore-api/src/main/java/com/eternalcode/core/delay/GuavaDefaultDelay.java new file mode 100644 index 000000000..b3cb6b3c7 --- /dev/null +++ b/eternalcore-api/src/main/java/com/eternalcode/core/delay/GuavaDefaultDelay.java @@ -0,0 +1,58 @@ +package com.eternalcode.core.delay; + +import java.time.Duration; + +/** + * {@link DefaultDelay} implementation backed by {@link GuavaDelay}. + *

+ * Stores a per-entry expiration instant in the cache. Unlike using + * {@code expireAfterWrite}, each entry is managed individually by its + * {@link java.time.Instant}. + *

+ * Calling {@link #markDelay(Object)} uses the predefined {@link #defaultDelay}. + * + * @param the type of key used to identify delays + */ +final class GuavaDefaultDelay extends GuavaDelay implements DefaultDelay { + + private final Duration defaultDelay; + + /** + * Creates a new delay manager with the given default delay and + * the default maximum cache size. + * + * @param defaultDelay the default duration applied when marking a key, + * must be positive + */ + GuavaDefaultDelay(Duration defaultDelay) { + this(defaultDelay, DEFAULT_MAXIMUM_SIZE); + } + + /** + * Creates a new delay manager with the given default delay and a + * custom maximum cache size. + * + * @param defaultDelay the default duration applied when marking a key, + * must be positive + * @param maximumSize maximum number of entries allowed in the cache + */ + GuavaDefaultDelay(Duration defaultDelay, long maximumSize) { + super(maximumSize); + this.defaultDelay = defaultDelay; + } + + /** + * Marks the specified key with the configured {@link #defaultDelay}. + * + * @param key the key to mark + */ + @Override + public void markDelay(T key) { + putDelay(key, this.defaultDelay); + } + + @Override + public void markDelay(T key, Duration duration) { + putDelay(key, duration); + } +} diff --git a/eternalcore-api/src/main/java/com/eternalcode/core/delay/GuavaDelay.java b/eternalcore-api/src/main/java/com/eternalcode/core/delay/GuavaDelay.java new file mode 100644 index 000000000..85db812ab --- /dev/null +++ b/eternalcore-api/src/main/java/com/eternalcode/core/delay/GuavaDelay.java @@ -0,0 +1,162 @@ +package com.eternalcode.core.delay; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + +import java.time.Duration; +import java.time.Instant; + +/** + * Base class providing shared logic for delay implementations using a Guava cache. + *

+ * Each key is associated with an {@link Instant} representing the expiration time. + * Expired entries are cleaned up eagerly on read operations. + *

+ * Contract: + *

    + *
  • {@link #getRemaining(Object)} returns {@code Duration.ZERO} if no active delay exists.
  • + *
  • {@link #getExpireAt(Object)} returns {@code null} if no active delay exists.
  • + *
  • Durations <= 0 in {@code putDelay} or {@code extendDelay} are treated as no delay (entry is removed).
  • + *
+ *

+ * Thread-safe as guaranteed by Guava's {@link Cache}. + * + * @param the type of key used to identify delays + */ +abstract class GuavaDelay { + + /** Default maximum number of cache entries. */ + protected static final long DEFAULT_MAXIMUM_SIZE = 50_000L; + + /** Underlying Guava cache mapping keys to expiration instants. */ + protected final Cache cache; + + /** + * Creates a new GuavaDelay with a custom maximum cache size. + * + * @param maximumSize the maximum number of entries in the cache + */ + protected GuavaDelay(long maximumSize) { + this.cache = CacheBuilder.newBuilder() + .maximumSize(maximumSize) + .build(); + } + + /** + * Stores a delay until the given expiration instant. + * + * @param key the key to mark + * @param expireAt the expiration instant + */ + protected void putDelay(T key, Instant expireAt) { + this.cache.put(key, expireAt); + } + + /** + * Stores a delay for the given duration starting from now. + * Durations <= 0 are treated as no delay and will remove the entry. + * + * @param key the key to mark + * @param duration the delay duration + */ + protected void putDelay(T key, Duration duration) { + if (duration.isZero() || duration.isNegative()) { + this.cache.invalidate(key); + return; + } + + this.cache.put(key, Instant.now().plus(duration)); + } + + /** + * Removes any existing delay for the given key. + * + * @param key the key to unmark + */ + public void unmarkDelay(T key) { + this.cache.invalidate(key); + } + + /** + * Checks if the given key currently has an active delay. + * Expired entries are removed on read. + * + * @param key the key to check + * @return true if an active delay exists, false otherwise + */ + public boolean hasDelay(T key) { + Instant until = this.cache.getIfPresent(key); + if (until == null) { + return false; + } + + if (!Instant.now().isBefore(until)) { + this.cache.invalidate(key); + return false; + } + + return true; + } + + /** + * Returns the remaining duration of the delay for the given key. + * Returns {@code Duration.ZERO} if no active delay exists. + * + * @param key the key to check + * @return remaining duration, or {@code Duration.ZERO} if none + */ + public Duration getRemaining(T key) { + Instant until = this.cache.getIfPresent(key); + if (until == null) { + return Duration.ZERO; + } + + Duration left = Duration.between(Instant.now(), until); + if (left.isNegative() || left.isZero()) { + this.cache.invalidate(key); + return Duration.ZERO; + } + + return left; + } + + /** + * Returns the expiration instant of the delay for the given key. + * Returns {@code null} if no active delay exists. + * + * @param key the key to check + * @return expiration instant, or {@code null} if none + */ + public Instant getExpireAt(T key) { + Instant until = this.cache.getIfPresent(key); + if (until == null) { + return null; + } + + if (Instant.now().isAfter(until)) { + this.cache.invalidate(key); + return null; + } + + return until; + } + + /** + * Extends the delay for the given key by the specified duration. + * If no active delay exists, a new one is created starting now. + * Durations <= 0 are ignored. + * + * @param key the key to extend + * @param extra the duration to add + */ + public void extendDelay(T key, Duration extra) { + if (extra.isZero() || extra.isNegative()) { + return; + } + + Instant base = this.cache.getIfPresent(key); + Instant now = Instant.now(); + Instant start = (base == null || !now.isBefore(base)) ? now : base; + this.cache.put(key, start.plus(extra)); + } +} diff --git a/eternalcore-api/src/main/java/com/eternalcode/core/delay/GuavaExplicitDelay.java b/eternalcore-api/src/main/java/com/eternalcode/core/delay/GuavaExplicitDelay.java new file mode 100644 index 000000000..d53cc5104 --- /dev/null +++ b/eternalcore-api/src/main/java/com/eternalcode/core/delay/GuavaExplicitDelay.java @@ -0,0 +1,34 @@ +package com.eternalcode.core.delay; + +import java.time.Duration; + +/** + * ExplicitDelay implementation backed by {@link GuavaDelay} using Guava cache. + *

+ * Each key must be marked with an explicit duration when creating a delay. + * + * @param the type of key used to identify the delay + */ +final class GuavaExplicitDelay extends GuavaDelay implements ExplicitDelay { + + /** + * Creates a new ExplicitDelay with the default maximum cache size. + */ + GuavaExplicitDelay() { + this(DEFAULT_MAXIMUM_SIZE); + } + + /** + * Creates a new ExplicitDelay with a custom maximum cache size. + * + * @param maximumSize maximum number of entries allowed in the cache + */ + GuavaExplicitDelay(long maximumSize) { + super(maximumSize); + } + + @Override + public void markDelay(T key, Duration duration) { + putDelay(key, duration); + } +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/afk/AfkCommand.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/afk/AfkCommand.java index d37aa2270..c61292ed5 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/afk/AfkCommand.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/afk/AfkCommand.java @@ -1,9 +1,8 @@ package com.eternalcode.core.feature.afk; -import static com.eternalcode.core.feature.afk.AfkCommand.AFK_BYPASS_PERMISSION; - import com.eternalcode.annotations.scan.command.DescriptionDocs; import com.eternalcode.annotations.scan.permission.PermissionDocs; +import com.eternalcode.core.delay.DefaultDelay; import com.eternalcode.core.delay.Delay; import com.eternalcode.core.injector.annotations.Inject; import com.eternalcode.core.notice.NoticeService; @@ -12,9 +11,12 @@ import dev.rollczi.litecommands.annotations.context.Sender; import dev.rollczi.litecommands.annotations.execute.Execute; import dev.rollczi.litecommands.annotations.permission.Permission; +import org.bukkit.entity.Player; + import java.time.Duration; import java.util.UUID; -import org.bukkit.entity.Player; + +import static com.eternalcode.core.feature.afk.AfkCommand.AFK_BYPASS_PERMISSION; @Command(name = "afk") @Permission("eternalcore.afk") @@ -30,14 +32,14 @@ class AfkCommand { private final NoticeService noticeService; private final AfkSettings afkSettings; private final AfkService afkService; - private final Delay delay; + private final DefaultDelay delay; @Inject AfkCommand(NoticeService noticeService, AfkSettings afkSettings, AfkService afkService) { this.noticeService = noticeService; this.afkSettings = afkSettings; this.afkService = afkService; - this.delay = new Delay<>(() -> this.afkSettings.afkCommandDelay()); + this.delay = Delay.withDefault(this.afkSettings.afkCommandDelay()); } @Execute @@ -51,7 +53,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() @@ -64,6 +66,6 @@ void execute(@Sender Player player) { } this.afkService.switchAfk(uuid, AfkReason.COMMAND); - this.delay.markDelay(uuid, this.afkSettings.afkCommandDelay()); + this.delay.markDelay(uuid); } } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/HelpOpCommand.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/HelpOpCommand.java index ad77e0d31..0a277d5cc 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/HelpOpCommand.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/helpop/HelpOpCommand.java @@ -2,6 +2,7 @@ import com.eternalcode.annotations.scan.command.DescriptionDocs; import com.eternalcode.annotations.scan.permission.PermissionDocs; +import com.eternalcode.core.delay.DefaultDelay; import com.eternalcode.core.delay.Delay; import com.eternalcode.core.event.EventCaller; import com.eternalcode.core.feature.helpop.event.HelpOpEvent; @@ -14,11 +15,12 @@ import dev.rollczi.litecommands.annotations.execute.Execute; import dev.rollczi.litecommands.annotations.join.Join; import dev.rollczi.litecommands.annotations.permission.Permission; -import java.time.Duration; -import java.util.UUID; import org.bukkit.Server; import org.bukkit.entity.Player; +import java.time.Duration; +import java.util.UUID; + @Command(name = "helpop", aliases = { "report" }) @Permission("eternalcore.helpop") @PermissionDocs( @@ -34,7 +36,7 @@ class HelpOpCommand { private final HelpOpSettings helpOpSettings; private final EventCaller eventCaller; private final Server server; - private final Delay delay; + private final DefaultDelay delay; @Inject HelpOpCommand(NoticeService noticeService, HelpOpSettings helpOpSettings, EventCaller eventCaller, Server server) { @@ -42,7 +44,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 @@ -58,7 +60,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()) @@ -91,7 +93,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); } } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportCommand.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportCommand.java index 201a96bc1..2aa863902 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportCommand.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/randomteleport/RandomTeleportCommand.java @@ -1,11 +1,7 @@ package com.eternalcode.core.feature.randomteleport; -import static com.eternalcode.core.feature.randomteleport.RandomTeleportPermissionConstant.RTP_BYPASS_PERMISSION; -import static com.eternalcode.core.feature.randomteleport.RandomTeleportPermissionConstant.RTP_COMMAND_OTHER; -import static com.eternalcode.core.feature.randomteleport.RandomTeleportPermissionConstant.RTP_COMMAND_SELF; -import static com.eternalcode.core.feature.randomteleport.RandomTeleportPlaceholders.PLACEHOLDERS; - import com.eternalcode.annotations.scan.command.DescriptionDocs; +import com.eternalcode.core.delay.DefaultDelay; import com.eternalcode.core.delay.Delay; import com.eternalcode.core.injector.annotations.Inject; import com.eternalcode.core.notice.NoticeService; @@ -16,9 +12,15 @@ import dev.rollczi.litecommands.annotations.context.Sender; import dev.rollczi.litecommands.annotations.execute.Execute; import dev.rollczi.litecommands.annotations.permission.Permission; +import org.bukkit.entity.Player; + import java.time.Duration; import java.util.UUID; -import org.bukkit.entity.Player; + +import static com.eternalcode.core.feature.randomteleport.RandomTeleportPermissionConstant.RTP_BYPASS_PERMISSION; +import static com.eternalcode.core.feature.randomteleport.RandomTeleportPermissionConstant.RTP_COMMAND_OTHER; +import static com.eternalcode.core.feature.randomteleport.RandomTeleportPermissionConstant.RTP_COMMAND_SELF; +import static com.eternalcode.core.feature.randomteleport.RandomTeleportPlaceholders.PLACEHOLDERS; @Command(name = "rtp", aliases = "randomteleport") class RandomTeleportCommand { @@ -27,7 +29,7 @@ class RandomTeleportCommand { private final RandomTeleportService randomTeleportService; private final RandomTeleportTaskService randomTeleportTaskService; private final RandomTeleportSettings randomTeleportSettings; - private final Delay cooldown; + private final DefaultDelay cooldown; @Inject RandomTeleportCommand( @@ -40,7 +42,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()); } @Execute @@ -68,7 +70,7 @@ void executeSelf(@Sender Player player) { this.handleTeleportSuccess(player); }); - this.cooldown.markDelay(uuid, this.randomTeleportSettings.cooldown()); + this.cooldown.markDelay(uuid); } @Execute @@ -96,7 +98,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) { @@ -129,7 +131,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()) diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/repair/RepairCommand.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/repair/RepairCommand.java index 3fa32d1c1..cc3024f7f 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/repair/RepairCommand.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/repair/RepairCommand.java @@ -1,6 +1,7 @@ package com.eternalcode.core.feature.repair; import com.eternalcode.annotations.scan.command.DescriptionDocs; +import com.eternalcode.core.delay.DefaultDelay; import com.eternalcode.core.delay.Delay; import com.eternalcode.core.injector.annotations.Inject; import com.eternalcode.core.notice.NoticeService; @@ -9,8 +10,6 @@ import dev.rollczi.litecommands.annotations.context.Sender; import dev.rollczi.litecommands.annotations.execute.Execute; import dev.rollczi.litecommands.annotations.permission.Permission; -import java.time.Duration; -import java.util.UUID; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; @@ -18,18 +17,21 @@ import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.Repairable; +import java.time.Duration; +import java.util.UUID; + @Command(name = "repair") class RepairCommand { private final NoticeService noticeService; - private final Delay delay; + private final DefaultDelay delay; private final RepairSettings repairSettings; @Inject 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 @@ -73,7 +75,7 @@ void repair(@Sender Player player) { .player(player.getUniqueId()) .send(); - this.delay.markDelay(uuid, this.repairSettings.repairDelay()); + this.delay.markDelay(uuid); } @Execute(name = "all") @@ -117,7 +119,7 @@ void repairAll(@Sender Player player) { .player(player.getUniqueId()) .send(); - this.delay.markDelay(uuid, this.repairSettings.repairDelay()); + this.delay.markDelay(uuid); } @Execute(name = "armor") @@ -161,12 +163,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()