Fix bug with levels up commands reporting incorrect level

This commit is contained in:
nossr50 2023-08-27 16:19:46 -07:00
parent 2d8bdbded8
commit edab455581
14 changed files with 150 additions and 43 deletions

View File

@ -1123,7 +1123,7 @@ public final class ExperienceAPI {
* @throws InvalidFormulaTypeException if the given formulaType is not valid
*/
public static int getXpNeededToLevel(int level) {
return mcMMO.getFormulaManager().getXPtoNextLevel(level, ExperienceConfig.getInstance().getFormulaType());
return mcMMO.p.getFormulaManager().getXPtoNextLevel(level, ExperienceConfig.getInstance().getFormulaType());
}
/**
@ -1137,7 +1137,7 @@ public final class ExperienceAPI {
* @throws InvalidFormulaTypeException if the given formulaType is not valid
*/
public static int getXpNeededToLevel(int level, String formulaType) {
return mcMMO.getFormulaManager().getXPtoNextLevel(level, getFormulaType(formulaType));
return mcMMO.p.getFormulaManager().getXPtoNextLevel(level, getFormulaType(formulaType));
}
/**

View File

@ -18,7 +18,7 @@ public class ConvertExperienceCommand implements CommandExecutor {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (args.length == 2) {
FormulaType previousType = mcMMO.getFormulaManager().getPreviousFormulaType();
FormulaType previousType = mcMMO.p.getFormulaManager().getPreviousFormulaType();
FormulaType newType = FormulaType.getFormulaType(args[1].toUpperCase(Locale.ENGLISH));
if (newType == FormulaType.UNKNOWN) {

View File

@ -199,7 +199,7 @@ public class ExperienceConfig extends BukkitConfig {
/* Curve settings */
public FormulaType getFormulaType() {
return FormulaType.getFormulaType(config.getString("Experience_Formula.Curve"));
return FormulaType.getFormulaType(config.getString("Experience_Formula.Curve", "LINEAR"));
}
public boolean getCumulativeCurveEnabled() {
@ -208,11 +208,13 @@ public class ExperienceConfig extends BukkitConfig {
/* Curve values */
public double getMultiplier(FormulaType type) {
return config.getDouble("Experience_Formula." + StringUtils.getCapitalized(type.toString()) + "_Values.multiplier");
double def = type == FormulaType.LINEAR ? 20D : 0.1D;
return config.getDouble("Experience_Formula." + StringUtils.getCapitalized(type.toString()) + "_Values.multiplier", def);
}
public int getBase(FormulaType type) {
return config.getInt("Experience_Formula." + StringUtils.getCapitalized(type.toString()) + "_Values.base");
int def = type == FormulaType.LINEAR ? 1020 : 2000;
return config.getInt("Experience_Formula." + StringUtils.getCapitalized(type.toString()) + "_Values.base", def);
}
public double getExponent(FormulaType type) {

View File

@ -203,7 +203,7 @@ public class Party {
public int getXpToLevel() {
FormulaType formulaType = ExperienceConfig.getInstance().getFormulaType();
return (mcMMO.getFormulaManager().getXPtoNextLevel(level, formulaType)) * (getOnlineMembers().size() + mcMMO.p.getGeneralConfig().getPartyXpCurveMultiplier());
return (mcMMO.p.getFormulaManager().getXPtoNextLevel(level, formulaType)) * (getOnlineMembers().size() + mcMMO.p.getGeneralConfig().getPartyXpCurveMultiplier());
}
public String getXpToLevelPercentage() {

View File

@ -663,7 +663,7 @@ public class McMMOPlayer implements Identified {
}
final McMMOPlayerPreXpGainEvent mcMMOPlayerPreXpGainEvent = new McMMOPlayerPreXpGainEvent(player, primarySkillType, xp, xpGainReason);
Bukkit.getPluginManager().callEvent(mcMMOPlayerPreXpGainEvent);
mcMMO.p.getServer().getPluginManager().callEvent(mcMMOPlayerPreXpGainEvent);
xp = mcMMOPlayerPreXpGainEvent.getXpGained();
if (SkillTools.isChildSkill(primarySkillType)) {

View File

@ -428,7 +428,7 @@ public class PlayerProfile {
int level = (ExperienceConfig.getInstance().getCumulativeCurveEnabled()) ? UserManager.getPlayer(playerName).getPowerLevel() : skills.get(primarySkillType);
FormulaType formulaType = ExperienceConfig.getInstance().getFormulaType();
return mcMMO.getFormulaManager().getXPtoNextLevel(level, formulaType);
return mcMMO.p.getFormulaManager().getXPtoNextLevel(level, formulaType);
}
private int getChildSkillLevel(PrimarySkillType primarySkillType) {

View File

@ -60,9 +60,9 @@ public class SelfListener implements Listener {
}
final Set<Integer> levelsAchieved = new LinkedHashSet<>();
for(int i = 1; i <= event.getLevelsGained(); i++)
{
levelsAchieved.add(event.getSkillLevel() + i);
int startingLevel = event.getSkillLevel() - event.getLevelsGained();
for (int i = 0; i < event.getLevelsGained(); i++) {
levelsAchieved.add(startingLevel + (i + 1));
}
plugin.getLevelUpCommandManager().apply(mcMMOPlayer, skill, levelsAchieved);
}

View File

@ -87,7 +87,7 @@ public class mcMMO extends JavaPlugin {
private static SalvageableManager salvageableManager;
private static ModManager modManager;
private static DatabaseManager databaseManager;
private static FormulaManager formulaManager;
private FormulaManager formulaManager;
private static UpgradeManager upgradeManager;
private static LevelUpCommandManager levelUpCommandManager;
private static MaterialMapStore materialMapStore;
@ -428,7 +428,7 @@ public class mcMMO extends JavaPlugin {
xpEventEnabled = !xpEventEnabled;
}
public static FormulaManager getFormulaManager() {
public FormulaManager getFormulaManager() {
return formulaManager;
}

View File

@ -52,7 +52,7 @@ public class FormulaConversionTask extends BukkitRunnable {
convertedUsers++;
Misc.printProgress(convertedUsers, DatabaseManager.progressInterval, startMillis);
}
mcMMO.getFormulaManager().setPreviousFormulaType(formulaType);
mcMMO.p.getFormulaManager().setPreviousFormulaType(formulaType);
sender.sendMessage(LocaleLoader.getString("Commands.mcconvert.Experience.Finish", formulaType.toString()));
}
@ -63,13 +63,13 @@ public class FormulaConversionTask extends BukkitRunnable {
for (PrimarySkillType primarySkillType : SkillTools.NON_CHILD_SKILLS) {
int oldLevel = profile.getSkillLevel(primarySkillType);
int oldXPLevel = profile.getSkillXpLevel(primarySkillType);
int totalOldXP = mcMMO.getFormulaManager().calculateTotalExperience(oldLevel, oldXPLevel);
int totalOldXP = mcMMO.p.getFormulaManager().calculateTotalExperience(oldLevel, oldXPLevel);
if (totalOldXP == 0) {
continue;
}
int[] newExperienceValues = mcMMO.getFormulaManager().calculateNewLevel(primarySkillType, (int) Math.floor(totalOldXP / ExperienceConfig.getInstance().getExpModifier()), formulaType);
int[] newExperienceValues = mcMMO.p.getFormulaManager().calculateNewLevel(primarySkillType, (int) Math.floor(totalOldXP / ExperienceConfig.getInstance().getExpModifier()), formulaType);
int newLevel = newExperienceValues[0];
int newXPlevel = newExperienceValues[1];

View File

@ -263,7 +263,6 @@ public final class EventUtils {
if (isLevelUp) {
NotificationManager.processLevelUpBroadcasting(mmoPlayer, skill, mmoPlayer.getSkillLevel(skill));
NotificationManager.processPowerLevelUpBroadcasting(mmoPlayer, mmoPlayer.getPowerLevel());
}
}

View File

@ -6,6 +6,7 @@ import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.LogUtils;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.VisibleForTesting;
import java.io.File;
import java.util.HashMap;
@ -25,7 +26,19 @@ public class FormulaManager {
public FormulaManager() {
/* Setting for Classic Mode (Scales a lot of stuff up by * 10) */
initExperienceNeededMaps();
loadFormula();
if (!formulaFile.exists()) {
previousFormula = FormulaType.UNKNOWN;
return;
}
previousFormula = FormulaType.getFormulaType(YamlConfiguration.loadConfiguration(formulaFile).getString("Previous_Formula", "UNKNOWN"));
}
@VisibleForTesting
public FormulaManager(FormulaType previousFormulaType) {
/* Setting for Classic Mode (Scales a lot of stuff up by * 10) */
initExperienceNeededMaps();
this.previousFormula = previousFormulaType;
}
/**
@ -122,7 +135,7 @@ public class FormulaManager {
*/
//TODO: When the heck is Unknown used?
if (formulaType == FormulaType.UNKNOWN) {
if (formulaType == null || formulaType == FormulaType.UNKNOWN) {
formulaType = FormulaType.LINEAR;
}
@ -209,18 +222,6 @@ public class FormulaManager {
}
}
/**
* Load formula file.
*/
public void loadFormula() {
if (!formulaFile.exists()) {
previousFormula = FormulaType.UNKNOWN;
return;
}
previousFormula = FormulaType.getFormulaType(YamlConfiguration.loadConfiguration(formulaFile).getString("Previous_Formula", "UNKNOWN"));
}
/**
* Save formula file.
*/

View File

@ -139,7 +139,7 @@ public class NotificationManager {
notificationType, message, destination, mcMMO.p.getAdvancedConfig().doesNotificationSendCopyToChat(notificationType));
//Call event
Bukkit.getServer().getPluginManager().callEvent(customEvent);
mcMMO.p.getServer().getPluginManager().callEvent(customEvent);
return customEvent;
}

View File

@ -3,13 +3,18 @@ package com.gmail.nossr50;
import com.gmail.nossr50.commands.levelup.LevelUpCommandManager;
import com.gmail.nossr50.config.*;
import com.gmail.nossr50.config.experience.ExperienceConfig;
import com.gmail.nossr50.datatypes.experience.FormulaType;
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.player.PlayerProfile;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.datatypes.skills.SubSkillType;
import com.gmail.nossr50.events.experience.McMMOPlayerLevelUpEvent;
import com.gmail.nossr50.events.experience.McMMOPlayerPreXpGainEvent;
import com.gmail.nossr50.listeners.SelfListener;
import com.gmail.nossr50.util.*;
import com.gmail.nossr50.util.blockmeta.ChunkManager;
import com.gmail.nossr50.util.experience.FormulaManager;
import com.gmail.nossr50.util.player.NotificationManager;
import com.gmail.nossr50.util.player.UserManager;
import com.gmail.nossr50.util.skills.RankUtils;
import com.gmail.nossr50.util.skills.SkillTools;
@ -26,8 +31,10 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.internal.matchers.Not;
import java.util.UUID;
import java.util.function.Function;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
@ -39,11 +46,11 @@ public abstract class MMOTestEnvironmentBasic {
protected MockedStatic<Bukkit> mockedBukkit;
protected MockedStatic<ChatConfig> mockedChatConfig;
protected MockedStatic<ExperienceConfig> experienceConfig;
private MockedStatic<NotificationManager> mockedNotificationManager;
protected MockedStatic<Permissions> mockedPermissions;
protected MockedStatic<RankUtils> mockedRankUtils;
protected MockedStatic<UserManager> mockedUserManager;
protected MockedStatic<Misc> mockedMisc;
protected MockedStatic<SkillTools> mockedSkillTools;
protected MockedStatic<EventUtils> mockedEventUtils;
protected SelfListener selfListener;
protected TransientEntityTracker transientEntityTracker;
@ -57,6 +64,8 @@ public abstract class MMOTestEnvironmentBasic {
protected PluginManager pluginManager;
protected World world;
private FormulaManager formulaManager;
/* Mocks */
protected Player player;
@ -87,6 +96,10 @@ public abstract class MMOTestEnvironmentBasic {
mcMMO.p = mock(mcMMO.class);
when(mcMMO.p.getLogger()).thenReturn(logger);
// formula manager
formulaManager = new FormulaManager(FormulaType.UNKNOWN);
when(mcMMO.p.getFormulaManager()).thenReturn(formulaManager);
// place store
chunkManager = mock(ChunkManager.class);
when(mcMMO.getPlaceStore()).thenReturn(chunkManager);
@ -115,7 +128,7 @@ public abstract class MMOTestEnvironmentBasic {
mockExperienceConfig();
// wire skill tools
this.skillTools = new SkillTools(mcMMO.p);
this.skillTools = Mockito.spy(new SkillTools(mcMMO.p));
when(mcMMO.p.getSkillTools()).thenReturn(skillTools);
this.transientEntityTracker = new TransientEntityTracker();
@ -123,6 +136,8 @@ public abstract class MMOTestEnvironmentBasic {
mockPermissions();
mockNotifications();
mockedRankUtils = Mockito.mockStatic(RankUtils.class);
// wire server
@ -132,11 +147,15 @@ public abstract class MMOTestEnvironmentBasic {
// wire plugin manager
this.pluginManager = mock(PluginManager.class);
when(mockedServer.getPluginManager()).thenReturn(pluginManager);
// Process level up events in our self listener
Mockito.doAnswer(invocation -> {
selfListener.onPlayerLevelUp(invocation.getArgument(0));
return null;
}).when(pluginManager).callEvent(any(McMMOPlayerLevelUpEvent.class));
// Don't process pre-gain events
Mockito.doAnswer((ignored) -> null).when(pluginManager).callEvent(any(McMMOPlayerPreXpGainEvent.class));
// wire world
this.world = mock(World.class);
@ -177,9 +196,12 @@ public abstract class MMOTestEnvironmentBasic {
private void mockPermissions() {
mockedPermissions = Mockito.mockStatic(Permissions.class);
when(Permissions.isSubSkillEnabled(any(Player.class), any(SubSkillType.class))).thenReturn(true);
// Mockito.when(Permissions.canUseSubSkill(any(Player.class), any(SubSkillType.class))).thenReturn(true);
when(Permissions.isSubSkillEnabled(any(Player.class), any(SubSkillType.class))).thenReturn(true);
// Mockito.when(Permissions.canUseSubSkill(any(Player.class), any(SubSkillType.class))).thenReturn(true);
when(Permissions.skillEnabled(any(Player.class), any(PrimarySkillType.class))).thenReturn(true);
}
private void mockNotifications() {
mockedNotificationManager = Mockito.mockStatic(NotificationManager.class);
}
private void mockRankConfig() {
@ -203,6 +225,10 @@ public abstract class MMOTestEnvironmentBasic {
generalConfig = mock(GeneralConfig.class);
when(generalConfig.getLocale()).thenReturn("en_US");
when(mcMMO.p.getGeneralConfig()).thenReturn(generalConfig);
// Experience related
when(generalConfig.getLevelCap(any(PrimarySkillType.class))).thenReturn(Integer.MAX_VALUE);
when(generalConfig.getPowerLevelCap()).thenReturn(Integer.MAX_VALUE);
}
private void mockExperienceConfig() {
@ -212,6 +238,12 @@ public abstract class MMOTestEnvironmentBasic {
// Combat
when(ExperienceConfig.getInstance().getCombatXP(EntityType.COW)).thenReturn(1D);
when(ExperienceConfig.getInstance().getFormulaType()).thenReturn(FormulaType.LINEAR);
when(ExperienceConfig.getInstance().getBase(FormulaType.LINEAR)).thenReturn(1020);
when(ExperienceConfig.getInstance().getMultiplier(FormulaType.LINEAR)).thenReturn(20D);
when(ExperienceConfig.getInstance().getFormulaSkillModifier(any(PrimarySkillType.class))).thenReturn(1D);
when(ExperienceConfig.getInstance().getExperienceGainsGlobalMultiplier()).thenReturn(1D);
when(ExperienceConfig.getInstance().getExpModifier()).thenReturn(1D);
}
protected void cleanupBaseEnvironment() {
@ -243,5 +275,8 @@ public abstract class MMOTestEnvironmentBasic {
if (mockedEventUtils != null) {
mockedEventUtils.close();
}
if (mockedNotificationManager != null) {
mockedNotificationManager.close();
}
}
}

View File

@ -2,6 +2,7 @@ package com.gmail.nossr50.commands.levelup;
import com.gmail.nossr50.MMOTestEnvironmentBasic;
import com.gmail.nossr50.datatypes.experience.XPGainReason;
import com.gmail.nossr50.datatypes.experience.XPGainSource;
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.events.experience.McMMOPlayerLevelUpEvent;
@ -11,11 +12,9 @@ import org.bukkit.Bukkit;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import java.util.Set;
import java.util.function.BiPredicate;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ -51,6 +50,71 @@ class LevelUpCommandTest extends MMOTestEnvironmentBasic {
mockedBukkit.verify(() -> Bukkit.dispatchCommand(any(), any()), atLeast(5));
}
@Test
void levelUpViaXPGainShouldRunCommandFiveTimes() {
// GIVEN level up command for Mining should always execute for Mining level up
assert mcMMO.p.getLevelUpCommandManager().isEmpty();
final String commandStr = "say hello";
final LevelUpCommand levelUpCommand
= buildLevelUpCommand(commandStr, (s, ignored) -> s == skill);
mcMMO.p.getLevelUpCommandManager().registerCommand(levelUpCommand);
// WHEN player gains 5 levels in mining via command
assertEquals(0, mmoPlayer.getSkillLevel(skill));
int levelsGained = 5;
for (int i = 0; i < 5; i++) {
mmoPlayer.applyXpGain(skill, mmoPlayer.getProfile().getXpToLevel(skill), XPGainReason.COMMAND, XPGainSource.COMMAND);
}
// THEN the command should be checked for execution
verify(levelUpCommandManager, times(levelsGained)).apply(any(), any(), any());
verify(levelUpCommand, times(levelsGained)).process(any(), any(), any());
// THEN the command should have executed
verify(levelUpCommand, times(levelsGained)).executeCommand(any(McMMOPlayer.class), any(PrimarySkillType.class), anyInt());
mockedBukkit.verify(() -> Bukkit.dispatchCommand(any(), any()), atLeast(5));
}
@Test
void levelUpViaXPGainShouldRunCommandFiveTimesWithPlaceholders() {
// GIVEN level up command for Mining should always execute for Mining level up
assert mcMMO.p.getLevelUpCommandManager().isEmpty();
String playerName = "Momshroom";
when (player.getName()).thenReturn(playerName);
assertEquals(player.getName(), playerName);
final String commandStr = "say hello %player%, you have reached level %level%";
final String expectedStr1 = "say hello " + playerName + ", you have reached level 1";
final String expectedStr2 = "say hello " + playerName + ", you have reached level 2";
final String expectedStr3 = "say hello " + playerName + ", you have reached level 3";
final String expectedStr4 = "say hello " + playerName + ", you have reached level 4";
final String expectedStr5 = "say hello " + playerName + ", you have reached level 5";
final LevelUpCommand levelUpCommand
= buildLevelUpCommand(commandStr, (s, ignored) -> s == skill);
mcMMO.p.getLevelUpCommandManager().registerCommand(levelUpCommand);
// WHEN player gains 5 levels in mining via command
assertEquals(0, mmoPlayer.getSkillLevel(skill));
int levelsGained = 5;
for (int i = 0; i < 5; i++) {
mmoPlayer.applyXpGain(skill, mmoPlayer.getProfile().getXpToLevel(skill), XPGainReason.COMMAND, XPGainSource.COMMAND);
}
// THEN the command should be checked for execution
verify(levelUpCommandManager, times(levelsGained)).apply(any(), any(), any());
verify(levelUpCommand, times(levelsGained)).process(any(), any(), any());
// THEN the command should have executed
verify(levelUpCommand, times(levelsGained)).executeCommand(any(McMMOPlayer.class), any(PrimarySkillType.class), anyInt());
mockedBukkit.verify(() -> Bukkit.dispatchCommand(any(), any()), atLeast(5));
// AND THEN the message for each level up should have happened at least once
// verify that Bukkit.dispatchCommand got executed at least 5 times with the correct injectedCommand
mockedBukkit.verify(() -> Bukkit.dispatchCommand(any(), eq(expectedStr1)));
mockedBukkit.verify(() -> Bukkit.dispatchCommand(any(), eq(expectedStr2)));
mockedBukkit.verify(() -> Bukkit.dispatchCommand(any(), eq(expectedStr3)));
mockedBukkit.verify(() -> Bukkit.dispatchCommand(any(), eq(expectedStr4)));
mockedBukkit.verify(() -> Bukkit.dispatchCommand(any(), eq(expectedStr5)));
}
@Test
void levelUpShouldRunCommandFiveTimesWithPlaceholders() {
// GIVEN level up command for Mining should always execute for Mining level up
@ -80,7 +144,7 @@ class LevelUpCommandTest extends MMOTestEnvironmentBasic {
}
@Test
void levelUpShouldRunCommandFiveTimesWithPlaceholdersForLevel() {
void levelUpViaAddLevelsShouldRunCommandFiveTimesWithPlaceholdersForLevel() {
// GIVEN level up command for Mining should always execute for Mining level up
assert mcMMO.p.getLevelUpCommandManager().isEmpty();
String playerName = "Momshroom";
@ -97,11 +161,17 @@ class LevelUpCommandTest extends MMOTestEnvironmentBasic {
final LevelUpCommand levelUpCommand
= buildLevelUpCommand(commandStr, (s, ignored) -> s == skill);
mcMMO.p.getLevelUpCommandManager().registerCommand(levelUpCommand);
int levelsGained = 5;
// WHEN player gains 5 levels in mining
McMMOPlayerLevelUpEvent event = new McMMOPlayerLevelUpEvent(player, skill, levelsGained, XPGainReason.PVE);
selfListener.onPlayerLevelUp(event);
int levelsGained = 5;
mmoPlayer.getProfile().addLevels(skill, levelsGained);
EventUtils.tryLevelChangeEvent(
player,
skill,
levelsGained,
mmoPlayer.getProfile().getSkillXpLevelRaw(skill),
true,
XPGainReason.COMMAND);
// THEN the command should be checked for execution
verify(levelUpCommandManager).apply(any(), any(), any());