mirror of
https://github.com/mcMMO-Dev/mcMMO.git
synced 2026-08-18 11:26:06 +00:00
Route McMMOPlayer addXp and addLevels through level up processing
Both wrote raw values into the profile, so plugins using them left skills over their XP threshold with no level ups, events, or XP bar updates. addXp now runs the normal XP gain path with the amount unmodified, and addLevels fires the level change events like the addlevels command. Internal callers that revert cancelled events or apply vampirism gains keep raw writes via the profile.
This commit is contained in:
@ -120,6 +120,7 @@ Version 2.3.000
|
||||
(API) Deprecated DelayedHerbalismXPCheckTask and HerbalismManager#awardXPForBlockSnapshots for removal
|
||||
(API) Deprecated unused public utility methods and fields
|
||||
(API) Deprecated the misspelled PlayerProfile#getChimaerWingDATS in favor of getChimaeraWingDATS
|
||||
(API) McMMOPlayer#addXp and McMMOPlayer#addLevels now fire the XP and level change events and process level ups instead of writing raw values (See notes)
|
||||
(API) Added DatabaseManager#readLeaderboardSnapshot for reading every leaderboard scope in one call
|
||||
(Codebase) Added scoreboard-library 2.8.0 as a shaded dependency for the packet-based scoreboard implementation
|
||||
(Codebase) Added ViaVersion to plugin.yml softdepend
|
||||
@ -216,6 +217,9 @@ Version 2.3.000
|
||||
-- Child skill levels through the API --
|
||||
Adding levels to Salvage or Smelting through the mcMMO API caused an error for online players. Child skills have no levels of their own, so added levels now split evenly across the skill's parent skills, matching how child skill XP and the offline API variants already behaved.
|
||||
|
||||
-- XP and levels added through the API --
|
||||
(API) Only matters on servers running plugins that give out mcMMO XP or levels through McMMOPlayer#addXp or McMMOPlayer#addLevels. These previously wrote raw values without awarding level ups, leaving skills stuck over their XP threshold with no level up message, sound, or scoreboard update. They now behave like any normal gain and fire the usual events, so plugins listening for mcMMO XP and level changes will start seeing these gains too.
|
||||
|
||||
Version 2.2.054
|
||||
Added compatibility for new blocks and items from Chaos Cubed (Minecraft 26.2) to mcMMO
|
||||
Fixed party/admin chat allowing players to use color codes without the 'mcmmo.chat.colors' permission
|
||||
|
||||
@ -1241,12 +1241,41 @@ public class McMMOPlayer implements Identified {
|
||||
profile.modifySkill(skill, level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds levels to a skill and fires the level change events, like the /addlevels command.
|
||||
* Levels added to a child skill split evenly across its parent skills.
|
||||
*
|
||||
* @param skill the skill to add levels to
|
||||
* @param levels the number of levels to add
|
||||
*/
|
||||
public void addLevels(PrimarySkillType skill, int levels) {
|
||||
if (SkillTools.isChildSkill(skill)) {
|
||||
var parentSkills = mcMMO.p.getSkillTools().getChildSkillParents(skill);
|
||||
int dividedLevels = levels / parentSkills.size();
|
||||
|
||||
for (PrimarySkillType parentSkill : parentSkills) {
|
||||
addLevels(parentSkill, dividedLevels);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
final float xpRemoved = profile.getSkillXpLevelRaw(skill);
|
||||
profile.addLevels(skill, levels);
|
||||
EventUtils.tryLevelChangeEvent(this, skill, levels, xpRemoved, true,
|
||||
XPGainReason.UNKNOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds XP as a normal gain: fires the XP gain events, awards level ups, and updates the
|
||||
* XP bar. The amount is applied as-is, without the rate and perk modifiers that
|
||||
* {@link #beginXpGain(PrimarySkillType, float, XPGainReason, XPGainSource)} applies.
|
||||
*
|
||||
* @param skill the skill to add XP to
|
||||
* @param xp the amount of XP to add
|
||||
*/
|
||||
public void addXp(PrimarySkillType skill, float xp) {
|
||||
profile.addXp(skill, xp);
|
||||
applyXpGain(skill, xp, XPGainReason.UNKNOWN, XPGainSource.SELF);
|
||||
}
|
||||
|
||||
public void setAbilityDATS(SuperAbilityType ability, long DATS) {
|
||||
|
||||
@ -285,7 +285,7 @@ public final class EventUtils {
|
||||
} else {
|
||||
mmoPlayer.modifySkill(skill, mmoPlayer.getSkillLevel(skill)
|
||||
- (isLevelUp ? levelsChanged : -levelsChanged));
|
||||
mmoPlayer.addXp(skill, xpRemoved);
|
||||
mmoPlayer.getProfile().addXp(skill, xpRemoved);
|
||||
}
|
||||
} else if (isLevelUp && mmoPlayer != null) {
|
||||
NotificationManager.processLevelUpBroadcasting(mmoPlayer, skill,
|
||||
@ -462,7 +462,7 @@ public final class EventUtils {
|
||||
boolean isCancelled = event.isCancelled();
|
||||
|
||||
if (!isCancelled) {
|
||||
mmoPlayer.addXp(skill, event.getRawXpGained());
|
||||
mmoPlayer.getProfile().addXp(skill, event.getRawXpGained());
|
||||
mmoPlayer.getProfile().registerXpGain(skill, event.getRawXpGained());
|
||||
}
|
||||
|
||||
@ -546,7 +546,10 @@ public final class EventUtils {
|
||||
String skillName = primarySkillType.toString();
|
||||
int victimSkillLevel = victimProfile.getSkillLevel(primarySkillType);
|
||||
|
||||
killerPlayer.addLevels(primarySkillType, levelChangedKiller.get(skillName));
|
||||
// Raw write: these gains are governed by the vampirism event above, so the
|
||||
// level change events that addLevels fires must not run here
|
||||
killerPlayer.getProfile()
|
||||
.addLevels(primarySkillType, levelChangedKiller.get(skillName));
|
||||
killerPlayer.beginUnsharedXpGain(primarySkillType,
|
||||
experienceChangedKiller.get(skillName), XPGainReason.VAMPIRISM,
|
||||
XPGainSource.VAMPIRISM);
|
||||
|
||||
@ -0,0 +1,112 @@
|
||||
package com.gmail.nossr50.datatypes.player;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.anyInt;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.gmail.nossr50.MMOTestEnvironment;
|
||||
import com.gmail.nossr50.config.experience.ExperienceConfig;
|
||||
import com.gmail.nossr50.datatypes.experience.FormulaType;
|
||||
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
|
||||
import com.gmail.nossr50.events.experience.McMMOPlayerLevelUpEvent;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.util.Permissions;
|
||||
import com.gmail.nossr50.util.experience.FormulaManager;
|
||||
import java.util.logging.Logger;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Event;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
/**
|
||||
* Covers the public XP and level adding methods on {@link McMMOPlayer}. Other plugins award
|
||||
* progression through these, so they must behave like any normal gain: XP that crosses a
|
||||
* threshold levels the player up, and added levels fire the level change events. Raw writes
|
||||
* that leave a skill sitting over its XP threshold must not be reachable here.
|
||||
*/
|
||||
class McMMOPlayerAddXpAndLevelsTest extends MMOTestEnvironment {
|
||||
private static final Logger logger = Logger.getLogger(
|
||||
McMMOPlayerAddXpAndLevelsTest.class.getName());
|
||||
private static final int XP_TO_NEXT_LEVEL = 10;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
mockBaseEnvironment(logger);
|
||||
stubLevelUpEnvironment();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
cleanUpStaticMocks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stubs everything the level-up path needs so gains translate into level-ups: skill
|
||||
* permissions, disabled level caps, and a flat XP curve.
|
||||
*/
|
||||
private void stubLevelUpEnvironment() {
|
||||
when(Permissions.skillEnabled(any(Player.class), any(PrimarySkillType.class)))
|
||||
.thenReturn(true);
|
||||
when(generalConfig.getPowerLevelCap()).thenReturn(Integer.MAX_VALUE);
|
||||
when(generalConfig.getLevelCap(any(PrimarySkillType.class))).thenReturn(Integer.MAX_VALUE);
|
||||
when(ExperienceConfig.getInstance().getFormulaType()).thenReturn(FormulaType.LINEAR);
|
||||
|
||||
final FormulaManager formulaManager = mock(FormulaManager.class);
|
||||
when(formulaManager.getXPtoNextLevel(anyInt(), any(FormulaType.class)))
|
||||
.thenReturn(XP_TO_NEXT_LEVEL);
|
||||
when(mcMMO.getFormulaManager()).thenReturn(formulaManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
void addXpShouldAwardALevelUpWhenTheGainCrossesTheThreshold() {
|
||||
// Given - a level 0 player on a flat 10-XP-per-level curve
|
||||
|
||||
// When - another plugin adds 15 XP to Mining
|
||||
mmoPlayer.addXp(PrimarySkillType.MINING, XP_TO_NEXT_LEVEL + 5F);
|
||||
|
||||
// Then - the player levels up and keeps the remainder instead of sitting at 15/10 XP
|
||||
assertThat(mmoPlayer.getSkillLevel(PrimarySkillType.MINING)).isEqualTo(1);
|
||||
assertThat(mmoPlayer.getProfile().getSkillXpLevelRaw(PrimarySkillType.MINING))
|
||||
.isEqualTo(5F);
|
||||
}
|
||||
|
||||
@Test
|
||||
void addXpShouldAccrueTheExactAmountWhenBelowTheThreshold() {
|
||||
// Given - a level 0 player on a flat 10-XP-per-level curve
|
||||
|
||||
// When - another plugin adds less XP than the next level needs
|
||||
mmoPlayer.addXp(PrimarySkillType.MINING, XP_TO_NEXT_LEVEL - 5F);
|
||||
|
||||
// Then - the XP accrues unmodified by rates or perks and no level is awarded
|
||||
assertThat(mmoPlayer.getSkillLevel(PrimarySkillType.MINING)).isZero();
|
||||
assertThat(mmoPlayer.getProfile().getSkillXpLevelRaw(PrimarySkillType.MINING))
|
||||
.isEqualTo(5F);
|
||||
}
|
||||
|
||||
@Test
|
||||
void addLevelsShouldFireTheLevelUpEventForTheAddedLevels() {
|
||||
// Given - a level 0 player
|
||||
|
||||
// When - another plugin adds 3 Mining levels
|
||||
mmoPlayer.addLevels(PrimarySkillType.MINING, 3);
|
||||
|
||||
// Then - the levels are applied and the level up event announces the change
|
||||
assertThat(mmoPlayer.getSkillLevel(PrimarySkillType.MINING)).isEqualTo(3);
|
||||
final ArgumentCaptor<Event> eventCaptor = ArgumentCaptor.forClass(Event.class);
|
||||
verify(pluginManager, atLeastOnce()).callEvent(eventCaptor.capture());
|
||||
assertThat(eventCaptor.getAllValues())
|
||||
.filteredOn(McMMOPlayerLevelUpEvent.class::isInstance)
|
||||
.map(McMMOPlayerLevelUpEvent.class::cast)
|
||||
.singleElement()
|
||||
.satisfies(levelUpEvent -> {
|
||||
assertThat(levelUpEvent.getSkill()).isEqualTo(PrimarySkillType.MINING);
|
||||
assertThat(levelUpEvent.getLevelsGained()).isEqualTo(3);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user