McMMOPlayer & PlayerProfile redesigned

This is part 1 of a huge refactor, I'm going to be separating this refactor into a few commits for a cleaner history.
This commit is contained in:
nossr50 2020-08-17 20:40:31 -07:00
parent 1d950be7e4
commit 58432ce80e
2 changed files with 344 additions and 938 deletions

View File

@ -1,449 +1,136 @@
package com.gmail.nossr50.datatypes.player; package com.gmail.nossr50.datatypes.player;
import com.gmail.nossr50.config.AdvancedConfig; import com.gmail.nossr50.datatypes.MobHealthBarType;
import com.gmail.nossr50.config.Config; import org.bukkit.entity.Player;
import com.gmail.nossr50.config.experience.ExperienceConfig; import org.jetbrains.annotations.NotNull;
import com.gmail.nossr50.datatypes.MobHealthbarType;
import com.gmail.nossr50.datatypes.experience.FormulaType;
import com.gmail.nossr50.datatypes.experience.SkillXpGain;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.datatypes.skills.SuperAbilityType;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.runnables.player.PlayerProfileSaveTask;
import com.gmail.nossr50.skills.child.FamilyTree;
import com.gmail.nossr50.util.experience.ExperienceBarManager;
import com.gmail.nossr50.util.player.UserManager;
import com.gmail.nossr50.util.skills.SkillUtils;
import com.google.common.collect.ImmutableMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.DelayQueue;
public class PlayerProfile { public class PlayerProfile {
private final String playerName;
private UUID uuid;
private boolean loaded;
private volatile boolean changed;
/* HUDs */ /* All of the persistent data for a player that gets saved and loaded from DB */
private MobHealthbarType mobHealthbarType; private final @NotNull PersistentPlayerData persistentPlayerData; //All persistent data is kept here
private int scoreboardTipsShown;
private int saveAttempts = 0;
/* Skill Data */ /* Managers */
private final Map<PrimarySkillType, Integer> skills = new HashMap<>(); // Skill & Level private final @NotNull ExperienceManager experienceManager;
private final Map<PrimarySkillType, Float> skillsXp = new HashMap<>(); // Skill & XP private final @NotNull CooldownManager cooldownManager;
private final Map<SuperAbilityType, Integer> abilityDATS = new HashMap<>(); // Ability & Cooldown
private final Map<UniqueDataType, Integer> uniquePlayerData = new HashMap<>(); //Misc data that doesn't fit into other categories (chimaera wing, etc..)
private Map<PrimarySkillType, ExperienceBarManager.BarState> xpBarState = new HashMap<>();
// Store previous XP gains for diminished returns /**
private final DelayQueue<SkillXpGain> gainedSkillsXp = new DelayQueue<>(); * Create a new PlayerProfile for a {@link Player}
private final HashMap<PrimarySkillType, Float> rollingSkillsXp = new HashMap<>(); * @param player target player
@Deprecated
public PlayerProfile(String playerName) {
this(playerName, null);
}
public PlayerProfile(String playerName, UUID uuid) {
this.uuid = uuid;
this.playerName = playerName;
mobHealthbarType = Config.getInstance().getMobHealthbarDefault();
scoreboardTipsShown = 0;
for (SuperAbilityType superAbilityType : SuperAbilityType.values()) {
abilityDATS.put(superAbilityType, 0);
}
for (PrimarySkillType primarySkillType : PrimarySkillType.NON_CHILD_SKILLS) {
skills.put(primarySkillType, AdvancedConfig.getInstance().getStartingLevel());
skillsXp.put(primarySkillType, 0F);
}
//Misc Cooldowns
uniquePlayerData.put(UniqueDataType.CHIMAERA_WING_DATS, 0); //Chimaera wing
}
@Deprecated
public PlayerProfile(String playerName, boolean isLoaded) {
this(playerName);
this.loaded = isLoaded;
this.xpBarState = SkillUtils.generateDefaultBarStateMap();
}
public PlayerProfile(String playerName, UUID uuid, boolean isLoaded) {
this(playerName, uuid);
this.loaded = isLoaded;
this.xpBarState = SkillUtils.generateDefaultBarStateMap();
}
public PlayerProfile(String playerName, UUID uuid, Map<PrimarySkillType, Integer> levelData, Map<PrimarySkillType, Float> xpData, Map<SuperAbilityType, Integer> cooldownData, MobHealthbarType mobHealthbarType, int scoreboardTipsShown, Map<UniqueDataType, Integer> uniqueProfileData, Map<PrimarySkillType, ExperienceBarManager.BarState> barStateMap) {
this.playerName = playerName;
this.uuid = uuid;
this.mobHealthbarType = mobHealthbarType;
this.scoreboardTipsShown = scoreboardTipsShown;
skills.putAll(levelData);
skillsXp.putAll(xpData);
abilityDATS.putAll(cooldownData);
uniquePlayerData.putAll(uniqueProfileData);
xpBarState.putAll(barStateMap);
loaded = true;
}
public void scheduleAsyncSave() {
new PlayerProfileSaveTask(this, false).runTaskAsynchronously(mcMMO.p);
}
public void scheduleSyncSave() {
new PlayerProfileSaveTask(this, true).runTask(mcMMO.p);
}
public void scheduleAsyncSaveDelay() {
new PlayerProfileSaveTask(this, false).runTaskLaterAsynchronously(mcMMO.p, 20);
}
@Deprecated
public void scheduleSyncSaveDelay() {
new PlayerProfileSaveTask(this, true).runTaskLater(mcMMO.p, 20);
}
public void save(boolean useSync) {
if (!changed || !loaded) {
saveAttempts = 0;
return;
}
// TODO should this part be synchronized?
PlayerProfile profileCopy = new PlayerProfile(playerName, uuid, ImmutableMap.copyOf(skills), ImmutableMap.copyOf(skillsXp), ImmutableMap.copyOf(abilityDATS), mobHealthbarType, scoreboardTipsShown, ImmutableMap.copyOf(uniquePlayerData), ImmutableMap.copyOf(xpBarState));
changed = !mcMMO.getDatabaseManager().saveUser(profileCopy);
if (changed) {
mcMMO.p.getLogger().severe("PlayerProfile saving failed for player: " + playerName + " " + uuid);
if(saveAttempts > 0)
{
mcMMO.p.getLogger().severe("Attempted to save profile for player "+getPlayerName()
+ " resulted in failure. "+saveAttempts+" have been made so far.");
}
if(saveAttempts < 10)
{
saveAttempts++;
if(useSync)
scheduleSyncSave(); //Execute sync saves immediately
else
scheduleAsyncSaveDelay();
} else {
mcMMO.p.getLogger().severe("mcMMO has failed to save the profile for "
+getPlayerName()+" numerous times." +
" mcMMO will now stop attempting to save this profile." +
" Check your console for errors and inspect your DB for issues.");
}
} else {
saveAttempts = 0;
}
}
public String getPlayerName() {
return playerName;
}
public UUID getUniqueId() {
return uuid;
}
public void setUniqueId(UUID uuid) {
markProfileDirty();
this.uuid = uuid;
}
public boolean isLoaded() {
return loaded;
}
/*
* Mob Healthbars
*/ */
public PlayerProfile(@NotNull Player player) {
public MobHealthbarType getMobHealthbarType() { /* New Data */
return mobHealthbarType; this(player.getUniqueId(), player.getName());
}
public void setMobHealthbarType(MobHealthbarType mobHealthbarType) {
markProfileDirty();
this.mobHealthbarType = mobHealthbarType;
} }
/** /**
* Marks the profile as "dirty" which flags a profile to be saved in the next save operation * Create a new PlayerProfile for a {@link Player}
* @param playerUUID target player's UUID
* @param playerName target player's name
*/ */
public void markProfileDirty() { public PlayerProfile(@NotNull UUID playerUUID, @NotNull String playerName) {
changed = true; /* New Data */
this.persistentPlayerData = new PersistentPlayerData(playerUUID, playerName);
this.experienceManager = new ExperienceManager(persistentPlayerData);
this.cooldownManager = new CooldownManager(persistentPlayerData);
} }
/**
* Create a PlayerProfile for {@link PersistentPlayerData}
* This will be used for existing data
* @param persistentPlayerData target persistent player data
*/
public PlayerProfile(@NotNull PersistentPlayerData persistentPlayerData) {
this.persistentPlayerData = persistentPlayerData;
this.experienceManager = new ExperienceManager(persistentPlayerData);
this.cooldownManager = new CooldownManager(persistentPlayerData);
}
/**
* Get the saved player name for this profile
* @return the saved player name for this profile
*/
public @NotNull String getPlayerName() {
return getPersistentPlayerData().getPlayerName();
}
/**
* Get the saved {@link UUID} for this profile
* @return the saved {@link UUID} for this profile
*/
public @NotNull UUID getUniqueId() {
return getPersistentPlayerData().getPlayerUUID();
}
/**
* Get the current {@link MobHealthBarType} for this profile
* @return the mob health bar setting for this profile
*/
public @NotNull MobHealthBarType getMobHealthBarType() {
return getPersistentPlayerData().getMobHealthBarType();
}
/**
* Set the {@link MobHealthBarType} for this profile
* @param mobHealthbarType desired mob health bar type
*/
public void setMobHealthBarType(@NotNull MobHealthBarType mobHealthbarType) {
getPersistentPlayerData().setMobHealthBarType(mobHealthbarType);
}
/**
* The number of times scoreboard tips have been shown to this profile
* @return the scoreboard tips view count
*/
public int getScoreboardTipsShown() { public int getScoreboardTipsShown() {
return scoreboardTipsShown; return getPersistentPlayerData().getScoreboardTipsShown();
} }
/**
* Replace the scoreboard view count
* @param scoreboardTipsShown new value
*/
public void setScoreboardTipsShown(int scoreboardTipsShown) { public void setScoreboardTipsShown(int scoreboardTipsShown) {
markProfileDirty(); getPersistentPlayerData().setScoreboardTipsShown(scoreboardTipsShown);
this.scoreboardTipsShown = scoreboardTipsShown;
} }
/**
* Increments the scoreboard tip view count by 1
*/
public void increaseTipsShown() { public void increaseTipsShown() {
setScoreboardTipsShown(getScoreboardTipsShown() + 1); setScoreboardTipsShown(getScoreboardTipsShown() + 1);
} }
/*
* Cooldowns
*/
public int getChimaerWingDATS() { return uniquePlayerData.get(UniqueDataType.CHIMAERA_WING_DATS);}
protected void setChimaeraWingDATS(int DATS) {
markProfileDirty();
uniquePlayerData.put(UniqueDataType.CHIMAERA_WING_DATS, DATS);
}
public void setUniqueData(UniqueDataType uniqueDataType, int newData) {
markProfileDirty();
uniquePlayerData.put(uniqueDataType, newData);
}
public long getUniqueData(UniqueDataType uniqueDataType) { return uniquePlayerData.get(uniqueDataType); }
/** /**
* Get the current deactivation timestamp of an ability. * Retrieves a reference to the {@link PersistentPlayerData} for this profile
* * @return the persistent data for this profile
* @param ability The {@link SuperAbilityType} to get the DATS for
* @return the deactivation timestamp for the ability
*/ */
public long getAbilityDATS(SuperAbilityType ability) { public @NotNull PersistentPlayerData getPersistentPlayerData() {
return abilityDATS.get(ability); return persistentPlayerData;
} }
/** /**
* Set the current deactivation timestamp of an ability. * Invert the current value of the party chat spy toggle
*
* @param ability The {@link SuperAbilityType} to set the DATS for
* @param DATS the DATS of the ability
*/ */
public void setAbilityDATS(SuperAbilityType ability, long DATS) { public void togglePartyChatSpying() {
markProfileDirty(); persistentPlayerData.togglePartyChatSpying();
abilityDATS.put(ability, (int) (DATS * .001D));
} }
/** /**
* Reset all ability cooldowns. * Retrieve the {@link ExperienceManager} for this profile
* @return the experience manager for this profile
*/ */
public void resetCooldowns() { public @NotNull ExperienceManager getExperienceManager() {
markProfileDirty(); return experienceManager;
abilityDATS.replaceAll((a, v) -> 0);
}
/*
* Xp Functions
*/
public int getSkillLevel(PrimarySkillType skill) {
return skill.isChildSkill() ? getChildSkillLevel(skill) : skills.get(skill);
}
public float getSkillXpLevelRaw(PrimarySkillType skill) {
return skillsXp.get(skill);
}
public int getSkillXpLevel(PrimarySkillType skill) {
if(skill.isChildSkill()) {
return 0;
}
return (int) Math.floor(getSkillXpLevelRaw(skill));
}
public void setSkillXpLevel(PrimarySkillType skill, float xpLevel) {
if (skill.isChildSkill()) {
return;
}
markProfileDirty();
skillsXp.put(skill, xpLevel);
}
public float levelUp(PrimarySkillType skill) {
float xpRemoved = getXpToLevel(skill);
markProfileDirty();
skills.put(skill, skills.get(skill) + 1);
skillsXp.put(skill, skillsXp.get(skill) - xpRemoved);
return xpRemoved;
} }
/** /**
* Remove Xp from a skill. * Retrieve the {@link CooldownManager} for this profile
* * @return the cooldown manager for this profile
* @param skill Type of skill to modify
* @param xp Amount of xp to remove
*/ */
public void removeXp(PrimarySkillType skill, int xp) { public @NotNull CooldownManager getCooldownManager() {
if (skill.isChildSkill()) { return cooldownManager;
return;
}
markProfileDirty();
skillsXp.put(skill, skillsXp.get(skill) - xp);
}
public void removeXp(PrimarySkillType skill, float xp) {
if (skill.isChildSkill()) {
return;
}
markProfileDirty();
skillsXp.put(skill, skillsXp.get(skill) - xp);
}
/**
* Modify a skill level.
*
* @param skill Type of skill to modify
* @param level New level value for the skill
*/
public void modifySkill(PrimarySkillType skill, int level) {
if (skill.isChildSkill()) {
return;
}
markProfileDirty();
//Don't allow levels to be negative
if(level < 0)
level = 0;
skills.put(skill, level);
skillsXp.put(skill, 0F);
}
/**
* Add levels to a skill.
*
* @param skill Type of skill to add levels to
* @param levels Number of levels to add
*/
public void addLevels(PrimarySkillType skill, int levels) {
modifySkill(skill, skills.get(skill) + levels);
}
/**
* Add Experience to a skill.
*
* @param skill Type of skill to add experience to
* @param xp Number of experience to add
*/
public void addXp(PrimarySkillType skill, float xp) {
markProfileDirty();
if (skill.isChildSkill()) {
Set<PrimarySkillType> parentSkills = FamilyTree.getParents(skill);
float dividedXP = (xp / parentSkills.size());
for (PrimarySkillType parentSkill : parentSkills) {
skillsXp.put(parentSkill, skillsXp.get(parentSkill) + dividedXP);
}
}
else {
skillsXp.put(skill, skillsXp.get(skill) + xp);
}
}
/**
* Get the registered amount of experience gained
* This is used for diminished XP returns
*
* @return xp Experience amount registered
*/
public float getRegisteredXpGain(PrimarySkillType primarySkillType) {
float xp = 0F;
if (rollingSkillsXp.get(primarySkillType) != null) {
xp = rollingSkillsXp.get(primarySkillType);
}
return xp;
}
/**
* Register an experience gain
* This is used for diminished XP returns
*
* @param primarySkillType Skill being used
* @param xp Experience amount to add
*/
public void registerXpGain(PrimarySkillType primarySkillType, float xp) {
gainedSkillsXp.add(new SkillXpGain(primarySkillType, xp));
rollingSkillsXp.put(primarySkillType, getRegisteredXpGain(primarySkillType) + xp);
}
/**
* Remove experience gains older than a given time
* This is used for diminished XP returns
*/
public void purgeExpiredXpGains() {
SkillXpGain gain;
while ((gain = gainedSkillsXp.poll()) != null) {
rollingSkillsXp.put(gain.getSkill(), getRegisteredXpGain(gain.getSkill()) - gain.getXp());
}
}
/**
* Get the amount of Xp remaining before the next level.
*
* @param primarySkillType Type of skill to check
* @return the total amount of Xp until next level
*/
public int getXpToLevel(PrimarySkillType primarySkillType) {
if(primarySkillType.isChildSkill()) {
return 0;
}
int level = (ExperienceConfig.getInstance().getCumulativeCurveEnabled()) ? UserManager.getPlayer(playerName).getPowerLevel() : skills.get(primarySkillType);
FormulaType formulaType = ExperienceConfig.getInstance().getFormulaType();
return mcMMO.getFormulaManager().getXPtoNextLevel(level, formulaType);
}
private int getChildSkillLevel(PrimarySkillType primarySkillType) {
Set<PrimarySkillType> parents = FamilyTree.getParents(primarySkillType);
int sum = 0;
for (PrimarySkillType parent : parents) {
sum += Math.min(getSkillLevel(parent), parent.getMaxLevel());
}
return sum / parents.size();
}
public HashMap<PrimarySkillType, ExperienceBarManager.BarState> getXpBarStateMap() {
return (HashMap<PrimarySkillType, ExperienceBarManager.BarState>) xpBarState;
} }
} }