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.3.5 (Latest)
- Servers sharing one set of tables (MySQL without
mysql.server-id): fixed votes being overwritten by another server's old copy. Every server kept each player it had ever seen in memory for its whole uptime, and saved that copy again whenever the player quit, changed or not. Somebody who voted or claimed on one server and later only visited another had their totals, streak and claimed milestones written back to what that server last remembered. A join now starts from the database, and a quit no longer saves a record that has not changed. Single servers and networks with aserver-idper backend were not affected - Players who are offline and fully saved are dropped from memory after each save, so memory no longer grows for the whole uptime
- A vote's milestone claims are now saved together with the vote. They could miss that save and wait for the next one, which on a crash in between paid the milestone again
v1.3.4
- MySQL: fixed one unsaveable player record stopping every player's votes from being saved. Dirty players are written as one transaction, so a single row the database rejected (the classic case is a name longer than 16 characters, from a typo on a vote site or in
/voteplus setvotes) rolled back the whole batch, which was then retried every 15 seconds for good. Nothing was saved from that moment until a restart, and the final save at shutdown failed the same way. A rejected batch is now saved row by row, and only the bad row is dropped, with a warning - Votes for a name longer than 16 characters are ignored with a warning. No such player can exist.
/voteplus giveand/voteplus setvotesrefuse such a name up front - MySQL: an overlong service name or address is cut to fit its column instead of failing the whole vote log batch
- Networks using
mysql.server-id: every server now runs its own month-end. The "this month is done" markers were shared by the whole database, so the first server to notice the new month closed it out and all the others skipped their archive, payout and monthly reset. Months missed on those servers can be closed with/voteplus archive <period> confirm. Single servers are unaffected /voteplus monthlyno longer pays out the month that is still running. With every finished month already closed it used to pay the current standings early, and the markers it left made the real month-end skip its payout and its archive. It now says there is nothing to catch up on/voteplus setvotes <player> streak <n>now sticks. Streaks are worked out from the recorded vote days, so the number used to be thrown away by the player's next vote. It now rewrites those days, so the streak readsnand the next vote carries it on. Capped at 3650/voteplus resetnow clears the recorded vote days as well. They were left behind, so a reset player's old streak and best streak came back with their next vote. It also drops anything still waiting in the write buffer, which could put the record back a few seconds after the resettdxvoteplus.adminnow includes everytdxvoteplus.admin.*node. Ops never noticed, but a non-op staff group given onlytdxvoteplus.adminwas refused every/voteplussubcommand- The week is now pinned to Sunday to Saturday instead of being left to the JVM's defaults. This is what current Java versions already did, so no counter moves
- Corrected comments in the shipped files:
broadcast-to-onlineinvote-party.ymlswitches the party rewards, not the announcement. Menu text does not resolve PlaceholderAPI tokens. The reminder text isreminders.nudge.%month%is a key such as2026-04. The top-N placeholder is%tdxvoteplus_top_<period>_name_<n>%./voteplususage lists every subcommand
v1.3.3
- Fixed the Daily and Weekly leaderboards showing players from earlier days and weeks. A player's period counts only roll over when they next vote, so a board listed everybody's last voting day until each of them voted again. The boards now only count votes from the current day, week or month
- The same fix applies everywhere a count is shown:
/vote stats, the main and My Votes menus, milestone progress, the%tdxvoteplus_daily%,_weekly%and_monthly%placeholders and the API. Yesterday's votes no longer show as today's - A streak now shows
0once a full day has passed without a vote, instead of keeping the old number until the next vote - Fixed queued offline votes being payable twice. They were all paid out first and only marked as claimed afterwards, so clicking Claim All again while the first run was still going, or claiming while the join auto-claim was running, paid the same votes again. Each vote is now claimed in the database before it is paid, which also holds across servers sharing one database
/voteplus setvoteswithdaily,weeklyormonthlynow stamps the current period, so the value shows up on the boards and survives the player's next vote
v1.3.2
- Fixed repeating
MONTHLYmilestones never becoming claimable again. The month rollover stamped the new month on every player, which skipped the per-player rollover that clears those claims, so anybody who was not online at rollover kept them claimed forever - The monthly reset no longer zeroes players who already voted in the new month. That happened when it ran some time after the month flipped, or when
/voteplus archivewas used mid-month - MySQL 8:
rankis a reserved word from MySQL 8.0.2, so themonthly_historytable could not be created there and the plugin disabled itself on startup. The column is now quoted everywhere. MariaDB was not affected - Folia: the vote sound and particles now run on the voter's own region, and queued monthly rewards run their console commands on the global region
- Fixed queued monthly rewards being lost when the winner logged out in the moment between the rewards being claimed and being run. They are put back for the next join
- Fixed sounds with an underscore in their key never playing, including
ENTITY_EXPERIENCE_ORB_PICKUPandBLOCK_NOTE_BLOCK_PLING, both of whicheffects.ymlsuggests
v1.3.1
- Fixed votes being lost when two votes for the same player arrived at the same moment, which is normal for somebody working through several vote sites. Each vote runs on its own async thread and the counter update is a read-modify-write, so one increment could overwrite the other and the streak's day set could be corrupted. Votes for one player are now applied one at a time
- The same lock covers the milestone check, so two votes crossing a threshold together can no longer pay the same milestone twice, and a
/voteplus setvotesedit can no longer be overwritten by a vote landing in the middle of it - Fixed a small window in the autosave flush where a player marked dirty between the copy and the clear was dropped without being saved. That vote's counter update was lost if the server stopped before the player's next vote
- Fixed the first vote party never firing on a fresh database. With no
party.total_partiesrow yet, the startup snapshot never ran, so the first real party was consumed as that snapshot and nothing happened. A missing row now counts as0, and the server that wins the claim always fires - MySQL: the first increment of a counter returned a stale
LAST_INSERT_ID()instead of the stored value, which could mute later parties on that server until a restart. The value is now read back on that path
v1.3.0
- Folia and CanvasMC support. All scheduling now goes through the threaded-regions scheduler: anything touching a player runs on that player's region, broadcasts and console commands run on the global region, and database and HTTP work stays async. The same jar runs on Paper
- Requires Minecraft 26.1 or newer.
plugin.ymlnow declaresapi-version: '26.1'andfolia-supported: true, so an older server refuses to load it. Stay on v1.2.16 for 1.20.x to 1.21.x - Streaks are now worked out from the set of days a player voted on instead of a single "last vote" marker. An offline vote claimed days later counts for the day it was cast, so a late claim can repair a streak instead of breaking it
- New
vote_daystable, created automatically. The first time a player votes after the upgrade, their existing streak is carried over as it was, so nobody's streak resets and no milestone fires a second time - A replayed offline vote only adds to the daily, weekly and monthly counters when it was cast in the current period. Claiming a three day old vote no longer inflates today's daily count. The total always counts
v1.2.16
- 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_partiesis now the cross-server fire signal. When any server's refresh task detects the counter going up since its last local fire, it runstrigger()so its players see the broadcast + get rewards too- Per-server CAS dedup via
lastFiredTotalPartiesAtomicLong so the winning server doesn't double-fire (once fromincrement, once from its own refresh) - Startup snapshot, first refresh after enable seeds
lastFiredTotalPartieswithout 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_partiescounter is still accurate for placeholders / leaderboards
v1.2.15
- Fixed
/voteplus archive <period> confirmrefusing to run whenmonthly-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.applyCounterUpdateonly resets a single player'smonthly_voteson 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'smonthly_votesto 0 + stampslast_monthly_keywith the new period - True past-period standings are now reconstructed from the append-only
votestable viaaggregateMonthlyTopFromVotes, not the liveplayers.monthly_votescolumn. 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 archiveinvocation - New
/voteplus archive <period> confirmadmin 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_rewardstable 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_rewardstable with a uuid index. SQLite serialises drains through its lock; MySQL usesSELECT … FOR UPDATEso 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: truemilestones still stay claimed across rollovers as configured
v1.2.11
- Cleaned up
vote-rewards.ymlcomment text (configs-only, no code change)
v1.2.10
- Suppressed the public
vote.broadcastfor 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-claimkey invote-rewards.yml(defaultfalse; settrueto broadcast every replayed vote like real-time votes) - Real-time votes still honour the top-level
broadcastswitch - 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; permissiontdxvoteplus.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
DatabaseProviderdiagnostics: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.resolveUuidnow consults theplayerstable by name first, so new rows store under the player's real Mojang UUIDPlayerJoinListenerandClaimMenurepair existing orphaned rows on next login via a two-pronged UUID/name lookupstorage.ymlnow documents the per-server-id vs Votifier-forwarding interaction
v1.2.7
- New optional
mysql.server-idfield 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
IncompatibleClassChangeErroron Paper 1.20.4 caused byInventoryViewbeing a class in 1.20.x but an interface in 1.21+; routed click handling throughevent.getInventory()instead ofevent.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/toguis/(with one-time folder migration)
v1.2.3
- Leaderboard hopper filter
- Merged Milestones and Milestone Rewards GUIs into one screen
- Dropped
material/slotfromvote-sites.yml - Fixed 1.20.x prefix shadow tags