-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Version 1.0.0 · English documentation
A configurable event clock for Paper/Spigot, with cooldowns, five-field UNIX CRON, optional PlaceholderAPI, and YAML persistence. No database, GUI, permission nodes, or global polling loop.
- Installation
- Quick start: a fortnightly KeyAll
- Files
- Event example
- Scheduling and lifecycle
- CRON
- World scope and actions
- Placeholders
- Commands
- Upgrading to 1.0.0
- Troubleshooting
- Build and validation
Stop the server, remove the previous plugin jar, install EventClockRK-1.0.0.jar, then start again. Keep the entire plugins/EventClockRK folder, including event definitions, schedules, and data. The alias is now /ecrk; /eventclock remains available and /ec is no longer registered.
Text timers now hide leading zero units by default: 0d 0h 5m 0s becomes 5m 0s, while 2h 0m 15s keeps its intermediate zero. time-units.hide-leading-zero defaults to true even in existing configurations where the key is absent. Set it to false to restore leading zeros. Keep time-units.hide-zero: false to preserve intermediate/trailing zeros; true still hides all zero units. Numeric seconds and fixed clock format are unchanged.
- Use a server runtime compatible with Java 17 or newer. Recent Minecraft versions can require Java 21; follow your server's Java requirement. This plugin's bytecode targets Java 17.
- Put
EventClockRK-1.0.0.jarin the server'spluginsfolder and start the server. - Edit the generated YAML files in
plugins/EventClockRK/. The example is disabled by default. - Run
/ecrk reloadfrom the server console or as an OP player. Use/ecrk listand/ecrk info <id>to inspect events. - Optional: install PlaceholderAPI on your server. EventClockRK registers its own placeholders automatically; no expansion download is needed for EventClockRK itself. Other plugins' placeholders need their own integrations/expansions.
The compatibility target is Paper/Spigot 1.16–1.21.x, using the Spigot 1.16.5 API without NMS or version-specific code. This release has automated tests against that API and a simulated Bukkit adapter; it has not been run on a live server for every version. Folia is outside this compatibility target. Older server builds must themselves support the chosen Java runtime.
This example runs every two weeks on Friday at 1:15 AM in Lima, anchored to September 11, 2026.
Create plugins/EventClockRK/events/keyall.yml:
keyall:
enabled: true
repeat: true
duration: 0
cooldown: 0
worlds: []
min-players-online: 1
alerts: [300, 60]
broadcast-message: '&aThe scheduled event has started!'
actions:
- 'say Hello from EventClockRK!'
end-actions: []
end-broadcast-message: ''
cancel-broadcast-message: ''The action above is a test message, not a key reward. Replace it with your reward plugin's actual command. EventClockRK does not include a crate or key system.
In plugins/EventClockRK/schedulers.yml:
timezone: America/Lima
keyall-fortnightly:
times:
- '15 1 * * 5'
interval-weeks: 2
reference-date: '2026-09-11'
random: false
events:
- keyallKeep one top-level timezone setting when adding more schedules. The event's cooldown: 0 is valid because this scheduler references it.
The sequence includes September 11, September 25, October 9, and so on. Loading this configuration later uses the next future occurrence; missed dates are not replayed.
/ecrk reload
/ecrk info keyall
/ecrk forcetrigger keyall
The force command immediately executes the configured actions. It does not change the fortnightly CRON calendar.
With PlaceholderAPI installed, test the countdown and next date in-game:
/papi parse me %keyall_time%
/papi parse me %keyall_next_datetime%
At least one eligible player must be online for the automatic occurrence in this example. If no eligible player is online, that occurrence is skipped.
plugins/EventClockRK/
config.yml # Global display settings; English defaults
messages.yml # Internal messages, statuses, and time-unit labels
schedulers.yml # One timezone and any number of CRON schedules
events/
example.yml # Generated only when the directory is empty
anything.yml # Each top-level key defines a separate event ID
data/
event-id.yml # Automatically generated state; do not edit
Event IDs use lowercase letters, numbers, _ and -, up to 64 characters, starting with a letter or number. File-reserved names are rejected. IDs are case-sensitive. Event filenames do not determine IDs. Duplicate IDs are logged with their file; the first declaration in sorted filename order wins.
Malformed events and broken scheduler references are reported individually, and valid entries load. Invalid global YAML or timezone aborts reload before replacing the running engine. A malformed data file disables its event instead of silently resetting its history. Removing an event definition removes its data on successful reload; invalid declarations preserve their data, and an unreadable event file suppresses orphan cleanup.
keyall:
enabled: true
repeat: true
duration: 0
cooldown: 259200
worlds: []
min-players-online: 1
alerts: [600, 300, 60]
broadcast-message: '<green>A key for everyone!'
actions:
- 'your-key-plugin give %player% vote 1'
end-actions: []
end-broadcast-message: ''
cancel-broadcast-message: ''Replace the example action with your key plugin's actual command. enabled, repeat, duration, cooldown, worlds, min-players-online, alerts, broadcast-message, and actions are required with the types shown. Empty action/message lists are allowed. End/cancellation fields are optional. Time fields contain integer seconds. Duration 0 means instantaneous; cooldown must be positive unless a valid CRON scheduler controls the event.
- Cooldowns run from the start of one event to the next due start, including offline time. An overdue cooldown fires once on return; it does not replay every missed interval.
- If a due cooldown lacks players, it waits without polling. Joining or changing worlds rechecks waiting events. If a CRON occurrence lacks players, that occurrence is skipped.
- Any valid scheduler reference overrides the event's cooldown. CRON missed while the plugin was stopped or the event was paused is skipped.
- An already active event ignores every additional start attempt, including forced attempts. A skipped cooldown attempt schedules the next full interval; a skipped CRON occurrence waits for its next occurrence.
-
pausefreezes remaining cooldown time and suppresses automatic CRON starts, while an active duration keeps running.resumerestores the frozen cooldown or waits for the next future CRON occurrence. -
repeat: falsepersists completion after its first start, including across reloads/restarts.resetcooldowndoes not clear completion. A force command can explicitly run it again. -
forcetriggerbypasses disabled, paused, completed, and minimum-player restrictions. It retains pause/disabled settings and resets a cooldown from that forced start. It still respects world scope and active conflicts. - Active durations resume after a restart. If one expired offline, its end actions run once when loading; they are not replayed on subsequent restarts.
- Cancel runs
end-actionswith%cancelled% = true, pluscancel-broadcast-message. An empty cancellation message uses the internalevent-cancelledmessage. It never broadcasts the natural completion text. Natural endings use%cancelled% = falseandend-broadcast-message, falling back toevent-endedwhen empty. - State is saved on transitions using a temporary YAML file and atomic replacement where supported. Like external console commands generally, actions cannot provide a transactional exactly-once guarantee across a sudden process crash. State is committed before dispatch to avoid blindly duplicating rewards after a crash.
Every cooldown or CRON schedule uses a one-shot scheduled wakeup; durations and advance alerts have their own one-shot tasks. There is no repeating tick/second scan. Bukkit timers have tick resolution and depend on server TPS, so lag can delay execution. An early wake caused by a backward clock change is rescheduled. No real-time precision beyond the server scheduler is promised.
timezone: America/Lima
keyall-thursday:
times: ['0 14 * * 4']
random: false
events: [keyall]
fortnightly-raid:
times: ['0 20 * * 0']
interval-weeks: 2
reference-date: '2026-09-13'
random: false
events: [raidboss]Create raidboss in an event file before enabling that schedule. Fields are minute, hour, day of month, month, weekday (0 Sunday–6 Saturday). Numeric values, *, lists, ranges, and steps are supported, e.g. */15 8-20 * * 1-5. When both day-of-month and weekday are restricted, UNIX OR matching is used. Named weekdays/months, 7 for Sunday, Quartz ?, L, W, and # are not accepted.
interval-weeks and quoted reference-date must be supplied together. Seven-day blocks are counted from that exact date, with no occurrences before it; matching blocks have an index divisible by the interval. Intervals range from 1 to 5200 weeks. Search is bounded to 400 years to reject impossible calendars. In DST zones, nonexistent local times are skipped and each instant of a repeated local time is a separate occurrence.
random: true chooses one entry from the full configured valid event list. If that entry is disabled, paused, completed, active, or lacks players, that occurrence is skipped without rerolling. random: false attempts all entries. Identical expressions within a scheduler cause one occurrence, while separate schedulers are independent. Time/progress/date placeholders and pre-start alerts are suppressed for any event referenced by a random scheduler; its status remains available.
worlds: [] means all loaded worlds. Otherwise, only online players in named worlds count toward the minimum, appear in online variables, receive messages, and participate in player actions.
| Variable | Meaning |
|---|---|
%event% |
Internal event ID |
%player% |
Execute the command once per eligible online player |
%world% |
With %player%: the player's world. Without it: execute once per eligible loaded world |
%online_players% |
Eligible player names separated by spaces |
%online_count% |
Number of eligible players |
%cancelled% |
true on cancellation, false on natural ending/start |
A command with neither %player% nor %world% runs once total, including a command with %online_players%. Messages are resolved separately for each eligible recipient, so %player%/%world% identify that recipient. Player-dependent external placeholders in a once-total or per-world command receive a null player context; use %player% when a per-player external value is required.
Own variables resolve first, then PlaceholderAPI. External placeholders remain untouched if it is absent. All actions use the server console on the main server thread through Bukkit's synchronous scheduler. A command that reloads/disables EventClockRK ends the remainder of its old action batch.
Internal messages automatically receive config.yml's prefix. Event broadcast fields do not. Messages support mixed legacy &a, hex &#FF5500, and MiniMessage colors/gradients. Editing messages.yml changes language; language: en identifies the bundled English defaults and does not download translations.
For ID keyall, use %keyall_time%, etc. Supported suffixes:
time time_seconds time_short
time_dh time_dhm time_dhms
time_hms time_ms next_datetime
progress_percent progress_bar
status
While active, time/progress describe the active duration; otherwise they describe the cooldown or next scheduled start. Paused cooldowns display frozen time. Disabled/completed inactive events and random-scheduled time/progress/date values display placeholders.not-active. Paused or completed events have no displayed next date. Status precedence is active, completed, paused, inactive; enabled waiting events use inactive.
timer-format selects generic time: 1 = total HH:MM:SS, 2 = largest unit, 3 = full text. time-units controls which units appear in text formats and whether zero values are hidden; omitted larger units roll into the next shown unit. Numeric seconds and fixed clock format remain numeric. Unit labels live in messages.yml under time. The progress bar's length/characters and the SimpleDateFormat-style date format live in config.yml. Cooldown dates use the system timezone; scheduled dates use schedulers.yml's timezone.
PlaceholderAPI splits the identifier at the first underscore. EventClockRK groups IDs such as raid and raid_boss under one raid expansion while preserving %raid_boss_time%. If another expansion owns that prefix (e.g. vault), EventClockRK logs the conflict and skips its own placeholders for that prefix without taking over the existing expansion. Choose unique event prefixes.
Only OP players and local/remote server console may use these. There are no permission nodes in plugin.yml. Command blocks are not server console and are excluded.
/ecrk list
/ecrk info <id>
/ecrk forcetrigger <id>
/ecrk resetcooldown <id>
/ecrk pause <id>
/ecrk resume <id>
/ecrk cancel <id>
/ecrk reload
/eventclock is the full command. Subcommands and IDs have tab completion. Create/edit events only through YAML files.
Requires JDK 17+ and Maven 3.9+. Run mvn clean verify; the installable shaded artifact is target/EventClockRK-1.0.0.jar. Adventure color libraries are bundled and relocated to avoid server/plugin conflicts. Bukkit and PlaceholderAPI are provided by the server, not bundled.
Automated tests cover CRON/Lima/fortnight/DST cases, persistence, pause/resume/force/cancel, one-shot completion, offline recovery, random scheduling, world scoping, command authorization, invalid YAML, placeholder formatting, mixed colors, and PlaceholderAPI registration conflicts. The Bukkit integration tests use a deterministic simulated adapter, not an actual Minecraft process. Before production, test the jar on your exact server build with your real reward/end commands and installed PlaceholderAPI expansions.
Implementation references: Spigot API Maven repository, PlaceholderAPI expansion documentation.
These global settings belong in config.yml:
timer-format: 3
time-units:
days: true
hours: true
minutes: true
seconds: true
hide-zero: false
hide-leading-zero: true
next-datetime-format: 'yyyy-MM-dd HH:mm:ss z'
progress-bar:
length: 10
filled: '▓'
empty: '░'With these defaults:
| Remaining time | Display |
|---|---|
| Two days, three hours, fifteen minutes, twenty seconds | 2d 3h 15m 20s |
| Two hours, zero minutes, fifteen seconds | 2h 0m 15s |
| Five minutes | 5m 0s |
| Twenty seconds | 20s |
| No remaining time | 0s |
Keep hide-zero: false to retain intermediate and trailing zeros. hide-leading-zero: true only removes leading zero units. This default also applies when an existing configuration does not contain the new key.
Replace keyall with your event ID.
| Placeholder | Output |
|---|---|
%keyall_time% |
Remaining time using the configured generic format |
%keyall_time_seconds% |
Numeric remaining seconds |
%keyall_time_short% |
Largest unit, such as 2d
|
%keyall_time_dh% |
Days and hours |
%keyall_time_dhm% |
Days, hours and minutes |
%keyall_time_dhms% |
Days, hours, minutes and seconds |
%keyall_time_hms% |
Total hours, minutes and seconds |
%keyall_time_ms% |
Total minutes and seconds |
%keyall_next_datetime% |
Next start date and time |
%keyall_progress_percent% |
Number from 0 to 100, without a percent sign |
%keyall_progress_bar% |
Configurable visual progress bar |
%keyall_status% |
Configurable status text |
An enabled event waiting for its next occurrence may show Inactive: it means no active duration is running, not necessarily that the event is disabled.
Create an event that stays active for five minutes, with one hour between starts:
timed-event:
enabled: true
repeat: true
duration: 300
cooldown: 3600
worlds: []
min-players-online: 1
alerts: [60]
broadcast-message: '&aThe five-minute event has started!'
actions: []
end-actions: []
end-broadcast-message: '&aThe event finished!'
cancel-broadcast-message: '&cThe event was cancelled.'Leave this ID out of schedulers.yml to use its cooldown.
/ecrk forcetrigger timed-event
/ecrk cancel timed-event
Cancellation executes end actions and the cancellation announcement. It never sends the natural completion announcement.
| Problem | What to check |
|---|---|
/ec opens EnderChest |
Use /ecrk or /eventclock. Version 1.0.0 does not register /ec. |
Event missing from list
|
Check console errors, required fields, YAML indentation and duplicate IDs. |
cooldown: 0 is rejected |
A valid scheduler must reference that exact ID. Otherwise use a positive cooldown. |
| A scheduled occurrence did not run | Check timezone, online players, enabled/paused/completed/active state and whether the server was online. |
| Placeholder remains unchanged | Check PlaceholderAPI, the ID, expansion conflicts and support in the displaying plugin. |
Placeholder shows ---
|
Check for a disabled/completed event or a random scheduler reference. |
| Leading zeros are still visible | Use hide-leading-zero: true, retain hide-zero: false, and reload. Confirm the server is running the new jar. |
| Reward command fails | Test its exact syntax from the console using your reward plugin. |
| Command access denied | Use an OP player or server console. No permission nodes are available. |
World filtering does not override an external command's scope. A global broadcast command from another plugin may still reach every player even when EventClockRK's own announcements are world-filtered.
To suspend an event while preserving its history, leave it defined and disable or pause it. Removing its definition and reloading also removes its saved state.
- Removed the beta version suffix.
- Replaced
/ecwith/ecrk;/eventclockremains available. - Added default suppression of leading zero time units while retaining intermediate zeros.
- Preserved compatibility with existing event configuration and saved data.
- Passed 53 automated tests, including simulated Bukkit integration tests.
The administrator reported successful KeyAll announcements, command execution and restart persistence on the preceding beta. A complete live Paper/Spigot version matrix has not been validated.