TDX Leaderboards
Changelog
Version history and release notes for TDX Leaderboards.
All notable changes to TDX Leaderboards are documented here. Versions are listed newest-first.
v1.9.14 (Latest)
- Fixed the personal "You" line on each board showing 0 while the top-N rows showed the correct value. The async pre-fetch in
openMainGuiwas callingPlayer.getStatistic()from a background thread (unreliable on Paper) and storing the resulting 0 into a per-viewer cache; the top entries were rescued by theboard_cachemerge inupdateBoardbut the personal lookup had no merge fallback, so the two values disagreed placeBoardItemsandplacePointsItemnow read the viewer's value straight from the in-memory leaderboard (board.getAllEntries()/pointsManager.getPointsLeaderboard()) — same source the top rows render from, so the values can never disagree again- Removed the
viewerDatacache,ViewerDatarecord, async pre-fetch inopenMainGui, and theclearViewerDataclose-handler call. Restored the simple 1-tick delayed re-fill for ResetVault interference defense - Trade-off: viewer's row is now refreshed by the periodic
updateBoardcycle (default 30s) rather than per-open. Net win: GUI opens are sync-fast and never disagree with themselves
v1.9.13
- Fixed
/lbadmin reset confirmleaving DELTA boards empty until each online player relogged. The command was clearingmonthly_snapshots(along with the rest of the period's data) but never re-establishing baselines, sogetMonthlyValuereturned 0 for every online player andupdateBoardsilently skipped them. Boards stayed frozen empty until the natural per-join snapshot path re-populated rows /lbadmin reset confirmnow: clears month data → wipes in-memory leaderboard caches → callsforceSnapshotAllPlayersto take fresh DELTA baselines (idempotent IF-ABSENT, can't clobber existing) → kicks off an asyncupdateAllBoardsso the GUI catches up immediately- New
/lbadmin snapshot confirmrecovery command. Takes fresh snapshots for online players without clearing any data — use this if you already ran the broken/lbadmin reseton a pre-v1.9.13 install and lost DELTA-board tracking; ACCUMULATE-board progress is untouched
v1.9.12
- Fixed the new History GUI getting stuck on "Loading…" forever.
player.openInventory()was being called from inside theInventoryClickEventthat triggered the open — Bukkit's behaviour for opening a new inventory mid-click is unreliable, and the title rendered while later mutations from the async refill failed to sync to the client view - Both
openInventoryand the chained async DB read now defer to the next tick viarunTask. The async fetch is scheduled inside the deferred open so the order is guaranteed - Async DB reads are wrapped in try/catch with a visible failure card ("Failed to load — see server console for details") instead of an indefinite spinner. Console gets the stacktrace
- Same deferral applied to back-button navigation from the period list to the main GUI
v1.9.11
- Fixed
/lbadmin archive <past period>wiping current-period progress for online DELTA-board players.forceSnapshotAllPlayerswas overwriting existing new-period snapshots with the player's current stat, which zeroed the monthly value of anyone online at the moment of the archive forceSnapshotAllPlayersnow usessaveSnapshotsIfAbsentBatch(INSERT … DO NOTHING/INSERT IGNORE) — same idempotent path the join handler already uses. Live rollovers behave identically (no existing rows → every write succeeds); catch-up archives leave the new period's already-established baselines alone- Symptom this fixes: large negative
%rank_change%lore indicators (-114,-186, etc.) appearing on boards right after running/lbadmin archive
v1.9.10
- Per-board period history GUI. New "Past Periods" button on the main
/lbGUI opens a three-tier flow: pick a past period → pick a board (or "Points overall") → see that period's archived top 10 with player heads - The chat-only
/lb history <period> <board>command is unchanged for power users - Reads
period_archiveandpoints_archiveasync with a "Loading…" placeholder, so opening the GUI never blocks the main thread - New
HistoryGuiManager,HistoryHolder(with type/period/board state + slot mappings), and history button entry inmain-leaderboard.yml - Config-merge friendly: existing installs get the history button on next reload; legacy GUIs without the
items.historyblock silently skip placement
v1.9.9
- Reward delivery is now offline-aware. When a winner is offline at period rollover, the reward command is queued in a new
pending_rewardstable and replayed when they next join —give %player% diamondand other online-only commands no longer silently disappear - New
pending_rewardstable with a uuid index. SQLite uses its global lock for serialised drain; MySQL usesSELECT … FOR UPDATEinside a transaction so two backends sharing one DB can't both claim the same queue when a player joins both at once - Per-period idempotency markers (
rewards_distributed_<period>,archived_<period>) in themetadatatable. A mid-reset crash, or a second/lbadmin archive 2026-04 confirminvocation, no longer re-runs distribution and double-rewards winners - 2-second delay before draining the offline queue so the player's chunks and inventory finish loading before
give-style commands fire - Removed dead
RewardManager.distributeRewards()legacy code path that read from in-memory caches (replaced bydistributeRewardsForreading the period's frozen DB caches)
v1.9.8
- Fixed monthly reset silently failing to archive + distribute rewards. Three compounding bugs:
lastResetPerioddefaulted to the current period on every startup (so missed crossings became invisible after a restart), the field was in-memory only (so a successful reset was forgotten across restarts), andisResetTimerequiredgetHour() == resetHourexactly, which the hourly tick frequently missed - New
metadatak/v table. The last successfully reset period is persisted there, so reboots no longer mask missed resets - Reset is now triggered by period-key change, not exact-hour matching. Check interval lowered to 5 minutes; the reset fires once when
currentPeriodKey() != lastResetPeriod - Reset reads final standings from the period's frozen
board_cache/points_cacherows, so catch-up resets (period rollover happened while server was offline) work the same as live resets - New
/lbadmin archive <period> confirmadmin command to retroactively archive + distribute rewards for a past period — use this to fix April after upgrading
v1.9.7
- Fixed two more main-thread SQLite freezes the v1.9.6 batch fix didn't cover
/lbGUI open now pre-fetches the viewer's per-board monthly value and points total on an async worker, then refills the inventory from a per-viewer cache — no DB read happens on the main thread while rendering. Refills triggered by category-tab clicks and ResetVault interference reuse the same cachePlayerJoinListener's "show points on join" message now readsgetPlayerPointsasync and hops back to main thread only to send the chat line- Per-viewer cache is dropped on inventory close so it doesn't grow
v1.9.6
- Fixed server-thread freezes on player join when DELTA boards were configured. The join handler called
snapshotAllPlayers()synchronously, iterating every online player × every DELTA board with a locked SQLite write per row — so a single join could stall the tick loop for 30+ seconds while contending with the async board-update cycle for the sameReentrantLock - New
BoardManager.snapshotPlayerAsync(Player)only snapshots the joining player and dispatches the writes off-thread - New
DatabaseProvider.saveSnapshotsIfAbsentBatch(...)writes all rows in a single transaction usingINSERT … ON CONFLICT DO NOTHING(SQLite) /INSERT IGNORE(MySQL) — one fsync per join instead of one per row, no read-then-write roundtrip
v1.9.5
- Compatibility with Paper 1.20.x through 1.21.11+
- MySQL / MariaDB storage backend alongside SQLite
- DecentHolograms integration for physical leaderboard displays
- 16 board types including PlaceholderAPI-driven boards
- Points-from-position scoring across all boards
- Milestone alerts with configurable thresholds
- Per-board reward overrides at period end
- Weekly and biweekly reset schedules (in addition to monthly)
- Config-version auto-merge so upgrades preserve your customisations
v1.0, Initial Release
Date: 2026-02-26
Features
- 11 default leaderboard boards: Balance, Player Kills, Playtime, Votes, Skills XP, Monsters Killed, Animals Bred, Fish Caught, Crops Harvested, Blocks Placed, Ores Mined
- Monthly tracking with automatic resets
- Points system: awards 10 for 1st, 9 for 2nd down to 1 for 10th across all boards
- Automatic rewards: configurable commands at month end based on points ranking
- Customisable GUI: MiniMessage-formatted chest GUI with configurable layout, materials, and custom model data
- Built-in event tracking for ores mined, crops harvested, and blocks placed
- Vault integration for balance leaderboard
- PlaceholderAPI support with expansions for all boards, points, and rankings
- SQLite storage with WAL mode for performance
Board Types
STATISTIC, Minecraft vanilla statistics (delta tracking)CUSTOM_ORES/CUSTOM_CROPS/CUSTOM_BLOCKS, Plugin event trackingVAULT_BALANCE, Vault economy (current value)PLACEHOLDER, Any PlaceholderAPI placeholder