From 56eccb056efde8ddfbcde1408bb23891766fc9f6 Mon Sep 17 00:00:00 2001 From: Pablo Herrera Date: Fri, 3 Jan 2025 01:34:29 +0100 Subject: [PATCH] Fix persistent cooldown issues Signed-off-by: Pablo Herrera --- .../tc/oc/pgm/command/MapPoolCommand.java | 130 ++++++++++-------- .../main/java/tc/oc/pgm/db/SQLDatastore.java | 6 +- .../tc/oc/pgm/rotation/vote/VoteData.java | 15 +- 3 files changed, 82 insertions(+), 69 deletions(-) diff --git a/core/src/main/java/tc/oc/pgm/command/MapPoolCommand.java b/core/src/main/java/tc/oc/pgm/command/MapPoolCommand.java index cbf5f74066..b1f436b34a 100644 --- a/core/src/main/java/tc/oc/pgm/command/MapPoolCommand.java +++ b/core/src/main/java/tc/oc/pgm/command/MapPoolCommand.java @@ -4,6 +4,8 @@ import static net.kyori.adventure.text.Component.text; import static net.kyori.adventure.text.Component.translatable; import static net.kyori.adventure.text.event.HoverEvent.showText; +import static net.kyori.adventure.text.format.NamedTextColor.*; +import static tc.oc.pgm.util.text.TemporalComponent.duration; import static tc.oc.pgm.util.text.TextException.exception; import java.text.DecimalFormat; @@ -17,7 +19,6 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.event.ClickEvent; -import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextDecoration; import org.bukkit.command.CommandSender; import org.incendo.cloud.annotation.specifier.FlagYielding; @@ -41,7 +42,9 @@ import tc.oc.pgm.rotation.pools.VotingPool; import tc.oc.pgm.rotation.vote.MapPoll; import tc.oc.pgm.util.Audience; +import tc.oc.pgm.util.LiquidMetal; import tc.oc.pgm.util.PrettyPaginatedComponentResults; +import tc.oc.pgm.util.StringUtils; import tc.oc.pgm.util.named.MapNameStyle; import tc.oc.pgm.util.text.TextException; import tc.oc.pgm.util.text.TextFormatter; @@ -62,13 +65,20 @@ public void pool( @Flag(value = "score", aliases = "s") boolean scores, @Flag(value = "chance", aliases = "c") boolean chance, @Flag(value = "order", aliases = "o") boolean order, - @Flag(value = "all", aliases = "a") boolean all) { + @Flag(value = "all", aliases = "a") boolean all, + @Flag(value = "name", aliases = "n") String name) { // Default to current pool if (mapPool == null) mapPool = poolManager.getActiveMapPool(); if (mapPool == null || (type != null && mapPool.getType() != type)) throw exception("pool.noPoolMatch"); List maps = mapPool.getMaps(); + if (name != null) { + String normalized = StringUtils.normalize(name); + maps = maps.stream() + .filter(mi -> LiquidMetal.match(mi.getNormalizedName(), normalized)) + .toList(); + } int resultsPerPage = all ? maps.size() : 8; int pages = all ? 1 : (maps.size() + resultsPerPage - 1) / resultsPerPage; @@ -76,18 +86,17 @@ public void pool( Component mapPoolComponent = TextFormatter.paginate( text() .append(translatable("pool.name")) - .append(text(" (", NamedTextColor.DARK_AQUA)) - .append(text(mapPool.getName(), NamedTextColor.AQUA)) - .append(text(")", NamedTextColor.DARK_AQUA)) + .append(text(" (", DARK_AQUA)) + .append(text(mapPool.getName(), AQUA)) + .append(text(")", DARK_AQUA)) .build(), page, pages, - NamedTextColor.DARK_AQUA, - NamedTextColor.AQUA, + DARK_AQUA, + AQUA, false); - Component title = - TextFormatter.horizontalLineHeading(source, mapPoolComponent, NamedTextColor.BLUE, 250); + Component title = TextFormatter.horizontalLineHeading(source, mapPoolComponent, BLUE, 250); VotingPool votes = (scores || chance) && mapPool instanceof VotingPool ? (VotingPool) mapPool : null; @@ -115,16 +124,18 @@ public void pool( @Override public Component format(MapInfo map, int index) { index++; - TextComponent.Builder entry = text() - .append(text( - index + ". ", nextPos == index ? NamedTextColor.DARK_AQUA : NamedTextColor.WHITE)); - if (votes != null && scores) - entry.append( - text(SCORE_FORMAT.format(votes.getMapScore(map)) + " ", NamedTextColor.YELLOW)); - if (votes != null && chance) - entry.append(text(SCORE_FORMAT.format(chances.get(map)) + " ", NamedTextColor.YELLOW)); - entry.append(map.getStyledName(MapNameStyle.COLOR_WITH_AUTHORS)); - return entry.build(); + TextComponent.Builder r = + text().append(text(index + ". ", nextPos == index ? DARK_AQUA : WHITE)); + if (votes != null) { + var cd = votes.getVoteData(map).remainingCooldown(votes.constants); + if (cd.isPositive()) r.append(duration(cd, RED).color(YELLOW)).appendSpace(); + else { + if (scores) r.append(text(SCORE_FORMAT.format(votes.getMapScore(map)) + " ", YELLOW)); + if (chance) r.append(text(SCORE_FORMAT.format(chances.get(map)) + " ", YELLOW)); + } + } + r.append(map.getStyledName(MapNameStyle.COLOR_WITH_AUTHORS)); + return r.build(); } }.display(sender, maps, page); } @@ -151,45 +162,36 @@ public void pools( int resultsPerPage = 8; int pages = (mapPools.size() + resultsPerPage - 1) / resultsPerPage; - Component paginated = TextFormatter.paginate( - translatable("pool.title"), - page, - pages, - NamedTextColor.DARK_AQUA, - NamedTextColor.AQUA, - true); + Component paginated = + TextFormatter.paginate(translatable("pool.title"), page, pages, DARK_AQUA, AQUA, true); - Component formattedTitle = - TextFormatter.horizontalLineHeading(source, paginated, NamedTextColor.BLUE); + Component formattedTitle = TextFormatter.horizontalLineHeading(source, paginated, BLUE); new PrettyPaginatedComponentResults(formattedTitle, resultsPerPage) { @Override public Component format(MapPool mapPool, int index) { - Component arrow = text( - "» ", - poolManager.getActiveMapPool().equals(mapPool) - ? NamedTextColor.GREEN - : NamedTextColor.WHITE); + Component arrow = + text("» ", poolManager.getActiveMapPool().equals(mapPool) ? GREEN : WHITE); Component maps = text() - .append(text(" (", NamedTextColor.DARK_AQUA)) - .append(translatable("map.title", NamedTextColor.DARK_GREEN)) - .append(text(": ", NamedTextColor.DARK_GREEN)) - .append(text(mapPool.getMaps().size(), NamedTextColor.WHITE)) - .append(text(")", NamedTextColor.DARK_AQUA)) + .append(text(" (", DARK_AQUA)) + .append(translatable("map.title", DARK_GREEN)) + .append(text(": ", DARK_GREEN)) + .append(text(mapPool.getMaps().size(), WHITE)) + .append(text(")", DARK_AQUA)) .build(); Component players = text() - .append(text(" (", NamedTextColor.DARK_AQUA)) - .append(translatable("match.info.players", NamedTextColor.AQUA)) - .append(text(": ", NamedTextColor.AQUA)) - .append(text(mapPool.getPlayers(), NamedTextColor.WHITE)) - .append(text(")", NamedTextColor.DARK_AQUA)) + .append(text(" (", DARK_AQUA)) + .append(translatable("match.info.players", AQUA)) + .append(text(": ", AQUA)) + .append(text(mapPool.getPlayers(), WHITE)) + .append(text(")", DARK_AQUA)) .build(); return text() .append(arrow) - .append(text(mapPool.getName(), NamedTextColor.GOLD)) + .append(text(mapPool.getName(), GOLD)) .append(maps) .append(mapPool.isDynamic() ? players : empty()) .build(); @@ -214,10 +216,8 @@ public void setPool( if (newPool == null) throw exception("pool.noPoolMatch"); if (newPool.equals(poolManager.getActiveMapPool())) { - sender.sendMessage(translatable( - "pool.matching", - NamedTextColor.GRAY, - text(newPool.getName(), NamedTextColor.LIGHT_PURPLE))); + sender.sendMessage( + translatable("pool.matching", GRAY, text(newPool.getName(), LIGHT_PURPLE))); return; } @@ -259,13 +259,12 @@ public void skip( ((Rotation) pool).advance(positions); Component message = text() - .append(text("[", NamedTextColor.WHITE)) - .append(translatable("pool.name", NamedTextColor.GOLD)) - .append(text("] [", NamedTextColor.WHITE)) - .append(text(pool.getName(), NamedTextColor.AQUA)) - .append(text("]", NamedTextColor.WHITE)) - .append( - translatable("pool.skip", NamedTextColor.GREEN, text(positions, NamedTextColor.AQUA))) + .append(text("[", WHITE)) + .append(translatable("pool.name", GOLD)) + .append(text("] [", WHITE)) + .append(text(pool.getName(), AQUA)) + .append(text("]", WHITE)) + .append(translatable("pool.skip", GREEN, text(positions, AQUA))) .build(); sender.sendMessage(message); @@ -281,7 +280,7 @@ public void voteNext( boolean voteResult = poll.toggleVote(map, player); Component voteAction = translatable( voteResult ? "vote.for" : "vote.abstain", - voteResult ? NamedTextColor.GREEN : NamedTextColor.RED, + voteResult ? GREEN : RED, map.getStyledName(MapNameStyle.COLOR)); player.sendMessage(voteAction); poll.sendBook(player, forceOpen); @@ -309,7 +308,18 @@ public void rot( if (poolManager.getActiveMapPool().getType() != MapPoolType.ORDERED) throw exception("pool.noRotation"); - pool(sender, source, poolManager, page, MapPoolType.ORDERED, null, false, false, false, all); + pool( + sender, + source, + poolManager, + page, + MapPoolType.ORDERED, + null, + false, + false, + false, + all, + null); }); } @@ -382,13 +392,11 @@ private void wrapLegacy(String replace, Audience sender, String[] rawArgs, Runna private TextException alternativeUsage(String[] rawArgs, String replace) { rawArgs[0] = "/" + replace; String altCommand = String.join(" ", rawArgs); - Component cmd = - text(altCommand).color(NamedTextColor.YELLOW).decorate(TextDecoration.UNDERLINED); + Component cmd = text(altCommand).color(YELLOW).decorate(TextDecoration.UNDERLINED); return exception( "command.alternativeUsage", - cmd.hoverEvent( - showText(translatable("command.clickToRun", cmd).color(NamedTextColor.GREEN))) + cmd.hoverEvent(showText(translatable("command.clickToRun", cmd).color(GREEN))) .clickEvent(ClickEvent.runCommand(altCommand))); } } diff --git a/core/src/main/java/tc/oc/pgm/db/SQLDatastore.java b/core/src/main/java/tc/oc/pgm/db/SQLDatastore.java index 407c5332e7..59596ec994 100644 --- a/core/src/main/java/tc/oc/pgm/db/SQLDatastore.java +++ b/core/src/main/java/tc/oc/pgm/db/SQLDatastore.java @@ -310,9 +310,9 @@ public void query(PreparedStatement statement) throws SQLException { while (result.next()) { String id = result.getString(1); var map = maps.computeIfAbsent(id, k -> new SQLMapData(id, -1)); - map.lastPlayed = Instant.ofEpochMilli(result.getLong(1)); - map.lastDuration = Duration.ofMillis(result.getLong(2)); - map.score = result.getDouble(3); + map.lastPlayed = Instant.ofEpochMilli(result.getLong(2)); + map.lastDuration = Duration.ofMillis(result.getLong(3)); + map.score = result.getDouble(4); } } } diff --git a/core/src/main/java/tc/oc/pgm/rotation/vote/VoteData.java b/core/src/main/java/tc/oc/pgm/rotation/vote/VoteData.java index aec99087b4..d34ea49991 100644 --- a/core/src/main/java/tc/oc/pgm/rotation/vote/VoteData.java +++ b/core/src/main/java/tc/oc/pgm/rotation/vote/VoteData.java @@ -44,12 +44,17 @@ public double getScore() { return mapData.score(); } - public boolean isOnCooldown(VotingPool.VoteConstants constants) { - long duration = mapData.lastDuration().toMinutes(); - if (constants.minCooldown() == -1 || constants.minCooldown() > duration) return false; - long cooldownSeconds = duration * SECONDS_PER_DAY / constants.minutesPerDay(); + public Duration remainingCooldown(VotingPool.VoteConstants constants) { + long lastMins = mapData.lastDuration().toMinutes(); + if (constants.minCooldown() == -1 || constants.minCooldown() > lastMins) return Duration.ZERO; + long cooldownSeconds = lastMins * SECONDS_PER_DAY / constants.minutesPerDay(); + + var cd = Duration.between(Instant.now(), mapData.lastPlayed().plusSeconds(cooldownSeconds)); + return cd.isNegative() ? Duration.ZERO : cd; + } - return Instant.now().isAfter(mapData.lastPlayed().plusSeconds(cooldownSeconds)); + public boolean isOnCooldown(VotingPool.VoteConstants constants) { + return remainingCooldown(constants).isPositive(); } public void tickScore(VotingPool.VoteConstants constants) {