
DonutShards
DonutShard is a feature-rich custom currency plugin. Earn Shards by PvP kills and AFK farming, then spend them in a beautiful GUI shop with multiple categories.
Список изменений
[1.1.0] — Bug Fix & Refactor Release
Bug Fixes
🔴 Critical
-
[BUG-1] DonutShardProvider.take() race condition — Replaced two-step
getBalance() → take()withshardManager.safeTake()(atomic CAS viaConcurrentHashMap.compute()). Players can no longer go negative or bypass balance checks when AFK ticker and shop purchase happen simultaneously. -
[BUG-2] ShopHook.charge() double-fires spent hook —
DonutShardAPI.take()already callsfireSpentHooks()internally. Removed the redundantplugin.getApiProvider().fireSpent()call inShopHook.charge(). ExternalShardEventHookimplementations no longer receive 2 events for 1 purchase.
🟡 Medium
-
[BUG-3] DonutShardProvider.hooks not thread-safe — Changed
ArrayList<ShardEventHook>toCopyOnWriteArrayList. PreventsConcurrentModificationExceptionwhen hooks are fired from the async SQLite writer thread while another thread callsregisterHook()/unregisterHook(). -
[BUG-4] autoDetectAfkPlayers() checks world only, not zone bounds — Added
plugin.getAfkConfigManager().isInAnyZone(player.getLocation())check before creating an auto-AFK session. Previously any player in the correct world (not just inside the zone) would start earning rewards. -
[BUG-5] getRank() O(n log n) on every PAPI render — Added
rankCache(Map<UUID, Integer>) with 60s TTL inShardManager, mirroring the existing leaderboard cache.invalidateLeaderboardCache()now resets both caches. PlaceholderAPI%donutshard_rank%no longer sorts all players on every tab-list refresh. -
[BUG-6] ShardShopGUI calls saveAll() after every purchase — Added
Storage.savePlayer(UUID)default method. YamlStorage overrides it to write onlybalances.yml(vs. all 4 YAML files). SQLiteStorage keeps the default no-op (already async). Reduces synchronous main-thread I/O on busy shops.
🟢 Low
-
[BUG-7] AFK ticker does not invalidate leaderboard cache — Added
shardManager.invalidateLeaderboardCache()at the end oftickAllSessions()whenever shards were distributed. Leaderboard now reflects AFK rewards within the same 60s cache window. -
[BUG-8] autoDetect calls startSession() → unnecessary teleport — Split auto-detect path into
beginSessionInPlace()which does NOT teleport.startSession()(called by/afk) continues to teleport. Players standing inside a zone are no longer sent to spawn on auto-detection. -
[BUG-9] Duplicate
afk_session_restoredkey in en.yml — Removed the stale copy at line 88 (the parser was silently using the version at line 105). -
[BUG-10] sqlite-jdbc not relocated in maven-shade-plugin — Added
<relocation>block inpom.xmlto repackageorg.sqlite→dev.duong2012g.libs.sqlite. Prevents ClassLoader conflicts with other plugins that also shade sqlite-jdbc (e.g. LuckPerms, AdvancedBan). -
[BUG-11]
donutshard.zone.bypassnot declared in plugin.yml — Added the permission toplugin.ymlwithdefault: opand included it as a child ofdonutshard.admin. LuckPerms / PermissionsEx now show it in autocomplete. -
[BUG-12] 17 dead message getters in ConfigManager — Removed all
getMsgXxx()fields and getters following the completed migration ofDonutShardCommandtoLanguageManager.ConfigManagernow only holds numeric/boolean/format config values. Theprefixfield was also removed (LanguageManager is now the single source-of-truth for all messages).
Refactors
-
ConfigManager cleaned — Down from ~230 lines to ~120 lines. Zero message strings remain; all messages live in
languages/<code>.yml. -
AfkManager restructured — Code reorganised into three clearly labelled sections: ① Session lifecycle, ② Reward ticker, ③ Auto-detect. No external API change.
-
ShardManager — dual cache —
getTopBalances()andgetRank()now share the same 60s TTL and are invalidated together viainvalidateLeaderboardCache(). -
AfkSettingCommand migrated — All inline response messages now use
lm().getPrefix()consistently. Thereloadsubcommand now reloads AFK config, shop config, and language files in addition to the main config. -
plugin.yml — All permissions now fully declared including
donutshard.zone.bypassanddonutshard.language.donutshard.admingrants all child permissions including zone bypass.
