surroundingBlocks = PowerUtils.getSurroundingBlocks(blockFace, centerBlock, radius, depth);
-
+
if (surroundingBlocks.isEmpty()) {
debuggingMessages.sendConsoleMessage(ChatColor.RED + "No surrounding blocks found.");
return;
}
-
+
// Handle durability reduction for the main block first
if (player.getGameMode().equals(GameMode.SURVIVAL)) {
PowerUtils.reduceDurability(player, handItem);
-
+
// Schedule a delayed inventory update to ensure proper tool breaking
Bukkit.getScheduler().runTaskLater(plugin, () -> {
if (player.getInventory().getItemInMainHand().getType() == Material.AIR) {
@@ -111,14 +114,14 @@ public void onBlockBreak(BlockBreakEvent event) {
}
}, 1L);
}
-
+
for (Block block : surroundingBlocks) {
int exp = checkAndBreakBlock(player, handItem, block);
-
+
// Handle durability reduction per surrounding block
if (player.getGameMode().equals(GameMode.SURVIVAL)) {
PowerUtils.reduceDurability(player, handItem);
-
+
// Schedule a delayed inventory update to prevent tool reappearing
Bukkit.getScheduler().runTaskLater(plugin, () -> {
if (player.getInventory().getItemInMainHand().getType() == Material.AIR) {
@@ -126,17 +129,17 @@ public void onBlockBreak(BlockBreakEvent event) {
}
}, 1L);
}
-
+
// Handle XP drops
if (exp > 0) {
BlockExpEvent expEvent = new BlockExpEvent(block, exp);
plugin.getServer().getPluginManager().callEvent(expEvent);
-
+
ExperienceOrb orb = block.getWorld().spawn(block.getLocation(), ExperienceOrb.class);
orb.setExperience(expEvent.getExpToDrop());
}
}
- }
+ }
/**
* Checks and breaks a block if it is compatible with the PowerTool.
@@ -172,18 +175,30 @@ private int checkAndBreakBlock(Player player, ItemStack handItem, @Nonnull Block
.sendConsoleMessage(ChatColor.GREEN + "β
Jobs Reborn notified for block: " + block.getType());
}
- // XP Drops
- int expToDrop = switch (blockMat) {
- case COAL_ORE, DEEPSLATE_COAL_ORE -> (int) (Math.random() * 3); // 0-2 XP
- case DIAMOND_ORE, DEEPSLATE_DIAMOND_ORE, EMERALD_ORE, DEEPSLATE_EMERALD_ORE ->
- 3 + (int) (Math.random() * 5); // 3-7 XP
- case REDSTONE_ORE, DEEPSLATE_REDSTONE_ORE, LAPIS_ORE, DEEPSLATE_LAPIS_ORE ->
- 2 + (int) (Math.random() * 4); // 2-6 XP
- case NETHER_QUARTZ_ORE -> 2 + (int) (Math.random() * 3); // 2-5 XP
- case NETHER_GOLD_ORE -> 1 + (int) (Math.random() * 5); // 1-5 XP
- case SPAWNER -> 15 + (int) (Math.random() * 30); // 15-43 XP
- default -> 0; // No XP for blocks not listed
- };
+ // Retrieve XP drop configuration for this block
+ int expToDrop = 0;
+ try {
+ ConfigurationSection xpDropsSection = plugin.getConfig().getConfigurationSection("xp-drops");
+ if (xpDropsSection == null) {
+ plugin.getLogger().warning("XP drops configuration section 'xp-drops' not found. Using default XP of 0 for " + blockMat);
+ } else {
+ ConfigurationSection blockSection = xpDropsSection.getConfigurationSection(blockMat.toString());
+ if (blockSection == null) {
+ debuggingMessages.sendConsoleMessage("XP drop configuration for block " + blockMat + " not found. Using default XP of 0.");
+ } else {
+ int minXp = blockSection.getInt("min", 0);
+ int maxXp = blockSection.getInt("max", minXp);
+ if (maxXp < minXp) {
+ plugin.getLogger().warning("Invalid XP configuration for block " + blockMat + ": max (" + maxXp + ") is less than min (" + minXp + "). Using default XP of 0.");
+ } else {
+ // Calculate a random XP value within the range (inclusive)
+ expToDrop = minXp + new Random().nextInt(maxXp - minXp + 1);
+ }
+ }
+ }
+ } catch (Exception e) {
+ plugin.getLogger().severe("Error while retrieving XP drop configuration for block " + blockMat + ": " + e.getMessage());
+ }
// Break the block naturally if conditions are met
if (block.breakNaturally(handItem) && player.getGameMode().equals(GameMode.SURVIVAL)) {
@@ -192,7 +207,13 @@ private int checkAndBreakBlock(Player player, ItemStack handItem, @Nonnull Block
}
}
- return expToDrop; // Return XP only for eligible blocks
+ if (handItem != null && handItem.getItemMeta() != null && handItem.getItemMeta().hasEnchant(Enchantment.SILK_TOUCH)) {
+ debuggingMessages.sendConsoleMessage("Silk Touch detected on tool; no XP will be dropped for block " + blockMat);
+ return expToDrop = 0;
+ }
+ else {
+ return expToDrop;
+ }
}
return 0; // Return 0 XP if conditions aren't met
diff --git a/src/main/java/jodelle/powermining/managers/ConfigMigrator.java b/src/main/java/jodelle/powermining/managers/ConfigMigrator.java
index 3716bfa..e306b28 100644
--- a/src/main/java/jodelle/powermining/managers/ConfigMigrator.java
+++ b/src/main/java/jodelle/powermining/managers/ConfigMigrator.java
@@ -1,16 +1,18 @@
package jodelle.powermining.managers;
import jodelle.powermining.PowerMining;
-
import org.bukkit.configuration.file.FileConfiguration;
import java.util.*;
/**
- * Handles the migration of the configuration file to ensure compatibility with newer versions of the PowerMining plugin.
+ * Handles the migration of the configuration file to ensure compatibility with
+ * newer versions of the PowerMining plugin.
*
*
- * This class checks for outdated configuration formats and updates them to the latest version.
- * It ensures that required settings are present, renames deprecated keys, and converts the recipe format
+ * This class checks for outdated configuration formats and updates them to the
+ * latest version.
+ * It ensures that required settings are present, renames deprecated keys, and
+ * converts the recipe format
* to a structured format while enforcing a consistent order of tools.
*
*/
@@ -21,12 +23,13 @@ public class ConfigMigrator {
/**
* The current version of the configuration format.
*/
- private static final String CURRENT_VERSION = "1.0"; // New version for migration
+ private static final String CURRENT_VERSION = "1.2"; // Updated version for migration
/**
* Constructs a {@code ConfigMigrator} instance.
*
- * @param plugin The instance of {@link PowerMining} responsible for managing the configuration.
+ * @param plugin The instance of {@link PowerMining} responsible for managing
+ * the configuration.
*/
public ConfigMigrator(PowerMining plugin) {
this.plugin = plugin;
@@ -36,18 +39,29 @@ public ConfigMigrator(PowerMining plugin) {
* Checks and migrates the configuration file if it is outdated.
*
*
- * This method ensures that required configuration keys exist, renames deprecated keys,
- * removes old sections, and converts recipes into the new structured format while maintaining
+ * This method ensures that required configuration keys exist, renames
+ * deprecated keys,
+ * removes old sections, and converts recipes into the new structured format
+ * while maintaining
* a predefined order for tools.
*
*/
public void migrateConfig() {
FileConfiguration config = plugin.getConfig();
- String configVersion = config.getString("configVersion", null);
+ String configVersionStr = config.getString("configVersion", null);
+ double configVersion = 0.0;
+ if (configVersionStr != null) {
+ try {
+ configVersion = Double.parseDouble(configVersionStr);
+ } catch (NumberFormatException e) {
+ plugin.getLogger()
+ .warning("Invalid configVersion format (" + configVersionStr + "). Defaulting version to 0.");
+ }
+ }
- // If config is already updated, stop migration
- if ("1.0".equals(configVersion)) {
- plugin.getLogger().info("Config is already up-to-date (v1.0). No migration needed.");
+ // If config is already updated (>= 1.2), stop migration
+ if (configVersion >= 1.2) {
+ plugin.getLogger().info("Config is already up-to-date (v" + configVersion + "). No migration needed.");
return;
}
@@ -72,91 +86,142 @@ public void migrateConfig() {
plugin.getLogger().info("Renamed 'Deep' to 'Depth' (Value: " + depthValue + ")");
}
- // STEP 1: Extract Old Recipes Before Deleting
- List> oldRecipes = config.getList("Recipes"); // Store before deleting
+ if (configVersion < 1.0) {
+ // STEP 1: Extract Old Recipes Before Deleting
+ List> oldRecipes = config.getList("Recipes"); // Store before deleting
- // STEP 2: Remove Old "Recipes" Section
- if (config.contains("Recipes")) {
- config.set("Recipes", null);
- plugin.getLogger().info("Removed old 'Recipes' section.");
- }
-
- // STEP 3: Convert Old Recipe Format to New Format
- Map> newRecipes = new HashMap<>();
+ // STEP 2: Remove Old "Recipes" Section
+ if (config.contains("Recipes")) {
+ config.set("Recipes", null);
+ plugin.getLogger().info("Removed old 'Recipes' section.");
+ }
- if (oldRecipes != null) {
- for (Object entry : oldRecipes) {
- if (!(entry instanceof LinkedHashMap))
- continue;
- LinkedHashMap, ?> rawMap = (LinkedHashMap, ?>) entry;
+ // STEP 3: Convert Old Recipe Format to New Format
+ Map> newRecipes = new HashMap<>();
- for (Map.Entry, ?> rawEntry : rawMap.entrySet()) {
- if (!(rawEntry.getKey() instanceof String) || !(rawEntry.getValue() instanceof List)) {
- plugin.getLogger().warning("Skipping invalid recipe format: " + rawEntry);
+ if (oldRecipes != null) {
+ for (Object entry : oldRecipes) {
+ if (!(entry instanceof LinkedHashMap))
continue;
- }
+ LinkedHashMap, ?> rawMap = (LinkedHashMap, ?>) entry;
- String toolName = (String) rawEntry.getKey();
- List> rawList = (List>) rawEntry.getValue();
- Map ingredients = new LinkedHashMap<>();
+ for (Map.Entry, ?> rawEntry : rawMap.entrySet()) {
+ if (!(rawEntry.getKey() instanceof String) || !(rawEntry.getValue() instanceof List)) {
+ plugin.getLogger().warning("Skipping invalid recipe format: " + rawEntry);
+ continue;
+ }
- String[] layout = { "", "", "" };
- for (int i = 0; i < rawList.size(); i++) {
- String value = (String) rawList.get(i);
- int row = i / 3;
- char symbol = (char) ('A' + i);
+ String toolName = (String) rawEntry.getKey();
+ List> rawList = (List>) rawEntry.getValue();
+ Map ingredients = new LinkedHashMap<>();
- if (value.equalsIgnoreCase("EMPTY") || value.equalsIgnoreCase("AIR")) {
- ingredients.put(String.valueOf(symbol), "AIR");
- } else {
- ingredients.put(String.valueOf(symbol), value);
- }
+ String[] layout = { "", "", "" };
+ for (int i = 0; i < rawList.size(); i++) {
+ String value = (String) rawList.get(i);
+ int row = i / 3;
+ char symbol = (char) ('A' + i);
- layout[row] += symbol;
- }
+ if (value.equalsIgnoreCase("EMPTY") || value.equalsIgnoreCase("AIR")) {
+ ingredients.put(String.valueOf(symbol), "AIR");
+ } else {
+ ingredients.put(String.valueOf(symbol), value);
+ }
- Map orderedRecipeData = new LinkedHashMap<>();
- orderedRecipeData.put("recipe-ingredients", ingredients);
- orderedRecipeData.put("recipe-shape", Arrays.asList(layout));
+ layout[row] += symbol;
+ }
- newRecipes.put(toolName, orderedRecipeData);
+ Map orderedRecipeData = new LinkedHashMap<>();
+ orderedRecipeData.put("recipe-ingredients", ingredients);
+ orderedRecipeData.put("recipe-shape", Arrays.asList(layout));
+
+ newRecipes.put(toolName, orderedRecipeData);
+ }
}
}
- }
- // STEP 4: Enforce Tool Order
- List toolOrder = Arrays.asList(
- "WOODEN_HAMMER", "STONE_HAMMER", "IRON_HAMMER", "GOLDEN_HAMMER", "DIAMOND_HAMMER", "NETHERITE_HAMMER",
- "WOODEN_EXCAVATOR", "STONE_EXCAVATOR", "IRON_EXCAVATOR", "GOLDEN_EXCAVATOR", "DIAMOND_EXCAVATOR",
- "NETHERITE_EXCAVATOR",
- "WOODEN_PLOW", "STONE_PLOW", "IRON_PLOW", "GOLDEN_PLOW", "DIAMOND_PLOW", "NETHERITE_PLOW");
+ // STEP 4: Enforce Tool Order
+ List toolOrder = Arrays.asList(
+ "WOODEN_HAMMER", "STONE_HAMMER", "IRON_HAMMER", "GOLDEN_HAMMER", "DIAMOND_HAMMER",
+ "NETHERITE_HAMMER",
+ "WOODEN_EXCAVATOR", "STONE_EXCAVATOR", "IRON_EXCAVATOR", "GOLDEN_EXCAVATOR", "DIAMOND_EXCAVATOR",
+ "NETHERITE_EXCAVATOR",
+ "WOODEN_PLOW", "STONE_PLOW", "IRON_PLOW", "GOLDEN_PLOW", "DIAMOND_PLOW", "NETHERITE_PLOW");
- Map> migratedRecipes = new LinkedHashMap<>();
+ Map> migratedRecipes = new LinkedHashMap<>();
- for (String toolName : toolOrder) {
- if (!newRecipes.containsKey(toolName))
- continue;
+ for (String toolName : toolOrder) {
+ if (!newRecipes.containsKey(toolName))
+ continue;
- Map recipeData = newRecipes.get(toolName);
+ Map recipeData = newRecipes.get(toolName);
- Map orderedRecipeData = new LinkedHashMap<>();
- if (recipeData.containsKey("recipe-ingredients")) {
- orderedRecipeData.put("recipe-ingredients", recipeData.get("recipe-ingredients"));
- }
- if (recipeData.containsKey("recipe-shape")) {
- orderedRecipeData.put("recipe-shape", recipeData.get("recipe-shape"));
+ Map orderedRecipeData = new LinkedHashMap<>();
+ if (recipeData.containsKey("recipe-ingredients")) {
+ orderedRecipeData.put("recipe-ingredients", recipeData.get("recipe-ingredients"));
+ }
+ if (recipeData.containsKey("recipe-shape")) {
+ orderedRecipeData.put("recipe-shape", recipeData.get("recipe-shape"));
+ }
+
+ migratedRecipes.put(toolName, orderedRecipeData);
}
- migratedRecipes.put(toolName, orderedRecipeData);
+ // STEP 5: Store the Correctly Ordered Recipes in "recipes"
+ config.set("recipes", migratedRecipes);
+ plugin.getLogger().info("Recipes migrated successfully with enforced tool order.");
}
- // STEP 5: Store the Correctly Ordered Recipes in "recipes"
- config.set("recipes", migratedRecipes);
- plugin.getLogger().info("Recipes migrated successfully with enforced tool order.");
+ if (configVersion < 1.2) {
+ // STEP 6: Migrate config to version 1.2 by adding the xp-drops section
+ if (!config.contains("xp-drops") || config.getConfigurationSection("xp-drops").getKeys(false).isEmpty()) {
+ config.createSection("xp-drops");
+ config.set("xp-drops.COAL_ORE.min", 0);
+ config.set("xp-drops.COAL_ORE.max", 2);
+ config.set("xp-drops.DEEPSLATE_COAL_ORE.min", 0);
+ config.set("xp-drops.DEEPSLATE_COAL_ORE.max", 2);
+ config.set("xp-drops.DIAMOND_ORE.min", 3);
+ config.set("xp-drops.DIAMOND_ORE.max", 7);
+ config.set("xp-drops.DEEPSLATE_DIAMOND_ORE.min", 3);
+ config.set("xp-drops.DEEPSLATE_DIAMOND_ORE.max", 7);
+ config.set("xp-drops.EMERALD_ORE.min", 3);
+ config.set("xp-drops.EMERALD_ORE.max", 7);
+ config.set("xp-drops.DEEPSLATE_EMERALD_ORE.min", 3);
+ config.set("xp-drops.DEEPSLATE_EMERALD_ORE.max", 7);
+ config.set("xp-drops.LAPIS_ORE.min", 2);
+ config.set("xp-drops.LAPIS_ORE.max", 5);
+ config.set("xp-drops.DEEPSLATE_LAPIS_ORE.min", 2);
+ config.set("xp-drops.DEEPSLATE_LAPIS_ORE.max", 5);
+ config.set("xp-drops.REDSTONE_ORE.min", 1);
+ config.set("xp-drops.REDSTONE_ORE.max", 5);
+ config.set("xp-drops.DEEPSLATE_REDSTONE_ORE.min", 1);
+ config.set("xp-drops.DEEPSLATE_REDSTONE_ORE.max", 5);
+ config.set("xp-drops.NETHER_QUARTZ_ORE.min", 2);
+ config.set("xp-drops.NETHER_QUARTZ_ORE.max", 5);
+ config.set("xp-drops.NETHER_GOLD_ORE.min", 0);
+ config.set("xp-drops.NETHER_GOLD_ORE.max", 1);
+ config.set("xp-drops.SCULK.min", 1);
+ config.set("xp-drops.SCULK.max", 1);
+ config.set("xp-drops.SCULK_SENSOR.min", 5);
+ config.set("xp-drops.SCULK_SENSOR.max", 5);
+ config.set("xp-drops.SCULK_SHRIEKER.min", 5);
+ config.set("xp-drops.SCULK_SHRIEKER.max", 5);
+ config.set("xp-drops.SCULK_CATALYST.min", 5);
+ config.set("xp-drops.SCULK_CATALYST.max", 5);
+ config.set("xp-drops.CALIBRATED_SCULK_SENSOR.min", 5);
+ config.set("xp-drops.CALIBRATED_SCULK_SENSOR.max", 5);
+ config.set("xp-drops.SPAWNER.min", 15);
+ config.set("xp-drops.SPAWNER.max", 43);
+ plugin.getLogger().info("Added new xp-drops section to config with default values.");
+ } else {
+ plugin.getLogger().info("xp-drops section already exists, skipping creation.");
+ }
+
+ // Update the configuration version to 1.2
+ config.set("configVersion", CURRENT_VERSION);
+ plugin.getLogger().info("Config version updated to " + CURRENT_VERSION + ".");
+ }
// STEP 6: Save Config (Now That Everything is Set)
- config.set("configVersion", CURRENT_VERSION);
- plugin.getLogger().info("Set configVersion to " + CURRENT_VERSION);
plugin.saveConfig();
plugin.reloadConfig(); // Reload config, so recipes are correctly loaded
plugin.getLogger().info("Config migration complete.");
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index 91940ca..87453bd 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -765,6 +765,84 @@ recipes:
- "NHN"
- "ENE"
+# β‘ XP Drops Configuration
+# =========================
+#
+# πΉ What this does:
+# - Defines the XP drop ranges for various block types when broken.
+# - Each block material has a minimum and maximum XP value.
+# - Changes require a server restart or running: `/powermining admin reload`
+#
+# πΉ How to Define XP Drop Ranges:
+# - Use exact block material names from Spigot's Material list (case-sensitive).
+# - Full list of valid materials: π [Spigot API - Material List](https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/Material.html)
+# - The range is defined using two keys: `min` and `max`.
+# - Example:
+# COAL_ORE:
+# min: 0
+# max: 2
+#
+# πΉ Important Notes:
+# - Ensure that `max` is not less than `min` to avoid configuration errors.
+# - Available default values can be found in the plugin documentation or GitHub.
+#
+# β‘ Customize as needed!
+xp-drops:
+ COAL_ORE:
+ min: 0
+ max: 2
+ DEEPSLATE_COAL_ORE:
+ min: 0
+ max: 2
+ DIAMOND_ORE:
+ min: 3
+ max: 7
+ DEEPSLATE_DIAMOND_ORE:
+ min: 3
+ max: 7
+ EMERALD_ORE:
+ min: 3
+ max: 7
+ DEEPSLATE_EMERALD_ORE:
+ min: 3
+ max: 7
+ LAPIS_ORE:
+ min: 2
+ max: 5
+ DEEPSLATE_LAPIS_ORE:
+ min: 2
+ max: 5
+ REDSTONE_ORE:
+ min: 1
+ max: 5
+ DEEPSLATE_REDSTONE_ORE:
+ min: 1
+ max: 5
+ NETHER_QUARTZ_ORE:
+ min: 2
+ max: 5
+ NETHER_GOLD_ORE:
+ min: 0
+ max: 1
+ SCULK:
+ min: 1
+ max: 1
+ SCULK_SENSOR:
+ min: 5
+ max: 5
+ SCULK_SHRIEKER:
+ min: 5
+ max: 5
+ SCULK_CATALYST:
+ min: 5
+ max: 5
+ CALIBRATED_SCULK_SENSOR:
+ min: 5
+ max: 5
+ SPAWNER:
+ min: 15
+ max: 43
+
# βοΈ Configuration Version
# ------------------------
# This value tracks the version of the configuration file.
@@ -773,7 +851,7 @@ recipes:
# π¨ Do NOT change this value! π¨
# - Modifying it may cause the plugin to stop working.
# - If thereβs an issue, delete the config file and restart the server or reload the plugin to regenerate it.
-configVersion: "1.0"
+configVersion: "1.2"
# π οΈ Debug Mode
# -------------
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 289d98a..7da6ac8 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -1,6 +1,6 @@
name: JodellePowerMining
main: jodelle.powermining.PowerMining
-version: 1.1.2
+version: 1.2
authors: [JodelleLover, Holt]
description: Implements Hammer, Excavator and Plow tools for faster mining, version updated from BloodyShade.
api-version: "1.21.4"