TDXDevelopment
TDX VotePlus

Changelog

Version history and release notes for TDX VotePlus.

All notable changes to TDX VotePlus are documented here. Versions are listed newest-first.


v1.2.16 (Latest)

  • Fixed vote parties only firing on one server in a shared-DB cluster. The original v1.2.6 design used an atomic compare-and-subtract so exactly one server wins the threshold-crossing race, but never wired the cross-server propagation that was supposed to follow — peer servers' refresh task only synced the counter value, never fired trigger() locally. Result: only the winning server's online players got the broadcast + rewards. If MySQL latency was asymmetric, one backend could lose the race every time (matches the "kiwi never triggers" report)
  • total_parties is now the cross-server fire signal. When any server's refresh task detects the counter going up since its last local fire, it runs trigger() so its players see the broadcast + get rewards too
  • Per-server CAS dedup via lastFiredTotalParties AtomicLong so the winning server doesn't double-fire (once from increment, once from its own refresh)
  • Startup snapshot — first refresh after enable seeds lastFiredTotalParties without replaying historical parties as fresh celebrations
  • Lag-collapse — if multiple parties happened between refreshes, players see one celebration rather than a flurry; the underlying total_parties counter is still accurate for placeholders / leaderboards

v1.2.15

  • Fixed /voteplus archive <period> confirm refusing to run when monthly-rewards.enabled: false. Archive (writing history + bulk-resetting the leaderboard) is independent of reward distribution — a tracking-only server should still be able to close out past periods cleanly. The check is now decoupled: archive + bulk reset always run; rewards distribution + winner broadcast only fire when rewards are enabled
  • The auto-rollover hourly check still respects enabled: false (so users who deliberately disabled the system don't get surprise side effects)

v1.2.14

  • Fixed the Vote Leaderboard not rolling over to the new month. The per-vote rollover in VoteService.applyCounterUpdate only resets a single player's monthly_votes on their first vote of the new period, so the leaderboard kept showing last-month's standings for any player who hadn't voted yet
  • New DataManager.resetAllMonthlyVotes(newMonthKey) runs once per period change and bulk-resets every row's monthly_votes to 0 + stamps last_monthly_key with the new period
  • True past-period standings are now reconstructed from the append-only votes table via aggregateMonthlyTopFromVotes, not the live players.monthly_votes column. So archive + rewards work correctly even days after rollover, when fast voters have already triggered their per-row reset
  • Per-period idempotency markers (monthly.archived_<period>, monthly.rewards_distributed_<period>) prevent double-distribution if the rollover is re-run after a crash or a duplicate /voteplus archive invocation
  • New /voteplus archive <period> confirm admin command for catch-up. Reconstructs the period's top-N from votes, archives to history, distributes rewards (online + offline-queued), bulk-resets monthly counters
  • Offline winners are no longer skipped. Each command-type reward is queued in the new pending_rewards table and replayed on the winner's next join. Non-command rewards (items, messages, fireworks) are still skipped for offline winners — they need a live Player at apply time
  • New pending_rewards table with a uuid index. SQLite serialises drains through its lock; MySQL uses SELECT … FOR UPDATE so two backends sharing one DB can't both claim the same queue when a player joins both at once

v1.2.13

  • Renamed the "Next Monthly Rewards" card in the Vote Leaderboard GUI to "Top Monthly Voters" with shorter "Reset in" lore

v1.2.12

  • Fixed repeating milestones (one-time: false) firing on every vote past the threshold instead of once per period
  • Daily / weekly / monthly key changes and streak breaks now clear the matching claims so repeating milestones fire again on the next period
  • one-time: true milestones still stay claimed across rollovers as configured

v1.2.11

  • Cleaned up vote-rewards.yml comment text (configs-only, no code change)

v1.2.10

  • Suppressed the public vote.broadcast for replayed offline votes by default, so a multi-day offline voter rejoining no longer dumps a wall of "Player just voted!" lines into chat
  • New offline-queue.broadcast-on-claim key in vote-rewards.yml (default false; set true to broadcast every replayed vote like real-time votes)
  • Real-time votes still honour the top-level broadcast switch
  • Personal reward messages, counters, streaks, milestones, and vote-party progress on replayed votes are unaffected

v1.2.9

  • New /voteplus offline [player] admin subcommand for diagnosing missing votes; permission tdxvoteplus.admin.debug
  • Always-on [VotifierHook] Received vote: log line on every incoming vote
  • Debug-gated [VoteService] pipeline trace covering UUID resolution, online vs offline path, and the target table on queue insert
  • "Queued offline vote for …" log line no longer requires debug mode
  • New DatabaseProvider diagnostics: dumpOfflineQueue, countAllOfflineVotes, offlineQueueTableName

v1.2.8

  • Fixed offline votes silently lost when a player's name had fallen out of the server's usercache.json
  • VoteService.resolveUuid now consults the players table by name first, so new rows store under the player's real Mojang UUID
  • PlayerJoinListener and ClaimMenu repair existing orphaned rows on next login via a two-pronged UUID/name lookup
  • storage.yml now documents the per-server-id vs Votifier-forwarding interaction

v1.2.7

  • New optional mysql.server-id field for per-server player data isolation when multiple servers share one MySQL database
  • Scoped tables: players, votes, offline_queue, monthly_history
  • Shared table: metadata, so the vote-party counter stays global across the cluster
  • Startup banner shows (shared tables) or (server-id 'survival') so the mode is visible at a glance
  • Config-version bumped 7 to 8, auto-merged into existing installs

v1.2.6

  • Fixed the main menu "Next Milestone" card showing a streak milestone while the Milestones GUI showed a non-streak one
  • Fixed vote-party counter drift across servers sharing one MySQL DB; the counter now uses DB-side atomic increment with compare-and-subtract for threshold crossing
  • 5-second background refresh task so peer servers see the shared counter move between votes

v1.2.5

  • Fixed IncompatibleClassChangeError on Paper 1.20.4 caused by InventoryView being a class in 1.20.x but an interface in 1.21+; routed click handling through event.getInventory() instead of event.getView().getTopInventory()

v1.2.4

  • Filtered streak rewards out of the Milestones GUI
  • Made milestone item lore configurable via lore: field on each milestone entry
  • Fixed duplicate-clock bug in Streaks GUI
  • Hidden enchant glints and attribute modifiers via ItemFlag.values()
  • Renamed gui_menus/ to guis/ (with one-time folder migration)

v1.2.3

  • Leaderboard hopper filter
  • Merged Milestones and Milestone Rewards GUIs into one screen
  • Dropped material / slot from vote-sites.yml
  • Fixed 1.20.x prefix shadow tags