Skip to content

fix: duration countdown resets to full duration on reload#52

Merged
ez-plugins merged 14 commits into
mainfrom
fix/double-command-execution
May 22, 2026
Merged

fix: duration countdown resets to full duration on reload#52
ez-plugins merged 14 commits into
mainfrom
fix/double-command-execution

Conversation

@ez-plugins
Copy link
Copy Markdown
Owner

@ez-plugins ez-plugins commented May 22, 2026

Added

  • Folia support - the plugin now runs on Folia (marked folia-supported: true in plugin.yml). Folia's GlobalRegionScheduler is used automatically when detected at runtime; Paper/Spigot continue to use the Bukkit scheduler.
  • compat/ package hierarchy - clean OOP abstraction layer:
    • compat.platform.PlatformDetector - detects Folia vs. Paper/Spigot at startup via RegionizedServer class presence.
    • compat.scheduler.SchedulerAdapter / TaskHandle - platform-agnostic scheduler interface (runTask, runTaskTimer, runTaskLater, runTaskAsync).
    • compat.scheduler.BukkitSchedulerAdapter - SchedulerAdapter backed by BukkitScheduler (Paper/Spigot).
    • compat.scheduler.FoliaSchedulerAdapter - SchedulerAdapter backed by GlobalRegionScheduler (Folia); only class-loaded when Folia is detected.
    • compat.scheduler.SchedulerAdapterFactory - chooses the correct adapter with graceful fallback if Folia classes are missing.
    • compat.version.ServerVersionUtil - canonical location for runtime Minecraft version detection (replaces util.ServerVersionUtil).
    • compat.material.MaterialCompat - canonical location for cross-version material resolution (replaces util.MaterialCompat).
  • Startup platform log - PluginBootstrap now logs the active scheduler adapter class on enable (e.g. Scheduler: BukkitSchedulerAdapter).

Changed

  • util.ServerVersionUtil and util.MaterialCompat are now @Deprecated delegation stubs that forward to their canonical compat.* counterparts; they will be removed in a future release.

  • All internal scheduler usages (CountdownManager, FireworkShowManager, PatternScheduler, ChatInputListener, all listener.actions.* GUI action classes, SpigotIntegration, SpigotUpdateChecker) now go through SchedulerAdapter instead of Bukkit.getScheduler() / BukkitRunnable directly.

  • Scoreboard API modernised - ScoreboardDisplay now delegates to compat.scoreboard.ScoreboardCompat. On Paper 1.20.3+ (Criteria.DUMMY available) the modern registerNewObjective(String, Criteria, Component) overload is used; older builds fall back to the legacy string overload. The deprecated Scoreboard.resetScores(String) calls are replaced by objective unregister/re-register via ScoreboardCompat.resetObjective.

  • Title API modernised - TitleDisplay now delegates to compat.title.TitleCompat. On Paper 1.18+ the Adventure Player.showTitle(Title) / Player.clearTitle() APIs are preferred over the deprecated sendTitle(String...) / resetTitle(). TitleValidator now also accepts showTitle as a valid title capability. A Spigot/legacy string fallback is retained.

  • Per-player notification targeting - NotificationBuilder.players(Collection<Player>) restricts a notification to specific players. EzCountdownApi.sendNotification(Notification, Collection<Player>) provides an inline alternative without building a Notification first.

  • Countdown.isVisibleTo(Player) - centralises per-player visibility logic (permission gate + target-player set) in one method; all display handlers now use it instead of duplicated inline checks.

  • CountdownBuilder.targetPlayers(Collection<Player>) - restrict a persistent countdown's display to specific players.

  • Developer-friendly exception hierarchy - new com.skyblockexp.ezcountdown.api.exception package:

    • EzCountdownException - base unchecked exception; catch this for all EzCountdown API errors.
    • CountdownNotFoundException - thrown when referencing a countdown name that does not exist; carries getCountdownName().
    • DuplicateCountdownException - thrown when creating a countdown whose name already exists; carries getCountdownName().
    • InvalidConfigurationException - thrown by NotificationBuilder.build() and future builder validators when configuration is invalid; replaces the generic IllegalStateException.
  • api-version bumped to 1.18 in plugin.yml; minimum supported Minecraft version is Paper/Spigot 1.18. Dialog display continues to require Paper 1.21.7+.

  • Broadened Java compatibility - release JARs now target Java 17 bytecode (previously Java 21), so the plugin runs on Paper 1.18 - 1.20.4 (Java 17) as well as Paper 1.20.5+ (Java 21). The jdk21 Maven profile also targets Java 17 bytecode.

  • ServerVersionUtil - new utility class (com.skyblockexp.ezcountdown.util.ServerVersionUtil) for runtime Minecraft version detection; enables future conditional feature gating without hard API dependencies.

  • Startup guard - onEnable now logs the detected MC and Java version and disables the plugin with a clear error message if the server is too old (MC < 1.18 or Java < 17).

Changed

  • All display handlers (ActionBarDisplay, TitleDisplay, ChatDisplay, BossBarDisplay, ScoreboardDisplay, DialogDisplay) use countdown.isVisibleTo(player) instead of duplicated inline permission checks.
  • NotificationBuilder.build() now throws InvalidConfigurationException (a subclass of EzCountdownException) instead of a plain IllegalStateException.
  • ScoreboardDisplay catch blocks now include NoSuchMethodError so the scoreboard falls back to chat if the String-criteria registerNewObjective overload is ever removed in a future Paper build.
  • Project version bumped from 1.4.3 to 2.0.0.

Fixed

  • DURATION countdown resets to full duration on /countdown reload - resumeRunningCountdowns() previously called handler.onStart(), which always sets targetInstant to now + fullDuration, discarding the target_epoch saved in storage. It now calls handler.ensureTarget() instead, which is a no-op when a target is already present. The same guard was added to the legacy fallback path for handler-less countdown types. End-commands that fired once before a reload will no longer fire again unexpectedly due to the countdown silently restarting.

ez-plugins added 14 commits May 22, 2026 14:15
resumeRunningCountdowns() now calls handler.ensureTarget() instead of
handler.onStart(), which is a no-op when a target is already present.
The same guard was added to the legacy fallback path for handler-less
countdown types. End-commands that fired once before a reload will no
longer fire again unexpectedly.

Bumped version to 1.4.3.
…th Java 21 and 25

- smoke-papermc: matrix over paper+folia × MC 1.21/26.1 × Java 21/25
  Downloads server JARs from api.papermc.io; skips gracefully when a
  platform/version combo has no builds yet.
- smoke-spigot: matrix over MC 1.21/26.1 × Java 21/25
  Compiles Spigot via BuildTools (cached by MC×Java); skips gracefully
  when BuildTools does not yet support the requested version.
Both jobs need unit-tests and feature-tests to pass first.
Add optional player targeting to ephemeral notifications so a
notification can be shown to a specific subset of players rather
than broadcasting to all online players.

Changes:
- Countdown: add targetPlayers Set<UUID> field + setTargetPlayers() +
  getTargetPlayers() + isVisibleTo(Player) helper that combines both
  visibility-permission and player-set checks
- CountdownBuilder: add targetPlayers(Collection<Player>) builder method
- Notification: add targetPlayers Set<UUID> field + getTargetPlayers()
- NotificationBuilder: add players(Collection<Player>) builder method
- EzCountdownApi: add sendNotification(Notification, Collection<Player>)
  default method (UnsupportedOperationException for 3rd-party impls)
- EzCountdownApiImpl: implement per-player overload; original single-arg
  overload delegates to it; also respects players embedded in the
  Notification itself (via builder)
- All display handlers (ActionBar, Title, Chat, BossBar, Scoreboard,
  Dialog, DisplayManager): replace inline permission check with
  countdown.isVisibleTo(player)
- Tests: add per-player tests to EzCountdownApiImplTest and
  NotificationBuilderTest
Scheduler
- Add compat.scheduler.SchedulerAdapter / TaskHandle interface
- BukkitSchedulerAdapter (Paper/Spigot) and FoliaSchedulerAdapter (Folia)
- SchedulerAdapterFactory: selects adapter at startup, falls back gracefully
- Wire SchedulerAdapter through CountdownManager, FireworkShowManager,
  PatternScheduler, ChatInputListener, all listener/actions classes,
  SpigotIntegration, SpigotUpdateChecker

Platform detection
- compat.platform.PlatformDetector: detects Folia via RegionizedServer class
- plugin.yml: folia-supported: true, api-version: 1.18

Version / material compat
- compat.version.ServerVersionUtil: canonical runtime MC-version helper
- compat.material.MaterialCompat: canonical cross-version material resolver
- util.ServerVersionUtil / util.MaterialCompat: deprecated delegation stubs

Scoreboard API modernisation (Paper 1.20.3+)
- compat.scoreboard.ScoreboardCompat: uses registerNewObjective(Criteria,
  Component) when Criteria.DUMMY is available; falls back to legacy String
  overload; guards against null Criteria.DUMMY in test environments
- ScoreboardCompat.resetObjective: replaces deprecated
  getEntries/resetScores with objective unregister+reregister
- ScoreboardDisplay: delegate to ScoreboardCompat in display() and
  displayMultiple()

Title API modernisation (Paper 1.18+)
- compat.title.TitleCompat: prefers Adventure showTitle(Title) /
  clearTitle(); falls back to deprecated sendTitle / resetTitle on Spigot
- TitleDisplay: delegate to TitleCompat; remove hand-rolled try/catch
- TitleValidator: accept showTitle as valid title capability

Tests
- MissedRunPolicyTest / MissedRunPolicyRunAllEdgeCasesTest: stub
  registry.scheduler() with BukkitSchedulerAdapter
- ScoreboardDisplayStackableTest: stub modern registerNewObjective overload

All 145 tests pass.
…n MockBukkit

PlayerMock.showTitle(net.kyori.adventure.title.Title) goes through the
Adventure Audience default sendTitlePart path which MockBukkit does not
bridge to the nextTitle() queue, so TitleDisplayFeatureTest always saw null.

TitleCompat.sendTitle now uses the deprecated-but-universal
sendTitle(String, String, int, int, int) with @SuppressWarnings.
TitleCompat.clearTitle still prefers clearTitle() over resetTitle().
TitleValidator checks sendTitle first, showTitle as secondary fallback.
getBukkitVersion() for MC 26.x returns "26.1-R0.1-SNAPSHOT" rather
than "1.26.1-R0.1-SNAPSHOT". The old parseMinor() split by "." and
took parts[1] ("1-R0" -> 1), triggering the >= 1.18 guard and
disabling the plugin.

ServerVersionUtil.parseFrom(String) now detects legacy "1.X.Y" vs
new "X.Y" format. For the new format it returns parts[0] as the
effective minor (26 >= 18, so the startup check passes). A
versionDisplay() method exposes the full "1.21" / "26.1" string
used in the startup log line.

Add ServerVersionUtilParseTest covering both formats, bare major,
and garbage input. CI smoke test matrix already covers mc-prefix
"26.1".
Paper/Folia/Spigot builds for MC 26.x are not yet published on the
APIs, so every 26.1 matrix cell silently skips with exit 0 — giving
false confidence without exercising anything. Remove it for now.

Version parsing for the new X.Y format is already covered by
ServerVersionUtilParseTest. Re-add 26.1 once real server builds are
available on api.papermc.io.
…uilds

Mark mc-prefix 26.1 matrix entries with experimental: true so the jobs
use continue-on-error: true — unavailable versions show as a visible
(orange) warning in CI without blocking the PR.

Change the 'no builds found' exit from 0 to 1 so missing MC versions
fail the job instead of silently passing. BuildTools failure and missing
Spigot JAR likewise now exit 1.

When Paper/Folia/Spigot 26.1 builds become available the smoke tests
will automatically exercise the plugin; at that point flip experimental
to false to make them strict.
BuildTools requires network access, Git, and exact version support — it
fails for reasons unrelated to the plugin. Reverting exit 1 to exit 0
with ::warning:: so a BuildTools failure or missing JAR is a visible
skip, not a PR blocker.

Only actual plugin errors in the server log (SEVERE.*EzCountdown etc.)
remain as hard exit 1 failures.
Confirmed: Paper and Folia top out at 1.21.11. The 26.x version format
is covered by ServerVersionUtilParseTest unit tests. Re-add 26.1 to the
matrix once official builds are published.
- Add '26.1' to mc-prefix matrix in smoke-papermc job
- Exclude java: 21 + mc-prefix: 26.1 (requires Java 25+)
- Migrate resolve step from api.papermc.io/v2 to fill.papermc.io/v3
  - v3 returns versions as {family: [version, ...]} dict
  - Build objects include direct download URL (fill-data.papermc.io)
  - Prefer STABLE channel, fall back to latest build
- Download step now uses direct URL from resolve step output
…tion

Multi-line Python inside python3 -c "..." was starting at column 0,
which caused the YAML block scalar to terminate prematurely, breaking
the entire workflow file and preventing any jobs from running.
actions/checkout@v6 and actions/setup-java@v5 do not exist;
downgrade to the latest actual versions (v4).
@ez-plugins ez-plugins merged commit db43f10 into main May 22, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant