fix issues with Alchemy and older MC versions

This commit is contained in:
nossr50 2024-05-03 13:53:53 -07:00
parent 5b822bc626
commit 75561350c1
14 changed files with 165 additions and 128 deletions

View File

@ -1,8 +1,8 @@
Version 2.2.007
Compatibility with the 1.20.5 / 1.20.6 MC Update
Added armadillo to combat experience in experience.yml
Fixed bug where Alchemy was not brewing certain potions (haste, etc)
Alchemy now has more warning/errors that will print out to help debug
Fixed bug where the probability of success of Graceful Roll was not being calculated correctly
Added armadillo to combat experience in experience.yml
Fixed bug where Green Thumb did not replant if seed was in the off hand
NOTES:

13
pom.xml
View File

@ -222,6 +222,11 @@
</pluginRepositories>
<repositories>
<!-- Protocol Lib Repository -->
<repository>
<id>dmulloy2-repo</id>
<url>https://repo.dmulloy2.net/repository/public/</url>
</repository>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
@ -268,6 +273,12 @@
<version>3.25.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>LATEST</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
@ -364,7 +375,7 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.20.5-R0.1-SNAPSHOT</version>
<version>1.19.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>

View File

@ -15,15 +15,14 @@ import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.potion.PotionType;
import org.codehaus.plexus.util.StringUtils;
import org.jetbrains.annotations.VisibleForTesting;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import static com.gmail.nossr50.util.PotionUtil.*;
import static com.gmail.nossr50.util.text.StringUtils.convertKeyToName;
public class PotionConfig extends LegacyConfigLoader {
@ -130,7 +129,7 @@ public class PotionConfig extends LegacyConfigLoader {
final String key = potion_section.getName();
final String displayName = potion_section.getString("Name") != null
? LocaleLoader.addColors(potion_section.getString("Name"))
: null;
: convertKeyToName(key);
final ConfigurationSection potionData = potion_section.getConfigurationSection("PotionData");
boolean extended = false;
@ -165,12 +164,16 @@ public class PotionConfig extends LegacyConfigLoader {
return null;
}
// Set the name of the potion
potionMeta.setDisplayName(displayName);
// extended and upgraded seem to be mutually exclusive
if (extended && upgraded) {
mcMMO.p.getLogger().warning("Potion " + key + " has both Extended and Upgraded set to true," +
" defaulting to Extended.");
upgraded = false;
}
String potionTypeStr = potionData.getString("PotionType", null);
if (potionTypeStr == null) {
mcMMO.p.getLogger().severe("PotionConfig: Missing PotionType for " + displayName + ", from configuration section:" +
@ -186,7 +189,9 @@ public class PotionConfig extends LegacyConfigLoader {
}
if (potionType == null) {
mcMMO.p.getLogger().severe("PotionConfig: Failed to parse potion type for: " + potionTypeStr);
mcMMO.p.getLogger().severe("PotionConfig: Failed to parse potion type for: " + potionTypeStr
+ ", upgraded: " + upgraded + ", extended: " + extended + " for potion " + displayName
+ ", from configuration section: " + potion_section);
return null;
}
@ -357,5 +362,4 @@ public class PotionConfig extends LegacyConfigLoader {
}
return Color.fromRGB(red / colors.size(), green / colors.size(), blue / colors.size());
}
}

View File

@ -7,7 +7,6 @@ import org.bukkit.inventory.meta.PotionMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
@ -16,18 +15,18 @@ import static com.gmail.nossr50.util.PotionUtil.samePotionType;
import static java.util.Objects.requireNonNull;
public class AlchemyPotion {
private final ItemStack potion;
private final Map<ItemStack, String> alchemyPotionChildren;
private final @NotNull ItemStack potionItemstack;
private final @NotNull Map<ItemStack, String> alchemyPotionChildren;
public AlchemyPotion(ItemStack potion, Map<ItemStack, String> alchemyPotionChildren) {
this.potion = requireNonNull(potion, "potion cannot be null");
public AlchemyPotion(@NotNull ItemStack potionItemStack, @NotNull Map<ItemStack, String> alchemyPotionChildren) {
this.potionItemstack = requireNonNull(potionItemStack, "potionItemStack cannot be null");
this.alchemyPotionChildren = requireNonNull(alchemyPotionChildren, "alchemyPotionChildren cannot be null");
}
public @NotNull ItemStack toItemStack(int amount) {
final ItemStack potion = new ItemStack(this.potion);
potion.setAmount(Math.max(1, amount));
return potion;
final ItemStack clone = potionItemstack.clone();
clone.setAmount(Math.max(1, amount));
return clone;
}
public Map<ItemStack, String> getAlchemyPotionChildren() {
@ -49,7 +48,7 @@ public class AlchemyPotion {
requireNonNull(otherPotion, "otherPotion cannot be null");
// TODO: Investigate?
// We currently don't compare base potion effects, likely because they are derived from the potion type
if (otherPotion.getType() != potion.getType() || !otherPotion.hasItemMeta()) {
if (otherPotion.getType() != potionItemstack.getType() || !otherPotion.hasItemMeta()) {
return false;
}
@ -76,25 +75,19 @@ public class AlchemyPotion {
return false;
}
if (!otherPotionMeta.hasDisplayName() && getAlchemyPotionMeta().hasDisplayName()) {
return false;
}
var alchemyPotionName = getAlchemyPotionMeta().hasDisplayName() ? getAlchemyPotionMeta().getDisplayName() : null;
return (alchemyPotionName == null && !otherPotionMeta.hasDisplayName()) || otherPotionMeta.getDisplayName().equals(alchemyPotionName);
return true;
}
public PotionMeta getAlchemyPotionMeta() {
return (PotionMeta) potion.getItemMeta();
return (PotionMeta) potionItemstack.getItemMeta();
}
public boolean isSplash() {
return potion.getType() == Material.SPLASH_POTION;
return potionItemstack.getType() == Material.SPLASH_POTION;
}
public boolean isLingering() {
return potion.getType() == Material.LINGERING_POTION;
return potionItemstack.getType() == Material.LINGERING_POTION;
}
@Override
@ -102,18 +95,18 @@ public class AlchemyPotion {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AlchemyPotion that = (AlchemyPotion) o;
return Objects.equals(potion, that.potion) && Objects.equals(alchemyPotionChildren, that.alchemyPotionChildren);
return Objects.equals(potionItemstack, that.potionItemstack) && Objects.equals(alchemyPotionChildren, that.alchemyPotionChildren);
}
@Override
public int hashCode() {
return Objects.hash(potion, alchemyPotionChildren);
return Objects.hash(potionItemstack, alchemyPotionChildren);
}
@Override
public String toString() {
return "AlchemyPotion{" +
"potion=" + potion +
"potion=" + potionItemstack +
", alchemyPotionChildren=" + alchemyPotionChildren +
'}';
}

View File

@ -136,7 +136,7 @@ public class Roll extends AcrobaticsSubSkill {
* Graceful is double the odds of a normal roll
*/
Probability probability = getRollProbability(player);
Probability gracefulProbability = Probability.ofPercent(probability.getValue() * 2);
Probability gracefulProbability = Probability.ofValue(probability.getValue() * 2);
String[] gracefulRollStrings = ProbabilityUtil.getRNGDisplayValues(gracefulProbability);
gracefulRollChance = gracefulRollStrings[0];
gracefulRollChanceLucky = gracefulRollStrings[1];
@ -274,7 +274,7 @@ public class Roll extends AcrobaticsSubSkill {
@NotNull
public static Probability getGracefulProbability(Player player) {
double gracefulOdds = ProbabilityUtil.getSubSkillProbability(SubSkillType.ACROBATICS_ROLL, player).getValue() * 2;
return Probability.ofPercent(gracefulOdds);
return Probability.ofValue(gracefulOdds);
}
/**

View File

@ -0,0 +1,18 @@
package com.gmail.nossr50.protocollib;
import com.gmail.nossr50.mcMMO;
import org.bukkit.plugin.Plugin;
// TODO: Finish this class
public class ProtocolLibManager {
Plugin protocolLibPluginRef;
mcMMO pluginRef;
public ProtocolLibManager(mcMMO pluginRef) {
this.pluginRef = pluginRef;
}
public boolean isProtocolLibPresent() {
protocolLibPluginRef = pluginRef.getServer().getPluginManager().getPlugin("ProtocolLib");
return protocolLibPluginRef != null;
}
}

View File

@ -3,6 +3,7 @@ package com.gmail.nossr50.runnables.skills;
import com.gmail.nossr50.skills.alchemy.Alchemy;
import com.gmail.nossr50.skills.alchemy.AlchemyPotionBrewer;
import com.gmail.nossr50.util.CancellableRunnable;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.block.BrewingStand;
import org.bukkit.entity.Player;
@ -31,8 +32,7 @@ public class AlchemyBrewCheckTask extends CancellableRunnable {
if (oldInventory[Alchemy.INGREDIENT_SLOT] == null || newInventory[Alchemy.INGREDIENT_SLOT] == null || !oldInventory[Alchemy.INGREDIENT_SLOT].isSimilar(newInventory[Alchemy.INGREDIENT_SLOT]) || !validBrew) {
Alchemy.brewingStandMap.get(location).cancelBrew();
}
}
else if (validBrew) {
} else if (validBrew) {
Alchemy.brewingStandMap.put(location, new AlchemyBrewTask(brewingStand, player));
}
}

View File

@ -11,6 +11,7 @@ import com.gmail.nossr50.util.CancellableRunnable;
import com.gmail.nossr50.util.Misc;
import com.gmail.nossr50.util.Permissions;
import com.gmail.nossr50.util.player.UserManager;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.BlockState;
@ -67,33 +68,65 @@ public class AlchemyBrewTask extends CancellableRunnable {
@Override
public void run() {
if (player == null || !player.isValid() || brewingStand == null || brewingStand.getType() != Material.BREWING_STAND || !AlchemyPotionBrewer.isValidIngredient(player, ((BrewingStand) brewingStand).getInventory().getContents()[Alchemy.INGREDIENT_SLOT])) {
// Check if preconditions for brewing are not met
if (shouldCancelBrewing()) {
if (Alchemy.brewingStandMap.containsKey(location)) {
Alchemy.brewingStandMap.remove(location);
}
this.cancel();
return;
}
// Initialize the brewing stand on the first run
initializeBrewing();
// Update the brewing process timer
brewTimer -= brewSpeed;
// Check if the brewing process should finish
if (isBrewingComplete()) {
this.cancel();
finish();
} else {
updateBrewingTime();
}
}
private boolean shouldCancelBrewing() {
if (player == null) {
return true;
}
if (!player.isValid()) {
return true;
}
if (brewingStand == null) {
return true;
}
if (brewingStand.getType() != Material.BREWING_STAND) {
return true;
}
if (!AlchemyPotionBrewer.isValidIngredient(player, ((BrewingStand) brewingStand).getInventory().getContents()[Alchemy.INGREDIENT_SLOT])) {
return true;
}
return false;
}
private void initializeBrewing() {
if (firstRun) {
firstRun = false;
((BrewingStand) brewingStand).setFuelLevel(fuel);
}
brewTimer -= brewSpeed;
// Vanilla potion brewing completes when BrewingTime == 1
if (brewTimer < Math.max(brewSpeed, 2)) {
this.cancel();
finish();
}
else {
((BrewingStand) brewingStand).setBrewingTime((int) brewTimer);
}
}
private boolean isBrewingComplete() {
return brewTimer < Math.max(brewSpeed, 2);
}
private void updateBrewingTime() {
((BrewingStand) brewingStand).setBrewingTime((int) brewTimer);
}
private void finish() {
McMMOPlayerBrewEvent event = new McMMOPlayerBrewEvent(player, brewingStand);
mcMMO.p.getServer().getPluginManager().callEvent(event);

View File

@ -11,40 +11,6 @@ import java.util.List;
import java.util.Map;
public final class Alchemy {
/*public enum Tier {
EIGHT(8),
SEVEN(7),
SIX(6),
FIVE(5),
FOUR(4),
THREE(3),
TWO(2),
ONE(1);
int numerical;
private Tier(int numerical) {
this.numerical = numerical;
}
public int toNumerical() {
return numerical;
}
public static Tier fromNumerical(int numerical) {
for (Tier tier : Tier.values()) {
if (tier.toNumerical() == numerical) {
return tier;
}
}
return null;
}
protected int getLevel() {
return mcMMO.p.getAdvancedConfig().getConcoctionsTierLevel(this);
}
}*/
public static final int INGREDIENT_SLOT = 3;
public static int catalysisMaxBonusLevel = mcMMO.p.getAdvancedConfig().getCatalysisMaxBonusLevel();

View File

@ -9,6 +9,7 @@ import com.gmail.nossr50.runnables.player.PlayerUpdateInventoryTask;
import com.gmail.nossr50.runnables.skills.AlchemyBrewCheckTask;
import com.gmail.nossr50.util.Permissions;
import com.gmail.nossr50.util.player.UserManager;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.BlockState;
import org.bukkit.block.BrewingStand;
@ -31,7 +32,9 @@ public final class AlchemyPotionBrewer {
}
for (int i = 0; i < 3; i++) {
if (contents[i] == null || contents[i].getType() != Material.POTION && contents[i].getType() != Material.SPLASH_POTION && contents[i].getType() != Material.LINGERING_POTION) {
if (contents[i] == null || contents[i].getType() != Material.POTION
&& contents[i].getType() != Material.SPLASH_POTION
&& contents[i].getType() != Material.LINGERING_POTION) {
continue;
}
@ -101,52 +104,71 @@ public final class AlchemyPotionBrewer {
}
public static void finishBrewing(BlockState brewingStand, Player player, boolean forced) {
// Check if the brewing stand block state is an actual brewing stand
if (!(brewingStand instanceof BrewingStand)) {
return;
}
BrewerInventory inventory = ((BrewingStand) brewingStand).getInventory();
ItemStack ingredient = inventory.getIngredient() == null ? null : inventory.getIngredient().clone();
// Retrieve the inventory of the brewing stand and clone the current ingredient for safe manipulation
final BrewerInventory inventory = ((BrewingStand) brewingStand).getInventory();
final ItemStack ingredient = inventory.getIngredient() == null ? null : inventory.getIngredient().clone();
// Check if the brewing stand has a valid ingredient; if not, exit the method
if (!hasIngredient(inventory, player)) {
// debug
return;
}
// Initialize lists to hold the potions before and after brewing, initially setting them to null
List<AlchemyPotion> inputList = new ArrayList<>(Collections.nCopies(3, null));
List<ItemStack> outputList = new ArrayList<>(Collections.nCopies(3, null));
// Process each of the three slots in the brewing stand
for (int i = 0; i < 3; i++) {
ItemStack item = inventory.getItem(i);
if (isEmpty(item) || item.getType() == Material.GLASS_BOTTLE || !mcMMO.p.getPotionConfig().isValidPotion(item)) {
// Skip the slot if it's empty, contains a glass bottle, or holds an invalid potion
if (isEmpty(item)
|| item.getType() == Material.GLASS_BOTTLE
|| !mcMMO.p.getPotionConfig().isValidPotion(item)) {
// debug
continue;
}
// Retrieve the potion configurations for the input and resulting output potion
AlchemyPotion input = mcMMO.p.getPotionConfig().getPotion(item);
AlchemyPotion output = input.getChild(ingredient);
// Update the input list with the current potion
inputList.set(i, input);
// If there is a valid output potion, add it to the output list
if (output != null) {
outputList.set(i, output.toItemStack(item.getAmount()).clone());
}
}
// Create a fake brewing event and pass it to the plugin's event system
FakeBrewEvent event = new FakeBrewEvent(brewingStand.getBlock(), inventory, outputList, ((BrewingStand) brewingStand).getFuelLevel());
mcMMO.p.getServer().getPluginManager().callEvent(event);
// If the event is cancelled or there are no potions processed, exit the method
if (event.isCancelled() || inputList.isEmpty()) {
// debug
return;
}
// Update the brewing inventory with the new potions
for (int i = 0; i < 3; i++) {
if(outputList.get(i) != null) {
inventory.setItem(i, outputList.get(i));
}
}
// Remove the used ingredient from the brewing inventory
removeIngredient(inventory, player);
// Handle potion brewing success and related effects for each potion processed
for (AlchemyPotion input : inputList) {
if (input == null) continue;
@ -155,13 +177,14 @@ public final class AlchemyPotionBrewer {
if (output != null && player != null) {
PotionStage potionStage = PotionStage.getPotionStage(input, output);
//TODO: hmm
// Update player alchemy skills or effects based on brewing success
if (UserManager.hasPlayerDataKey(player)) {
UserManager.getPlayer(player).getAlchemyManager().handlePotionBrewSuccesses(potionStage, 1);
}
}
}
// If the brewing was not forced by external conditions, schedule a new update
if (!forced) {
scheduleUpdate(inventory);
}

View File

@ -39,6 +39,7 @@ public class PotionUtil {
static {
// We used to use uncraftable as the potion type
// this type isn't available anymore, so water will do
potionDataClass = getPotionDataClass();
legacyPotionTypes.put("UNCRAFTABLE", "WATER");
legacyPotionTypes.put("JUMP", "LEAPING");
legacyPotionTypes.put("SPEED", "SWIFTNESS");
@ -55,9 +56,8 @@ public class PotionUtil {
methodPotionTypeGetEffectType = getPotionTypeEffectType();
methodPotionTypeGetPotionEffects = getPotionTypeGetPotionEffects();
methodSetBasePotionData = getSetBasePotionData();
potionDataClass = getPotionDataClass();
if (methodPotionMetaGetBasePotionData != null) {
if (potionDataClass != null) {
COMPATIBILITY_MODE = PotionCompatibilityType.PRE_1_20_5;
} else {
COMPATIBILITY_MODE = PotionCompatibilityType.POST_1_20_5;
@ -161,7 +161,7 @@ public class PotionUtil {
*/
private static @Nullable Method getBasePotionData() {
try {
return PotionType.class.getMethod("getBasePotionData");
return PotionMeta.class.getMethod("getBasePotionData");
} catch (NoSuchMethodException e) {
return null;
}
@ -419,7 +419,7 @@ public class PotionUtil {
* @param upgraded true if the potion is upgraded
*/
public static void setBasePotionType(PotionMeta potionMeta, PotionType potionType, boolean extended, boolean upgraded) {
if (COMPATIBILITY_MODE == PotionCompatibilityType.PRE_1_20_5) {
if (methodPotionMetaSetBasePotionType == null) {
setBasePotionTypeLegacy(potionMeta, potionType, extended, upgraded);
} else {
setBasePotionTypeModern(potionMeta, potionType);

View File

@ -186,4 +186,9 @@ public class StringUtils {
}
}
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);
}
}

View File

@ -14,7 +14,7 @@ author: nossr50
authors: [GJ, NuclearW, bm01, Glitchfinder, TfT_02, t00thpick1, Riking, electronicboy, kashike]
website: https://www.mcmmo.org
main: com.gmail.nossr50.mcMMO
softdepend: [WorldGuard, CombatTag, HealthBar, PlaceholderAPI]
softdepend: [WorldGuard, CombatTag, HealthBar, PlaceholderAPI, ProtocolLib]
load: POSTWORLD
folia-supported: true
api-version: 1.13

View File

@ -1,46 +1,30 @@
package com.gmail.nossr50.util;
import org.bukkit.potion.PotionType;
import org.junit.jupiter.api.Test;
import static com.gmail.nossr50.util.PotionUtil.convertLegacyNames;
import static com.gmail.nossr50.util.PotionUtil.matchPotionType;
import static org.junit.jupiter.api.Assertions.assertEquals;
class PotionUtilTest {
@Test
void testDisplay() {
// System.out.println("\n");
// System.out.println("\n");
// System.out.println("\n");
// System.out.println("\n");
// for(var s : PotionType.values()) {
// System.out.println("PotionType.getKey().getKey(): " + s.getKey().getKey());
// System.out.println("PotionType.name(): " + s.name());
// System.out.println("PotionType.toString():" + s.toString());
// System.out.println("\n");
// }
}
@Test
void testMatchPotionType() {
String potionTypeStr = "UNCRAFTABLE";
PotionType potionType = matchPotionType(potionTypeStr, false, false);
assertEquals(PotionType.WATER, potionType);
String potionTypeStr2 = "NIGHT_VISION";
PotionType potionType2 = matchPotionType(potionTypeStr2, false, false);
assertEquals(PotionType.NIGHT_VISION, potionType2);
String nightVisionLong = "NIGHT_VISION";
PotionType potionType3 = matchPotionType(nightVisionLong, false, true);
assertEquals(PotionType.LONG_NIGHT_VISION, potionType3);
nightVisionLong = "LONG_NIGHT_VISION";
potionType3 = matchPotionType(nightVisionLong, false, true);
assertEquals(PotionType.LONG_NIGHT_VISION, potionType3);
}
// @Test
// void testMatchPotionType() {
// String potionTypeStr = "UNCRAFTABLE";
// PotionType potionType = matchPotionType(potionTypeStr, false, false);
// assertEquals(PotionType.WATER, potionType);
//
// String potionTypeStr2 = "NIGHT_VISION";
// PotionType potionType2 = matchPotionType(potionTypeStr2, false, false);
// assertEquals(PotionType.NIGHT_VISION, potionType2);
//
// String nightVisionLong = "NIGHT_VISION";
// PotionType potionType3 = matchPotionType(nightVisionLong, false, true);
// assertEquals(PotionType.LONG_NIGHT_VISION, potionType3);
//
// nightVisionLong = "LONG_NIGHT_VISION";
// potionType3 = matchPotionType(nightVisionLong, false, true);
// assertEquals(PotionType.LONG_NIGHT_VISION, potionType3);
// }
@Test
void testConvertLegacyNames() {