
EzCountdown
Run flash sales, timed events, and launches with stunning countdowns - ActionBar, BossBar, Titles & Placeholders!
Fixed
- JitPack build compatibility - corrected
jitpack.ymlJava selector toopenjdk21and pinned JitPack install to./mvnw -B -ntp -DskipTests installso builds consistently use Java 21 when compiling againstpaper-api 1.21.11. - GitHub Actions version drift - updated workflow actions to current major versions across CI/release publishing (
actions/checkout@v6,actions/setup-java@v5,actions/cache@v5,actions/upload-artifact@v7). - Paper/Folia smoke test resolution for 26.x - CI smoke jobs now resolve builds from
fill.papermc.io/v3and correctly include MC26.1with Java 25 where required.
Changed
- Modrinth release step - replaced the legacy
Kir-Antipov/mc-publishaction invocation in release workflow with a direct Modrinth v2 API upload path for clearer failure diagnostics and up-to-date version resolution.
- JitPack build compatibility - corrected
Added
- Folia support - the plugin now runs on Folia (marked
folia-supported: trueinplugin.yml). Folia'sGlobalRegionScheduleris 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 viaRegionizedServerclass presence.compat.scheduler.SchedulerAdapter/TaskHandle- platform-agnostic scheduler interface (runTask,runTaskTimer,runTaskLater,runTaskAsync).compat.scheduler.BukkitSchedulerAdapter-SchedulerAdapterbacked byBukkitScheduler(Paper/Spigot).compat.scheduler.FoliaSchedulerAdapter-SchedulerAdapterbacked byGlobalRegionScheduler(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 (replacesutil.ServerVersionUtil).compat.material.MaterialCompat- canonical location for cross-version material resolution (replacesutil.MaterialCompat).
- Startup platform log -
PluginBootstrapnow logs the active scheduler adapter class on enable (e.g.Scheduler: BukkitSchedulerAdapter).
Changed
-
util.ServerVersionUtilandutil.MaterialCompatare now@Deprecateddelegation stubs that forward to their canonicalcompat.*counterparts; they will be removed in a future release. -
All internal scheduler usages (
CountdownManager,FireworkShowManager,PatternScheduler,ChatInputListener, alllistener.actions.*GUI action classes,SpigotIntegration,SpigotUpdateChecker) now go throughSchedulerAdapterinstead ofBukkit.getScheduler()/BukkitRunnabledirectly. -
Scoreboard API modernised -
ScoreboardDisplaynow delegates tocompat.scoreboard.ScoreboardCompat. On Paper 1.20.3+ (Criteria.DUMMYavailable) the modernregisterNewObjective(String, Criteria, Component)overload is used; older builds fall back to the legacy string overload. The deprecatedScoreboard.resetScores(String)calls are replaced by objective unregister/re-register viaScoreboardCompat.resetObjective. -
Title API modernised -
TitleDisplaynow delegates tocompat.title.TitleCompat. On Paper 1.18+ the AdventurePlayer.showTitle(Title)/Player.clearTitle()APIs are preferred over the deprecatedsendTitle(String...)/resetTitle().TitleValidatornow also acceptsshowTitleas 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 aNotificationfirst. -
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.exceptionpackage:EzCountdownException- base unchecked exception; catch this for all EzCountdown API errors.CountdownNotFoundException- thrown when referencing a countdown name that does not exist; carriesgetCountdownName().DuplicateCountdownException- thrown when creating a countdown whose name already exists; carriesgetCountdownName().InvalidConfigurationException- thrown byNotificationBuilder.build()and future builder validators when configuration is invalid; replaces the genericIllegalStateException.
-
api-versionbumped to1.18inplugin.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
jdk21Maven 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 -
onEnablenow 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) usecountdown.isVisibleTo(player)instead of duplicated inline permission checks. NotificationBuilder.build()now throwsInvalidConfigurationException(a subclass ofEzCountdownException) instead of a plainIllegalStateException.ScoreboardDisplaycatch blocks now includeNoSuchMethodErrorso the scoreboard falls back to chat if the String-criteriaregisterNewObjectiveoverload is ever removed in a future Paper build.- Project version bumped from
1.4.3to2.0.0.
Fixed
- DURATION countdown resets to full duration on
/countdown reload-resumeRunningCountdowns()previously calledhandler.onStart(), which always setstargetInstanttonow + fullDuration, discarding thetarget_epochsaved in storage. It now callshandler.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.
- Folia support - the plugin now runs on Folia (marked
Added
- Configurable time format: new
display.time-formatsection inconfig.yml.pattern(default"{days}d {hours}h {minutes}m {seconds}s") - customize the token layout used for the{formatted}placeholder and%ezcountdown_<name>_formatted%PAPI expansion.hide-leading-zeros(defaulttrue) - when enabled, leading space-delimited segments whose unit value is zero are suppressed. For example,0d 0h 5m 3sis displayed as5m 3s.- Applies to countdown display messages, discord webhook
{time_left}, PlaceholderAPI, and the GUI preview action. - Hot-reloads with
/countdown reload.
- Configurable time format: new
Fixed
- Bossbar color/style ignored -
display.bossbar.coloranddisplay.bossbar.styleincountdowns.ymlhad no effect; the boss bar always rendered in the default blue/solid style. All four countdown-type handlers (DurationHandler,FixedDateHandler,RecurringHandler,ManualHandler) now read these fields from config and pass them to theCountdownconstructor.
- Bossbar color/style ignored -
Added
- Notification API —
EzCountdownApi.sendNotification(Notification)fires a one-shot ephemeral timed display from any plugin without creating a persistent countdown or touchingcountdowns.yml. Notificationmodel — lightweight immutable value object with factory methodsNotification.ofSeconds(long),Notification.of(Duration), and a fluentNotificationBuilder(display types, format message, start/end messages).NotificationBuilder— fluent builder forNotification; all fields have sensible defaults (ACTION_BARdisplay,{formatted}message);build()validates duration > 0.- Ephemeral countdown support —
Countdown.isEphemeral()/CountdownBuilder.ephemeral(boolean); ephemeral countdowns are removed from memory automatically when they end and are never written to storage.
- Notification API —
- Fixed countdown count in bStats
- Fixed small memory leak issue causing lag on bigger servers
- Bugfix: Duration countdowns were resetting to their full configured duration after a server restart
- Add DialogDisplay handler with no-flicker logic (only sends packet when formatted message text actually changes per player)
- Add DialogDisplayValidator for graceful runtime detection of the experimental Paper dialog API (requires Paper 1.21.7+)
- Register DIALOG in DisplayManager.configureHandlers() alongside existing display types
- Add --display flag to /countdown create command e.g. /countdown create test duration 10s --display DIALOG
- Add commands.error message key to messages.yml (fixes blank chat line when command handler throws an uncaught exception)
- Add DIALOG to config.yml display-types comment
- Added full documentation on https://ez-plugins.github.io/EzCountdown/
- Fixed issue in countdown showing on scoreboard/bossbar when this type was not configured for this countdown.
Feature changes
- Fix issue in tick scheduling of the countdowns
- Added configurable refresh speed for the displays
display: refresh: # How often the bossbar progress bar and title are refreshed, in ticks (20 ticks = 1 second). # Lower values produce smoother animation. Minimum is 1. bossbar-ticks: 1 # How often the scoreboard sidebar is refreshed, in ticks. Minimum is 1. scoreboard-ticks: 1Code structure changes
- Added
MessageBatchstructure to keep more clarity in 1 countdown with multiple displays being 1 "batch" - Added
DisplayMessagethat represents 1 message for 1 display
- Added configurable
colorforbossbarcountdown display - Added configurable
styleforbossbarcountdown display
- Added configurable
- Added a new, improved countdown manager GUI

- Fixed
/ezcdreloading issue, duplicating countdowns - Added
{translate:message.key}placeholders restart_countdown: <true|false>added for countdownscountdown_start: <countdown_name>added for countdowns
- Added a new, improved countdown manager GUI
- Refactored the full plugin
- Added
CountdownBuilder(fluent API for creating countdowns) - Added feature and unit testing (MockBukkit, JUnit)
- Added Maven CI build and publish workflow
- Open source release: https://github.com/ez-plugins/EzCountdown
- Fixed issue in FIXED countdown starting up after restart when
running: truewas enabled for the countdown
- Fixed issue in FIXED countdown starting up after restart when
New features:
Locations system:
- Added locations.yml for managing named teleport locations.
- New command: /ezcd location <add|delete>
to create or remove locations. - Per-countdown teleport: Teleport players to a configured location at countdown start and/or end via the teleport: section in countdowns.yml.
- Advanced firework shows:
- Per-countdown firework shows can be configured for start and/or end events.
- Firework options include: location, color, power, count, interval (ticks between rows), and rows (number of concentric circles).
- Fireworks launch in a visually impressive circle around the specified location.
Other improvements:
- Professional tab-completion for /ezcd location commands.
- Configuration file (countdowns.yml) is no longer overwritten by runtime state or reloads
- Locations system:
- Added locations.yml for managing named teleport locations.
- New command: /ezcd location <add|delete>
to create or remove locations. - Per-countdown teleport: Teleport players to a configured location at countdown start and/or end via the teleport: section in countdowns.yml.
- Advanced firework shows:
- Per-countdown firework shows can be configured for start and/or end events.
- Firework options include: location, color, power, count, interval (ticks between rows), and rows (number of concentric circles).
- Fireworks launch in a visually impressive circle around the specified location.
Other improvements:
- Professional tab-completion for /ezcd location commands.
- Configuration file (countdowns.yml) is no longer overwritten by runtime state or reloads

