Sueta

DonutShards

1.2.0

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.

·Plugin
  1. 1.2.0latestDonutShards 1.2.0Jun 20, 2026
    2845 downloads
  2. 1.1.0DonutShards 1.1.0Jun 9, 2026

    [1.1.0] — Bug Fix & Refactor Release

    Bug Fixes
    #### 🔴 Critical - [BUG-1] DonutShardProvider.take() race condition — Replaced two-step getBalance() → take() with shardManager.safeTake() (atomic CAS via ConcurrentHashMap.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 calls fireSpentHooks() internally. Removed the redundant plugin.getApiProvider().fireSpent() call in ShopHook.charge(). External ShardEventHook implementations no longer receive 2 events for 1 purchase. #### 🟡 Medium - [BUG-3] DonutShardProvider.hooks not thread-safe — Changed ArrayList<ShardEventHook> to CopyOnWriteArrayList. Prevents ConcurrentModificationException when hooks are fired from the async SQLite writer thread while another thread calls registerHook() / 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 in ShardManager, 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 only balances.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 of tickAllSessions() 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_restored` key 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 in pom.xml to repackage org.sqlite → dev.duong2012g.libs.sqlite. Prevents ClassLoader conflicts with other plugins that also shade sqlite-jdbc (e.g. LuckPerms, AdvancedBan). - [BUG-11] `donutshard.zone.bypass` not declared in plugin.yml — Added the permission to plugin.yml with default: op and included it as a child of donutshard.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 of DonutShardCommand to LanguageManager. ConfigManager now only holds numeric/boolean/format config values. The prefix field 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() and getRank() now share the same 60s TTL and are invalidated together via invalidateLeaderboardCache(). - AfkSettingCommand migrated — All inline response messages now use lm().getPrefix() consistently. The reload subcommand 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.bypass and donutshard.language. donutshard.admin grants all child permissions including zone bypass.
    368 downloads
  3. 1.0.0DonutShards 1.0.0May 23, 2026
    303 downloads

Similar projects

Plugin →