fishcoin, now with 100% more minecraft
Find a file
2026-05-20 17:04:37 -04:00
src/main v1.9 - added wilfred. run. 2026-05-20 17:04:37 -04:00
.gitignore v1.8.3 - finally updated default config to match - whoops 2025-12-23 18:39:34 -05:00
changelog.md v1.9 - added wilfred. run. 2026-05-20 17:04:37 -04:00
pom.xml v1.9 - added wilfred. run. 2026-05-20 17:04:37 -04:00
README.md v1.8.7 - (1.21.11) merged completed-tasks.json into user_data.json, new /fc wallet command, server-name config, refactored FishcoinLogic helpers 2025-12-26 21:02:27 -05:00

fishcoin-minecraft

now fishcoin can power your smp

spigot/paper/bukkit version, will be merged into fabric branch eventually

for minecraft spigot/paper/bukkit 1.21.11

to build, run mvn clean package in the root directory

  • you'll find the built jar in the target folder

features

all of these features are configurable

  • /fishcoin (fc) command to manage your wallet, send fishcoin around, and recieve rewards from gameplay
  • withdraw coins as items, with the real fishcoin held in the server wallet
  • configurable death penalty (perfect for keepInventory)

rewards

  • passive income (just playing the game, configurable to increase near other players)
  • advancements
  • kill players to recieve their bounty
  • be the first to find world structures (or shortly after the first)
  • open loot chests for the first time
  • open a vault or ominous vault
  • eat an enchanted gapple
  • break a mob spawner
  • fill an end portal

new hidden advancements (for single time rewards)

  • make a cake
  • tame a cat
  • gloat the dragon egg (hold egg in your hand, in sunlight near another player)
  • touch the world border (overworld)
  • touch the world border corner (overworld)
  • touch the world border (nether)
  • touch the world border corner (nether)
  • touch the world border (end)
  • touch the world border corner (end)

more

advanced info

want to use your self-hosted stuff? use these hidden config options:

api: "<api-url-here>"
java-pack-url: "<java-pack-url>"
java-pack-hash: "<java-pack-hash>"
bedrock-pack-url: "<bedrock-pack-url>"
bedrock-mappings-url: "<backrock-mappings-url>"
receipt-viewer-url: "<receipt-viewer-url>&key="

if you're developing another plugin, use FishcoinHook to interact with fishcoin

note: FishcoinHook is loaded via reflection, so no compile dependency needed. always wrap calls in try-catch blocks.

give coins with custom fishcoin receipt message (memo)

try {
    Class<?> hookClass = Class.forName("com.fisheater.FishcoinHook");
    java.lang.reflect.Method method = hookClass.getMethod("give", String.class, long.class, String.class);
    method.invoke(null, "PlayerName", 100L, "Quiz reward for correct answer");
} catch (ClassNotFoundException e) {
    plugin.getLogger().warning("FishcoinHook not found");
} catch (Exception e) {
    plugin.getLogger().warning("FishcoinHook error: " + e.getMessage());
}

take coins with custom fishcoin receipt message (memo)

try {
    Class<?> hookClass = Class.forName("com.fisheater.FishcoinHook");
    java.lang.reflect.Method method = hookClass.getMethod("take", String.class, long.class, String.class, java.util.function.Consumer.class);
    method.invoke(null, "PlayerName", 50L, "Shop purchase: Diamond Sword", (java.util.function.Consumer<Boolean>) success -> {
        if (success) {
            player.sendMessage("purchase complete!");
        } else {
            player.sendMessage("insufficient funds");
        }
    });
} catch (ClassNotFoundException e) {
    plugin.getLogger().warning("FishcoinHook not found");
} catch (Exception e) {
    plugin.getLogger().warning("FishcoinHook error: " + e.getMessage());
}

check player balance (async)

try {
    Class<?> hookClass = Class.forName("com.fisheater.FishcoinHook");
    java.lang.reflect.Method method = hookClass.getMethod("getBalance", String.class, java.util.function.Consumer.class);
    method.invoke(null, "PlayerName", (java.util.function.Consumer<Long>) balance -> {
        Bukkit.getScheduler().runTask(plugin, () -> {
            if (balance == -1) {
                player.sendMessage("no wallet linked");
            } else {
                player.sendMessage("your balance: ₣" + java.text.NumberFormat.getInstance().format(balance));
            }
        });
    });
} catch (ClassNotFoundException e) {
    plugin.getLogger().warning("FishcoinHook not found");
} catch (Exception e) {
    plugin.getLogger().warning("FishcoinHook error: " + e.getMessage());
}

check server wallet balance (async)

try {
    Class<?> hookClass = Class.forName("com.fisheater.FishcoinHook");
    java.lang.reflect.Method method = hookClass.getMethod("getServerWalletBalance", java.util.function.Consumer.class);
    method.invoke(null, (java.util.function.Consumer<Long>) balance -> {
        Bukkit.getScheduler().runTask(plugin, () -> {
            if (balance == -1) {
                player.sendMessage("server wallet unavailable");
            } else {
                player.sendMessage("server wallet: ₣" + java.text.NumberFormat.getInstance().format(balance));
            }
        });
    });
} catch (ClassNotFoundException e) {
    plugin.getLogger().warning("FishcoinHook not found");
} catch (Exception e) {
    plugin.getLogger().warning("FishcoinHook error: " + e.getMessage());
}

check if player is currently active (from PlaytimeIncentive check)

try {
    Class<?> hookClass = Class.forName("com.fisheater.FishcoinHook");
    java.lang.reflect.Method method = hookClass.getMethod("isPlayerActive", String.class);
    boolean active = (boolean) method.invoke(null, player.getName());

    if (active) {
        player.sendMessage("§aYou are eligible for active player rewards!");
    } else {
        player.sendMessage("§cYou must be active to claim rewards.");
    }
} catch (ClassNotFoundException e) {
    plugin.getLogger().warning("FishcoinHook not found");
} catch (Exception e) {
    plugin.getLogger().warning("FishcoinHook error: " + e.getMessage());
}

complete example: shop plugin

@EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
    Player player = event.getPlayer();
    
    try {
        Class<?> hookClass = Class.forName("com.fisheater.FishcoinHook");
        
        // check if they have a wallet
        java.lang.reflect.Method hasWallet = hookClass.getMethod("hasWallet", String.class);
        boolean wallet = (boolean) hasWallet.invoke(null, player.getName());
        
        if (!wallet) {
            player.sendMessage("§cyou need to link your fishcoin wallet!");
            return;
        }
        
        // charge them
        java.lang.reflect.Method take = hookClass.getMethod("take", String.class, long.class, java.util.function.Consumer.class);
        take.invoke(null, player.getName(), 100L, (java.util.function.Consumer<Boolean>) success -> {
            if (success) {
                player.getInventory().addItem(new ItemStack(Material.DIAMOND));
                player.sendMessage("§apurchased diamond for ₣100!");
            } else {
                player.sendMessage("§cnot enough fishcoin!");
            }
        });
    } catch (ClassNotFoundException e) {
        player.sendMessage("§cFishcoin plugin not installed!");
    } catch (Exception e) {
        plugin.getLogger().warning("FishcoinHook error: " + e.getMessage());
    }
}