All Mods Resource Packs Data Packs Shaders Modpacks Plugins
DonutShards

DonutShards

by Duong2012G • Downloads: 0 • Followers: 0
Description
Gallery
Changelog
Versions

🍩 DonutShard

A feature-rich custom currency plugin for PaperMC servers — earn Shards by killing, AFK farming, and spending them in a configurable shop.

Version API Java License


✨ Features

Feature Description
💰 Shard Currency Custom economy currency with leaderboard, balance commands
⚔️ Kill Rewards Earn Shards for PvP kills with cooldown and bonus multiplier
🌙 AFK System Earn Shards passively while AFK in designated warp zones
🛡️ Zone Protection WorldGuard-like protection for AFK zones (no WorldGuard required)
🏪 Shard Shop Multi-category GUI shop — buy items with Shards
🌐 Multi-language 5 built-in languages: vi en zh es fr
📊 Leaderboard /shard top with PlaceholderAPI support
🔌 Developer API Hook into Shard events from other plugins
💾 Dual Storage YAML or SQLite, with WAL-mode async writes

📦 Requirements

Requirement Version
PaperMC 1.21.x
Java 21+
PlaceholderAPI 2.11+ (optional)

⚙️ Installation

  1. Download DonutShard-1.0.0.jar and drop it into your /plugins folder.
  2. Start the server — config files generate automatically.
  3. Edit plugins/DonutShard/config.yml to your liking.
  4. Run /shard reload in-game to apply changes without restarting.

🗂️ File Structure

plugins/DonutShard/
├── config.yml          # Main config (kill reward, storage, leaderboard)
├── afk.yml             # AFK zones, protection rules, earn rates
├── shop.yml            # Shop categories & GUI layout
├── shops/
│   ├── Spawners.yml    # Spawner items
│   ├── Ores.yml        # Ore blocks
│   └── Farm.yml        # Farming items
├── languages/
│   ├── en.yml
│   ├── vi.yml
│   ├── zh.yml
│   ├── es.yml
│   └── fr.yml
└── data/               # YAML storage files (if YAML mode)
    ├── balances.yml
    ├── afk_sessions.yml
    ├── afk_warps.yml
    └── daily_earnings.yml

🔧 Configuration

config.yml — Quick Reference

language: en           # Active language (en/vi/zh/es/fr)

kill-reward:
  enabled: true
  shards-per-kill: 10
  kill-cooldown: 30    # seconds before same kill gives reward again
  ignore-afk-victims: false
  bonus-multiplier:
    enabled: false
    threshold: 1000    # if victim has >= this many Shards
    multiplier: 1.5

storage:
  type: SQLITE         # YAML or SQLITE
  auto-save-interval: 5  # minutes

leaderboard:
  size: 10

afk.yml — AFK System

enabled: true
shards-per-minute: 1
daily-limit: 100       # 0 = unlimited
teleport-cooldown: 5   # seconds between /afk uses
auto-detect-afk: true  # auto-detect players already in AFK world

protection:
  default:
    no-damage: true
    no-hunger: true
    no-pvp: true
    no-mob-spawn: true
    no-item-drop: true
    restore-health-on-enter: true
    clear-effects-on-enter: true

Adding a Shop Category

  1. Create plugins/DonutShard/shops/MyCategory.yml:
gui-title: "<gold>⭐ My Category"
items:
  - id: DIAMOND
    name: "<aqua>💎 Diamond"
    price: 100
    slot: 10
    quantity: 1
    lore:
      - "<gray>Shiny and expensive"
  - id: SPAWNER
    name: "<red>🔥 Blaze Spawner"
    spawnertype: BLAZE
    price: 5000
    slot: 11
    quantity: 1
  1. Add the category to shop.yml under categories:
categories:
  MyCategory:
    icon: DIAMOND
    name: "<aqua>My Category"
    slot: 4
    lore:
      - "<gray>Buy diamonds and spawners"
  1. Run /shard reload. No restart needed!

💬 Commands

Command Description Permission
/shard balance [player] View Shard balance donutshard.balance
/shard give <player> <amount> Give Shards donutshard.give
/shard take <player> <amount> Take Shards donutshard.take
/shard set <player> <amount> Set Shards donutshard.set
/shard top [page] View leaderboard donutshard.top
/shard reload Reload config donutshard.reload
/shard set-afk-warp <name> Set AFK warp to a world donutshard.admin
/shard remove-afk-warp <name> Remove AFK warp donutshard.admin
/shard list-afk-warp List all AFK warps donutshard.admin
/shard language <code> Change language donutshard.language
/afk [warpname] Enter an AFK zone donutshard.afk
/afk-setting Manage AFK zone settings donutshard.afk.admin
/shardshop Open the Shard Shop donutshard.shop

Aliases: /shard → /donutshard, /ds


🛡️ Permissions

Permission Description Default
donutshard.balance Check own balance true
donutshard.balance.others Check other's balance op
donutshard.give Give Shards to others op
donutshard.take Take Shards from others op
donutshard.set Set Shard balance op
donutshard.top View leaderboard true
donutshard.reload Reload plugin op
donutshard.admin All admin commands op
donutshard.afk Use /afk command true
donutshard.afk.admin Manage AFK settings op
donutshard.shop Open Shard shop true
donutshard.kill.bypass This player's deaths give no Shards false
donutshard.zone.bypass Bypass zone protection op
donutshard.language Change language true

📊 PlaceholderAPI

Register these placeholders in any compatible plugin:

Placeholder Returns
%donutshard_balance% Player's Shard balance
%donutshard_rank% Player's leaderboard rank
%donutshard_top_1% to %donutshard_top_10% Top player name at rank N
%donutshard_top_balance_1% Top player balance at rank N

🔌 Developer API

Add DonutShard as a dependency in your plugin:

// Check availability
if (DonutShardAPI.isAvailable()) {

    // Get balance
    long balance = DonutShardAPI.getBalance(player);

    // Give Shards with a reason
    DonutShardAPI.give(player, 100, "Quest reward");

    // Take Shards (returns false if insufficient)
    boolean success = DonutShardAPI.take(player, 50);

    // Listen to Shard events
    DonutShardAPI.registerHook(new ShardEventHook() {
        @Override
        public void onShardEarned(UUID uuid, long amount, String reason) {
            // ...
        }
        @Override
        public void onShardSpent(UUID uuid, long amount, String reason, long newBalance) {
            // ...
        }
    });
}

🔨 Building from Source

git clone https://github.com/duong2012g/DonutShard.git
cd DonutShard
mvn clean package
# Output: target/DonutShard-1.0.0.jar

Requirements: Java 21, Maven 3.8+


📝 License

MIT — free to use, modify, and redistribute with attribution.


Made with ❤️ by duong2012g

All Versions

+ Add Version
Name Game version Platforms Published Downloads Actions
1.21.x
Bukkit Paper Purpur Spigot
7h ago 1
1.21.x
Bukkit Paper Purpur Spigot
7h ago 1
1.21.x
Bukkit Paper Purpur Spigot
7h ago 1
[1.1.0] DonutShards 1.1.0 by Duong2012G on Oct 15, 2026
Download
## [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.

DonutShards Icon
Download DonutShards
Select game version
Select platform