force-class-selection fixes

This commit is contained in:
Jules 2023-05-14 16:34:43 +02:00
parent e939b6e868
commit 651e46697e
14 changed files with 213 additions and 185 deletions

View File

@ -141,7 +141,7 @@
<dependency>
<groupId>fr.phoenixdevt</groupId>
<artifactId>MMOProfiles-Dist</artifactId>
<artifactId>Profile-API</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>provided</scope>
<optional>true</optional>

View File

@ -29,13 +29,13 @@ import net.Indyuce.mmocore.guild.GuildModuleType;
import net.Indyuce.mmocore.guild.GuildRelationHandler;
import net.Indyuce.mmocore.guild.provided.Guild;
import net.Indyuce.mmocore.guild.provided.MMOCoreGuildModule;
import net.Indyuce.mmocore.guild.provided.YAMLGuildDataManager;
import net.Indyuce.mmocore.manager.*;
import net.Indyuce.mmocore.manager.data.DataProvider;
import net.Indyuce.mmocore.manager.data.GuildDataManager;
import net.Indyuce.mmocore.manager.data.LegacyDataProvider;
import net.Indyuce.mmocore.manager.data.PlayerDataManager;
import net.Indyuce.mmocore.manager.data.sql.SQLDataHandler;
import net.Indyuce.mmocore.guild.provided.YAMLGuildDataManager;
import net.Indyuce.mmocore.manager.profession.*;
import net.Indyuce.mmocore.manager.social.BoosterManager;
import net.Indyuce.mmocore.manager.social.PartyManager;
@ -258,8 +258,7 @@ public class MMOCore extends JavaPlugin {
* that after registering all the professses otherwise the player datas can't
* recognize what profess the player has and professes will be lost
*/
playerDataManager.setupAll();
playerDataManager.registerEvents(EventPriority.NORMAL, EventPriority.NORMAL);
playerDataManager.initialize(EventPriority.NORMAL, EventPriority.NORMAL);
// load guild data after loading player data
dataProvider.getGuildManager().load();
@ -271,25 +270,6 @@ public class MMOCore extends JavaPlugin {
MMOCoreCommandTreeRoot mmoCoreCommand = new MMOCoreCommandTreeRoot();
getCommand("mmocore").setExecutor(mmoCoreCommand);
getCommand("mmocore").setTabCompleter(mmoCoreCommand);
if (getConfig().getBoolean("auto-save.enabled"))
{
int autosave = getConfig().getInt("auto-save.interval") * 20;
new BukkitRunnable() {
public void run() {
// Save player data
for (PlayerData data : PlayerData.getAll())
if (data.isSynchronized())
dataProvider.getDataManager().getDataHandler().saveData(data, false);
// Save guild info
for (Guild guild : dataProvider.getGuildManager().getAll())
dataProvider.getGuildManager().save(guild);
}
}.runTaskTimerAsynchronously(MMOCore.plugin, autosave, autosave);
}
}
@Override
@ -306,8 +286,6 @@ public class MMOCore extends JavaPlugin {
for (PlayerData data : PlayerData.getAll())
if (data.isSynchronized()) {
data.close();
// Saves player health before saveData as the player will be considered offline into it if it is async.
data.setHealth(data.getPlayer().getHealth());
dataProvider.getDataManager().getDataHandler().saveData(data, true);
}

View File

@ -427,6 +427,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
@Override
public void close() {
// Saves player health before saveData as the player will be considered offline into it if it is async
health = getPlayer().getHealth();
// Remove from party if it is MMO Party Module

View File

@ -1,17 +1,26 @@
package net.Indyuce.mmocore.comp.profile;
import fr.phoenixdevt.profile.ProfileDataModule;
import fr.phoenixdevt.profile.event.ProfileCreateEvent;
import fr.phoenixdevt.profile.event.ProfileDeleteEvent;
import fr.phoenixdevt.profile.placeholder.PlaceholderRequest;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.api.player.MMOPlayerData;
import io.lumine.mythic.lib.comp.profile.ProfileDataModuleImpl;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.player.attribute.PlayerAttribute;
import net.Indyuce.mmocore.experience.Profession;
import net.Indyuce.mmocore.manager.InventoryManager;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;
public class MMOCoreProfileDataModule extends ProfileDataModuleImpl {
public MMOCoreProfileDataModule() {
super(MMOCore.plugin);
public class MMOCoreProfileDataModule implements ProfileDataModule, Listener {
@Override
public JavaPlugin getOwningPlugin() {
return MMOCore.plugin;
}
@Override
@ -43,4 +52,22 @@ public class MMOCoreProfileDataModule extends ProfileDataModuleImpl {
placeholderRequest.validate();
});
}
@EventHandler
public void onProfileCreate(ProfileCreateEvent event) {
// Force to choose class first
if (MMOCore.plugin.configManager.forceClassSelection) {
final PlayerData playerData = PlayerData.get(event.getPlayerData().getUniqueId());
InventoryManager.CLASS_SELECT.newInventory(playerData, () -> event.validate(this)).open();
}
// Validate event directly
else event.validate(this);
}
@EventHandler
public void onProfileDelete(ProfileDeleteEvent event) {
event.validate(this);
}
}

View File

@ -20,8 +20,11 @@ import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.inventory.ItemStack;
import javax.annotation.Nullable;
public class ClassConfirmation extends EditableInventory {
private final PlayerClass playerClass;
@ -37,7 +40,11 @@ public class ClassConfirmation extends EditableInventory {
}
public GeneratedInventory newInventory(PlayerData data, PluginInventory last, boolean subclass) {
return new ClassConfirmationInventory(data, this, playerClass, last, subclass);
return newInventory(data, last, subclass, null);
}
public GeneratedInventory newInventory(PlayerData data, PluginInventory last, boolean subclass, @Nullable Runnable profileRunnable) {
return new ClassConfirmationInventory(data, this, playerClass, last, subclass, profileRunnable);
}
public class UnlockedItem extends InventoryItem<ClassConfirmationInventory> {
@ -105,26 +112,33 @@ public class ClassConfirmation extends EditableInventory {
private final PluginInventory last;
private final boolean subclass;
public ClassConfirmationInventory(PlayerData playerData, EditableInventory editable, PlayerClass profess, PluginInventory last, boolean subclass) {
@Nullable
private final Runnable profileRunnable;
private boolean canClose;
public ClassConfirmationInventory(PlayerData playerData, EditableInventory editable, PlayerClass profess, PluginInventory last, boolean subclass, @Nullable Runnable profileRunnable) {
super(playerData, editable);
this.profess = profess;
this.last = last;
this.subclass = subclass;
this.profileRunnable = profileRunnable;
}
@Override
public void whenClicked(InventoryClickContext context, InventoryItem item) {
if (item.getFunction().equals("back"))
if (item.getFunction().equals("back")) {
canClose = true;
last.open();
else if (item.getFunction().equals("yes")) {
} else if (item.getFunction().equals("yes")) {
PlayerChangeClassEvent called = new PlayerChangeClassEvent(playerData, profess);
Bukkit.getPluginManager().callEvent(called);
if (called.isCancelled())
return;
canClose = true;
playerData.giveClassPoints(-1);
if (subclass)
playerData.setClass(profess);
@ -134,9 +148,22 @@ public class ClassConfirmation extends EditableInventory {
MMOCore.plugin.configManager.getSimpleMessage("class-select", "class", profess.getName()).send(player);
MMOCore.plugin.soundManager.getSound(SoundEvent.SELECT_CLASS).playTo(player);
player.closeInventory();
if (profileRunnable != null) profileRunnable.run();
}
}
@Override
public void open() {
canClose = false;
super.open();
}
@Override
public void whenClosed(InventoryCloseEvent event) {
if (profileRunnable != null && !canClose)
Bukkit.getScheduler().runTaskLater(MMOCore.plugin, () -> open(), 2 * 20);
}
@Override
public String calculateName() {
return getName().replace("{class}", profess.getName());

View File

@ -16,14 +16,17 @@ import net.Indyuce.mmocore.gui.api.item.InventoryItem;
import net.Indyuce.mmocore.gui.api.item.SimplePlaceholderItem;
import net.Indyuce.mmocore.manager.InventoryManager;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@ -41,7 +44,11 @@ public class ClassSelect extends EditableInventory {
}
public GeneratedInventory newInventory(PlayerData data) {
return new ProfessSelectionInventory(data, this);
return newInventory(data, null);
}
public GeneratedInventory newInventory(PlayerData data, @Nullable Runnable profileRunnable) {
return new ProfessSelectionInventory(data, this, profileRunnable);
}
public class ClassItem extends SimplePlaceholderItem<ProfessSelectionInventory> {
@ -51,9 +58,10 @@ public class ClassSelect extends EditableInventory {
public ClassItem(ConfigurationSection config) {
super(Material.BARRIER, config);
Validate.isTrue(config.getString("function").length()>6,"Couldn't find the class associated to: "+config.getString("function"));
Validate.isTrue(config.getString("function").length() > 6, "Couldn't find the class associated to: " + config.getString("function"));
String classId = UtilityMethods.enumName(config.getString("function").substring(6));
this.playerClass = Objects.requireNonNull(MMOCore.plugin.classManager.get(classId),classId+" does not correspond to any classId.");
this.playerClass = Objects.requireNonNull(MMOCore.plugin.classManager.get(classId), classId + " does not correspond to any classId.");
this.name = config.getString("name");
this.lore = config.getStringList("lore");
}
@ -93,8 +101,16 @@ public class ClassSelect extends EditableInventory {
}
public class ProfessSelectionInventory extends GeneratedInventory {
public ProfessSelectionInventory(PlayerData playerData, EditableInventory editable) {
@Nullable
private final Runnable profileRunnable;
private boolean canClose;
public ProfessSelectionInventory(PlayerData playerData, EditableInventory editable, @Nullable Runnable profileRunnable) {
super(playerData, editable);
this.profileRunnable = profileRunnable;
}
@Override
@ -107,7 +123,7 @@ public class ClassSelect extends EditableInventory {
if (item instanceof ClassItem) {
PlayerClass profess = ((ClassItem) item).playerClass;
if (playerData.getClassPoints() < 1) {
if (profileRunnable == null && playerData.getClassPoints() < 1) {
MMOCore.plugin.soundManager.getSound(SoundEvent.CANT_SELECT_CLASS).playTo(player);
new ConfigMessage("cant-choose-new-class").send(player);
return;
@ -125,10 +141,23 @@ public class ClassSelect extends EditableInventory {
return;
}
canClose = true;
final PlayerClass playerClass = findDeepestSubclass(playerData, profess);
InventoryManager.CLASS_CONFIRM.get(MMOCoreUtils.ymlName(playerClass.getId())).newInventory(playerData, this, false).open();
InventoryManager.CLASS_CONFIRM.get(MMOCoreUtils.ymlName(playerClass.getId())).newInventory(playerData, this, false, profileRunnable).open();
}
}
@Override
public void open() {
canClose = false;
super.open();
}
@Override
public void whenClosed(InventoryCloseEvent event) {
if (profileRunnable != null && !canClose)
Bukkit.getScheduler().runTaskLater(MMOCore.plugin, () -> open(), 2 * 20);
}
}
/**

View File

@ -126,7 +126,7 @@ public class SubclassSelect extends EditableInventory {
return;
}
InventoryManager.CLASS_CONFIRM.get(classId).newInventory(playerData, this, true).open();
InventoryManager.CLASS_CONFIRM.get(classId).newInventory(playerData, this, true, null).open();
}
}
}

View File

@ -22,6 +22,7 @@ public abstract class GeneratedInventory extends PluginInventory {
public GeneratedInventory(PlayerData playerData, EditableInventory editable) {
super(playerData);
this.editable = editable;
this.adaptor = editable.getAdaptorType().supply(this);
}
@ -66,6 +67,8 @@ public abstract class GeneratedInventory extends PluginInventory {
@Override
public void open() {
if (!getPlayerData().isOnline()) return;
/*
* Very important, in order to prevent ghost items, the loaded items map
* must be cleared when the inventory is updated or open at least twice

View File

@ -28,7 +28,7 @@ public class ConfigManager {
public final CommandVerbose commandVerbose = new CommandVerbose();
public boolean overrideVanillaExp, canCreativeCast, passiveSkillNeedBound, cobbleGeneratorXP, saveDefaultClassInfo, splitMainExp, splitProfessionExp, disableQuestBossBar,
pvpModeEnabled, pvpModeInvulnerabilityCanDamage;
pvpModeEnabled, pvpModeInvulnerabilityCanDamage, forceClassSelection;
public String partyChatPrefix, noSkillBoundPlaceholder;
public ChatColor staminaFull, staminaHalf, staminaEmpty;
public long combatLogTimer, lootChestExpireTime, lootChestPlayerCooldown, globalSkillCooldown;
@ -133,6 +133,7 @@ public class ConfigManager {
splitMainExp = MMOCore.plugin.getConfig().getBoolean("party.main-exp-split");
splitProfessionExp = MMOCore.plugin.getConfig().getBoolean("party.profession-exp-split");
disableQuestBossBar = MMOCore.plugin.getConfig().getBoolean("mmocore-quests.disable-boss-bar");
forceClassSelection = MMOCore.plugin.getConfig().getBoolean("force-class-selection");
// Combat
pvpModeEnabled = config.getBoolean("pvp_mode.enabled");

View File

@ -14,117 +14,121 @@ import net.Indyuce.mmocore.guild.provided.Guild;
import javax.annotation.Nullable;
public abstract class GuildDataManager {
protected final Map<String, Guild> guilds = new HashMap<>();
protected final Map<String, Guild> guilds = new HashMap<>();
public Guild newRegisteredGuild(UUID owner, String name, String tag) {
Guild guild = new Guild(owner, name, tag);
registerGuild(guild);
return guild;
}
public Guild newRegisteredGuild(UUID owner, String name, String tag) {
Guild guild = new Guild(owner, name, tag);
registerGuild(guild);
return guild;
}
public void registerGuild(Guild guild) {
guilds.put(guild.getId(), guild);
}
public void registerGuild(Guild guild) {
guilds.put(guild.getId(), guild);
}
public boolean isRegistered(Guild guild) {
return guilds.containsValue(guild);
}
public boolean isRegistered(Guild guild) {
return guilds.containsValue(guild);
}
public boolean isRegistered(String tag) {
return guilds.containsKey(tag);
}
public boolean isRegistered(String tag) {
return guilds.containsKey(tag);
}
public void unregisterGuild(Guild guild) {
guild.forEachMember(member -> guild.removeMember(member, true));
// guild.getMembers().clear();
guilds.remove(guild.getId());
delete(guild);
}
public void unregisterGuild(Guild guild) {
guild.forEachMember(member -> guild.removeMember(member, true));
// guild.getMembers().clear();
guilds.remove(guild.getId());
delete(guild);
}
@Nullable
public Guild getGuild(String guild) {
return guilds.get(guild);
}
public void saveAll() {
for (Guild guild : guilds.values()) save(guild);
}
public Collection<Guild> getAll() {
return guilds.values();
}
@Nullable
public Guild getGuild(String guild) {
return guilds.get(guild);
}
@Deprecated
public void reload() {
for (Guild guild : guilds.values())
save(guild);
guilds.clear();
load();
config = new GuildConfiguration();
}
public Collection<Guild> getAll() {
return guilds.values();
}
public abstract void save(Guild guild);
@Deprecated
public void reload() {
for (Guild guild : guilds.values())
save(guild);
guilds.clear();
load();
config = new GuildConfiguration();
}
// TODO move to constructor, useless to handle vie abstract method
public abstract void load();
public abstract void save(Guild guild);
public abstract void delete(Guild guild);
// TODO move to constructor, useless to handle vie abstract method
public abstract void load();
// TODO fix this
// Shitty code for loading config values for guilds.
private GuildConfiguration config;
public abstract void delete(Guild guild);
public GuildConfiguration getConfig() {
return config == null ? config = new GuildConfiguration() : config;
}
// TODO fix this
// Shitty code for loading config values for guilds.
private GuildConfiguration config;
public static class GuildConfiguration {
private final String prefix;
private final boolean uppercaseTags;
private final NamingRules tagRules, nameRules;
public GuildConfiguration getConfig() {
return config == null ? config = new GuildConfiguration() : config;
}
public GuildConfiguration() {
FileConfiguration config = new ConfigFile("guilds").getConfig();
public static class GuildConfiguration {
private final String prefix;
private final boolean uppercaseTags;
private final NamingRules tagRules, nameRules;
this.prefix = config.getString("chat-prefix", "*");
this.uppercaseTags = config.getBoolean("uppercase-tags", true);
this.tagRules = new NamingRules(config.getConfigurationSection("rules.tag"));
this.nameRules = new NamingRules(config.getConfigurationSection("rules.name"));
}
public GuildConfiguration() {
FileConfiguration config = new ConfigFile("guilds").getConfig();
public String getPrefix() {
return prefix;
}
this.prefix = config.getString("chat-prefix", "*");
this.uppercaseTags = config.getBoolean("uppercase-tags", true);
this.tagRules = new NamingRules(config.getConfigurationSection("rules.tag"));
this.nameRules = new NamingRules(config.getConfigurationSection("rules.name"));
}
public boolean shouldUppercaseTags() {
return uppercaseTags;
}
public String getPrefix() {
return prefix;
}
public NamingRules getTagRules() {
return tagRules;
}
public boolean shouldUppercaseTags() {
return uppercaseTags;
}
public NamingRules getNameRules() {
return nameRules;
}
public NamingRules getTagRules() {
return tagRules;
}
public static class NamingRules {
private final String regex;
private final int min, max;
public NamingRules getNameRules() {
return nameRules;
}
public NamingRules(ConfigurationSection config) {
regex = config.getString("matches", "[a-zA-Z-_!?]+");
min = config.getInt("min-length", 3);
max = config.getInt("max-length", 5);
}
public static class NamingRules {
private final String regex;
private final int min, max;
public String getRegex() {
return regex;
}
public NamingRules(ConfigurationSection config) {
regex = config.getString("matches", "[a-zA-Z-_!?]+");
min = config.getInt("min-length", 3);
max = config.getInt("max-length", 5);
}
public int getMin() {
return min;
}
public String getRegex() {
return regex;
}
public int getMax() {
return max;
}
}
}
public int getMin() {
return min;
}
public int getMax() {
return max;
}
}
}
}

View File

@ -33,4 +33,9 @@ public class PlayerDataManager extends SynchronizedDataManager<PlayerData, Offli
public void loadDefaultData(ConfigurationSection config) {
defaultData = new DefaultPlayerData(config);
}
@Override
public void whenAutoSaved() {
MMOCore.plugin.nativeGuildManager.saveAll();
}
}

View File

@ -38,10 +38,6 @@ public class MMOCoreBukkit {
if (plugin.getConfig().getBoolean("vanilla-exp-redirection.enabled"))
Bukkit.getPluginManager().registerEvents(new RedirectVanillaExp(plugin.getConfig().getDouble("vanilla-exp-redirection.ratio")), plugin);
if (plugin.getConfig().getBoolean("force-class-choose-on-first-login"))
Bukkit.getPluginManager().registerEvents(new ForceChooseClassListener(), MMOCore.plugin);
Bukkit.getPluginManager().registerEvents(new WaypointsListener(), plugin);
Bukkit.getPluginManager().registerEvents(new PlayerListener(), plugin);
Bukkit.getPluginManager().registerEvents(new GoldPouchesListener(), plugin);

View File

@ -1,47 +0,0 @@
package net.Indyuce.mmocore.listener.option;
import io.lumine.mythic.lib.api.event.SynchronizedDataLoadEvent;
import io.lumine.mythic.lib.api.util.TemporaryListener;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.event.PlayerChangeClassEvent;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.manager.InventoryManager;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryCloseEvent;
public class ForceChooseClassListener implements Listener {
@EventHandler
public void onJoin(SynchronizedDataLoadEvent event) {
if (event.getManager().getOwningPlugin().equals(MMOCore.plugin)) {
PlayerData playerData = PlayerData.get(event.getHolder().getProfileId());
if (playerData.isProfessNull()) {
// Open GUI
InventoryManager.CLASS_SELECT.newInventory(playerData).open();
// Re-open GUI till the player
new TemporaryListener(PlayerChangeClassEvent.getHandlerList(), InventoryCloseEvent.getHandlerList()) {
@EventHandler
public void whenClosed(InventoryCloseEvent event) {
if (event.getPlayer().equals(playerData.getPlayer()))
InventoryManager.CLASS_SELECT.newInventory(playerData).open();
}
@EventHandler
public void whenChoose(PlayerChangeClassEvent event) {
if (event.getPlayer().equals(playerData.getPlayer())) close();
}
@Override
public void whenClosed() {
}
};
}
}
}
}

View File

@ -193,10 +193,14 @@ vanilla-exp-redirection:
# Requires a SERVER reload when changed.
override-vanilla-exp: true
# Forces the player to choose a class at first join.
force-class-choose-on-first-login: false
# When enabled and when using a profile plugin, MMOCore will
# force the user to choose a class. The profile will not be
# created until they choose a class.
#
# This option is useless unless you have installed a profile plugin.
force-class-selection: true
# Check the target player's RPG profile when shift when shift right clicking.
# Check the target player's RPG profile on shift-right click.
shift-click-player-profile-check: false
# If main class experience holograms should be displayed
@ -215,7 +219,7 @@ max-skill-slots: 8
# When set to true, passive skills must be bound in order to take effect.
# When set to false, unlocked skills will take effect right away.
# Temporarily disabled.
# Disabled for now.
# passive-skill-need-bound: true
# Fun extra RPG feature that switches the player's hotbar with