Merge upstream into fixed kotlin script changes.

Signed-off-by: Gabriel Harris-Rouquette <gabizou@me.com>
This commit is contained in:
Gabriel Harris-Rouquette
2019-02-16 18:58:54 -08:00
503 changed files with 12353 additions and 11568 deletions

View File

@ -11,6 +11,7 @@ dependencies {
exclude(Deps.Groups.guava, Deps.Modules.guava)
exclude(Deps.Groups.checker, Deps.Modules.checker)
}
compile(Libs.flowmath)
compile(Libs.jdbc)
compile(Libs.juli)
testCompile(Libs.junitDep)

View File

@ -1,5 +0,0 @@
package com.gmail.nossr50;
public class McmmoCore {
}

View File

@ -0,0 +1,73 @@
package com.gmail.nossr50.core;
import com.gmail.nossr50.core.data.database.DatabaseManager;
import com.gmail.nossr50.core.mcmmo.event.EventCommander;
import com.gmail.nossr50.core.mcmmo.plugin.Plugin;
import com.gmail.nossr50.core.mcmmo.server.Server;
import com.gmail.nossr50.core.mcmmo.tasks.TaskScheduler;
import com.gmail.nossr50.core.platform.Platform;
import com.gmail.nossr50.core.util.ModManager;
import com.gmail.nossr50.core.util.experience.FormulaManager;
import com.gmail.nossr50.core.util.upgrade.UpgradeManager;
import java.io.File;
import java.util.logging.Logger;
public class McmmoCore {
//TODO: Wire all this stuff
public static Plugin p;
private static EventCommander eventCommander;
private static Logger logger;
private static Platform platform;
private static boolean retroModeEnabled;
//Why do all these things need to be here? Sigh...
private static DatabaseManager databaseManager;
private static UpgradeManager upgradeManager; //TODO: I can't even remember what this one did
private static FormulaManager formulaManager;
private static ModManager modManager; //TODO: Probably need to rewrite this
/**
* Returns our Logger
* @return the logger
*/
public static Logger getLogger()
{
return logger;
}
public static EventCommander getEventCommander() {
return eventCommander;
}
public static Server getServer() {
return platform.getServer();
}
public static TaskScheduler getTaskScheduler()
{
return platform.getScheduler();
}
public static java.io.InputStream getResource(String path)
{
return platform.getResource(path);
}
public static File getDataFolderPath()
{
return platform.getDataFolderPath();
}
public static DatabaseManager getDatabaseManager() { return databaseManager; }
public static UpgradeManager getUpgradeManager() { return upgradeManager; }
public static FormulaManager getFormulaManager() { return formulaManager; }
public static boolean isRetroModeEnabled() { return retroModeEnabled; }
public static ModManager getModManager() { return modManager; }
public static String getModDataFolderPath() { return platform.getModDataFolderPath(); }
}

View File

@ -0,0 +1,34 @@
This file is just going to take note of all the caveats of mcMMO code as I abstract out bukkit
1) In several places mcMMO is disabling itself, pretty sure this is not a good idea and this should be changed
eg: in McMMOPlayer, and when loading configs and finding bad values
2) mcMMO uses a a global reference of its Plugin class for Bukkit in order to schedule tasks
3) Need to configure the logger
4) Safety check the hardcore/vampirism commands
5) Tweak configs to not do any string operations
6) Need to add and check validation for all current configs
7) Weird stuff going on with ageables and strings in general in StringUtils
8) Reduce the amount of string operations in mcMMO as much as possible
////////////
CONFIG NOTES
///////////
1) All Config files need their validation redone and checked
2) All Config files need unload programmed in
3) All Config files need string operations reduced and to be double checked for errors in paths
4) Need to setup removing old keys on configs

View File

@ -0,0 +1,89 @@
package com.gmail.nossr50.core.api;
import com.gmail.nossr50.core.data.UserManager;
import com.gmail.nossr50.core.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.core.mcmmo.entity.Living;
import com.gmail.nossr50.core.mcmmo.entity.Player;
import com.gmail.nossr50.core.runnables.skills.BleedTimerTask;
import com.gmail.nossr50.core.skills.SuperAbilityType;
public final class AbilityAPI {
private AbilityAPI() {
}
public static boolean berserkEnabled(Player player) {
return UserManager.getPlayer(player).getAbilityMode(SuperAbilityType.BERSERK);
}
public static boolean gigaDrillBreakerEnabled(Player player) {
return UserManager.getPlayer(player).getAbilityMode(SuperAbilityType.GIGA_DRILL_BREAKER);
}
public static boolean greenTerraEnabled(Player player) {
return UserManager.getPlayer(player).getAbilityMode(SuperAbilityType.GREEN_TERRA);
}
public static boolean serratedStrikesEnabled(Player player) {
return UserManager.getPlayer(player).getAbilityMode(SuperAbilityType.SERRATED_STRIKES);
}
public static boolean skullSplitterEnabled(Player player) {
return UserManager.getPlayer(player).getAbilityMode(SuperAbilityType.SKULL_SPLITTER);
}
public static boolean superBreakerEnabled(Player player) {
return UserManager.getPlayer(player).getAbilityMode(SuperAbilityType.SUPER_BREAKER);
}
public static boolean treeFellerEnabled(Player player) {
return UserManager.getPlayer(player).getAbilityMode(SuperAbilityType.TREE_FELLER);
}
public static boolean isAnyAbilityEnabled(Player player) {
McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
for (SuperAbilityType ability : SuperAbilityType.values()) {
if (mcMMOPlayer.getAbilityMode(ability)) {
return true;
}
}
return false;
}
public static void resetCooldowns(Player player) {
UserManager.getPlayer(player).resetCooldowns();
}
public static void setBerserkCooldown(Player player, long cooldown) {
UserManager.getPlayer(player).setAbilityDATS(SuperAbilityType.BERSERK, cooldown);
}
public static void setGigaDrillBreakerCooldown(Player player, long cooldown) {
UserManager.getPlayer(player).setAbilityDATS(SuperAbilityType.GIGA_DRILL_BREAKER, cooldown);
}
public static void setGreenTerraCooldown(Player player, long cooldown) {
UserManager.getPlayer(player).setAbilityDATS(SuperAbilityType.GREEN_TERRA, cooldown);
}
public static void setSerratedStrikesCooldown(Player player, long cooldown) {
UserManager.getPlayer(player).setAbilityDATS(SuperAbilityType.SERRATED_STRIKES, cooldown);
}
public static void setSkullSplitterCooldown(Player player, long cooldown) {
UserManager.getPlayer(player).setAbilityDATS(SuperAbilityType.SKULL_SPLITTER, cooldown);
}
public static void setSuperBreakerCooldown(Player player, long cooldown) {
UserManager.getPlayer(player).setAbilityDATS(SuperAbilityType.SUPER_BREAKER, cooldown);
}
public static void setTreeFellerCooldown(Player player, long cooldown) {
UserManager.getPlayer(player).setAbilityDATS(SuperAbilityType.TREE_FELLER, cooldown);
}
public static boolean isBleeding(Living entity) {
return BleedTimerTask.isBleeding(entity);
}
}

View File

@ -0,0 +1,153 @@
package com.gmail.nossr50.core.api;
import com.gmail.nossr50.core.chat.ChatManager;
import com.gmail.nossr50.core.chat.ChatManagerFactory;
import com.gmail.nossr50.core.chat.PartyChatManager;
import com.gmail.nossr50.core.data.UserManager;
import com.gmail.nossr50.core.datatypes.chat.ChatMode;
import com.gmail.nossr50.core.mcmmo.entity.Player;
import com.gmail.nossr50.core.party.PartyManager;
public final class ChatAPI {
private ChatAPI() {
}
/**
* Send a message to all members of a party
* </br>
* This function is designed for API usage.
*
* @param plugin The plugin sending the message
* @param sender The name of the sender
* @param displayName The display name of the sender
* @param party The name of the party to send to
* @param message The message to send
*/
public static void sendPartyChat(Plugin plugin, String sender, String displayName, String party, String message) {
getPartyChatManager(plugin, party).handleChat(sender, displayName, message);
}
/**
* Send a message to all members of a party
* </br>
* This function is designed for API usage.
*
* @param plugin The plugin sending the message
* @param sender The name of the sender to display in the chat
* @param party The name of the party to send to
* @param message The message to send
*/
public static void sendPartyChat(Plugin plugin, String sender, String party, String message) {
getPartyChatManager(plugin, party).handleChat(sender, message);
}
/**
* Send a message to administrators
* </br>
* This function is designed for API usage.
*
* @param plugin The plugin sending the message
* @param sender The name of the sender
* @param displayName The display name of the sender
* @param message The message to send
*/
public static void sendAdminChat(Plugin plugin, String sender, String displayName, String message) {
ChatManagerFactory.getChatManager(plugin, ChatMode.ADMIN).handleChat(sender, displayName, message);
}
/**
* Send a message to administrators
* </br>
* This function is designed for API usage.
*
* @param plugin The plugin sending the message
* @param sender The name of the sender to display in the chat
* @param message The message to send
*/
public static void sendAdminChat(Plugin plugin, String sender, String message) {
ChatManagerFactory.getChatManager(plugin, ChatMode.ADMIN).handleChat(sender, message);
}
/**
* Check if a player is currently talking in party chat.
*
* @param player The player to check
* @return true if the player is using party chat, false otherwise
*/
public static boolean isUsingPartyChat(Player player) {
return UserManager.getPlayer(player).isChatEnabled(ChatMode.PARTY);
}
/**
* Check if a player is currently talking in party chat.
*
* @param playerName The name of the player to check
* @return true if the player is using party chat, false otherwise
*/
public static boolean isUsingPartyChat(String playerName) {
return UserManager.getPlayer(playerName).isChatEnabled(ChatMode.PARTY);
}
/**
* Check if a player is currently talking in admin chat.
*
* @param player The player to check
* @return true if the player is using admin chat, false otherwise
*/
public static boolean isUsingAdminChat(Player player) {
return UserManager.getPlayer(player).isChatEnabled(ChatMode.ADMIN);
}
/**
* Check if a player is currently talking in admin chat.
*
* @param playerName The name of the player to check
* @return true if the player is using admin chat, false otherwise
*/
public static boolean isUsingAdminChat(String playerName) {
return UserManager.getPlayer(playerName).isChatEnabled(ChatMode.ADMIN);
}
/**
* Toggle the party chat mode of a player.
*
* @param player The player to toggle party chat on.
*/
public static void togglePartyChat(Player player) {
UserManager.getPlayer(player).toggleChat(ChatMode.PARTY);
}
/**
* Toggle the party chat mode of a player.
*
* @param playerName The name of the player to toggle party chat on.
*/
public static void togglePartyChat(String playerName) {
UserManager.getPlayer(playerName).toggleChat(ChatMode.PARTY);
}
/**
* Toggle the admin chat mode of a player.
*
* @param player The player to toggle admin chat on.
*/
public static void toggleAdminChat(Player player) {
UserManager.getPlayer(player).toggleChat(ChatMode.ADMIN);
}
/**
* Toggle the admin chat mode of a player.
*
* @param playerName The name of the player to toggle party chat on.
*/
public static void toggleAdminChat(String playerName) {
UserManager.getPlayer(playerName).toggleChat(ChatMode.ADMIN);
}
private static ChatManager getPartyChatManager(Plugin plugin, String party) {
ChatManager chatManager = ChatManagerFactory.getChatManager(plugin, ChatMode.PARTY);
((PartyChatManager) chatManager).setParty(PartyManager.getParty(party));
return chatManager;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,241 @@
package com.gmail.nossr50.core.api;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.config.MainConfig;
import com.gmail.nossr50.core.data.UserManager;
import com.gmail.nossr50.core.datatypes.interactions.NotificationType;
import com.gmail.nossr50.core.datatypes.party.Party;
import com.gmail.nossr50.core.datatypes.party.PartyLeader;
import com.gmail.nossr50.core.mcmmo.entity.Player;
import com.gmail.nossr50.core.party.PartyManager;
import com.gmail.nossr50.core.util.player.NotificationManager;
import java.util.*;
public final class PartyAPI {
private PartyAPI() {
}
/**
* Get the name of the party a player is in.
* </br>
* This function is designed for API usage.
*
* @param player The player to check the party name of
* @return the name of the player's party, or null if not in a party
*/
public static String getPartyName(Player player) {
if (!inParty(player)) {
return null;
}
return UserManager.getPlayer(player).getParty().getName();
}
/**
* Checks if a player is in a party.
* </br>
* This function is designed for API usage.
*
* @param player The player to check
* @return true if the player is in a party, false otherwise
*/
public static boolean inParty(Player player) {
return UserManager.getPlayer(player).inParty();
}
/**
* Check if two players are in the same party.
* </br>
* This function is designed for API usage.
*
* @param playera The first player to check
* @param playerb The second player to check
* @return true if the two players are in the same party, false otherwise
*/
public static boolean inSameParty(Player playera, Player playerb) {
return PartyManager.inSameParty(playera, playerb);
}
/**
* Get a list of all current parties.
* </br>
* This function is designed for API usage.
*
* @return the list of parties.
*/
public static List<Party> getParties() {
return PartyManager.getParties();
}
/**
* Add a player to a party.
* </br>
* This function is designed for API usage.
*
* @param player The player to add to the party
* @param partyName The party to add the player to
* @deprecated parties can have limits, use the other method
*/
@Deprecated
public static void addToParty(Player player, String partyName) {
Party party = PartyManager.getParty(partyName);
if (party == null) {
party = new Party(new PartyLeader(player.getUUID(), player.getName()), partyName);
} else {
if (PartyManager.isPartyFull(player, party)) {
NotificationManager.sendPlayerInformation(player, NotificationType.PARTY_MESSAGE, "Commands.Party.PartyFull", party.toString());
return;
}
}
PartyManager.addToParty(UserManager.getPlayer(player), party);
}
/**
* The max party size of the server
* 0 or less for no size limit
*
* @return the max party size on this server
*/
public static int getMaxPartySize() {
return MainConfig.getInstance().getPartyMaxSize();
}
/**
* Add a player to a party.
* </br>
* This function is designed for API usage.
*
* @param player The player to add to the party
* @param partyName The party to add the player to
* @param bypassLimit if true bypasses party size limits
*/
public static void addToParty(Player player, String partyName, boolean bypassLimit) {
Party party = PartyManager.getParty(partyName);
if (party == null) {
party = new Party(new PartyLeader(player.getUUID(), player.getName()), partyName);
}
PartyManager.addToParty(UserManager.getPlayer(player), party);
}
/**
* Remove a player from a party.
* </br>
* This function is designed for API usage.
*
* @param player The player to remove
*/
public static void removeFromParty(Player player) {
PartyManager.removeFromParty(UserManager.getPlayer(player));
}
/**
* Get the leader of a party.
* </br>
* This function is designed for API usage.
*
* @param partyName The party name
* @return the leader of the party
*/
public static String getPartyLeader(String partyName) {
return PartyManager.getPartyLeaderName(partyName);
}
/**
* Set the leader of a party.
* </br>
* This function is designed for API usage.
*
* @param partyName The name of the party to set the leader of
* @param playerName The playerName to set as leader
*/
@Deprecated
public static void setPartyLeader(String partyName, String playerName) {
PartyManager.setPartyLeader(McmmoCore.getServer().getOfflinePlayer(playerName).getUniqueId(), PartyManager.getParty(partyName));
}
/**
* Get a list of all players in this player's party.
* </br>
* This function is designed for API usage.
*
* @param player The player to check
* @return all the players in the player's party
*/
@Deprecated
public static List<OfflinePlayer> getOnlineAndOfflineMembers(Player player) {
List<OfflinePlayer> members = new ArrayList<OfflinePlayer>();
for (UUID memberUniqueId : PartyManager.getAllMembers(player).keySet()) {
OfflinePlayer member = mcMMO.p.getServer().getOfflinePlayer(memberUniqueId);
members.add(member);
}
return members;
}
/**
* Get a list of all player names in this player's party.
* </br>
* This function is designed for API usage.
*
* @param player The player to check
* @return all the player names in the player's party
*/
@Deprecated
public static LinkedHashSet<String> getMembers(Player player) {
return (LinkedHashSet<String>) PartyManager.getAllMembers(player).values();
}
/**
* Get a list of all player names and uuids in this player's party.
* </br>
* This function is designed for API usage.
*
* @param player The player to check
* @return all the player names and uuids in the player's party
*/
public static LinkedHashMap<UUID, String> getMembersMap(Player player) {
return PartyManager.getAllMembers(player);
}
/**
* Get a list of all online players in this party.
* </br>
* This function is designed for API usage.
*
* @param partyName The party to check
* @return all online players in this party
*/
public static List<Player> getOnlineMembers(String partyName) {
return PartyManager.getOnlineMembers(partyName);
}
/**
* Get a list of all online players in this player's party.
* </br>
* This function is designed for API usage.
*
* @param player The player to check
* @return all online players in the player's party
*/
public static List<Player> getOnlineMembers(Player player) {
return PartyManager.getOnlineMembers(player);
}
public static boolean hasAlly(String partyName) {
return getAllyName(partyName) != null;
}
public static String getAllyName(String partyName) {
Party ally = PartyManager.getParty(partyName).getAlly();
if (ally != null) {
return ally.getName();
}
return null;
}
}

View File

@ -0,0 +1,94 @@
package com.gmail.nossr50.core.api;
import com.gmail.nossr50.core.skills.PrimarySkillType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public final class SkillAPI {
private SkillAPI() {
}
/**
* Returns a list of strings with mcMMO's skills
* This includes parent and child skills
* </br>
* This function is designed for API usage.
*
* @return a list of strings with valid skill names
*/
public static List<String> getSkills() {
return getListFromEnum(Arrays.asList(PrimarySkillType.values()));
}
/**
* Returns a list of strings with mcMMO's skills
* This only includes parent skills
* </br>
* This function is designed for API usage.
*
* @return a list of strings with valid skill names
*/
public static List<String> getNonChildSkills() {
return getListFromEnum(PrimarySkillType.NON_CHILD_SKILLS);
}
/**
* Returns a list of strings with mcMMO's skills
* This only includes child skills
* </br>
* This function is designed for API usage.
*
* @return a list of strings with valid skill names
*/
public static List<String> getChildSkills() {
return getListFromEnum(PrimarySkillType.CHILD_SKILLS);
}
/**
* Returns a list of strings with mcMMO's skills
* This only includes combat skills
* </br>
* This function is designed for API usage.
*
* @return a list of strings with valid skill names
*/
public static List<String> getCombatSkills() {
return getListFromEnum(PrimarySkillType.COMBAT_SKILLS);
}
/**
* Returns a list of strings with mcMMO's skills
* This only includes gathering skills
* </br>
* This function is designed for API usage.
*
* @return a list of strings with valid skill names
*/
public static List<String> getGatheringSkills() {
return getListFromEnum(PrimarySkillType.GATHERING_SKILLS);
}
/**
* Returns a list of strings with mcMMO's skills
* This only includes misc skills
* </br>
* This function is designed for API usage.
*
* @return a list of strings with valid skill names
*/
public static List<String> getMiscSkills() {
return getListFromEnum(PrimarySkillType.MISC_SKILLS);
}
private static List<String> getListFromEnum(List<PrimarySkillType> skillsTypes) {
List<String> skills = new ArrayList<String>();
for (PrimarySkillType primarySkillType : skillsTypes) {
skills.add(primarySkillType.name());
}
return skills;
}
}

View File

@ -0,0 +1,9 @@
package com.gmail.nossr50.core.api.exceptions;
public class InvalidFormulaTypeException extends RuntimeException {
private static final long serialVersionUID = 3368670229490121886L;
public InvalidFormulaTypeException() {
super("That is not a valid FormulaType.");
}
}

View File

@ -0,0 +1,9 @@
package com.gmail.nossr50.core.api.exceptions;
public class InvalidPlayerException extends RuntimeException {
private static final long serialVersionUID = 907213002618581385L;
public InvalidPlayerException() {
super("That player does not exist in the database.");
}
}

View File

@ -0,0 +1,9 @@
package com.gmail.nossr50.core.api.exceptions;
public class InvalidSkillException extends RuntimeException {
private static final long serialVersionUID = 942705284195791157L;
public InvalidSkillException() {
super("That is not a valid skill.");
}
}

View File

@ -0,0 +1,9 @@
package com.gmail.nossr50.core.api.exceptions;
public class InvalidXPGainReasonException extends RuntimeException {
private static final long serialVersionUID = 4427052841957931157L;
public InvalidXPGainReasonException() {
super("That is not a valid XPGainReason.");
}
}

View File

@ -0,0 +1,11 @@
package com.gmail.nossr50.core.api.exceptions;
import com.gmail.nossr50.core.mcmmo.entity.Player;
public class McMMOPlayerNotFoundException extends RuntimeException {
private static final long serialVersionUID = 761917904993202836L;
public McMMOPlayerNotFoundException(Player player) {
super("McMMOPlayer object was not found for: " + player.getName() + " " + player.getUUID());
}
}

View File

@ -0,0 +1,21 @@
package com.gmail.nossr50.core.chat;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.config.MainConfig;
import com.gmail.nossr50.core.events.chat.McMMOAdminChatEvent;
public class AdminChatManager extends ChatManager {
protected AdminChatManager() {
super(MainConfig.getInstance().getAdminDisplayNames(), MainConfig.getInstance().getAdminChatPrefix());
}
@Override
public void handleChat(String senderName, String displayName, String message, boolean isAsync) {
handleChat(new McMMOAdminChatEvent(senderName, displayName, message, isAsync));
}
@Override
protected void sendMessage() {
McmmoCore.getServer().broadcast(message, "mcmmo.chat.adminchat");
}
}

View File

@ -0,0 +1,79 @@
package com.gmail.nossr50.core.chat;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.data.UserManager;
import com.gmail.nossr50.core.datatypes.party.Party;
import com.gmail.nossr50.core.events.chat.McMMOChatEvent;
import com.gmail.nossr50.core.events.chat.McMMOPartyChatEvent;
import com.gmail.nossr50.core.locale.LocaleLoader;
import com.gmail.nossr50.core.mcmmo.entity.Player;
public abstract class ChatManager {
protected boolean useDisplayNames;
protected String chatPrefix;
protected String senderName;
protected String displayName;
protected String message;
protected ChatManager(boolean useDisplayNames, String chatPrefix) {
this.useDisplayNames = useDisplayNames;
this.chatPrefix = chatPrefix;
}
protected void handleChat(McMMOChatEvent event) {
McmmoCore.getEventCommander().callEvent(event);
if (event.isCancelled()) {
return;
}
senderName = event.getSender();
displayName = useDisplayNames ? event.getDisplayName() : senderName;
message = LocaleLoader.formatString(chatPrefix, displayName) + " " + event.getMessage();
sendMessage();
/*
* Party Chat Spying
* Party messages will be copied to people with the mcmmo.admin.chatspy permission node
*/
if (event instanceof McMMOPartyChatEvent) {
//We need to grab the party chat name
McMMOPartyChatEvent partyChatEvent = (McMMOPartyChatEvent) event;
//Find the people with permissions
for (Player player : McmmoCore.getServer().getOnlinePlayers()) {
//Check for toggled players
if (UserManager.getPlayer(player).isPartyChatSpying()) {
Party adminParty = UserManager.getPlayer(player).getParty();
//Only message admins not part of this party
if (adminParty != null) {
//TODO: Incorporate JSON
if (!adminParty.getName().equalsIgnoreCase(partyChatEvent.getParty()))
player.sendMessage(LocaleLoader.getString("Commands.AdminChatSpy.Chat", partyChatEvent.getParty(), message));
} else {
player.sendMessage(LocaleLoader.getString("Commands.AdminChatSpy.Chat", partyChatEvent.getParty(), message));
}
}
}
}
}
public void handleChat(String senderName, String message) {
handleChat(senderName, senderName, message, false);
}
public void handleChat(Player player, String message, boolean isAsync) {
handleChat(player.getName(), player.getDisplayName(), message, isAsync);
}
public void handleChat(String senderName, String displayName, String message) {
handleChat(senderName, displayName, message, false);
}
public abstract void handleChat(String senderName, String displayName, String message, boolean isAsync);
protected abstract void sendMessage();
}

View File

@ -0,0 +1,30 @@
package com.gmail.nossr50.core.chat;
import com.gmail.nossr50.core.datatypes.chat.ChatMode;
import org.bukkit.plugin.Plugin;
import java.util.HashMap;
public class ChatManagerFactory {
private static final HashMap<Plugin, AdminChatManager> adminChatManagers = new HashMap<Plugin, AdminChatManager>();
private static final HashMap<Plugin, PartyChatManager> partyChatManagers = new HashMap<Plugin, PartyChatManager>();
public static ChatManager getChatManager(Plugin plugin, ChatMode mode) {
switch (mode) {
case ADMIN:
if (!adminChatManagers.containsKey(plugin)) {
adminChatManagers.put(plugin, new AdminChatManager(plugin));
}
return adminChatManagers.get(plugin);
case PARTY:
if (!partyChatManagers.containsKey(plugin)) {
partyChatManagers.put(plugin, new PartyChatManager(plugin));
}
return partyChatManagers.get(plugin);
default:
return null;
}
}
}

View File

@ -0,0 +1,28 @@
package com.gmail.nossr50.core.chat;
import com.gmail.nossr50.core.config.MainConfig;
import com.gmail.nossr50.core.datatypes.party.Party;
import com.gmail.nossr50.core.events.chat.McMMOPartyChatEvent;
import com.gmail.nossr50.core.runnables.party.PartyChatTask;
public class PartyChatManager extends ChatManager {
private Party party;
protected PartyChatManager(Plugin plugin) {
super(plugin, MainConfig.getInstance().getPartyDisplayNames(), MainConfig.getInstance().getPartyChatPrefix());
}
public void setParty(Party party) {
this.party = party;
}
@Override
public void handleChat(String senderName, String displayName, String message, boolean isAsync) {
handleChat(new McMMOPartyChatEvent(plugin, senderName, displayName, party.getName(), message, isAsync));
}
@Override
protected void sendMessage() {
new PartyChatTask(plugin, party, senderName, displayName, message).runTask(plugin);
}
}

View File

@ -0,0 +1,723 @@
package com.gmail.nossr50.core.config;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.datatypes.interactions.NotificationType;
import com.gmail.nossr50.core.mcmmo.colors.ChatColor;
import com.gmail.nossr50.core.skills.SubSkillType;
import com.gmail.nossr50.core.skills.subskills.AbstractSubSkill;
import java.util.ArrayList;
import java.util.List;
//@ConfigSerializable
public class AdvancedConfig extends ConfigValidated {
public static final String SKILLS = "Skills";
public static final String GENERAL = "General";
public static final String ABILITY = "Ability";
public static final String LENGTH = "Length";
public static final String INCREASE_LEVEL = "IncreaseLevel";
public static final String ENCHANT_BUFF = "EnchantBuff";
public static final String ACROBATICS = "Acrobatics";
public static final String DODGE = "Dodge";
public static final String CHANCE = "Chance";
public static final String CHANCE_MAX = CHANCE + "Max";
public static final String BONUS = "Bonus";
public static final String MAX_BONUS_LEVEL = "Max" + BONUS + "Level";
public static final String MODIFIER = "Modifier";
public static final String DAMAGE_MODIFIER = "Damage" + MODIFIER;
public static final String DAMAGE_THRESHOLD = "DamageThreshold";
public static final String ALCHEMY = "Alchemy";
public static final String CATALYSIS = "Catalysis";
public static final String MIN_SPEED = "MinSpeed";
public static final String MAX_SPEED = "MaxSpeed";
public static final String ARCHERY = "Archery";
public static final String SKILL_SHOT = "SkillShot";
public static final String MULTIPLIER = "Multiplier";
public static final String RANK_DAMAGE_MULTIPLIER = "RankDamage" + MULTIPLIER;
public static final String BONUS_DAMAGE = BONUS + "Damage";
public static final String FORCE_MULTIPLIER = "Force" + MULTIPLIER;
public static final String AXES = "Axes";
public static final String STANDARD = "Standard";
public static final String RETRO_MODE = "RetroMode";
public static final String CAP_LEVEL = "CapLevel";
public static final String KNOCKBACK_MODIFIER = "Knockback" + MODIFIER;
public static final String PVP_MODIFIER = "PVP_" + MODIFIER;
public static final String PVE_MODIFIER = "PVE_" + MODIFIER;
public static final String FISHING = "Fishing";
public static final String MASTER_ANGLER = "MasterAngler";
public static final String BOAT_MODIFIER = "Boat" + MODIFIER;
public static final String BIOME_MODIFIER = "Biome" + MODIFIER;
public static final String XP = "XP";
public static final String VANILLA_XPMULTIPLIER = "Vanilla" + XP + MULTIPLIER;
public static final String RANK = "Rank_";
public static final String TAMING = "Taming";
public static final String CALL_OF_THE_WILD = "CallOfTheWild";
public static final String MIN_HORSE_JUMP_STRENGTH = "MinHorseJumpStrength";
public static final String MAX_HORSE_JUMP_STRENGTH = "MaxHorseJumpStrength";
public static final String SHOCK_PROOF = "ShockProof";
public static final String UNARMED = "Unarmed";
public static final String STARTING_LEVEL = "StartingLevel";
public static final String AXE_MASTERY = "AxeMastery";
public static final String CRITICAL_STRIKES = "CriticalStrikes";
public static final String GREATER_IMPACT = "GreaterImpact";
public static final String ARMOR_IMPACT = "ArmorImpact";
public static final String SKULL_SPLITTER = "SkullSplitter.";
public static final String MAX_PERCENTAGE_DURABILITY_DAMAGE = "MaxPercentageDurabilityDamage";
public static final String SHAKE = "Shake";
public static final String MINING = "Mining";
public static final String BLAST_MINING = "BlastMining";
public static final String LEVELS = "Levels";
public static final String BLAST_DAMAGE_DECREASE = "BlastDamageDecrease";
public static final String ORE_BONUS = "Ore" + BONUS;
public static final String DEBRIS_REDUCTION = "DebrisReduction";
public static final String DROP_MULTIPLIER = "Drop" + MULTIPLIER;
public static final String BLAST_RADIUS = "BlastRadius";
public static final String REPAIR = "Repair";
public static final String REPAIR_MASTERY = "RepairMastery";
public static final String MAX_BONUS_PERCENTAGE = "Max" + BONUS + "Percentage";
public static final String ARCANE_FORGING = "ArcaneForging";
public static final String MAY_LOSE_ENCHANTS = "May_Lose_Enchants";
public static final String KEEP_ENCHANTS = "Keep_Enchants_";
public static final String DOWNGRADES = "Downgrades_";
public static final String ENABLED = "Enabled";
public static final String DOWNGRADES_ENABLED = DOWNGRADES + ENABLED;
public static final String SALVAGE = "Salvage";
public static final String ARCANE_SALVAGE = "ArcaneSalvage";
public static final String ENCHANT_DOWNGRADE_ENABLED = "EnchantDowngrade" + ENABLED;
public static final String ENCHANT_LOSS_ENABLED = "EnchantLoss" + ENABLED;
public static final String EXTRACT_FULL_ENCHANT = "ExtractFullEnchant";
public static final String EXTRACT_PARTIAL_ENCHANT = "ExtractPartialEnchant";
public static final String SMELTING = "Smelting";
public static final String FUEL_EFFICIENCY = "FuelEfficiency";
public static final String FLUX = "Flux";
public static final String SWORDS = "Swords";
public static final String RUPTURE = "Rupture";
public static final String DAMAGE_PLAYER = "DamagePlayer";
public static final String DAMAGE_MOBS = "DamageMobs";
public static final String MAX_TICKS = "MaxTicks";
public static final String BASE_TICKS = "BaseTicks";
public static final String COUNTER_ATTACK = "CounterAttack";
public static final String SERRATED_STRIKES = "SerratedStrikes";
public static final String TICKS = "Ticks";
public static final String GORE = "Gore";
public static final String FAST_FOOD = "FastFood";
public static final String FAST_FOOD_SERVICE = FAST_FOOD + "Service";
public static final String PUMMEL = "Pummel";
public static final String THICK_FUR = "ThickFur";
public static final String SHARPENED_CLAWS = "SharpenedClaws";
public static final String DISARM = "Disarm";
public static final String ANTI_THEFT = "AntiTheft";
public static final String DAZE = "Daze";
public static final String MAX_DAMAGE = "MaxDamage";
public static final String ROLL = "Roll";
public static final String GRACEFUL_ROLL = "Graceful" + ROLL;
public static final String ARROW_DEFLECT = "ArrowDeflect";
public static final String IRON_GRIP = "IronGrip";
public static final String WOODCUTTING = "Woodcutting";
public static final String HARVEST_LUMBER = "HarvestLumber";
public static final String FEEDBACK = "Feedback";
public static final String SKILL_COMMAND = "SkillCommand";
public static final String BLANK_LINES_ABOVE_HEADER = "BlankLinesAboveHeader";
public static final String ACTION_BAR_NOTIFICATIONS = "ActionBarNotifications";
public static final String SEND_COPY_OF_MESSAGE_TO_CHAT = "SendCopyOfMessageToChat";
public static final String EVENTS = "Events";
public static final String SEND_TITLES = "SendTitles";
private static AdvancedConfig instance;
private AdvancedConfig() {
super(McmmoCore.getDataFolderPath().getAbsoluteFile(), "advanced.yml", true);
}
public static AdvancedConfig getInstance() {
if (instance == null) {
instance = new AdvancedConfig();
}
return instance;
}
/**
* The version of this config
*
* @return
*/
@Override
public double getConfigVersion() {
return 1;
}
@Override
public List<String> validateKeys() {
// Validate all the settings!
List<String> reason = new ArrayList<String>();
/* GENERAL */
if (getAbilityLength() < 1) {
reason.add(SKILLS + "." + GENERAL + "." + ABILITY + "." + LENGTH + ".<mode>." + INCREASE_LEVEL + " should be at least 1!");
}
if (getEnchantBuff() < 1) {
reason.add(SKILLS + "." + GENERAL + "." + ABILITY + "." + ENCHANT_BUFF + " should be at least 1!");
}
/* ACROBATICS */
if (getMaximumProbability(SubSkillType.ACROBATICS_DODGE) < 1) {
reason.add(SKILLS + "." + ACROBATICS + "." + DODGE + "." + CHANCE_MAX + " should be at least 1!");
}
if (getMaxBonusLevel(SubSkillType.ACROBATICS_DODGE) < 1) {
reason.add(SKILLS + "." + ACROBATICS + "." + DODGE + "." + MAX_BONUS_LEVEL + " should be at least 1!");
}
if (getDodgeDamageModifier() <= 1) {
reason.add(SKILLS + "." + ACROBATICS + "." + DODGE + "." + DAMAGE_MODIFIER + " should be greater than 1!");
}
if (getMaximumProbability(SubSkillType.ACROBATICS_ROLL) < 1) {
reason.add(SKILLS + "." + ACROBATICS + "." + ROLL + "." + CHANCE_MAX + " should be at least 1!");
}
if (getMaxBonusLevel(SubSkillType.ACROBATICS_ROLL) < 1) {
reason.add(SKILLS + "." + ACROBATICS + "." + ROLL + "." + MAX_BONUS_LEVEL + " should be at least 1!");
}
if (getRollDamageThreshold() < 0) {
reason.add(SKILLS + "." + ACROBATICS + "." + ROLL + "." + DAMAGE_THRESHOLD + " should be at least 0!");
}
if (getGracefulRollDamageThreshold() < 0) {
reason.add(SKILLS + "." + ACROBATICS + "." + GRACEFUL_ROLL + "." + DAMAGE_THRESHOLD + " should be at least 0!");
}
if (getCatalysisMinSpeed() <= 0) {
reason.add(SKILLS + "." + ALCHEMY + "." + CATALYSIS + "." + MIN_SPEED + " must be greater than 0!");
}
if (getCatalysisMaxSpeed() < getCatalysisMinSpeed()) {
reason.add(SKILLS + "." + ALCHEMY + "." + CATALYSIS + "." + MAX_SPEED + " should be at least Skills.Alchemy.Catalysis." + MIN_SPEED + "!");
}
/* ARCHERY */
if (getSkillShotRankDamageMultiplier() <= 0) {
reason.add(SKILLS + "." + ARCHERY + "." + SKILL_SHOT + "." + RANK_DAMAGE_MULTIPLIER + " should be greater than 0!");
}
if (getMaximumProbability(SubSkillType.ARCHERY_DAZE) < 1) {
reason.add(SKILLS + "." + ARCHERY + "." + DAZE + "." + CHANCE_MAX + " should be at least 1!");
}
if (getMaxBonusLevel(SubSkillType.ARCHERY_DAZE) < 1) {
reason.add(SKILLS + "." + ARCHERY + "." + DAZE + "." + MAX_BONUS_LEVEL + " should be at least 1!");
}
if (getDazeBonusDamage() < 0) {
reason.add(SKILLS + "." + ARCHERY + "." + DAZE + "." + BONUS_DAMAGE + " should be at least 0!");
}
if (getMaximumProbability(SubSkillType.ARCHERY_ARROW_RETRIEVAL) < 1) {
reason.add(SKILLS + "." + ARCHERY + ".Retrieve." + CHANCE_MAX + " should be at least 1!");
}
if (getMaxBonusLevel(SubSkillType.ARCHERY_ARROW_RETRIEVAL) < 1) {
reason.add(SKILLS + "." + ARCHERY + ".Retrieve." + MAX_BONUS_LEVEL + " should be at least 1!");
}
if (getForceMultiplier() < 0) {
reason.add(SKILLS + "." + ARCHERY + "." + FORCE_MULTIPLIER + " should be at least 0!");
}
/* AXES */
if(getAxeMasteryRankDamageMultiplier() < 0)
{
reason.add(SKILLS + "." + AXES + "." + AXE_MASTERY + "." + RANK_DAMAGE_MULTIPLIER + " should be at least 0!");
}
if (getMaximumProbability(SubSkillType.AXES_CRITICAL_STRIKES) < 1) {
reason.add(SKILLS + "." + AXES + ".CriticalHit." + CHANCE_MAX + " should be at least 1!");
}
if (getMaxBonusLevel(SubSkillType.AXES_CRITICAL_STRIKES) < 1) {
reason.add(SKILLS + "." + AXES + ".CriticalHit." + MAX_BONUS_LEVEL + " should be at least 1!");
}
if (getCriticalStrikesPVPModifier() < 1) {
reason.add(SKILLS + "." + AXES + "." + CRITICAL_STRIKES + "." + PVP_MODIFIER + " should be at least 1!");
}
if (getCriticalStrikesPVPModifier() < 1) {
reason.add(SKILLS + "." + AXES + "." + CRITICAL_STRIKES + "." + PVE_MODIFIER + " should be at least 1!");
}
if (getGreaterImpactChance() < 1) {
reason.add(SKILLS + "." + AXES + "." + GREATER_IMPACT + "." + CHANCE + " should be at least 1!");
}
if (getGreaterImpactModifier() < 1) {
reason.add(SKILLS + "." + AXES + "." + GREATER_IMPACT + "." + KNOCKBACK_MODIFIER + " should be at least 1!");
}
if (getGreaterImpactBonusDamage() < 1) {
reason.add(SKILLS + "." + AXES + "." + GREATER_IMPACT + "." + BONUS_DAMAGE + " should be at least 1!");
}
if (getArmorImpactIncreaseLevel() < 1) {
reason.add(SKILLS + "." + AXES + "." + ARMOR_IMPACT + "." + INCREASE_LEVEL + " should be at least 1!");
}
if (getImpactChance() < 1) {
reason.add(SKILLS + "." + AXES + "." + ARMOR_IMPACT + "." + CHANCE + " should be at least 1!");
}
if (getArmorImpactMaxDurabilityDamage() < 1) {
reason.add(SKILLS + "." + AXES + "." + ARMOR_IMPACT + "." + MAX_PERCENTAGE_DURABILITY_DAMAGE + " should be at least 1!");
}
if (getSkullSplitterModifier() < 1) {
reason.add(SKILLS + "." + AXES + "." + SKULL_SPLITTER + DAMAGE_MODIFIER + " should be at least 1!");
}
/*if (getFishermanDietRankChange() < 1) {
reason.add(SKILLS + "." + FISHING + ".FishermansDiet.RankChange should be at least 1!");
}*/
if (getMasterAnglerBoatModifier() < 1) {
reason.add(SKILLS + "." + FISHING + "." + MASTER_ANGLER + "." + BOAT_MODIFIER + " should be at least 1!");
}
if (getMasterAnglerBiomeModifier() < 1) {
reason.add(SKILLS + "." + FISHING + "." + MASTER_ANGLER + "." + BIOME_MODIFIER + " should be at least 1!");
}
/* HERBALISM */
/*if (getFarmerDietRankChange() < 1) {
reason.add(SKILLS + ".Herbalism.FarmersDiet.RankChange should be at least 1!");
}
if (getGreenThumbStageChange() < 1) {
reason.add(SKILLS + ".Herbalism.GreenThumb.StageChange should be at least 1!");
}*/
if (getMaximumProbability(SubSkillType.HERBALISM_GREEN_THUMB) < 1) {
reason.add(SKILLS + ".Herbalism.GreenThumb." + CHANCE_MAX + " should be at least 1!");
}
if (getMaxBonusLevel(SubSkillType.HERBALISM_GREEN_THUMB) < 1) {
reason.add(SKILLS + ".Herbalism.GreenThumb." + MAX_BONUS_LEVEL + " should be at least 1!");
}
if (getMaximumProbability(SubSkillType.HERBALISM_DOUBLE_DROPS) < 1) {
reason.add(SKILLS + ".Herbalism.DoubleDrops." + CHANCE_MAX + " should be at least 1!");
}
if (getMaxBonusLevel(SubSkillType.HERBALISM_DOUBLE_DROPS) < 1) {
reason.add(SKILLS + ".Herbalism.DoubleDrops." + MAX_BONUS_LEVEL + " should be at least 1!");
}
if (getMaximumProbability(SubSkillType.HERBALISM_HYLIAN_LUCK) < 1) {
reason.add(SKILLS + ".Herbalism.HylianLuck." + CHANCE_MAX + " should be at least 1!");
}
if (getMaxBonusLevel(SubSkillType.HERBALISM_HYLIAN_LUCK) < 1) {
reason.add(SKILLS + ".Herbalism.HylianLuck." + MAX_BONUS_LEVEL + " should be at least 1!");
}
if (getMaximumProbability(SubSkillType.HERBALISM_SHROOM_THUMB) < 1) {
reason.add(SKILLS + ".Herbalism.ShroomThumb." + CHANCE_MAX + " should be at least 1!");
}
if (getMaxBonusLevel(SubSkillType.HERBALISM_SHROOM_THUMB) < 1) {
reason.add(SKILLS + ".Herbalism.ShroomThumb." + MAX_BONUS_LEVEL + " should be at least 1!");
}
/* MINING */
if (getMaximumProbability(SubSkillType.MINING_DOUBLE_DROPS) < 1) {
reason.add(SKILLS + "." + MINING + ".DoubleDrops." + CHANCE_MAX + " should be at least 1!");
}
if (getMaxBonusLevel(SubSkillType.MINING_DOUBLE_DROPS) < 1) {
reason.add(SKILLS + "." + MINING + ".DoubleDrops." + MAX_BONUS_LEVEL + " should be at least 1!");
}
/* REPAIR */
if (getRepairMasteryMaxBonus() < 1) {
reason.add(SKILLS + "." + REPAIR + "." + REPAIR_MASTERY + "." + MAX_BONUS_PERCENTAGE + " should be at least 1!");
}
if (getRepairMasteryMaxLevel() < 1) {
reason.add(SKILLS + "." + REPAIR + "." + REPAIR_MASTERY + "." + MAX_BONUS_LEVEL + " should be at least 1!");
}
if (getMaximumProbability(SubSkillType.REPAIR_SUPER_REPAIR) < 1) {
reason.add(SKILLS + "." + REPAIR + ".SuperRepair." + CHANCE_MAX + " should be at least 1!");
}
if (getMaxBonusLevel(SubSkillType.REPAIR_SUPER_REPAIR) < 1) {
reason.add(SKILLS + "." + REPAIR + ".SuperRepair." + MAX_BONUS_LEVEL + " should be at least 1!");
}
/* SMELTING */
if (getBurnModifierMaxLevel() < 1) {
reason.add(SKILLS + "." + SMELTING + "." + FUEL_EFFICIENCY + "." + MAX_BONUS_LEVEL + " should be at least 1!");
}
if (getBurnTimeMultiplier() < 1) {
reason.add(SKILLS + "." + SMELTING + "." + FUEL_EFFICIENCY + "." + MULTIPLIER + " should be at least 1!");
}
if (getMaxBonusLevel(SubSkillType.SMELTING_SECOND_SMELT) < 1) {
reason.add(SKILLS + "." + SMELTING + ".SecondSmelt." + MAX_BONUS_LEVEL + " should be at least 1!");
}
if (getMaximumProbability(SubSkillType.SMELTING_SECOND_SMELT) < 1) {
reason.add(SKILLS + "." + SMELTING + ".SecondSmelt." + CHANCE_MAX + " should be at least 1!");
}
if (getFluxMiningChance() < 1) {
reason.add(SKILLS + "." + SMELTING + "." + FLUX + MINING + "." + CHANCE + " should be at least 1!");
}
/* SWORDS */
if (getMaximumProbability(SubSkillType.SWORDS_RUPTURE) < 1) {
reason.add(SKILLS + "." + SWORDS + "." + RUPTURE + "." + CHANCE_MAX + " should be at least 1!");
}
if (getMaxBonusLevel(SubSkillType.SWORDS_RUPTURE) < 1) {
reason.add(SKILLS + "." + SWORDS + "." + RUPTURE + "." + MAX_BONUS_LEVEL + " should be at least 1!");
}
if (getRuptureMaxTicks() < 1) {
reason.add(SKILLS + "." + SWORDS + "." + RUPTURE + "." + MAX_TICKS + " should be at least 1!");
}
if (getRuptureMaxTicks() < getRuptureBaseTicks()) {
reason.add(SKILLS + "." + SWORDS + "." + RUPTURE + "." + MAX_TICKS + " should be at least Skills.Swords.Rupture." + BASE_TICKS + "!");
}
if (getRuptureBaseTicks() < 1) {
reason.add(SKILLS + "." + SWORDS + "." + RUPTURE + "." + BASE_TICKS + " should be at least 1!");
}
if (getMaximumProbability(SubSkillType.SWORDS_COUNTER_ATTACK) < 1) {
reason.add(SKILLS + "." + SWORDS + "." + COUNTER_ATTACK + "." + CHANCE_MAX + " should be at least 1!");
}
if (getMaxBonusLevel(SubSkillType.SWORDS_COUNTER_ATTACK) < 1) {
reason.add(SKILLS + "." + SWORDS + "." + COUNTER_ATTACK + "." + MAX_BONUS_LEVEL + " should be at least 1!");
}
if (getCounterModifier() < 1) {
reason.add(SKILLS + "." + SWORDS + "." + COUNTER_ATTACK + "." + DAMAGE_MODIFIER + " should be at least 1!");
}
if (getSerratedStrikesModifier() < 1) {
reason.add(SKILLS + "." + SWORDS + "." + SERRATED_STRIKES + "." + DAMAGE_MODIFIER + " should be at least 1!");
}
if (getSerratedStrikesTicks() < 1) {
reason.add(SKILLS + "." + SWORDS + "." + SERRATED_STRIKES + "." + RUPTURE + "Ticks should be at least 1!");
}
/* TAMING */
if (getMaximumProbability(SubSkillType.TAMING_GORE) < 1) {
reason.add(SKILLS + "." + TAMING + "." + GORE + "." + CHANCE_MAX + " should be at least 1!");
}
if (getMaxBonusLevel(SubSkillType.TAMING_GORE) < 1) {
reason.add(SKILLS + "." + TAMING + "." + GORE + "." + MAX_BONUS_LEVEL + " should be at least 1!");
}
if (getGoreModifier() < 1) {
reason.add(SKILLS + "." + TAMING + "." + GORE + "." + MODIFIER + " should be at least 1!");
}
if (getFastFoodChance() < 1) {
reason.add(SKILLS + "." + TAMING + "." + FAST_FOOD + "." + CHANCE + " should be at least 1!");
}
if (getThickFurModifier() < 1) {
reason.add(SKILLS + "." + TAMING + "." + THICK_FUR + "." + MODIFIER + " should be at least 1!");
}
if (getShockProofModifier() < 1) {
reason.add(SKILLS + "." + TAMING + "." + SHOCK_PROOF + "." + MODIFIER + " should be at least 1!");
}
if (getSharpenedClawsBonus() < 1) {
reason.add(SKILLS + "." + TAMING + "." + SHARPENED_CLAWS + "." + BONUS + " should be at least 1!");
}
if (getMaxHorseJumpStrength() < 0 || getMaxHorseJumpStrength() > 2) {
reason.add(SKILLS + "." + TAMING + "." + CALL_OF_THE_WILD + "." + MAX_HORSE_JUMP_STRENGTH + " should be between 0 and 2!");
}
/* UNARMED */
if (getMaximumProbability(SubSkillType.UNARMED_DISARM) < 1) {
reason.add(SKILLS + "." + UNARMED + "." + DISARM + "." + CHANCE_MAX + " should be at least 1!");
}
if (getMaxBonusLevel(SubSkillType.UNARMED_DISARM) < 1) {
reason.add(SKILLS + "." + UNARMED + "." + DISARM + "." + MAX_BONUS_LEVEL + " should be at least 1!");
}
if (getMaximumProbability(SubSkillType.UNARMED_ARROW_DEFLECT) < 1) {
reason.add(SKILLS + "." + UNARMED + "." + ARROW_DEFLECT + "." + CHANCE_MAX + " should be at least 1!");
}
if (getMaxBonusLevel(SubSkillType.UNARMED_ARROW_DEFLECT) < 1) {
reason.add(SKILLS + "." + UNARMED + "." + ARROW_DEFLECT + "." + MAX_BONUS_LEVEL + " should be at least 1!");
}
if (getMaximumProbability(SubSkillType.UNARMED_IRON_GRIP) < 1) {
reason.add(SKILLS + "." + UNARMED + "." + IRON_GRIP + "." + CHANCE_MAX + " should be at least 1!");
}
if (getMaxBonusLevel(SubSkillType.UNARMED_IRON_GRIP) < 1) {
reason.add(SKILLS + "." + UNARMED + "." + IRON_GRIP + "." + MAX_BONUS_LEVEL + " should be at least 1!");
}
/* WOODCUTTING */
/*if (getLeafBlowUnlockLevel() < 0) {
reason.add("Skills.Woodcutting.LeafBlower.UnlockLevel should be at least 0!");
}*/
if (getMaximumProbability(SubSkillType.WOODCUTTING_HARVEST_LUMBER) < 1) {
reason.add(SKILLS + "." + WOODCUTTING + "." + HARVEST_LUMBER + "." + CHANCE_MAX + " should be at least 1!");
}
if (getMaxBonusLevel(SubSkillType.WOODCUTTING_HARVEST_LUMBER) < 1) {
reason.add(SKILLS + "." + WOODCUTTING + "." + HARVEST_LUMBER + "." + MAX_BONUS_LEVEL + " should be at least 1!");
}
return reason;
}
/* GENERAL */
public int getStartingLevel() { return getIntValue(SKILLS, GENERAL, STARTING_LEVEL); }
/**
* This returns the maximum level at which superabilities will stop lengthening from scaling alongside skill level.
* It returns a different value depending on whether or not the server is in retro mode
* @return the level at which abilities stop increasing in length
*/
public int getAbilityLengthCap() {
if(!McmmoCore.isRetroModeEnabled())
return getIntValue(SKILLS, GENERAL, ABILITY, LENGTH, STANDARD, CAP_LEVEL);
else
return getIntValue(SKILLS, GENERAL, ABILITY, LENGTH, RETRO_MODE, CAP_LEVEL);
}
/**
* This returns the frequency at which abilities will increase in length
* It returns a different value depending on whether or not the server is in retro mode
* @return the number of levels required per ability length increase
*/
public int getAbilityLength() {
if(!McmmoCore.isRetroModeEnabled())
return getIntValue(SKILLS, GENERAL, ABILITY, LENGTH, STANDARD, INCREASE_LEVEL);
else
return getIntValue(SKILLS, GENERAL, ABILITY, LENGTH, RETRO_MODE, INCREASE_LEVEL);
}
public int getEnchantBuff() { return getIntValue(SKILLS, GENERAL, ABILITY, ENCHANT_BUFF); }
/**
* Grabs the max bonus level for a skill used in RNG calculations
* All max level values in the config are multiplied by 10 if the server is in retro mode as the values in the config are based around the new 1-100 skill system scaling
* A value of 10 in the file will be returned as 100 for retro mode servers to accommodate the change in scaling
* @param subSkillType target subskill
* @return the level at which this skills max benefits will be reached on the curve
*/
public int getMaxBonusLevel(SubSkillType subSkillType) {
String[] category = subSkillType.getAdvConfigAddress();
if(!McmmoCore.isRetroModeEnabled())
return getIntValue(category[0], category[1], category[2], MAX_BONUS_LEVEL, STANDARD);
else
return getIntValue(category[0], category[1], category[2], MAX_BONUS_LEVEL, RETRO_MODE);
}
public int getMaxBonusLevel(AbstractSubSkill abstractSubSkill) {
return getMaxBonusLevel(abstractSubSkill.getSubSkillType());
}
public double getMaximumProbability(SubSkillType subSkillType) {
String[] category = subSkillType.getAdvConfigAddress();
double maximumProbability = getDoubleValue(category[0], category[1], category[2], CHANCE_MAX);
return maximumProbability;
}
public double getMaximumProbability(AbstractSubSkill abstractSubSkill)
{
return getMaximumProbability(abstractSubSkill.getSubSkillType());
}
/* Notification Settings */
public boolean doesSkillCommandSendBlankLines()
{
return getBooleanValue(FEEDBACK, SKILL_COMMAND, BLANK_LINES_ABOVE_HEADER);
}
public boolean doesNotificationUseActionBar(NotificationType notificationType)
{
return getBooleanValue(FEEDBACK, ACTION_BAR_NOTIFICATIONS, notificationType.toString(), ENABLED);
}
public boolean doesNotificationSendCopyToChat(NotificationType notificationType)
{
return getBooleanValue(FEEDBACK, ACTION_BAR_NOTIFICATIONS, notificationType.toString(), SEND_COPY_OF_MESSAGE_TO_CHAT);
}
public boolean useTitlesForXPEvent()
{
return getBooleanValue(FEEDBACK, EVENTS, XP, SEND_TITLES);
}
private ChatColor getChatColorFromKey(String keyLocation) {
String colorName = getStringValue(keyLocation);
return getChatColor(colorName);
}
private ChatColor getChatColor(String configColor) {
for (ChatColor chatColor : ChatColor.values()) {
if (configColor.equalsIgnoreCase(chatColor.toString()))
return chatColor;
}
//Invalid Color
System.out.println("[mcMMO] " + configColor + " is an invalid color value");
return ChatColor.WHITE;
}
/* ACROBATICS */
public double getDodgeDamageModifier() { return getDoubleValue(SKILLS, ACROBATICS, DODGE, DAMAGE_MODIFIER); }
public double getRollDamageThreshold() { return getDoubleValue(SKILLS, ACROBATICS, ROLL, DAMAGE_THRESHOLD); }
public double getGracefulRollDamageThreshold() { return getDoubleValue(SKILLS, ACROBATICS, GRACEFUL_ROLL, DAMAGE_THRESHOLD); }
/* ALCHEMY */
public int getCatalysisMaxBonusLevel() { return getIntValue(SKILLS, ALCHEMY, CATALYSIS, MAX_BONUS_LEVEL); }
public double getCatalysisMinSpeed() { return getDoubleValue(SKILLS, ALCHEMY, CATALYSIS, MIN_SPEED); }
public double getCatalysisMaxSpeed() { return getDoubleValue(SKILLS, ALCHEMY, CATALYSIS, MAX_SPEED); }
/* ARCHERY */
public double getSkillShotRankDamageMultiplier() { return getDoubleValue(SKILLS, ARCHERY, SKILL_SHOT, RANK_DAMAGE_MULTIPLIER); }
public double getSkillShotDamageMax() { return getDoubleValue(SKILLS, ARCHERY, SKILL_SHOT, MAX_DAMAGE); }
public double getDazeBonusDamage() { return getDoubleValue(SKILLS, ARCHERY, DAZE, BONUS_DAMAGE); }
public double getForceMultiplier() { return getDoubleValue(SKILLS, ARCHERY, FORCE_MULTIPLIER); }
/* AXES */
public double getAxeMasteryRankDamageMultiplier() { return getDoubleValue(SKILLS, AXES, AXE_MASTERY, RANK_DAMAGE_MULTIPLIER); }
public double getCriticalStrikesPVPModifier() { return getDoubleValue(SKILLS, AXES, CRITICAL_STRIKES, PVP_MODIFIER); }
public double getCriticalStrikesPVEModifier() { return getDoubleValue(SKILLS, AXES, CRITICAL_STRIKES, PVE_MODIFIER); }
public double getGreaterImpactChance() { return getDoubleValue(SKILLS, AXES, GREATER_IMPACT, CHANCE); }
public double getGreaterImpactModifier() { return getDoubleValue(SKILLS, AXES, GREATER_IMPACT, KNOCKBACK_MODIFIER); }
public double getGreaterImpactBonusDamage() { return getDoubleValue(SKILLS, AXES, GREATER_IMPACT, BONUS_DAMAGE); }
public int getArmorImpactIncreaseLevel() {
int increaseLevel = getIntValue(SKILLS, AXES, ARMOR_IMPACT, INCREASE_LEVEL);
if(McmmoCore.isRetroModeEnabled())
return increaseLevel * 10;
return increaseLevel;
}
public double getImpactChance() { return getDoubleValue(SKILLS, AXES, ARMOR_IMPACT, CHANCE); }
public double getArmorImpactMaxDurabilityDamage() { return getDoubleValue(SKILLS, AXES, ARMOR_IMPACT, MAX_PERCENTAGE_DURABILITY_DAMAGE); }
public double getSkullSplitterModifier() { return getDoubleValue(SKILLS, AXES, SKULL_SPLITTER, DAMAGE_MODIFIER); }
/* EXCAVATION */
//Nothing to configure, everything is already configurable in config.yml
/* FISHING */
public double getShakeChance(int rank) { return getDoubleValue(SKILLS, FISHING, SHAKE, CHANCE, RANK, String.valueOf(rank)); }
public int getFishingVanillaXPModifier(int rank) { return getIntValue(SKILLS, FISHING, VANILLA_XPMULTIPLIER, RANK, String.valueOf(rank)); }
public double getMasterAnglerBoatModifier() {return getDoubleValue(SKILLS, FISHING, MASTER_ANGLER, BOAT_MODIFIER); }
public double getMasterAnglerBiomeModifier() {return getDoubleValue(SKILLS, FISHING, MASTER_ANGLER, BIOME_MODIFIER); }
/* HERBALISM */
//public int getFarmerDietRankChange() { return getIntValue(SKILLS, ".Herbalism.FarmersDiet.RankChange"); }
//public int getGreenThumbStageChange() { return getIntValue(SKILLS, ".Herbalism.GreenThumb.StageChange"); }
/* MINING */
public int getBlastMiningRankLevel(int rank) { return getIntValue(SKILLS, MINING, BLAST_MINING, RANK, LEVELS, RANK, String.valueOf(rank)); }
public double getBlastDamageDecrease(int rank) { return getDoubleValue(SKILLS, MINING, BLAST_MINING, BLAST_DAMAGE_DECREASE, RANK, String.valueOf(rank)); }
public double getOreBonus(int rank) { return getDoubleValue(SKILLS, MINING, BLAST_MINING, ORE_BONUS, RANK, String.valueOf(rank)); }
public double getDebrisReduction(int rank) { return getDoubleValue(SKILLS, MINING, BLAST_MINING, DEBRIS_REDUCTION, RANK, String.valueOf(rank)); }
public int getDropMultiplier(int rank) { return getIntValue(SKILLS, MINING, BLAST_MINING, DROP_MULTIPLIER, RANK, String.valueOf(rank)); }
public double getBlastRadiusModifier(int rank) { return getDoubleValue(SKILLS, MINING, BLAST_MINING, BLAST_RADIUS, MODIFIER, RANK, String.valueOf(rank)); }
/* REPAIR */
public double getRepairMasteryMaxBonus() { return getDoubleValue(SKILLS, REPAIR, REPAIR_MASTERY, MAX_BONUS_PERCENTAGE); }
public int getRepairMasteryMaxLevel() { return getIntValue(SKILLS, REPAIR, REPAIR_MASTERY, MAX_BONUS_LEVEL); }
/* Arcane Forging */
public boolean getArcaneForgingEnchantLossEnabled() { return getBooleanValue(SKILLS, REPAIR, ARCANE_FORGING, MAY_LOSE_ENCHANTS); }
public double getArcaneForgingKeepEnchantsChance(int rank) { return getDoubleValue(SKILLS, REPAIR, ARCANE_FORGING, KEEP_ENCHANTS, CHANCE, RANK, String.valueOf(rank)); }
public boolean getArcaneForgingDowngradeEnabled() { return getBooleanValue(SKILLS, REPAIR, ARCANE_FORGING, DOWNGRADES_ENABLED); }
public double getArcaneForgingDowngradeChance(int rank) { return getDoubleValue(SKILLS, REPAIR, ARCANE_FORGING, DOWNGRADES, CHANCE, RANK, String.valueOf(rank)); }
/* SALVAGE */
public boolean getArcaneSalvageEnchantDowngradeEnabled() { return getBooleanValue(SKILLS, SALVAGE, ARCANE_SALVAGE, ENCHANT_DOWNGRADE_ENABLED); }
public boolean getArcaneSalvageEnchantLossEnabled() { return getBooleanValue(SKILLS, SALVAGE, ARCANE_SALVAGE, ENCHANT_LOSS_ENABLED); }
public double getArcaneSalvageExtractFullEnchantsChance(int rank) { return getDoubleValue(SKILLS, SALVAGE, ARCANE_SALVAGE, EXTRACT_FULL_ENCHANT, RANK, String.valueOf(rank)); }
public double getArcaneSalvageExtractPartialEnchantsChance(int rank) { return getDoubleValue(SKILLS, SALVAGE, ARCANE_SALVAGE, EXTRACT_PARTIAL_ENCHANT, RANK, String.valueOf(rank)); }
/* SMELTING */
public int getBurnModifierMaxLevel() { return getIntValue(SKILLS, SMELTING, FUEL_EFFICIENCY, MAX_BONUS_LEVEL); }
public double getBurnTimeMultiplier() { return getDoubleValue(SKILLS, SMELTING, FUEL_EFFICIENCY, MULTIPLIER); }
public double getFluxMiningChance() { return getDoubleValue(SKILLS, SMELTING, FLUX, MINING, CHANCE); }
public int getSmeltingRankLevel(int rank) { return getIntValue(SKILLS, SMELTING, RANK, LEVELS, RANK, String.valueOf(rank)); }
public int getSmeltingVanillaXPBoostMultiplier(int rank) { return getIntValue(SKILLS, SMELTING, VANILLA_XPMULTIPLIER, RANK, String.valueOf(rank)); }
/* SWORDS */
public double getRuptureDamagePlayer() { return getDoubleValue(SKILLS, SWORDS, RUPTURE, DAMAGE_PLAYER); }
public double getRuptureDamageMobs() { return getDoubleValue(SKILLS, SWORDS, RUPTURE, DAMAGE_MOBS); }
public int getRuptureMaxTicks() { return getIntValue(SKILLS, SWORDS, RUPTURE, MAX_TICKS); }
public int getRuptureBaseTicks() { return getIntValue(SKILLS, SWORDS, RUPTURE, BASE_TICKS); }
public double getCounterModifier() { return getDoubleValue(SKILLS, SWORDS, COUNTER_ATTACK, DAMAGE_MODIFIER); }
public double getSerratedStrikesModifier() { return getDoubleValue(SKILLS, SWORDS, SERRATED_STRIKES, DAMAGE_MODIFIER); }
public int getSerratedStrikesTicks() { return getIntValue(SKILLS, SWORDS, SERRATED_STRIKES, RUPTURE, TICKS); }
/* TAMING */
public double getGoreModifier() { return getDoubleValue(SKILLS, TAMING, GORE, MODIFIER); }
public double getFastFoodChance() { return getDoubleValue(SKILLS, TAMING, FAST_FOOD_SERVICE, CHANCE); }
public double getPummelChance() { return getDoubleValue(SKILLS, TAMING, PUMMEL, CHANCE); }
public double getThickFurModifier() { return getDoubleValue(SKILLS, TAMING, THICK_FUR, MODIFIER); }
public double getShockProofModifier() { return getDoubleValue(SKILLS, TAMING, SHOCK_PROOF, MODIFIER); }
public double getSharpenedClawsBonus() { return getDoubleValue(SKILLS, TAMING, SHARPENED_CLAWS, BONUS); }
public double getMinHorseJumpStrength() { return getDoubleValue(SKILLS, TAMING, CALL_OF_THE_WILD, MIN_HORSE_JUMP_STRENGTH); }
public double getMaxHorseJumpStrength() { return getDoubleValue(SKILLS, TAMING, CALL_OF_THE_WILD, MAX_HORSE_JUMP_STRENGTH); }
/* UNARMED */
public boolean getDisarmProtected() { return getBooleanValue(SKILLS, UNARMED, DISARM, ANTI_THEFT); }
/* WOODCUTTING */
}

View File

@ -0,0 +1,59 @@
package com.gmail.nossr50.core.config;
import com.gmail.nossr50.core.skills.PrimarySkillType;
import com.gmail.nossr50.core.skills.child.FamilyTree;
import com.gmail.nossr50.core.util.StringUtils;
import java.util.EnumSet;
public class ChildConfig extends AutoUpdateConfigLoader {
public ChildConfig() {
super("child.yml");
loadKeys();
}
@Override
protected void loadKeys() {
config.setDefaults(YamlConfiguration.loadConfiguration(plugin.getResourceAsReader("child.yml")));
FamilyTree.clearRegistrations(); // when reloading, need to clear statics
for (PrimarySkillType skill : PrimarySkillType.CHILD_SKILLS) {
plugin.debug("Finding parents of " + skill.name());
EnumSet<PrimarySkillType> parentSkills = EnumSet.noneOf(PrimarySkillType.class);
boolean useDefaults = false; // If we had an error we back out and use defaults
for (String name : config.getStringList(StringUtils.getCapitalized(skill.name()))) {
try {
PrimarySkillType parentSkill = PrimarySkillType.valueOf(name.toUpperCase());
FamilyTree.enforceNotChildSkill(parentSkill);
parentSkills.add(parentSkill);
} catch (IllegalArgumentException ex) {
plugin.getLogger().warning(name + " is not a valid skill type, or is a child skill!");
useDefaults = true;
break;
}
}
if (useDefaults) {
parentSkills.clear();
for (String name : config.getDefaults().getStringList(StringUtils.getCapitalized(skill.name()))) {
/* We do less checks in here because it's from inside our jar.
* If they're dedicated enough to have modified it, they can have the errors it may produce.
* Alternatively, this can be used to allow child skills to be parent skills, provided there are no circular dependencies this is an advanced sort of configuration.
*/
parentSkills.add(PrimarySkillType.valueOf(name.toUpperCase()));
}
}
// Register them
for (PrimarySkillType parentSkill : parentSkills) {
plugin.debug("Registering " + parentSkill.name() + " as parent of " + skill.name());
FamilyTree.registerParent(skill, parentSkill);
}
}
FamilyTree.closeRegistration();
}
}

View File

@ -0,0 +1,38 @@
package com.gmail.nossr50.core.config;
/**
* This class is used to define settings for upgrading EXTREMELY OLD versions of mcMMO to newer versions
* It could probably be deleted
*/
public class ChunkConversionOptions {
private static final boolean chunkletsEnabled = true;
private static final int conversionRate = 1;
private static final boolean useEnchantmentBuffs = true;
private static final int uuidConvertAmount = 5;
private static final int mojangRateLimit = 50000;
private static final long mojangLimitPeriod = 600000;
public static boolean getChunkletsEnabled() {
return chunkletsEnabled;
}
public static int getConversionRate() {
return conversionRate;
}
public static boolean useEnchantmentBuffs() {
return useEnchantmentBuffs;
}
public static int getUUIDConvertAmount() {
return uuidConvertAmount;
}
public static int getMojangRateLimit() {
return mojangRateLimit;
}
public static long getMojangLimitPeriod() {
return mojangLimitPeriod;
}
}

View File

@ -0,0 +1,338 @@
package com.gmail.nossr50.core.config;
import com.gmail.nossr50.core.McmmoCore;
import com.google.common.io.Files;
import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.commented.CommentedConfigurationNode;
import ninja.leaping.configurate.loader.ConfigurationLoader;
import ninja.leaping.configurate.yaml.YAMLConfigurationLoader;
import org.yaml.snakeyaml.DumperOptions;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
/**
* Handles loading and cacheing configuration settings from a configurable compatible config file
*/
//@ConfigSerializable
public abstract class Config implements VersionedConfig, Unload {
/* SETTINGS */
private boolean mergeNewKeys;
/* PATH VARS */
public final File DIRECTORY_DATA_FOLDER; //Directory that the file is in
public final String FILE_RELATIVE_PATH; //Relative Path to the file
protected final String DIRECTORY_DEFAULTS = "defaults";
/* LOADERS */
private YAMLConfigurationLoader defaultCopyLoader;
private YAMLConfigurationLoader userCopyLoader;
/* CONFIG FILES */
private File resourceConfigCopy; //Copy of the default config from the JAR (file is copied so that admins can easily compare to defaults)
private File resourceUserCopy; //File in the /$MCMMO_ROOT/mcMMO/ directory that may contain user edited settings
/* ROOT NODES */
private ConfigurationNode userRootNode = null;
private ConfigurationNode defaultRootNode = null;
/* CONFIG MANAGER */
private ConfigurationLoader<CommentedConfigurationNode> configManager;
public Config(String pathToParentFolder, String relativePath, boolean mergeNewKeys) {
//TODO: Check if this works...
this(new File(pathToParentFolder), relativePath, mergeNewKeys);
System.out.println("mcMMO Debug: Don't forget to check if loading config file by string instead of File works...");
}
public Config(File pathToParentFolder, String relativePath, boolean mergeNewKeys) {
/*
* These must be at the top
*/
this.mergeNewKeys = mergeNewKeys; //Whether or not we add new keys when they are found
mkdirDefaults(); // Make our default config dir
DIRECTORY_DATA_FOLDER = pathToParentFolder; //Data Folder for our plugin
FILE_RELATIVE_PATH = relativePath; //Relative path to config from a parent folder
//Attempt IO Operations
try {
//Makes sure we have valid Files corresponding to this config
initConfigFiles();
//Init MainConfig Loaders
initConfigLoaders();
//Load MainConfig Nodes
loadConfig();
//Attempt to update user file, and then load it into memory
readConfig();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Initializes the default copy File and the user config File
* @throws IOException
*/
private void initConfigFiles() throws IOException {
//Init our config copy
resourceConfigCopy = initDefaultConfig();
//Init the user file
resourceUserCopy = initUserConfig();
}
/**
* Loads the root node for the default config File and user config File
*/
private void loadConfig()
{
try {
final ConfigurationNode defaultConfig = this.defaultCopyLoader.load();
defaultRootNode = defaultConfig;
final ConfigurationNode userConfig = this.userCopyLoader.load();
userRootNode = userConfig;
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Initializes the YAMLConfigurationLoaders for this config
*/
private void initConfigLoaders()
{
this.defaultCopyLoader = YAMLConfigurationLoader.builder().setPath(resourceConfigCopy.toPath()).setFlowStyle(DumperOptions.FlowStyle.BLOCK).build();
this.userCopyLoader = YAMLConfigurationLoader.builder().setPath(resourceUserCopy.toPath()).setFlowStyle(DumperOptions.FlowStyle.FLOW).build();
}
/**
* Copies a new file from the JAR to the defaults directory and uses that new file to initialize our resourceConfigCopy
* @see Config#resourceConfigCopy
* @throws IOException
*/
private File initDefaultConfig() throws IOException {
return copyDefaultFromJar(getDefaultConfigCopyRelativePath(), true);
}
/**
* Attemps to load the config file if it exists, if it doesn't it copies a new one from within the JAR
* @return user config File
* @see Config#resourceUserCopy
* @throws IOException
*/
private File initUserConfig() throws IOException {
File userCopy = new File(DIRECTORY_DATA_FOLDER, FILE_RELATIVE_PATH); //Load the user file;
if(userCopy.exists())
{
// Yay
return userCopy;
}
else
{
//If it's gone we copy default files
//Note that we don't copy the values from the default copy put in /defaults/ that file exists only as a reference to admins and is unreliable
return copyDefaultFromJar(FILE_RELATIVE_PATH, false);
}
}
/**
* Used to make a new config file at a specified relative output path inside the data directory by copying the matching file found in that same relative path within the JAR
* @param relativeOutputPath the path to the output file
* @param deleteOld whether or not to delete the existing output file on disk
* @return a copy of the default config within the JAR
* @throws IOException
*/
private File copyDefaultFromJar(String relativeOutputPath, boolean deleteOld) throws IOException
{
/*
* Gen a Default config from inside the JAR
*/
McmmoCore.getLogger().info("Preparing to copy internal resource file (in JAR) - "+FILE_RELATIVE_PATH);
InputStream inputStream = McmmoCore.getResource(FILE_RELATIVE_PATH);
byte[] buffer = new byte[inputStream.available()];
inputStream.read(buffer);
//This is a copy of the default file, which we will overwrite every time mcMMO loads
File targetFile = new File(DIRECTORY_DATA_FOLDER, relativeOutputPath);
//Wipe old default file on disk
if (targetFile.exists() && deleteOld)
{
McmmoCore.getLogger().info("Updating file " + relativeOutputPath);
targetFile.delete(); //Necessary?
}
if(!targetFile.exists())
{
targetFile.getParentFile().mkdirs();
targetFile.createNewFile(); //New File Boys
}
Files.write(buffer, targetFile);
McmmoCore.getLogger().info("Created config file - " + relativeOutputPath);
inputStream.close(); //Close the input stream
return targetFile;
}
/**
* The path to the defaults directory
* @return the path to the defaults directory
*/
private String getDefaultConfigCopyRelativePath() {
return DIRECTORY_DEFAULTS + File.separator + FILE_RELATIVE_PATH;
}
/**
* Creates the defaults directory
*/
private void mkdirDefaults() {
//Make Default Subdirectory
File defaultsDir = new File (DIRECTORY_DATA_FOLDER, "defaults");
if(!defaultsDir.exists())
defaultsDir.mkdir();
}
/**
* Configs are versioned based on when they had significant changes to keys
* @return current MainConfig Version String
*/
public String getVersion()
{
return String.valueOf(getConfigVersion());
}
/**
* Attempts to read the loaded config file
* MainConfig will have any necessary updates applied
* MainConfig will be compared to the default config to see if it is missing any nodes
* MainConfig will have any missing nodes inserted with their default value
*/
public void readConfig() {
McmmoCore.getLogger().info("Attempting to read " + FILE_RELATIVE_PATH + ".");
int version = this.userRootNode.getNode("ConfigVersion").getInt();
McmmoCore.getLogger().info(FILE_RELATIVE_PATH + " version is " + version);
//Update our config
updateConfig();
}
/**
* Compares the users config file to the default and adds any missing nodes and applies any necessary updates
*/
private void updateConfig()
{
McmmoCore.getLogger().info(defaultRootNode.getChildrenMap().size() +" items in default children map");
McmmoCore.getLogger().info(userRootNode.getChildrenMap().size() +" items in default root map");
// Merge Values from default
if(mergeNewKeys)
userRootNode = userRootNode.mergeValuesFrom(defaultRootNode);
removeOldKeys();
// Update config version
updateConfigVersion();
//Attempt to save
try {
saveUserCopy();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Saves the current state information of the config to the users copy (which they may edit)
* @throws IOException
*/
private void saveUserCopy() throws IOException
{
McmmoCore.getLogger().info("Saving new node");
userCopyLoader.save(userRootNode);
}
/**
* Performs any necessary operations to update this config
*/
private void updateConfigVersion() {
// Set a version for our config
this.userRootNode.getNode("ConfigVersion").setValue(getConfigVersion());
McmmoCore.getLogger().info("Updated config to ["+getConfigVersion()+"] - " + FILE_RELATIVE_PATH);
}
/**
* Returns the root node of this config
* @return the root node of this config
*/
protected ConfigurationNode getUserRootNode() {
return userRootNode;
}
/**
* Grabs an int from the specified node
* @param path
* @return the int from the node, null references will zero initialize
*/
public int getIntValue(String... path)
{
return userRootNode.getNode(path).getInt();
}
/**
* Grabs a double from the specified node
* @param path
* @return the double from the node, null references will zero initialize
*/
public double getDoubleValue(String... path)
{
return userRootNode.getNode(path).getDouble();
}
/**
* Grabs a boolean from the specified node
* @param path
* @return the boolean from the node, null references will zero initialize
*/
public boolean getBooleanValue(String... path)
{
return userRootNode.getNode(path).getBoolean();
}
/**
* Grabs a string from the specified node
* @param path
* @return the string from the node, null references will zero initialize
*/
public String getStringValue(String... path)
{
return userRootNode.getNode(path).getString();
}
/**
* Checks to see if a node exists in the user's config file
* @param path path to the node
* @return true if the node exists
*/
public boolean hasNode(String... path) {
return (userRootNode.getNode(path) != null);
}
}

View File

@ -0,0 +1,7 @@
package com.gmail.nossr50.core.config;
import java.util.Collection;
public interface ConfigCollection<T> {
Collection<T> getLoadedCollection();
}

View File

@ -0,0 +1,19 @@
package com.gmail.nossr50.core.config;
import java.io.File;
/**
* Represents a config file that registers keys after its initialized
*/
public abstract class ConfigCollections extends Config implements RegistersKeys, ConfigCollection {
public ConfigCollections(String pathToParentFolder, String relativePath, boolean mergeNewKeys) {
super(pathToParentFolder, relativePath, mergeNewKeys);
loadKeys();
}
public ConfigCollections(File pathToParentFolder, String relativePath, boolean mergeNewKeys) {
super(pathToParentFolder, relativePath, mergeNewKeys);
loadKeys();
}
}

View File

@ -0,0 +1,44 @@
package com.gmail.nossr50.core.config;
import com.gmail.nossr50.core.McmmoCore;
import java.io.File;
import java.util.List;
/**
* This class is used for config files that validate their entries
*/
public abstract class ConfigValidated extends Config implements DefaultKeys {
public ConfigValidated(String parentFolderPath, String relativePath, boolean mergeNewKeys)
{
super(parentFolderPath, relativePath, mergeNewKeys);
validateEntries();
}
public ConfigValidated(File parentFolderFile, String relativePath, boolean mergeNewKeys)
{
super(parentFolderFile, relativePath, mergeNewKeys);
validateEntries();
}
/**
* Prints all errors found when validating the config
*/
private void validateEntries()
{
/*
* Print Errors about Keys
*/
List<String> validKeyErrors = validateKeys(); // Validate Keys
if(validKeyErrors != null && validKeyErrors.size() > 0)
{
for(String error : validKeyErrors)
{
McmmoCore.getLogger().severe(error);
}
}
}
}

View File

@ -0,0 +1,61 @@
package com.gmail.nossr50.core.config;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.skills.PrimarySkillType;
import com.gmail.nossr50.core.skills.subskills.AbstractSubSkill;
import com.gmail.nossr50.core.util.StringUtils;
public class CoreSkillsConfig extends Config {
private static CoreSkillsConfig instance;
public CoreSkillsConfig() {
super(McmmoCore.getDataFolderPath().getAbsoluteFile(),"coreskills.yml", true);
}
public static CoreSkillsConfig getInstance() {
if (instance == null)
return new CoreSkillsConfig();
return instance;
}
/**
* The version of this config
*
* @return
*/
@Override
public double getConfigVersion() {
return 1;
}
@Override
public void unload() {
instance = null;
}
/*
* Skill Settings
*/
/**
* Whether or not a skill is enabled
* Defaults true
*
* @param abstractSubSkill SubSkill definition to check
* @return true if subskill is enabled
*/
public boolean isSkillEnabled(AbstractSubSkill abstractSubSkill) {
return getBooleanValue(StringUtils.getCapitalized(abstractSubSkill.getPrimarySkill().toString()) + "." + abstractSubSkill.getConfigKeyName() + ".Enabled", true);
}
/**
* Whether or not this primary skill is enabled
*
* @param primarySkillType target primary skill
* @return true if enabled
*/
public boolean isPrimarySkillEnabled(PrimarySkillType primarySkillType) {
return getBooleanValue(StringUtils.getCapitalized(primarySkillType.toString()) + ".Enabled", true);
}
}

View File

@ -0,0 +1,10 @@
package com.gmail.nossr50.core.config;
import java.util.List;
/**
* This is for config validation
*/
public interface DefaultKeys {
List<String> validateKeys();
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,124 @@
package com.gmail.nossr50.core.config;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.skills.SubSkillType;
import com.gmail.nossr50.core.skills.subskills.AbstractSubSkill;
import java.util.ArrayList;
import java.util.List;
public class RankConfig extends ConfigValidated {
private static RankConfig instance;
public RankConfig() {
super(McmmoCore.getDataFolderPath().getAbsoluteFile(),"skillranks.yml", true);
this.instance = this;
}
public static RankConfig getInstance() {
if (instance == null)
return new RankConfig();
return instance;
}
@Override
public void unload() {
instance = null;
}
/**
* The version of this config
*
* @return
*/
@Override
public double getConfigVersion() {
return 1;
}
@Override
public List<String> validateKeys() {
List<String> reason = new ArrayList<String>();
/*
* In the future this method will check keys for all skills, but for now it only checks overhauled skills
*/
checkKeys(reason);
return reason;
}
/**
* Returns the unlock level for a subskill depending on the gamemode
*
* @param subSkillType target subskill
* @param rank the rank we are checking
* @return the level requirement for a subskill at this particular rank
*/
public int getSubSkillUnlockLevel(SubSkillType subSkillType, int rank) {
String key = subSkillType.getRankConfigAddress();
return findRankByRootAddress(rank, key);
}
/**
* Returns the unlock level for a subskill depending on the gamemode
*
* @param abstractSubSkill target subskill
* @param rank the rank we are checking
* @return the level requirement for a subskill at this particular rank
*/
public int getSubSkillUnlockLevel(AbstractSubSkill abstractSubSkill, int rank) {
String key = abstractSubSkill.getPrimaryKeyName() + "." + abstractSubSkill.getConfigKeyName();
return findRankByRootAddress(rank, key);
}
/**
* Returns the unlock level for a subskill depending on the gamemode
*
* @param key root address of the subskill in the rankskills.yml file
* @param rank the rank we are checking
* @return the level requirement for a subskill at this particular rank
*/
private int findRankByRootAddress(int rank, String key) {
String scalingKey = MainConfig.getInstance().getIsRetroMode() ? ".RetroMode." : ".Standard.";
String targetRank = "Rank_" + rank;
key += scalingKey;
key += targetRank;
return getIntValue(key);
}
/**
* Checks for valid keys for subskill ranks
*/
private void checkKeys(List<String> reasons) {
//For now we will only check ranks of stuff I've overhauled
for (SubSkillType subSkillType : SubSkillType.values()) {
//Keeping track of the rank requirements and making sure there are no logical errors
int curRank = 0;
int prevRank = 0;
for (int x = 0; x < subSkillType.getNumRanks(); x++) {
if (curRank > 0)
prevRank = curRank;
curRank = getSubSkillUnlockLevel(subSkillType, x);
//Do we really care if its below 0? Probably not
if (curRank < 0) {
reasons.add(subSkillType.getAdvConfigAddress() + ".Rank_Levels.Rank_" + curRank + ".LevelReq should be above or equal to 0!");
}
if (prevRank > curRank) {
//We're going to allow this but we're going to warn them
plugin.getLogger().info("You have the ranks for the subskill " + subSkillType.toString() + " set up poorly, sequential ranks should have ascending requirements");
}
}
}
}
}

View File

@ -0,0 +1,11 @@
package com.gmail.nossr50.core.config;
/**
* A class that registers keys
*/
public interface RegistersKeys {
/**
* Loads up keys
*/
void loadKeys();
}

View File

@ -0,0 +1,77 @@
package com.gmail.nossr50.core.config;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.util.sounds.SoundType;
import java.util.ArrayList;
import java.util.List;
public class SoundConfig extends ConfigValidated {
private static SoundConfig instance;
public SoundConfig() {
super(McmmoCore.getDataFolderPath().getAbsoluteFile(), "sounds.yml", true);
this.instance = this;
}
public static SoundConfig getInstance() {
if (instance == null)
return new SoundConfig();
return instance;
}
@Override
public void unload() {
instance = null;
}
/**
* The version of this config
*
* @return
*/
@Override
public double getConfigVersion() {
return 1;
}
@Override
public List<String> validateKeys() {
ArrayList<String> reasons = new ArrayList<>();
for (SoundType soundType : SoundType.values()) {
if (getDoubleValue("Sounds." + soundType.toString() + ".Volume") < 0) {
reasons.add("[mcMMO] Sound volume cannot be below 0 for " + soundType.toString());
}
//Sounds with custom pitching don't use pitch values
if (!soundType.usesCustomPitch()) {
if (getDoubleValue("Sounds." + soundType.toString() + ".Pitch") < 0) {
reasons.add("[mcMMO] Sound pitch cannot be below 0 for " + soundType.toString());
}
}
}
return reasons;
}
public float getMasterVolume() {
return (float) getDoubleValue("Sounds.MasterVolume", 1.0);
}
public float getVolume(SoundType soundType) {
String key = "Sounds." + soundType.toString() + ".Volume";
return (float) getDoubleValue(key);
}
public float getPitch(SoundType soundType) {
String key = "Sounds." + soundType.toString() + ".Pitch";
return (float) getDoubleValue(key);
}
public boolean getIsEnabled(SoundType soundType) {
String key = "Sounds." + soundType.toString() + ".Enabled";
return getBooleanValue(key, true);
}
}

View File

@ -0,0 +1,8 @@
package com.gmail.nossr50.core.config;
/**
* Unloads values, sort of like a constructor
*/
public interface Unload {
void unload();
}

View File

@ -0,0 +1,12 @@
package com.gmail.nossr50.core.config;
/**
* Represents a config that is version checked
*/
public interface VersionedConfig {
/**
* The version of this config
* @return
*/
double getConfigVersion();
}

View File

@ -0,0 +1,74 @@
package com.gmail.nossr50.core.config;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.mcmmo.world.World;
import java.io.*;
import java.util.ArrayList;
/**
* Blacklist certain features in certain worlds
*/
public class WorldBlacklist {
private static ArrayList<String> blacklist;
private final String blackListFileName = "world_blacklist.txt";
public WorldBlacklist() {
blacklist = new ArrayList<>();
init();
}
public static boolean isWorldBlacklisted(World world) {
for (String s : blacklist) {
if (world.getName().equalsIgnoreCase(s))
return true;
}
return false;
}
public void init() {
//Make the blacklist file if it doesn't exist
//TODO: Check if this works
File blackListFile = new File(McmmoCore.getDataFolderPath().getAbsoluteFile() + File.separator + blackListFileName);
try {
if (!blackListFile.exists())
blackListFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
//Load up the blacklist
loadBlacklist(blackListFile);
//registerFlags();
}
private void loadBlacklist(File blackListFile) {
try {
FileReader fileReader = new FileReader(blackListFile);
BufferedReader bufferedReader = new BufferedReader(fileReader);
String currentLine;
while ((currentLine = bufferedReader.readLine()) != null) {
if (currentLine.length() == 0)
continue;
if (!blacklist.contains(currentLine))
blacklist.add(currentLine);
}
//Close readers
bufferedReader.close();
fileReader.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
McmmoCore.getLogger().info(blacklist.size() + " entries in mcMMO World Blacklist");
}
}

View File

@ -0,0 +1,107 @@
package com.gmail.nossr50.core.config.collectionconfigs;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.config.ConfigCollection;
import com.gmail.nossr50.core.config.ConfigCollections;
import com.gmail.nossr50.core.skills.child.salvage.salvageables.Salvageable;
import com.gmail.nossr50.core.skills.primary.repair.repairables.Repairable;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;
/**
* Represents a collection of config files that serve a similar purpose
* For example, files named repair.*.yml are all loaded into memory, this lets admins keep their config files clean
*
* To be honest I'm not sure how many people make use of this system, but I'm keeping it since its been in mcMMO for like 6+ years
*/
public final class MultiConfigManager {
public static final String DEFAULT_MULTICONFIG_FILENAME_SUFFIX = ".vanilla.yml";
//Configs
public RepairConfig vanillaRepairConfig; //This is the main config file that mcMMO will copy out
public SalvageConfig vanillaSalvageConfig;
private static List<Repairable> repairables;
private static List<Salvageable> salvageables;
public MultiConfigManager(String fileNamePrefix)
{
//init Collections
repairables = new ArrayList<>();
salvageables = new ArrayList<>();
//init vanilla configs
vanillaRepairConfig = new RepairConfig(getVanillaConfigName("repair"));
vanillaSalvageConfig = new SalvageConfig(getVanillaConfigName("salvage"));
//add valid vanilla collections to main collection
repairables.addAll(vanillaRepairConfig.getLoadedCollection());
salvageables.addAll(vanillaSalvageConfig.getLoadedCollection());
//add valid custom collections to main collection
loadCustomCollections("repair", repairables, RepairConfig.class);
loadCustomCollections("salvage", salvageables, SalvageConfig.class);
}
/**
* mcMMO allows collection config files to be named things like repair.whatevernameyouwanthere.yml and so on,
* these files are treated in the same way as the vanilla file. They serve the purpose of organization
* @param configPrefix the prefix of the file name, for example "repair", "salvage", etc
* @param collection the collection that will be added to
*/
public void loadCustomCollections(String configPrefix, Collection<?> collection, Class<? extends ConfigCollection> configClass)
{
String vanillaConfigFileName = getVanillaConfigName(configPrefix);
//Find other files
Pattern pattern = Pattern.compile(configPrefix+"\\.(?:.+)\\.yml");
File dataFolder = McmmoCore.getDataFolderPath();
for (String fileName : dataFolder.list()) {
//Vanilla Config is already loaded
if(fileName.equalsIgnoreCase(vanillaConfigFileName))
continue;
//Find files that match the pattern
if (!pattern.matcher(fileName).matches()) {
continue;
}
//Init file
File currentFile = new File(dataFolder, fileName);
//Make sure its not a directory (needed?)
if(currentFile.isDirectory())
continue;
try {
ConfigCollections customConfig = configClass.getClass().getConstructor(fileName).newInstance();
collection.addAll(customConfig.getLoadedCollection());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
private String getVanillaConfigName(String configPrefix)
{
return configPrefix+DEFAULT_MULTICONFIG_FILENAME_SUFFIX;
}
}

View File

@ -0,0 +1,186 @@
package com.gmail.nossr50.core.config.collectionconfigs;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.config.ConfigCollections;
import com.gmail.nossr50.core.mcmmo.item.ItemStack;
import com.gmail.nossr50.core.skills.ConfigItemCategory;
import com.gmail.nossr50.core.skills.MaterialType;
import com.gmail.nossr50.core.skills.primary.repair.repairables.Repairable;
import com.gmail.nossr50.core.skills.primary.repair.repairables.RepairableFactory;
import com.gmail.nossr50.core.util.InvalidItemException;
import com.gmail.nossr50.core.util.ItemUtils;
import com.gmail.nossr50.core.util.skills.SkillUtils;
import ninja.leaping.configurate.ConfigurationNode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
/**
* This config
*/
public class RepairConfig extends ConfigCollections {
private List<Repairable> repairables;
public RepairConfig(String fileName) {
super(McmmoCore.getDataFolderPath().getAbsoluteFile(), fileName, false);
}
@Override
public void unload() {
repairables = null;
}
@Override
public Collection getLoadedCollection() {
return repairables == null ? new ArrayList<Repairable>() : repairables;
}
/**
* The version of this config
*
* @return
*/
@Override
public double getConfigVersion() {
return 1;
}
@Override
public void loadKeys() {
repairables = new ArrayList<Repairable>();
ConfigurationNode repairablesNode = getUserRootNode().getNode("Repairables");
List<? extends ConfigurationNode> repairablesNodeChildrenList = repairablesNode.getChildrenList();
Iterator<? extends ConfigurationNode> configIter = repairablesNodeChildrenList.iterator();
for(Iterator<? extends ConfigurationNode> i = repairablesNodeChildrenList.iterator(); i.hasNext();)
{
ConfigurationNode iterNode = i.next();
//TODO: Verify that this is getting the key
String key = iterNode.getKey().toString(); //Get the String of the node
// Validate all the things!
List<String> reason = new ArrayList<String>();
try {
// ItemStack Material
ConfigItemCategory configItemCategory = ItemUtils.matchItemType(key);
} catch (InvalidItemException e) {
e.printStackTrace();
}
if (itemType == null) {
reason.add("Invalid material: " + key);
}
// Repair Material Type
MaterialType repairMaterialType = MaterialType.OTHER;
String repairMaterialTypeString = getStringValue("Repairables." + key + ".MaterialType", "OTHER");
if (!config.contains("Repairables." + key + ".MaterialType") && itemType != null) {
ItemStack repairItem = ItemStack.makeNew(itemType);
if (ItemUtils.isWoodTool(repairItem)) {
repairMaterialType = MaterialType.WOOD;
} else if (ItemUtils.isStoneTool(repairItem)) {
repairMaterialType = MaterialType.STONE;
} else if (ItemUtils.isStringTool(repairItem)) {
repairMaterialType = MaterialType.STRING;
} else if (ItemUtils.isLeatherArmor(repairItem)) {
repairMaterialType = MaterialType.LEATHER;
} else if (ItemUtils.isIronArmor(repairItem) || ItemUtils.isIronTool(repairItem)) {
repairMaterialType = MaterialType.IRON;
} else if (ItemUtils.isGoldArmor(repairItem) || ItemUtils.isGoldTool(repairItem)) {
repairMaterialType = MaterialType.GOLD;
} else if (ItemUtils.isDiamondArmor(repairItem) || ItemUtils.isDiamondTool(repairItem)) {
repairMaterialType = MaterialType.DIAMOND;
}
} else {
try {
repairMaterialType = MaterialType.valueOf(repairMaterialTypeString);
} catch (IllegalArgumentException ex) {
reason.add(key + " has an invalid MaterialType of " + repairMaterialTypeString);
}
}
// Repair Material
String repairMaterialName = getStringValue("Repairables." + key + ".RepairMaterial");
Material repairMaterial = (repairMaterialName == null ? repairMaterialType.getDefaultMaterial() : Material.matchMaterial(repairMaterialName));
if (repairMaterial == null) {
reason.add(key + " has an invalid repair material: " + repairMaterialName);
}
// Maximum Durability
short maximumDurability = (itemType != null ? itemType.getMaxDurability() : (short) getIntValue("Repairables." + key + ".MaximumDurability"));
if (maximumDurability <= 0) {
maximumDurability = (short) getIntValue("Repairables." + key + ".MaximumDurability");
}
if (maximumDurability <= 0) {
reason.add("Maximum durability of " + key + " must be greater than 0!");
}
// ItemStack Type
ConfigItemCategory repairConfigItemCategory = ConfigItemCategory.OTHER;
String repairItemTypeString = getStringValue("Repairables." + key + ".ItemType", "OTHER");
if (!config.contains("Repairables." + key + ".ItemType") && itemType != null) {
ItemStack repairItem = new ItemStack(itemType);
if (ItemUtils.isMinecraftTool(repairItem)) {
repairConfigItemCategory = ConfigItemCategory.TOOL;
} else if (ItemUtils.isArmor(repairItem)) {
repairConfigItemCategory = ConfigItemCategory.ARMOR;
}
} else {
try {
repairConfigItemCategory = ConfigItemCategory.valueOf(repairItemTypeString);
} catch (IllegalArgumentException ex) {
reason.add(key + " has an invalid ItemType of " + repairItemTypeString);
}
}
byte repairMetadata = (byte) getIntValue("Repairables." + key + ".RepairMaterialMetadata", -1);
int minimumLevel = getIntValue("Repairables." + key + ".MinimumLevel");
double xpMultiplier = getDoubleValue("Repairables." + key + ".XpMultiplier", 1);
if (minimumLevel < 0) {
reason.add(key + " has an invalid MinimumLevel of " + minimumLevel);
}
// Minimum Quantity
int minimumQuantity = (itemType != null ? SkillUtils.getRepairAndSalvageQuantities(new ItemStack(itemType), repairMaterial, repairMetadata) : getIntValue("Repairables." + key + ".MinimumQuantity", 2));
if (minimumQuantity <= 0 && itemType != null) {
minimumQuantity = getIntValue("Repairables." + key + ".MinimumQuantity", 2);
}
if (minimumQuantity <= 0) {
reason.add("Minimum quantity of " + key + " must be greater than 0!");
}
if (noErrorsInRepairable(reason)) {
Repairable repairable = RepairableFactory.getRepairable(itemType, repairMaterial, repairMetadata, minimumLevel, minimumQuantity, maximumDurability, repairConfigItemCategory, repairMaterialType, xpMultiplier);
repairables.add(repairable);
}
}
}
/**
* Check if there are any errors for this repairable and if there are reports them to console
* @param issues errors related to loading a repairable
* @return returns true if there are no errors for this repairable
*/
private boolean noErrorsInRepairable(List<String> issues) {
for (String issue : issues) {
McmmoCore.getLogger().warning(issue);
}
return issues.isEmpty();
}
}

View File

@ -0,0 +1,169 @@
package com.gmail.nossr50.core.config.collectionconfigs;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.config.Config;
import com.gmail.nossr50.core.config.ConfigCollection;
import com.gmail.nossr50.core.config.ConfigCollections;
import com.gmail.nossr50.core.mcmmo.item.ItemStack;
import com.gmail.nossr50.core.skills.MaterialType;
import com.gmail.nossr50.core.skills.child.salvage.salvageables.Salvageable;
import com.gmail.nossr50.core.skills.child.salvage.salvageables.SalvageableFactory;
import com.gmail.nossr50.core.util.ItemUtils;
import com.gmail.nossr50.core.util.skills.SkillUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
public class SalvageConfig extends ConfigCollections {
private List<Salvageable> salvageables;
public SalvageConfig(String fileName) {
super(McmmoCore.getDataFolderPath().getAbsoluteFile(), fileName, false);
}
@Override
public Collection getLoadedCollection() {
return salvageables == null ? new ArrayList<Salvageable>() : salvageables;
}
@Override
public void unload() {
salvageables = null;
}
/**
* The version of this config
*
* @return
*/
@Override
public double getConfigVersion() {
return 1;
}
@Override
protected void loadKeys() {
salvageables = new ArrayList<Salvageable>();
ConfigurationSection section = config.getConfigurationSection("Salvageables");
Set<String> keys = section.getKeys(false);
for (String key : keys) {
// Validate all the things!
List<String> reason = new ArrayList<String>();
// ItemStack Material
Material itemMaterial = Material.matchMaterial(key);
if (itemMaterial == null) {
reason.add("Invalid material: " + key);
}
// Salvage Material Type
MaterialType salvageMaterialType = MaterialType.OTHER;
String salvageMaterialTypeString = getStringValue("Salvageables." + key + ".MaterialType", "OTHER");
if (!config.contains("Salvageables." + key + ".MaterialType") && itemMaterial != null) {
ItemStack salvageItem = new ItemStack(itemMaterial);
if (ItemUtils.isWoodTool(salvageItem)) {
salvageMaterialType = MaterialType.WOOD;
} else if (ItemUtils.isStoneTool(salvageItem)) {
salvageMaterialType = MaterialType.STONE;
} else if (ItemUtils.isStringTool(salvageItem)) {
salvageMaterialType = MaterialType.STRING;
} else if (ItemUtils.isLeatherArmor(salvageItem)) {
salvageMaterialType = MaterialType.LEATHER;
} else if (ItemUtils.isIronArmor(salvageItem) || ItemUtils.isIronTool(salvageItem)) {
salvageMaterialType = MaterialType.IRON;
} else if (ItemUtils.isGoldArmor(salvageItem) || ItemUtils.isGoldTool(salvageItem)) {
salvageMaterialType = MaterialType.GOLD;
} else if (ItemUtils.isDiamondArmor(salvageItem) || ItemUtils.isDiamondTool(salvageItem)) {
salvageMaterialType = MaterialType.DIAMOND;
}
} else {
try {
salvageMaterialType = MaterialType.valueOf(salvageMaterialTypeString.replace(" ", "_").toUpperCase());
} catch (IllegalArgumentException ex) {
reason.add(key + " has an invalid MaterialType of " + salvageMaterialTypeString);
}
}
// Salvage Material
String salvageMaterialName = getStringValue("Salvageables." + key + ".SalvageMaterial");
Material salvageMaterial = (salvageMaterialName == null ? salvageMaterialType.getDefaultMaterial() : Material.matchMaterial(salvageMaterialName));
if (salvageMaterial == null) {
reason.add(key + " has an invalid salvage material: " + salvageMaterialName);
}
// Maximum Durability
short maximumDurability = (itemMaterial != null ? itemMaterial.getMaxDurability() : (short) getIntValue("Salvageables." + key + ".MaximumDurability"));
// ItemStack Type
ItemType salvageItemType = ItemType.OTHER;
String salvageItemTypeString = getStringValue("Salvageables." + key + ".ItemType", "OTHER");
if (!config.contains("Salvageables." + key + ".ItemType") && itemMaterial != null) {
ItemStack salvageItem = new ItemStack(itemMaterial);
if (ItemUtils.isMinecraftTool(salvageItem)) {
salvageItemType = ItemType.TOOL;
} else if (ItemUtils.isArmor(salvageItem)) {
salvageItemType = ItemType.ARMOR;
}
} else {
try {
salvageItemType = ItemType.valueOf(salvageItemTypeString.replace(" ", "_").toUpperCase());
} catch (IllegalArgumentException ex) {
reason.add(key + " has an invalid ItemType of " + salvageItemTypeString);
}
}
byte salvageMetadata = (byte) getIntValue("Salvageables." + key + ".SalvageMaterialMetadata", -1);
int minimumLevel = getIntValue("Salvageables." + key + ".MinimumLevel");
double xpMultiplier = getDoubleValue("Salvageables." + key + ".XpMultiplier", 1);
if (minimumLevel < 0) {
reason.add(key + " has an invalid MinimumLevel of " + minimumLevel);
}
// Maximum Quantity
int maximumQuantity = (itemMaterial != null ? SkillUtils.getRepairAndSalvageQuantities(new ItemStack(itemMaterial), salvageMaterial, salvageMetadata) : getIntValue("Salvageables." + key + ".MaximumQuantity", 2));
if (maximumQuantity <= 0 && itemMaterial != null) {
maximumQuantity = getIntValue("Salvageables." + key + ".MaximumQuantity", 1);
}
int configMaximumQuantity = getIntValue("Salvageables." + key + ".MaximumQuantity", -1);
if (configMaximumQuantity > 0) {
maximumQuantity = configMaximumQuantity;
}
if (maximumQuantity <= 0) {
reason.add("Maximum quantity of " + key + " must be greater than 0!");
}
if (noErrorsInSalvageable(reason)) {
Salvageable salvageable = SalvageableFactory.getSalvageable(itemMaterial, salvageMaterial, salvageMetadata, minimumLevel, maximumQuantity, maximumDurability, salvageItemType, salvageMaterialType, xpMultiplier);
salvageables.add(salvageable);
}
}
}
private boolean noErrorsInSalvageable(List<String> issues) {
if (!issues.isEmpty()) {
plugin.getLogger().warning("Errors have been found in: " + fileName);
plugin.getLogger().warning("The following issues were found:");
}
for (String issue : issues) {
plugin.getLogger().warning(issue);
}
return issues.isEmpty();
}
}

View File

@ -0,0 +1,448 @@
package com.gmail.nossr50.core.config.experience;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.config.ConfigValidated;
import com.gmail.nossr50.core.datatypes.experience.FormulaType;
import com.gmail.nossr50.core.mcmmo.BlockType;
import com.gmail.nossr50.core.mcmmo.bossbars.BarColor;
import com.gmail.nossr50.core.mcmmo.bossbars.BarStyle;
import com.gmail.nossr50.core.mcmmo.entity.EntityType;
import com.gmail.nossr50.core.skills.MaterialType;
import com.gmail.nossr50.core.skills.PotionStage;
import com.gmail.nossr50.core.skills.PrimarySkillType;
import com.gmail.nossr50.core.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
public class ExperienceConfig extends ConfigValidated {
public static final String EXPLOIT_FIX = "ExploitFix";
public static final String ENDERMAN_ENDERMITE_FARMS = "EndermanEndermiteFarms";
public static final String EXPERIENCE = "Experience";
public static final String EXPERIENCE_FORMULA = EXPERIENCE + "_Formula";
public static final String CURVE = "Curve";
public static final String VALUES = "_Values";
public static final String MULTIPLIER = "multiplier";
public static final String BASE = "base";
public static final String EXPONENT = "exponent";
public static final String MULTIPLIER1 = "Multiplier";
public static final String GLOBAL = "Global";
public static final String MOBSPAWNERS = "Mobspawners";
public static final String BREEDING = "Breeding";
public static final String MODIFIER = "Modifier";
public static final String CUSTOM_XP_PERK = "Custom_XP_Perk";
public static final String BOOST = "Boost";
public static final String DIMISHED_RETURNS = "Dimished_Returns";
public static final String GUARANTEED_MINIMUM_PERCENTAGE = "Guaranteed_Minimum_Percentage";
public static final String DIMINISHED_RETURNS = "Diminished_Returns";
public static final String ENABLE = "Enable";
public static final String ENABLED = ENABLE + "d";
public static final String TIME_INTERVAL = "Time_Interval";
public static final String CONVERSION = "Conversion";
public static final String EXP = "Exp_";
public static final String PVP = "PVP";
public static final String REWARDS = "Rewards";
public static final String COMBAT = "Combat";
public static final String ANIMALS = "Animals";
public static final String BARS = "_Bars";
public static final String UPDATE = "Update";
public static final String PASSIVE = "Passive";
public static final String THIS_MAY_CAUSE_LAG = "ThisMayCauseLag";
public static final String ALWAYS = "Always";
public static final String TITLES_WHEN_XPIS_GAINED = "TitlesWhenXPIsGained";
public static final String EXTRA_DETAILS = "ExtraDetails";
public static final String COLOR = "Color";
public static final String BAR_STYLE = "BarStyle";
public static final String ACROBATICS = "Acrobatics";
public static final String DODGE = "Dodge";
public static final String ROLL = "Roll";
public static final String FALL = "Fall";
public static final String FEATHER = "Feather";
public static final String ALCHEMY = "Alchemy";
public static final String POTION_STAGE = "Potion_Stage_";
public static final String ARCHERY = "Archery";
public static final String DISTANCE = "Distance_";
public static final String FISHING = "Fishing";
public static final String SHAKE = "Shake";
public static final String REPAIR = "Repair";
public static final String BASE1 = "Base";
public static final String TAMING = "Taming";
public static final String ANIMAL_TAMING = "Animal_Taming";
public static final String PARTY = "Party";
public static final String THRESHOLD = "Threshold";
public static final String CUMULATIVE = "Cumulative_";
public static final String OCELOT = "Ocelot";
public static final String WOLF = "Wolf";
public static final String FEATHER_FALL_MULTIPLIER = "FeatherFall_Multiplier";
private static ExperienceConfig instance;
//TODO: Should merge be false? Seems okay to leave it as true..
private ExperienceConfig() {
super(McmmoCore.getDataFolderPath().getAbsoluteFile(), "experience.yml", true);
}
public static ExperienceConfig getInstance() {
if (instance == null) {
instance = new ExperienceConfig();
}
return instance;
}
/**
* The version of this config
*
* @return
*/
@Override
public double getConfigVersion() {
return 1;
}
@Override
public void unload() {
instance = null; //TODO: this might be a bit problematic
}
@Override
public List<String> validateKeys() {
List<String> reason = new ArrayList<String>();
/*
* FORMULA SETTINGS
*/
/* Curve values */
if (getMultiplier(FormulaType.EXPONENTIAL) <= 0) {
reason.add(EXPERIENCE_FORMULA + ".Exponential" + VALUES + "." + MULTIPLIER + " should be greater than 0!");
}
if (getMultiplier(FormulaType.LINEAR) <= 0) {
reason.add(EXPERIENCE_FORMULA + ".Linear" + VALUES + "." + MULTIPLIER + " should be greater than 0!");
}
if (getExponent(FormulaType.EXPONENTIAL) <= 0) {
reason.add(EXPERIENCE_FORMULA + ".Exponential" + VALUES + "." + EXPONENT + " should be greater than 0!");
}
/* Global modifier */
if (getExperienceGainsGlobalMultiplier() <= 0) {
reason.add(EXPERIENCE_FORMULA + "." + MULTIPLIER1 + "." + GLOBAL + " should be greater than 0!");
}
/* PVP modifier */
if (getPlayerVersusPlayerXP() < 0) {
reason.add(EXPERIENCE_FORMULA + "." + MULTIPLIER1 + "." + PVP + " should be at least 0!");
}
/* Spawned Mob modifier */
if (getSpawnedMobXpMultiplier() < 0) {
reason.add(EXPERIENCE_FORMULA + "." + MOBSPAWNERS + "." + MULTIPLIER1 + " should be at least 0!");
}
/* Bred Mob modifier */
if (getBredMobXpMultiplier() < 0) {
reason.add(EXPERIENCE_FORMULA + "." + BREEDING + "." + MULTIPLIER1 + " should be at least 0!");
}
/* Conversion */
if (getExpModifier() <= 0) {
reason.add(CONVERSION + "." + EXP + MODIFIER + " should be greater than 0!");
}
/*
* XP SETTINGS
*/
/* Alchemy */
for (PotionStage potionStage : PotionStage.values()) {
if (getPotionXP(potionStage) < 0) {
reason.add(EXPERIENCE + "." + ALCHEMY + "." + POTION_STAGE + potionStage.toNumerical() + " should be at least 0!");
}
}
/* Archery */
if (getArcheryDistanceMultiplier() < 0) {
reason.add(EXPERIENCE + "." + ARCHERY + "." + DISTANCE + MULTIPLIER1 + " should be at least 0!");
}
/* Combat XP Multipliers */
if (getAnimalsXP() < 0) {
reason.add(EXPERIENCE + "." + COMBAT + "." + MULTIPLIER1 + "." + ANIMALS + " should be at least 0!");
}
if (getDodgeXPModifier() < 0) {
reason.add("Skills." + ACROBATICS + "." + DODGE + "_XP_" + MODIFIER + " should be at least 0!");
}
if (getRollXPModifier() < 0) {
reason.add("Skills." + ACROBATICS + "." + ROLL + "_XP_" + MODIFIER + " should be at least 0!");
}
if (getFallXPModifier() < 0) {
reason.add("Skills." + ACROBATICS + "." + FALL + "_XP_" + MODIFIER + " should be at least 0!");
}
/* Fishing */
// TODO: Add validation for each fish type once enum is available.
if (getFishingShakeXP() <= 0) {
reason.add(EXPERIENCE + "." + FISHING + "." + SHAKE + " should be greater than 0!");
}
/* Repair */
if (getRepairXPBase() <= 0) {
reason.add(EXPERIENCE + "." + REPAIR + "." + BASE1 + " should be greater than 0!");
}
/* Taming */
if (getTamingXP(EntityType.WOLF) <= 0) {
reason.add(EXPERIENCE + "." + TAMING + "." + ANIMAL_TAMING + "." + WOLF + " should be greater than 0!");
}
if (getTamingXP(EntityType.OCELOT) <= 0) {
reason.add(EXPERIENCE + "." + TAMING + "." + ANIMAL_TAMING + "." + OCELOT + " should be greater than 0!");
}
return reason;
}
/*
* FORMULA SETTINGS
*/
/* EXPLOIT TOGGLES */
public boolean isEndermanEndermiteFarmingPrevented() {
return getBooleanValue(EXPLOIT_FIX, ENDERMAN_ENDERMITE_FARMS);
}
/* Curve settings */
public FormulaType getFormulaType() {
return FormulaType.getFormulaType(getStringValue(EXPERIENCE_FORMULA, CURVE));
}
public boolean getCumulativeCurveEnabled() {
return getBooleanValue(EXPERIENCE_FORMULA, CUMULATIVE + CURVE);
}
/* Curve values */
public double getMultiplier(FormulaType type) {
return getDoubleValue(EXPERIENCE_FORMULA, StringUtils.getCapitalized(type.toString()) + VALUES, MULTIPLIER);
}
public int getBase(FormulaType type) {
return getIntValue(EXPERIENCE_FORMULA, StringUtils.getCapitalized(type.toString()) + VALUES, BASE);
}
public double getExponent(FormulaType type) {
return getDoubleValue(EXPERIENCE_FORMULA, StringUtils.getCapitalized(type.toString()) + VALUES, EXPONENT);
}
/* Global modifier */
public double getExperienceGainsGlobalMultiplier() {
return getDoubleValue(EXPERIENCE_FORMULA, MULTIPLIER1, GLOBAL);
}
//TODO: Rewrite this
/*public void setExperienceGainsGlobalMultiplier(double value) {
config.set(EXPERIENCE_FORMULA, MULTIPLIER1, GLOBAL, value);
}*/
/* PVP modifier */
public double getPlayerVersusPlayerXP() {
return getDoubleValue(EXPERIENCE_FORMULA, MULTIPLIER1, PVP);
}
/* Spawned Mob modifier */
public double getSpawnedMobXpMultiplier() {
return getDoubleValue(EXPERIENCE_FORMULA, MOBSPAWNERS, MULTIPLIER1);
}
public double getBredMobXpMultiplier() {
return getDoubleValue(EXPERIENCE_FORMULA, BREEDING, MULTIPLIER1);
}
/* Skill modifiers */
public double getFormulaSkillModifier(PrimarySkillType skill) {
return getDoubleValue(EXPERIENCE_FORMULA, MODIFIER, StringUtils.getCapitalized(skill.toString()));
}
/* Custom XP perk */
public double getCustomXpPerkBoost() {
return getDoubleValue(EXPERIENCE_FORMULA, CUSTOM_XP_PERK, BOOST);
}
/* Diminished Returns */
public float getDiminishedReturnsCap() {
return (float) getDoubleValue(DIMISHED_RETURNS, GUARANTEED_MINIMUM_PERCENTAGE);
}
public boolean getDiminishedReturnsEnabled() {
return getBooleanValue(DIMINISHED_RETURNS, ENABLED);
}
public int getDiminishedReturnsThreshold(PrimarySkillType skill) {
return getIntValue(DIMINISHED_RETURNS, THRESHOLD, StringUtils.getCapitalized(skill.toString()));
}
public int getDiminishedReturnsTimeInterval() {
return getIntValue(DIMINISHED_RETURNS, TIME_INTERVAL);
}
/* Conversion */
public double getExpModifier() {
return getDoubleValue(CONVERSION, EXP + MODIFIER);
}
/*
* XP SETTINGS
*/
/* General Settings */
public boolean getExperienceGainsPlayerVersusPlayerEnabled() {
return getBooleanValue(EXPERIENCE, PVP, REWARDS);
}
/* Combat XP Multipliers */
public double getCombatXP(EntityType entity) {
return getDoubleValue(EXPERIENCE, COMBAT, MULTIPLIER1, entity.getConfigName());
}
public double getAnimalsXP(EntityType entity) {
return getDoubleValue(EXPERIENCE, COMBAT, MULTIPLIER1, entity.getConfigName());
}
public double getAnimalsXP() {
return getDoubleValue(EXPERIENCE, COMBAT, MULTIPLIER1, ANIMALS);
}
public boolean hasCombatXP(EntityType entity) {
return hasNode(EXPERIENCE, COMBAT, MULTIPLIER1, entity.getConfigName());
}
/* Materials */
/**
* Gets the raw XP given for breaking this block, this does not include modifiers
* @param skill The skill to give XP for
* @param blockType the type of block
* @return the raw amount of XP for this block before modifiers
*/
public int getXp(PrimarySkillType skill, BlockType blockType) {
//TODO: This is going to need to be changed, this code here is only placeholder
String[] path = new String[]{ EXPERIENCE, StringUtils.getCapitalized(skill.toString()), blockType.getConfigName() };
return getIntValue(path);
}
/**
* Checks if a block gives XP
* This is used to determine whether or not mcMMO should track a block that is placed by a user, among other things.
* Note: If the block has an entry in the config that will return true even if the XP is 0, this does not check the value of the XP
* @param skill The skill to check for
* @param blockType the type of block
* @return true if the block does give XP
*/
public boolean doesBlockGiveSkillXP(PrimarySkillType skill, BlockType blockType) {
//TODO: This used to support wildcard characters, seems a bit unnecessary to do so.
//TODO: This is going to need to be changed, this code here is only placeholder
String[] path = new String[] {EXPERIENCE, StringUtils.getCapitalized(skill.toString()), blockType.getConfigName()};
return hasNode(path);
}
/*
* Experience Bar Stuff
*/
public boolean isPartyExperienceBarsEnabled() {
return getBooleanValue(EXPERIENCE + BARS, UPDATE, PARTY);
}
public boolean isPassiveGainsExperienceBarsEnabled() {
return getBooleanValue(EXPERIENCE + BARS, UPDATE, PASSIVE);
}
public boolean getDoExperienceBarsAlwaysUpdateTitle() {
return getBooleanValue(EXPERIENCE + BARS, THIS_MAY_CAUSE_LAG, ALWAYS + UPDATE + TITLES_WHEN_XPIS_GAINED, ENABLE) || getAddExtraDetails();
}
public boolean getAddExtraDetails() {
return getBooleanValue(EXPERIENCE + BARS, THIS_MAY_CAUSE_LAG, ALWAYS + UPDATE + TITLES_WHEN_XPIS_GAINED, EXTRA_DETAILS);
}
public boolean isExperienceBarsEnabled() {
return getBooleanValue(EXPERIENCE + BARS, ENABLE);
}
public boolean isExperienceBarEnabled(PrimarySkillType primarySkillType) {
return getBooleanValue(EXPERIENCE + BARS, StringUtils.getCapitalized(primarySkillType.toString()), ENABLE);
}
public BarColor getExperienceBarColor(PrimarySkillType primarySkillType) {
String colorValueFromConfig = getStringValue(EXPERIENCE + BARS, StringUtils.getCapitalized(primarySkillType.toString()), COLOR);
for (BarColor barColor : BarColor.values()) {
if (barColor.toString().equalsIgnoreCase(colorValueFromConfig))
return barColor;
}
//In case the value is invalid
return BarColor.WHITE;
}
public BarStyle getExperienceBarStyle(PrimarySkillType primarySkillType) {
String colorValueFromConfig = getStringValue(EXPERIENCE + BARS, StringUtils.getCapitalized(primarySkillType.toString()), BAR_STYLE);
for (BarStyle barStyle : BarStyle.values()) {
if (barStyle.toString().equalsIgnoreCase(colorValueFromConfig))
return barStyle;
}
//In case the value is invalid
return BarStyle.SOLID;
}
/* Acrobatics */
public int getDodgeXPModifier() {
return getIntValue(EXPERIENCE, ACROBATICS, DODGE);
}
public int getRollXPModifier() {
return getIntValue(EXPERIENCE, ACROBATICS, ROLL);
}
public int getFallXPModifier() {
return getIntValue(EXPERIENCE, ACROBATICS, FALL);
}
public double getFeatherFallXPModifier() {
return getDoubleValue(EXPERIENCE, ACROBATICS, FEATHER_FALL_MULTIPLIER);
}
/* Alchemy */
public double getPotionXP(PotionStage stage) {
return getDoubleValue(EXPERIENCE, ALCHEMY, POTION_STAGE + stage.toNumerical());
}
/* Archery */
public double getArcheryDistanceMultiplier() {
return getDoubleValue(EXPERIENCE, ARCHERY, DISTANCE + MULTIPLIER1);
}
public int getFishingShakeXP() {
return getIntValue(EXPERIENCE, FISHING, SHAKE);
}
/* Repair */
public double getRepairXPBase() {
return getDoubleValue(EXPERIENCE, REPAIR, BASE1);
}
public double getRepairXP(MaterialType repairMaterialType) {
return getDoubleValue(EXPERIENCE, REPAIR, StringUtils.getCapitalized(repairMaterialType.toString()));
}
/* Taming */
public int getTamingXP(EntityType type) {
return getIntValue(EXPERIENCE, TAMING, ANIMAL_TAMING, type.getConfigName());
}
}

View File

@ -0,0 +1,35 @@
package com.gmail.nossr50.core.config.mods;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.util.ModManager;
import java.io.File;
import java.util.regex.Pattern;
public class ArmorConfigManager {
public ArmorConfigManager() {
Pattern middlePattern = Pattern.compile("armor\\.(?:.+)\\.yml");
Pattern startPattern = Pattern.compile("(?:.+)\\.armor\\.yml");
File dataFolder = new File(McmmoCore.getModDataFolderPath());
File vanilla = new File(dataFolder, "armor.default.yml");
ModManager modManager = mcMMO.getModManager();
if (!vanilla.exists()) {
plugin.saveResource(vanilla.getParentFile().getName() + File.separator + "armor.default.yml", false);
}
for (String fileName : dataFolder.list()) {
if (!middlePattern.matcher(fileName).matches() && !startPattern.matcher(fileName).matches()) {
continue;
}
File file = new File(dataFolder, fileName);
if (file.isDirectory()) {
continue;
}
modManager.registerCustomArmor(new CustomArmorConfig(fileName));
}
}
}

View File

@ -0,0 +1,35 @@
package com.gmail.nossr50.core.config.mods;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.util.ModManager;
import java.io.File;
import java.util.regex.Pattern;
public class BlockConfigManager {
public BlockConfigManager() {
Pattern middlePattern = Pattern.compile("blocks\\.(?:.+)\\.yml");
Pattern startPattern = Pattern.compile("(?:.+)\\.blocks\\.yml");
File dataFolder = new File(McmmoCore.getModDataFolderPath());
File vanilla = new File(dataFolder, "blocks.default.yml");
ModManager modManager = McmmoCore.getModManager();
if (!vanilla.exists()) {
plugin.saveResource(vanilla.getParentFile().getName() + File.separator + "blocks.default.yml", false);
}
for (String fileName : dataFolder.list()) {
if (!middlePattern.matcher(fileName).matches() && !startPattern.matcher(fileName).matches()) {
continue;
}
File file = new File(dataFolder, fileName);
if (file.isDirectory()) {
continue;
}
modManager.registerCustomBlocks(new CustomBlockConfig(fileName));
}
}
}

View File

@ -0,0 +1,110 @@
package com.gmail.nossr50.core.config.mods;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.config.ConfigCollections;
import com.gmail.nossr50.core.mcmmo.item.ItemStack;
import com.gmail.nossr50.core.skills.ConfigItemCategory;
import com.gmail.nossr50.core.skills.MaterialType;
import com.gmail.nossr50.core.skills.primary.repair.repairables.Repairable;
import com.gmail.nossr50.core.skills.primary.repair.repairables.RepairableFactory;
import com.gmail.nossr50.core.util.skills.SkillUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class CustomArmorConfig extends ConfigCollections {
public List<Material> customBoots = new ArrayList<Material>();
public List<Material> customChestplates = new ArrayList<Material>();
public List<Material> customHelmets = new ArrayList<Material>();
public List<Material> customLeggings = new ArrayList<Material>();
public List<Repairable> repairables = new ArrayList<Repairable>();
private boolean needsUpdate = false;
protected CustomArmorConfig(String fileName) {
super(McmmoCore.getDataFolderPath().getPath() + "mods", fileName, false);
loadKeys();
}
/**
* The version of this config
*
* @return
*/
@Override
public double getConfigVersion() {
return 1;
}
@Override
public void loadKeys() {
loadArmor("Boots", customBoots);
loadArmor("Chestplates", customChestplates);
loadArmor("Helmets", customHelmets);
loadArmor("Leggings", customLeggings);
if (needsUpdate) {
needsUpdate = false;
backup();
}
}
private void loadArmor(String armorType, List<Material> materialList) {
if (needsUpdate) {
return;
}
ConfigurationSection armorSection = config.getConfigurationSection(armorType);
if (armorSection == null) {
return;
}
Set<String> armorConfigSet = armorSection.getKeys(false);
for (String armorName : armorConfigSet) {
if (config.contains(armorType + "." + armorName + "." + ".ID")) {
needsUpdate = true;
return;
}
Material armorMaterial = Material.matchMaterial(armorName);
if (armorMaterial == null) {
plugin.getLogger().warning("Invalid material name. This item will be skipped. - " + armorName);
continue;
}
boolean repairable = getBooleanValue(armorType + "." + armorName + ".Repairable");
Material repairMaterial = Material.matchMaterial(getStringValue(armorType + "." + armorName + ".Repair_Material", ""));
if (repairable && (repairMaterial == null)) {
plugin.getLogger().warning("Incomplete repair information. This item will be unrepairable. - " + armorName);
repairable = false;
}
if (repairable) {
byte repairData = (byte) getIntValue(armorType + "." + armorName + ".Repair_Material_Data_Value", -1);
int repairQuantity = SkillUtils.getRepairAndSalvageQuantities(new ItemStack(armorMaterial), repairMaterial, repairData);
if (repairQuantity == 0) {
repairQuantity = getIntValue(armorType + "." + armorName + ".Repair_Material_Quantity", 2);
}
String repairItemName = getStringValue(armorType + "." + armorName + ".Repair_Material_Pretty_Name");
int repairMinimumLevel = getIntValue(armorType + "." + armorName + ".Repair_MinimumLevel", 0);
double repairXpMultiplier = getDoubleValue(armorType + "." + armorName + ".Repair_XpMultiplier", 1);
short durability = armorMaterial.getMaxDurability();
if (durability == 0) {
durability = (short) getIntValue(armorType + "." + armorName + ".Durability", 70);
}
repairables.add(RepairableFactory.getRepairable(armorMaterial, repairMaterial, repairData, repairItemName, repairMinimumLevel, repairQuantity, durability, ConfigItemCategory.ARMOR, MaterialType.OTHER, repairXpMultiplier));
}
materialList.add(armorMaterial);
}
}
}

View File

@ -0,0 +1,95 @@
package com.gmail.nossr50.core.config.mods;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.config.ConfigCollections;
import com.gmail.nossr50.core.datatypes.mods.CustomBlock;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
public class CustomBlockConfig extends ConfigCollections {
public List<Material> customExcavationBlocks = new ArrayList<>();
public List<Material> customHerbalismBlocks = new ArrayList<>();
public List<Material> customMiningBlocks = new ArrayList<>();
public List<Material> customOres = new ArrayList<>();
public List<Material> customLogs = new ArrayList<>();
public List<Material> customLeaves = new ArrayList<>();
public List<Material> customAbilityBlocks = new ArrayList<>();
public HashMap<Material, CustomBlock> customBlockMap = new HashMap<>();
private boolean needsUpdate = false;
protected CustomBlockConfig(String fileName) {
super(McmmoCore.getDataFolderPath().getPath() + "mods", fileName, false);
loadKeys();
}
@Override
protected void loadKeys() {
loadBlocks("Excavation", customExcavationBlocks);
loadBlocks("Herbalism", customHerbalismBlocks);
loadBlocks("Mining", customMiningBlocks);
loadBlocks("Woodcutting", null);
loadBlocks("Ability_Blocks", customAbilityBlocks);
if (needsUpdate) {
needsUpdate = false;
backup();
}
}
private void loadBlocks(String skillType, List<Material> blockList) {
if (needsUpdate) {
return;
}
ConfigurationSection skillSection = config.getConfigurationSection(skillType);
if (skillSection == null) {
return;
}
Set<String> skillConfigSet = skillSection.getKeys(false);
for (String blockName : skillConfigSet) {
if (config.contains(skillType + "." + blockName + ".Drop_Item")) {
needsUpdate = true;
return;
}
String[] blockInfo = blockName.split("[|]");
Material blockMaterial = Material.matchMaterial(blockInfo[0]);
if (blockMaterial == null) {
plugin.getLogger().warning("Invalid material name. This item will be skipped. - " + blockInfo[0]);
continue;
}
if (blockList != null) {
blockList.add(blockMaterial);
}
if (skillType.equals("Ability_Blocks")) {
continue;
}
int xp = getIntValue(skillType + "." + blockName + ".XP_Gain");
int smeltingXp = 0;
if (skillType.equals("Mining") && getBooleanValue(skillType + "." + blockName + ".Is_Ore")) {
customOres.add(blockMaterial);
smeltingXp = getIntValue(skillType + "." + blockName + ".Smelting_XP_Gain", xp / 10);
} else if (skillType.equals("Woodcutting")) {
if (getBooleanValue(skillType + "." + blockName + ".Is_Log")) {
customLogs.add(blockMaterial);
} else {
customLeaves.add(blockMaterial);
xp = 0; // Leaves don't grant XP
}
}
customBlockMap.put(blockMaterial, new CustomBlock(xp, getBooleanValue(skillType + "." + blockName + ".Double_Drops_Enabled"), smeltingXp));
}
}
}

View File

@ -0,0 +1,59 @@
package com.gmail.nossr50.core.config.mods;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.config.Config;
import com.gmail.nossr50.core.datatypes.mods.CustomEntity;
import com.gmail.nossr50.core.mcmmo.item.ItemStack;
import java.util.HashMap;
public class CustomEntityConfig extends Config {
public HashMap<String, CustomEntity> customEntityClassMap = new HashMap<String, CustomEntity>();
public HashMap<String, CustomEntity> customEntityTypeMap = new HashMap<String, CustomEntity>();
protected CustomEntityConfig(String fileName) {
super(McmmoCore.getDataFolderPath().getPath() + "mods", fileName);
loadKeys();
}
@Override
protected void loadKeys() {
if (config.getConfigurationSection("Hostile") != null) {
backup();
return;
}
for (String entityName : config.getKeys(false)) {
Class<?> clazz = null;
String className = getStringValue(entityName + ".Class", "");
try {
clazz = ClassUtils.getClass(className);
} catch (ClassNotFoundException e) {
plugin.getLogger().warning("Invalid class (" + className + ") detected for " + entityName + ".");
plugin.getLogger().warning("This custom entity may not function properly.");
}
String entityTypeName = entityName.replace("_", ".");
double xpMultiplier = getDoubleValue(entityName + ".XP_Multiplier", 1.0D);
boolean canBeTamed = getBooleanValue(entityName + ".Tameable");
int tamingXp = getIntValue(entityName + ".Taming_XP");
boolean canBeSummoned = getBooleanValue(entityName + ".CanBeSummoned");
Material callOfTheWildMaterial = Material.matchMaterial(getStringValue(entityName + ".COTW_Material", ""));
byte callOfTheWildData = (byte) getIntValue(entityName + ".COTW_Material_Data");
int callOfTheWildAmount = getIntValue(entityName + ".COTW_Material_Amount");
if (canBeSummoned && (callOfTheWildMaterial == null || callOfTheWildAmount == 0)) {
plugin.getLogger().warning("Incomplete Call of the Wild information. This entity will not be able to be summoned by Call of the Wild.");
canBeSummoned = false;
}
CustomEntity entity = new CustomEntity(xpMultiplier, canBeTamed, tamingXp, canBeSummoned, (canBeSummoned ? new ItemStack(callOfTheWildMaterial) : null), callOfTheWildAmount);
customEntityTypeMap.put(entityTypeName, entity);
customEntityClassMap.put(clazz == null ? null : clazz.getName(), entity);
}
}
}

View File

@ -0,0 +1,112 @@
package com.gmail.nossr50.core.config.mods;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.config.Config;
import com.gmail.nossr50.core.datatypes.mods.CustomTool;
import com.gmail.nossr50.core.mcmmo.item.ItemStack;
import com.gmail.nossr50.core.skills.MaterialType;
import com.gmail.nossr50.core.skills.primary.repair.repairables.Repairable;
import com.gmail.nossr50.core.skills.primary.repair.repairables.RepairableFactory;
import com.gmail.nossr50.core.util.skills.SkillUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class CustomToolConfig extends Config {
public List<Material> customAxes = new ArrayList<Material>();
public List<Material> customBows = new ArrayList<Material>();
public List<Material> customHoes = new ArrayList<Material>();
public List<Material> customPickaxes = new ArrayList<Material>();
public List<Material> customShovels = new ArrayList<Material>();
public List<Material> customSwords = new ArrayList<Material>();
public HashMap<Material, CustomTool> customToolMap = new HashMap<Material, CustomTool>();
public List<Repairable> repairables = new ArrayList<Repairable>();
private boolean needsUpdate = false;
protected CustomToolConfig(String fileName) {
super(McmmoCore.getDataFolderPath().getPath() + "mods", fileName);
loadKeys();
}
@Override
protected void loadKeys() {
loadTool("Axes", customAxes);
loadTool("Bows", customBows);
loadTool("Hoes", customHoes);
loadTool("Pickaxes", customPickaxes);
loadTool("Shovels", customShovels);
loadTool("Swords", customSwords);
if (needsUpdate) {
needsUpdate = false;
backup();
}
}
private void loadTool(String toolType, List<Material> materialList) {
if (needsUpdate) {
return;
}
ConfigurationSection toolSection = config.getConfigurationSection(toolType);
if (toolSection == null) {
return;
}
Set<String> toolConfigSet = toolSection.getKeys(false);
for (String toolName : toolConfigSet) {
if (config.contains(toolType + "." + toolName + "." + ".ID")) {
needsUpdate = true;
return;
}
Material toolMaterial = Material.matchMaterial(toolName);
if (toolMaterial == null) {
plugin.getLogger().warning("Invalid material name. This item will be skipped. - " + toolName);
continue;
}
boolean repairable = getBooleanValue(toolType + "." + toolName + ".Repairable");
Material repairMaterial = Material.matchMaterial(getStringValue(toolType + "." + toolName + ".Repair_Material", ""));
if (repairable && (repairMaterial == null)) {
plugin.getLogger().warning("Incomplete repair information. This item will be unrepairable. - " + toolName);
repairable = false;
}
if (repairable) {
byte repairData = (byte) getIntValue(toolType + "." + toolName + ".Repair_Material_Data_Value", -1);
int repairQuantity = SkillUtils.getRepairAndSalvageQuantities(new ItemStack(toolMaterial), repairMaterial, repairData);
if (repairQuantity == 0) {
repairQuantity = getIntValue(toolType + "." + toolName + ".Repair_Material_Quantity", 2);
}
String repairItemName = getStringValue(toolType + "." + toolName + ".Repair_Material_Pretty_Name");
int repairMinimumLevel = getIntValue(toolType + "." + toolName + ".Repair_MinimumLevel", 0);
double repairXpMultiplier = getDoubleValue(toolType + "." + toolName + ".Repair_XpMultiplier", 1);
short durability = toolMaterial.getMaxDurability();
if (durability == 0) {
durability = (short) getIntValue(toolType + "." + toolName + ".Durability", 60);
}
repairables.add(RepairableFactory.getRepairable(toolMaterial, repairMaterial, repairData, repairItemName, repairMinimumLevel, repairQuantity, durability, ItemType.TOOL, MaterialType.OTHER, repairXpMultiplier));
}
double multiplier = getDoubleValue(toolType + "." + toolName + ".XP_Modifier", 1.0);
boolean abilityEnabled = getBooleanValue(toolType + "." + toolName + ".Ability_Enabled", true);
int tier = getIntValue(toolType + "." + toolName + ".Tier", 1);
CustomTool tool = new CustomTool(tier, abilityEnabled, multiplier);
materialList.add(toolMaterial);
customToolMap.put(toolMaterial, tool);
}
}
}

View File

@ -0,0 +1,35 @@
package com.gmail.nossr50.core.config.mods;
import com.gmail.nossr50.core.util.ModManager;
import java.io.File;
import java.util.regex.Pattern;
public class EntityConfigManager {
public EntityConfigManager(mcMMO plugin) {
Pattern middlePattern = Pattern.compile("entities\\.(?:.+)\\.yml");
Pattern startPattern = Pattern.compile("(?:.+)\\.entities\\.yml");
File dataFolder = new File(mcMMO.getModDirectory());
File vanilla = new File(dataFolder, "entities.default.yml");
ModManager modManager = mcMMO.getModManager();
if (!vanilla.exists()) {
plugin.saveResource(vanilla.getParentFile().getName() + File.separator + "entities.default.yml", false);
}
for (String fileName : dataFolder.list()) {
if (!middlePattern.matcher(fileName).matches() && !startPattern.matcher(fileName).matches()) {
continue;
}
File file = new File(dataFolder, fileName);
if (file.isDirectory()) {
continue;
}
modManager.registerCustomEntities(new CustomEntityConfig(fileName));
}
}
}

View File

@ -0,0 +1,35 @@
package com.gmail.nossr50.core.config.mods;
import com.gmail.nossr50.core.util.ModManager;
import java.io.File;
import java.util.regex.Pattern;
public class ToolConfigManager {
public ToolConfigManager(mcMMO plugin) {
Pattern middlePattern = Pattern.compile("tools\\.(?:.+)\\.yml");
Pattern startPattern = Pattern.compile("(?:.+)\\.tools\\.yml");
File dataFolder = new File(mcMMO.getModDirectory());
File vanilla = new File(dataFolder, "tools.default.yml");
ModManager modManager = mcMMO.getModManager();
if (!vanilla.exists()) {
plugin.saveResource(vanilla.getParentFile().getName() + File.separator + "tools.default.yml", false);
}
for (String fileName : dataFolder.list()) {
if (!middlePattern.matcher(fileName).matches() && !startPattern.matcher(fileName).matches()) {
continue;
}
File file = new File(dataFolder, fileName);
if (file.isDirectory()) {
continue;
}
modManager.registerCustomTools(new CustomToolConfig(fileName));
}
}
}

View File

@ -0,0 +1,42 @@
package com.gmail.nossr50.core.config.party;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.config.Config;
import com.gmail.nossr50.core.util.StringUtils;
public class ItemWeightConfig extends Config {
private static ItemWeightConfig instance;
private ItemWeightConfig() {
super(McmmoCore.getDataFolderPath().getAbsoluteFile(), "itemweights.yml");
}
public static ItemWeightConfig getInstance() {
if (instance == null) {
instance = new ItemWeightConfig();
}
return instance;
}
public int getItemWeight(Material material) {
return getIntValue("Item_Weights." + StringUtils.getPrettyItemString(material).replace(" ", "_"), getIntValue("Item_Weights.Default"));
}
public HashSet<Material> getMiscItems() {
HashSet<Material> miscItems = new HashSet<Material>();
for (String item : getStringValueList("Party_Shareables.Misc_Items")) {
Material material = Material.getMaterial(item.toUpperCase());
if (material != null) {
miscItems.add(material);
}
}
return miscItems;
}
@Override
protected void loadKeys() {
}
}

View File

@ -0,0 +1,276 @@
package com.gmail.nossr50.core.config.skills.alchemy;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.config.Config;
import com.gmail.nossr50.core.mcmmo.colors.ChatColor;
import com.gmail.nossr50.core.mcmmo.item.ItemStack;
import com.gmail.nossr50.core.skills.primary.alchemy.AlchemyPotion;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class PotionConfig extends Config {
private static PotionConfig instance;
private List<ItemStack> concoctionsIngredientsTierOne = new ArrayList<ItemStack>();
private List<ItemStack> concoctionsIngredientsTierTwo = new ArrayList<ItemStack>();
private List<ItemStack> concoctionsIngredientsTierThree = new ArrayList<ItemStack>();
private List<ItemStack> concoctionsIngredientsTierFour = new ArrayList<ItemStack>();
private List<ItemStack> concoctionsIngredientsTierFive = new ArrayList<ItemStack>();
private List<ItemStack> concoctionsIngredientsTierSix = new ArrayList<ItemStack>();
private List<ItemStack> concoctionsIngredientsTierSeven = new ArrayList<ItemStack>();
private List<ItemStack> concoctionsIngredientsTierEight = new ArrayList<ItemStack>();
private Map<String, AlchemyPotion> potionMap = new HashMap<String, AlchemyPotion>();
private PotionConfig() {
super(McmmoCore.getDataFolderPath().getAbsoluteFile(),"potions.yml");
loadKeys();
}
public static PotionConfig getInstance() {
if (instance == null) {
instance = new PotionConfig();
}
return instance;
}
@Override
protected void loadKeys() {
loadConcoctions();
loadPotionMap();
}
private void loadConcoctions() {
ConfigurationSection concoctionSection = config.getConfigurationSection("Concoctions");
loadConcoctionsTier(concoctionsIngredientsTierOne, concoctionSection.getStringList("Tier_One_Ingredients"));
loadConcoctionsTier(concoctionsIngredientsTierTwo, concoctionSection.getStringList("Tier_Two_Ingredients"));
loadConcoctionsTier(concoctionsIngredientsTierThree, concoctionSection.getStringList("Tier_Three_Ingredients"));
loadConcoctionsTier(concoctionsIngredientsTierFour, concoctionSection.getStringList("Tier_Four_Ingredients"));
loadConcoctionsTier(concoctionsIngredientsTierFive, concoctionSection.getStringList("Tier_Five_Ingredients"));
loadConcoctionsTier(concoctionsIngredientsTierSix, concoctionSection.getStringList("Tier_Six_Ingredients"));
loadConcoctionsTier(concoctionsIngredientsTierSeven, concoctionSection.getStringList("Tier_Seven_Ingredients"));
loadConcoctionsTier(concoctionsIngredientsTierEight, concoctionSection.getStringList("Tier_Eight_Ingredients"));
concoctionsIngredientsTierTwo.addAll(concoctionsIngredientsTierOne);
concoctionsIngredientsTierThree.addAll(concoctionsIngredientsTierTwo);
concoctionsIngredientsTierFour.addAll(concoctionsIngredientsTierThree);
concoctionsIngredientsTierFive.addAll(concoctionsIngredientsTierFour);
concoctionsIngredientsTierSix.addAll(concoctionsIngredientsTierFive);
concoctionsIngredientsTierSeven.addAll(concoctionsIngredientsTierSix);
concoctionsIngredientsTierEight.addAll(concoctionsIngredientsTierSeven);
}
private void loadConcoctionsTier(List<ItemStack> ingredientList, List<String> ingredientStrings) {
if (ingredientStrings != null && ingredientStrings.size() > 0) {
for (String ingredientString : ingredientStrings) {
ItemStack ingredient = loadIngredient(ingredientString);
if (ingredient != null) {
ingredientList.add(ingredient);
}
}
}
}
/**
* Find the Potions configuration section and load all defined potions.
*/
private void loadPotionMap() {
ConfigurationSection potionSection = config.getConfigurationSection("Potions");
int pass = 0;
int fail = 0;
for (String potionName : potionSection.getKeys(false)) {
AlchemyPotion potion = loadPotion(potionSection.getConfigurationSection(potionName));
if (potion != null) {
potionMap.put(potionName, potion);
pass++;
} else {
fail++;
}
}
mcMMO.p.debug("Loaded " + pass + " Alchemy potions, skipped " + fail + ".");
}
/**
* Parse a ConfigurationSection representing a AlchemyPotion.
* Returns null if input cannot be parsed.
*
* @param potion_section ConfigurationSection to be parsed.
* @return Parsed AlchemyPotion.
*/
private AlchemyPotion loadPotion(ConfigurationSection potion_section) {
try {
String name = potion_section.getString("Name");
if (name != null) {
name = ChatColor.translateAlternateColorCodes('&', name);
}
PotionData data;
if (!potion_section.contains("PotionData")) { // Backwards config compatability
short dataValue = Short.parseShort(potion_section.getName());
Potion potion = Potion.fromDamage(dataValue);
data = new PotionData(potion.getType(), potion.hasExtendedDuration(), potion.getLevel() == 2);
} else {
ConfigurationSection potionData = potion_section.getConfigurationSection("PotionData");
data = new PotionData(PotionType.valueOf(potionData.getString("PotionType", "WATER")), potionData.getBoolean("Extended", false), potionData.getBoolean("Upgraded", false));
}
Material material = Material.POTION;
String mat = potion_section.getString("Material", null);
if (mat != null) {
material = Material.valueOf(mat);
}
List<String> lore = new ArrayList<String>();
if (potion_section.contains("Lore")) {
for (String line : potion_section.getStringList("Lore")) {
lore.add(ChatColor.translateAlternateColorCodes('&', line));
}
}
List<PotionEffect> effects = new ArrayList<PotionEffect>();
if (potion_section.contains("Effects")) {
for (String effect : potion_section.getStringList("Effects")) {
String[] parts = effect.split(" ");
PotionEffectType type = parts.length > 0 ? PotionEffectType.getByName(parts[0]) : null;
int amplifier = parts.length > 1 ? Integer.parseInt(parts[1]) : 0;
int duration = parts.length > 2 ? Integer.parseInt(parts[2]) : 0;
if (type != null) {
effects.add(new PotionEffect(type, duration, amplifier));
} else {
mcMMO.p.getLogger().warning("Failed to parse effect for potion " + name + ": " + effect);
}
}
}
Color color = null;
if (potion_section.contains("Color")) {
color = Color.fromRGB(potion_section.getInt("Color"));
} else {
color = this.generateColor(effects);
}
Map<ItemStack, String> children = new HashMap<ItemStack, String>();
if (potion_section.contains("Children")) {
for (String child : potion_section.getConfigurationSection("Children").getKeys(false)) {
ItemStack ingredient = loadIngredient(child);
if (ingredient != null) {
children.put(ingredient, potion_section.getConfigurationSection("Children").getString(child));
} else {
mcMMO.p.getLogger().warning("Failed to parse child for potion " + name + ": " + child);
}
}
}
return new AlchemyPotion(material, data, name, lore, effects, color, children);
} catch (Exception e) {
mcMMO.p.getLogger().warning("Failed to load Alchemy potion: " + potion_section.getName());
return null;
}
}
/**
* Parse a string representation of an ingredient.
* Format: '&lt;MATERIAL&gt;[:data]'
* Returns null if input cannot be parsed.
*
* @param ingredient String representing an ingredient.
* @return Parsed ingredient.
*/
private ItemStack loadIngredient(String ingredient) {
if (ingredient == null || ingredient.isEmpty()) {
return null;
}
Material material = Material.getMaterial(ingredient);
if (material != null) {
return new ItemStack(material, 1);
}
return null;
}
public List<ItemStack> getIngredients(int tier) {
switch (tier) {
case 8:
return concoctionsIngredientsTierEight;
case 7:
return concoctionsIngredientsTierSeven;
case 6:
return concoctionsIngredientsTierSix;
case 5:
return concoctionsIngredientsTierFive;
case 4:
return concoctionsIngredientsTierFour;
case 3:
return concoctionsIngredientsTierThree;
case 2:
return concoctionsIngredientsTierTwo;
case 1:
default:
return concoctionsIngredientsTierOne;
}
}
public boolean isValidPotion(ItemStack item) {
return getPotion(item) != null;
}
public AlchemyPotion getPotion(String name) {
return potionMap.get(name);
}
public AlchemyPotion getPotion(ItemStack item) {
for (AlchemyPotion potion : potionMap.values()) {
if (potion.isSimilar(item)) {
return potion;
}
}
return null;
}
public Color generateColor(List<PotionEffect> effects) {
if (effects != null && !effects.isEmpty()) {
List<Color> colors = new ArrayList<Color>();
for (PotionEffect effect : effects) {
if (effect.getType().getColor() != null) {
colors.add(effect.getType().getColor());
}
}
if (!colors.isEmpty()) {
if (colors.size() > 1) {
return calculateAverageColor(colors);
}
return colors.get(0);
}
}
return null;
}
public Color calculateAverageColor(List<Color> colors) {
int red = 0;
int green = 0;
int blue = 0;
for (Color color : colors) {
red += color.getRed();
green += color.getGreen();
blue += color.getBlue();
}
Color color = Color.fromRGB(red / colors.size(), green / colors.size(), blue / colors.size());
return color;
}
}

View File

@ -0,0 +1,357 @@
package com.gmail.nossr50.core.config.treasure;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.config.Config;
import com.gmail.nossr50.core.mcmmo.colors.ChatColor;
import com.gmail.nossr50.core.mcmmo.item.ItemStack;
import com.gmail.nossr50.core.skills.treasure.*;
import com.gmail.nossr50.core.util.EnchantmentUtils;
import com.gmail.nossr50.core.util.StringUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class TreasureConfig extends Config {
private static TreasureConfig instance;
public HashMap<String, List<ExcavationTreasure>> excavationMap = new HashMap<String, List<ExcavationTreasure>>();
public HashMap<EntityType, List<ShakeTreasure>> shakeMap = new HashMap<EntityType, List<ShakeTreasure>>();
public HashMap<String, List<HylianTreasure>> hylianMap = new HashMap<String, List<HylianTreasure>>();
public HashMap<Rarity, List<FishingTreasure>> fishingRewards = new HashMap<Rarity, List<FishingTreasure>>();
public HashMap<Rarity, List<EnchantmentTreasure>> fishingEnchantments = new HashMap<Rarity, List<EnchantmentTreasure>>();
private TreasureConfig() {
super(McmmoCore.getDataFolderPath().getAbsoluteFile(),"treasures.yml");
loadKeys();
validate();
}
public static TreasureConfig getInstance() {
if (instance == null) {
instance = new TreasureConfig();
}
return instance;
}
@Override
protected boolean validateKeys() {
// Validate all the settings!
List<String> reason = new ArrayList<String>();
for (String tier : config.getConfigurationSection("Enchantment_Drop_Rates").getKeys(false)) {
double totalEnchantDropRate = 0;
double totalItemDropRate = 0;
for (Rarity rarity : Rarity.values()) {
double enchantDropRate = getDoubleValue("Enchantment_Drop_Rates." + tier + "." + rarity.toString());
double itemDropRate = getDoubleValue("Item_Drop_Rates." + tier + "." + rarity.toString());
if ((enchantDropRate < 0.0 || enchantDropRate > 100.0) && rarity != Rarity.RECORD) {
reason.add("The enchant drop rate for " + tier + " items that are " + rarity.toString() + "should be between 0.0 and 100.0!");
}
if (itemDropRate < 0.0 || itemDropRate > 100.0) {
reason.add("The item drop rate for " + tier + " items that are " + rarity.toString() + "should be between 0.0 and 100.0!");
}
totalEnchantDropRate += enchantDropRate;
totalItemDropRate += itemDropRate;
}
if (totalEnchantDropRate < 0 || totalEnchantDropRate > 100.0) {
reason.add("The total enchant drop rate for " + tier + " should be between 0.0 and 100.0!");
}
if (totalItemDropRate < 0 || totalItemDropRate > 100.0) {
reason.add("The total item drop rate for " + tier + " should be between 0.0 and 100.0!");
}
}
return noErrorsInConfig(reason);
}
@Override
protected void loadKeys() {
if (config.getConfigurationSection("Treasures") != null) {
backup();
return;
}
loadTreasures("Fishing");
loadTreasures("Excavation");
loadTreasures("Hylian_Luck");
loadEnchantments();
for (EntityType entity : EntityType.values()) {
if (entity.isAlive()) {
loadTreasures("Shake." + entity.toString());
}
}
}
private void loadTreasures(String type) {
boolean isFishing = type.equals("Fishing");
boolean isShake = type.contains("Shake");
boolean isExcavation = type.equals("Excavation");
boolean isHylian = type.equals("Hylian_Luck");
ConfigurationSection treasureSection = config.getConfigurationSection(type);
if (treasureSection == null) {
return;
}
// Initialize fishing HashMap
for (Rarity rarity : Rarity.values()) {
if (!fishingRewards.containsKey(rarity)) {
fishingRewards.put(rarity, (new ArrayList<FishingTreasure>()));
}
}
for (String treasureName : treasureSection.getKeys(false)) {
// Validate all the things!
List<String> reason = new ArrayList<String>();
String[] treasureInfo = treasureName.split("[|]");
String materialName = treasureInfo[0];
/*
* Material, Amount, and Data
*/
Material material;
if (materialName.contains("INVENTORY")) {
// Use magic material BEDROCK to know that we're grabbing something from the inventory and not a normal treasure
if (!shakeMap.containsKey(EntityType.PLAYER))
shakeMap.put(EntityType.PLAYER, new ArrayList<ShakeTreasure>());
shakeMap.get(EntityType.PLAYER).add(new ShakeTreasure(new ItemStack(Material.BEDROCK, 1, (byte) 0), 1, getInventoryStealDropChance(), getInventoryStealDropLevel()));
continue;
} else {
material = Material.matchMaterial(materialName);
}
int amount = getIntValue(type + "." + treasureName + ".Amount");
short data = (treasureInfo.length == 2) ? Short.parseShort(treasureInfo[1]) : (short) getIntValue(type + "." + treasureName + ".Data");
if (material == null) {
reason.add("Invalid material: " + materialName);
}
if (amount <= 0) {
reason.add("Amount of " + treasureName + " must be greater than 0! " + amount);
}
if (material != null && material.isBlock() && (data > 127 || data < -128)) {
reason.add("Data of " + treasureName + " is invalid! " + data);
}
/*
* XP, Drop Chance, and Drop Level
*/
int xp = getIntValue(type + "." + treasureName + ".XP");
double dropChance = getDoubleValue(type + "." + treasureName + ".Drop_Chance");
int dropLevel = getIntValue(type + "." + treasureName + ".Drop_Level");
if (xp < 0) {
reason.add(treasureName + " has an invalid XP value: " + xp);
}
if (dropChance < 0.0D) {
reason.add(treasureName + " has an invalid Drop_Chance: " + dropChance);
}
if (dropLevel < 0) {
reason.add(treasureName + " has an invalid Drop_Level: " + dropLevel);
}
/*
* Specific Types
*/
Rarity rarity = null;
if (isFishing) {
rarity = Rarity.getRarity(getStringValue(type + "." + treasureName + ".Rarity"));
if (rarity == null) {
reason.add("Invalid Rarity for item: " + treasureName);
}
}
/*
* Itemstack
*/
ItemStack item = null;
if (materialName.contains("POTION")) {
Material mat = Material.matchMaterial(materialName);
if (mat == null) {
reason.add("Potion format for Treasures.yml has changed");
} else {
item = new ItemStack(mat, amount, data);
PotionMeta itemMeta = (PotionMeta) item.getItemMeta();
PotionType potionType = null;
try {
potionType = PotionType.valueOf(getStringValue(type + "." + treasureName + ".PotionData.PotionType", "WATER"));
} catch (IllegalArgumentException ex) {
reason.add("Invalid Potion_Type: " + getStringValue(type + "." + treasureName + ".PotionData.PotionType", "WATER"));
}
boolean extended = getBooleanValue(type + "." + treasureName + ".PotionData.Extended", false);
boolean upgraded = getBooleanValue(type + "." + treasureName + ".PotionData.Upgraded", false);
itemMeta.setBasePotionData(new PotionData(potionType, extended, upgraded));
if (config.contains(type + "." + treasureName + ".Custom_Name")) {
itemMeta.setDisplayName(ChatColor.translateAlternateColorCodes('&', getStringValue(type + "." + treasureName + ".Custom_Name")));
}
if (config.contains(type + "." + treasureName + ".Lore")) {
List<String> lore = new ArrayList<String>();
for (String s : getStringValueList(type + "." + treasureName + ".Lore")) {
lore.add(ChatColor.translateAlternateColorCodes('&', s));
}
itemMeta.setLore(lore);
}
item.setItemMeta(itemMeta);
}
} else if (material != null) {
item = new ItemStack(material, amount, data);
if (config.contains(type + "." + treasureName + ".Custom_Name")) {
ItemMeta itemMeta = item.getItemMeta();
itemMeta.setDisplayName(ChatColor.translateAlternateColorCodes('&', getStringValue(type + "." + treasureName + ".Custom_Name")));
item.setItemMeta(itemMeta);
}
if (config.contains(type + "." + treasureName + ".Lore")) {
ItemMeta itemMeta = item.getItemMeta();
List<String> lore = new ArrayList<String>();
for (String s : getStringValueList(type + "." + treasureName + ".Lore")) {
lore.add(ChatColor.translateAlternateColorCodes('&', s));
}
itemMeta.setLore(lore);
item.setItemMeta(itemMeta);
}
}
if (noErrorsInConfig(reason)) {
if (isFishing) {
fishingRewards.get(rarity).add(new FishingTreasure(item, xp));
} else if (isShake) {
ShakeTreasure shakeTreasure = new ShakeTreasure(item, xp, dropChance, dropLevel);
EntityType entityType = EntityType.valueOf(type.substring(6));
if (!shakeMap.containsKey(entityType))
shakeMap.put(entityType, new ArrayList<ShakeTreasure>());
shakeMap.get(entityType).add(shakeTreasure);
} else if (isExcavation) {
ExcavationTreasure excavationTreasure = new ExcavationTreasure(item, xp, dropChance, dropLevel);
List<String> dropList = getStringValueList(type + "." + treasureName + ".Drops_From");
for (String blockType : dropList) {
if (!excavationMap.containsKey(blockType))
excavationMap.put(blockType, new ArrayList<ExcavationTreasure>());
excavationMap.get(blockType).add(excavationTreasure);
}
} else if (isHylian) {
HylianTreasure hylianTreasure = new HylianTreasure(item, xp, dropChance, dropLevel);
List<String> dropList = getStringValueList(type + "." + treasureName + ".Drops_From");
for (String dropper : dropList) {
if (dropper.equals("Bushes")) {
AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.FERN), hylianTreasure);
AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.TALL_GRASS), hylianTreasure);
for (Material species : Tag.SAPLINGS.getValues())
AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(species), hylianTreasure);
AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.DEAD_BUSH), hylianTreasure);
continue;
}
if (dropper.equals("Flowers")) {
AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.POPPY), hylianTreasure);
AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.DANDELION), hylianTreasure);
AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.BLUE_ORCHID), hylianTreasure);
AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.ALLIUM), hylianTreasure);
AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.AZURE_BLUET), hylianTreasure);
AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.ORANGE_TULIP), hylianTreasure);
AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.PINK_TULIP), hylianTreasure);
AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.RED_TULIP), hylianTreasure);
AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(Material.WHITE_TULIP), hylianTreasure);
continue;
}
if (dropper.equals("Pots")) {
for (Material species : Tag.FLOWER_POTS.getValues())
AddHylianTreasure(StringUtils.getFriendlyConfigMaterialString(species), hylianTreasure);
continue;
}
AddHylianTreasure(dropper, hylianTreasure);
}
}
}
}
}
private void AddHylianTreasure(String dropper, HylianTreasure treasure) {
if (!hylianMap.containsKey(dropper))
hylianMap.put(dropper, new ArrayList<HylianTreasure>());
hylianMap.get(dropper).add(treasure);
}
private void loadEnchantments() {
for (Rarity rarity : Rarity.values()) {
if (rarity == Rarity.RECORD) {
continue;
}
if (!fishingEnchantments.containsKey(rarity)) {
fishingEnchantments.put(rarity, (new ArrayList<EnchantmentTreasure>()));
}
ConfigurationSection enchantmentSection = config.getConfigurationSection("Enchantments_Rarity." + rarity.toString());
if (enchantmentSection == null) {
return;
}
for (String enchantmentName : enchantmentSection.getKeys(false)) {
int level = getIntValue("Enchantments_Rarity." + rarity.toString() + "." + enchantmentName);
Enchantment enchantment = EnchantmentUtils.getByName(enchantmentName);
if (enchantment == null) {
plugin.getLogger().warning("Skipping invalid enchantment in treasures.yml: " + enchantmentName);
continue;
}
fishingEnchantments.get(rarity).add(new EnchantmentTreasure(enchantment, level));
}
}
}
public boolean getInventoryStealEnabled() {
return config.contains("Shake.PLAYER.INVENTORY");
}
public boolean getInventoryStealStacks() {
return getBooleanValue("Shake.PLAYER.INVENTORY.Whole_Stacks");
}
public double getInventoryStealDropChance() {
return getDoubleValue("Shake.PLAYER.INVENTORY.Drop_Chance");
}
public int getInventoryStealDropLevel() {
return getIntValue("Shake.PLAYER.INVENTORY.Drop_Level");
}
public double getItemDropRate(int tier, Rarity rarity) {
return getDoubleValue("Item_Drop_Rates.Tier_" + tier + "." + rarity.toString());
}
public double getEnchantmentDropRate(int tier, Rarity rarity) {
return getDoubleValue("Enchantment_Drop_Rates.Tier_" + tier + "." + rarity.toString());
}
}

View File

@ -0,0 +1,113 @@
package com.gmail.nossr50.core.data;
import com.gmail.nossr50.core.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.core.mcmmo.entity.Player;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
public final class UserManager {
private UserManager() {
}
/**
* Track a new user.
*
* @param mcMMOPlayer the player profile to start tracking
*/
public static void track(McMMOPlayer mcMMOPlayer) {
mcMMOPlayer.getPlayer().setMetadata(mcMMO.playerDataKey, new FixedMetadataValue(mcMMO.p, mcMMOPlayer));
}
/**
* Remove a user.
*
* @param player The Player object
*/
public static void remove(Player player) {
player.removeMetadata(mcMMO.playerDataKey, mcMMO.p);
}
/**
* Clear all users.
*/
public static void clearAll() {
for (Player player : mcMMO.p.getServer().getOnlinePlayers()) {
remove(player);
}
}
/**
* Save all users ON THIS THREAD.
*/
public static void saveAll() {
ImmutableList<Player> onlinePlayers = ImmutableList.copyOf(mcMMO.p.getServer().getOnlinePlayers());
mcMMO.p.debug("Saving mcMMOPlayers... (" + onlinePlayers.size() + ")");
for (Player player : onlinePlayers) {
try {
getPlayer(player).getProfile().save();
} catch (Exception e) {
mcMMO.p.getLogger().warning("Could not save mcMMO player data for player: " + player.getName());
}
}
}
public static Collection<McMMOPlayer> getPlayers() {
Collection<McMMOPlayer> playerCollection = new ArrayList<McMMOPlayer>();
for (Player player : mcMMO.p.getServer().getOnlinePlayers()) {
if (hasPlayerDataKey(player)) {
playerCollection.add(getPlayer(player));
}
}
return playerCollection;
}
/**
* Get the McMMOPlayer of a player by name.
*
* @param playerName The name of the player whose McMMOPlayer to retrieve
* @return the player's McMMOPlayer object
*/
public static McMMOPlayer getPlayer(String playerName) {
return retrieveMcMMOPlayer(playerName, false);
}
public static McMMOPlayer getOfflinePlayer(OfflinePlayer player) {
if (player instanceof Player) {
return getPlayer((Player) player);
}
return retrieveMcMMOPlayer(player.getName(), true);
}
public static McMMOPlayer getOfflinePlayer(String playerName) {
return retrieveMcMMOPlayer(playerName, true);
}
public static McMMOPlayer getPlayer(Player player) {
return (McMMOPlayer) player.getMetadata(mcMMO.playerDataKey).get(0).value();
}
private static McMMOPlayer retrieveMcMMOPlayer(String playerName, boolean offlineValid) {
Player player = mcMMO.p.getServer().getPlayerExact(playerName);
if (player == null) {
if (!offlineValid) {
mcMMO.p.getLogger().warning("A valid mcMMOPlayer object could not be found for " + playerName + ".");
}
return null;
}
return getPlayer(player);
}
public static boolean hasPlayerDataKey(Entity entity) {
return entity != null && entity.hasMetadata(mcMMO.playerDataKey);
}
}

View File

@ -0,0 +1,152 @@
package com.gmail.nossr50.core.data.blockmeta;
import com.gmail.nossr50.core.mcmmo.block.Block;
import com.gmail.nossr50.core.mcmmo.world.World;
public interface ChunkletManager {
/**
* Loads a specific chunklet
*
* @param cx Chunklet X coordinate that needs to be loaded
* @param cy Chunklet Y coordinate that needs to be loaded
* @param cz Chunklet Z coordinate that needs to be loaded
* @param world World that the chunklet needs to be loaded in
*/
public void loadChunklet(int cx, int cy, int cz, World world);
/**
* Unload a specific chunklet
*
* @param cx Chunklet X coordinate that needs to be unloaded
* @param cy Chunklet Y coordinate that needs to be unloaded
* @param cz Chunklet Z coordinate that needs to be unloaded
* @param world World that the chunklet needs to be unloaded from
*/
public void unloadChunklet(int cx, int cy, int cz, World world);
/**
* Load a given Chunk's Chunklet data
*
* @param cx Chunk X coordinate that is to be loaded
* @param cz Chunk Z coordinate that is to be loaded
* @param world World that the Chunk is in
*/
public void loadChunk(int cx, int cz, World world);
/**
* Unload a given Chunk's Chunklet data
*
* @param cx Chunk X coordinate that is to be unloaded
* @param cz Chunk Z coordinate that is to be unloaded
* @param world World that the Chunk is in
*/
public void unloadChunk(int cx, int cz, World world);
/**
* Informs the ChunkletManager a chunk is loaded
*
* @param cx Chunk X coordinate that is loaded
* @param cz Chunk Z coordinate that is loaded
* @param world World that the chunk was loaded in
*/
public void chunkLoaded(int cx, int cz, World world);
/**
* Informs the ChunkletManager a chunk is unloaded
*
* @param cx Chunk X coordinate that is unloaded
* @param cz Chunk Z coordinate that is unloaded
* @param world World that the chunk was unloaded in
*/
public void chunkUnloaded(int cx, int cz, World world);
/**
* Save all ChunkletStores related to the given world
*
* @param world World to save
*/
public void saveWorld(World world);
/**
* Unload all ChunkletStores from memory related to the given world after saving them
*
* @param world World to unload
*/
public void unloadWorld(World world);
/**
* Load all ChunkletStores from all loaded chunks from this world into memory
*
* @param world World to load
*/
public void loadWorld(World world);
/**
* Save all ChunkletStores
*/
public void saveAll();
/**
* Unload all ChunkletStores after saving them
*/
public void unloadAll();
/**
* Check to see if a given location is set to true
*
* @param x X coordinate to check
* @param y Y coordinate to check
* @param z Z coordinate to check
* @param world World to check in
* @return true if the given location is set to true, false if otherwise
*/
public boolean isTrue(int x, int y, int z, World world);
/**
* Check to see if a given block location is set to true
*
* @param block Block location to check
* @return true if the given block location is set to true, false if otherwise
*/
public boolean isTrue(Block block);
/**
* Set a given location to true, should create stores as necessary if the location does not exist
*
* @param x X coordinate to set
* @param y Y coordinate to set
* @param z Z coordinate to set
* @param world World to set in
*/
public void setTrue(int x, int y, int z, World world);
/**
* Set a given block location to true, should create stores as necessary if the location does not exist
*
* @param block Block location to set
*/
public void setTrue(Block block);
/**
* Set a given location to false, should not create stores if one does not exist for the given location
*
* @param x X coordinate to set
* @param y Y coordinate to set
* @param z Z coordinate to set
* @param world World to set in
*/
public void setFalse(int x, int y, int z, World world);
/**
* Set a given block location to false, should not create stores if one does not exist for the given location
*
* @param block Block location to set
*/
public void setFalse(Block block);
/**
* Delete any ChunkletStores that are empty
*/
public void cleanUp();
}

View File

@ -0,0 +1,15 @@
package com.gmail.nossr50.core.data.blockmeta;
import com.gmail.nossr50.core.config.ChunkConversionOptions;
public class ChunkletManagerFactory {
public static ChunkletManager getChunkletManager() {
ChunkConversionOptions hConfig = ChunkConversionOptions.getInstance();
if (hConfig.getChunkletsEnabled()) {
return new HashChunkletManager();
}
return new NullChunkletManager();
}
}

View File

@ -0,0 +1,48 @@
package com.gmail.nossr50.core.data.blockmeta;
import java.io.Serializable;
/**
* A ChunkletStore should be responsible for a 16x16x64 area of data
*/
public interface ChunkletStore extends Serializable {
/**
* Checks the value at the given coordinates
*
* @param x x coordinate in current chunklet
* @param y y coordinate in current chunklet
* @param z z coordinate in current chunklet
* @return true if the value is true at the given coordinates, false if otherwise
*/
public boolean isTrue(int x, int y, int z);
/**
* Set the value to true at the given coordinates
*
* @param x x coordinate in current chunklet
* @param y y coordinate in current chunklet
* @param z z coordinate in current chunklet
*/
public void setTrue(int x, int y, int z);
/**
* Set the value to false at the given coordinates
*
* @param x x coordinate in current chunklet
* @param y y coordinate in current chunklet
* @param z z coordinate in current chunklet
*/
public void setFalse(int x, int y, int z);
/**
* @return true if all values in the chunklet are false, false if otherwise
*/
public boolean isEmpty();
/**
* Set all values in this ChunkletStore to the values from another provided ChunkletStore
*
* @param otherStore Another ChunkletStore that this one should copy all data from
*/
public void copyFrom(ChunkletStore otherStore);
}

View File

@ -0,0 +1,8 @@
package com.gmail.nossr50.core.data.blockmeta;
public class ChunkletStoreFactory {
protected static ChunkletStore getChunkletStore() {
// TODO: Add in loading from config what type of store we want.
return new PrimitiveExChunkletStore();
}
}

View File

@ -0,0 +1,398 @@
package com.gmail.nossr50.core.data.blockmeta;
import com.gmail.nossr50.core.mcmmo.block.Block;
import com.gmail.nossr50.core.mcmmo.world.World;
import java.io.*;
import java.util.HashMap;
public class HashChunkletManager implements ChunkletManager {
public HashMap<String, ChunkletStore> store = new HashMap<String, ChunkletStore>();
@Override
public void loadChunklet(int cx, int cy, int cz, World world) {
File dataDir = new File(world.getWorldFolder(), "mcmmo_data");
File cxDir = new File(dataDir, "" + cx);
if (!cxDir.exists()) {
return;
}
File czDir = new File(cxDir, "" + cz);
if (!czDir.exists()) {
return;
}
File yFile = new File(czDir, "" + cy);
if (!yFile.exists()) {
return;
}
ChunkletStore in = deserializeChunkletStore(yFile);
if (in != null) {
store.put(world.getName() + "," + cx + "," + cz + "," + cy, in);
}
}
@Override
public void unloadChunklet(int cx, int cy, int cz, World world) {
File dataDir = new File(world.getWorldFolder(), "mcmmo_data");
if (store.containsKey(world.getName() + "," + cx + "," + cz + "," + cy)) {
File cxDir = new File(dataDir, "" + cx);
if (!cxDir.exists()) {
cxDir.mkdir();
}
File czDir = new File(cxDir, "" + cz);
if (!czDir.exists()) {
czDir.mkdir();
}
File yFile = new File(czDir, "" + cy);
ChunkletStore out = store.get(world.getName() + "," + cx + "," + cz + "," + cy);
serializeChunkletStore(out, yFile);
store.remove(world.getName() + "," + cx + "," + cz + "," + cy);
}
}
@Override
public void loadChunk(int cx, int cz, World world) {
File dataDir = new File(world.getWorldFolder(), "mcmmo_data");
File cxDir = new File(dataDir, "" + cx);
if (!cxDir.exists()) {
return;
}
File czDir = new File(cxDir, "" + cz);
if (!czDir.exists()) {
return;
}
for (int y = 0; y < 4; y++) {
File yFile = new File(czDir, "" + y);
if (!yFile.exists()) {
continue;
}
ChunkletStore in = deserializeChunkletStore(yFile);
if (in != null) {
store.put(world.getName() + "," + cx + "," + cz + "," + y, in);
}
}
}
@Override
public void unloadChunk(int cx, int cz, World world) {
File dataDir = new File(world.getWorldFolder(), "mcmmo_data");
for (int y = 0; y < 4; y++) {
if (store.containsKey(world.getName() + "," + cx + "," + cz + "," + y)) {
File cxDir = new File(dataDir, "" + cx);
if (!cxDir.exists()) {
cxDir.mkdir();
}
File czDir = new File(cxDir, "" + cz);
if (!czDir.exists()) {
czDir.mkdir();
}
File yFile = new File(czDir, "" + y);
ChunkletStore out = store.get(world.getName() + "," + cx + "," + cz + "," + y);
serializeChunkletStore(out, yFile);
store.remove(world.getName() + "," + cx + "," + cz + "," + y);
}
}
}
@Override
public void chunkLoaded(int cx, int cz, World world) {
//loadChunk(cx, cz, world);
}
@Override
public void chunkUnloaded(int cx, int cz, World world) {
unloadChunk(cx, cx, world);
}
@Override
public void saveWorld(World world) {
String worldName = world.getName();
File dataDir = new File(world.getWorldFolder(), "mcmmo_data");
if (!dataDir.exists()) {
dataDir.mkdirs();
}
for (String key : store.keySet()) {
String[] info = key.split(",");
if (worldName.equals(info[0])) {
File cxDir = new File(dataDir, "" + info[1]);
if (!cxDir.exists()) {
cxDir.mkdir();
}
File czDir = new File(cxDir, "" + info[2]);
if (!czDir.exists()) {
czDir.mkdir();
}
File yFile = new File(czDir, "" + info[3]);
serializeChunkletStore(store.get(key), yFile);
}
}
}
@Override
public void unloadWorld(World world) {
saveWorld(world);
String worldName = world.getName();
for (String key : store.keySet()) {
String tempWorldName = key.split(",")[0];
if (tempWorldName.equals(worldName)) {
store.remove(key);
return;
}
}
}
@Override
public void loadWorld(World world) {
//for (Chunk chunk : world.getLoadedChunks()) {
// this.chunkLoaded(chunk.getX(), chunk.getZ(), world);
//}
}
@Override
public void saveAll() {
for (World world : mcMMO.p.getServer().getWorlds()) {
saveWorld(world);
}
}
@Override
public void unloadAll() {
saveAll();
for (World world : mcMMO.p.getServer().getWorlds()) {
unloadWorld(world);
}
}
@Override
public boolean isTrue(int x, int y, int z, World world) {
int cx = x >> 4;
int cz = z >> 4;
int cy = y >> 6;
String key = world.getName() + "," + cx + "," + cz + "," + cy;
if (!store.containsKey(key)) {
loadChunklet(cx, cy, cz, world);
}
if (!store.containsKey(key)) {
return false;
}
ChunkletStore check = store.get(world.getName() + "," + cx + "," + cz + "," + cy);
int ix = Math.abs(x) % 16;
int iz = Math.abs(z) % 16;
int iy = Math.abs(y) % 64;
return check.isTrue(ix, iy, iz);
}
@Override
public boolean isTrue(Block block) {
return isTrue(block.getX(), block.getY(), block.getZ(), block.getWorld());
}
@Override
public void setTrue(int x, int y, int z, World world) {
int cx = x >> 4;
int cz = z >> 4;
int cy = y >> 6;
int ix = Math.abs(x) % 16;
int iz = Math.abs(z) % 16;
int iy = Math.abs(y) % 64;
String key = world.getName() + "," + cx + "," + cz + "," + cy;
if (!store.containsKey(key)) {
loadChunklet(cx, cy, cz, world);
}
ChunkletStore cStore = store.get(key);
if (cStore == null) {
cStore = ChunkletStoreFactory.getChunkletStore();
store.put(world.getName() + "," + cx + "," + cz + "," + cy, cStore);
}
cStore.setTrue(ix, iy, iz);
}
@Override
public void setTrue(Block block) {
setTrue(block.getX(), block.getY(), block.getZ(), block.getWorld());
}
@Override
public void setFalse(int x, int y, int z, World world) {
int cx = x >> 4;
int cz = z >> 4;
int cy = y >> 6;
int ix = Math.abs(x) % 16;
int iz = Math.abs(z) % 16;
int iy = Math.abs(y) % 64;
String key = world.getName() + "," + cx + "," + cz + "," + cy;
if (!store.containsKey(key)) {
loadChunklet(cx, cy, cz, world);
}
ChunkletStore cStore = store.get(key);
if (cStore == null) {
return; // No need to make a store for something we will be setting to false
}
cStore.setFalse(ix, iy, iz);
}
@Override
public void setFalse(Block block) {
setFalse(block.getX(), block.getY(), block.getZ(), block.getWorld());
}
@Override
public void cleanUp() {
for (String key : store.keySet()) {
if (store.get(key).isEmpty()) {
String[] info = key.split(",");
File dataDir = new File(mcMMO.p.getServer().getWorld(info[0]).getWorldFolder(), "mcmmo_data");
File cxDir = new File(dataDir, "" + info[1]);
if (!cxDir.exists()) {
continue;
}
File czDir = new File(cxDir, "" + info[2]);
if (!czDir.exists()) {
continue;
}
File yFile = new File(czDir, "" + info[3]);
yFile.delete();
// Delete empty directories
if (czDir.list().length == 0) {
czDir.delete();
}
if (cxDir.list().length == 0) {
cxDir.delete();
}
}
}
}
/**
* @param cStore ChunkletStore to save
* @param location Where on the disk to put it
*/
private void serializeChunkletStore(ChunkletStore cStore, File location) {
FileOutputStream fileOut = null;
ObjectOutputStream objOut = null;
try {
if (!location.exists()) {
location.createNewFile();
}
fileOut = new FileOutputStream(location);
objOut = new ObjectOutputStream(fileOut);
objOut.writeObject(cStore);
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (objOut != null) {
try {
objOut.flush();
objOut.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
if (fileOut != null) {
try {
fileOut.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
/**
* @param location Where on the disk to read from
* @return ChunkletStore from the specified location
*/
private ChunkletStore deserializeChunkletStore(File location) {
ChunkletStore storeIn = null;
FileInputStream fileIn = null;
ObjectInputStream objIn = null;
try {
fileIn = new FileInputStream(location);
objIn = new ObjectInputStream(fileIn);
storeIn = (ChunkletStore) objIn.readObject();
} catch (IOException ex) {
if (ex instanceof EOFException) {
// EOF should only happen on Chunklets that somehow have been corrupted.
//mcMMO.p.getLogger().severe("Chunklet data at " + location.toString() + " could not be read due to an EOFException, data in this area will be lost.");
return ChunkletStoreFactory.getChunkletStore();
} else if (ex instanceof StreamCorruptedException) {
// StreamCorrupted happens when the Chunklet is no good.
//mcMMO.p.getLogger().severe("Chunklet data at " + location.toString() + " is corrupted, data in this area will be lost.");
return ChunkletStoreFactory.getChunkletStore();
} else if (ex instanceof UTFDataFormatException) {
// UTF happens when the Chunklet cannot be read or is corrupted
//mcMMO.p.getLogger().severe("Chunklet data at " + location.toString() + " could not be read due to an UTFDataFormatException, data in this area will be lost.");
return ChunkletStoreFactory.getChunkletStore();
}
ex.printStackTrace();
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
} finally {
if (objIn != null) {
try {
objIn.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
if (fileIn != null) {
try {
fileIn.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
// TODO: Make this less messy, as it is, it's kinda... depressing to do it like this.
// Might also make a mess when we move to stacks, but at that point I think I will write a new Manager...
// IMPORTANT! If ChunkletStoreFactory is going to be returning something other than PrimitiveEx we need to remove this, as it will be breaking time for old maps
/*
if (!(storeIn instanceof PrimitiveExChunkletStore)) {
ChunkletStore tempStore = ChunkletStoreFactory.getChunkletStore();
if (storeIn != null) {
tempStore.copyFrom(storeIn);
}
storeIn = tempStore;
}
*/
return storeIn;
}
}

View File

@ -0,0 +1,101 @@
package com.gmail.nossr50.core.data.blockmeta;
import org.bukkit.World;
import org.bukkit.block.Block;
/**
* A ChunkletManager implementation that does nothing and returns false for all checks.
* <p>
* Useful for turning off Chunklets without actually doing much work
*/
public class NullChunkletManager implements ChunkletManager {
@Override
public void loadChunklet(int cx, int cy, int cz, World world) {
return;
}
@Override
public void unloadChunklet(int cx, int cy, int cz, World world) {
return;
}
@Override
public void loadChunk(int cx, int cz, World world) {
return;
}
@Override
public void unloadChunk(int cx, int cz, World world) {
return;
}
@Override
public void chunkLoaded(int cx, int cz, World world) {
return;
}
@Override
public void chunkUnloaded(int cx, int cz, World world) {
return;
}
@Override
public void saveWorld(World world) {
return;
}
@Override
public void unloadWorld(World world) {
return;
}
@Override
public void loadWorld(World world) {
return;
}
@Override
public void saveAll() {
return;
}
@Override
public void unloadAll() {
return;
}
@Override
public boolean isTrue(int x, int y, int z, World world) {
return false;
}
@Override
public boolean isTrue(Block block) {
return false;
}
@Override
public void setTrue(int x, int y, int z, World world) {
return;
}
@Override
public void setTrue(Block block) {
return;
}
@Override
public void setFalse(int x, int y, int z, World world) {
return;
}
@Override
public void setFalse(Block block) {
return;
}
@Override
public void cleanUp() {
return;
}
}

View File

@ -0,0 +1,50 @@
package com.gmail.nossr50.core.data.blockmeta;
public class PrimitiveChunkletStore implements ChunkletStore {
private static final long serialVersionUID = -3453078050608607478L;
/**
* X, Z, Y
*/
public boolean[][][] store = new boolean[16][16][64];
@Override
public boolean isTrue(int x, int y, int z) {
return store[x][z][y];
}
@Override
public void setTrue(int x, int y, int z) {
store[x][z][y] = true;
}
@Override
public void setFalse(int x, int y, int z) {
store[x][z][y] = false;
}
@Override
public boolean isEmpty() {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = 0; y < 64; y++) {
if (store[x][z][y]) {
return false;
}
}
}
}
return true;
}
@Override
public void copyFrom(ChunkletStore otherStore) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = 0; y < 64; y++) {
store[x][z][y] = otherStore.isTrue(x, y, z);
}
}
}
}
}

View File

@ -0,0 +1,182 @@
package com.gmail.nossr50.core.data.blockmeta;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
public class PrimitiveExChunkletStore implements ChunkletStore, Externalizable {
private static final long serialVersionUID = 8603603827094383873L;
/**
* X, Z, Y
*/
public boolean[][][] store = new boolean[16][16][64];
/*
* The address byte: A single byte which contains x and z values which correspond to the x and z Chunklet-coordinates
*
* In Chunklet-coordinates, the only valid values are 0-15, so we can fit both into a single byte.
*
* The top 4 bits of the address byte are for the x value
* The bottom 4 bits of the address byte are for the z value
*
* Examples:
* An address byte with a value 00000001 would be split like so:
* - x = 0000 = 0
* - z = 0001 = 1
* => Chunklet coordinates (0, 1)
*
* 01011111
* - x = 0101 = 5
* - z = 1111 = 15
* => Chunklet coordinates (5, 15)
*/
protected static byte makeAddressByte(int x, int z) {
return (byte) ((x << 4) + z);
}
protected static int addressByteX(byte address) {
return (address & 0xF0) >>> 4;
}
protected static int addressByteZ(byte address) {
return address & 0x0F;
}
@Override
public boolean isTrue(int x, int y, int z) {
return store[x][z][y];
}
@Override
public void setTrue(int x, int y, int z) {
store[x][z][y] = true;
}
@Override
public void setFalse(int x, int y, int z) {
store[x][z][y] = false;
}
@Override
public boolean isEmpty() {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = 0; y < 64; y++) {
if (store[x][z][y]) {
return false;
}
}
}
}
return true;
}
@Override
public void copyFrom(ChunkletStore otherStore) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = 0; y < 64; y++) {
store[x][z][y] = otherStore.isTrue(x, y, z);
}
}
}
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
byte[] buffer = new byte[2304]; // 2304 is 16*16*9
int bufferIndex = 0;
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = 0; y < 64; y++) {
if (store[x][z][y]) {
byte[] temp = constructColumn(x, z);
for (int i = 0; i < 9; i++) {
buffer[bufferIndex] = temp[i];
bufferIndex++;
}
break;
}
}
}
}
out.write(buffer, 0, bufferIndex);
out.flush();
}
// For this we assume that store has been initialized to be all false by now
@Override
public void readExternal(ObjectInput in) throws IOException {
byte[] temp = new byte[9];
// Could probably reorganize this loop to print nasty things if it does not equal 9 or -1
while (in.read(temp, 0, 9) == 9) {
int x = addressByteX(temp[0]);
int z = addressByteZ(temp[0]);
boolean[] yColumn = new boolean[64];
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
yColumn[j + (i * 8)] = (temp[i + 1] & (1 << j)) != 0;
}
}
store[x][z] = yColumn;
}
}
/*
* The column: An array of 9 bytes which represent all y values for a given (x,z) Chunklet-coordinate
*
* The first byte is an address byte, this provides the x and z values.
* The next 8 bytes are all y values from 0 to 63, with each byte containing 8 bits of true/false data
*
* Each of these 8 bytes address to a y value from right to left
*
* Examples:
* 00000001 represents that the lowest y value in this byte is true, all others are off
* 10000000 represents that the highest y value in this byte is true, all others are off
* 10000001 represents that the lowest and highest y values in this byte are true, all others are off
*
* Full columns:
* See comment on Address byte for information on how to use that byte
*
* Example:
* ADDRESS_BYTE 10000000 00000001 00000000 00000000 00000000 00000000 00000000 00000000
* - x, z from ADDRESS_BYTE
* - The next byte contains data from 0 to 7
* - 1 is set in the highest bit position, this is 7 in y coordinate
* - The next byte contains data from 8 to 15
* - 1 is set in the lowest bit position, this is 8 in the y coordinate
* Therefore, for this column: There are true values at (x, 7, z) and (x, 8, z)
*/
private byte[] constructColumn(int x, int z) {
byte[] column = new byte[9];
int index = 1;
column[0] = makeAddressByte(x, z);
for (int i = 0; i < 8; i++) {
byte yCompressed = 0x0;
int subColumnIndex = 8 * i;
int subColumnEnd = subColumnIndex + 8;
for (int y = subColumnIndex; y < subColumnEnd; y++) {
if (store[x][z][y]) {
yCompressed |= 1 << (y % 8);
}
}
column[index] = yCompressed;
index++;
}
return column;
}
}

View File

@ -0,0 +1,196 @@
package com.gmail.nossr50.core.data.blockmeta.chunkmeta;
import com.gmail.nossr50.core.mcmmo.block.Block;
import com.gmail.nossr50.core.mcmmo.block.BlockState;
import com.gmail.nossr50.core.mcmmo.entity.Entity;
import com.gmail.nossr50.core.mcmmo.world.World;
import java.io.IOException;
public interface ChunkManager {
public void closeAll();
public ChunkStore readChunkStore(World world, int x, int z) throws IOException;
public void writeChunkStore(World world, int x, int z, ChunkStore data);
public void closeChunkStore(World world, int x, int z);
/**
* Loads a specific chunklet
*
* @param cx Chunklet X coordinate that needs to be loaded
* @param cy Chunklet Y coordinate that needs to be loaded
* @param cz Chunklet Z coordinate that needs to be loaded
* @param world World that the chunklet needs to be loaded in
*/
public void loadChunklet(int cx, int cy, int cz, World world);
/**
* Unload a specific chunklet
*
* @param cx Chunklet X coordinate that needs to be unloaded
* @param cy Chunklet Y coordinate that needs to be unloaded
* @param cz Chunklet Z coordinate that needs to be unloaded
* @param world World that the chunklet needs to be unloaded from
*/
public void unloadChunklet(int cx, int cy, int cz, World world);
/**
* Load a given Chunk's Chunklet data
*
* @param cx Chunk X coordinate that is to be loaded
* @param cz Chunk Z coordinate that is to be loaded
* @param world World that the Chunk is in
*/
public void loadChunk(int cx, int cz, World world, Entity[] entities);
/**
* Unload a given Chunk's Chunklet data
*
* @param cx Chunk X coordinate that is to be unloaded
* @param cz Chunk Z coordinate that is to be unloaded
* @param world World that the Chunk is in
*/
public void unloadChunk(int cx, int cz, World world);
/**
* Saves a given Chunk's Chunklet data
*
* @param cx Chunk X coordinate that is to be saved
* @param cz Chunk Z coordinate that is to be saved
* @param world World that the Chunk is in
*/
public void saveChunk(int cx, int cz, World world);
public boolean isChunkLoaded(int cx, int cz, World world);
/**
* Informs the ChunkletManager a chunk is loaded
*
* @param cx Chunk X coordinate that is loaded
* @param cz Chunk Z coordinate that is loaded
* @param world World that the chunk was loaded in
*/
public void chunkLoaded(int cx, int cz, World world);
/**
* Informs the ChunkletManager a chunk is unloaded
*
* @param cx Chunk X coordinate that is unloaded
* @param cz Chunk Z coordinate that is unloaded
* @param world World that the chunk was unloaded in
*/
public void chunkUnloaded(int cx, int cz, World world);
/**
* Save all ChunkletStores related to the given world
*
* @param world World to save
*/
public void saveWorld(World world);
/**
* Unload all ChunkletStores from memory related to the given world after saving them
*
* @param world World to unload
*/
public void unloadWorld(World world);
/**
* Load all ChunkletStores from all loaded chunks from this world into memory
*
* @param world World to load
*/
public void loadWorld(World world);
/**
* Save all ChunkletStores
*/
public void saveAll();
/**
* Unload all ChunkletStores after saving them
*/
public void unloadAll();
/**
* Check to see if a given location is set to true
*
* @param x X coordinate to check
* @param y Y coordinate to check
* @param z Z coordinate to check
* @param world World to check in
* @return true if the given location is set to true, false if otherwise
*/
public boolean isTrue(int x, int y, int z, World world);
/**
* Check to see if a given block location is set to true
*
* @param block Block location to check
* @return true if the given block location is set to true, false if otherwise
*/
public boolean isTrue(Block block);
/**
* Check to see if a given BlockState location is set to true
*
* @param blockState BlockState to check
* @return true if the given BlockState location is set to true, false if otherwise
*/
public boolean isTrue(BlockState blockState);
/**
* Set a given location to true, should create stores as necessary if the location does not exist
*
* @param x X coordinate to set
* @param y Y coordinate to set
* @param z Z coordinate to set
* @param world World to set in
*/
public void setTrue(int x, int y, int z, World world);
/**
* Set a given block location to true, should create stores as necessary if the location does not exist
*
* @param block Block location to set
*/
public void setTrue(Block block);
/**
* Set a given BlockState location to true, should create stores as necessary if the location does not exist
*
* @param blockState BlockState location to set
*/
public void setTrue(BlockState blockState);
/**
* Set a given location to false, should not create stores if one does not exist for the given location
*
* @param x X coordinate to set
* @param y Y coordinate to set
* @param z Z coordinate to set
* @param world World to set in
*/
public void setFalse(int x, int y, int z, World world);
/**
* Set a given block location to false, should not create stores if one does not exist for the given location
*
* @param block Block location to set
*/
public void setFalse(Block block);
/**
* Set a given BlockState location to false, should not create stores if one does not exist for the given location
*
* @param blockState BlockState location to set
*/
public void setFalse(BlockState blockState);
/**
* Delete any ChunkletStores that are empty
*/
public void cleanUp();
}

View File

@ -0,0 +1,15 @@
package com.gmail.nossr50.core.data.blockmeta.chunkmeta;
import com.gmail.nossr50.core.config.ChunkConversionOptions;
public class ChunkManagerFactory {
public static ChunkManager getChunkManager() {
ChunkConversionOptions hConfig = ChunkConversionOptions.getInstance();
if (hConfig.getChunkletsEnabled()) {
return new HashChunkManager();
}
return new NullChunkManager();
}
}

View File

@ -0,0 +1,78 @@
package com.gmail.nossr50.core.data.blockmeta.chunkmeta;
import com.gmail.nossr50.core.data.blockmeta.ChunkletStore;
import java.io.Serializable;
/**
* A ChunkStore should be responsible for a 16x16xWorldHeight area of data
*/
public interface ChunkStore extends Serializable {
/**
* Checks the chunk's save state
*
* @return true if the has been modified since it was last saved
*/
public boolean isDirty();
/**
* Checks the chunk's save state
*
* @param dirty the save state of the current chunk
*/
public void setDirty(boolean dirty);
/**
* Checks the chunk's x coordinate
*
* @return the chunk's x coordinate.
*/
public int getChunkX();
/**
* Checks the chunk's z coordinate
*
* @return the chunk's z coordinate.
*/
public int getChunkZ();
/**
* Checks the value at the given coordinates
*
* @param x x coordinate in current chunklet
* @param y y coordinate in current chunklet
* @param z z coordinate in current chunklet
* @return true if the value is true at the given coordinates, false if otherwise
*/
public boolean isTrue(int x, int y, int z);
/**
* Set the value to true at the given coordinates
*
* @param x x coordinate in current chunklet
* @param y y coordinate in current chunklet
* @param z z coordinate in current chunklet
*/
public void setTrue(int x, int y, int z);
/**
* Set the value to false at the given coordinates
*
* @param x x coordinate in current chunklet
* @param y y coordinate in current chunklet
* @param z z coordinate in current chunklet
*/
public void setFalse(int x, int y, int z);
/**
* @return true if all values in the chunklet are false, false if otherwise
*/
public boolean isEmpty();
/**
* Set all values in this ChunkletStore to the values from another provided ChunkletStore
*
* @param otherStore Another ChunkletStore that this one should copy all data from
*/
public void copyFrom(ChunkletStore otherStore);
}

View File

@ -0,0 +1,10 @@
package com.gmail.nossr50.core.data.blockmeta.chunkmeta;
import com.gmail.nossr50.core.mcmmo.world.World;
public class ChunkStoreFactory {
protected static ChunkStore getChunkStore(World world, int x, int z) {
// TODO: Add in loading from config what type of store we want.
return new PrimitiveChunkStore(world, x, z);
}
}

View File

@ -0,0 +1,459 @@
package com.gmail.nossr50.core.data.blockmeta.chunkmeta;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.data.blockmeta.conversion.BlockStoreConversionZDirectory;
import com.gmail.nossr50.core.mcmmo.block.Block;
import com.gmail.nossr50.core.mcmmo.block.BlockState;
import com.gmail.nossr50.core.mcmmo.entity.Entity;
import com.gmail.nossr50.core.mcmmo.world.World;
import java.io.*;
import java.util.*;
public class HashChunkManager implements ChunkManager {
public HashMap<String, ChunkStore> store = new HashMap<String, ChunkStore>();
public ArrayList<BlockStoreConversionZDirectory> converters = new ArrayList<BlockStoreConversionZDirectory>();
private HashMap<UUID, HashMap<Long, McMMOSimpleRegionFile>> regionFiles = new HashMap<UUID, HashMap<Long, McMMOSimpleRegionFile>>();
private HashMap<UUID, Boolean> oldData = new HashMap<UUID, Boolean>();
@Override
public synchronized void closeAll() {
for (UUID uid : regionFiles.keySet()) {
HashMap<Long, McMMOSimpleRegionFile> worldRegions = regionFiles.get(uid);
for (Iterator<McMMOSimpleRegionFile> worldRegionIterator = worldRegions.values().iterator(); worldRegionIterator.hasNext(); ) {
McMMOSimpleRegionFile rf = worldRegionIterator.next();
if (rf != null) {
rf.close();
worldRegionIterator.remove();
}
}
}
regionFiles.clear();
}
@Override
public synchronized ChunkStore readChunkStore(World world, int x, int z) throws IOException {
McMMOSimpleRegionFile rf = getSimpleRegionFile(world, x, z);
InputStream in = rf.getInputStream(x, z);
if (in == null) {
return null;
}
ObjectInputStream objectStream = new ObjectInputStream(in);
try {
Object o = objectStream.readObject();
if (o instanceof ChunkStore) {
return (ChunkStore) o;
}
throw new RuntimeException("Wrong class type read for chunk meta data for " + x + ", " + z);
} catch (IOException e) {
// Assume the format changed
return null;
//throw new RuntimeException("Unable to process chunk meta data for " + x + ", " + z, e);
} catch (ClassNotFoundException e) {
// Assume the format changed
//System.out.println("[SpoutPlugin] is Unable to find serialized class for " + x + ", " + z + ", " + e.getMessage());
return null;
//throw new RuntimeException("Unable to find serialized class for " + x + ", " + z, e);
} finally {
objectStream.close();
}
}
@Override
public synchronized void writeChunkStore(World world, int x, int z, ChunkStore data) {
if (!data.isDirty()) {
return;
}
try {
McMMOSimpleRegionFile rf = getSimpleRegionFile(world, x, z);
ObjectOutputStream objectStream = new ObjectOutputStream(rf.getOutputStream(x, z));
objectStream.writeObject(data);
objectStream.flush();
objectStream.close();
data.setDirty(false);
} catch (IOException e) {
throw new RuntimeException("Unable to write chunk meta data for " + x + ", " + z, e);
}
}
@Override
public synchronized void closeChunkStore(World world, int x, int z) {
McMMOSimpleRegionFile rf = getSimpleRegionFile(world, x, z);
if (rf != null) {
rf.close();
}
}
private synchronized McMMOSimpleRegionFile getSimpleRegionFile(World world, int x, int z) {
File directory = new File(world.getWorldFolder(), "mcmmo_regions");
directory.mkdirs();
UUID key = world.getUUID();
HashMap<Long, McMMOSimpleRegionFile> worldRegions = regionFiles.get(key);
if (worldRegions == null) {
worldRegions = new HashMap<Long, McMMOSimpleRegionFile>();
regionFiles.put(key, worldRegions);
}
int rx = x >> 5;
int rz = z >> 5;
long key2 = (((long) rx) << 32) | ((rz) & 0xFFFFFFFFL);
McMMOSimpleRegionFile regionFile = worldRegions.get(key2);
if (regionFile == null) {
File file = new File(directory, "mcmmo_" + rx + "_" + rz + "_.mcm");
regionFile = new McMMOSimpleRegionFile(file, rx, rz);
worldRegions.put(key2, regionFile);
}
return regionFile;
}
@Override
public synchronized void loadChunklet(int cx, int cy, int cz, World world) {
loadChunk(cx, cz, world, null);
}
@Override
public synchronized void unloadChunklet(int cx, int cy, int cz, World world) {
unloadChunk(cx, cz, world);
}
@Override
public synchronized void loadChunk(int cx, int cz, World world, Entity[] entities) {
if (world == null || store.containsKey(world.getName() + "," + cx + "," + cz)) {
return;
}
UUID key = world.getUUID();
if (!oldData.containsKey(key)) {
oldData.put(key, (new File(world.getWorldFolder(), "mcmmo_data")).exists());
} else if (oldData.get(key)) {
if (convertChunk(new File(world.getWorldFolder(), "mcmmo_data"), cx, cz, world, true)) {
return;
}
}
ChunkStore chunkStore = null;
try {
chunkStore = readChunkStore(world, cx, cz);
} catch (Exception e) {
e.printStackTrace();
}
if (chunkStore == null) {
return;
}
store.put(world.getName() + "," + cx + "," + cz, chunkStore);
}
@Override
public synchronized void unloadChunk(int cx, int cz, World world) {
saveChunk(cx, cz, world);
if (store.containsKey(world.getName() + "," + cx + "," + cz)) {
store.remove(world.getName() + "," + cx + "," + cz);
//closeChunkStore(world, cx, cz);
}
}
@Override
public synchronized void saveChunk(int cx, int cz, World world) {
if (world == null) {
return;
}
String key = world.getName() + "," + cx + "," + cz;
if (store.containsKey(key)) {
ChunkStore out = store.get(world.getName() + "," + cx + "," + cz);
if (!out.isDirty()) {
return;
}
writeChunkStore(world, cx, cz, out);
}
}
@Override
public synchronized boolean isChunkLoaded(int cx, int cz, World world) {
if (world == null) {
return false;
}
return store.containsKey(world.getName() + "," + cx + "," + cz);
}
@Override
public synchronized void chunkLoaded(int cx, int cz, World world) {
}
@Override
public synchronized void chunkUnloaded(int cx, int cz, World world) {
if (world == null) {
return;
}
unloadChunk(cx, cz, world);
}
@Override
public synchronized void saveWorld(World world) {
if (world == null) {
return;
}
closeAll();
String worldName = world.getName();
List<String> keys = new ArrayList<String>(store.keySet());
for (String key : keys) {
String[] info = key.split(",");
if (worldName.equals(info[0])) {
try {
saveChunk(Integer.parseInt(info[1]), Integer.parseInt(info[2]), world);
} catch (Exception e) {
// Ignore
}
}
}
}
@Override
public synchronized void unloadWorld(World world) {
if (world == null) {
return;
}
closeAll();
String worldName = world.getName();
List<String> keys = new ArrayList<String>(store.keySet());
for (String key : keys) {
String[] info = key.split(",");
if (worldName.equals(info[0])) {
try {
unloadChunk(Integer.parseInt(info[1]), Integer.parseInt(info[2]), world);
} catch (Exception e) {
// Ignore
}
}
}
}
@Override
public synchronized void loadWorld(World world) {
}
@Override
public synchronized void saveAll() {
closeAll();
for (World world : McmmoCore.getServer().getWorlds()) {
saveWorld(world);
}
}
@Override
public synchronized void unloadAll() {
closeAll();
for (World world : McmmoCore.getServer().getWorlds()) {
unloadWorld(world);
}
}
@Override
public synchronized boolean isTrue(int x, int y, int z, World world) {
if (world == null) {
return false;
}
int cx = x >> 4;
int cz = z >> 4;
String key = world.getName() + "," + cx + "," + cz;
if (!store.containsKey(key)) {
loadChunk(cx, cz, world, null);
}
if (!store.containsKey(key)) {
return false;
}
ChunkStore check = store.get(key);
int ix = Math.abs(x) % 16;
int iz = Math.abs(z) % 16;
return check.isTrue(ix, y, iz);
}
@Override
public synchronized boolean isTrue(Block block) {
if (block == null) {
return false;
}
return isTrue(block.getX(), block.getY(), block.getZ(), block.getWorld());
}
@Override
public synchronized boolean isTrue(BlockState blockState) {
if (blockState == null) {
return false;
}
return isTrue(blockState.getBlock().getX(), blockState.getBlock().getY(), blockState.getBlock().getZ(), blockState.getBlock().getWorld());
}
@Override
public synchronized void setTrue(int x, int y, int z, World world) {
if (world == null) {
return;
}
int cx = x >> 4;
int cz = z >> 4;
int ix = Math.abs(x) % 16;
int iz = Math.abs(z) % 16;
String key = world.getName() + "," + cx + "," + cz;
if (!store.containsKey(key)) {
loadChunk(cx, cz, world, null);
}
ChunkStore cStore = store.get(key);
if (cStore == null) {
cStore = ChunkStoreFactory.getChunkStore(world, cx, cz);
store.put(key, cStore);
}
cStore.setTrue(ix, y, iz);
}
@Override
public synchronized void setTrue(Block block) {
if (block == null) {
return;
}
setTrue(block.getX(), block.getY(), block.getZ(), block.getWorld());
}
@Override
public void setTrue(BlockState blockState) {
if (blockState == null) {
return;
}
setTrue(blockState.getBlock().getX(), blockState.getBlock().getY(), blockState.getBlock().getZ(), blockState.getBlock().getWorld());
}
@Override
public synchronized void setFalse(int x, int y, int z, World world) {
if (world == null) {
return;
}
int cx = x >> 4;
int cz = z >> 4;
int ix = Math.abs(x) % 16;
int iz = Math.abs(z) % 16;
String key = world.getName() + "," + cx + "," + cz;
if (!store.containsKey(key)) {
loadChunk(cx, cz, world, null);
}
ChunkStore cStore = store.get(key);
if (cStore == null) {
return; // No need to make a store for something we will be setting to false
}
cStore.setFalse(ix, y, iz);
}
@Override
public synchronized void setFalse(Block block) {
if (block == null) {
return;
}
setFalse(block.getX(), block.getY(), block.getZ(), block.getWorld());
}
@Override
public synchronized void setFalse(BlockState blockState) {
if (blockState == null) {
return;
}
setFalse(blockState.getBlock().getX(), blockState.getBlock().getY(), blockState.getBlock().getZ(), blockState.getBlock().getWorld());
}
@Override
public synchronized void cleanUp() {
}
public synchronized void convertChunk(File dataDir, int cx, int cz, World world) {
convertChunk(dataDir, cx, cz, world, false);
}
public synchronized boolean convertChunk(File dataDir, int cx, int cz, World world, boolean actually) {
if (!actually || !dataDir.exists()) {
return false;
}
File cxDir = new File(dataDir, "" + cx);
if (!cxDir.exists()) {
return false;
}
File czDir = new File(cxDir, "" + cz);
if (!czDir.exists()) {
return false;
}
boolean conversionSet = false;
for (BlockStoreConversionZDirectory converter : this.converters) {
if (converter == null) {
continue;
}
if (converter.taskID >= 0) {
continue;
}
converter.start(world, cxDir, czDir);
conversionSet = true;
break;
}
if (!conversionSet) {
BlockStoreConversionZDirectory converter = new BlockStoreConversionZDirectory();
converter.start(world, cxDir, czDir);
converters.add(converter);
}
return true;
}
}

View File

@ -0,0 +1,39 @@
/*
* This file is part of SpoutPlugin.
*
* Copyright (c) 2011-2012, SpoutDev <http://www.spout.org/>
* SpoutPlugin is licensed under the GNU Lesser General Public License.
*
* SpoutPlugin is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SpoutPlugin is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.gmail.nossr50.core.data.blockmeta.chunkmeta;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class McMMOSimpleChunkBuffer extends ByteArrayOutputStream {
final McMMOSimpleRegionFile rf;
final int index;
McMMOSimpleChunkBuffer(McMMOSimpleRegionFile rf, int index) {
super(1024);
this.rf = rf;
this.index = index;
}
@Override
public void close() throws IOException {
rf.write(index, buf, count);
}
}

View File

@ -0,0 +1,302 @@
/*
* This file is part of SpoutPlugin.
*
* Copyright (c) 2011-2012, SpoutDev <http://www.spout.org/>
* SpoutPlugin is licensed under the GNU Lesser General Public License.
*
* SpoutPlugin is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SpoutPlugin is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.gmail.nossr50.core.data.blockmeta.chunkmeta;
import java.io.*;
import java.util.ArrayList;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;
public class McMMOSimpleRegionFile {
@SuppressWarnings("unused")
private static long TIMEOUT_TIME = 300000; // 5 min
private final int[] dataStart = new int[1024];
private final int[] dataActualLength = new int[1024];
private final int[] dataLength = new int[1024];
private final ArrayList<Boolean> inuse = new ArrayList<Boolean>();
private final int rx;
private final int rz;
private final int defaultSegmentSize;
private final File parent;
private RandomAccessFile file;
private int segmentSize;
private int segmentMask;
@SuppressWarnings("unused")
private long lastAccessTime = System.currentTimeMillis();
public McMMOSimpleRegionFile(File f, int rx, int rz) {
this(f, rx, rz, 10);
}
public McMMOSimpleRegionFile(File f, int rx, int rz, int defaultSegmentSize) {
this.rx = rx;
this.rz = rz;
this.defaultSegmentSize = defaultSegmentSize;
this.parent = f;
lastAccessTime = System.currentTimeMillis();
if (file == null) {
try {
this.file = new RandomAccessFile(parent, "rw");
if (file.length() < 4096 * 3) {
for (int i = 0; i < 1024 * 3; i++) {
file.writeInt(0);
}
file.seek(4096 * 2);
file.writeInt(defaultSegmentSize);
}
file.seek(4096 * 2);
this.segmentSize = file.readInt();
this.segmentMask = (1 << segmentSize) - 1;
int reservedSegments = this.sizeToSegments(4096 * 3);
for (int i = 0; i < reservedSegments; i++) {
while (inuse.size() <= i) {
inuse.add(false);
}
inuse.set(i, true);
}
file.seek(0);
for (int i = 0; i < 1024; i++) {
dataStart[i] = file.readInt();
}
for (int i = 0; i < 1024; i++) {
dataActualLength[i] = file.readInt();
dataLength[i] = sizeToSegments(dataActualLength[i]);
setInUse(i, true);
}
extendFile();
} catch (IOException fnfe) {
throw new RuntimeException(fnfe);
}
}
}
public synchronized final RandomAccessFile getFile() {
lastAccessTime = System.currentTimeMillis();
if (file == null) {
try {
this.file = new RandomAccessFile(parent, "rw");
if (file.length() < 4096 * 3) {
for (int i = 0; i < 1024 * 3; i++) {
file.writeInt(0);
}
file.seek(4096 * 2);
file.writeInt(defaultSegmentSize);
}
file.seek(4096 * 2);
this.segmentSize = file.readInt();
this.segmentMask = (1 << segmentSize) - 1;
int reservedSegments = this.sizeToSegments(4096 * 3);
for (int i = 0; i < reservedSegments; i++) {
while (inuse.size() <= i) {
inuse.add(false);
}
inuse.set(i, true);
}
file.seek(0);
for (int i = 0; i < 1024; i++) {
dataStart[i] = file.readInt();
}
for (int i = 0; i < 1024; i++) {
dataActualLength[i] = file.readInt();
dataLength[i] = sizeToSegments(dataActualLength[i]);
setInUse(i, true);
}
extendFile();
} catch (IOException fnfe) {
throw new RuntimeException(fnfe);
}
}
return file;
}
public synchronized boolean testCloseTimeout() {
/*
if (System.currentTimeMillis() - TIMEOUT_TIME > lastAccessTime) {
close();
return true;
}
*/
return false;
}
public synchronized DataOutputStream getOutputStream(int x, int z) {
int index = getChunkIndex(x, z);
return new DataOutputStream(new DeflaterOutputStream(new McMMOSimpleChunkBuffer(this, index)));
}
public synchronized DataInputStream getInputStream(int x, int z) throws IOException {
int index = getChunkIndex(x, z);
int actualLength = dataActualLength[index];
if (actualLength == 0) {
return null;
}
byte[] data = new byte[actualLength];
getFile().seek(dataStart[index] << segmentSize);
getFile().readFully(data);
return new DataInputStream(new InflaterInputStream(new ByteArrayInputStream(data)));
}
synchronized void write(int index, byte[] buffer, int size) throws IOException {
int oldStart = setInUse(index, false);
int start = findSpace(oldStart, size);
getFile().seek(start << segmentSize);
getFile().write(buffer, 0, size);
dataStart[index] = start;
dataActualLength[index] = size;
dataLength[index] = sizeToSegments(size);
setInUse(index, true);
saveFAT();
}
public synchronized void close() {
try {
if (file != null) {
file.seek(4096 * 2);
file.close();
}
file = null;
} catch (IOException ioe) {
throw new RuntimeException("Unable to close file", ioe);
}
}
private synchronized int setInUse(int index, boolean used) {
if (dataActualLength[index] == 0) {
return dataStart[index];
}
int start = dataStart[index];
int end = start + dataLength[index];
for (int i = start; i < end; i++) {
while (i > inuse.size() - 1) {
inuse.add(false);
}
Boolean old = inuse.set(i, used);
if (old != null && old == used) {
if (old) {
throw new IllegalStateException("Attempting to overwrite an in-use segment");
}
throw new IllegalStateException("Attempting to delete empty segment");
}
}
return dataStart[index];
}
private synchronized void extendFile() throws IOException {
long extend = (-getFile().length()) & segmentMask;
getFile().seek(getFile().length());
while ((extend--) > 0) {
getFile().write(0);
}
}
private synchronized int findSpace(int oldStart, int size) {
int segments = sizeToSegments(size);
boolean oldFree = true;
for (int i = oldStart; i < inuse.size() && i < oldStart + segments; i++) {
if (inuse.get(i)) {
oldFree = false;
break;
}
}
if (oldFree) {
return oldStart;
}
int start = 0;
int end = 0;
while (end < inuse.size()) {
if (inuse.get(end)) {
end++;
start = end;
} else {
end++;
}
if (end - start >= segments) {
return start;
}
}
return start;
}
private synchronized int sizeToSegments(int size) {
if (size <= 0) {
return 1;
}
return ((size - 1) >> segmentSize) + 1;
}
private synchronized Integer getChunkIndex(int x, int z) {
if (rx != (x >> 5) || rz != (z >> 5)) {
throw new RuntimeException(x + ", " + z + " not in region " + rx + ", " + rz);
}
x = x & 0x1F;
z = z & 0x1F;
return (x << 5) + z;
}
private synchronized void saveFAT() throws IOException {
getFile().seek(0);
for (int i = 0; i < 1024; i++) {
getFile().writeInt(dataStart[i]);
}
for (int i = 0; i < 1024; i++) {
getFile().writeInt(dataActualLength[i]);
}
}
}

View File

@ -0,0 +1,124 @@
package com.gmail.nossr50.core.data.blockmeta.chunkmeta;
import com.gmail.nossr50.core.mcmmo.block.Block;
import com.gmail.nossr50.core.mcmmo.block.BlockState;
import com.gmail.nossr50.core.mcmmo.entity.Entity;
import com.gmail.nossr50.core.mcmmo.world.World;
import java.io.IOException;
public class NullChunkManager implements ChunkManager {
@Override
public void closeAll() {
}
@Override
public ChunkStore readChunkStore(World world, int x, int z) throws IOException {
return null;
}
@Override
public void writeChunkStore(World world, int x, int z, ChunkStore data) {
}
@Override
public void closeChunkStore(World world, int x, int z) {
}
@Override
public void loadChunklet(int cx, int cy, int cz, World world) {
}
@Override
public void unloadChunklet(int cx, int cy, int cz, World world) {
}
@Override
public void loadChunk(int cx, int cz, World world, Entity[] entities) {
}
@Override
public void unloadChunk(int cx, int cz, World world) {
}
@Override
public void saveChunk(int cx, int cz, World world) {
}
@Override
public boolean isChunkLoaded(int cx, int cz, World world) {
return true;
}
@Override
public void chunkLoaded(int cx, int cz, World world) {
}
@Override
public void chunkUnloaded(int cx, int cz, World world) {
}
@Override
public void saveWorld(World world) {
}
@Override
public void unloadWorld(World world) {
}
@Override
public void loadWorld(World world) {
}
@Override
public void saveAll() {
}
@Override
public void unloadAll() {
}
@Override
public boolean isTrue(int x, int y, int z, World world) {
return false;
}
@Override
public boolean isTrue(Block block) {
return false;
}
@Override
public boolean isTrue(BlockState blockState) {
return false;
}
@Override
public void setTrue(int x, int y, int z, World world) {
}
@Override
public void setTrue(Block block) {
}
@Override
public void setTrue(BlockState blockState) {
}
@Override
public void setFalse(int x, int y, int z, World world) {
}
@Override
public void setFalse(Block block) {
}
@Override
public void setFalse(BlockState blockState) {
}
@Override
public void cleanUp() {
}
}

View File

@ -0,0 +1,149 @@
package com.gmail.nossr50.core.data.blockmeta.chunkmeta;
import com.gmail.nossr50.core.data.blockmeta.ChunkletStore;
import com.gmail.nossr50.core.mcmmo.world.World;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.UUID;
public class PrimitiveChunkStore implements ChunkStore {
private static final long serialVersionUID = -1L;
private static final int CURRENT_VERSION = 7;
private static final int MAGIC_NUMBER = 0xEA5EDEBB;
/**
* X, Z, Y
*/
public boolean[][][] store;
transient private boolean dirty = false;
private int cx;
private int cz;
private UUID worldUid;
public PrimitiveChunkStore(World world, int cx, int cz) {
this.cx = cx;
this.cz = cz;
this.worldUid = world.getUUID();
this.store = new boolean[16][16][world.getMaxHeight()];
}
@Override
public boolean isDirty() {
return dirty;
}
@Override
public void setDirty(boolean dirty) {
this.dirty = dirty;
}
@Override
public int getChunkX() {
return cx;
}
@Override
public int getChunkZ() {
return cz;
}
@Override
public boolean isTrue(int x, int y, int z) {
return store[x][z][y];
}
@Override
public void setTrue(int x, int y, int z) {
if (y >= store[0][0].length || y < 0)
return;
store[x][z][y] = true;
dirty = true;
}
@Override
public void setFalse(int x, int y, int z) {
if (y >= store[0][0].length || y < 0)
return;
store[x][z][y] = false;
dirty = true;
}
@Override
public boolean isEmpty() {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = 0; y < store[0][0].length; y++) {
if (store[x][z][y]) {
return false;
}
}
}
}
return true;
}
@Override
public void copyFrom(ChunkletStore otherStore) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = 0; y < store[0][0].length; y++) {
store[x][z][y] = otherStore.isTrue(x, y, z);
}
}
}
dirty = true;
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeInt(MAGIC_NUMBER);
out.writeInt(CURRENT_VERSION);
out.writeLong(worldUid.getLeastSignificantBits());
out.writeLong(worldUid.getMostSignificantBits());
out.writeInt(cx);
out.writeInt(cz);
out.writeObject(store);
dirty = false;
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
int magic = in.readInt();
// Can be used to determine the format of the file
int fileVersionNumber = in.readInt();
if (magic != MAGIC_NUMBER) {
fileVersionNumber = 0;
}
long lsb = in.readLong();
long msb = in.readLong();
worldUid = new UUID(msb, lsb);
cx = in.readInt();
cz = in.readInt();
store = (boolean[][][]) in.readObject();
if (fileVersionNumber < 5) {
fixArray();
dirty = true;
}
}
private void fixArray() {
boolean[][][] temp = this.store;
this.store = new boolean[16][16][Bukkit.getWorld(worldUid).getMaxHeight()];
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = 0; y < store[0][0].length; y++) {
try {
store[x][z][y] = temp[x][y][z];
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}

View File

@ -0,0 +1,90 @@
package com.gmail.nossr50.core.data.blockmeta.conversion;
import com.gmail.nossr50.core.config.ChunkConversionOptions;
import java.io.File;
public class BlockStoreConversionMain implements Runnable {
BukkitScheduler scheduler;
File dataDir;
File[] xDirs;
BlockStoreConversionXDirectory[] converters;
private int taskID, i;
private org.bukkit.World world;
public BlockStoreConversionMain(org.bukkit.World world) {
this.taskID = -1;
this.world = world;
this.scheduler = mcMMO.p.getServer().getScheduler();
this.dataDir = new File(this.world.getWorldFolder(), "mcmmo_data");
this.converters = new BlockStoreConversionXDirectory[ChunkConversionOptions.getInstance().getConversionRate()];
}
public void start() {
if (this.taskID >= 0) {
return;
}
this.taskID = this.scheduler.runTaskLater(mcMMO.p, this, 1).getTaskId();
return;
}
@Override
public void run() {
if (!this.dataDir.exists()) {
softStop();
return;
}
if (!this.dataDir.isDirectory()) {
this.dataDir.delete();
softStop();
return;
}
if (this.dataDir.listFiles().length <= 0) {
this.dataDir.delete();
softStop();
return;
}
this.xDirs = this.dataDir.listFiles();
for (this.i = 0; (this.i < ChunkConversionOptions.getInstance().getConversionRate()) && (this.i < this.xDirs.length); this.i++) {
if (this.converters[this.i] == null) {
this.converters[this.i] = new BlockStoreConversionXDirectory();
}
this.converters[this.i].start(this.world, this.xDirs[this.i]);
}
softStop();
}
public void stop() {
if (this.taskID < 0) {
return;
}
this.scheduler.cancelTask(this.taskID);
this.taskID = -1;
}
public void softStop() {
stop();
if (this.dataDir.exists() || this.dataDir.isDirectory()) {
start();
return;
}
mcMMO.p.getLogger().info("Finished converting the storage for " + world.getName() + ".");
this.dataDir = null;
this.xDirs = null;
this.world = null;
this.scheduler = null;
this.converters = null;
return;
}
}

View File

@ -0,0 +1,81 @@
package com.gmail.nossr50.core.data.blockmeta.conversion;
import com.gmail.nossr50.core.config.ChunkConversionOptions;
import com.gmail.nossr50.mcMMO;
import org.bukkit.scheduler.BukkitScheduler;
import java.io.File;
public class BlockStoreConversionXDirectory implements Runnable {
BukkitScheduler scheduler;
File dataDir;
File[] zDirs;
BlockStoreConversionZDirectory[] converters;
private int taskID, i;
private org.bukkit.World world;
public BlockStoreConversionXDirectory() {
this.taskID = -1;
}
public void start(org.bukkit.World world, File dataDir) {
this.world = world;
this.scheduler = mcMMO.p.getServer().getScheduler();
this.converters = new BlockStoreConversionZDirectory[ChunkConversionOptions.getInstance().getConversionRate()];
this.dataDir = dataDir;
if (this.taskID >= 0) {
return;
}
this.taskID = this.scheduler.runTaskLater(mcMMO.p, this, 1).getTaskId();
return;
}
@Override
public void run() {
if (!this.dataDir.exists()) {
stop();
return;
}
if (!this.dataDir.isDirectory()) {
this.dataDir.delete();
stop();
return;
}
if (this.dataDir.listFiles().length <= 0) {
this.dataDir.delete();
stop();
return;
}
this.zDirs = this.dataDir.listFiles();
for (this.i = 0; (this.i < ChunkConversionOptions.getInstance().getConversionRate()) && (this.i < this.zDirs.length); this.i++) {
if (this.converters[this.i] == null) {
this.converters[this.i] = new BlockStoreConversionZDirectory();
}
this.converters[this.i].start(this.world, this.dataDir, this.zDirs[this.i]);
}
stop();
}
public void stop() {
if (this.taskID < 0) {
return;
}
this.scheduler.cancelTask(this.taskID);
this.taskID = -1;
this.dataDir = null;
this.zDirs = null;
this.world = null;
this.scheduler = null;
this.converters = null;
}
}

View File

@ -0,0 +1,192 @@
package com.gmail.nossr50.core.data.blockmeta.conversion;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.data.blockmeta.ChunkletStore;
import com.gmail.nossr50.core.data.blockmeta.HashChunkletManager;
import com.gmail.nossr50.core.data.blockmeta.PrimitiveChunkletStore;
import com.gmail.nossr50.core.data.blockmeta.PrimitiveExChunkletStore;
import com.gmail.nossr50.core.data.blockmeta.chunkmeta.HashChunkManager;
import com.gmail.nossr50.core.data.blockmeta.chunkmeta.PrimitiveChunkStore;
import com.gmail.nossr50.core.mcmmo.tasks.TaskScheduler;
import com.gmail.nossr50.core.mcmmo.world.World;
import java.io.File;
public class BlockStoreConversionZDirectory implements Runnable {
public int taskID, cx, cz, x, y, z, y2, xPos, zPos, cxPos, czPos;
private String cxs, czs, chunkletName, chunkName;
private World world;
//private BukkitScheduler scheduler;
private TaskScheduler scheduler;
private File xDir, dataDir;
private HashChunkletManager manager;
private HashChunkManager newManager;
private ChunkletStore tempChunklet;
private PrimitiveChunkletStore primitiveChunklet = null;
private PrimitiveExChunkletStore primitiveExChunklet = null;
private PrimitiveChunkStore currentChunk;
private boolean[] oldArray, newArray;
public BlockStoreConversionZDirectory() {
this.taskID = -1;
}
public void start(World world, File xDir, File dataDir) {
this.world = world;
this.scheduler = McmmoCore.getTaskScheduler();
this.manager = new HashChunkletManager();
this.newManager = (HashChunkManager) mcMMO.getPlaceStore();
this.dataDir = dataDir;
this.xDir = xDir;
if (this.taskID >= 0) {
return;
}
// Bukkit - this.taskID = this.scheduler.runTaskLater(mcMMO.p, this, 1).getTaskId();
this.taskID = scheduler.scheduleTask(this, 1).getTaskId();
return;
}
@Override
public void run() {
if (!this.dataDir.exists()) {
stop();
return;
}
if (!this.dataDir.isDirectory()) {
this.dataDir.delete();
stop();
return;
}
if (this.dataDir.listFiles().length <= 0) {
this.dataDir.delete();
stop();
return;
}
this.cxs = this.xDir.getName();
this.czs = this.dataDir.getName();
this.cx = 0;
this.cz = 0;
try {
this.cx = Integer.parseInt(this.cxs);
this.cz = Integer.parseInt(this.czs);
} catch (Exception e) {
this.dataDir.delete();
stop();
return;
}
this.manager.loadChunk(this.cx, this.cz, this.world);
for (this.y = 0; this.y < (this.world.getMaxHeight() / 64); this.y++) {
this.chunkletName = this.world.getName() + "," + this.cx + "," + this.cz + "," + this.y;
this.tempChunklet = this.manager.store.get(this.chunkletName);
if (this.tempChunklet instanceof PrimitiveChunkletStore) {
this.primitiveChunklet = (PrimitiveChunkletStore) this.tempChunklet;
} else if (this.tempChunklet instanceof PrimitiveExChunkletStore) {
this.primitiveExChunklet = (PrimitiveExChunkletStore) this.tempChunklet;
}
if (this.tempChunklet == null) {
continue;
}
this.chunkName = this.world.getName() + "," + this.cx + "," + this.cz;
this.currentChunk = (PrimitiveChunkStore) this.newManager.store.get(this.chunkName);
if (this.currentChunk != null) {
this.xPos = this.cx * 16;
this.zPos = this.cz * 16;
for (this.x = 0; this.x < 16; this.x++) {
for (this.z = 0; this.z < 16; this.z++) {
this.cxPos = this.xPos + this.x;
this.czPos = this.zPos + this.z;
for (this.y2 = (64 * this.y); this.y2 < (64 * this.y + 64); this.y2++) {
try {
if (!this.manager.isTrue(this.cxPos, this.y2, this.czPos, this.world)) {
continue;
}
this.newManager.setTrue(this.cxPos, this.y2, this.czPos, this.world);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
continue;
}
this.newManager.setTrue(this.cx * 16, 0, this.cz * 16, this.world);
this.newManager.setFalse(this.cx * 16, 0, this.cz * 16, this.world);
this.currentChunk = (PrimitiveChunkStore) this.newManager.store.get(this.chunkName);
for (this.x = 0; this.x < 16; this.x++) {
for (this.z = 0; this.z < 16; this.z++) {
if (this.primitiveChunklet != null) {
this.oldArray = this.primitiveChunklet.store[x][z];
}
if (this.primitiveExChunklet != null) {
this.oldArray = this.primitiveExChunklet.store[x][z];
} else {
return;
}
this.newArray = this.currentChunk.store[x][z];
if (this.oldArray.length < 64) {
return;
} else if (this.newArray.length < ((this.y * 64) + 64)) {
return;
}
System.arraycopy(this.oldArray, 0, this.newArray, (this.y * 64), 64);
}
}
}
this.manager.unloadChunk(this.cx, this.cz, this.world);
this.newManager.unloadChunk(this.cx, this.cz, this.world);
for (File yFile : dataDir.listFiles()) {
if (!yFile.exists()) {
continue;
}
yFile.delete();
}
stop();
}
public void stop() {
if (this.taskID < 0) {
return;
}
this.scheduler.cancelTask(taskID);
this.taskID = -1;
this.cxs = null;
this.czs = null;
this.chunkletName = null;
this.chunkName = null;
this.manager = null;
this.xDir = null;
this.dataDir = null;
this.tempChunklet = null;
this.primitiveChunklet = null;
this.primitiveExChunklet = null;
this.currentChunk = null;
}
}

View File

@ -0,0 +1,137 @@
package com.gmail.nossr50.core.data.database;
import com.gmail.nossr50.core.config.MainConfig;
import com.gmail.nossr50.core.datatypes.database.DatabaseType;
import com.gmail.nossr50.core.datatypes.database.PlayerStat;
import com.gmail.nossr50.core.datatypes.player.PlayerProfile;
import com.gmail.nossr50.core.skills.PrimarySkillType;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public interface DatabaseManager {
// One month in milliseconds
public final long PURGE_TIME = 2630000000L * MainConfig.getInstance().getOldUsersCutoff();
// During convertUsers, how often to output a status
public final int progressInterval = 200;
/**
* Purge users with 0 power level from the database.
*/
public void purgePowerlessUsers();
/**
* Purge users who haven't logged on in over a certain time frame from the database.
*/
public void purgeOldUsers();
/**
* Remove a user from the database.
*
* @param playerName The name of the user to remove
* @return true if the user was successfully removed, false otherwise
*/
public boolean removeUser(String playerName);
/**
* Save a user to the database.
*
* @param profile The profile of the player to save
* @return true if successful, false on failure
*/
public boolean saveUser(PlayerProfile profile);
/**
* Retrieve leaderboard info.
*
* @param skill The skill to retrieve info on
* @param pageNumber Which page in the leaderboards to retrieve
* @param statsPerPage The number of stats per page
* @return the requested leaderboard information
*/
public List<PlayerStat> readLeaderboard(PrimarySkillType skill, int pageNumber, int statsPerPage);
/**
* Retrieve rank info into a HashMap from PrimarySkillType to the rank.
* <p>
* The special value <code>null</code> is used to represent the Power
* Level rank (the combination of all skill levels).
*
* @param playerName The name of the user to retrieve the rankings for
* @return the requested rank information
*/
public Map<PrimarySkillType, Integer> readRank(String playerName);
/**
* Add a new user to the database.
*
* @param playerName The name of the player to be added to the database
* @param uuid The uuid of the player to be added to the database
*/
public void newUser(String playerName, UUID uuid);
/**
* Load a player from the database.
*
* @param playerName The name of the player to load from the database
* @param createNew Whether to create a new record if the player is not
* found
* @return The player's data, or an unloaded PlayerProfile if not found
* and createNew is false
* @deprecated replaced by {@link #loadPlayerProfile(String playerName, UUID uuid, boolean createNew)}
*/
@Deprecated
public PlayerProfile loadPlayerProfile(String playerName, boolean createNew);
/**
* Load a player from the database.
*
* @param uuid The uuid of the player to load from the database
* @return The player's data, or an unloaded PlayerProfile if not found
*/
public PlayerProfile loadPlayerProfile(UUID uuid);
/**
* Load a player from the database. Attempt to use uuid, fall back on playername
*
* @param playerName The name of the player to load from the database
* @param uuid The uuid of the player to load from the database
* @param createNew Whether to create a new record if the player is not
* found
* @return The player's data, or an unloaded PlayerProfile if not found
* and createNew is false
*/
public PlayerProfile loadPlayerProfile(String playerName, UUID uuid, boolean createNew);
/**
* Get all users currently stored in the database.
*
* @return list of playernames
*/
public List<String> getStoredUsers();
/**
* Convert all users from this database to the provided database using
* {@link #saveUser(PlayerProfile)}.
*
* @param destination The DatabaseManager to save to
*/
public void convertUsers(DatabaseManager destination);
public boolean saveUserUUID(String userName, UUID uuid);
public boolean saveUserUUIDs(Map<String, UUID> fetchedUUIDs);
/**
* Retrieve the type of database in use. Custom databases should return CUSTOM.
*
* @return The type of database
*/
public DatabaseType getDatabaseType();
/**
* Called when the plugin disables
*/
public void onDisable();
}

View File

@ -0,0 +1,82 @@
package com.gmail.nossr50.core.data.database;
import com.gmail.nossr50.config.Config;
import com.gmail.nossr50.core.mcmmo.database.DatabaseType;
import com.gmail.nossr50.mcMMO;
public class DatabaseManagerFactory {
private static Class<? extends DatabaseManager> customManager = null;
public static DatabaseManager getDatabaseManager() {
if (customManager != null) {
try {
return createDefaultCustomDatabaseManager();
} catch (Exception e) {
mcMMO.p.debug("Could not create custom database manager");
e.printStackTrace();
} catch (Throwable e) {
mcMMO.p.debug("Failed to create custom database manager");
e.printStackTrace();
}
mcMMO.p.debug("Falling back on " + (Config.getInstance().getUseMySQL() ? "SQL" : "Flatfile") + " database");
}
return Config.getInstance().getUseMySQL() ? new SQLDatabaseManager() : new FlatfileDatabaseManager();
}
public static Class<? extends DatabaseManager> getCustomDatabaseManagerClass() {
return customManager;
}
/**
* Sets the custom DatabaseManager class for mcMMO to use. This should be
* called prior to mcMMO enabling.
* <p/>
* The provided class must have an empty constructor, which is the one
* that will be used.
* <p/>
* This method is intended for API use, but it should not be considered
* stable. This method is subject to change and/or removal in future
* versions.
*
* @param clazz the DatabaseManager class to use
* @throws IllegalArgumentException if the provided class does not have
* an empty constructor
*/
public static void setCustomDatabaseManagerClass(Class<? extends DatabaseManager> clazz) {
try {
clazz.getConstructor();
customManager = clazz;
} catch (Throwable e) {
throw new IllegalArgumentException("Provided database manager class must have an empty constructor", e);
}
}
public static DatabaseManager createDatabaseManager(DatabaseType type) {
switch (type) {
case DatabaseType.FLATFILE:
return new FlatfileDatabaseManager();
case DatabaseType.SQL:
return new SQLDatabaseManager();
case DatabaseType.CUSTOM:
try {
return createDefaultCustomDatabaseManager();
} catch (Throwable e) {
e.printStackTrace();
}
default:
return null;
}
}
public static DatabaseManager createDefaultCustomDatabaseManager() throws Throwable {
return customManager.getConstructor().newInstance();
}
public static DatabaseManager createCustomDatabaseManager(Class<? extends DatabaseManager> clazz) throws Throwable {
return clazz.getConstructor().newInstance();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,53 @@
package com.gmail.nossr50.core.datatypes;
import com.gmail.nossr50.core.mcmmo.world.Location;
public class LimitedSizeList {
private final int size;
public Location[] limitedSizeOrderedList;
public LimitedSizeList(int size) {
this.size = size;
limitedSizeOrderedList = new Location[size];
}
/**
* Adds objects to our limited size ordered list
* New objects are added to the front
*
* @param newItem
*/
public void add(Location newItem) {
Location[] newList = new Location[size];
for (int i = 0; i < size - 1; i++) {
if (i != 0)
newList[i] = limitedSizeOrderedList[i - 1];
else
newList[i] = newItem;
}
limitedSizeOrderedList = newList;
}
/**
* Returns true if the object is anywhere in our list
*
* @param targetLoc the object to check for
* @return true if the object is in our list
*/
public boolean contains(Location targetLoc) {
for (Location iter : limitedSizeOrderedList) {
if (iter == null)
continue;
if (iter.getX() == targetLoc.getX()
&& iter.getY() == targetLoc.getY()
&& iter.getZ() == targetLoc.getZ())
return true;
}
return false;
}
}

View File

@ -0,0 +1,24 @@
package com.gmail.nossr50.core.datatypes.chat;
import com.gmail.nossr50.core.locale.LocaleLoader;
public enum ChatMode {
ADMIN(LocaleLoader.getString("Commands.AdminChat.On"), LocaleLoader.getString("Commands.AdminChat.Off")),
PARTY(LocaleLoader.getString("Commands.Party.Chat.On"), LocaleLoader.getString("Commands.Party.Chat.Off"));
private String enabledMessage;
private String disabledMessage;
private ChatMode(String enabledMessage, String disabledMessage) {
this.enabledMessage = enabledMessage;
this.disabledMessage = disabledMessage;
}
public String getEnabledMessage() {
return enabledMessage;
}
public String getDisabledMessage() {
return disabledMessage;
}
}

View File

@ -0,0 +1,23 @@
package com.gmail.nossr50.core.datatypes.database;
public enum DatabaseType {
FLATFILE,
SQL,
CUSTOM;
public static DatabaseType getDatabaseType(String typeName) {
for (DatabaseType type : values()) {
if (type.name().equalsIgnoreCase(typeName)) {
return type;
}
}
if (typeName.equalsIgnoreCase("file")) {
return FLATFILE;
} else if (typeName.equalsIgnoreCase("mysql")) {
return SQL;
}
return CUSTOM;
}
}

View File

@ -0,0 +1,11 @@
package com.gmail.nossr50.core.datatypes.database;
public class PlayerStat {
public String name;
public int statVal = 0;
public PlayerStat(String name, int value) {
this.name = name;
this.statVal = value;
}
}

View File

@ -0,0 +1,17 @@
package com.gmail.nossr50.core.datatypes.database;
public enum UpgradeType {
ADD_FISHING,
ADD_BLAST_MINING_COOLDOWN,
ADD_SQL_INDEXES,
ADD_MOB_HEALTHBARS,
DROP_SQL_PARTY_NAMES,
DROP_SPOUT,
ADD_ALCHEMY,
ADD_UUIDS,
ADD_UUIDS_PARTY,
ADD_SCOREBOARD_TIPS,
DROP_NAME_UNIQUENESS,
ADD_SKILL_TOTAL,
ADD_UNIQUE_PLAYER_DATA,
}

View File

@ -0,0 +1,15 @@
package com.gmail.nossr50.core.datatypes.experience;
public enum FormulaType {
LINEAR,
EXPONENTIAL,
UNKNOWN;
public static FormulaType getFormulaType(String string) {
try {
return valueOf(string);
} catch (IllegalArgumentException ex) {
return UNKNOWN;
}
}
}

View File

@ -0,0 +1,54 @@
package com.gmail.nossr50.core.datatypes.experience;
import com.gmail.nossr50.core.config.experience.ExperienceConfig;
import com.gmail.nossr50.core.skills.PrimarySkillType;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
public class SkillXpGain implements Delayed {
private final long expiryTime;
private final float xp;
private final PrimarySkillType type;
public SkillXpGain(PrimarySkillType type, float xp) {
this.expiryTime = System.currentTimeMillis() + getDuration();
this.xp = xp;
this.type = type;
}
private static long getDuration() {
return TimeUnit.MINUTES.toMillis(ExperienceConfig.getInstance().getDiminishedReturnsTimeInterval());
}
public PrimarySkillType getSkill() {
return type;
}
public float getXp() {
return xp;
}
public int compareTo(SkillXpGain other) {
if (this.expiryTime < other.expiryTime) {
return -1;
} else if (this.expiryTime > other.expiryTime) {
return 1;
}
return 0;
}
@Override
public int compareTo(Delayed other) {
if (other instanceof SkillXpGain) {
// Use more efficient method if possible (private fields)
return this.compareTo((SkillXpGain) other);
}
return (int) (getDelay(TimeUnit.MILLISECONDS) - other.getDelay(TimeUnit.MILLISECONDS));
}
@Override
public long getDelay(TimeUnit arg0) {
return arg0.convert(expiryTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
}

View File

@ -0,0 +1,21 @@
package com.gmail.nossr50.core.datatypes.experience;
public enum XPGainReason {
PVP,
PVE,
VAMPIRISM,
SHARED_PVP,
SHARED_PVE,
COMMAND,
UNKNOWN;
public static XPGainReason getXPGainReason(String reason) {
for (XPGainReason type : values()) {
if (type.name().equalsIgnoreCase(reason)) {
return type;
}
}
return null;
}
}

View File

@ -0,0 +1,10 @@
package com.gmail.nossr50.core.datatypes.experience;
public enum XPGainSource {
SELF,
VAMPIRISM, //From Vampirism kills
PASSIVE, //Smelting, Brewing, etc...
PARTY_MEMBERS, //From other members of a party
COMMAND,
CUSTOM, //Outside Sources
}

View File

@ -0,0 +1,34 @@
package com.gmail.nossr50.core.datatypes.interactions;
/**
* This class helps define the types of information interactions we will have with players
*/
public enum NotificationType {
XP_GAIN("ExperienceGain"),
HARDCORE_MODE("HardcoreMode"),
NO_PERMISSION("NoPermission"),
SUBSKILL_UNLOCKED("SubSkillUnlocked"),
LEVEL_UP_MESSAGE("LevelUps"),
HOLIDAY("Holiday"),
SUBSKILL_MESSAGE("SubSkillInteraction"),
SUBSKILL_MESSAGE_FAILED("SubSkillFailed"),
TOOL("ToolReady"),
REQUIREMENTS_NOT_MET("RequirementsNotMet"),
ABILITY_OFF("AbilityOff"),
ABILITY_COOLDOWN("AbilityCoolDown"),
ABILITY_REFRESHED("AbilityRefreshed"),
SUPER_ABILITY("SuperAbilityInteraction"),
SUPER_ABILITY_ALERT_OTHERS("SuperAbilityAlertOthers"),
ITEM_MESSAGE("ItemMessage"),
PARTY_MESSAGE("PartyMessage");
final String niceName;
NotificationType(String niceName) {
this.niceName = niceName;
}
@Override
public String toString() {
return niceName;
}}

View File

@ -0,0 +1,9 @@
package com.gmail.nossr50.core.datatypes.json;
public class CustomBaseComponent extends BaseComponent {
@Override
public BaseComponent duplicate() {
return this;
}
}

View File

@ -0,0 +1,29 @@
package com.gmail.nossr50.core.datatypes.json;
public class McMMOUrl {
public static final String urlWebsite = "https://www.mcmmo.org";
public static final String urlDiscord = "https://discord.gg/bJ7pFS9";
public static final String urlPatreon = "https://www.patreon.com/com.gmail.nossr50";
public static final String urlWiki = "https://www.mcmmo.org/wiki/";
public static final String urlSpigot = "http://spigot.mcmmo.org";
public static final String urlTranslate = "https://www.mcmmo.org/translate/";
public static String getUrl(McMMOWebLinks webLinks) {
switch (webLinks) {
case WIKI:
return urlWiki;
case PATREON:
return urlPatreon;
case SPIGOT:
return urlSpigot;
case DISCORD:
return urlDiscord;
case WEBSITE:
return urlWebsite;
case HELP_TRANSLATE:
return urlTranslate;
default:
return "https://www.mcmmo.org";
}
}
}

View File

@ -0,0 +1,40 @@
package com.gmail.nossr50.core.datatypes.json;
import com.gmail.nossr50.core.locale.LocaleLoader;
import com.gmail.nossr50.core.util.StringUtils;
public enum McMMOWebLinks {
WEBSITE,
DISCORD,
PATREON,
SPIGOT,
HELP_TRANSLATE,
WIKI;
public String getUrl() {
return McMMOUrl.getUrl(this);
}
public String getNiceTitle() {
return StringUtils.getCapitalized(toString());
}
public String getLocaleDescription() {
switch (this) {
case WEBSITE:
return LocaleLoader.getString("JSON.URL.Website");
case DISCORD:
return LocaleLoader.getString("JSON.URL.Discord");
case PATREON:
return LocaleLoader.getString("JSON.URL.Patreon");
case HELP_TRANSLATE:
return LocaleLoader.getString("JSON.URL.Translation");
case SPIGOT:
return LocaleLoader.getString("JSON.URL.Spigot");
case WIKI:
return LocaleLoader.getString("JSON.URL.Wiki");
default:
return "";
}
}
}

View File

@ -0,0 +1,12 @@
package com.gmail.nossr50.core.datatypes.meta;
/**
* This class is for storing mob names since we switch them to heart values
*/
public class OldName extends FixedMetadataValue {
public OldName(String oldName, mcMMO plugin) {
super(plugin, oldName);
}
}

View File

@ -0,0 +1,25 @@
package com.gmail.nossr50.core.datatypes.mods;
public class CustomBlock {
private int xpGain;
private boolean canDoubleDrop;
private int smeltingXpGain;
public CustomBlock(int xpGain, boolean canDoubleDrop, int smeltingXpGain) {
this.xpGain = xpGain;
this.canDoubleDrop = canDoubleDrop;
this.smeltingXpGain = smeltingXpGain;
}
public int getXpGain() {
return xpGain;
}
public boolean isDoubleDropEnabled() {
return canDoubleDrop;
}
public int getSmeltingXpGain() {
return smeltingXpGain;
}
}

View File

@ -0,0 +1,46 @@
package com.gmail.nossr50.core.datatypes.mods;
import com.gmail.nossr50.core.mcmmo.item.ItemStack;
public class CustomEntity {
private double xpMultiplier;
private boolean canBeTamed;
private int tamingXP;
private boolean canBeSummoned;
private ItemStack callOfTheWildItem;
private int callOfTheWildAmount;
public CustomEntity(double xpMultiplier, boolean canBeTamed, int tamingXP, boolean canBeSummoned, ItemStack callOfTheWildItem, int callOfTheWildAmount) {
this.xpMultiplier = xpMultiplier;
this.canBeTamed = canBeTamed;
this.tamingXP = tamingXP;
this.canBeSummoned = canBeSummoned;
this.callOfTheWildItem = callOfTheWildItem;
this.callOfTheWildAmount = callOfTheWildAmount;
}
public double getXpMultiplier() {
return xpMultiplier;
}
public boolean canBeTamed() {
return canBeTamed;
}
public int getTamingXP() {
return tamingXP;
}
public boolean canBeSummoned() {
return canBeSummoned;
}
public ItemStack getCallOfTheWildItem() {
return callOfTheWildItem;
}
public int getCallOfTheWildAmount() {
return callOfTheWildAmount;
}
}

View File

@ -0,0 +1,25 @@
package com.gmail.nossr50.core.datatypes.mods;
public class CustomTool {
private double xpMultiplier;
private boolean abilityEnabled;
private int tier;
public CustomTool(int tier, boolean abilityEnabled, double xpMultiplier) {
this.xpMultiplier = xpMultiplier;
this.abilityEnabled = abilityEnabled;
this.tier = tier;
}
public double getXpMultiplier() {
return xpMultiplier;
}
public boolean isAbilityEnabled() {
return abilityEnabled;
}
public int getTier() {
return tier;
}
}

View File

@ -0,0 +1,34 @@
package com.gmail.nossr50.core.datatypes.party;
import com.gmail.nossr50.core.locale.LocaleLoader;
import com.gmail.nossr50.core.mcmmo.item.ItemStack;
import com.gmail.nossr50.core.util.ItemUtils;
import com.gmail.nossr50.core.util.StringUtils;
public enum ItemShareType {
LOOT,
MINING,
HERBALISM,
WOODCUTTING,
MISC;
public static ItemShareType getShareType(ItemStack itemStack) {
if (ItemUtils.isMobDrop(itemStack)) {
return LOOT;
} else if (ItemUtils.isMiningDrop(itemStack)) {
return MINING;
} else if (ItemUtils.isHerbalismDrop(itemStack)) {
return HERBALISM;
} else if (ItemUtils.isWoodcuttingDrop(itemStack)) {
return WOODCUTTING;
} else if (ItemUtils.isMiscDrop(itemStack)) {
return MISC;
}
return null;
}
public String getLocaleString() {
return LocaleLoader.getString("Party.ItemShare.Category." + StringUtils.getCapitalized(this.toString()));
}
}

View File

@ -0,0 +1,370 @@
package com.gmail.nossr50.core.datatypes.party;
import com.gmail.nossr50.core.config.MainConfig;
import com.gmail.nossr50.core.config.experience.ExperienceConfig;
import com.gmail.nossr50.core.datatypes.experience.FormulaType;
import com.gmail.nossr50.core.locale.LocaleLoader;
import com.gmail.nossr50.core.mcmmo.colors.ChatColor;
import com.gmail.nossr50.core.mcmmo.commands.CommandSender;
import com.gmail.nossr50.core.mcmmo.entity.Player;
import com.gmail.nossr50.core.party.PartyManager;
import com.gmail.nossr50.core.util.EventUtils;
import com.gmail.nossr50.core.util.sounds.SoundManager;
import com.gmail.nossr50.core.util.sounds.SoundType;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.UUID;
public class Party {
private final LinkedHashMap<UUID, String> members = new LinkedHashMap<UUID, String>();
private final List<Player> onlineMembers = new ArrayList<Player>();
private PartyLeader leader;
private String name;
private String password;
private boolean locked;
private Party ally;
private int level;
private float xp;
private ShareMode xpShareMode = ShareMode.NONE;
private ShareMode itemShareMode = ShareMode.NONE;
private boolean shareLootDrops = true;
private boolean shareMiningDrops = true;
private boolean shareHerbalismDrops = true;
private boolean shareWoodcuttingDrops = true;
private boolean shareMiscDrops = true;
public Party(String name) {
this.name = name;
}
public Party(PartyLeader leader, String name) {
this.leader = leader;
this.name = name;
this.locked = true;
this.level = 0;
}
public Party(PartyLeader leader, String name, String password) {
this.leader = leader;
this.name = name;
this.password = password;
this.locked = true;
this.level = 0;
}
public Party(PartyLeader leader, String name, String password, boolean locked) {
this.leader = leader;
this.name = name;
this.password = password;
this.locked = locked;
this.level = 0;
}
public LinkedHashMap<UUID, String> getMembers() {
return members;
}
public List<Player> getOnlineMembers() {
return onlineMembers;
}
public List<Player> getVisibleMembers(Player player) {
ArrayList<Player> visibleMembers = new ArrayList<>();
for (Player p : onlineMembers) {
if (player.canSee(p))
visibleMembers.add(p);
}
return visibleMembers;
}
public List<String> getOnlinePlayerNames(CommandSender sender) {
Player player = sender instanceof Player ? (Player) sender : null;
List<String> onlinePlayerNames = new ArrayList<String>();
for (Player onlinePlayer : getOnlineMembers()) {
if (player != null && player.canSee(onlinePlayer)) {
onlinePlayerNames.add(onlinePlayer.getName());
}
}
return onlinePlayerNames;
}
public boolean addOnlineMember(Player player) {
return onlineMembers.add(player);
}
public boolean removeOnlineMember(Player player) {
return onlineMembers.remove(player);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public PartyLeader getLeader() {
return leader;
}
public void setLeader(PartyLeader leader) {
this.leader = leader;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public boolean isLocked() {
return locked;
}
public void setLocked(boolean locked) {
this.locked = locked;
}
public Party getAlly() {
return ally;
}
public void setAlly(Party ally) {
this.ally = ally;
}
public List<String> getItemShareCategories() {
List<String> shareCategories = new ArrayList<String>();
for (ItemShareType shareType : ItemShareType.values()) {
if (sharingDrops(shareType)) {
shareCategories.add(shareType.getLocaleString());
}
}
return shareCategories;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public float getXp() {
return xp;
}
public void setXp(float xp) {
this.xp = xp;
}
public void addXp(float xp) {
setXp(getXp() + xp);
}
protected float levelUp() {
float xpRemoved = getXpToLevel();
setLevel(getLevel() + 1);
setXp(getXp() - xpRemoved);
return xpRemoved;
}
public int getXpToLevel() {
FormulaType formulaType = ExperienceConfig.getInstance().getFormulaType();
return (mcMMO.getFormulaManager().getCachedXpToLevel(level, formulaType)) * (getOnlineMembers().size() + MainConfig.getInstance().getPartyXpCurveMultiplier());
}
public String getXpToLevelPercentage() {
DecimalFormat percent = new DecimalFormat("##0.00%");
return percent.format(this.getXp() / getXpToLevel());
}
/**
* Applies an experience gain
*
* @param xp Experience amount to add
*/
public void applyXpGain(float xp) {
if (!EventUtils.handlePartyXpGainEvent(this, xp)) {
return;
}
if (getXp() < getXpToLevel()) {
return;
}
int levelsGained = 0;
float xpRemoved = 0;
while (getXp() >= getXpToLevel()) {
if (hasReachedLevelCap()) {
setXp(0);
return;
}
xpRemoved += levelUp();
levelsGained++;
}
if (!EventUtils.handlePartyLevelChangeEvent(this, levelsGained, xpRemoved)) {
return;
}
if (!MainConfig.getInstance().getPartyInformAllMembers()) {
Player leader = mcMMO.p.getServer().getPlayer(this.leader.getUniqueId());
if (leader != null) {
leader.sendMessage(LocaleLoader.getString("Party.LevelUp", levelsGained, getLevel()));
if (MainConfig.getInstance().getLevelUpSoundsEnabled()) {
SoundManager.sendSound(leader, leader.getLocation(), SoundType.LEVEL_UP);
}
}
return;
}
PartyManager.informPartyMembersLevelUp(this, levelsGained, getLevel());
}
public boolean hasReachedLevelCap() {
return MainConfig.getInstance().getPartyLevelCap() < getLevel() + 1;
}
public ShareMode getXpShareMode() {
return xpShareMode;
}
public void setXpShareMode(ShareMode xpShareMode) {
this.xpShareMode = xpShareMode;
}
public ShareMode getItemShareMode() {
return itemShareMode;
}
public void setItemShareMode(ShareMode itemShareMode) {
this.itemShareMode = itemShareMode;
}
public boolean sharingDrops(ItemShareType shareType) {
switch (shareType) {
case HERBALISM:
return shareHerbalismDrops;
case LOOT:
return shareLootDrops;
case MINING:
return shareMiningDrops;
case MISC:
return shareMiscDrops;
case WOODCUTTING:
return shareWoodcuttingDrops;
default:
return false;
}
}
public void setSharingDrops(ItemShareType shareType, boolean enabled) {
switch (shareType) {
case HERBALISM:
shareHerbalismDrops = enabled;
break;
case LOOT:
shareLootDrops = enabled;
break;
case MINING:
shareMiningDrops = enabled;
break;
case MISC:
shareMiscDrops = enabled;
break;
case WOODCUTTING:
shareWoodcuttingDrops = enabled;
break;
default:
return;
}
}
public boolean hasMember(String memberName) {
return this.getMembers().values().contains(memberName);
}
public boolean hasMember(UUID uuid) {
return this.getMembers().keySet().contains(uuid);
}
public String createMembersList(Player player) {
StringBuilder memberList = new StringBuilder();
for (Player otherPlayer : this.getVisibleMembers(player)) {
String memberName = otherPlayer.getName();
if (this.getLeader().getUniqueId().equals(otherPlayer.getUniqueId())) {
memberList.append(ChatColor.GOLD);
if (otherPlayer == null) {
memberName = memberName.substring(0, 1) + ChatColor.GRAY + ChatColor.ITALIC + "" + memberName.substring(1);
}
} else if (otherPlayer != null) {
memberList.append(ChatColor.WHITE);
} else {
memberList.append(ChatColor.GRAY);
}
if (player.getName().equalsIgnoreCase(otherPlayer.getName())) {
memberList.append(ChatColor.ITALIC);
}
memberList.append(memberName).append(ChatColor.RESET).append(" ");
}
return memberList.toString();
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instanceof Party)) {
return false;
}
Party other = (Party) obj;
if ((this.getName() == null) || (other.getName() == null)) {
return false;
}
return this.getName().equals(other.getName());
}
}

View File

@ -0,0 +1,49 @@
package com.gmail.nossr50.core.datatypes.party;
import com.gmail.nossr50.core.config.MainConfig;
import com.gmail.nossr50.core.locale.LocaleLoader;
import com.gmail.nossr50.core.mcmmo.entity.Player;
import com.gmail.nossr50.core.util.Permissions;
import com.gmail.nossr50.core.util.StringUtils;
public enum PartyFeature {
CHAT,
TELEPORT,
ALLIANCE,
ITEM_SHARE,
XP_SHARE;
public String getLocaleString() {
return LocaleLoader.getString("Party.Feature." + StringUtils.getPrettyPartyFeatureString(this).replace(" ", ""));
}
public String getFeatureLockedLocaleString() {
return LocaleLoader.getString("Ability.Generic.Template.Lock", LocaleLoader.getString("Party.Feature.Locked." + StringUtils.getPrettyPartyFeatureString(this).replace(" ", ""), MainConfig.getInstance().getPartyFeatureUnlockLevel(this)));
}
public boolean hasPermission(Player player) {
PartySubcommandType partySubCommandType;
switch (this) {
case CHAT:
partySubCommandType = PartySubcommandType.CHAT;
break;
case TELEPORT:
partySubCommandType = PartySubcommandType.TELEPORT;
break;
case ALLIANCE:
partySubCommandType = PartySubcommandType.ALLIANCE;
break;
case ITEM_SHARE:
partySubCommandType = PartySubcommandType.ITEMSHARE;
break;
case XP_SHARE:
partySubCommandType = PartySubcommandType.XPSHARE;
break;
default:
return false;
}
return Permissions.partySubcommand(player, partySubCommandType);
}
}

View File

@ -0,0 +1,21 @@
package com.gmail.nossr50.core.datatypes.party;
import java.util.UUID;
public class PartyLeader {
private UUID uuid;
private String playerName;
public PartyLeader(UUID uuid, String playerName) {
this.uuid = uuid;
this.playerName = playerName;
}
public UUID getUniqueId() {
return uuid;
}
public String getPlayerName() {
return playerName;
}
}

View File

@ -0,0 +1,67 @@
package com.gmail.nossr50.core.datatypes.party;
import com.gmail.nossr50.core.config.MainConfig;
import com.gmail.nossr50.core.mcmmo.entity.Player;
import com.gmail.nossr50.core.util.Misc;
public class PartyTeleportRecord {
private Player requestor;
private boolean enabled, confirmRequired;
private int timeout, lastUse;
public PartyTeleportRecord() {
requestor = null;
enabled = true;
confirmRequired = MainConfig.getInstance().getPTPCommandConfirmRequired();
timeout = 0;
lastUse = 0;
}
public boolean isEnabled() {
return enabled;
}
public void toggleEnabled() {
enabled = !enabled;
}
public Player getRequestor() {
return requestor;
}
public void setRequestor(Player requestor) {
this.requestor = requestor;
}
public boolean hasRequest() {
return (requestor != null);
}
public void removeRequest() {
requestor = null;
}
public boolean isConfirmRequired() {
return confirmRequired;
}
public void toggleConfirmRequired() {
confirmRequired = !confirmRequired;
}
public int getLastUse() {
return lastUse;
}
public void actualizeLastUse() {
lastUse = (int) (System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR);
}
public int getTimeout() {
return timeout;
}
public void actualizeTimeout() {
timeout = (int) (System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR);
}
}

View File

@ -0,0 +1,24 @@
package com.gmail.nossr50.core.datatypes.party;
import com.gmail.nossr50.core.util.commands.CommandUtils;
public enum ShareMode {
NONE,
EQUAL,
RANDOM;
public static ShareMode getShareMode(String string) {
try {
return valueOf(string);
} catch (IllegalArgumentException ex) {
if (string.equalsIgnoreCase("even")) {
return EQUAL;
} else if (CommandUtils.shouldDisableToggle(string)) {
return NONE;
}
return null;
}
}
}

View File

@ -0,0 +1,995 @@
package com.gmail.nossr50.core.datatypes.player;
import com.gmail.nossr50.core.McmmoCore;
import com.gmail.nossr50.core.config.AdvancedConfig;
import com.gmail.nossr50.core.config.MainConfig;
import com.gmail.nossr50.core.config.WorldBlacklist;
import com.gmail.nossr50.core.config.experience.ExperienceConfig;
import com.gmail.nossr50.core.data.UserManager;
import com.gmail.nossr50.core.datatypes.chat.ChatMode;
import com.gmail.nossr50.core.datatypes.experience.XPGainReason;
import com.gmail.nossr50.core.datatypes.experience.XPGainSource;
import com.gmail.nossr50.core.datatypes.interactions.NotificationType;
import com.gmail.nossr50.core.datatypes.mods.CustomTool;
import com.gmail.nossr50.core.datatypes.party.Party;
import com.gmail.nossr50.core.datatypes.party.PartyTeleportRecord;
import com.gmail.nossr50.core.locale.LocaleLoader;
import com.gmail.nossr50.core.mcmmo.entity.Player;
import com.gmail.nossr50.core.mcmmo.item.ItemStack;
import com.gmail.nossr50.core.mcmmo.meta.Metadata;
import com.gmail.nossr50.core.mcmmo.meta.MetadataDefinitions;
import com.gmail.nossr50.core.mcmmo.world.Location;
import com.gmail.nossr50.core.party.PartyManager;
import com.gmail.nossr50.core.party.ShareHandler;
import com.gmail.nossr50.core.runnables.skills.AbilityDisableTask;
import com.gmail.nossr50.core.runnables.skills.BleedTimerTask;
import com.gmail.nossr50.core.runnables.skills.ToolLowerTask;
import com.gmail.nossr50.core.skills.PrimarySkillType;
import com.gmail.nossr50.core.skills.SkillManager;
import com.gmail.nossr50.core.skills.SuperAbilityType;
import com.gmail.nossr50.core.skills.ToolType;
import com.gmail.nossr50.core.skills.child.FamilyTree;
import com.gmail.nossr50.core.skills.child.salvage.SalvageManager;
import com.gmail.nossr50.core.skills.child.smelting.SmeltingManager;
import com.gmail.nossr50.core.skills.primary.acrobatics.AcrobaticsManager;
import com.gmail.nossr50.core.skills.primary.alchemy.AlchemyManager;
import com.gmail.nossr50.core.skills.primary.archery.ArcheryManager;
import com.gmail.nossr50.core.skills.primary.axes.AxesManager;
import com.gmail.nossr50.core.skills.primary.excavation.ExcavationManager;
import com.gmail.nossr50.core.skills.primary.fishing.FishingManager;
import com.gmail.nossr50.core.skills.primary.herbalism.HerbalismManager;
import com.gmail.nossr50.core.skills.primary.mining.MiningManager;
import com.gmail.nossr50.core.skills.primary.repair.RepairManager;
import com.gmail.nossr50.core.skills.primary.swords.SwordsManager;
import com.gmail.nossr50.core.skills.primary.taming.TamingManager;
import com.gmail.nossr50.core.skills.primary.unarmed.UnarmedManager;
import com.gmail.nossr50.core.skills.primary.woodcutting.WoodcuttingManager;
import com.gmail.nossr50.core.util.EventUtils;
import com.gmail.nossr50.core.util.Misc;
import com.gmail.nossr50.core.util.Permissions;
import com.gmail.nossr50.core.util.experience.ExperienceBarManager;
import com.gmail.nossr50.core.util.player.NotificationManager;
import com.gmail.nossr50.core.util.scoreboards.ScoreboardManager;
import com.gmail.nossr50.core.util.skills.ParticleEffectUtils;
import com.gmail.nossr50.core.util.skills.PerksUtils;
import com.gmail.nossr50.core.util.skills.RankUtils;
import com.gmail.nossr50.core.util.skills.SkillUtils;
import com.gmail.nossr50.core.util.sounds.SoundManager;
import com.gmail.nossr50.core.util.sounds.SoundType;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public class McMMOPlayer {
private final Map<PrimarySkillType, SkillManager> skillManagers = new HashMap<PrimarySkillType, SkillManager>();
private final Map<SuperAbilityType, Boolean> abilityMode = new HashMap<SuperAbilityType, Boolean>();
private final Map<SuperAbilityType, Boolean> abilityInformed = new HashMap<SuperAbilityType, Boolean>();
private final Map<ToolType, Boolean> toolMode = new HashMap<ToolType, Boolean>();
private final Metadata playerMetadata;
private Player player;
private PlayerProfile profile;
private ExperienceBarManager experienceBarManager;
private Party party;
private Party invite;
private Party allianceInvite;
private int itemShareModifier;
private PartyTeleportRecord ptpRecord;
private boolean partyChatMode;
private boolean adminChatMode;
private boolean displaySkillNotifications = true;
private boolean abilityUse = true;
private boolean godMode;
private boolean chatSpy = false; //Off by default
private int recentlyHurt;
private int respawnATS;
private int teleportATS;
private long databaseATS;
//private int chimeraWingLastUse;
private Location teleportCommence;
private boolean isUsingUnarmed;
public McMMOPlayer(Player player, PlayerProfile profile) {
String playerName = player.getName();
UUID uuid = player.getUUID();
this.player = player;
playerMetadata = player.setMetadata(MetadataDefinitions.MCMMO_METADATA_PLAYERDATA_KEY, playerName); //new FixedMetadataValue(mcMMO.p, playerName);
this.profile = profile;
if (profile.getUniqueId() == null) {
profile.setUniqueId(uuid);
}
/*
* I'm using this method because it makes code shorter and safer (we don't have to add all SkillTypes manually),
* but I actually have no idea about the performance impact, if there is any.
* If in the future someone wants to remove this, don't forget to also remove what is in the PrimarySkillType enum. - bm01
*/
try {
for (PrimarySkillType primarySkillType : PrimarySkillType.values()) {
skillManagers.put(primarySkillType, primarySkillType.getManagerClass().getConstructor(McMMOPlayer.class).newInstance(this));
}
} catch (Exception e) {
e.printStackTrace();
McmmoCore.p.disablePlugin(); //Disable Plugin
//mcMMO.p.getPluginLoader().disablePlugin(mcMMO.p);
}
for (SuperAbilityType superAbilityType : SuperAbilityType.values()) {
abilityMode.put(superAbilityType, false);
abilityInformed.put(superAbilityType, true); // This is intended
}
for (ToolType toolType : ToolType.values()) {
toolMode.put(toolType, false);
}
experienceBarManager = new ExperienceBarManager(this);
}
/*public void hideXpBar(PrimarySkillType primarySkillType)
{
experienceBarManager.hideExperienceBar(primarySkillType);
}*/
public void processPostXpEvent(XPGainReason xpGainReason, PrimarySkillType primarySkillType, XPGainSource xpGainSource) {
//Updates from Party sources
if (xpGainSource == XPGainSource.PARTY_MEMBERS && !ExperienceConfig.getInstance().isPartyExperienceBarsEnabled())
return;
//Updates from passive sources (Alchemy, Smelting, etc...)
if (xpGainSource == XPGainSource.PASSIVE && !ExperienceConfig.getInstance().isPassiveGainsExperienceBarsEnabled())
return;
updateXPBar(primarySkillType);
}
public void processUnlockNotifications(PrimarySkillType primarySkillType, int skillLevel) {
RankUtils.executeSkillUnlockNotifications(this, primarySkillType, skillLevel);
}
public void updateXPBar(PrimarySkillType primarySkillType) {
//Skill Unlock Notifications
if (primarySkillType.isChildSkill())
return;
//XP BAR UPDATES
experienceBarManager.updateExperienceBar(primarySkillType);
}
public double getProgressInCurrentSkillLevel(PrimarySkillType primarySkillType) {
double currentXP = profile.getSkillXpLevel(primarySkillType);
double maxXP = profile.getXpToLevel(primarySkillType);
return (currentXP / maxXP);
}
public AcrobaticsManager getAcrobaticsManager() {
return (AcrobaticsManager) skillManagers.get(PrimarySkillType.ACROBATICS);
}
public AlchemyManager getAlchemyManager() {
return (AlchemyManager) skillManagers.get(PrimarySkillType.ALCHEMY);
}
public ArcheryManager getArcheryManager() {
return (ArcheryManager) skillManagers.get(PrimarySkillType.ARCHERY);
}
public AxesManager getAxesManager() {
return (AxesManager) skillManagers.get(PrimarySkillType.AXES);
}
public ExcavationManager getExcavationManager() {
return (ExcavationManager) skillManagers.get(PrimarySkillType.EXCAVATION);
}
public FishingManager getFishingManager() {
return (FishingManager) skillManagers.get(PrimarySkillType.FISHING);
}
public HerbalismManager getHerbalismManager() {
return (HerbalismManager) skillManagers.get(PrimarySkillType.HERBALISM);
}
public MiningManager getMiningManager() {
return (MiningManager) skillManagers.get(PrimarySkillType.MINING);
}
public RepairManager getRepairManager() {
return (RepairManager) skillManagers.get(PrimarySkillType.REPAIR);
}
public SalvageManager getSalvageManager() {
return (SalvageManager) skillManagers.get(PrimarySkillType.SALVAGE);
}
public SmeltingManager getSmeltingManager() {
return (SmeltingManager) skillManagers.get(PrimarySkillType.SMELTING);
}
public SwordsManager getSwordsManager() {
return (SwordsManager) skillManagers.get(PrimarySkillType.SWORDS);
}
public TamingManager getTamingManager() {
return (TamingManager) skillManagers.get(PrimarySkillType.TAMING);
}
public UnarmedManager getUnarmedManager() {
return (UnarmedManager) skillManagers.get(PrimarySkillType.UNARMED);
}
public WoodcuttingManager getWoodcuttingManager() {
return (WoodcuttingManager) skillManagers.get(PrimarySkillType.WOODCUTTING);
}
/*
* Abilities
*/
/**
* Reset the mode of all abilities.
*/
public void resetAbilityMode() {
for (SuperAbilityType ability : SuperAbilityType.values()) {
// Correctly disable and handle any special deactivate code
new AbilityDisableTask(this, ability).run();
}
}
/**
* Get the mode of an ability.
*
* @param ability The ability to check
* @return true if the ability is enabled, false otherwise
*/
public boolean getAbilityMode(SuperAbilityType ability) {
return abilityMode.get(ability);
}
/**
* Set the mode of an ability.
*
* @param ability The ability to check
* @param isActive True if the ability is active, false otherwise
*/
public void setAbilityMode(SuperAbilityType ability, boolean isActive) {
abilityMode.put(ability, isActive);
}
/**
* Get the informed state of an ability
*
* @param ability The ability to check
* @return true if the ability is informed, false otherwise
*/
public boolean getAbilityInformed(SuperAbilityType ability) {
return abilityInformed.get(ability);
}
/**
* Set the informed state of an ability.
*
* @param ability The ability to check
* @param isInformed True if the ability is informed, false otherwise
*/
public void setAbilityInformed(SuperAbilityType ability, boolean isInformed) {
abilityInformed.put(ability, isInformed);
}
/**
* Get the current prep mode of a tool.
*
* @param tool Tool to get the mode for
* @return true if the tool is prepped, false otherwise
*/
public boolean getToolPreparationMode(ToolType tool) {
return toolMode.get(tool);
}
public boolean getAbilityUse() {
return abilityUse;
}
public void toggleAbilityUse() {
abilityUse = !abilityUse;
}
/*
* Tools
*/
/**
* Reset the prep modes of all tools.
*/
public void resetToolPrepMode() {
for (ToolType tool : ToolType.values()) {
setToolPreparationMode(tool, false);
}
}
/**
* Set the current prep mode of a tool.
*
* @param tool Tool to set the mode for
* @param isPrepared true if the tool should be prepped, false otherwise
*/
public void setToolPreparationMode(ToolType tool, boolean isPrepared) {
toolMode.put(tool, isPrepared);
}
/*
* Recently Hurt
*/
public int getRecentlyHurt() {
return recentlyHurt;
}
public void setRecentlyHurt(int value) {
recentlyHurt = value;
}
public void actualizeRecentlyHurt() {
recentlyHurt = (int) (System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR);
}
/*
* Teleportation cooldown & warmup
*/
public int getChimeraWingLastUse() {
return profile.getChimaerWingDATS();
}
public void actualizeChimeraWingLastUse() {
profile.setChimaeraWingDATS((int) (System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR));
}
public Location getTeleportCommenceLocation() {
return teleportCommence;
}
public void setTeleportCommenceLocation(Location location) {
teleportCommence = location;
}
public void actualizeTeleportCommenceLocation(Player player) {
teleportCommence = player.getLocation();
}
/*
* Exploit Prevention
*/
public int getRespawnATS() {
return respawnATS;
}
public void actualizeRespawnATS() {
respawnATS = (int) (System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR);
}
public int getTeleportATS() {
return teleportATS;
}
public void actualizeTeleportATS() {
teleportATS = (int) (System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR);
}
public long getDatabaseATS() {
return databaseATS;
}
public void actualizeDatabaseATS() {
databaseATS = System.currentTimeMillis();
}
/*
* God Mode
*/
public boolean getGodMode() {
return godMode;
}
public void toggleGodMode() {
godMode = !godMode;
}
/*
* Party Chat Spy
*/
public boolean isPartyChatSpying() {
return chatSpy;
}
public void togglePartyChatSpying() {
chatSpy = !chatSpy;
}
/*
* Skill notifications
*/
public boolean useChatNotifications() {
return displaySkillNotifications;
}
public void toggleChatNotifications() {
displaySkillNotifications = !displaySkillNotifications;
}
/**
* Gets the power level of this player.
*
* @return the power level of the player
*/
public int getPowerLevel() {
int powerLevel = 0;
for (PrimarySkillType type : PrimarySkillType.NON_CHILD_SKILLS) {
if (type.getPermissions(player)) {
powerLevel += getSkillLevel(type);
}
}
return powerLevel;
}
/**
* Begins an experience gain. The amount will be affected by skill modifiers, global rate, perks, and may be shared with the party
*
* @param skill Skill being used
* @param xp Experience amount to process
*/
public void beginXpGain(PrimarySkillType skill, float xp, XPGainReason xpGainReason, XPGainSource xpGainSource) {
Validate.isTrue(xp >= 0.0, "XP gained should be greater than or equal to zero.");
if (xp <= 0.0) {
return;
}
if (skill.isChildSkill()) {
Set<PrimarySkillType> parentSkills = FamilyTree.getParents(skill);
float splitXp = xp / parentSkills.size();
for (PrimarySkillType parentSkill : parentSkills) {
if (parentSkill.getPermissions(player)) {
beginXpGain(parentSkill, splitXp, xpGainReason, xpGainSource);
}
}
return;
}
// Return if the experience has been shared
if (party != null && ShareHandler.handleXpShare(xp, this, skill, ShareHandler.getSharedXpGainReason(xpGainReason))) {
return;
}
beginUnsharedXpGain(skill, xp, xpGainReason, xpGainSource);
}
/**
* Begins an experience gain. The amount will be affected by skill modifiers, global rate and perks
*
* @param skill Skill being used
* @param xp Experience amount to process
*/
public void beginUnsharedXpGain(PrimarySkillType skill, float xp, XPGainReason xpGainReason, XPGainSource xpGainSource) {
applyXpGain(skill, modifyXpGain(skill, xp), xpGainReason, xpGainSource);
if (party == null) {
return;
}
if (!MainConfig.getInstance().getPartyXpNearMembersNeeded() || !PartyManager.getNearMembers(this).isEmpty()) {
party.applyXpGain(modifyXpGain(skill, xp));
}
}
/**
* Applies an experience gain
*
* @param primarySkillType Skill being used
* @param xp Experience amount to add
*/
public void applyXpGain(PrimarySkillType primarySkillType, float xp, XPGainReason xpGainReason, XPGainSource xpGainSource) {
if (!primarySkillType.getPermissions(player)) {
return;
}
if (primarySkillType.isChildSkill()) {
Set<PrimarySkillType> parentSkills = FamilyTree.getParents(primarySkillType);
for (PrimarySkillType parentSkill : parentSkills) {
applyXpGain(parentSkill, xp / parentSkills.size(), xpGainReason, xpGainSource);
}
return;
}
if (!EventUtils.handleXpGainEvent(player, primarySkillType, xp, xpGainReason)) {
return;
}
isUsingUnarmed = (primarySkillType == PrimarySkillType.UNARMED);
checkXp(primarySkillType, xpGainReason, xpGainSource);
}
/**
* Check the XP of a skill.
*
* @param primarySkillType The skill to check
*/
private void checkXp(PrimarySkillType primarySkillType, XPGainReason xpGainReason, XPGainSource xpGainSource) {
if (getSkillXpLevelRaw(primarySkillType) < getXpToLevel(primarySkillType)) {
UserManager.getPlayer(player).processPostXpEvent(xpGainReason, primarySkillType, mcMMO.p, xpGainSource);
return;
}
int levelsGained = 0;
float xpRemoved = 0;
while (getSkillXpLevelRaw(primarySkillType) >= getXpToLevel(primarySkillType)) {
if (hasReachedLevelCap(primarySkillType)) {
setSkillXpLevel(primarySkillType, 0);
break;
}
xpRemoved += profile.levelUp(primarySkillType);
levelsGained++;
}
if (!EventUtils.handleLevelChangeEvent(player, primarySkillType, levelsGained, xpRemoved, true, xpGainReason)) {
UserManager.getPlayer(player).processPostXpEvent(xpGainReason, primarySkillType, mcMMO.p, xpGainSource);
return;
}
if (MainConfig.getInstance().getLevelUpSoundsEnabled()) {
SoundManager.sendSound(player, player.getLocation(), SoundType.LEVEL_UP);
}
/*
* Check to see if the player unlocked any new skills
*/
NotificationManager.sendPlayerLevelUpNotification(UserManager.getPlayer(player), primarySkillType, levelsGained, profile.getSkillLevel(primarySkillType));
//UPDATE XP BARS
UserManager.getPlayer(player).processPostXpEvent(xpGainReason, primarySkillType, mcMMO.p, xpGainSource);
}
/*
* Players & Profiles
*/
public Player getPlayer() {
return player;
}
public PlayerProfile getProfile() {
return profile;
}
/*
* Party Stuff
*/
public void setupPartyData() {
party = PartyManager.getPlayerParty(player.getName(), player.getUniqueId());
ptpRecord = new PartyTeleportRecord();
if (inParty()) {
loginParty();
}
}
public Party getPartyInvite() {
return invite;
}
public void setPartyInvite(Party invite) {
this.invite = invite;
}
public boolean hasPartyInvite() {
return (invite != null);
}
public Party getParty() {
return party;
}
public void setParty(Party party) {
this.party = party;
}
public boolean inParty() {
return (party != null);
}
public void removeParty() {
party = null;
}
public void removePartyInvite() {
invite = null;
}
public PartyTeleportRecord getPartyTeleportRecord() {
return ptpRecord;
}
public Party getPartyAllianceInvite() {
return allianceInvite;
}
public void setPartyAllianceInvite(Party allianceInvite) {
this.allianceInvite = allianceInvite;
}
public boolean hasPartyAllianceInvite() {
return (allianceInvite != null);
}
public void removePartyAllianceInvite() {
allianceInvite = null;
}
public void loginParty() {
party.addOnlineMember(this.getPlayer());
}
public int getItemShareModifier() {
if (itemShareModifier < 10) {
setItemShareModifier(10);
}
return itemShareModifier;
}
public void setItemShareModifier(int modifier) {
itemShareModifier = Math.max(10, modifier);
}
/*
* Chat modes
*/
public boolean isChatEnabled(ChatMode mode) {
switch (mode) {
case ADMIN:
return adminChatMode;
case PARTY:
return partyChatMode;
default:
return false;
}
}
public void disableChat(ChatMode mode) {
switch (mode) {
case ADMIN:
adminChatMode = false;
return;
case PARTY:
partyChatMode = false;
return;
default:
return;
}
}
public void enableChat(ChatMode mode) {
switch (mode) {
case ADMIN:
adminChatMode = true;
partyChatMode = false;
return;
case PARTY:
partyChatMode = true;
adminChatMode = false;
return;
default:
return;
}
}
public void toggleChat(ChatMode mode) {
switch (mode) {
case ADMIN:
adminChatMode = !adminChatMode;
partyChatMode = !adminChatMode && partyChatMode;
return;
case PARTY:
partyChatMode = !partyChatMode;
adminChatMode = !partyChatMode && adminChatMode;
return;
default:
return;
}
}
public boolean isUsingUnarmed() {
return isUsingUnarmed;
}
/**
* Modifies an experience gain using skill modifiers, global rate and perks
*
* @param primarySkillType Skill being used
* @param xp Experience amount to process
* @return Modified experience
*/
private float modifyXpGain(PrimarySkillType primarySkillType, float xp) {
if (player.getGameMode() == GameMode.CREATIVE || (primarySkillType.getMaxLevel() <= getSkillLevel(primarySkillType)) || (MainConfig.getInstance().getPowerLevelCap() <= getPowerLevel())) {
return 0;
}
xp = (float) (xp / primarySkillType.getXpModifier() * ExperienceConfig.getInstance().getExperienceGainsGlobalMultiplier());
if (MainConfig.getInstance().getToolModsEnabled()) {
CustomTool tool = mcMMO.getModManager().getTool(player.getInventory().getItemInMainHand());
if (tool != null) {
xp *= tool.getXpMultiplier();
}
}
return PerksUtils.handleXpPerks(player, xp, primarySkillType);
}
public void checkGodMode() {
if (godMode && !Permissions.mcgod(player)
|| godMode && WorldBlacklist.isWorldBlacklisted(player.getWorld())) {
toggleGodMode();
player.sendMessage(LocaleLoader.getString("Commands.GodMode.Forbidden"));
}
}
public void checkParty() {
if (inParty() && !Permissions.party(player)) {
removeParty();
player.sendMessage(LocaleLoader.getString("Party.Forbidden"));
}
}
/**
* Check to see if an ability can be activated.
*
* @param skill The skill the ability is based on
*/
public void checkAbilityActivation(PrimarySkillType skill) {
ToolType tool = skill.getTool();
SuperAbilityType ability = skill.getAbility();
if (getAbilityMode(ability) || !ability.getPermissions(player)) {
return;
}
//TODO: This is hacky and temporary solution until skills are move to the new system
//Potential problems with this include skills with two super abilities (ie mining)
if (!skill.isSuperAbilityUnlocked(getPlayer())) {
int diff = RankUtils.getSuperAbilityUnlockRequirement(skill.getAbility()) - getSkillLevel(skill);
//Inform the player they are not yet skilled enough
NotificationManager.sendPlayerInformation(player, NotificationType.ABILITY_COOLDOWN, "Skills.AbilityGateRequirementFail", String.valueOf(diff), skill.getName());
return;
}
int timeRemaining = calculateTimeRemaining(ability);
if (timeRemaining > 0) {
/*
* Axes and Woodcutting are odd because they share the same tool.
* We show them the too tired message when they take action.
*/
if (skill == PrimarySkillType.WOODCUTTING || skill == PrimarySkillType.AXES) {
NotificationManager.sendPlayerInformation(player, NotificationType.ABILITY_COOLDOWN, "Skills.TooTired", String.valueOf(timeRemaining));
//SoundManager.sendSound(player, player.getLocation(), SoundType.TIRED);
}
return;
}
if (EventUtils.callPlayerAbilityActivateEvent(player, skill).isCancelled()) {
return;
}
//These values change depending on whether or not the server is in retro mode
int abilityLengthVar = AdvancedConfig.getInstance().getAbilityLength();
int abilityLengthCap = AdvancedConfig.getInstance().getAbilityLengthCap();
int ticks;
//Ability cap of 0 or below means no cap
if (abilityLengthCap > 0) {
ticks = PerksUtils.handleActivationPerks(player, 2 + (Math.min(abilityLengthCap, getSkillLevel(skill)) / abilityLengthVar), ability.getMaxLength());
} else {
ticks = PerksUtils.handleActivationPerks(player, 2 + (getSkillLevel(skill) / abilityLengthVar), ability.getMaxLength());
}
// Notify people that ability has been activated
ParticleEffectUtils.playAbilityEnabledEffect(player);
if (useChatNotifications()) {
NotificationManager.sendPlayerInformation(player, NotificationType.SUPER_ABILITY, ability.getAbilityOn());
//player.sendMessage(ability.getAbilityOn());
}
SkillUtils.sendSkillMessage(player, NotificationType.SUPER_ABILITY_ALERT_OTHERS, ability.getAbilityPlayer());
//Sounds
SoundManager.worldSendSound(player.getWorld(), player.getLocation(), SoundType.ABILITY_ACTIVATED_GENERIC);
// Enable the ability
profile.setAbilityDATS(ability, System.currentTimeMillis() + (ticks * Misc.TIME_CONVERSION_FACTOR));
setAbilityMode(ability, true);
if (ability == SuperAbilityType.SUPER_BREAKER || ability == SuperAbilityType.GIGA_DRILL_BREAKER) {
SkillUtils.handleAbilitySpeedIncrease(player);
}
setToolPreparationMode(tool, false);
new AbilityDisableTask(this, ability).runTaskLater(mcMMO.p, ticks * Misc.TICK_CONVERSION_FACTOR);
}
public void processAbilityActivation(PrimarySkillType skill) {
if (MainConfig.getInstance().getAbilitiesOnlyActivateWhenSneaking() && !player.isSneaking()) {
return;
}
ItemStack inHand = player.getInventory().getItemInMainHand();
if (mcMMO.getModManager().isCustomTool(inHand) && !mcMMO.getModManager().getTool(inHand).isAbilityEnabled()) {
return;
}
if (!getAbilityUse()) {
return;
}
for (SuperAbilityType superAbilityType : SuperAbilityType.values()) {
if (getAbilityMode(superAbilityType)) {
return;
}
}
SuperAbilityType ability = skill.getAbility();
ToolType tool = skill.getTool();
/*
* Woodcutting & Axes need to be treated differently.
* Basically the tool always needs to ready and we check to see if the cooldown is over when the user takes action
*/
if (tool.inHand(inHand) && !getToolPreparationMode(tool)) {
if (skill != PrimarySkillType.WOODCUTTING && skill != PrimarySkillType.AXES) {
int timeRemaining = calculateTimeRemaining(ability);
if (!getAbilityMode(ability) && timeRemaining > 0) {
NotificationManager.sendPlayerInformation(player, NotificationType.ABILITY_COOLDOWN, "Skills.TooTired", String.valueOf(timeRemaining));
return;
}
}
if (MainConfig.getInstance().getAbilityMessagesEnabled()) {
NotificationManager.sendPlayerInformation(player, NotificationType.TOOL, tool.getRaiseTool());
SoundManager.sendSound(player, player.getLocation(), SoundType.TOOL_READY);
}
setToolPreparationMode(tool, true);
new ToolLowerTask(this, tool).runTaskLaterAsynchronously(mcMMO.p, 4 * Misc.TICK_CONVERSION_FACTOR);
}
}
/**
* Calculate the time remaining until the ability's cooldown expires.
*
* @param ability SuperAbilityType whose cooldown to check
* @return the number of seconds remaining before the cooldown expires
*/
public int calculateTimeRemaining(SuperAbilityType ability) {
long deactivatedTimestamp = profile.getAbilityDATS(ability) * Misc.TIME_CONVERSION_FACTOR;
return (int) (((deactivatedTimestamp + (PerksUtils.handleCooldownPerks(player, ability.getCooldown()) * Misc.TIME_CONVERSION_FACTOR)) - System.currentTimeMillis()) / Misc.TIME_CONVERSION_FACTOR);
}
private boolean hasReachedLevelCap(PrimarySkillType skill) {
return (skill.getMaxLevel() < getSkillLevel(skill) + 1) || (MainConfig.getInstance().getPowerLevelCap() < getPowerLevel() + 1);
}
/*
* These functions are wrapped from PlayerProfile so that we don't always have to store it alongside the McMMOPlayer object.
*/
public int getSkillLevel(PrimarySkillType skill) {
return profile.getSkillLevel(skill);
}
public float getSkillXpLevelRaw(PrimarySkillType skill) {
return profile.getSkillXpLevelRaw(skill);
}
public int getSkillXpLevel(PrimarySkillType skill) {
return profile.getSkillXpLevel(skill);
}
public void setSkillXpLevel(PrimarySkillType skill, float xpLevel) {
profile.setSkillXpLevel(skill, xpLevel);
}
public int getXpToLevel(PrimarySkillType skill) {
return profile.getXpToLevel(skill);
}
public void removeXp(PrimarySkillType skill, int xp) {
profile.removeXp(skill, xp);
}
public void modifySkill(PrimarySkillType skill, int level) {
profile.modifySkill(skill, level);
}
public void addLevels(PrimarySkillType skill, int levels) {
profile.addLevels(skill, levels);
}
public void addXp(PrimarySkillType skill, float xp) {
profile.addXp(skill, xp);
}
public void setAbilityDATS(SuperAbilityType ability, long DATS) {
profile.setAbilityDATS(ability, DATS);
}
public void resetCooldowns() {
profile.resetCooldowns();
}
public FixedMetadataValue getPlayerMetadata() {
return playerMetadata;
}
/**
* This method is called by PlayerQuitEvent to tear down the mcMMOPlayer.
*
* @param syncSave if true, data is saved synchronously
*/
public void logout(boolean syncSave) {
Player thisPlayer = getPlayer();
resetAbilityMode();
BleedTimerTask.bleedOut(thisPlayer);
if (syncSave) {
getProfile().save();
} else {
getProfile().scheduleAsyncSave();
}
UserManager.remove(thisPlayer);
if (MainConfig.getInstance().getScoreboardsEnabled())
ScoreboardManager.teardownPlayer(thisPlayer);
if (inParty()) {
party.removeOnlineMember(thisPlayer);
}
}
}

View File

@ -0,0 +1,385 @@
package com.gmail.nossr50.core.datatypes.player;
import com.gmail.nossr50.core.config.AdvancedConfig;
import com.gmail.nossr50.core.config.MainConfig;
import com.gmail.nossr50.core.config.experience.ExperienceConfig;
import com.gmail.nossr50.core.data.UserManager;
import com.gmail.nossr50.core.datatypes.experience.FormulaType;
import com.gmail.nossr50.core.datatypes.experience.SkillXpGain;
import com.gmail.nossr50.core.runnables.player.PlayerProfileSaveTask;
import com.gmail.nossr50.core.skills.MobHealthbarType;
import com.gmail.nossr50.core.skills.PrimarySkillType;
import com.gmail.nossr50.core.skills.SuperAbilityType;
import com.gmail.nossr50.core.skills.child.FamilyTree;
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.concurrent.DelayQueue;
public class PlayerProfile {
private final String playerName;
/* Skill Data */
private final Map<PrimarySkillType, Integer> skills = new HashMap<PrimarySkillType, Integer>(); // Skill & Level
private final Map<PrimarySkillType, Float> skillsXp = new HashMap<PrimarySkillType, Float>(); // Skill & XP
private final Map<SuperAbilityType, Integer> abilityDATS = new HashMap<SuperAbilityType, Integer>(); // Ability & Cooldown
private final Map<UniqueDataType, Integer> uniquePlayerData = new HashMap<>(); //Misc data that doesn't fit into other categories (chimaera wing, etc..)
private UUID uuid;
private boolean loaded;
private volatile boolean changed;
/* HUDs */
private MobHealthbarType mobHealthbarType;
private int scoreboardTipsShown;
// Store previous XP gains for deminished returns
private DelayQueue<SkillXpGain> gainedSkillsXp = new DelayQueue<SkillXpGain>();
private HashMap<PrimarySkillType, Float> rollingSkillsXp = new HashMap<PrimarySkillType, Float>();
@Deprecated
public PlayerProfile(String playerName) {
this(playerName, null);
}
public PlayerProfile(String playerName, UUID uuid) {
this.uuid = uuid;
this.playerName = playerName;
mobHealthbarType = MainConfig.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;
}
public PlayerProfile(String playerName, UUID uuid, boolean isLoaded) {
this(playerName, uuid);
this.loaded = isLoaded;
}
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) {
this.playerName = playerName;
this.uuid = uuid;
this.mobHealthbarType = mobHealthbarType;
this.scoreboardTipsShown = scoreboardTipsShown;
skills.putAll(levelData);
skillsXp.putAll(xpData);
abilityDATS.putAll(cooldownData);
uniquePlayerData.putAll(uniqueProfileData);
loaded = true;
}
public void scheduleAsyncSave() {
new PlayerProfileSaveTask(this).runTaskAsynchronously(mcMMO.p);
}
public void save() {
if (!changed || !loaded) {
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));
changed = !mcMMO.getDatabaseManager().saveUser(profileCopy);
if (changed) {
mcMMO.p.getLogger().warning("PlayerProfile saving failed for player: " + playerName + " " + uuid);
}
}
public String getPlayerName() {
return playerName;
}
public UUID getUniqueId() {
return uuid;
}
public void setUniqueId(UUID uuid) {
changed = true;
this.uuid = uuid;
}
public boolean isLoaded() {
return loaded;
}
/*
* Mob Healthbars
*/
public MobHealthbarType getMobHealthbarType() {
return mobHealthbarType;
}
public void setMobHealthbarType(MobHealthbarType mobHealthbarType) {
changed = true;
this.mobHealthbarType = mobHealthbarType;
}
public int getScoreboardTipsShown() {
return scoreboardTipsShown;
}
public void setScoreboardTipsShown(int scoreboardTipsShown) {
changed = true;
this.scoreboardTipsShown = scoreboardTipsShown;
}
public void increaseTipsShown() {
setScoreboardTipsShown(getScoreboardTipsShown() + 1);
}
/*
* Cooldowns
*/
public int getChimaerWingDATS() {
return uniquePlayerData.get(UniqueDataType.CHIMAERA_WING_DATS);
}
protected void setChimaeraWingDATS(int DATS) {
changed = true;
uniquePlayerData.put(UniqueDataType.CHIMAERA_WING_DATS, DATS);
}
public void setUniqueData(UniqueDataType uniqueDataType, int newData) {
changed = true;
uniquePlayerData.put(uniqueDataType, newData);
}
public long getUniqueData(UniqueDataType uniqueDataType) {
return uniquePlayerData.get(uniqueDataType);
}
/**
* Get the current deactivation timestamp of an ability.
*
* @param ability The {@link SuperAbilityType} to get the DATS for
* @return the deactivation timestamp for the ability
*/
public long getAbilityDATS(SuperAbilityType ability) {
return abilityDATS.get(ability);
}
/**
* Set the current deactivation timestamp of an ability.
*
* @param ability The {@link SuperAbilityType} to set the DATS for
* @param DATS the DATS of the ability
*/
protected void setAbilityDATS(SuperAbilityType ability, long DATS) {
changed = true;
abilityDATS.put(ability, (int) (DATS * .001D));
}
/**
* Reset all ability cooldowns.
*/
protected void resetCooldowns() {
changed = true;
for (SuperAbilityType ability : abilityDATS.keySet()) {
abilityDATS.put(ability, 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) {
return (int) Math.floor(getSkillXpLevelRaw(skill));
}
public void setSkillXpLevel(PrimarySkillType skill, float xpLevel) {
if (skill.isChildSkill()) {
return;
}
changed = true;
skillsXp.put(skill, xpLevel);
}
protected float levelUp(PrimarySkillType skill) {
float xpRemoved = getXpToLevel(skill);
changed = true;
skills.put(skill, skills.get(skill) + 1);
skillsXp.put(skill, skillsXp.get(skill) - xpRemoved);
return xpRemoved;
}
/**
* Remove Xp from a skill.
*
* @param skill Type of skill to modify
* @param xp Amount of xp to remove
*/
public void removeXp(PrimarySkillType skill, int xp) {
if (skill.isChildSkill()) {
return;
}
changed = true;
skillsXp.put(skill, skillsXp.get(skill) - xp);
}
public void removeXp(PrimarySkillType skill, float xp) {
if (skill.isChildSkill()) {
return;
}
changed = true;
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;
}
changed = true;
//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) {
changed = true;
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) {
int level = (ExperienceConfig.getInstance().getCumulativeCurveEnabled()) ? UserManager.getPlayer(playerName).getPowerLevel() : skills.get(primarySkillType);
FormulaType formulaType = ExperienceConfig.getInstance().getFormulaType();
return mcMMO.getFormulaManager().getCachedXpToLevel(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();
}
}

View File

@ -0,0 +1,5 @@
package com.gmail.nossr50.core.datatypes.player;
public enum UniqueDataType {
CHIMAERA_WING_DATS
}

Some files were not shown because too many files have changed in this diff Show More