diff --git a/CLAUDE.md b/CLAUDE.md index c65a622..d23967a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -48,7 +48,7 @@ The shaded JAR is produced at `target/MiningLevels-.jar`. ## External Dependencies -- **CrucialLib 3.0.0** — JSON persistence, localization, boss bars, player effects +- **CrucialLib 3.0.1** — JSON persistence, localization, boss bars, player effects - **PlaceholderAPI** (optional) — exposes level/XP placeholders and command rewards ## Testing Notes diff --git a/README.md b/README.md index ae10879..7304691 100644 --- a/README.md +++ b/README.md @@ -24,5 +24,5 @@ All dependencies are managed automatically by Maven. * Paper API * Version: 1.21 * CrucialLib - * Version: 3.0.0 + * Version: 3.0.1 * [GitHub](https://github.com/ChafficPlugins/CrucialLib) diff --git a/docs/index.md b/docs/index.md index 691a88c..0e6a3a1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -23,7 +23,7 @@ MiningLevels is a Spigot plugin that adds levels to your mining experience, brin - Java 21+ - Paper or Spigot 1.21+ -- [CrucialLib 3.0.0+](https://github.com/ChafficPlugins/CrucialLib) +- [CrucialLib 3.0.1+](https://github.com/ChafficPlugins/CrucialLib) - [PlaceholderAPI](https://www.spigotmc.org/resources/placeholderapi.6245/) (optional, required for placeholders and level-up commands) ## License diff --git a/docs/installation.md b/docs/installation.md index 48d19b1..018ecad 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -37,3 +37,7 @@ plugins/MiningLevels/ ``` Default `levels.json` and `blocks.json` are bundled inside the plugin JAR and copied automatically on first run. No internet connection is required for setup. + +### Player Auto-Registration + +Since version 1.3.1, players are automatically registered when they interact with any MiningLevels feature (mining, commands, placeholders, etc.). Previously, if a player was missing from `players.json`, actions like mining or running commands would fail with an error. Now the plugin creates a new player entry (level 0, 0 XP) on the fly and saves it immediately. Players are still also registered on join as before. diff --git a/pom.xml b/pom.xml index 72c6bbb..fe4da1f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ de.chafficplugins MiningLevels - 1.3.0 + 1.3.1 jar MiningLevels @@ -98,7 +98,7 @@ com.github.ChafficPlugins CrucialLib - v3.0.0 + v3.0.1 me.clip diff --git a/src/main/java/de/chafficplugins/mininglevels/api/MiningPlayer.java b/src/main/java/de/chafficplugins/mininglevels/api/MiningPlayer.java index 8c65888..b9b1349 100644 --- a/src/main/java/de/chafficplugins/mininglevels/api/MiningPlayer.java +++ b/src/main/java/de/chafficplugins/mininglevels/api/MiningPlayer.java @@ -246,6 +246,25 @@ public static MiningPlayer getMiningPlayer(UUID uuid) { return null; } + /** + * Gets the MiningPlayer with the given UUID, or creates a new one if it doesn't exist. + * @param uuid The UUID of the player to get or create. + * @return The MiningPlayer with the given UUID. + */ + public static MiningPlayer getOrCreateMiningPlayer(UUID uuid) { + MiningPlayer existing = getMiningPlayer(uuid); + if (existing != null) { + return existing; + } + MiningPlayer newPlayer = new MiningPlayer(uuid, 0, 0); + try { + save(); + } catch (IOException e) { + getPlugin().error("Failed to save new player: " + e.getMessage()); + } + return newPlayer; + } + /** * Checks if the given player is a MiningPlayer. * diff --git a/src/main/java/de/chafficplugins/mininglevels/gui/leaderboard/MiningLevelProfile.java b/src/main/java/de/chafficplugins/mininglevels/gui/leaderboard/MiningLevelProfile.java index 7a56ba0..76de946 100644 --- a/src/main/java/de/chafficplugins/mininglevels/gui/leaderboard/MiningLevelProfile.java +++ b/src/main/java/de/chafficplugins/mininglevels/gui/leaderboard/MiningLevelProfile.java @@ -33,8 +33,7 @@ public MiningLevelProfile(OfflinePlayer player) { @Override public void populate() { - MiningPlayer miningPlayer = MiningPlayer.getMiningPlayer(player.getUniqueId()); - if (miningPlayer == null) return; + MiningPlayer miningPlayer = MiningPlayer.getOrCreateMiningPlayer(player.getUniqueId()); ItemStack stack = Stack.getStack(miningPlayer.getUUID(), miningPlayer.getOfflinePlayer().getName(), List.of( Localizer.getLocalizedString(LOCALIZED_IDENTIFIER + "_" + CURRENT_LEVEL, miningPlayer.getLevel().getName()), Localizer.getLocalizedString(LOCALIZED_IDENTIFIER + "_" + CURRENT_XP, miningPlayer.getXp() + "", miningPlayer.getLevel().getNextLevelXP() + "") diff --git a/src/main/java/de/chafficplugins/mininglevels/listeners/MiningLevelsCommandListener.java b/src/main/java/de/chafficplugins/mininglevels/listeners/MiningLevelsCommandListener.java index 730354a..fe75861 100644 --- a/src/main/java/de/chafficplugins/mininglevels/listeners/MiningLevelsCommandListener.java +++ b/src/main/java/de/chafficplugins/mininglevels/listeners/MiningLevelsCommandListener.java @@ -146,11 +146,7 @@ public boolean onCommand(CommandSender sender, Command command, String label, St private boolean showLevelInfo(CommandSender sender) { Player player = (Player) sender; - MiningPlayer miningPlayer = MiningPlayer.getMiningPlayer(player.getUniqueId()); - if(miningPlayer == null) { - sendMessage(sender, ERROR_OCCURRED); - return true; - } + MiningPlayer miningPlayer = MiningPlayer.getOrCreateMiningPlayer(player.getUniqueId()); new MiningLevelProfile(player).open(player); diff --git a/src/main/java/de/chafficplugins/mininglevels/listeners/RewardCommandListener.java b/src/main/java/de/chafficplugins/mininglevels/listeners/RewardCommandListener.java index 5dd92a2..76b07c0 100644 --- a/src/main/java/de/chafficplugins/mininglevels/listeners/RewardCommandListener.java +++ b/src/main/java/de/chafficplugins/mininglevels/listeners/RewardCommandListener.java @@ -16,10 +16,7 @@ public boolean onCommand(CommandSender sender, Command command, String label, St if(command.getName().equalsIgnoreCase("miningrewards")) { if(sender instanceof Player) { Player player = (Player) sender; - MiningPlayer miningPlayer = MiningPlayer.getMiningPlayer(player.getUniqueId()); - if(miningPlayer == null) { - return true; - } + MiningPlayer miningPlayer = MiningPlayer.getOrCreateMiningPlayer(player.getUniqueId()); switch (miningPlayer.claim()) { case 0 -> sendMessage(player, NO_REWARDS, ChatColor.RED); case 1 -> sendMessage(player, REWARDS_CLAIMED, ChatColor.GREEN); diff --git a/src/main/java/de/chafficplugins/mininglevels/listeners/commands/LevelingCommands.java b/src/main/java/de/chafficplugins/mininglevels/listeners/commands/LevelingCommands.java index 4aef3ee..6098af0 100644 --- a/src/main/java/de/chafficplugins/mininglevels/listeners/commands/LevelingCommands.java +++ b/src/main/java/de/chafficplugins/mininglevels/listeners/commands/LevelingCommands.java @@ -19,14 +19,12 @@ public static void setLevel(CommandSender sender, String[] args) { if(args.length == 3) { Player player = Bukkit.getPlayer(args[1]); if(player != null) { - MiningPlayer miningPlayer = MiningPlayer.getMiningPlayer(player.getUniqueId()); - if(miningPlayer != null) { - int level = Integer.parseInt(args[2]); - if(level >= 0 && level < MiningLevel.miningLevels.size()) { - miningPlayer.setLevel(level); - sendMessage(player, NEW_LEVEL, String.valueOf(level)); - return; - } + MiningPlayer miningPlayer = MiningPlayer.getOrCreateMiningPlayer(player.getUniqueId()); + int level = Integer.parseInt(args[2]); + if(level >= 0 && level < MiningLevel.miningLevels.size()) { + miningPlayer.setLevel(level); + sendMessage(player, NEW_LEVEL, String.valueOf(level)); + return; } } else { sendMessage(sender, PLAYER_NOT_EXIST, args[1]); @@ -40,14 +38,12 @@ public static void setXP(CommandSender sender, String[] args) { if(args.length == 3) { Player player = Bukkit.getPlayer(args[1]); if(player != null) { - MiningPlayer miningPlayer = MiningPlayer.getMiningPlayer(player.getUniqueId()); - if(miningPlayer != null) { - int xp = Integer.parseInt(args[2]); - if(xp >= 0) { - miningPlayer.changeXp(xp); - sendMessage(player, XP_RECEIVED, String.valueOf(xp)); - return; - } + MiningPlayer miningPlayer = MiningPlayer.getOrCreateMiningPlayer(player.getUniqueId()); + int xp = Integer.parseInt(args[2]); + if(xp >= 0) { + miningPlayer.changeXp(xp); + sendMessage(player, XP_RECEIVED, String.valueOf(xp)); + return; } } else { sendMessage(sender, PLAYER_NOT_EXIST, args[1]); @@ -61,11 +57,9 @@ public static void level(CommandSender sender, String[] args) { if(args.length == 2) { Player player = Bukkit.getPlayer(args[1]); if(player != null) { - MiningPlayer miningPlayer = MiningPlayer.getMiningPlayer(player.getUniqueId()); - if(miningPlayer != null) { - sendMessage(sender, LEVEL_OF, player.getDisplayName(), String.valueOf(miningPlayer.getLevel().getName())); - return; - } + MiningPlayer miningPlayer = MiningPlayer.getOrCreateMiningPlayer(player.getUniqueId()); + sendMessage(sender, LEVEL_OF, player.getDisplayName(), String.valueOf(miningPlayer.getLevel().getName())); + return; } else { sendMessage(sender, PLAYER_NOT_EXIST, args[1]); return; diff --git a/src/main/java/de/chafficplugins/mininglevels/listeners/events/MiningEvents.java b/src/main/java/de/chafficplugins/mininglevels/listeners/events/MiningEvents.java index dece8b9..813b141 100644 --- a/src/main/java/de/chafficplugins/mininglevels/listeners/events/MiningEvents.java +++ b/src/main/java/de/chafficplugins/mininglevels/listeners/events/MiningEvents.java @@ -30,32 +30,27 @@ public void onBlockDamage(final BlockDamageEvent event) { final MiningBlock block = MiningBlock.getMiningBlock(event.getBlock().getType()); sendDebug(event.getPlayer(), "BlockDamageEvent: " + event.getBlock().getType().name()); if(block != null) { - final MiningPlayer miningPlayer = MiningPlayer.getMiningPlayer(event.getPlayer().getUniqueId()); - if(miningPlayer != null) { - if (miningPlayer.getLevel().getOrdinal() < block.getMinLevel()) { - sendDebug(event.getPlayer(), "BlockDamageEvent: " + "Level too low."); - MiningLevel level = MiningLevel.get(block.getMinLevel()); - if (level == null) return; - miningPlayer.showMessage(LEVEL_NEEDED, ChatColor.RED, level.getName()); - event.setCancelled(true); - } else { - ItemStack itemInUse = event.getPlayer().getInventory().getItemInMainHand(); - if (isMiningItem(itemInUse.getType())) { - if (miningPlayer.getLevel().getHasteLevel() > 0) { - sendDebug(event.getPlayer(), "BlockDamageEvent: " + "Haste level: " + miningPlayer.getLevel().getHasteLevel()); - event.getPlayer().addPotionEffect(new PotionEffect(PotionEffectType.HASTE, 5 * 20, miningPlayer.getLevel().getHasteLevel())); - } - if (MathUtils.randomDouble(0, 100) < miningPlayer.getLevel().getInstantBreakProbability()) { - sendDebug(event.getPlayer(), "BlockDamageEvent: " + "Instant break."); - event.setInstaBreak(true); //Insta break - } - } else { - sendDebug(event.getPlayer(), "BlockDamageEvent: " + "The held item " + itemInUse.getType() + " is not a mining item."); + final MiningPlayer miningPlayer = MiningPlayer.getOrCreateMiningPlayer(event.getPlayer().getUniqueId()); + if (miningPlayer.getLevel().getOrdinal() < block.getMinLevel()) { + sendDebug(event.getPlayer(), "BlockDamageEvent: " + "Level too low."); + MiningLevel level = MiningLevel.get(block.getMinLevel()); + if (level == null) return; + miningPlayer.showMessage(LEVEL_NEEDED, ChatColor.RED, level.getName()); + event.setCancelled(true); + } else { + ItemStack itemInUse = event.getPlayer().getInventory().getItemInMainHand(); + if (isMiningItem(itemInUse.getType())) { + if (miningPlayer.getLevel().getHasteLevel() > 0) { + sendDebug(event.getPlayer(), "BlockDamageEvent: " + "Haste level: " + miningPlayer.getLevel().getHasteLevel()); + event.getPlayer().addPotionEffect(new PotionEffect(PotionEffectType.HASTE, 5 * 20, miningPlayer.getLevel().getHasteLevel())); + } + if (MathUtils.randomDouble(0, 100) < miningPlayer.getLevel().getInstantBreakProbability()) { + sendDebug(event.getPlayer(), "BlockDamageEvent: " + "Instant break."); + event.setInstaBreak(true); //Insta break } + } else { + sendDebug(event.getPlayer(), "BlockDamageEvent: " + "The held item " + itemInUse.getType() + " is not a mining item."); } - } else { - sendDebug(event.getPlayer(), "BlockDamageEvent: " + "Error: Player is not registered to the plugin!"); - event.setCancelled(true); } } else { sendDebug(event.getPlayer(), "BlockDamageEvent: " + "Not a mining block."); @@ -71,51 +66,46 @@ public void onBlockBreak(final BlockBreakEvent event) { final MiningBlock block = MiningBlock.getMiningBlock(event.getBlock().getType()); sendDebug(event.getPlayer(), "BlockBreakEvent: " + event.getBlock().getType().name()); if(block != null) { - final MiningPlayer miningPlayer = MiningPlayer.getMiningPlayer(event.getPlayer().getUniqueId()); - if(miningPlayer != null) { - if(miningPlayer.getLevel().getOrdinal() < block.getMinLevel()) { - sendDebug(event.getPlayer(), "BlockBreakEvent: " + "Level too low."); - event.setCancelled(true); - MiningLevel level = MiningLevel.get(block.getMinLevel()); - if(level == null) return; - miningPlayer.showMessage(LEVEL_NEEDED, ChatColor.RED, level.getName()); - } else { - //check if the block was placed by a player - if (noXpBlocks.contains(event.getBlock())) { - sendDebug(event.getPlayer(), "BlockBreakEvent: " + "Config options disallow block to drop xp."); - return; - } - ItemStack itemInUse = event.getPlayer().getInventory().getItemInMainHand(); - if (!isMiningItem(itemInUse.getType())) { - sendDebug(event.getPlayer(), "BlockBreakEvent: " + "The held " + itemInUse.getType() + " item is not a mining item."); + final MiningPlayer miningPlayer = MiningPlayer.getOrCreateMiningPlayer(event.getPlayer().getUniqueId()); + if(miningPlayer.getLevel().getOrdinal() < block.getMinLevel()) { + sendDebug(event.getPlayer(), "BlockBreakEvent: " + "Level too low."); + event.setCancelled(true); + MiningLevel level = MiningLevel.get(block.getMinLevel()); + if(level == null) return; + miningPlayer.showMessage(LEVEL_NEEDED, ChatColor.RED, level.getName()); + } else { + //check if the block was placed by a player + if (noXpBlocks.contains(event.getBlock())) { + sendDebug(event.getPlayer(), "BlockBreakEvent: " + "Config options disallow block to drop xp."); + return; + } + ItemStack itemInUse = event.getPlayer().getInventory().getItemInMainHand(); + if (!isMiningItem(itemInUse.getType())) { + sendDebug(event.getPlayer(), "BlockBreakEvent: " + "The held " + itemInUse.getType() + " item is not a mining item."); + return; + } + + Bukkit.getScheduler().runTaskLater(plugin, () -> { + if (event.isCancelled()) { + sendDebug(event.getPlayer(), "BlockBreakEvent: " + "Event cancelled."); return; } + miningPlayer.alterXp(block.getXp()); + MiningLevel level = miningPlayer.getLevel(); - Bukkit.getScheduler().runTaskLater(plugin, () -> { - if (event.isCancelled()) { - sendDebug(event.getPlayer(), "BlockBreakEvent: " + "Event cancelled."); - return; - } - miningPlayer.alterXp(block.getXp()); - MiningLevel level = miningPlayer.getLevel(); + if (MathUtils.randomDouble(0, 100) < level.getExtraOreProbability()) { + Block actualBlock = event.getBlock(); - if (MathUtils.randomDouble(0, 100) < level.getExtraOreProbability()) { - Block actualBlock = event.getBlock(); + if (actualBlock.getDrops().isEmpty()) {return;} - if (actualBlock.getDrops().isEmpty()) {return;} - - int amount = (int) MathUtils.randomDouble(1, level.getMaxExtraOre()); - ItemStack item = actualBlock.getDrops().iterator().next(); - for (int i = 0; i < amount; i++) { - actualBlock.getDrops().add(item); - } - sendDebug(event.getPlayer(), "BlockBreakEvent: " + "Dropped " + amount + " extra ores."); + int amount = (int) MathUtils.randomDouble(1, level.getMaxExtraOre()); + ItemStack item = actualBlock.getDrops().iterator().next(); + for (int i = 0; i < amount; i++) { + actualBlock.getDrops().add(item); } - } , 2L); - } - } else { - sendDebug(event.getPlayer(), "BlockBreakEvent: " + "Error: Player is not registered to the plugin!"); - event.setCancelled(true); + sendDebug(event.getPlayer(), "BlockBreakEvent: " + "Dropped " + amount + " extra ores."); + } + } , 2L); } } else { sendDebug(event.getPlayer(), "BlockBreakEvent: " + "Not a mining block."); diff --git a/src/main/java/de/chafficplugins/mininglevels/listeners/events/NoXpBlockEvents.java b/src/main/java/de/chafficplugins/mininglevels/listeners/events/NoXpBlockEvents.java index 69bc799..c856fe6 100644 --- a/src/main/java/de/chafficplugins/mininglevels/listeners/events/NoXpBlockEvents.java +++ b/src/main/java/de/chafficplugins/mininglevels/listeners/events/NoXpBlockEvents.java @@ -75,8 +75,8 @@ public void onBlockExplode(final EntityExplodeEvent event) { ArrayList blocksToRemove = new ArrayList<>(); for(final Block block : event.blockList()) { MiningBlock miningBlock = MiningBlock.getMiningBlock(block.getType()); - MiningPlayer miningPlayer = MiningPlayer.getMiningPlayer(playerWithTNT.getPlayer().getUniqueId()); - if(miningBlock != null && miningPlayer != null) { + MiningPlayer miningPlayer = MiningPlayer.getOrCreateMiningPlayer(playerWithTNT.getPlayer().getUniqueId()); + if(miningBlock != null) { if(miningPlayer.getLevel().getOrdinal() < miningBlock.getMinLevel()) { blocksToRemove.add(block); } diff --git a/src/main/java/de/chafficplugins/mininglevels/placeholders/LevelPlaceholders.java b/src/main/java/de/chafficplugins/mininglevels/placeholders/LevelPlaceholders.java index a9826ab..e66bdeb 100644 --- a/src/main/java/de/chafficplugins/mininglevels/placeholders/LevelPlaceholders.java +++ b/src/main/java/de/chafficplugins/mininglevels/placeholders/LevelPlaceholders.java @@ -35,8 +35,7 @@ public boolean persist(){ @Override public String onRequest(OfflinePlayer player, @NotNull String identifier) { - MiningPlayer miningPlayer = MiningPlayer.getMiningPlayer(player.getUniqueId()); - if(miningPlayer == null) return "error"; + MiningPlayer miningPlayer = MiningPlayer.getOrCreateMiningPlayer(player.getUniqueId()); switch (identifier) { case "player_name" -> { return miningPlayer.getOfflinePlayer().getName();