Merge branch 'master' of github.com:mcMMO-Dev/mcMMO into tridentsxbows

This commit is contained in:
nossr50 2021-03-16 14:18:09 -07:00
commit 31904ef181
28 changed files with 1779 additions and 2993 deletions

View File

@ -103,8 +103,59 @@ Version 2.2.000
Parties got unnecessarily complex in my absence, I have removed many party features in order to simplify parties and bring them closer to my vision. I have also added new features which should improve parties where it matters.
About the removed party features, all the features I removed I consider poor quality features and I don't think they belong in mcMMO. Feel free to yell at me in discord if you disagree.
I don't know what genius decided to make parties public by default, when I found out that parties had been changed to such a system I could barely contain my disgust. Parties are back to being private, you get invited by a party leader or party officer. That is the only way to join a party.
Version 2.1.182
Fixed several errors in de locale (Thanks TheBusyBiscuit & w1tcherrr)
Fixed a bug where double smelt never succeeded if the furnace was empty
Added some safety so that mcMMO automatic save interval is never more frequent than 1 minute
Version 2.1.181
mcMMO no longer pointlessly tries to check for missing UUIDs for FlatFile database
Removed the "name change detected" message as some plugins (such as Plan) invoke API calls which spams the console with this message
Refactored code related to loading player data from the database
(API) Added DatabaseManager::loadPlayerProfile(String)
(API) Removed DatabaseManager::loadPlayerProfile(String, UUID, boolean)
(API) Removed DatabaseManager::loadPlayerProfile(String, boolean)
Version 2.1.180
mcMMO will now automatically remove corrupted data from mcmmo.users instead of catastrophic failure
When using FlatFile database (the default) mcMMO will try its best to inform you which players had corrupted data when it does repairs
Various minor optimizations and tweaks to the FlatFile database
mcMMO is now much more verbose when things go wrong with the FlatFile database (removed some silent errors, added more error messages/warnings)
mcMMO now uses UTF-8 compliant encoding for SQL databases (utf8mb4)
Fixed a bug where mcMMO could in some circumstances fail to update SQL schema and mark it as successful
Renamed updates.yml to updates_overhaul.yml to avoid some potential issues when upgrading from classic
NOTES:
This update was tested pretty thoroughly so it should be pretty safe, let me know if you have issues in the mcMMO discord or GitHub issues page for mcMMO!
Version 2.1.179
Fixed a bug for FlatFile databases where some players with changed nicknames would have their levels not loaded upon login (possibly wiping their data)
NOTES:
Players affected by this bug (introduced in 2.1.177) may have their data lost, but this patch reverts the change which caused this bug.
I suspect their data isn't lost and may be restored after this patch is loaded up, however if it is lost mcMMO makes regular backups so you can load one of those (check <Server Directory>/plugins/mcMMO/) or manually edit their levels via MMOEDIT as a solution of sorts.
Version 2.1.178
Item replacement in vanilla fishing override back to SALMON from AIR (see notes)
NOTES:
Apparently can't set items to AIR, my bad. I'll look into another solution for fishing plugin compatibility soon.
Version 2.1.177
Environmentally aware will now protect Wolves from Magma blocks
Fixed a bug where mcMMO would fail to update a players name when it detected a name change
mcMMO will treat vanished players as if they are offline when using the inspect command on them now (see notes)
mcMMO now listens to PlayerFishEvent at HIGH event priority instead of HIGHEST
Changed how vanilla fishing treasures are overridden (AIR instead of SALMON)
(API) Added McMMOReplaceVanillaTreasureEvent -- see notes
NOTES:
A few changes were made to the inspect command, it used to reject you when used on vanished players, now it will be processed as if they are offline.
Additionally if you do inspect a vanished player, it will not use their display name (consistent with offline players) as that would give them away for being online
McMMOReplaceVanillaTreasureEvent is an event which is fired when mcMMO replaces a vanilla treasure with AIR if the server config file is set to override vanilla treasures, this causes some issues for other fishing plugins so this event helps those plugins be more compatible
Version 2.1.176
Added another measure to prevent item stacks from reaching 65 from double smelt
Another fix for Double Smelt bringing item stack size to illegal values
Version 2.1.175
Fixed a bug where mcMMO would occasionally give a 65 item stack from a double smelt on a furnace

20
pom.xml
View File

@ -16,6 +16,16 @@
<system>GitHub</system>
</issueManagement>
<packaging>jar</packaging>
<distributionManagement>
<repository>
<id>neetgames</id>
<url>https://nexus.neetgames.com/repository/maven-releases/</url>
</repository>
<snapshotRepository>
<id>neetgames</id>
<url>https://nexus.neetgames.com/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
<build>
<finalName>${project.artifactId}</finalName>
<sourceDirectory>${basedir}/src/main/java</sourceDirectory>
@ -236,27 +246,27 @@
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-gson</artifactId>
<version>4.5.1</version>
<version>4.7.0</version>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-api</artifactId>
<version>4.5.1</version>
<version>4.7.0</version>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-nbt</artifactId>
<version>4.5.1</version>
<version>4.7.0</version>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-key</artifactId>
<version>4.5.1</version>
<version>4.7.0</version>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-gson-legacy-impl</artifactId>
<version>4.5.1</version>
<version>4.7.0</version>
</dependency>
<dependency>
<groupId>net.kyori</groupId>

View File

@ -22,7 +22,7 @@ public class DatabaseAPI {
* @return true if the player exists in the DB, false if they do not
*/
public boolean doesPlayerExistInDB(UUID uuid) {
PlayerProfile playerProfile = mcMMO.getDatabaseManager().queryPlayerDataByUUID(uuid);
PlayerProfile playerProfile = mcMMO.getDatabaseManager().queryPlayerDataByUUID(uuid, null);
return playerProfile.isLoaded();
}

File diff suppressed because it is too large Load Diff

View File

@ -58,7 +58,7 @@ public class ConvertDatabaseCommand implements CommandExecutor {
}
for (Player player : mcMMO.p.getServer().getOnlinePlayers()) {
PlayerProfile profile = oldDatabase.queryPlayerDataByUUID(player.getUniqueId());
PlayerProfile profile = oldDatabase.queryPlayerDataByUUID(player.getUniqueId(), null);
if(profile == null)
continue;

View File

@ -23,7 +23,7 @@ public class DatabaseRemovePlayerCommand implements TabExecutor {
String playerName = CommandUtils.getMatchedPlayerName(args[0]);
if (mcMMO.getUserManager().queryPlayer(playerName) == null
&& CommandUtils.hasNoProfile(sender, mcMMO.getDatabaseManager().queryPlayerDataByUUID(playerName, false))) {
&& CommandUtils.hasNoProfile(sender, mcMMO.getDatabaseManager().queryPlayerDataByUUID(playerName))) {
sender.sendMessage(LocaleLoader.getString("Commands.Offline"));
return true;
}

View File

@ -1,14 +1,13 @@
package com.gmail.nossr50.commands.experience;
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.player.PlayerProfile;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.Misc;
import com.gmail.nossr50.util.commands.CommandUtils;
import com.gmail.nossr50.util.player.UserManager;
import com.google.common.collect.ImmutableList;
import com.neetgames.mcmmo.player.MMOPlayer;
import com.neetgames.mcmmo.player.OnlineMMOPlayer;
import com.neetgames.mcmmo.skill.RootSkill;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
@ -16,7 +15,6 @@ import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
@ -25,7 +23,7 @@ import java.util.UUID;
public abstract class ExperienceCommand implements TabExecutor {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
RootSkill rootSkill;
PrimarySkillType skill;
if(args.length < 2) {
return false;
@ -46,27 +44,27 @@ public abstract class ExperienceCommand implements TabExecutor {
return true;
}
rootSkill = mcMMO.p.getSkillRegister().getSkill(args[0]);
skill = PrimarySkillType.getSkill(args[0]);
if (args[1].equalsIgnoreCase("all")) {
rootSkill = null;
skill = null;
}
if (rootSkill != null && rootSkill.isChildSkill()) {
if (skill != null && skill.isChildSkill())
{
sender.sendMessage(LocaleLoader.getString("Commands.Skill.ChildSkill"));
return true;
}
//Profile not loaded
Player player = (Player) sender;
OnlineMMOPlayer mmoPlayer = mcMMO.getUserManager().queryPlayer(player);
if(mmoPlayer == null) {
if(UserManager.getPlayer(sender.getName()) == null)
{
sender.sendMessage(LocaleLoader.getString("Profile.PendingLoad"));
return true;
}
editValues(mmoPlayer, rootSkill, Integer.parseInt(args[1]), isSilent(args));
editValues((Player) sender, UserManager.getPlayer(sender.getName()).getProfile(), skill, Integer.parseInt(args[1]), isSilent(args));
return true;
} else if((args.length == 3 && !isSilent(args))
|| (args.length == 4 && isSilent(args))) {
@ -79,13 +77,13 @@ public abstract class ExperienceCommand implements TabExecutor {
return true;
}
rootSkill = mcMMO.p.getSkillRegister().getSkill(args[1]);
skill = PrimarySkillType.getSkill(args[1]);
if (args[1].equalsIgnoreCase("all")) {
rootSkill = null;
skill = null;
}
if (rootSkill != null && rootSkill.isChildSkill())
if (skill != null && skill.isChildSkill())
{
sender.sendMessage(LocaleLoader.getString("Commands.Skill.ChildSkill"));
return true;
@ -94,25 +92,31 @@ public abstract class ExperienceCommand implements TabExecutor {
int value = Integer.parseInt(args[2]);
String playerName = CommandUtils.getMatchedPlayerName(args[0]);
OnlineMMOPlayer mmoPlayer = mcMMO.getUserManager().queryPlayer(playerName);
McMMOPlayer mcMMOPlayer = UserManager.getOfflinePlayer(playerName);
// If the mmoPlayer doesn't exist, create a temporary profile and check if it's present in the database. If it's not, abort the process.
if (mmoPlayer == null) {
// If the mcMMOPlayer doesn't exist, create a temporary profile and check if it's present in the database. If it's not, abort the process.
if (mcMMOPlayer == null) {
UUID uuid = null;
OfflinePlayer player = mcMMO.p.getServer().getOfflinePlayer(playerName);
if (player != null) {
uuid = player.getUniqueId();
}
PlayerProfile profile = mcMMO.getDatabaseManager().queryPlayerDataByUUID(playerName, uuid, false);
OfflinePlayer offlinePlayer = mcMMO.p.getServer().getOfflinePlayer(playerName);
PlayerProfile profile;
if (CommandUtils.hasNoProfile(sender, profile)) {
return true;
uuid = offlinePlayer.getUniqueId();
profile = mcMMO.getDatabaseManager().loadPlayerProfile(uuid, null);
//Check loading by UUID
if (CommandUtils.unloadedProfile(sender, profile)) {
//Check loading by name
profile = mcMMO.getDatabaseManager().loadPlayerProfile(playerName);
if(CommandUtils.unloadedProfile(sender, profile)) {
return true;
}
}
editValues(null, profile, rootSkill, value, isSilent(args));
editValues(null, profile, skill, value, isSilent(args));
}
else {
editValues(Misc.adaptPlayer(mmoPlayer), mcMMOPlayer.getProfile(), rootSkill, value, isSilent(args));
editValues(mcMMOPlayer.getPlayer(), mcMMOPlayer.getProfile(), skill, value, isSilent(args));
}
handleSenderMessage(sender, playerName, skill);
@ -148,27 +152,27 @@ public abstract class ExperienceCommand implements TabExecutor {
protected abstract boolean permissionsCheckSelf(CommandSender sender);
protected abstract boolean permissionsCheckOthers(CommandSender sender);
protected abstract void handleCommand(Player player, PlayerProfile profile, RootSkill rootSkill, int value);
protected abstract void handleCommand(Player player, PlayerProfile profile, PrimarySkillType skill, int value);
protected abstract void handlePlayerMessageAll(Player player, int value, boolean isSilent);
protected abstract void handlePlayerMessageSkill(Player player, int value, RootSkill rootSkill, boolean isSilent);
protected abstract void handlePlayerMessageSkill(Player player, int value, PrimarySkillType skill, boolean isSilent);
private boolean validateArguments(CommandSender sender, String skillName, String value) {
return !(CommandUtils.isInvalidInteger(sender, value) || (!skillName.equalsIgnoreCase("all") && CommandUtils.isInvalidSkill(sender, skillName)));
}
protected static void handleSenderMessage(CommandSender sender, String playerName, RootSkill rootSkill) {
if (rootSkill == null) {
protected static void handleSenderMessage(CommandSender sender, String playerName, PrimarySkillType skill) {
if (skill == null) {
sender.sendMessage(LocaleLoader.getString("Commands.addlevels.AwardAll.2", playerName));
}
else {
sender.sendMessage(LocaleLoader.getString("Commands.addlevels.AwardSkill.2", rootSkill.getName(), playerName));
sender.sendMessage(LocaleLoader.getString("Commands.addlevels.AwardSkill.2", skill.getName(), playerName));
}
}
protected void editValues(@NotNull MMOPlayer mmoPlayer, @Nullable RootSkill rootSkill, int value, boolean isSilent) {
if (primarySkillType == null) {
for (PrimarySkillType type : PrimarySkillType.NON_CHILD_SKILLS) {
handleCommand(player, profile, type, value);
protected void editValues(Player player, PlayerProfile profile, PrimarySkillType skill, int value, boolean isSilent) {
if (skill == null) {
for (PrimarySkillType primarySkillType : PrimarySkillType.NON_CHILD_SKILLS) {
handleCommand(player, profile, primarySkillType, value);
}
if (player != null) {
@ -176,10 +180,10 @@ public abstract class ExperienceCommand implements TabExecutor {
}
}
else {
handleCommand(player, profile, primarySkillType, value);
handleCommand(player, profile, skill, value);
if (player != null) {
handlePlayerMessageSkill(player, value, primarySkillType, isSilent);
handlePlayerMessageSkill(player, value, skill, isSilent);
}
}
}

View File

@ -1,14 +1,16 @@
package com.gmail.nossr50.commands.experience;
import com.gmail.nossr50.datatypes.experience.XPGainReason;
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.player.PlayerProfile;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.EventUtils;
import com.gmail.nossr50.util.Permissions;
import com.gmail.nossr50.util.commands.CommandUtils;
import com.gmail.nossr50.util.player.UserManager;
import com.google.common.collect.ImmutableList;
import com.neetgames.mcmmo.player.OnlineMMOPlayer;
import com.neetgames.mcmmo.skill.RootSkill;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
@ -28,7 +30,7 @@ import java.util.UUID;
public class SkillresetCommand implements TabExecutor {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
RootSkill rootSkill;
PrimarySkillType skill;
switch (args.length) {
case 1:
if (CommandUtils.noConsoleUsage(sender)) {
@ -45,14 +47,13 @@ public class SkillresetCommand implements TabExecutor {
}
if (args[0].equalsIgnoreCase("all")) {
rootSkill = null;
skill = null;
}
else {
rootSkill = mcMMO.p.getSkillRegister().getSkill(args[0]);
skill = PrimarySkillType.getSkill(args[0]);
}
editValues((Player) sender, mcMMO.getUserManager().queryPlayer(player)
, skill);
editValues((Player) sender, UserManager.getPlayer(sender.getName()).getProfile(), skill);
return true;
case 2:
@ -66,32 +67,38 @@ public class SkillresetCommand implements TabExecutor {
}
if (args[1].equalsIgnoreCase("all")) {
rootSkill = null;
skill = null;
}
else {
rootSkill = mcMMO.p.getSkillRegister().getSkill(args[1]);
skill = PrimarySkillType.getSkill(args[1]);
}
String playerName = CommandUtils.getMatchedPlayerName(args[0]);
OnlineMMOPlayer mmoPlayer = mcMMO.getUserManager().queryPlayer(playerName);
McMMOPlayer mcMMOPlayer = UserManager.getOfflinePlayer(playerName);
// If the mmoPlayer doesn't exist, create a temporary profile and check if it's present in the database. If it's not, abort the process.
if (mmoPlayer == null) {
// If the mcMMOPlayer doesn't exist, create a temporary profile and check if it's present in the database. If it's not, abort the process.
if (mcMMOPlayer == null) {
UUID uuid = null;
OfflinePlayer player = mcMMO.p.getServer().getOfflinePlayer(playerName);
if (player != null) {
uuid = player.getUniqueId();
}
PlayerProfile profile = mcMMO.getDatabaseManager().queryPlayerDataByUUID(playerName, uuid, false);
uuid = player.getUniqueId();
if (CommandUtils.hasNoProfile(sender, profile)) {
return true;
PlayerProfile profile = mcMMO.getDatabaseManager().loadPlayerProfile(uuid, playerName);
//Check loading by UUID
if (CommandUtils.unloadedProfile(sender, profile)) {
//Didn't find it by UUID so try to find it by name
profile = mcMMO.getDatabaseManager().loadPlayerProfile(playerName);
//Check if it was present in DB
if(CommandUtils.unloadedProfile(sender, profile)) {
return true;
}
}
editValues(null, profile, skill);
}
else {
editValues(Misc.adaptPlayer(mmoPlayer), mmoPlayer, skill);
editValues(mcMMOPlayer.getPlayer(), mcMMOPlayer.getProfile(), skill);
}
handleSenderMessage(sender, playerName, skill);
@ -115,18 +122,18 @@ public class SkillresetCommand implements TabExecutor {
}
}
protected void handleCommand(Player player, PlayerProfile profile, RootSkill rootSkill) {
int levelsRemoved = profile.getSkillLevel(rootSkill);
float xpRemoved = profile.getSkillXpLevelRaw(rootSkill);
protected void handleCommand(Player player, PlayerProfile profile, PrimarySkillType skill) {
int levelsRemoved = profile.getSkillLevel(skill);
float xpRemoved = profile.getSkillXpLevelRaw(skill);
profile.modifySkill(rootSkill, 0);
profile.modifySkill(skill, 0);
if (player == null) {
profile.scheduleAsyncSave();
return;
}
EventUtils.tryLevelChangeEvent(player, rootSkill, levelsRemoved, xpRemoved, false, XPGainReason.COMMAND);
EventUtils.tryLevelChangeEvent(player, skill, levelsRemoved, xpRemoved, false, XPGainReason.COMMAND);
}
protected boolean permissionsCheckSelf(CommandSender sender) {
@ -141,26 +148,26 @@ public class SkillresetCommand implements TabExecutor {
player.sendMessage(LocaleLoader.getString("Commands.Reset.All"));
}
protected void handlePlayerMessageSkill(Player player, RootSkill rootSkill) {
player.sendMessage(LocaleLoader.getString("Commands.Reset.Single", rootSkill.getName()));
protected void handlePlayerMessageSkill(Player player, PrimarySkillType skill) {
player.sendMessage(LocaleLoader.getString("Commands.Reset.Single", skill.getName()));
}
private boolean validateArguments(CommandSender sender, String skillName) {
return skillName.equalsIgnoreCase("all") || !CommandUtils.isInvalidSkill(sender, skillName);
}
protected static void handleSenderMessage(CommandSender sender, String playerName, RootSkill rootSkill) {
if (rootSkill == null) {
protected static void handleSenderMessage(CommandSender sender, String playerName, PrimarySkillType skill) {
if (skill == null) {
sender.sendMessage(LocaleLoader.getString("Commands.addlevels.AwardAll.2", playerName));
}
else {
sender.sendMessage(LocaleLoader.getString("Commands.addlevels.AwardSkill.2", rootSkill.getName(), playerName));
sender.sendMessage(LocaleLoader.getString("Commands.addlevels.AwardSkill.2", skill.getName(), playerName));
}
}
protected void editValues(Player player, PlayerProfile profile, RootSkill rootSkill) {
if (rootSkill == null) {
for (RootSkill rootSkill : PrimarySkillType.NON_CHILD_SKILLS) {
protected void editValues(Player player, PlayerProfile profile, PrimarySkillType skill) {
if (skill == null) {
for (PrimarySkillType primarySkillType : PrimarySkillType.NON_CHILD_SKILLS) {
handleCommand(player, profile, primarySkillType);
}

View File

@ -1,15 +1,16 @@
package com.gmail.nossr50.commands.player;
import com.gmail.nossr50.config.Config;
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.player.PlayerProfile;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.Permissions;
import com.gmail.nossr50.util.commands.CommandUtils;
import com.gmail.nossr50.util.player.UserManager;
import com.gmail.nossr50.util.scoreboards.ScoreboardManager;
import com.google.common.collect.ImmutableList;
import com.neetgames.mcmmo.skill.RootSkill;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
@ -25,19 +26,15 @@ public class InspectCommand implements TabExecutor {
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (args.length == 1) {
String playerName = CommandUtils.getMatchedPlayerName(args[0]);
McMMOPlayer mcMMOPlayer = UserManager.getOfflinePlayer(playerName);
PlayerProfile playerProfile = mcMMO.getUserManager().queryPlayer(playerName);
Player targetPlayer = Bukkit.getPlayer(playerName);
// If the mcMMOPlayer doesn't exist, create a temporary profile and check if it's present in the database. If it's not, abort the process.
if (mcMMOPlayer == null) {
PlayerProfile profile = mcMMO.getDatabaseManager().loadPlayerProfile(playerName); // Temporary Profile
if(playerProfile == null) {
//TODO: Localize
sender.sendMessage("Data was not found in the database for the given player name!");
return true;
}
if(targetPlayer == null) {
//Target is offline
if (!CommandUtils.isLoaded(sender, profile)) {
return true;
}
if (Config.getInstance().getScoreboardsEnabled()
&& sender instanceof Player
@ -52,42 +49,48 @@ public class InspectCommand implements TabExecutor {
sender.sendMessage(LocaleLoader.getString("Inspect.OfflineStats", playerName));
sender.sendMessage(LocaleLoader.getString("Stats.Header.Gathering"));
for (RootSkill rootSkill : PrimarySkillType.GATHERING_SKILLS) {
for (PrimarySkillType skill : PrimarySkillType.GATHERING_SKILLS) {
sender.sendMessage(CommandUtils.displaySkill(profile, skill));
}
sender.sendMessage(LocaleLoader.getString("Stats.Header.Combat"));
for (RootSkill rootSkill : PrimarySkillType.COMBAT_SKILLS) {
for (PrimarySkillType skill : PrimarySkillType.COMBAT_SKILLS) {
sender.sendMessage(CommandUtils.displaySkill(profile, skill));
}
sender.sendMessage(LocaleLoader.getString("Stats.Header.Misc"));
for (RootSkill rootSkill : PrimarySkillType.MISC_SKILLS) {
for (PrimarySkillType skill : PrimarySkillType.MISC_SKILLS) {
sender.sendMessage(CommandUtils.displaySkill(profile, skill));
}
} else {
if (CommandUtils.hidden(sender, targetPlayer, Permissions.inspectHidden(sender))) {
sender.sendMessage(LocaleLoader.getString("Inspect.Offline"));
return true;
} else if (CommandUtils.tooFar(sender, targetPlayer, Permissions.inspectFar(sender))) {
} else {
Player target = mcMMOPlayer.getPlayer();
boolean isVanished = false;
if (CommandUtils.hidden(sender, target, Permissions.inspectHidden(sender))) {
isVanished = true;
}
//Only distance check players who are online and not vanished
if (!isVanished && CommandUtils.tooFar(sender, target, Permissions.inspectFar(sender))) {
return true;
}
if (Config.getInstance().getScoreboardsEnabled()
&& sender instanceof Player && Config.getInstance().getInspectUseBoard()) {
ScoreboardManager.enablePlayerInspectScoreboard((Player) sender, playerProfile);
&& sender instanceof Player
&& Config.getInstance().getInspectUseBoard()) {
ScoreboardManager.enablePlayerInspectScoreboard((Player) sender, mcMMOPlayer);
if (!Config.getInstance().getInspectUseChat()) {
return true;
}
}
sender.sendMessage(LocaleLoader.getString("Inspect.Stats", targetPlayer.getName()));
CommandUtils.printGatheringSkills(targetPlayer, sender);
CommandUtils.printCombatSkills(targetPlayer, sender);
CommandUtils.printMiscSkills(targetPlayer, sender);
sender.sendMessage(LocaleLoader.getString("Commands.PowerLevel", playerProfile.getExperienceHandler().getPowerLevel()));
sender.sendMessage(LocaleLoader.getString("Inspect.Stats", target.getName()));
CommandUtils.printGatheringSkills(target, sender);
CommandUtils.printCombatSkills(target, sender);
CommandUtils.printMiscSkills(target, sender);
sender.sendMessage(LocaleLoader.getString("Commands.PowerLevel", mcMMOPlayer.getPowerLevel()));
}
return true;

View File

@ -1,16 +1,11 @@
package com.gmail.nossr50.database;
import com.gmail.nossr50.api.exceptions.InvalidSkillException;
import com.gmail.nossr50.config.Config;
import com.gmail.nossr50.datatypes.database.DatabaseType;
import com.gmail.nossr50.datatypes.database.PlayerStat;
import com.gmail.nossr50.datatypes.player.MMODataSnapshot;
import com.gmail.nossr50.datatypes.player.PlayerProfile;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.neetgames.mcmmo.exceptions.InvalidSkillException;
import com.neetgames.mcmmo.exceptions.ProfileRetrievalException;
import com.neetgames.mcmmo.player.MMOPlayerData;
import com.neetgames.mcmmo.skill.RootSkill;
import org.apache.commons.lang.NullArgumentException;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -39,35 +34,36 @@ public interface DatabaseManager {
* Remove a user from the database.
*
* @param playerName The name of the user to remove
* @param uuid uuid of player to remove, can be null
* @param uuid player UUID, can be null
* @return true if the user was successfully removed, false otherwise
*/
boolean removeUser(@NotNull String playerName, @Nullable UUID uuid);
boolean removeUser(String playerName, UUID uuid);
/**
* Removes any cache used for faster lookups
* Currently only used for SQL
* @param uuid target UUID to cleanup
*/
void removeCache(@NotNull UUID uuid);
void cleanupUser(UUID uuid);
/**
* Save a user to the database.
*
* @param mmoDataSnapshot Snapshot of player data to save
* @param profile The profile of the player to save
* @return true if successful, false on failure
*/
boolean saveUser(@NotNull MMODataSnapshot mmoDataSnapshot);
boolean saveUser(PlayerProfile profile);
/**
* Retrieve leaderboard info.
* Will never be null but it may be empty
*
* @param rootSkill The skill to retrieve info on
* @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
*/
@NotNull List<PlayerStat> readLeaderboard(@NotNull RootSkill rootSkill, int pageNumber, int statsPerPage) throws InvalidSkillException;
@NotNull List<PlayerStat> readLeaderboard(@Nullable PrimarySkillType skill, int pageNumber, int statsPerPage) throws InvalidSkillException;
/**
* Retrieve rank info into a HashMap from PrimarySkillType to the rank.
@ -78,74 +74,60 @@ public interface DatabaseManager {
* @param playerName The name of the user to retrieve the rankings for
* @return the requested rank information
*/
@NotNull Map<RootSkill, Integer> readRank(@NotNull String playerName);
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 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
*/
void insertNewUser(@NotNull String playerName, @NotNull UUID uuid) throws Exception;
void newUser(String playerName, UUID uuid);
@Nullable MMOPlayerData queryPlayerDataByPlayer(@NotNull Player player) throws ProfileRetrievalException, NullArgumentException;
@NotNull PlayerProfile newUser(@NotNull Player player);
/**
* Load player data (in the form of {@link PlayerProfile}) if player data exists
* Returns null if it doesn't
* Load a player from the database.
*
* @param playerName The name of the player to load from the database
* @return The player's data, or an unloaded PlayerProfile if not found
* and createNew is false
*/
@NotNull PlayerProfile loadPlayerProfile(@NotNull String playerName);
/**
* Load a player from the database.
*
* @param uuid The uuid of the player to load from the database
* @param playerName the current player name for this player
* @return The player's data, or null if not found
* @return The player's data, or an unloaded PlayerProfile if not found
*/
@Nullable MMOPlayerData queryPlayerDataByUUID(@NotNull UUID uuid, @NotNull String playerName) throws ProfileRetrievalException, NullArgumentException;
/**
* Load player data (in the form of {@link PlayerProfile}) if player data exists
* Returns null if it doesn't
*
* @param playerName the current player name for this player
* @return The player's data, or null if not found
*/
@Nullable MMOPlayerData queryPlayerByName(@NotNull String playerName) throws ProfileRetrievalException;
/**
* This method queries the DB for player data for target player
* If it fails to find data for this player, or if it does find data but the data is corrupted,
* it will then proceed to make brand new data for the target player, which will be saved to the DB during the next save
*
* This method will return null for all other errors, which indicates a problem with the DB, in which case mcMMO
* will try to load the player data periodically, but that isn't handled in this method
*
* @param player target player
* @return {@link PlayerProfile} for the target player
*/
@Nullable MMOPlayerData initPlayerProfile(@NotNull Player player) throws Exception;
@NotNull PlayerProfile loadPlayerProfile(@NotNull UUID uuid, @Nullable String playerName);
/**
* Get all users currently stored in the database.
*
* @return list of playernames
*/
@NotNull List<String> getStoredUsers();
List<String> getStoredUsers();
/**
* Convert all users from this database to the provided database using
* {@link #saveUser(MMODataSnapshot)}.
* {@link #saveUser(PlayerProfile)}.
*
* @param destination The DatabaseManager to save to
*/
void convertUsers(@NotNull DatabaseManager destination);
void convertUsers(DatabaseManager destination);
// boolean saveUserUUID(String userName, UUID uuid);
boolean saveUserUUID(String userName, UUID uuid);
// boolean saveUserUUIDs(Map<String, UUID> fetchedUUIDs);
boolean saveUserUUIDs(Map<String, UUID> fetchedUUIDs);
/**
* Retrieve the type of database in use. Custom databases should return CUSTOM.
*
* @return The type of database
*/
@NotNull DatabaseType getDatabaseType();
DatabaseType getDatabaseType();
/**
* Called when the plugin disables

View File

@ -33,6 +33,9 @@ import java.util.concurrent.locks.ReentrantLock;
public final class SQLDatabaseManager extends AbstractDatabaseManager {
private static final String ALL_QUERY_VERSION = "total";
public static final String MOBHEALTHBAR_VARCHAR = "VARCHAR(50)";
public static final String UUID_VARCHAR = "VARCHAR(36)";
public static final String USER_VARCHAR = "VARCHAR(40)";
private final String tablePrefix = Config.getInstance().getMySQLTablePrefix();
private final Map<UUID, Integer> cachedUserIDs = new HashMap<>();
@ -45,6 +48,8 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
private final ReentrantLock massUpdateLock = new ReentrantLock();
private final String CHARSET_SQL = "utf8mb4"; //This is compliant with UTF-8 while "utf8" is not, confusing but this is how it is.
protected SQLDatabaseManager() {
String connectionString = "jdbc:mysql://" + Config.getInstance().getMySQLServerName()
+ ":" + Config.getInstance().getMySQLServerPort() + "/" + Config.getInstance().getMySQLDatabaseName();
@ -565,6 +570,24 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
}
}
@Override
public @NotNull PlayerProfile newUser(@NotNull Player player) {
try {
Connection connection = getConnection(PoolIdentifier.SAVE);
int id = newUser(connection, player.getName(), player.getUniqueId());
if (id == -1) {
return new PlayerProfile(player.getName(), player.getUniqueId(), false);
} else {
return loadPlayerProfile(player.getUniqueId(), player.getName());
}
} catch (SQLException e) {
e.printStackTrace();
}
return new PlayerProfile(player.getName(), player.getUniqueId(), false);
}
private int newUser(Connection connection, String playerName, UUID uuid) {
ResultSet resultSet = null;
PreparedStatement statement = null;
@ -603,11 +626,26 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
return -1;
}
public @NotNull PlayerProfile loadPlayerProfile(@NotNull String playerName) {
try {
return loadPlayerFromDB(null, playerName);
} catch (RuntimeException e) {
e.printStackTrace();
return new PlayerProfile(playerName, false);
}
}
public @NotNull PlayerProfile loadPlayerProfile(@NotNull UUID uuid, @Nullable String playerName) {
return loadPlayerFromDB(uuid, playerName);
@Override
public @Nullable MMOPlayerData queryPlayerDataByPlayer(@NotNull Player player) throws ProfileRetrievalException, NullArgumentException {
return loadPlayerProfile(player, player.getName(), player.getUniqueId());
}
private PlayerProfile loadPlayerFromDB(@Nullable UUID uuid, @Nullable String playerName) throws RuntimeException {
if(uuid == null && playerName == null) {
throw new RuntimeException("Error looking up player, both UUID and playerName are null and one must not be.");
}
@Override
public @Nullable MMOPlayerData queryPlayerDataByUUID(@NotNull UUID uuid, @NotNull String playerName) throws ProfileRetrievalException, NullArgumentException {
return loadPlayerProfile(null, playerName, uuid);
@ -623,15 +661,8 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
int id = getUserID(connection, playerName, playerUUID);
if (id == -1) {
// There is no such user
if (player != null) {
id = newUser(connection, playerName, playerUUID);
if (id == -1) {
return null;
}
} else {
return null;
}
// There is no such user
return new PlayerProfile(playerName, false);
}
// There is such a user
writeMissingRows(connection, id);
@ -648,7 +679,10 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
resultSet.close();
statement.close();
if (!playerName.isEmpty() && !playerName.equalsIgnoreCase(name) && playerUUID != null) {
if (playerName != null
&& !playerName.isEmpty()
&& !playerName.equalsIgnoreCase(name)
&& playerUUID != null) {
statement = connection.prepareStatement(
"UPDATE `" + tablePrefix + "users` "
+ "SET user = ? "
@ -685,10 +719,11 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
tryClose(connection);
}
return null;
//Return empty profile
return new PlayerProfile(playerName, false);
}
public void convertUsers(@NotNull DatabaseManager destination) {
public void convertUsers(DatabaseManager destination) {
PreparedStatement statement = null;
Connection connection = null;
ResultSet resultSet = null;
@ -867,7 +902,7 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
+ "`lastlogin` int(32) unsigned NOT NULL,"
+ "PRIMARY KEY (`id`),"
+ "INDEX(`user`(20) ASC),"
+ "UNIQUE KEY `uuid` (`uuid`)) DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;");
+ "UNIQUE KEY `uuid` (`uuid`)) DEFAULT CHARSET=" + CHARSET_SQL + " AUTO_INCREMENT=1;");
tryClose(createStatement);
}
tryClose(resultSet);
@ -881,7 +916,7 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
+ "`mobhealthbar` varchar(50) NOT NULL DEFAULT '" + Config.getInstance().getMobHealthbarDefault() + "',"
+ "`scoreboardtips` int(10) NOT NULL DEFAULT '0',"
+ "PRIMARY KEY (`user_id`)) "
+ "DEFAULT CHARSET=latin1;");
+ "DEFAULT CHARSET=" + CHARSET_SQL + ";");
tryClose(createStatement);
}
tryClose(resultSet);
@ -908,7 +943,7 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
+ "`tridents` int(32) unsigned NOT NULL DEFAULT '0',"
+ "`crossbows` int(32) unsigned NOT NULL DEFAULT '0',"
+ "PRIMARY KEY (`user_id`)) "
+ "DEFAULT CHARSET=latin1;");
+ "DEFAULT CHARSET=" + CHARSET_SQL + ";");
tryClose(createStatement);
}
tryClose(resultSet);
@ -936,7 +971,7 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
+ "`crossbows` int(10) unsigned NOT NULL DEFAULT '0',"
+ "`total` int(10) unsigned NOT NULL DEFAULT '0',"
+ "PRIMARY KEY (`user_id`)) "
+ "DEFAULT CHARSET=latin1;");
+ "DEFAULT CHARSET=" + CHARSET_SQL + ";");
tryClose(createStatement);
}
tryClose(resultSet);
@ -1010,7 +1045,7 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
+ "`view_tridents` varchar(40) NOT NULL DEFAULT 'NORMAL',"
+ "`view_crossbows` varchar(40) NOT NULL DEFAULT 'NORMAL',"
+ "PRIMARY KEY (`user_id`)) "
+ "DEFAULT CHARSET=latin1;");
+ "DEFAULT CHARSET=" + CHARSET_SQL + ";");
tryClose(createStatement);
}
tryClose(resultSet);
@ -1135,6 +1170,11 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
case ADD_UNIQUE_PLAYER_DATA:
checkUpgradeAddUniqueChimaeraWing(statement);
break;
case SQL_CHARSET_UTF8MB4:
updateCharacterSet(statement);
break;
case ADD_SQL_2_2:
checkUpgradeAddTridentsAndCrossbowsSQL(statement);
break;
@ -1142,8 +1182,6 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
break;
}
mcMMO.getUpgradeManager().setUpgradeCompleted(upgrade);
}
catch (SQLException ex) {
printErrors(ex);
@ -1358,6 +1396,7 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
statement.execute("ALTER TABLE `" + tablePrefix + "users` "
+ "DROP INDEX `user`,"
+ "ADD INDEX `user` (`user`(20) ASC)");
mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.DROP_NAME_UNIQUENESS);
} catch (SQLException ex) {
ex.printStackTrace();
} finally {
@ -1385,6 +1424,7 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
private void checkUpgradeAddAlchemy(final Statement statement) throws SQLException {
try {
statement.executeQuery("SELECT `alchemy` FROM `" + tablePrefix + "skills` LIMIT 1");
mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.ADD_ALCHEMY);
}
catch (SQLException ex) {
mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for Alchemy...");
@ -1396,6 +1436,7 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
private void checkUpgradeAddBlastMiningCooldown(final Statement statement) throws SQLException {
try {
statement.executeQuery("SELECT `blast_mining` FROM `" + tablePrefix + "cooldowns` LIMIT 1");
mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.ADD_BLAST_MINING_COOLDOWN);
}
catch (SQLException ex) {
mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for Blast Mining...");
@ -1406,6 +1447,7 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
private void checkUpgradeAddUniqueChimaeraWing(final Statement statement) throws SQLException {
try {
statement.executeQuery("SELECT `chimaera_wing` FROM `" + tablePrefix + "cooldowns` LIMIT 1");
mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.ADD_UNIQUE_PLAYER_DATA);
}
catch (SQLException ex) {
mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for Chimaera Wing...");
@ -1416,6 +1458,7 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
private void checkUpgradeAddFishing(final Statement statement) throws SQLException {
try {
statement.executeQuery("SELECT `fishing` FROM `" + tablePrefix + "skills` LIMIT 1");
mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.ADD_FISHING);
}
catch (SQLException ex) {
mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for Fishing...");
@ -1427,6 +1470,7 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
private void checkUpgradeAddMobHealthbars(final Statement statement) throws SQLException {
try {
statement.executeQuery("SELECT `mobhealthbar` FROM `" + tablePrefix + "huds` LIMIT 1");
mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.ADD_MOB_HEALTHBARS);
}
catch (SQLException ex) {
mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for mob healthbars...");
@ -1437,6 +1481,7 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
private void checkUpgradeAddScoreboardTips(final Statement statement) throws SQLException {
try {
statement.executeQuery("SELECT `scoreboardtips` FROM `" + tablePrefix + "huds` LIMIT 1");
mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.ADD_SCOREBOARD_TIPS);
}
catch (SQLException ex) {
mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for scoreboard tips...");
@ -1465,6 +1510,8 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
}
}
}
mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.ADD_SQL_INDEXES);
}
catch (SQLException ex) {
printErrors(ex);
@ -1494,7 +1541,11 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
mcMMO.p.getLogger().info("Adding UUIDs to mcMMO MySQL user table...");
statement.executeUpdate("ALTER TABLE `" + tablePrefix + "users` ADD `uuid` varchar(36) NULL DEFAULT NULL");
statement.executeUpdate("ALTER TABLE `" + tablePrefix + "users` ADD UNIQUE INDEX `uuid` (`uuid`) USING BTREE");
new GetUUIDUpdatesRequired().runTaskLaterAsynchronously(mcMMO.p, 100); // wait until after first purge
}
mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.ADD_UUIDS);
}
catch (SQLException ex) {
printErrors(ex);
@ -1502,8 +1553,6 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
finally {
tryClose(resultSet);
}
new GetUUIDUpdatesRequired().runTaskLaterAsynchronously(mcMMO.p, 100); // wait until after first purge
}
private class GetUUIDUpdatesRequired extends BukkitRunnable {
@ -1561,6 +1610,8 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
mcMMO.p.getLogger().info("Removing party name from users table...");
statement.executeUpdate("ALTER TABLE `" + tablePrefix + "users` DROP COLUMN `party`");
}
mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.DROP_SQL_PARTY_NAMES);
}
catch (SQLException ex) {
printErrors(ex);
@ -1596,6 +1647,8 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
statement.executeUpdate("ALTER TABLE `" + tablePrefix + "skills` ADD INDEX `idx_total` (`total`) USING BTREE");
connection.commit();
}
mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.ADD_SKILL_TOTAL);
}
catch (SQLException ex) {
printErrors(ex);
@ -1627,6 +1680,8 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
mcMMO.p.getLogger().info("Removing Spout HUD type from huds table...");
statement.executeUpdate("ALTER TABLE `" + tablePrefix + "huds` DROP COLUMN `hudtype`");
}
mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.DROP_SPOUT);
}
catch (SQLException ex) {
printErrors(ex);
@ -1740,6 +1795,69 @@ public final class SQLDatabaseManager extends AbstractDatabaseManager {
}
}
private void updateCharacterSet(@NotNull Statement statement) {
//TODO: Could check the tables for being latin1 before executing queries but it seems moot because it is likely the same computational effort
/*
The following columns were set to use latin1 historically (now utf8mb4)
column user in <tablePrefix>users
column uuid in <tablePrefix>users
column mobhealthbar in <tablePrefix>huds
*/
//Alter users table
mcMMO.p.getLogger().info("SQL Converting tables from latin1 to utf8mb4");
//Update "user" column
try {
mcMMO.p.getLogger().info("Updating user column to new encoding");
statement.executeUpdate(getUpdateUserInUsersTableSQLQuery());
//Update "uuid" column
mcMMO.p.getLogger().info("Updating user column to new encoding");
statement.executeUpdate(getUpdateUUIDInUsersTableSQLQuery());
//Update "mobhealthbar" column
mcMMO.p.getLogger().info("Updating mobhealthbar column to new encoding");
statement.executeUpdate(getUpdateMobHealthBarInHudsTableSQLQuery());
mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.SQL_CHARSET_UTF8MB4);
} catch (SQLException e) {
e.printStackTrace();
}
}
@NotNull
private String getUpdateUserInUsersTableSQLQuery() {
return "ALTER TABLE\n" +
" " + tablePrefix + "users\n" +
" CHANGE user user\n" +
" " + USER_VARCHAR + "\n" +
" CHARACTER SET utf8mb4\n" +
" COLLATE utf8mb4_unicode_ci;";
}
@NotNull
private String getUpdateUUIDInUsersTableSQLQuery() {
return "ALTER TABLE\n" +
" " + tablePrefix + "users\n" +
" CHANGE uuid uuid\n" +
" " + UUID_VARCHAR + "\n" +
" CHARACTER SET utf8mb4\n" +
" COLLATE utf8mb4_unicode_ci;";
}
@NotNull
private String getUpdateMobHealthBarInHudsTableSQLQuery() {
return "ALTER TABLE\n" +
" " + tablePrefix + "huds\n" +
" CHANGE mobhealthbar mobhealthbar\n" +
" " + MOBHEALTHBAR_VARCHAR + "\n" +
" CHARACTER SET utf8mb4\n" +
" COLLATE utf8mb4_unicode_ci;";
}
@Override
public void removeCache(@NotNull UUID uuid) {
cachedUserIDs.remove(uuid);

View File

@ -17,5 +17,6 @@ public enum UpgradeType {
ADD_SQL_2_2,
FIX_SPELLING_NETHERITE_SALVAGE,
FIX_SPELLING_NETHERITE_REPAIR,
FIX_NETHERITE_SALVAGE_QUANTITIES
FIX_NETHERITE_SALVAGE_QUANTITIES,
SQL_CHARSET_UTF8MB4
}

View File

@ -0,0 +1,41 @@
package com.gmail.nossr50.events;
import org.bukkit.entity.Item;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
public class McMMOReplaceVanillaTreasureEvent extends Event {
private @NotNull ItemStack replacementItemStack;
private final @NotNull Item originalItem;
public McMMOReplaceVanillaTreasureEvent(@NotNull Item originalItem, @NotNull ItemStack replacementItemStack) {
this.originalItem = originalItem;
this.replacementItemStack = replacementItemStack;
}
/** Rest of file is required boilerplate for custom events **/
private static final @NotNull HandlerList handlers = new HandlerList();
@Override
public @NotNull HandlerList getHandlers() {
return handlers;
}
public static @NotNull HandlerList getHandlerList() {
return handlers;
}
public @NotNull ItemStack getReplacementItemStack() {
return replacementItemStack;
}
public void setReplacementItemStack(@NotNull ItemStack replacementItemStack) {
this.replacementItemStack = replacementItemStack;
}
public @NotNull Item getOriginalItem() {
return originalItem;
}
}

View File

@ -616,6 +616,7 @@ public class EntityListener implements Listener {
switch (cause) {
case CONTACT:
case FIRE:
case HOT_FLOOR:
case LAVA:
if (tamingManager.canUseEnvironmentallyAware()) {
tamingManager.processEnvironmentallyAware(wolf, event.getDamage());

View File

@ -8,6 +8,9 @@ import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.neetgames.mcmmo.player.OnlineMMOPlayer;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.datatypes.skills.SubSkillType;
import com.gmail.nossr50.datatypes.skills.subskills.taming.CallOfTheWildType;
import com.gmail.nossr50.events.McMMOReplaceVanillaTreasureEvent;
import com.gmail.nossr50.events.fake.FakePlayerAnimationEvent;
import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.party.ShareHandler;
@ -258,7 +261,7 @@ public class PlayerListener implements Listener {
*
* @param event The event to modify
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onPlayerFishHighest(PlayerFishEvent event) {
/* WORLD BLACKLIST CHECK */
if(WorldBlacklist.isWorldBlacklisted(event.getPlayer().getWorld()))
@ -292,12 +295,20 @@ public class PlayerListener implements Listener {
if(event.getCaught() != null) {
Item fishingCatch = (Item) event.getCaught();
if (Config.getInstance(). getFishingOverrideTreasures() &&
if (Config.getInstance().getFishingOverrideTreasures() &&
fishingCatch.getItemStack().getType() != Material.SALMON &&
fishingCatch.getItemStack().getType() != Material.COD &&
fishingCatch.getItemStack().getType() != Material.TROPICAL_FISH &&
fishingCatch.getItemStack().getType() != Material.PUFFERFISH) {
fishingCatch.setItemStack(new ItemStack(Material.SALMON, 1));
ItemStack replacementCatch = new ItemStack(Material.SALMON, 1);
McMMOReplaceVanillaTreasureEvent replaceVanillaTreasureEvent = new McMMOReplaceVanillaTreasureEvent(fishingCatch, replacementCatch);
Bukkit.getPluginManager().callEvent(replaceVanillaTreasureEvent);
//Replace
replacementCatch = replaceVanillaTreasureEvent.getReplacementItemStack();
fishingCatch.setItemStack(replacementCatch);
}
if (Permissions.vanillaXpBoost(player, PrimarySkillType.FISHING)) {

View File

@ -615,7 +615,11 @@ public class mcMMO extends JavaPlugin {
private void scheduleTasks() {
// Periodic save timer (Saves every 10 minutes by default)
long saveIntervalTicks = Config.getInstance().getSaveInterval() * 1200;
long second = 20;
long minute = second * 60;
long saveIntervalTicks = Math.max(minute, Config.getInstance().getSaveInterval() * minute);
new SaveTimerTask().runTaskTimer(this, saveIntervalTicks, saveIntervalTicks);
// Cleanup the backups folder

View File

@ -31,7 +31,7 @@ public class FormulaConversionTask extends BukkitRunnable {
// If the mmoPlayer doesn't exist, create a temporary profile and check if it's present in the database. If it's not, abort the process.
if (mmoPlayer == null) {
profile = mcMMO.getDatabaseManager().queryPlayerDataByUUID(playerName, false);
profile = mcMMO.getDatabaseManager().queryPlayerDataByUUID(playerName);
if (!profile.isLoaded()) {
mcMMO.p.debug("Profile not loaded.");

View File

@ -99,7 +99,7 @@ public class UUIDUpdateAsyncTask implements Runnable {
position += batch.size();
plugin.getLogger().info(String.format("Conversion progress: %d/%d users", position, userNames.size()));
if (position == userNames.size()) {
if (position +1 >= userNames.size()) {
mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.ADD_UUIDS);
awaiter.countDown();
plugin.getLogger().info("UUID checks completed");

View File

@ -2,12 +2,13 @@ package com.gmail.nossr50.runnables.player;
import com.gmail.nossr50.config.Config;
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.neetgames.mcmmo.player.MMOPlayerData;
import com.gmail.nossr50.datatypes.player.PlayerProfile;
import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.runnables.commands.McScoreboardKeepTask;
import com.gmail.nossr50.util.EventUtils;
import com.gmail.nossr50.util.Misc;
import com.gmail.nossr50.util.player.UserManager;
import com.gmail.nossr50.util.scoreboards.ScoreboardManager;
import org.bukkit.Server;
import org.bukkit.entity.Player;
@ -41,40 +42,46 @@ public class PlayerProfileLoadingTask extends BukkitRunnable {
return;
}
try {
MMOPlayerData mmoPlayerData = mcMMO.getDatabaseManager().queryPlayerDataByPlayer(player);
McMMOPlayer mmoPlayer = new McMMOPlayer(player, player.getUniqueId(), player.getName());
new ApplySuccessfulProfile(new McMMOPlayer(player, )).runTask(mcMMO.p);
PlayerProfile profile = mcMMO.getDatabaseManager().loadPlayerProfile(player.getUniqueId(), player.getName());
if(!profile.isLoaded()) {
mcMMO.p.getLogger().info("Creating new data for player: "+player.getName());
//Profile isn't loaded so add as new user
profile = mcMMO.getDatabaseManager().newUser(player);
}
// If successful, schedule the apply
if (profile.isLoaded()) {
new ApplySuccessfulProfile(new McMMOPlayer(player, profile)).runTask(mcMMO.p);
EventUtils.callPlayerProfileLoadEvent(player, profile);
return;
} catch () {
// Print errors to console/logs if we're failing at least 2 times in a row to load the profile
if (attempt >= 3)
{
//Log the error
mcMMO.p.getLogger().severe(LocaleLoader.getString("Profile.Loading.FailureNotice",
player.getName(), String.valueOf(attempt)));
//Notify the admins
mcMMO.p.getServer().broadcast(LocaleLoader.getString("Profile.Loading.FailureNotice", player.getName()), Server.BROADCAST_CHANNEL_ADMINISTRATIVE);
//Notify the player
player.sendMessage(LocaleLoader.getString("Profile.Loading.FailurePlayer", String.valueOf(attempt)).split("\n"));
}
// Increment attempt counter and try
attempt++;
new PlayerProfileLoadingTask(player, attempt).runTaskLaterAsynchronously(mcMMO.p, (100 + (attempt * 100)));
}
// Print errors to console/logs if we're failing at least 2 times in a row to load the profile
if (attempt >= 3)
{
//Log the error
mcMMO.p.getLogger().severe(LocaleLoader.getString("Profile.Loading.FailureNotice",
player.getName(), String.valueOf(attempt)));
//Notify the admins
mcMMO.p.getServer().broadcast(LocaleLoader.getString("Profile.Loading.FailureNotice", player.getName()), Server.BROADCAST_CHANNEL_ADMINISTRATIVE);
//Notify the player
player.sendMessage(LocaleLoader.getString("Profile.Loading.FailurePlayer", String.valueOf(attempt)).split("\n"));
}
// Increment attempt counter and try
attempt++;
new PlayerProfileLoadingTask(player, attempt).runTaskLaterAsynchronously(mcMMO.p, (100 + (attempt * 100)));
}
private class ApplySuccessfulProfile extends BukkitRunnable {
private final McMMOPlayer mmoPlayer;
private final McMMOPlayer mcMMOPlayer;
private ApplySuccessfulProfile(McMMOPlayer mmoPlayer) {
this.mmoPlayer = mmoPlayer;
private ApplySuccessfulProfile(McMMOPlayer mcMMOPlayer) {
this.mcMMOPlayer = mcMMOPlayer;
}
// Synchronized task
@ -86,8 +93,9 @@ public class PlayerProfileLoadingTask extends BukkitRunnable {
return;
}
mcMMO.getUserManager().track(mmoPlayer);
mmoPlayer.actualizeRespawnATS();
mcMMOPlayer.setupPartyData();
UserManager.track(mcMMOPlayer);
mcMMOPlayer.actualizeRespawnATS();
if (Config.getInstance().getScoreboardsEnabled()) {
ScoreboardManager.setupPlayer(player);

View File

@ -138,7 +138,7 @@ public class SmeltingManager extends SkillManager {
ItemStack furnaceResult = furnaceInventory.getResult();
if(furnaceResult == null)
return false;
return true; //This actually means there is nothing yet in the resulting item slot, which means it should always be okay to double smelt
int resultAmount = furnaceResult.getAmount(); //Amount before double smelt
int itemLimit = furnaceResult.getMaxStackSize();

View File

@ -344,6 +344,22 @@ public class ScoreboardManager {
}
}
public static void enablePlayerInspectScoreboard(@NotNull Player player, @NotNull McMMOPlayer targetMcMMOPlayer) {
ScoreboardWrapper wrapper = getWrapper(player);
if(wrapper == null) {
setupPlayer(player);
wrapper = getWrapper(player);
}
if(wrapper != null) {
wrapper.setOldScoreboard();
wrapper.setTypeInspectStats(targetMcMMOPlayer);
changeScoreboard(wrapper, Config.getInstance().getInspectScoreboardTime());
}
}
public static void enablePlayerCooldownScoreboard(Player player) {
ScoreboardWrapper wrapper = getWrapper(player);

View File

@ -24,6 +24,7 @@ import org.bukkit.scoreboard.DisplaySlot;
import org.bukkit.scoreboard.Objective;
import org.bukkit.scoreboard.Score;
import org.bukkit.scoreboard.Scoreboard;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map;
@ -321,6 +322,17 @@ public class ScoreboardWrapper {
loadObjective(LocaleLoader.getString("Scoreboard.Header.PlayerInspect", targetPlayer));
}
public void setTypeInspectStats(@NotNull McMMOPlayer mcMMOPlayer) {
this.sidebarType = SidebarType.STATS_BOARD;
targetPlayer = mcMMOPlayer.getPlayer().getName();
targetProfile = mcMMOPlayer.getProfile();
targetSkill = null;
leaderboardPage = -1;
loadObjective(LocaleLoader.getString("Scoreboard.Header.PlayerInspect", targetPlayer));
}
public void setTypeCooldowns() {
this.sidebarType = SidebarType.COOLDOWNS_BOARD;

View File

@ -16,9 +16,12 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
public class TextUtils {
private static @Nullable LegacyComponentSerializer customLegacySerializer;
private TextUtils() {
// We don't want any instances of this class.
}
/**
* Makes a single component from an array of components, can optionally add prefixes and suffixes to come before and after each component
* @param componentsArray target array

View File

@ -11,7 +11,7 @@ public class UpgradeManager extends ConfigLoader {
private final Set<UpgradeType> setNeededUpgrades;
public UpgradeManager() {
super("upgrades.yml");
super("upgrades_overhaul.yml"); //overhaul is added so we don't have any issues with classic
setNeededUpgrades = EnumSet.allOf(UpgradeType.class);

View File

@ -17,7 +17,7 @@ Acrobatics.SubSkill.GracefulRoll.Name = Anmutiges Abrollen
Acrobatics.SubSkill.Roll.Chance = Chance abzurollen: &e{0}
Acrobatics.SubSkill.Roll.Description = Lande gezielt, um deinen Fallschaden zu reduzieren.
Acrobatics.SubSkill.Roll.GraceChance = Chance anmutig abzurollen: &e{0}
Acrobatics.SubSkill.Roll.Mechanics = &7Abrollen ist eine aktive F\u00E4higkeit mit einem passiven Teil. Immer, wenn du Fallschaden nimmst, gibt es eine Chance, dass der Schaden reduziert wird, je nachdem wie hoch dein Akrobatik-Level ist. Auf Level 50 hast du eine &e{0}%&7 Chance, den Schaden zu reduzieren bzw. &e{1}%&7 wenn Anmutiges Abrollen aktiviert wird. Die Erfolgschance steigt linear bis Level &e{2}&7, auf welchem es seinen maximalen Wert erreicht. Jedes Akrobatik-Level gibt dir eine &e{3}%&7 Chance zum erfolgreichen Abrollen. H\u00E4ltst du im Fall die Duck-Taste (standardm\u00E4\u00DFig Shift), aktivierst du Anmutiges Abrollen, welches den Fallschaden um noch mehr Schaden reduzieren oder sogar komplett verhindern kann. Normales Abrollen wird maximal &c{4}&7 Schaden verhindern, Anmutiges Abrollen bis zu &a{5}&7.
Acrobatics.SubSkill.Roll.Mechanics = &7Abrollen ist eine aktive F\u00E4higkeit mit einem passiven Teil. Immer, wenn du Fallschaden nimmst, gibt es eine Chance, dass der Schaden reduziert wird, je nachdem wie hoch dein Akrobatik-Level ist. Auf Level 50 hast du eine &e{0}%&7 Chance, den Schaden zu reduzieren bzw. &e{1}%&7 wenn Anmutiges Abrollen aktiviert wird. Die Erfolgschance steigt linear bis Level &e{2}&7, auf welchem es seinen maximalen Wert erreicht. Jedes Akrobatik-Level gibt dir eine &e{3}%&7 Chance zum erfolgreichen Abrollen. H\u00E4ltst du im Fall die Duck-Taste (standardm\u00E4\u00DFig Shift), aktivierst du Anmutiges Abrollen, welches den Fallschaden auf noch weniger Schaden reduzieren oder sogar komplett verhindern kann. Normales Abrollen wird maximal &c{4}&7 Schaden verhindern, Anmutiges Abrollen bis zu &a{5}&7.
Acrobatics.SubSkill.Roll.Name = Abrollen
Acrobatics.SubSkill.Roll.Stat = Chance abzurollen
Acrobatics.SubSkill.Roll.Stat.Extra = Chance anmutig abzurollen
@ -62,7 +62,7 @@ Axes.Ability.Lower = &7&o**Du senkst deine Axt.**
Axes.Ability.Ready = &a&o**Du hebst deine Axt...**
Axes.Ability.Ready.Extra = &3Du &6hebst&3 deine Axt. &7({0} ist f\u00FCr {1}s pausiert)
Axes.Combat.CritStruck = &cDu wurdest &4schwer &cverwundet!
Axes.Combat.CriticalHit = &4&Kritischer Treffer!
Axes.Combat.CriticalHit = &4Kritischer Treffer!
Axes.Combat.GI.Proc = &a**Du landest einen &2gewaltigen &aSchlag**
Axes.Combat.GI.Struck = &a&o**Von einem Wuchtschlag getroffen**
Axes.Combat.SS.Struck = &a&o**Von einem Sch\u00E4delspalter getroffen**
@ -233,7 +233,7 @@ Commands.Party.PartyFull.InviteAccept = Du kannst der Party &a{0}&c nicht beitre
Commands.Party.Quit = &a- Verlasse deine aktuelle Party.
Commands.Party.Rename = &7Party Name wurde zu &f{0} &7ver\u00E4ndert
Commands.Party.SetSharing = &7Party {0} teilen: &3{1}
Commands.Party.ShareMode = &8Teilen Modus:
Commands.Party.ShareMode = &8Teilen-Modus:
Commands.Party.Status = &8Name: &f{0} {1} &8Level: &3{2}
Commands.Party.Status.Alliance = &8Verb\u00FCndeter: &f{0}
Commands.Party.Teleport = &a- Teleportiere dich zu Partymitgliedern.
@ -242,7 +242,7 @@ Commands.Party.ToggleShareCategory = &7Party Item teilen f\u00FCr&6{0} &7wurde &
Commands.Party.UnlockedFeatures = &8Freigeschaltete Features: &7&o{0}
Commands.Party1 = &a- Erstelle eine neue Party.
Commands.Party2 = &a- Tritt der Party eines Spielers bei.
Commands.PowerLevel = &4GESAMT LEVEL: &a{0}
Commands.PowerLevel = &4Gesamtlevel: &a{0}
Commands.PowerLevel.Capped = &4Gesamtlevel: &a{0} &4H\u00F6chstlevel: &e{1}
Commands.PowerLevel.Leaderboard = --mcMMO&9 Power-Level &eBestenliste--
Commands.Reset = &a- Setze ein Skilllevel auf 0
@ -262,14 +262,14 @@ Commands.Skill.ChildSkill = Unterskills sind f\u00FCr diesen Befehl nicht benutz
Commands.Skill.Invalid = Das ist kein g\u00FCltiger Skillname!
Commands.Skill.Leaderboard = --mcMMO &9{0}&e Bestenliste--
Commands.SkillInfo = &a- Detaillierte Informationen zu einem Skill.
Commands.Stats = &a- Zeige deine Skill Statistiken.
Commands.Stats = &a- Zeige deine Skill-Statistiken.
Commands.Stats.Self.Overhaul = Statistiken
Commands.ToggleAbility = &a- Schalte F\u00E4higkeiten-Aktivierung mit Rechtsklick an oder aus.
Commands.Usage.0 = &cDie korrekte Verwendung ist /{0}
Commands.Usage.1 = &cDie korrekte Verwendung ist /{0} {1}
Commands.Usage.2 = &cDie korrekte Verwendung ist /{0} {1} {2}
Commands.Usage.3 = &cDie korrekte Verwendung ist /{0} {1} {2} {3}
Commands.Usage.3.XP = &cDie korrekte Verwendung ist /{0} {1} {2} {3}&7 (Du kannst auch -s an das Ende des Befehls hinzuf\u00FC"gen, damit der Spieler nicht benachrichtigt wird.)
Commands.Usage.3.XP = &cDie korrekte Verwendung ist /{0} {1} {2} {3}&7 (Du kannst auch -s an das Ende des Befehls hinzuf\u00FCgen, damit der Spieler nicht benachrichtigt wird.)
Commands.Usage.FullClassName = Klassenname
Commands.Usage.Level = Level
Commands.Usage.Message = Nachricht
@ -285,7 +285,7 @@ Commands.XPBar.DisableAll = &6Alle mcMMO Erfahrungsleisten wurden deaktiviert, b
Commands.XPBar.Reset = &6Die Erfahrungsleisten-Einstellungen f\u00FCr mcMMO wurden zur\u00FCckgesetzt.
Commands.XPBar.SettingChanged = &6Die Erfahrungsleisten-Einstellungen f\u00FCr &a{0}&6 wurden gesetzt auf: &a{1}
Commands.XPBar.Usage = Die korrekte Verwendung ist /mmoxpbar <skillname | reset> <show | hide>
Commands.XPGain = &8XP ZUWACHS: &f{0}
Commands.XPGain = &8XP-Zuwachs: &f{0}
Commands.XPGain.Acrobatics = Fallen
Commands.XPGain.Alchemy = Tr\u00E4nke brauen
Commands.XPGain.Archery = Monster angreifen
@ -379,12 +379,12 @@ Excavation.SubSkill.GigaDrillBreaker.Description = Dreifache Droprate, dreifache
Excavation.SubSkill.GigaDrillBreaker.Name = Gigabohrer
Excavation.SubSkill.GigaDrillBreaker.Stat = Gigabohrer-Dauer
Fishing.Ability.Info = Zauberj\u00E4ger: &7 **Verbessert sich mit Schatzj\u00E4ger-Rang**
Fishing.Ability.Info = Zauberj\u00E4ger: &7**Verbessert sich mit Schatzj\u00E4ger-Rang**
Fishing.Ability.Locked.0 = Gesperrt bis Level {0}!
Fishing.Ability.Locked.1 = Gesperrt bis Level {0}!
Fishing.Ability.Locked.2 = Gesperrt bis Level {0}!
Fishing.Ability.TH.Boom = &c&lDeine Angelschnur hat sich in einer &4&lSeemine &c&lverfangen!
Fishing.Ability.TH.MagicFound = &bDu f\u00FChlst etwas Magisches in diesem Fang...
Fishing.Ability.TH.MagicFound = &bDu f\u00FChlst etwas Magisches an diesem Fang...
Fishing.Ability.TH.Poison = &7Irgendetwas stinkt hier...
Fishing.Chance.Raining = &9Regen-Bonus
Fishing.Exhausting = &c&oUnsachgem\u00E4\u00DFe Nutzung der Angelrute f\u00FChrt zu Erm\u00FCdung und Abnutzen der Rute.
@ -557,7 +557,7 @@ Inspect.OfflineStats = mcMMO Stats f\u00FCr Offline-Spieler &e{0}
Inspect.Stats = &amcMMO Stats f\u00FCr &e{0}
Inspect.TooFar = Du bist zu weit entfernt um den Spieler zu inspizieren!
Item.ChimaeraWing.Fail = &c**CHIMAERA FL\u00DCGEL GESCHEITERT!**
Item.ChimaeraWing.Fail = &c**Chimaera Fl\u00FCgel gescheitert!**
Item.ChimaeraWing.Lore = &7Teleportiert dich zu deinem Bett.
Item.ChimaeraWing.Name = Chimaera Fl\u00FCgel
Item.ChimaeraWing.NotEnough = Du ben\u00F6tigst &e{0}&c weitere &6{1}&c!
@ -595,7 +595,7 @@ JSON.JWrapper.Perks.Lucky = {0}% Bessere Chancen
JSON.JWrapper.Target.Block = Block
JSON.JWrapper.Target.Player = Spieler
JSON.JWrapper.Target.Type = Zieltyp:
JSON.LevelRequirement = Level Voraussetzung
JSON.LevelRequirement = Level-Voraussetzung
JSON.Locked = -=[NICHT VERF\u00DCGBAR]=-
JSON.Mining = Bergbau
JSON.Notification.SuperAbility = {0}
@ -610,7 +610,7 @@ JSON.Type.Passive = Passiv
JSON.Type.SuperAbility = Superf\u00E4higkeit
JSON.URL.Discord = Der offizielle (englische) mcMMO Discord Server!
JSON.URL.Patreon = Unterst\u00FCtze die Entwicklung von mcMMO \u00FCber nossr50's Patreon!
JSON.URL.Spigot = Die offizielle mcmmo Spigot Seite
JSON.URL.Spigot = Die offizielle mcMMO Spigot-Seite.
JSON.URL.Translation = \u00DCbersetze mcMMO in andere Sprachen!
JSON.URL.Website = Die offizielle mcMMO Website!
JSON.URL.Wiki = Das offizielle mcMMO Wiki!
@ -666,7 +666,7 @@ Mining.SubSkill.SuperBreaker.Stat = Superbrecher L\u00E4nge
Notifications.Admin.Format.Others = &6(&amcMMO &3Admin&6) &7{0}
Notifications.Admin.Format.Self = &6(&amcMMO&6) &7{0}
Notifications.Admin.XPRate.End.Others = {0} &7hat das Bonuserfahrungs-Event beendet
Notifications.Admin.XPRate.End.Others = {0} &7hat das Bonuserfahrungs-Event beendet.
Notifications.Admin.XPRate.End.Self = &7Du hast das Bonuserfahrungs-Event beendet.
Notifications.Admin.XPRate.Start.Others = {0} &7hat ein Bonuserfahrungs-Event mit einem Faktor von {1}x gestartet.
Notifications.Admin.XPRate.Start.Self = &7Du hast den globalen Erfahrungsraten-Multiplikator auf &6{0}x&7 gesetzt.
@ -715,7 +715,7 @@ Party.Help.0 = &cDie korrekte Benutzung ist &3{0} <spieler> [passwort].
Party.Help.1 = &cUm eine Gruppe zu erstellen, nutze &3{0} <gruppenname> [gruppenpasswort].
Party.Help.10 = &cNutze &3{0} &cum Erfahrungsteilung mit Mitgliedern zu aktivieren.
Party.Help.2 = &cNutze &3{0} &cf\u00FCr mehr Informationen.
Party.Help.3 = &cNutze &3{0} <spieler> [passwort] &czum beitreten oder &3{1} &czum verlassen.
Party.Help.3 = &cNutze &3{0} <spieler> [passwort] &czum Beitreten oder &3{1} &czum Verlassen.
Party.Help.4 = &cUm deine Gruppe zu sperren oder entsperren, nutze &3{0}.
Party.Help.5 = &cUm deine Gruppe per Passwort zu sch\u00FCtzen, nutze &3{0} <passwort>.
Party.Help.6 = &cUm einen Spieler aus deiner Gruppe zu entfernen, nutze &3{0} <spieler>.
@ -823,7 +823,7 @@ Repair.SubSkill.StoneRepair.Description = Repariere Stein-Werkzeuge.
Repair.SubSkill.StoneRepair.Name = Stein-Reparatur ({0}+ SKILL)
Repair.SubSkill.SuperRepair.Description = Doppelte Effektivit\u00E4t.
Repair.SubSkill.SuperRepair.Name = Super-Reparatur
Repair.SubSkill.SuperRepair.Stat = Chance auf Superreparatur
Repair.SubSkill.SuperRepair.Stat = Chance auf Super-Reparatur
Salvage.Ability.Bonus.0 = Fortgeschrittenes Verwerten
Salvage.Ability.Bonus.1 = Max Ertrag {0} Item zerst\u00F6rt
@ -845,7 +845,7 @@ Salvage.Skills.Success = &aItem verwertet!
Salvage.Skills.TooDamaged = &4Das Item ist zu besch\u00E4digt um verwertet zu werden.
Salvage.SubSkill.ArcaneSalvage.Description = Extrahiere Verzauberungen aus Items.
Salvage.SubSkill.ArcaneSalvage.Name = Magische Bergung
Salvage.SubSkill.ArcaneSalvage.Stat = Magische Bergung: &eRank {0}/{1}
Salvage.SubSkill.ArcaneSalvage.Stat = Magische Bergung: &eRang {0}/{1}
Salvage.SubSkill.ScrapCollector.Description = Verschrotte einen Gegenstand, um Materialien zur\u00FCckzugewinnen; eine perfekte Verschrottung erfordert Gl\u00FCck und Geschick.
Salvage.SubSkill.ScrapCollector.Name = Schrottsammler
Salvage.SubSkill.ScrapCollector.Stat = Schrottsammler: &aVerschrotte bis zu &e{0}&a Gegenst\u00E4nde. Hierbei spielt Gl\u00FCck eine gewisse Rolle.
@ -856,13 +856,13 @@ Scoreboard.Header.PlayerCooldowns = mcMMO Abklingzeiten
Scoreboard.Header.PlayerInspect = mcMMO Stats: {0}
Scoreboard.Header.PlayerRank = mcMMO Bestenlisten
Scoreboard.Header.PlayerStats = mcMMO Stats
Scoreboard.Header.PowerLevel = Gesamt Level
Scoreboard.Header.PowerLevel = Gesamt-Level
Scoreboard.Misc.Ability = F\u00E4higkeit
Scoreboard.Misc.Cooldown = &dAbklingzeit
Scoreboard.Misc.CurrentXP = &aAktuelle XP
Scoreboard.Misc.Level = &3Level
Scoreboard.Misc.Overall = &6Insgesamt
Scoreboard.Misc.PowerLevel = &6Gesamt Level
Scoreboard.Misc.PowerLevel = &6Gesamt-Level
Scoreboard.Misc.RemainingXP = Verbliebene XP
Server.ConsoleName = &e[Server]
@ -884,9 +884,9 @@ Skills.TooTired = Du bist zu m\u00FCde um diese F\u00E4higkeit zu verwenden. &e(
Skills.TooTired.Extra = &6{0} &eSuperf\u00E4higkeit CDs - {1}
Skills.TooTired.Named = &7(&6{0}&e {1}s&7)
Smelting.Ability.Locked.0 = Gesperrt bis {0}+ Skill (XP BOOST)
Smelting.Ability.Locked.1 = Gesperrt bis {0}+ Skill (SCHMELZTIEGEL)
Smelting.Effect.4 = Vanilla XP Boost
Smelting.Ability.Locked.0 = Gesperrt bis {0}+ Skill (XP-Boost)
Smelting.Ability.Locked.1 = Gesperrt bis {0}+ Skill (Schmelztiegel)
Smelting.Effect.4 = Vanilla XP-Boost
Smelting.Effect.5 = Erh\u00F6ht die erhaltene Erfahrung beim Schmelzen.
Smelting.Listener = Schmelzen:
Smelting.SkillName = Schmelzen
@ -895,7 +895,7 @@ Smelting.SubSkill.FluxMining.Name = Schmelztiegel
Smelting.SubSkill.FluxMining.Stat = Schmelztiegel Chance
Smelting.SubSkill.FuelEfficiency.Description = Erh\u00F6he die Brenndauer des Brennstoffes in \u00D6fen.
Smelting.SubSkill.FuelEfficiency.Name = Brennstoff Effizienz
Smelting.SubSkill.FuelEfficiency.Stat = Brennstoff Effizienz Multiplikator: &e{0}x
Smelting.SubSkill.FuelEfficiency.Stat = Brennstoff Effizienz-Multiplikator: &e{0}x
Smelting.SubSkill.SecondSmelt.Description = Verdoppelt den Ertrag beim Schmelzen.
Smelting.SubSkill.SecondSmelt.Name = Extra Schmelzung
Smelting.SubSkill.SecondSmelt.Stat = Extra Schmelzung Chance
@ -906,12 +906,12 @@ Smelting.SubSkill.UnderstandingTheArt.Stat = Vanilla Erfahrungsmultiplikator: &e
Stats.Header.Combat = &6-=Kampfskills=-
Stats.Header.Gathering = &6-=Sammelskills=-
Stats.Header.Misc = &6-=Weitere Skills=-
Stats.Own.Stats = &aSkill Statistik
Stats.Own.Stats = &aSkill-Statistik
Swords.Ability.Lower = &7&o**Du senkst dein Sschwert.**
Swords.Ability.Lower = &7&o**Du senkst dein Schwert.**
Swords.Ability.Ready = &a&o**Du hebst dein Schwert...**
Swords.Combat.Bleeding = &a**Gegner blutet**
Swords.Combat.Bleeding.Started = &4 Du blutest!
Swords.Combat.Bleeding.Started = &4Du blutest!
Swords.Combat.Bleeding.Stopped = &7Das Bluten hat &aaufgeh\u00F6rt&7!
Swords.Combat.Counter.Hit = &4Treffer durch Gegenangriff!
Swords.Combat.Countered = &a**Gegenangriff**
@ -920,7 +920,7 @@ Swords.Combat.SS.Struck = &4Getroffen von S\u00E4gezahnschlag!
Swords.Effect.4 = S\u00E4gezahnschlag, Blutung+
Swords.Effect.5 = {0} Ticks Blutung
Swords.Listener = Schwert:
Swords.SkillName = Sschwert
Swords.SkillName = Schwert
Swords.Skills.SS.Off = &a&o**S\u00E4gezahnschlag abgenutzt**
Swords.Skills.SS.On = &a&o**S\u00E4gezahnschlag aktiviert**
Swords.Skills.SS.Other.Off = {0}s &cS\u00E4gezahnschlag&a ist &aabgenutzt.
@ -983,7 +983,7 @@ Taming.SubSkill.Pummel.Name = Pummel
Taming.SubSkill.Pummel.TargetMessage = Du wurdest von einem Wolf zur\u00FCckgeschlagen!
Taming.SubSkill.SharpenedClaws.Description = Schadens-Bonus
Taming.SubSkill.SharpenedClaws.Name = Gesch\u00E4rfte Krallen
Taming.SubSkill.ShockProof.Description = Reduktion von Explosions-Schaden.
Taming.SubSkill.ShockProof.Description = Reduktion von Explosionsschaden.
Taming.SubSkill.ShockProof.Name = Schock-Sicher
Taming.SubSkill.ThickFur.Description = Verminderter Schaden, Feuer-Resistenz
Taming.SubSkill.ThickFur.Name = Dicker Pelz
@ -1030,7 +1030,7 @@ Unarmed.SubSkill.SteelArmStyle.Description = Verst\u00E4rkt deinen Arm mit der Z
Unarmed.SubSkill.SteelArmStyle.Name = St\u00E4hlerner Arm
Unarmed.SubSkill.UnarmedLimitBreak.Description = Durchbreche deine Grenzen!
Unarmed.SubSkill.UnarmedLimitBreak.Name = \u00DCberwindung
Unarmed.SubSkill.UnarmedLimitBreak.Stat = Bonus Schaden durch \u00DCberwindung
Unarmed.SubSkill.UnarmedLimitBreak.Stat = Bonus-Schaden durch \u00DCberwindung
UpdateChecker.NewAvailable = Eine neue Version von mcMMO ist auf Spigot erh\u00E4ltlich!
UpdateChecker.Outdated = Du verwendest eine veraltete mcMMO Version!
@ -1088,9 +1088,9 @@ XPBar.Woodcutting = Holzf\u00E4llen Level: &6{0}
XPRate.Event = &6Es findet derzeit ein Skill-Event statt! Du bekommst aktuell &c{0} &6mal so viel Erfahrung f\u00FCr deine Skills wie normal!
mcMMO.Description = &3\u00DCber das &emcMMO&3 Projekt:,&6mcMMO ist ein &copen source&6 RPG mod erstellt in Februar 2011&6von &9nossr50&6. Das Ziel ist es ein qualitatives RPG Erlebnis zu liefern.,&3Tips:,&6 - &aNutze &c/mcmmo help&a um Befehle zu sehen &6,- &aNutze &c/skillname&a f\u00FCr detaillierte Skill Infos,&3Entwickler:,&6 - &anossr50 &9(Erfinder & Projektleitung),&6 - &aGJ &9(Fr\u00FChere Projektleitung),&6 - &aNuclearW &9(Entwickler),&6 - &abm01 &9(Entwickler),&6 - &aTfT_02 &9(Entwickler),&6 - &aGlitchfinder &9(Entwickler),&6 - &at00thpick1 &9(Entwickler),&6 - &alumis31 &9 (Urspr\u00FCngliche Deutsche \u00DCbersetzung),&6 - &aOverCrave &9 (Neue Deutsche \u00DCbersetzung & \u00DCberarbeitung),&3N\u00FCtzliche Links:,&6 - &ahttps://github.com/mcMMO-Dev/mcMMO/issues&6 Bug Reporting,&6 - &ahttps://discord.gg/EJGVanb &6 Offizieller Discord (Englisch)
mcMMO.Description = &3\u00DCber das &emcMMO&3 Projekt: &6mcMMO ist ein &copen source&6 RPG-Mod erstellt im Februar 2011 &6von &9nossr50&6. Das Ziel ist es ein qualitatives RPG Erlebnis zu liefern. &3Tipps:&6 - &aNutze &c/mcmmo help&a um Befehle zu sehen, &6 - &aNutze &c/skillname&a f\u00FCr detaillierte Skill Infos, &3Entwickler:&6 - &anossr50 &9(Erfinder & Projektleitung),&6 - &aGJ &9(Fr\u00FChere Projektleitung),&6 - &aNuclearW &9(Entwickler),&6 - &abm01 &9(Entwickler),&6 - &aTfT_02 &9(Entwickler),&6 - &aGlitchfinder &9(Entwickler),&6 - &at00thpick1 &9(Entwickler),&6 - &alumis31 &9(Urspr\u00FCngliche Deutsche \u00DCbersetzung),&6 - &aOverCrave &9(Neue Deutsche \u00DCbersetzung & \u00DCberarbeitung),&6 - &aAnseba &9(\u00DCberarbeitung Deutsche \u00DCbersetzung), &3N\u00FCtzliche Links:&6 - &ahttps://github.com/mcMMO-Dev/mcMMO/issues&6 Bug Reporting,&6 - &ahttps://discord.gg/EJGVanb &6 Offizieller Discord (Englisch)
mcMMO.Description.FormerDevs = &3Ehemalige Entwickler: &aGJ, NuclearW, bm01, TfT_02, Glitchfinder
mcMMO.NoInvites = &cDu hast zurzeit keine Einladungen
mcMMO.NoInvites = &cDu hast zurzeit keine Einladungen.
mcMMO.NoPermission = &4Unzureichende Berechtigungen.
mcMMO.NoSkillNote = &8Wenn du keinen Zugriff auf einen Skill hast wird er hier nicht angezeigt.
mcMMO.Template.Prefix = &6(&amcMMO&6) &7{0}

View File

@ -12,3 +12,4 @@ Upgrades_Finished:
FIX_NETHERITE_SALVAGE_QUANTITIES: false
ADD_SQL_2_2: false
ADD_UUIDS: false
SQL_CHARSET_UTF8MB4: false

View File

@ -0,0 +1,32 @@
package com.gmail.nossr50.util.text;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
import org.junit.Assert;
import org.junit.Test;
/**
* This Unit Test checks if Adventure was set up correctly and works as expected.
* Normally we can rely on this to be the case. However sometimes our dependencies
* lack so far behind that things stop working correctly.
* This test ensures that basic functionality is guaranteed to work as we would expect.
*
* See https://github.com/mcMMO-Dev/mcMMO/pull/4446
*
*/
public class TextUtilsTest {
@Test
public void testColorizeText() {
String inputText = "&4This text should be red.";
/*
* If this method raises an exception, we know Adventure is not set up correctly.
* This will also make the test fail and warn us about it.
*/
TextComponent component = TextUtils.colorizeText(inputText);
Assert.assertEquals("Looks like Adventure is not working correctly.",
NamedTextColor.DARK_RED, component.color());
}
}