Browse Source

major optimizations to getting xp values for specific blocks

nossr50 8 months ago
parent
commit
ce18f3df97
28 changed files with 236 additions and 243 deletions
  1. 1 0
      Changelog.txt
  2. 11 5
      src/main/java/com/gmail/nossr50/api/ExperienceAPI.java
  3. 0 1
      src/main/java/com/gmail/nossr50/chat/ChatManager.java
  4. 0 1
      src/main/java/com/gmail/nossr50/commands/skills/CrossbowsCommand.java
  5. 0 1
      src/main/java/com/gmail/nossr50/commands/skills/MacesCommand.java
  6. 0 1
      src/main/java/com/gmail/nossr50/commands/skills/TridentsCommand.java
  7. 4 3
      src/main/java/com/gmail/nossr50/config/GeneralConfig.java
  8. 37 48
      src/main/java/com/gmail/nossr50/config/experience/ExperienceConfig.java
  9. 1 1
      src/main/java/com/gmail/nossr50/config/party/ItemWeightConfig.java
  10. 0 1
      src/main/java/com/gmail/nossr50/config/skills/alchemy/PotionConfig.java
  11. 0 1
      src/main/java/com/gmail/nossr50/config/skills/salvage/SalvageConfig.java
  12. 14 14
      src/main/java/com/gmail/nossr50/config/treasure/TreasureConfig.java
  13. 0 1
      src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java
  14. 1 1
      src/main/java/com/gmail/nossr50/datatypes/skills/SuperAbilityType.java
  15. 0 3
      src/main/java/com/gmail/nossr50/listeners/EntityListener.java
  16. 1 1
      src/main/java/com/gmail/nossr50/skills/alchemy/AlchemyManager.java
  17. 0 1
      src/main/java/com/gmail/nossr50/skills/axes/AxesManager.java
  18. 1 1
      src/main/java/com/gmail/nossr50/skills/excavation/Excavation.java
  19. 5 5
      src/main/java/com/gmail/nossr50/skills/herbalism/HerbalismManager.java
  20. 3 3
      src/main/java/com/gmail/nossr50/skills/repair/RepairManager.java
  21. 4 4
      src/main/java/com/gmail/nossr50/skills/salvage/SalvageManager.java
  22. 1 2
      src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java
  23. 0 1
      src/main/java/com/gmail/nossr50/util/AttributeMapper.java
  24. 0 2
      src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java
  25. 0 20
      src/main/java/com/gmail/nossr50/util/skills/SkillTools.java
  26. 16 9
      src/main/java/com/gmail/nossr50/util/sounds/SoundManager.java
  27. 136 111
      src/main/java/com/gmail/nossr50/util/text/StringUtils.java
  28. 0 1
      src/test/java/com/gmail/nossr50/MMOTestEnvironment.java

+ 1 - 0
Changelog.txt

@@ -1,4 +1,5 @@
 Version 2.2.024
+    Significant optimizations to the way mcMMO handles grabbing XP values for blocks
 
 Version 2.2.023
     Compatibility with Minecraft 1.21.3

+ 11 - 5
src/main/java/com/gmail/nossr50/api/ExperienceAPI.java

@@ -1154,15 +1154,19 @@ public final class ExperienceAPI {
     }
 
     /**
-     * Will add the appropriate type of XP from the block to the player based on the material of the blocks given if it matches the given skillType
+     * Will add the appropriate type of XP from the block to the player based on the material of the blocks given
+     * if it matches the given skillType
      * @param blockStates the blocks to reward XP for
      * @param mcMMOPlayer the target player
      * @param skillType target primary skill
      */
-    public static void addXpFromBlocksBySkill(ArrayList<BlockState> blockStates, McMMOPlayer mcMMOPlayer, PrimarySkillType skillType) {
+    public static void addXpFromBlocksBySkill(ArrayList<BlockState> blockStates, McMMOPlayer mcMMOPlayer,
+                                              PrimarySkillType skillType) {
         for(BlockState bs : blockStates) {
             if (ExperienceConfig.getInstance().getXp(skillType, bs.getType()) > 0) {
-                mcMMOPlayer.applyXpGain(skillType, ExperienceConfig.getInstance().getXp(skillType, bs.getType()), XPGainReason.PVE, XPGainSource.SELF);
+                mcMMOPlayer.applyXpGain(skillType,
+                        ExperienceConfig.getInstance().getXp(skillType, bs.getType()),
+                        XPGainReason.PVE, XPGainSource.SELF);
             }
         }
     }
@@ -1175,7 +1179,8 @@ public final class ExperienceAPI {
     public static void addXpFromBlock(BlockState blockState, McMMOPlayer mcMMOPlayer) {
         for(PrimarySkillType skillType : PrimarySkillType.values()) {
             if (ExperienceConfig.getInstance().getXp(skillType, blockState.getType()) > 0) {
-                mcMMOPlayer.applyXpGain(skillType, ExperienceConfig.getInstance().getXp(skillType, blockState.getType()), XPGainReason.PVE, XPGainSource.SELF);
+                mcMMOPlayer.applyXpGain(skillType, ExperienceConfig.getInstance().getXp(
+                        skillType, blockState.getType()), XPGainReason.PVE, XPGainSource.SELF);
             }
         }
     }
@@ -1188,7 +1193,8 @@ public final class ExperienceAPI {
      */
     public static void addXpFromBlockBySkill(BlockState blockState, McMMOPlayer mcMMOPlayer, PrimarySkillType skillType) {
         if (ExperienceConfig.getInstance().getXp(skillType, blockState.getType()) > 0) {
-            mcMMOPlayer.applyXpGain(skillType, ExperienceConfig.getInstance().getXp(skillType, blockState.getType()), XPGainReason.PVE, XPGainSource.SELF);
+            mcMMOPlayer.applyXpGain(skillType, ExperienceConfig.getInstance().getXp(skillType, blockState.getType()),
+                    XPGainReason.PVE, XPGainSource.SELF);
         }
     }
 

+ 0 - 1
src/main/java/com/gmail/nossr50/chat/ChatManager.java

@@ -14,7 +14,6 @@ import com.gmail.nossr50.util.Misc;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.text.StringUtils;
 import net.kyori.adventure.audience.Audience;
-import net.kyori.adventure.chat.SignedMessage;
 import net.kyori.adventure.text.TextComponent;
 import org.bukkit.command.ConsoleCommandSender;
 import org.jetbrains.annotations.NotNull;

+ 0 - 1
src/main/java/com/gmail/nossr50/commands/skills/CrossbowsCommand.java

@@ -6,7 +6,6 @@ import com.gmail.nossr50.util.skills.CombatUtils;
 import com.gmail.nossr50.util.skills.RankUtils;
 import com.gmail.nossr50.util.text.TextComponentFactory;
 import net.kyori.adventure.text.Component;
-import org.bukkit.ChatColor;
 import org.bukkit.entity.Player;
 
 import java.util.ArrayList;

+ 0 - 1
src/main/java/com/gmail/nossr50/commands/skills/MacesCommand.java

@@ -10,7 +10,6 @@ import com.gmail.nossr50.util.skills.RankUtils;
 import com.gmail.nossr50.util.skills.SkillUtils;
 import com.gmail.nossr50.util.text.TextComponentFactory;
 import net.kyori.adventure.text.Component;
-import org.bukkit.ChatColor;
 import org.bukkit.entity.Player;
 
 import java.util.ArrayList;

+ 0 - 1
src/main/java/com/gmail/nossr50/commands/skills/TridentsCommand.java

@@ -5,7 +5,6 @@ import com.gmail.nossr50.util.skills.CombatUtils;
 import com.gmail.nossr50.util.skills.SkillUtils;
 import com.gmail.nossr50.util.text.TextComponentFactory;
 import net.kyori.adventure.text.Component;
-import org.bukkit.ChatColor;
 import org.bukkit.entity.Player;
 
 import java.util.ArrayList;

+ 4 - 3
src/main/java/com/gmail/nossr50/config/GeneralConfig.java

@@ -711,7 +711,7 @@ public class GeneralConfig extends BukkitConfig {
         if (material.toString().equalsIgnoreCase("LILY_PAD"))
             return false;
 
-        return config.getBoolean("Bonus_Drops." + StringUtils.getCapitalized(skill.toString()) + "." + StringUtils.getPrettyItemString(material).replace(" ", "_"));
+        return config.getBoolean("Bonus_Drops." + StringUtils.getCapitalized(skill.toString()) + "." + StringUtils.getFormattedMaterialString(material).replace(" ", "_"));
     }
 
     public boolean getDoubleDropsDisabled(PrimarySkillType skill) {
@@ -890,8 +890,9 @@ public class GeneralConfig extends BukkitConfig {
     }
 
     /* Woodcutting */
-    public boolean getWoodcuttingDoubleDropsEnabled(BlockData material) {
-        return config.getBoolean("Bonus_Drops.Woodcutting." + StringUtils.getFriendlyConfigBlockDataString(material));
+    public boolean getWoodcuttingDoubleDropsEnabled(BlockData blockData) {
+        return config.getBoolean("Bonus_Drops.Woodcutting."
+                + StringUtils.getFormattedMaterialString(blockData.getMaterial()));
     }
 
     public boolean getTreeFellerSoundsEnabled() {

+ 37 - 48
src/main/java/com/gmail/nossr50/config/experience/ExperienceConfig.java

@@ -15,10 +15,15 @@ import org.bukkit.boss.BarStyle;
 import org.bukkit.entity.EntityType;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+
+import static com.gmail.nossr50.util.skills.SkillTools.isChildSkill;
 
 public class ExperienceConfig extends BukkitConfig {
     private static ExperienceConfig instance;
+    final private Map<PrimarySkillType, Map<Material, Integer>> blockExperienceMap = new HashMap<>();
 
     private ExperienceConfig() {
         super("experience.yml");
@@ -30,6 +35,23 @@ public class ExperienceConfig extends BukkitConfig {
             instance = new ExperienceConfig();
         }
 
+        for (PrimarySkillType skill : PrimarySkillType.values()) {
+            // Skip child skills
+            if (isChildSkill(skill)) {
+                continue;
+            }
+
+            final Map<Material, Integer> experienceMap = new HashMap<>();
+            for (Material material : Material.values()) {
+                int xp = instance.getConfigXp(skill, material);
+
+                if (xp > 0) {
+                    experienceMap.put(material, xp);
+                }
+            }
+            instance.blockExperienceMap.put(skill, experienceMap);
+        }
+
         return instance;
     }
 
@@ -321,15 +343,22 @@ public class ExperienceConfig extends BukkitConfig {
     }
 
     /* Materials  */
+    private int getConfigXp(PrimarySkillType skill, Material material) {
+        // prevents exploit
+        if (material == Material.LILY_PAD)
+            return 0;
+
+        final String baseString = "Experience_Values." + StringUtils.getCapitalized(skill.toString()) + ".";
+        final String configPath = baseString + StringUtils.getFormattedMaterialString(material);
+        return config.getInt(configPath, 0);
+    }
+
     public int getXp(PrimarySkillType skill, Material material) {
-        return getXpHelper(skill, StringUtils.getExplicitConfigMaterialString(material),
-                StringUtils.getFriendlyConfigMaterialString(material),
-                StringUtils.getWildcardConfigMaterialString(material));
+        return blockExperienceMap.get(skill).getOrDefault(material, 0);
     }
 
     public int getXp(PrimarySkillType skill, BlockState blockState) {
-        Material material = blockState.getType();
-        return getXp(skill, material);
+        return getXp(skill, blockState.getType());
     }
 
     public int getXp(PrimarySkillType skill, Block block) {
@@ -338,57 +367,17 @@ public class ExperienceConfig extends BukkitConfig {
     }
 
     public int getXp(PrimarySkillType skill, BlockData data) {
-        return getXpHelper(skill, StringUtils.getExplicitConfigBlockDataString(data),
-                StringUtils.getFriendlyConfigBlockDataString(data),
-                StringUtils.getWildcardConfigBlockDataString(data));
+        return getXp(skill, data.getMaterial());
     }
 
-    private int getXpHelper(PrimarySkillType skill, String explicitString, String friendlyString, String wildcardString) {
-        if (explicitString.equalsIgnoreCase("LILY_PAD")) {
-            return 0;
-        }
-
-        String baseString = "Experience_Values." + StringUtils.getCapitalized(skill.toString()) + ".";
-        String[] configStrings = {explicitString, friendlyString, wildcardString};
-
-        for (String configString : configStrings) {
-            String fullPath = baseString + configString;
-            if (config.contains(fullPath)) {
-                return config.getInt(fullPath);
-            }
-        }
-
-        return 0;
-    }
-
-
     public boolean doesBlockGiveSkillXP(PrimarySkillType skill, Material material) {
-        return doesBlockGiveSkillXPHelper(skill, StringUtils.getExplicitConfigMaterialString(material),
-                StringUtils.getFriendlyConfigMaterialString(material),
-                StringUtils.getWildcardConfigMaterialString(material));
+        return getXp(skill, material) > 0;
     }
 
     public boolean doesBlockGiveSkillXP(PrimarySkillType skill, BlockData data) {
-        return doesBlockGiveSkillXPHelper(skill, StringUtils.getExplicitConfigBlockDataString(data),
-                StringUtils.getFriendlyConfigBlockDataString(data),
-                StringUtils.getWildcardConfigBlockDataString(data));
+        return getXp(skill, data) > 0;
     }
 
-    private boolean doesBlockGiveSkillXPHelper(PrimarySkillType skill, String explicitString, String friendlyString, String wildcardString) {
-        String baseString = "Experience_Values." + StringUtils.getCapitalized(skill.toString()) + ".";
-        String[] configStrings = {explicitString, friendlyString, wildcardString};
-
-        for (String configString : configStrings) {
-            String fullPath = baseString + configString;
-            if (config.contains(fullPath)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-
     /*
      * Experience Bar Stuff
      */

+ 1 - 1
src/main/java/com/gmail/nossr50/config/party/ItemWeightConfig.java

@@ -23,7 +23,7 @@ public class ItemWeightConfig extends BukkitConfig {
     }
 
     public int getItemWeight(Material material) {
-        return config.getInt("Item_Weights." + StringUtils.getPrettyItemString(material).replace(" ", "_"), config.getInt("Item_Weights.Default"));
+        return config.getInt("Item_Weights." + StringUtils.getFormattedMaterialString(material).replace(" ", "_"), config.getInt("Item_Weights.Default"));
     }
 
     public HashSet<Material> getMiscItems() {

+ 0 - 1
src/main/java/com/gmail/nossr50/config/skills/alchemy/PotionConfig.java

@@ -21,7 +21,6 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.stream.Collectors;
 
 import static com.gmail.nossr50.util.ItemUtils.setItemName;
 import static com.gmail.nossr50.util.PotionUtil.*;

+ 0 - 1
src/main/java/com/gmail/nossr50/config/skills/salvage/SalvageConfig.java

@@ -9,7 +9,6 @@ import com.gmail.nossr50.skills.salvage.salvageables.Salvageable;
 import com.gmail.nossr50.skills.salvage.salvageables.SalvageableFactory;
 import com.gmail.nossr50.util.ItemUtils;
 import com.gmail.nossr50.util.LogUtils;
-import com.gmail.nossr50.util.skills.SkillUtils;
 import org.bukkit.Material;
 import org.bukkit.configuration.ConfigurationSection;
 import org.bukkit.inventory.ItemStack;

+ 14 - 14
src/main/java/com/gmail/nossr50/config/treasure/TreasureConfig.java

@@ -239,29 +239,29 @@ public class TreasureConfig extends BukkitConfig {
 
                     for (String dropper : dropList) {
                         if (dropper.equals("Bushes")) {
-                            AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.FERN), hylianTreasure);
-                            AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(BlockUtils.getShortGrass()), hylianTreasure);
+                            AddHylianTreasure(StringUtils.getFormattedMaterialString(Material.FERN), hylianTreasure);
+                            AddHylianTreasure(StringUtils.getFormattedMaterialString(BlockUtils.getShortGrass()), hylianTreasure);
                             for (Material species : Tag.SAPLINGS.getValues())
-                                AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(species), hylianTreasure);
+                                AddHylianTreasure(StringUtils.getFormattedMaterialString(species), hylianTreasure);
 
-                            AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.DEAD_BUSH), hylianTreasure);
+                            AddHylianTreasure(StringUtils.getFormattedMaterialString(Material.DEAD_BUSH), hylianTreasure);
                             continue;
                         }
                         if (dropper.equals("Flowers")) {
-                            AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.POPPY), hylianTreasure);
-                            AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.DANDELION), hylianTreasure);
-                            AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.BLUE_ORCHID), hylianTreasure);
-                            AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.ALLIUM), hylianTreasure);
-                            AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.AZURE_BLUET), hylianTreasure);
-                            AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.ORANGE_TULIP), hylianTreasure);
-                            AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.PINK_TULIP), hylianTreasure);
-                            AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.RED_TULIP), hylianTreasure);
-                            AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.WHITE_TULIP), hylianTreasure);
+                            AddHylianTreasure(StringUtils.getFormattedMaterialString(Material.POPPY), hylianTreasure);
+                            AddHylianTreasure(StringUtils.getFormattedMaterialString(Material.DANDELION), hylianTreasure);
+                            AddHylianTreasure(StringUtils.getFormattedMaterialString(Material.BLUE_ORCHID), hylianTreasure);
+                            AddHylianTreasure(StringUtils.getFormattedMaterialString(Material.ALLIUM), hylianTreasure);
+                            AddHylianTreasure(StringUtils.getFormattedMaterialString(Material.AZURE_BLUET), hylianTreasure);
+                            AddHylianTreasure(StringUtils.getFormattedMaterialString(Material.ORANGE_TULIP), hylianTreasure);
+                            AddHylianTreasure(StringUtils.getFormattedMaterialString(Material.PINK_TULIP), hylianTreasure);
+                            AddHylianTreasure(StringUtils.getFormattedMaterialString(Material.RED_TULIP), hylianTreasure);
+                            AddHylianTreasure(StringUtils.getFormattedMaterialString(Material.WHITE_TULIP), hylianTreasure);
                             continue;
                         }
                         if (dropper.equals("Pots")) {
                             for (Material species : Tag.FLOWER_POTS.getValues())
-                                AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(species), hylianTreasure);
+                                AddHylianTreasure(StringUtils.getFormattedMaterialString(species), hylianTreasure);
                             continue;
                         }
                         AddHylianTreasure(dropper, hylianTreasure);

+ 0 - 1
src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java

@@ -2,7 +2,6 @@ package com.gmail.nossr50.datatypes.player;
 
 import com.gmail.nossr50.api.exceptions.InvalidSkillException;
 import com.gmail.nossr50.chat.author.PlayerAuthor;
-import com.gmail.nossr50.config.AdvancedConfig;
 import com.gmail.nossr50.config.ChatConfig;
 import com.gmail.nossr50.config.WorldBlacklist;
 import com.gmail.nossr50.config.experience.ExperienceConfig;

+ 1 - 1
src/main/java/com/gmail/nossr50/datatypes/skills/SuperAbilityType.java

@@ -167,7 +167,7 @@ public enum SuperAbilityType {
     }
 
     public String getName() {
-        return StringUtils.getPrettyAbilityString(this);
+        return StringUtils.getPrettySuperAbilityString(this);
     }
 
     public String getLocalizedName() {

+ 0 - 3
src/main/java/com/gmail/nossr50/listeners/EntityListener.java

@@ -27,7 +27,6 @@ import org.bukkit.ChatColor;
 import org.bukkit.Material;
 import org.bukkit.NamespacedKey;
 import org.bukkit.OfflinePlayer;
-import org.bukkit.attribute.Attribute;
 import org.bukkit.block.Block;
 import org.bukkit.enchantments.Enchantment;
 import org.bukkit.entity.*;
@@ -45,8 +44,6 @@ import org.bukkit.potion.PotionEffect;
 import org.bukkit.potion.PotionEffectType;
 import org.bukkit.projectiles.ProjectileSource;
 
-import java.util.Arrays;
-import java.util.List;
 import java.util.Set;
 
 import static com.gmail.nossr50.util.AttributeMapper.MAPPED_MAX_HEALTH;

+ 1 - 1
src/main/java/com/gmail/nossr50/skills/alchemy/AlchemyManager.java

@@ -34,7 +34,7 @@ public class AlchemyManager extends SkillManager {
         StringBuilder list = new StringBuilder();
 
         for (ItemStack ingredient : getIngredients()) {
-            String string = StringUtils.getPrettyItemString(ingredient.getType());
+            String string = StringUtils.getFormattedMaterialString(ingredient.getType());
 
             list.append(", ").append(string);
         }

+ 0 - 1
src/main/java/com/gmail/nossr50/skills/axes/AxesManager.java

@@ -15,7 +15,6 @@ import com.gmail.nossr50.util.random.ProbabilityUtil;
 import com.gmail.nossr50.util.skills.CombatUtils;
 import com.gmail.nossr50.util.skills.ParticleEffectUtils;
 import com.gmail.nossr50.util.skills.RankUtils;
-import com.gmail.nossr50.util.skills.SkillUtils;
 import org.bukkit.entity.LivingEntity;
 import org.bukkit.entity.Player;
 import org.bukkit.inventory.EntityEquipment;

+ 1 - 1
src/main/java/com/gmail/nossr50/skills/excavation/Excavation.java

@@ -19,7 +19,7 @@ public class Excavation {
      * @return the list of treasures that could be found
      */
     protected static List<ExcavationTreasure> getTreasures(BlockState blockState) {
-        String friendly = StringUtils.getFriendlyConfigBlockDataString(blockState.getBlockData());
+        String friendly = StringUtils.getFormattedMaterialString(blockState.getBlockData().getMaterial());
         if (TreasureConfig.getInstance().excavationMap.containsKey(friendly))
             return TreasureConfig.getInstance().excavationMap.get(friendly);
         return new ArrayList<>();

+ 5 - 5
src/main/java/com/gmail/nossr50/skills/herbalism/HerbalismManager.java

@@ -113,7 +113,8 @@ public class HerbalismManager extends SkillManager {
                     mmoPlayer.getPlayer().sendMessage("Bush Reward Multiplier: " + rewardByAge);
                 }
 
-                int xpReward = ExperienceConfig.getInstance().getXp(PrimarySkillType.HERBALISM, blockState) * rewardByAge;
+                int xpReward = ExperienceConfig.getInstance().getXp(PrimarySkillType.HERBALISM, blockState.getType())
+                        * rewardByAge;
 
                 if (mmoPlayer.isDebugMode()) {
                     mmoPlayer.getPlayer().sendMessage("Bush XP: " + xpReward);
@@ -713,12 +714,11 @@ public class HerbalismManager extends SkillManager {
             return false;
         }
 
-        String friendly = StringUtils.getFriendlyConfigBlockDataString(blockState.getBlockData());
+        final String friendly = StringUtils.getFormattedMaterialString(blockState.getBlockData().getMaterial());
         if (!TreasureConfig.getInstance().hylianMap.containsKey(friendly))
             return false;
         List<HylianTreasure> treasures = TreasureConfig.getInstance().hylianMap.get(friendly);
 
-
         if (treasures.isEmpty()) {
             return false;
         }
@@ -750,12 +750,12 @@ public class HerbalismManager extends SkillManager {
         PlayerInventory playerInventory = getPlayer().getInventory();
         
         if (!playerInventory.contains(Material.BROWN_MUSHROOM, 1)) {
-            NotificationManager.sendPlayerInformation(getPlayer(), NotificationType.REQUIREMENTS_NOT_MET, "Skills.NeedMore", StringUtils.getPrettyItemString(Material.BROWN_MUSHROOM));
+            NotificationManager.sendPlayerInformation(getPlayer(), NotificationType.REQUIREMENTS_NOT_MET, "Skills.NeedMore", StringUtils.getFormattedMaterialString(Material.BROWN_MUSHROOM));
             return false;
         }
 
         if (!playerInventory.contains(Material.RED_MUSHROOM, 1)) {
-            NotificationManager.sendPlayerInformation(getPlayer(), NotificationType.REQUIREMENTS_NOT_MET, "Skills.NeedMore", StringUtils.getPrettyItemString(Material.RED_MUSHROOM));
+            NotificationManager.sendPlayerInformation(getPlayer(), NotificationType.REQUIREMENTS_NOT_MET, "Skills.NeedMore", StringUtils.getFormattedMaterialString(Material.RED_MUSHROOM));
             return false;
         }
 

+ 3 - 3
src/main/java/com/gmail/nossr50/skills/repair/RepairManager.java

@@ -96,7 +96,7 @@ public class RepairManager extends SkillManager {
 
         // Level check
         if (skillLevel < minimumRepairableLevel) {
-            NotificationManager.sendPlayerInformation(player, NotificationType.SUBSKILL_MESSAGE_FAILED, "Repair.Skills.Adept", String.valueOf(minimumRepairableLevel), StringUtils.getPrettyItemString(item.getType()));
+            NotificationManager.sendPlayerInformation(player, NotificationType.SUBSKILL_MESSAGE_FAILED, "Repair.Skills.Adept", String.valueOf(minimumRepairableLevel), StringUtils.getFormattedMaterialString(item.getType()));
             return;
         }
 
@@ -115,7 +115,7 @@ public class RepairManager extends SkillManager {
 
         // Check if they have the proper material to repair with
         if (!inventory.contains(repairMaterial)) {
-            String prettyName = repairable.getRepairMaterialPrettyName() == null ? StringUtils.getPrettyItemString(repairMaterial) : repairable.getRepairMaterialPrettyName();
+            String prettyName = repairable.getRepairMaterialPrettyName() == null ? StringUtils.getFormattedMaterialString(repairMaterial) : repairable.getRepairMaterialPrettyName();
 
             String materialsNeeded = "";
 
@@ -156,7 +156,7 @@ public class RepairManager extends SkillManager {
 
                 // Fail out with "you need material" if we don't find a suitable alternative.
                 if (possibleMaterial.isEmpty()) {
-                    String prettyName = repairable.getRepairMaterialPrettyName() == null ? StringUtils.getPrettyItemString(repairMaterial) : repairable.getRepairMaterialPrettyName();
+                    String prettyName = repairable.getRepairMaterialPrettyName() == null ? StringUtils.getFormattedMaterialString(repairMaterial) : repairable.getRepairMaterialPrettyName();
 
                     String materialsNeeded = "";
 

+ 4 - 4
src/main/java/com/gmail/nossr50/skills/salvage/SalvageManager.java

@@ -96,7 +96,7 @@ public class SalvageManager extends SkillManager {
         if (getSkillLevel() < minimumSalvageableLevel) {
             NotificationManager.sendPlayerInformation(player, NotificationType.REQUIREMENTS_NOT_MET,
                     "Salvage.Skills.Adept.Level",
-                    String.valueOf(minimumSalvageableLevel), StringUtils.getPrettyItemString(item.getType()));
+                    String.valueOf(minimumSalvageableLevel), StringUtils.getFormattedMaterialString(item.getType()));
             return;
         }
 
@@ -143,11 +143,11 @@ public class SalvageManager extends SkillManager {
 
         // We only send a confirmation message after processing the event (fixes #4694)
         if (lotteryResults == potentialSalvageYield && potentialSalvageYield != 1 && RankUtils.isPlayerMaxRankInSubSkill(player, SubSkillType.SALVAGE_ARCANE_SALVAGE)) {
-            NotificationManager.sendPlayerInformationChatOnly(player, "Salvage.Skills.Lottery.Perfect", String.valueOf(lotteryResults), StringUtils.getPrettyItemString(item.getType()));
+            NotificationManager.sendPlayerInformationChatOnly(player, "Salvage.Skills.Lottery.Perfect", String.valueOf(lotteryResults), StringUtils.getFormattedMaterialString(item.getType()));
         } else if (salvageable.getMaximumQuantity() == 1 || getSalvageLimit() >= salvageable.getMaximumQuantity()) {
-            NotificationManager.sendPlayerInformationChatOnly(player,  "Salvage.Skills.Lottery.Normal", String.valueOf(lotteryResults), StringUtils.getPrettyItemString(item.getType()));
+            NotificationManager.sendPlayerInformationChatOnly(player,  "Salvage.Skills.Lottery.Normal", String.valueOf(lotteryResults), StringUtils.getFormattedMaterialString(item.getType()));
         } else {
-            NotificationManager.sendPlayerInformationChatOnly(player,  "Salvage.Skills.Lottery.Untrained", String.valueOf(lotteryResults), StringUtils.getPrettyItemString(item.getType()));
+            NotificationManager.sendPlayerInformationChatOnly(player,  "Salvage.Skills.Lottery.Untrained", String.valueOf(lotteryResults), StringUtils.getFormattedMaterialString(item.getType()));
         }
 
         player.getInventory().setItemInMainHand(new ItemStack(Material.AIR));

+ 1 - 2
src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java

@@ -23,7 +23,6 @@ import com.gmail.nossr50.util.sounds.SoundType;
 import com.gmail.nossr50.util.text.StringUtils;
 import org.bukkit.Location;
 import org.bukkit.Material;
-import org.bukkit.attribute.Attribute;
 import org.bukkit.attribute.AttributeInstance;
 import org.bukkit.entity.*;
 import org.bukkit.inventory.ItemStack;
@@ -371,7 +370,7 @@ public class TamingManager extends SkillManager {
             } else {
                 //Player did not have enough of the item in their main hand
                 int difference = tamingSummon.getItemAmountRequired() - itemInMainHand.getAmount();
-                NotificationManager.sendPlayerInformationChatOnly(player, "Taming.Summon.COTW.NeedMoreItems", String.valueOf(difference), StringUtils.getPrettyItemString(itemInMainHand.getType()));
+                NotificationManager.sendPlayerInformationChatOnly(player, "Taming.Summon.COTW.NeedMoreItems", String.valueOf(difference), StringUtils.getFormattedMaterialString(itemInMainHand.getType()));
             }
         }
     }

+ 0 - 1
src/main/java/com/gmail/nossr50/util/AttributeMapper.java

@@ -1,7 +1,6 @@
 package com.gmail.nossr50.util;
 
 import com.gmail.nossr50.mcMMO;
-import org.bukkit.Registry;
 import org.bukkit.attribute.Attribute;
 
 import java.lang.reflect.Field;

+ 0 - 2
src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java

@@ -21,10 +21,8 @@ import com.gmail.nossr50.skills.unarmed.UnarmedManager;
 import com.gmail.nossr50.util.*;
 import com.gmail.nossr50.util.player.NotificationManager;
 import com.gmail.nossr50.util.player.UserManager;
-import org.bukkit.Bukkit;
 import org.bukkit.GameMode;
 import org.bukkit.Material;
-import org.bukkit.attribute.Attribute;
 import org.bukkit.attribute.AttributeInstance;
 import org.bukkit.entity.*;
 import org.bukkit.event.entity.EntityDamageByEntityEvent;

+ 0 - 20
src/main/java/com/gmail/nossr50/util/skills/SkillTools.java

@@ -387,26 +387,6 @@ public class SkillTools {
         return pluginRef.getGeneralConfig().getMaxLength(superAbilityType);
     }
 
-    public String getSuperAbilityOnLocaleKey(SuperAbilityType superAbilityType) {
-        return "SuperAbility." + StringUtils.getPrettyCamelCaseName(superAbilityType) + ".On";
-    }
-
-    public String getSuperAbilityOffLocaleKey(SuperAbilityType superAbilityType) {
-        return "SuperAbility." + StringUtils.getPrettyCamelCaseName(superAbilityType) + ".Off";
-    }
-
-    public String getSuperAbilityOtherPlayerActivationLocaleKey(SuperAbilityType superAbilityType) {
-        return "SuperAbility." + StringUtils.getPrettyCamelCaseName(superAbilityType) + ".Other.On";
-    }
-
-    public String getSuperAbilityOtherPlayerDeactivationLocaleKey(SuperAbilityType superAbilityType) {
-        return "SuperAbility." + StringUtils.getPrettyCamelCaseName(superAbilityType) + "Other.Off";
-    }
-
-    public String getSuperAbilityRefreshedLocaleKey(SuperAbilityType superAbilityType) {
-        return "SuperAbility." + StringUtils.getPrettyCamelCaseName(superAbilityType) + ".Refresh";
-    }
-
     public int getLevelCap(@NotNull PrimarySkillType primarySkillType) {
         return mcMMO.p.getGeneralConfig().getLevelCap(primarySkillType);
     }

+ 16 - 9
src/main/java/com/gmail/nossr50/util/sounds/SoundManager.java

@@ -9,14 +9,7 @@ import org.bukkit.World;
 import org.bukkit.entity.Player;
 
 public class SoundManager {
-    public static Sound CRIPPLE_SOUND;
-    static {
-        try {
-            CRIPPLE_SOUND = Sound.valueOf("ITEM_MACE_SMASH_GROUND");
-        } catch (IllegalArgumentException e) {
-            CRIPPLE_SOUND = Sound.BLOCK_ANVIL_PLACE;
-        }
-    }
+    private static Sound CRIPPLE_SOUND;
 
     /**
      * Sends a sound to the player
@@ -107,10 +100,24 @@ public class SoundManager {
             case DEFLECT_ARROWS, BLEED -> Sound.ENTITY_ENDER_EYE_DEATH;
             case GLASS -> Sound.BLOCK_GLASS_BREAK;
             case ITEM_CONSUMED -> Sound.ITEM_BOTTLE_EMPTY;
-            case CRIPPLE -> CRIPPLE_SOUND;
+            case CRIPPLE -> getCrippleSound();
         };
     }
 
+    private static Sound getCrippleSound() {
+        if (CRIPPLE_SOUND != null) {
+            return CRIPPLE_SOUND;
+        }
+
+        try {
+            CRIPPLE_SOUND = Sound.valueOf("ITEM_MACE_SMASH_GROUND");
+            return CRIPPLE_SOUND;
+        } catch (IllegalArgumentException e) {
+            CRIPPLE_SOUND = Sound.BLOCK_ANVIL_PLACE;
+            return CRIPPLE_SOUND;
+        }
+    }
+
     public static float getFizzPitch() {
         return 2.6F + (Misc.getRandom().nextFloat() - Misc.getRandom().nextFloat()) * 0.8F;
     }

+ 136 - 111
src/main/java/com/gmail/nossr50/util/text/StringUtils.java

@@ -3,192 +3,217 @@ package com.gmail.nossr50.util.text;
 import com.gmail.nossr50.datatypes.party.PartyFeature;
 import com.gmail.nossr50.datatypes.skills.SuperAbilityType;
 import org.bukkit.Material;
-import org.bukkit.block.data.Ageable;
 import org.bukkit.block.data.BlockData;
 import org.bukkit.entity.EntityType;
 import org.jetbrains.annotations.NotNull;
 
 import java.text.DecimalFormat;
+import java.util.HashMap;
 import java.util.Locale;
+import java.util.Map;
+import java.util.function.Function;
 
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Utility class for String operations, including formatting and caching deterministic results to improve performance.
+ */
 public class StringUtils {
 
-    protected static DecimalFormat percent = new DecimalFormat("##0.00%");
-    protected static DecimalFormat shortDecimal = new DecimalFormat("##0.0");
+    protected static final DecimalFormat percent = new DecimalFormat("##0.00%");
+    protected static final DecimalFormat shortDecimal = new DecimalFormat("##0.0");
+
+    // Using concurrent hash maps to avoid concurrency issues (Folia)
+    private static final Map<EntityType, String> formattedEntityStrings = new HashMap<>();
+    private static final Map<SuperAbilityType, String> formattedSuperAbilityStrings = new HashMap<>();
+    private static final Map<Material, String> formattedMaterialStrings = new HashMap<>();
+    private static final Map<PartyFeature, String> prettyPartyFeatureStringCache = new HashMap<>();
 
     /**
      * Gets a capitalized version of the target string.
+     * Results are cached to improve performance.
      *
-     * @param target
-     *            String to capitalize
+     * @param target String to capitalize
      * @return the capitalized string
      */
     public static String getCapitalized(String target) {
-        return target.substring(0, 1).toUpperCase() + target.substring(1).toLowerCase(Locale.ENGLISH);
+        if (target == null || target.isEmpty()) {
+            return target;
+        }
+        return target.substring(0, 1).toUpperCase(Locale.ENGLISH) + target.substring(1).toLowerCase(Locale.ENGLISH);
     }
 
+    /**
+     * Converts ticks to seconds, formatted to one decimal place.
+     *
+     * @param ticks Number of ticks
+     * @return String representation of seconds
+     */
     public static String ticksToSeconds(double ticks) {
         return shortDecimal.format(ticks / 20);
     }
 
-    public static String convertToCamelCaseString(String baseString, String splitBy) {
-        String[] substrings = baseString.split(splitBy);
-        String prettyString = "";
-        int size = 1;
-
-        for (String string : substrings) {
-            prettyString = prettyString.concat(getCapitalized(string));
-
-            if (size < substrings.length) {
-                prettyString = prettyString.concat("");
-            }
-
-            size++;
-        }
-
-        return prettyString;
-    }
-
-    public static String getPrettyCamelCaseName(Object o) {
-        return StringUtils.convertToCamelCaseString(o.toString(), "_");
-    }
-
-    public static String getPrettySuperAbilityName(SuperAbilityType superAbilityType) {
-        return StringUtils.getPrettySuperAbilityString(superAbilityType);
-    }
-
-    public static String getPrettySuperAbilityString(SuperAbilityType ability) {
-        return createPrettyString(ability.toString());
+    /**
+     * Gets a pretty string representation of a SuperAbilityType.
+     * Results are cached to improve performance.
+     *
+     * @param superAbilityType SuperAbilityType to convert
+     * @return Pretty string representation of the SuperAbilityType
+     */
+    public static String getPrettySuperAbilityString(SuperAbilityType superAbilityType) {
+        requireNonNull(superAbilityType, "superAbilityType cannot be null");
+        return formattedSuperAbilityStrings.computeIfAbsent(superAbilityType, StringUtils::createPrettyString);
     }
 
     /**
-     * Creates a string from an array skipping the first n elements
-     * @param args the array to iterate over when forming the string
-     * @param index the amount of elements to skip over
-     * @return the "trimmed" string
+     * Creates a string from an array skipping the first n elements.
+     *
+     * @param args  The array to iterate over when forming the string
+     * @param index The number of elements to skip over
+     * @return The "trimmed" string
      */
-    public static String buildStringAfterNthElement(@NotNull String @NotNull []args, int index) {
+    public static String buildStringAfterNthElement(@NotNull String @NotNull [] args, int index) {
         StringBuilder trimMessage = new StringBuilder();
 
         for (int i = index; i < args.length; i++) {
-            if (i + 1 >= args.length)
-                trimMessage.append(args[i]);
-            else
-                trimMessage.append(args[i]).append(" ");
+            if (i > index) {
+                trimMessage.append(' ');
+            }
+            trimMessage.append(args[i]);
         }
 
         return trimMessage.toString();
     }
 
-    public static String getPrettyItemString(Material material) {
-        return createPrettyString(material.toString());
-    }
-
-    public static String getPrettyEntityTypeString(EntityType entity) {
-        return createPrettyString(entity.toString());
-    }
-
-    public static String getPrettyAbilityString(SuperAbilityType ability) {
-        return createPrettyString(ability.toString());
-    }
-    
-    public static String getWildcardConfigBlockDataString(BlockData data) {
-        return getWildcardConfigMaterialString(data.getMaterial());
-    }
-
-    public static String getWildcardConfigMaterialString(Material data) {
-        return StringUtils.getPrettyItemString(data).replace(" ", "_") + "|*";
+    /**
+     * Gets a pretty string representation of a Material.
+     * Results are cached to improve performance.
+     *
+     * @param material Material to convert
+     * @return Pretty string representation of the Material
+     */
+    public static String getFormattedMaterialString(Material material) {
+        return formattedMaterialStrings.computeIfAbsent(material, StringUtils::createPrettyString);
     }
 
-    public static String getFriendlyConfigBlockDataString(BlockData data) {
-        switch(data.getMaterial()){
-            case CHORUS_FLOWER:
-            case COCOA:
-            case WHEAT:
-            case BEETROOTS:
-            case CARROTS:
-            case POTATOES:
-            case NETHER_WART: {
-                if (data instanceof Ageable ageData) {
-                    if (ageData.getAge() == ageData.getMaximumAge()) {
-                        return getPrettyItemString(data.getMaterial()).replace(" ", "_") + "_Ripe";
-                    }
-                }
-                return getPrettyItemString(data.getMaterial()).replace(" ", "_") + "_Ungrown";
-            }
-        }
-        return getPrettyItemString(data.getMaterial()).replace(" ", "_");
+    /**
+     * Gets a pretty string representation of an EntityType.
+     * Results are cached to improve performance.
+     *
+     * @param entityType EntityType to convert
+     * @return Pretty string representation of the EntityType
+     */
+    public static String getPrettyEntityTypeString(EntityType entityType) {
+        return formattedEntityStrings.computeIfAbsent(entityType, StringUtils::createPrettyString);
     }
 
-    public static String getFriendlyConfigMaterialString(Material data) {
-        return getPrettyItemString(data).replace(" ", "_");
+    /**
+     * Gets a wildcard configuration string for BlockData.
+     * Results are cached to improve performance.
+     *
+     * @param blockData BlockData to convert
+     * @return Wildcard configuration string
+     */
+    public static String getWildcardConfigBlockDataString(BlockData blockData) {
+        return getFormattedMaterialString(blockData.getMaterial());
     }
 
+    /**
+     * Gets an explicit configuration string for BlockData.
+     * Results are cached to improve performance.
+     *
+     * @param data BlockData to convert
+     * @return Explicit configuration string
+     */
     public static String getExplicitConfigBlockDataString(BlockData data) {
-        return getExplicitConfigMaterialString(data.getMaterial());
-    }
-
-    public static String getExplicitConfigMaterialString(Material data) {
-        return StringUtils.getPrettyItemString(data).replace(" ", "_");
+        return getFormattedMaterialString(data.getMaterial());
     }
 
+    /**
+     * Gets a pretty string representation of a PartyFeature.
+     * Results are cached to improve performance.
+     *
+     * @param partyFeature PartyFeature to convert
+     * @return Pretty string representation
+     */
     public static String getPrettyPartyFeatureString(PartyFeature partyFeature) {
-        return createPrettyString(partyFeature.toString());
+        return prettyPartyFeatureStringCache.computeIfAbsent(partyFeature, StringUtils::createPrettyString);
     }
 
+    /**
+     * Creates a pretty string from a base string by splitting underscores and capitalizing words.
+     *
+     * @param baseString String to convert
+     * @return Pretty string
+     */
     private static String createPrettyString(String baseString) {
-        String[] substrings = baseString.split("_");
-        String prettyString = "";
-        int size = 1;
-
-        for (String string : substrings) {
-            prettyString = prettyString.concat(getCapitalized(string));
+        return PRETTY_STRING_FUNC.apply(baseString);
+    }
 
-            if (size < substrings.length) {
-                prettyString = prettyString.concat(" ");
+    /**
+     * Function to create a pretty string from a base string.
+     */
+    private static final Function<String, String> PRETTY_STRING_FUNC = baseString -> {
+        if (baseString.contains("_") && !baseString.contains(" ")) {
+            return prettify(baseString.split("_"));
+        } else {
+            if(baseString.contains(" ")) {
+                return prettify(baseString.split(" "));
+            } else{
+                return getCapitalized(baseString);
             }
+        }
+    };
 
-            size++;
+    private static @NotNull String prettify(String[] substrings) {
+        final StringBuilder prettyString = new StringBuilder();
+
+        for (int i = 0; i < substrings.length; i++) {
+            prettyString.append(getCapitalized(substrings[i]));
+            if (i < substrings.length - 1) {
+                prettyString.append(' ');
+            }
         }
 
-        return prettyString;
+        return prettyString.toString();
+    }
+
+    /**
+     * Creates a pretty string from an object.
+     * @param object Object to convert
+     * @return Pretty string representation of the object
+     */
+    private static String createPrettyString(Object object) {
+        return createPrettyString(object.toString());
     }
 
     /**
-     * Determine if a string represents an Integer
+     * Determine if a string represents an Integer.
      *
-     * @param string
-     *            String to check
+     * @param string String to check
      * @return true if the string is an Integer, false otherwise
      */
     public static boolean isInt(String string) {
         try {
             Integer.parseInt(string);
             return true;
-        } catch (NumberFormatException nFE) {
+        } catch (NumberFormatException ignored) {
             return false;
         }
     }
 
     /**
-     * Determine if a string represents a Double
+     * Determine if a string represents a Double.
      *
-     * @param string
-     *            String to check
+     * @param string String to check
      * @return true if the string is a Double, false otherwise
      */
     public static boolean isDouble(String string) {
         try {
             Double.parseDouble(string);
             return true;
-        } catch (NumberFormatException nFE) {
+        } catch (NumberFormatException ignored) {
             return false;
         }
     }
-
-    public static String convertKeyToName(@NotNull String key) {
-        // used when no display name is given for a potion
-        final String noUnderscores = key.replace("_", " ").toLowerCase(Locale.ENGLISH);
-        return org.codehaus.plexus.util.StringUtils.capitalise(noUnderscores);
-    }
 }

+ 0 - 1
src/test/java/com/gmail/nossr50/MMOTestEnvironment.java

@@ -21,7 +21,6 @@ import com.gmail.nossr50.util.skills.RankUtils;
 import com.gmail.nossr50.util.skills.SkillTools;
 import com.gmail.nossr50.util.sounds.SoundManager;
 import org.bukkit.*;
-import org.bukkit.attribute.Attribute;
 import org.bukkit.block.Block;
 import org.bukkit.entity.Player;
 import org.bukkit.event.Event;