1
0
mirror of https://github.com/mcMMO-Dev/mcMMO.git synced 2026-02-18 17:53:00 +01:00

Compare commits

..

19 Commits

Author SHA1 Message Date
riking
ffd11f6e0e Use PlayerLoginEvent instead to alleviate ban concerns
Research indicates that there is still enough time from login to join for this to be a good idea.
2013-07-03 22:16:32 -07:00
riking
f1f9ffc10b Prefetch profile information from the database.
Note that this implementation may open us to a denial-of-service attack by a banned user - AsyncPlayerPreLoginEvent is called before the ban lists are checked.

Look into the consequences of recieving an InterruptedException in the middle of loadPlayerProfile on the integrity of the database. If possible, we would prefer to interrupt the profile fetching task when the player stops logging in, to mitigate any possible denial-of-service attacks.
2013-07-03 21:52:19 -07:00
riking
8fe18be79b Move that PlayerProfile up another level! 2013-07-03 21:12:23 -07:00
T00thpick1
f0dcfb0346 Temporary for building for old peoples 2013-07-02 12:48:35 -04:00
T00thpick1
c85d52e594 Make Riking's stuff work 2013-07-02 12:42:09 -04:00
Travis Ralston
4342abca3d Update EMetrics version to use 0.0.5-SNAPSHOT (MCStats R7) 2013-07-01 17:11:58 +02:00
T00thpick1
11e4ff34d6 Flatfile repair 2013-07-01 02:36:21 -04:00
Kane York
f118ac14ca Specify returning of generated keys before using them
This is a herp derp fix
2013-07-01 00:35:07 -04:00
T00thpick1
f5eb7a10de Only set custom class when valid class 2013-07-01 00:34:54 -04:00
T00thpick1
36b09421e8 These should all be 0 too, for consistancy 2013-06-29 23:42:28 -04:00
T00thpick1
d6b39a11e7 0 is nonexistant, not -1 2013-06-29 23:41:08 -04:00
riking
23729f45ee Pull changes from dev-dbman (commit f63c5e3) 2013-06-28 15:02:58 -07:00
T00thpick1
27d7b73e57 Pretty sure this should be Fake 2013-06-28 16:22:55 -04:00
TfT_02
af60f08a8b Localize the ScoreboardManager
Closes #1232
2013-06-28 19:17:39 +02:00
TfT_02
c19d3fd068 Fixed bug with Ice Fishing which allowed players to break protected blocks
Fixes #2074
2013-06-28 14:06:54 +02:00
TfT_02
0f8312dd5e Minor cleanup 2013-06-28 14:06:13 +02:00
TfT_02
1edb11cedf Disable mob healthbars for boss mobs!
Fixes #1210
2013-06-28 13:36:48 +02:00
TfT_02
6fea7c8d74 Update the changelog: Fixed update check 2013-06-27 22:23:09 +02:00
riking
a4a1ba4331 Make UpdateChecker run asynchronously and not block startup
Closes #1049
2013-06-27 09:48:39 -07:00
31 changed files with 1227 additions and 635 deletions

View File

@@ -29,6 +29,9 @@ Version 1.4.06-dev
+ Added information about /party itemshare and /party expshare to the party help page + Added information about /party itemshare and /party expshare to the party help page
+ Added option to use scoreboards for power level display instead of Spout. + Added option to use scoreboards for power level display instead of Spout.
+ Added permission node to prevent inspecting hidden players + Added permission node to prevent inspecting hidden players
+ Added SQL to Flatfile database conversion
+ Added ability to use custom database managers and convert to/from them
= Fixed bug which could cause the server to hang for a minute when checking for updates. (Thanks to Riking)
= Fixed bug where spawned arrows could throw ArrayIndexOutOfBoundsException = Fixed bug where spawned arrows could throw ArrayIndexOutOfBoundsException
= Fixed bug where custom Spout titles were overwritten by mcMMO. = Fixed bug where custom Spout titles were overwritten by mcMMO.
= Fixed bug where Nether Quartz wasn't included in Smelting or item sharing = Fixed bug where Nether Quartz wasn't included in Smelting or item sharing
@@ -52,6 +55,8 @@ Version 1.4.06-dev
= Fixed bug where the chance of a successful Gracefull Roll was twice as high as displayed = Fixed bug where the chance of a successful Gracefull Roll was twice as high as displayed
= Fixed bug where lucky perks where not working = Fixed bug where lucky perks where not working
= Fixed bug with Ice Fishing on a single block of ice = Fixed bug with Ice Fishing on a single block of ice
= Fixed bug with Ice Fishing which allowed players to break ice in protected areas
= Fixed a small bug with mob healthbars and bosses, such as EnderDragons and Withers
! Changed Spout notification tiers to be stored in SpoutConfig instead of AdvancedConfig ! Changed Spout notification tiers to be stored in SpoutConfig instead of AdvancedConfig
! Changed Berserk to add items to inventory rather than denying pickup ! Changed Berserk to add items to inventory rather than denying pickup
! Changed Call of the Wild, newly summoned pet's will have a custom name. (added permission node to disable this) ! Changed Call of the Wild, newly summoned pet's will have a custom name. (added permission node to disable this)

View File

@@ -145,7 +145,7 @@
<dependency> <dependency>
<groupId>org.bukkit</groupId> <groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId> <artifactId>bukkit</artifactId>
<version>LATEST</version> <version>1.5.2-R1.0</version>
<type>jar</type> <type>jar</type>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
@@ -165,7 +165,7 @@
<dependency> <dependency>
<groupId>com.turt2live.metrics</groupId> <groupId>com.turt2live.metrics</groupId>
<artifactId>MetricsExtension</artifactId> <artifactId>MetricsExtension</artifactId>
<version>0.0.4-SNAPSHOT</version> <version>0.0.5-SNAPSHOT</version>
</dependency> </dependency>
</dependencies> </dependencies>
<distributionManagement> <distributionManagement>

View File

@@ -4,6 +4,7 @@ import java.util.Set;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.api.exceptions.InvalidPlayerException; import com.gmail.nossr50.api.exceptions.InvalidPlayerException;
import com.gmail.nossr50.api.exceptions.InvalidSkillException; import com.gmail.nossr50.api.exceptions.InvalidSkillException;
import com.gmail.nossr50.config.Config; import com.gmail.nossr50.config.Config;
@@ -552,7 +553,7 @@ public final class ExperienceAPI {
} }
private static PlayerProfile getOfflineProfile(String playerName) { private static PlayerProfile getOfflineProfile(String playerName) {
PlayerProfile profile = new PlayerProfile(playerName, false); PlayerProfile profile = mcMMO.getDatabaseManager().loadPlayerProfile(playerName, false);
if (!profile.isLoaded()) { if (!profile.isLoaded()) {
throw new InvalidPlayerException(); throw new InvalidPlayerException();

View File

@@ -10,7 +10,6 @@ import org.bukkit.command.TabExecutor;
import org.bukkit.util.StringUtil; import org.bukkit.util.StringUtil;
import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.datatypes.player.PlayerProfile;
import com.gmail.nossr50.locale.LocaleLoader; import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.util.commands.CommandUtils; import com.gmail.nossr50.util.commands.CommandUtils;
import com.gmail.nossr50.util.player.UserManager; import com.gmail.nossr50.util.player.UserManager;
@@ -22,7 +21,7 @@ public class McremoveCommand implements TabExecutor {
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
switch (args.length) { switch (args.length) {
case 1: case 1:
if (UserManager.getPlayer(args[0]) == null && CommandUtils.unloadedProfile(sender, new PlayerProfile(args[0], false))) { if (UserManager.getPlayer(args[0]) == null && CommandUtils.unloadedProfile(sender, mcMMO.getDatabaseManager().loadPlayerProfile(args[0], false))) {
return true; return true;
} }

View File

@@ -0,0 +1,42 @@
package com.gmail.nossr50.commands.database;
import java.util.List;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import com.gmail.nossr50.config.Config;
import com.gmail.nossr50.database.DatabaseManagerFactory;
import com.gmail.nossr50.locale.LocaleLoader;
import com.google.common.collect.ImmutableList;
public class MmoshowdbCommand implements TabExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length != 0) {
return false;
}
else {
Class<?> clazz = DatabaseManagerFactory.getCustomDatabaseManagerClass();
if (clazz != null) {
sender.sendMessage(LocaleLoader.getString("Commands.mmoshowdb", clazz.getName()));
return true;
}
else {
if (Config.getInstance().getUseMySQL()) {
sender.sendMessage(LocaleLoader.getString("Commands.mmoshowdb", "sql"));
}
else {
sender.sendMessage(LocaleLoader.getString("Commands.mmoshowdb", "flatfile"));
}
return true;
}
}
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
return ImmutableList.of();
}
}

View File

@@ -9,41 +9,130 @@ import org.bukkit.entity.Player;
import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.config.Config; import com.gmail.nossr50.config.Config;
import com.gmail.nossr50.database.DatabaseManager;
import com.gmail.nossr50.database.DatabaseManagerFactory;
import com.gmail.nossr50.datatypes.player.PlayerProfile;
import com.gmail.nossr50.locale.LocaleLoader; import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.runnables.database.SQLConversionTask; import com.gmail.nossr50.runnables.database.ConversionTask;
import com.gmail.nossr50.util.player.UserManager; import com.gmail.nossr50.util.player.UserManager;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
public class MmoupdateCommand implements TabExecutor { public class MmoupdateCommand implements TabExecutor {
@Override @Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!Config.getInstance().getUseMySQL()) {
sender.sendMessage("SQL Mode is not enabled."); // TODO: Localize
return true;
}
switch (args.length) { switch (args.length) {
case 0: case 1:
sender.sendMessage(LocaleLoader.getString("Commands.mmoupdate.Start")); String argType = args[0];
String oldType = validateName(sender, args[0]);
if (oldType == null) {
return true;
}
String newType = getCurrentDb();
if (newType.equals(oldType)) {
sender.sendMessage(LocaleLoader.getString("Commands.mmoupdate.Same", argType));
return true;
}
DatabaseManager oldDb;
if (oldType == "sql") {
oldDb = DatabaseManagerFactory.createSQLDatabaseManager();
}
else if (oldType == "flatfile") {
oldDb = DatabaseManagerFactory.createFlatfileDatabaseManager();
}
else try {
@SuppressWarnings("unchecked")
Class<? extends DatabaseManager> clazz = (Class<? extends DatabaseManager>) Class.forName(oldType);
oldDb = DatabaseManagerFactory.createCustomDatabaseManager((Class<? extends DatabaseManager>) clazz);
oldType = clazz.getSimpleName(); // For pretty-printing; we have the database now
}
catch (Throwable e) {
return false;
}
sender.sendMessage(LocaleLoader.getString("Commands.mmoupdate.Start", oldType, newType));
// Convert the online players right away, without waiting
// first, flush out the current data
UserManager.saveAll(); UserManager.saveAll();
UserManager.clearAll(); UserManager.clearAll();
new SQLConversionTask().runTaskLaterAsynchronously(mcMMO.p, 1);
for (Player player : mcMMO.p.getServer().getOnlinePlayers()) { for (Player player : mcMMO.p.getServer().getOnlinePlayers()) {
// Get the profile from the old database and save it in the new
PlayerProfile profile = oldDb.loadPlayerProfile(player.getName(), false);
if (profile.isLoaded()) {
mcMMO.getDatabaseManager().saveUser(profile);
}
// Reload from the current database via UserManager
UserManager.addUser(player); UserManager.addUser(player);
} }
sender.sendMessage(LocaleLoader.getString("Commands.mmoupdate.Finish")); // Schedule the task for all users
new ConversionTask(oldDb, sender, oldType, newType).runTaskAsynchronously(mcMMO.p);
return true; return true;
default: default:
return false; break;
}
return false;
}
/**
* @return null - if type not recognized / class not found
* empty string - if type is same as current
* normalized string - if type is recognized
*/
private String validateName(CommandSender sender, String type) {
if (type.equalsIgnoreCase("sql") || type.equalsIgnoreCase("mysql")) {
return "sql";
}
if (type.equalsIgnoreCase("flatfile") || type.equalsIgnoreCase("file")) {
return "flatfile";
}
try {
Class<?> clazz = Class.forName(type);
if (!DatabaseManager.class.isAssignableFrom(clazz)) {
sender.sendMessage(LocaleLoader.getString("Commands.mmoupdate.InvalidType", type));
return null;
}
return type;
}
catch (Exception e) {
sender.sendMessage(LocaleLoader.getString("Commands.mmoupdate.InvalidType", type));
return null;
}
}
private String getCurrentDb() {
if (DatabaseManagerFactory.getCustomDatabaseManagerClass() != null) {
return DatabaseManagerFactory.getCustomDatabaseManagerClass().getSimpleName();
}
if (Config.getInstance().getUseMySQL()) {
return "sql";
}
else {
return "flatfile";
} }
} }
@Override @Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) { public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
return ImmutableList.of(); Class<?> clazz = DatabaseManagerFactory.getCustomDatabaseManagerClass();
if (clazz != null) {
return ImmutableList.of("flatfile", "sql", clazz.getName());
}
return ImmutableList.of("flatfile", "sql");
} }
} }

View File

@@ -10,6 +10,7 @@ import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil; import org.bukkit.util.StringUtil;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.datatypes.player.McMMOPlayer; import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.player.PlayerProfile; import com.gmail.nossr50.datatypes.player.PlayerProfile;
import com.gmail.nossr50.datatypes.skills.SkillType; import com.gmail.nossr50.datatypes.skills.SkillType;
@@ -68,7 +69,7 @@ public abstract class ExperienceCommand implements TabExecutor {
// 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 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) { if (mcMMOPlayer == null) {
profile = new PlayerProfile(args[0], false); profile = mcMMO.getDatabaseManager().loadPlayerProfile(args[0], false);
if (CommandUtils.unloadedProfile(sender, profile)) { if (CommandUtils.unloadedProfile(sender, profile)) {
return true; return true;

View File

@@ -5,7 +5,6 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.datatypes.player.PlayerProfile;
import com.gmail.nossr50.datatypes.skills.SkillType; import com.gmail.nossr50.datatypes.skills.SkillType;
import com.gmail.nossr50.events.experience.McMMOPlayerLevelUpEvent; import com.gmail.nossr50.events.experience.McMMOPlayerLevelUpEvent;
import com.gmail.nossr50.locale.LocaleLoader; import com.gmail.nossr50.locale.LocaleLoader;
@@ -61,7 +60,7 @@ public class SkillresetCommand extends ExperienceCommand {
// 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 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) { if (mcMMOPlayer == null) {
profile = new PlayerProfile(args[0], false); profile = mcMMO.getDatabaseManager().loadPlayerProfile(args[0], false);
if (CommandUtils.unloadedProfile(sender, profile)) { if (CommandUtils.unloadedProfile(sender, profile)) {
return true; return true;

View File

@@ -10,6 +10,7 @@ import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil; import org.bukkit.util.StringUtil;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.config.Config; import com.gmail.nossr50.config.Config;
import com.gmail.nossr50.datatypes.player.McMMOPlayer; import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.player.PlayerProfile; import com.gmail.nossr50.datatypes.player.PlayerProfile;
@@ -35,7 +36,7 @@ public class InspectCommand implements TabExecutor {
// 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 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) { if (mcMMOPlayer == null) {
PlayerProfile profile = new PlayerProfile(args[0], false); // Temporary Profile PlayerProfile profile = mcMMO.getDatabaseManager().loadPlayerProfile(args[0], false); // Temporary Profile
if (CommandUtils.inspectOffline(sender, profile, Permissions.inspectOffline(sender))) { if (CommandUtils.inspectOffline(sender, profile, Permissions.inspectOffline(sender))) {
return true; return true;

View File

@@ -13,7 +13,6 @@ import org.bukkit.util.StringUtil;
import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.config.Config; import com.gmail.nossr50.config.Config;
import com.gmail.nossr50.datatypes.player.McMMOPlayer; import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.player.PlayerProfile;
import com.gmail.nossr50.runnables.commands.McrankCommandAsyncTask; import com.gmail.nossr50.runnables.commands.McrankCommandAsyncTask;
import com.gmail.nossr50.util.Permissions; import com.gmail.nossr50.util.Permissions;
import com.gmail.nossr50.util.commands.CommandUtils; import com.gmail.nossr50.util.commands.CommandUtils;
@@ -61,7 +60,7 @@ public class McrankCommand implements TabExecutor {
return true; return true;
} }
} }
else if (CommandUtils.inspectOffline(sender, new PlayerProfile(playerName, false), Permissions.mcrankOffline(sender))) { else if (CommandUtils.inspectOffline(sender, mcMMO.getDatabaseManager().loadPlayerProfile(playerName, false), Permissions.mcrankOffline(sender))) {
return true; return true;
} }

View File

@@ -8,6 +8,7 @@ import com.gmail.nossr50.datatypes.database.PlayerStat;
import com.gmail.nossr50.datatypes.player.PlayerProfile; import com.gmail.nossr50.datatypes.player.PlayerProfile;
public interface DatabaseManager { public interface DatabaseManager {
// One month in milliseconds
public final long PURGE_TIME = 2630000000L * Config.getInstance().getOldUsersCutoff(); public final long PURGE_TIME = 2630000000L * Config.getInstance().getOldUsersCutoff();
/** /**
@@ -64,16 +65,25 @@ public interface DatabaseManager {
* Load a player from the database. * Load a player from the database.
* *
* @param playerName The name of the player to load from the database * @param playerName The name of the player to load from the database
* @return The player's data * @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 List<String> loadPlayerData(String playerName); public PlayerProfile loadPlayerProfile(String playerName, boolean createNew);
/** /**
* Convert player data to a different storage format. * Get all users currently stored in the database.
* *
* @param data The player's data * @return list of playernames
* @return true if the conversion was successful, false otherwise
* @throws Exception
*/ */
public boolean convert(String[] data) throws Exception; public List<String> getStoredUsers();
/**
* Convert all users from this database to the provided database using
* {@link #saveUser(PlayerProfile)}.
*
* @param the DatabaseManager to save to
*/
public void convertUsers(DatabaseManager destination);
} }

View File

@@ -1,9 +1,66 @@
package com.gmail.nossr50.database; package com.gmail.nossr50.database;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.config.Config; import com.gmail.nossr50.config.Config;
public class DatabaseManagerFactory { public class DatabaseManagerFactory {
private static Class<? extends DatabaseManager> customManager = null;
public static DatabaseManager getDatabaseManager() { public static DatabaseManager getDatabaseManager() {
if (customManager != null) {
try {
return createCustomDatabaseManager(customManager);
} 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(); return Config.getInstance().getUseMySQL() ? new SQLDatabaseManager() : new FlatfileDatabaseManager();
} }
/**
* 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 man 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((Class<?>) null);
customManager = clazz;
} catch (Throwable e) {
throw new IllegalArgumentException("Provided database manager class must have an empty constructor", e);
}
}
public static Class<? extends DatabaseManager> getCustomDatabaseManagerClass() {
return customManager;
}
// For data conversion purposes
public static FlatfileDatabaseManager createFlatfileDatabaseManager() {
return new FlatfileDatabaseManager();
}
public static SQLDatabaseManager createSQLDatabaseManager() {
return new SQLDatabaseManager();
}
public static DatabaseManager createCustomDatabaseManager(Class<? extends DatabaseManager> clazz) throws Throwable {
return customManager.getConstructor((Class<?>) null).newInstance((Object[]) null);
}
} }

View File

@@ -2,6 +2,7 @@ package com.gmail.nossr50.database;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.FileWriter; import java.io.FileWriter;
@@ -13,6 +14,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.mcMMO;
@@ -32,10 +34,11 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
private final long UPDATE_WAIT_TIME = 600000L; // 10 minutes private final long UPDATE_WAIT_TIME = 600000L; // 10 minutes
private final File usersFile; private final File usersFile;
private static final Object fileWritingLock = new Object();
protected FlatfileDatabaseManager() { protected FlatfileDatabaseManager() {
usersFile = new File(mcMMO.getUsersFilePath()); usersFile = new File(mcMMO.getUsersFilePath());
createDatabase(); checkStructure();
updateLeaderboards(); updateLeaderboards();
} }
@@ -44,9 +47,49 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
mcMMO.p.getLogger().info("Purging powerless users..."); mcMMO.p.getLogger().info("Purging powerless users...");
for (PlayerStat stat : powerLevels) { BufferedReader in = null;
if (stat.statVal == 0 && mcMMO.p.getServer().getPlayerExact(stat.name) == null && removeUser(stat.name)) { FileWriter out = null;
purgedUsers++; String usersFilePath = mcMMO.getUsersFilePath();
// This code is O(n) instead of O(n²)
synchronized (fileWritingLock) {
try {
in = new BufferedReader(new FileReader(usersFilePath));
StringBuilder writer = new StringBuilder();
String line = "";
while ((line = in.readLine()) != null) {
String[] character = line.split(":");
Map<SkillType, Integer> skills = getSkillMapFromLine(character);
boolean powerless = true;
for (int skill : skills.values()) {
if (skill != 0) {
powerless = false;
break;
}
}
// If they're still around, rewrite them to the file.
if (!powerless) {
writer.append(line).append("\r\n");
}
else {
purgedUsers++;
Misc.profileCleanup(character[0]);
}
}
// Write the new file
out = new FileWriter(usersFilePath);
out.write(writer.toString());
}
catch (IOException e) {
mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
}
finally {
tryClose(in);
tryClose(out);
} }
} }
@@ -59,9 +102,46 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
mcMMO.p.getLogger().info("Purging old users..."); mcMMO.p.getLogger().info("Purging old users...");
for (OfflinePlayer player : mcMMO.p.getServer().getOfflinePlayers()) {
if (!player.isOnline() && (currentTime - player.getLastPlayed() > PURGE_TIME && removeUser(player.getName()))) { BufferedReader in = null;
removedPlayers++; FileWriter out = null;
String usersFilePath = mcMMO.getUsersFilePath();
// This code is O(n) instead of O(n²)
synchronized (fileWritingLock) {
try {
in = new BufferedReader(new FileReader(usersFilePath));
StringBuilder writer = new StringBuilder();
String line = "";
while ((line = in.readLine()) != null) {
String[] character = line.split(":");
String name = character[0];
OfflinePlayer player = Bukkit.getOfflinePlayer(name);
boolean old = true;
if (player != null) {
old = (currentTime - player.getLastPlayed()) > PURGE_TIME;
}
if (!old) {
writer.append(line).append("\r\n");
}
else {
removedPlayers++;
Misc.profileCleanup(name);
}
}
// Write the new file
out = new FileWriter(usersFilePath);
out.write(writer.toString());
}
catch (IOException e) {
mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
}
finally {
tryClose(in);
tryClose(out);
} }
} }
@@ -75,45 +155,32 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
FileWriter out = null; FileWriter out = null;
String usersFilePath = mcMMO.getUsersFilePath(); String usersFilePath = mcMMO.getUsersFilePath();
try { synchronized (fileWritingLock) {
in = new BufferedReader(new FileReader(usersFilePath)); try {
StringBuilder writer = new StringBuilder(); in = new BufferedReader(new FileReader(usersFilePath));
String line = ""; StringBuilder writer = new StringBuilder();
String line = "";
while ((line = in.readLine()) != null) { while ((line = in.readLine()) != null) {
// Write out the same file but when we get to the player we want to remove, we skip his line. // Write out the same file but when we get to the player we want to remove, we skip his line.
if (!worked && line.split(":")[0].equalsIgnoreCase(playerName)) { if (!worked && line.split(":")[0].equalsIgnoreCase(playerName)) {
mcMMO.p.getLogger().info("User found, removing..."); mcMMO.p.getLogger().info("User found, removing...");
worked = true; worked = true;
continue; // Skip the player continue; // Skip the player
}
writer.append(line).append("\r\n");
} }
writer.append(line).append("\r\n"); out = new FileWriter(usersFilePath); // Write out the new file
out.write(writer.toString());
} }
catch (Exception e) {
out = new FileWriter(usersFilePath); // Write out the new file mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
out.write(writer.toString());
}
catch (Exception e) {
mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
}
finally {
if (in != null) {
try {
in.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
} }
finally {
if (out != null) { tryClose(in);
try { tryClose(out);
out.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
} }
} }
@@ -129,89 +196,76 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
FileWriter out = null; FileWriter out = null;
String usersFilePath = mcMMO.getUsersFilePath(); String usersFilePath = mcMMO.getUsersFilePath();
try { synchronized (fileWritingLock) {
// Open the file try {
in = new BufferedReader(new FileReader(usersFilePath)); // Open the file
StringBuilder writer = new StringBuilder(); in = new BufferedReader(new FileReader(usersFilePath));
String line; StringBuilder writer = new StringBuilder();
String line;
// While not at the end of the file // While not at the end of the file
while ((line = in.readLine()) != null) { while ((line = in.readLine()) != null) {
// Read the line in and copy it to the output it's not the player we want to edit // Read the line in and copy it to the output it's not the player we want to edit
if (!line.split(":")[0].equalsIgnoreCase(playerName)) { if (!line.split(":")[0].equalsIgnoreCase(playerName)) {
writer.append(line).append("\r\n"); writer.append(line).append("\r\n");
} }
else { else {
// Otherwise write the new player information // Otherwise write the new player information
writer.append(playerName).append(":"); writer.append(playerName).append(":");
writer.append(profile.getSkillLevel(SkillType.MINING)).append(":"); writer.append(profile.getSkillLevel(SkillType.MINING)).append(":");
writer.append(":"); writer.append(":");
writer.append(":"); writer.append(":");
writer.append(profile.getSkillXpLevel(SkillType.MINING)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.MINING)).append(":");
writer.append(profile.getSkillLevel(SkillType.WOODCUTTING)).append(":"); writer.append(profile.getSkillLevel(SkillType.WOODCUTTING)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.WOODCUTTING)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.WOODCUTTING)).append(":");
writer.append(profile.getSkillLevel(SkillType.REPAIR)).append(":"); writer.append(profile.getSkillLevel(SkillType.REPAIR)).append(":");
writer.append(profile.getSkillLevel(SkillType.UNARMED)).append(":"); writer.append(profile.getSkillLevel(SkillType.UNARMED)).append(":");
writer.append(profile.getSkillLevel(SkillType.HERBALISM)).append(":"); writer.append(profile.getSkillLevel(SkillType.HERBALISM)).append(":");
writer.append(profile.getSkillLevel(SkillType.EXCAVATION)).append(":"); writer.append(profile.getSkillLevel(SkillType.EXCAVATION)).append(":");
writer.append(profile.getSkillLevel(SkillType.ARCHERY)).append(":"); writer.append(profile.getSkillLevel(SkillType.ARCHERY)).append(":");
writer.append(profile.getSkillLevel(SkillType.SWORDS)).append(":"); writer.append(profile.getSkillLevel(SkillType.SWORDS)).append(":");
writer.append(profile.getSkillLevel(SkillType.AXES)).append(":"); writer.append(profile.getSkillLevel(SkillType.AXES)).append(":");
writer.append(profile.getSkillLevel(SkillType.ACROBATICS)).append(":"); writer.append(profile.getSkillLevel(SkillType.ACROBATICS)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.REPAIR)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.REPAIR)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.UNARMED)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.UNARMED)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.HERBALISM)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.HERBALISM)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.EXCAVATION)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.EXCAVATION)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.ARCHERY)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.ARCHERY)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.SWORDS)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.SWORDS)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.AXES)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.AXES)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.ACROBATICS)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.ACROBATICS)).append(":");
writer.append(":"); writer.append(":");
writer.append(profile.getSkillLevel(SkillType.TAMING)).append(":"); writer.append(profile.getSkillLevel(SkillType.TAMING)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.TAMING)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.TAMING)).append(":");
writer.append((int) profile.getSkillDATS(AbilityType.BERSERK)).append(":"); writer.append((int) profile.getSkillDATS(AbilityType.BERSERK)).append(":");
writer.append((int) profile.getSkillDATS(AbilityType.GIGA_DRILL_BREAKER)).append(":"); writer.append((int) profile.getSkillDATS(AbilityType.GIGA_DRILL_BREAKER)).append(":");
writer.append((int) profile.getSkillDATS(AbilityType.TREE_FELLER)).append(":"); writer.append((int) profile.getSkillDATS(AbilityType.TREE_FELLER)).append(":");
writer.append((int) profile.getSkillDATS(AbilityType.GREEN_TERRA)).append(":"); writer.append((int) profile.getSkillDATS(AbilityType.GREEN_TERRA)).append(":");
writer.append((int) profile.getSkillDATS(AbilityType.SERRATED_STRIKES)).append(":"); writer.append((int) profile.getSkillDATS(AbilityType.SERRATED_STRIKES)).append(":");
writer.append((int) profile.getSkillDATS(AbilityType.SKULL_SPLITTER)).append(":"); writer.append((int) profile.getSkillDATS(AbilityType.SKULL_SPLITTER)).append(":");
writer.append((int) profile.getSkillDATS(AbilityType.SUPER_BREAKER)).append(":"); writer.append((int) profile.getSkillDATS(AbilityType.SUPER_BREAKER)).append(":");
HudType hudType = profile.getHudType(); HudType hudType = profile.getHudType();
writer.append(hudType == null ? "STANDARD" : hudType.toString()).append(":"); writer.append(hudType == null ? "STANDARD" : hudType.toString()).append(":");
writer.append(profile.getSkillLevel(SkillType.FISHING)).append(":"); writer.append(profile.getSkillLevel(SkillType.FISHING)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.FISHING)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.FISHING)).append(":");
writer.append((int) profile.getSkillDATS(AbilityType.BLAST_MINING)).append(":"); writer.append((int) profile.getSkillDATS(AbilityType.BLAST_MINING)).append(":");
writer.append(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR).append(":"); writer.append(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR).append(":");
MobHealthbarType mobHealthbarType = profile.getMobHealthbarType(); MobHealthbarType mobHealthbarType = profile.getMobHealthbarType();
writer.append(mobHealthbarType == null ? Config.getInstance().getMobHealthbarDefault().toString() : mobHealthbarType.toString()).append(":"); writer.append(mobHealthbarType == null ? Config.getInstance().getMobHealthbarDefault().toString() : mobHealthbarType.toString()).append(":");
writer.append("\r\n"); writer.append("\r\n");
}
} }
// Write the new file
out = new FileWriter(usersFilePath);
out.write(writer.toString());
} }
catch (Exception e) {
// Write the new file e.printStackTrace();
out = new FileWriter(usersFilePath);
out.write(writer.toString());
}
catch (Exception e) {
e.printStackTrace();
}
finally {
if (in != null) {
try {
in.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
} }
finally {
if (out != null) { tryClose(in);
try { tryClose(out);
out.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
} }
} }
} }
@@ -239,145 +293,168 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
} }
public void newUser(String playerName) { public void newUser(String playerName) {
try { BufferedWriter out = null;
// Open the file to write the player synchronized (fileWritingLock) {
BufferedWriter out = new BufferedWriter(new FileWriter(mcMMO.getUsersFilePath(), true)); try {
// Open the file to write the player
out = new BufferedWriter(new FileWriter(mcMMO.getUsersFilePath(), true));
// Add the player to the end // Add the player to the end
out.append(playerName).append(":"); out.append(playerName).append(":");
out.append("0:"); // Mining out.append("0:"); // Mining
out.append(":"); out.append(":");
out.append(":"); out.append(":");
out.append("0:"); // Xp out.append("0:"); // Xp
out.append("0:"); // Woodcutting out.append("0:"); // Woodcutting
out.append("0:"); // WoodCuttingXp out.append("0:"); // WoodCuttingXp
out.append("0:"); // Repair out.append("0:"); // Repair
out.append("0:"); // Unarmed out.append("0:"); // Unarmed
out.append("0:"); // Herbalism out.append("0:"); // Herbalism
out.append("0:"); // Excavation out.append("0:"); // Excavation
out.append("0:"); // Archery out.append("0:"); // Archery
out.append("0:"); // Swords out.append("0:"); // Swords
out.append("0:"); // Axes out.append("0:"); // Axes
out.append("0:"); // Acrobatics out.append("0:"); // Acrobatics
out.append("0:"); // RepairXp out.append("0:"); // RepairXp
out.append("0:"); // UnarmedXp out.append("0:"); // UnarmedXp
out.append("0:"); // HerbalismXp out.append("0:"); // HerbalismXp
out.append("0:"); // ExcavationXp out.append("0:"); // ExcavationXp
out.append("0:"); // ArcheryXp out.append("0:"); // ArcheryXp
out.append("0:"); // SwordsXp out.append("0:"); // SwordsXp
out.append("0:"); // AxesXp out.append("0:"); // AxesXp
out.append("0:"); // AcrobaticsXp out.append("0:"); // AcrobaticsXp
out.append(":"); out.append(":");
out.append("0:"); // Taming out.append("0:"); // Taming
out.append("0:"); // TamingXp out.append("0:"); // TamingXp
out.append("0:"); // DATS out.append("0:"); // DATS
out.append("0:"); // DATS out.append("0:"); // DATS
out.append("0:"); // DATS out.append("0:"); // DATS
out.append("0:"); // DATS out.append("0:"); // DATS
out.append("0:"); // DATS out.append("0:"); // DATS
out.append("0:"); // DATS out.append("0:"); // DATS
out.append("0:"); // DATS out.append("0:"); // DATS
out.append("STANDARD").append(":"); // HUD out.append("STANDARD").append(":"); // HUD
out.append("0:"); // Fishing out.append("0:"); // Fishing
out.append("0:"); // FishingXp out.append("0:"); // FishingXp
out.append("0:"); // Blast Mining out.append("0:"); // Blast Mining
out.append(String.valueOf(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR)).append(":"); // LastLogin out.append(String.valueOf(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR)).append(":"); // LastLogin
out.append(Config.getInstance().getMobHealthbarDefault().toString()).append(":"); // Mob Healthbar HUD out.append(Config.getInstance().getMobHealthbarDefault().toString()).append(":"); // Mob Healthbar HUD
// Add more in the same format as the line above // Add more in the same format as the line above
out.newLine(); out.newLine();
out.close(); }
} catch (Exception e) {
catch (Exception e) { e.printStackTrace();
e.printStackTrace(); }
} finally {
} tryClose(out);
public List<String> loadPlayerData(String playerName) {
List<String> playerData = new ArrayList<String>();
try {
// Open the user file
FileReader file = new FileReader(mcMMO.getUsersFilePath());
BufferedReader in = new BufferedReader(file);
String line;
while ((line = in.readLine()) != null) {
// Find if the line contains the player we want.
String[] character = line.split(":");
if (!character[0].equalsIgnoreCase(playerName)) {
continue;
}
// Skill levels
playerData.add(character[24]); // Taming
playerData.add(character[1]); // Mining
playerData.add(character[7]); // Repair
playerData.add(character[5]); // Woodcutting
playerData.add(character[8]); // Unarmed
playerData.add(character[9]); // Herbalism
playerData.add(character[10]); // Excavation
playerData.add(character[11]); // Archery
playerData.add(character[12]); // Swords
playerData.add(character[13]); // Axes
playerData.add(character[14]); // Acrobatics
playerData.add(character[34]); // Fishing
// Experience
playerData.add(character[25]); // Taming
playerData.add(character[4]); // Mining
playerData.add(character[15]); // Repair
playerData.add(character[6]); // Woodcutting
playerData.add(character[16]); // Unarmed
playerData.add(character[17]); // Herbalism
playerData.add(character[18]); // Excavation
playerData.add(character[19]); // Archery
playerData.add(character[20]); // Swords
playerData.add(character[21]); // Axes
playerData.add(character[22]); // Acrobatics
playerData.add(character[35]); // Fishing
// Cooldowns
playerData.add(null); // Taming
playerData.add(character[32]); // SuperBreaker
playerData.add(null); // Repair
playerData.add(character[28]); // Tree Feller
playerData.add(character[26]); // Beserk
playerData.add(character[29]); // Green Terra
playerData.add(character[27]); // Giga Drill Breaker
playerData.add(null); // Archery
playerData.add(character[30]); // Serrated Strikes
playerData.add(character[31]); // Skull Splitter
playerData.add(null); // Acrobatics
playerData.add(character[36]); // Blast Mining
playerData.add(character.length > 33 ? character[33] : null); // HudType
playerData.add(character.length > 38 ? character[38] : null); // MobHealthBar
} }
in.close();
} }
catch (Exception e) {
e.printStackTrace();
}
return playerData;
} }
public boolean convert(String[] character) throws Exception { public PlayerProfile loadPlayerProfile(String playerName, boolean create) {
// Not implemented BufferedReader in = null;
return false; String usersFilePath = mcMMO.getUsersFilePath();
synchronized (fileWritingLock) {
try {
// Open the user file
in = new BufferedReader(new FileReader(usersFilePath));
String line;
while ((line = in.readLine()) != null) {
// Find if the line contains the player we want.
String[] character = line.split(":");
if (!character[0].equalsIgnoreCase(playerName)) {
continue;
}
PlayerProfile p = loadFromLine(character);
in.close();
return p;
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
tryClose(in);
}
}
if (create) {
newUser(playerName);
return new PlayerProfile(playerName, true);
}
return new PlayerProfile(playerName);
}
public void convertUsers(DatabaseManager destination) {
BufferedReader in = null;
String usersFilePath = mcMMO.getUsersFilePath();
synchronized (fileWritingLock) {
try {
// Open the user file
in = new BufferedReader(new FileReader(usersFilePath));
String line;
while ((line = in.readLine()) != null) {
String[] character = line.split(":");
try {
destination.saveUser(loadFromLine(character));
}
catch (Exception e) {
e.printStackTrace();
}
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
tryClose(in);
}
}
} }
public boolean checkConnected() { public boolean checkConnected() {
// Not implemented // Not implemented
return false; return true;
}
public List<String> getStoredUsers() {
ArrayList<String> users = new ArrayList<String>();
BufferedReader in = null;
String usersFilePath = mcMMO.getUsersFilePath();
synchronized (fileWritingLock) {
try {
// Open the user file
in = new BufferedReader(new FileReader(usersFilePath));
String line;
while ((line = in.readLine()) != null) {
String[] character = line.split(":");
users.add(character[0]);
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
tryClose(in);
}
}
return users;
} }
/** /**
* Update the leader boards. * Update the leader boards.
*/ */
private void updateLeaderboards() { private void updateLeaderboards() {
// Only update FFS leaderboards every 10 minutes.. this puts a lot of strain on the server (depending on the size of the database) and should not be done frequently // Only update FFS leaderboards every 10 minutes.. this puts a lot of strain on the server (depending on the size of the database) and should not be done frequently
if (System.currentTimeMillis() < lastUpdate + UPDATE_WAIT_TIME) { if (System.currentTimeMillis() < lastUpdate + UPDATE_WAIT_TIME) {
@@ -402,43 +479,50 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
List<PlayerStat> taming = new ArrayList<PlayerStat>(); List<PlayerStat> taming = new ArrayList<PlayerStat>();
List<PlayerStat> fishing = new ArrayList<PlayerStat>(); List<PlayerStat> fishing = new ArrayList<PlayerStat>();
BufferedReader in = null;
// Read from the FlatFile database and fill our arrays with information // Read from the FlatFile database and fill our arrays with information
try { synchronized (fileWritingLock) {
BufferedReader in = new BufferedReader(new FileReader(usersFilePath)); try {
String line = ""; in = new BufferedReader(new FileReader(usersFilePath));
ArrayList<String> players = new ArrayList<String>(); String line = "";
ArrayList<String> players = new ArrayList<String>();
while ((line = in.readLine()) != null) { while ((line = in.readLine()) != null) {
String[] data = line.split(":"); String[] data = line.split(":");
String playerName = data[0]; String playerName = data[0];
int powerLevel = 0; int powerLevel = 0;
// Prevent the same player from being added multiple times (I'd like to note that this shouldn't happen...) // Prevent the same player from being added multiple times (I'd like to note that this shouldn't happen...)
if (players.contains(playerName)) { if (players.contains(playerName)) {
continue; continue;
}
players.add(playerName);
Map<SkillType, Integer> skills = getSkillMapFromLine(data);
powerLevel += putStat(acrobatics, playerName, skills.get(SkillType.ACROBATICS));
powerLevel += putStat(archery, playerName, skills.get(SkillType.ARCHERY));
powerLevel += putStat(axes, playerName, skills.get(SkillType.AXES));
powerLevel += putStat(excavation, playerName, skills.get(SkillType.EXCAVATION));
powerLevel += putStat(fishing, playerName, skills.get(SkillType.FISHING));
powerLevel += putStat(herbalism, playerName, skills.get(SkillType.HERBALISM));
powerLevel += putStat(mining, playerName, skills.get(SkillType.MINING));
powerLevel += putStat(repair, playerName, skills.get(SkillType.REPAIR));
powerLevel += putStat(swords, playerName, skills.get(SkillType.SWORDS));
powerLevel += putStat(taming, playerName, skills.get(SkillType.TAMING));
powerLevel += putStat(unarmed, playerName, skills.get(SkillType.UNARMED));
powerLevel += putStat(woodcutting, playerName, skills.get(SkillType.WOODCUTTING));
putStat(powerLevels, playerName, powerLevel);
} }
players.add(playerName);
powerLevel += loadStat(mining, playerName, data, 1);
powerLevel += loadStat(woodcutting, playerName, data, 5);
powerLevel += loadStat(repair, playerName, data, 7);
powerLevel += loadStat(unarmed, playerName, data, 8);
powerLevel += loadStat(herbalism, playerName, data, 9);
powerLevel += loadStat(excavation, playerName, data, 10);
powerLevel += loadStat(archery, playerName, data, 11);
powerLevel += loadStat(swords, playerName, data, 12);
powerLevel += loadStat(axes, playerName, data, 13);
powerLevel += loadStat(acrobatics, playerName, data, 14);
powerLevel += loadStat(taming, playerName, data, 24);
powerLevel += loadStat(fishing, playerName, data, 34);
powerLevels.add(new PlayerStat(playerName, powerLevel));
} }
in.close(); catch (Exception e) {
} mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
catch (Exception e) { }
mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString()); finally {
tryClose(in);
}
} }
SkillComparator c = new SkillComparator(); SkillComparator c = new SkillComparator();
@@ -471,8 +555,44 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
playerStatHash.put(SkillType.FISHING, fishing); playerStatHash.put(SkillType.FISHING, fishing);
} }
private void createDatabase() { /**
* Checks that the file is present and valid
*/
private void checkStructure() {
if (usersFile.exists()) { if (usersFile.exists()) {
BufferedReader in = null;
FileWriter out = null;
String usersFilePath = mcMMO.getUsersFilePath();
synchronized (fileWritingLock) {
try {
in = new BufferedReader(new FileReader(usersFilePath));
StringBuilder writer = new StringBuilder();
String line = "";
while ((line = in.readLine()) != null) {
String[] character = line.split(":");
// If they're valid, rewrite them to the file.
if (character.length >= 37) {
writer.append(line).append("\r\n");
} else {
// Placeholder, repair row if needed (I.E. when new skills are added and such)
}
}
// Write the new file
out = new FileWriter(usersFilePath);
out.write(writer.toString());
}
catch (IOException e) {
mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
}
finally {
tryClose(in);
tryClose(out);
}
}
return; return;
} }
@@ -487,6 +607,15 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
} }
} }
private void tryClose(Closeable c) {
if (c == null) return;
try {
c.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private Integer getPlayerRank(String playerName, List<PlayerStat> statsList) { private Integer getPlayerRank(String playerName, List<PlayerStat> statsList) {
if (statsList == null) { if (statsList == null) {
return null; return null;
@@ -505,14 +634,8 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
return null; return null;
} }
private int loadStat(List<PlayerStat> statList, String playerName, String[] data, int dataIndex) { private int putStat(List<PlayerStat> statList, String playerName, int statValue) {
if (data.length <= dataIndex) {
return 0;
}
int statValue = Integer.parseInt(data[dataIndex]);
statList.add(new PlayerStat(playerName, statValue)); statList.add(new PlayerStat(playerName, statValue));
return statValue; return statValue;
} }
@@ -522,4 +645,75 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
return (o2.statVal - o1.statVal); return (o2.statVal - o1.statVal);
} }
} }
private PlayerProfile loadFromLine(String[] character) throws Exception {
Map<SkillType, Integer> skills = getSkillMapFromLine(character); // Skill levels
Map<SkillType, Float> skillsXp = new HashMap<SkillType, Float>(); // Skill & XP
Map<AbilityType, Integer> skillsDATS = new HashMap<AbilityType, Integer>(); // Ability & Cooldown
HudType hudType;
MobHealthbarType mobHealthbarType;
// TODO on updates, put new values in a try{} ?
skillsXp.put(SkillType.TAMING, (float) Integer.valueOf(character[25]));
skillsXp.put(SkillType.MINING, (float) Integer.valueOf(character[4]));
skillsXp.put(SkillType.REPAIR, (float) Integer.valueOf(character[15]));
skillsXp.put(SkillType.WOODCUTTING, (float) Integer.valueOf(character[6]));
skillsXp.put(SkillType.UNARMED, (float) Integer.valueOf(character[16]));
skillsXp.put(SkillType.HERBALISM, (float) Integer.valueOf(character[17]));
skillsXp.put(SkillType.EXCAVATION, (float) Integer.valueOf(character[18]));
skillsXp.put(SkillType.ARCHERY, (float) Integer.valueOf(character[19]));
skillsXp.put(SkillType.SWORDS, (float) Integer.valueOf(character[20]));
skillsXp.put(SkillType.AXES, (float) Integer.valueOf(character[21]));
skillsXp.put(SkillType.ACROBATICS, (float) Integer.valueOf(character[22]));
skillsXp.put(SkillType.FISHING, (float) Integer.valueOf(character[35]));
// Taming - Unused
skillsDATS.put(AbilityType.SUPER_BREAKER, Integer.valueOf(character[32]));
// Repair - Unused
skillsDATS.put(AbilityType.TREE_FELLER, Integer.valueOf(character[28]));
skillsDATS.put(AbilityType.BERSERK, Integer.valueOf(character[26]));
skillsDATS.put(AbilityType.GREEN_TERRA, Integer.valueOf(character[29]));
skillsDATS.put(AbilityType.GIGA_DRILL_BREAKER, Integer.valueOf(character[27]));
// Archery - Unused
skillsDATS.put(AbilityType.SERRATED_STRIKES, Integer.valueOf(character[30]));
skillsDATS.put(AbilityType.SKULL_SPLITTER, Integer.valueOf(character[31]));
// Acrobatics - Unused
skillsDATS.put(AbilityType.BLAST_MINING, Integer.valueOf(character[36]));
try {
hudType = HudType.valueOf(character[33]);
}
catch (Exception e) {
hudType = HudType.STANDARD; // Shouldn't happen unless database is being tampered with
}
try {
mobHealthbarType = MobHealthbarType.valueOf(character[38]);
}
catch (Exception e) {
mobHealthbarType = Config.getInstance().getMobHealthbarDefault();
}
return new PlayerProfile(character[0], skills, skillsXp, skillsDATS, hudType, mobHealthbarType);
}
private Map<SkillType, Integer> getSkillMapFromLine(String[] character) {
Map<SkillType, Integer> skills = new HashMap<SkillType, Integer>(); // Skill & Level
skills.put(SkillType.TAMING, Integer.valueOf(character[24]));
skills.put(SkillType.MINING, Integer.valueOf(character[1]));
skills.put(SkillType.REPAIR, Integer.valueOf(character[7]));
skills.put(SkillType.WOODCUTTING, Integer.valueOf(character[5]));
skills.put(SkillType.UNARMED, Integer.valueOf(character[8]));
skills.put(SkillType.HERBALISM, Integer.valueOf(character[9]));
skills.put(SkillType.EXCAVATION, Integer.valueOf(character[10]));
skills.put(SkillType.ARCHERY, Integer.valueOf(character[11]));
skills.put(SkillType.SWORDS, Integer.valueOf(character[12]));
skills.put(SkillType.AXES, Integer.valueOf(character[13]));
skills.put(SkillType.ACROBATICS, Integer.valueOf(character[14]));
skills.put(SkillType.FISHING, Integer.valueOf(character[34]));
return skills;
}
} }

View File

@@ -5,12 +5,14 @@ import java.sql.DriverManager;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.logging.Level;
import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.config.Config; import com.gmail.nossr50.config.Config;
@@ -23,7 +25,6 @@ import com.gmail.nossr50.datatypes.skills.SkillType;
import com.gmail.nossr50.datatypes.spout.huds.HudType; import com.gmail.nossr50.datatypes.spout.huds.HudType;
import com.gmail.nossr50.runnables.database.SQLReconnectTask; import com.gmail.nossr50.runnables.database.SQLReconnectTask;
import com.gmail.nossr50.util.Misc; import com.gmail.nossr50.util.Misc;
import com.gmail.nossr50.util.StringUtils;
public final class SQLDatabaseManager implements DatabaseManager { public final class SQLDatabaseManager implements DatabaseManager {
private String connectionString; private String connectionString;
@@ -50,7 +51,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
protected SQLDatabaseManager() { protected SQLDatabaseManager() {
checkConnected(); checkConnected();
createStructure(); checkStructure();
} }
public void purgePowerlessUsers() { public void purgePowerlessUsers() {
@@ -102,7 +103,16 @@ public final class SQLDatabaseManager implements DatabaseManager {
} }
public void saveUser(PlayerProfile profile) { public void saveUser(PlayerProfile profile) {
checkConnected();
int userId = readId(profile.getPlayerName()); int userId = readId(profile.getPlayerName());
if (userId == -1) {
newUser(profile.getPlayerName());
userId = readId(profile.getPlayerName());
if (userId == -1) {
mcMMO.p.getLogger().log(Level.WARNING, "Failed to save user " + profile.getPlayerName());
return;
}
}
MobHealthbarType mobHealthbarType = profile.getMobHealthbarType(); MobHealthbarType mobHealthbarType = profile.getMobHealthbarType();
HudType hudType = profile.getHudType(); HudType hudType = profile.getHudType();
@@ -292,12 +302,13 @@ public final class SQLDatabaseManager implements DatabaseManager {
PreparedStatement statement = null; PreparedStatement statement = null;
try { try {
statement = connection.prepareStatement("INSERT INTO " + tablePrefix + "users (user, lastlogin) VALUES (?, ?)"); statement = connection.prepareStatement("INSERT INTO " + tablePrefix + "users (user, lastlogin) VALUES (?, ?)", Statement.RETURN_GENERATED_KEYS);
statement.setString(1, playerName); statement.setString(1, playerName);
statement.setLong(2, System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR); statement.setLong(2, System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR);
statement.execute(); statement.execute();
writeMissingRows(readId(playerName)); int id = readId(playerName);
writeMissingRows(id);
} }
catch (SQLException ex) { catch (SQLException ex) {
printErrors(ex); printErrors(ex);
@@ -314,8 +325,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
} }
} }
public List<String> loadPlayerData(String playerName) { public PlayerProfile loadPlayerProfile(String playerName, boolean create) {
List<String> playerData = null;
PreparedStatement statement = null; PreparedStatement statement = null;
try { try {
@@ -324,7 +334,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
+ "s.taming, s.mining, s.repair, s.woodcutting, s.unarmed, s.herbalism, s.excavation, s.archery, s.swords, s.axes, s.acrobatics, s.fishing, " + "s.taming, s.mining, s.repair, s.woodcutting, s.unarmed, s.herbalism, s.excavation, s.archery, s.swords, s.axes, s.acrobatics, s.fishing, "
+ "e.taming, e.mining, e.repair, e.woodcutting, e.unarmed, e.herbalism, e.excavation, e.archery, e.swords, e.axes, e.acrobatics, e.fishing, " + "e.taming, e.mining, e.repair, e.woodcutting, e.unarmed, e.herbalism, e.excavation, e.archery, e.swords, e.axes, e.acrobatics, e.fishing, "
+ "c.taming, c.mining, c.repair, c.woodcutting, c.unarmed, c.herbalism, c.excavation, c.archery, c.swords, c.axes, c.acrobatics, c.blast_mining, " + "c.taming, c.mining, c.repair, c.woodcutting, c.unarmed, c.herbalism, c.excavation, c.archery, c.swords, c.axes, c.acrobatics, c.blast_mining, "
+ "h.hudtype, h.mobhealthbar, NOW() " + "h.hudtype, h.mobhealthbar "
+ "FROM " + tablePrefix + "users u " + "FROM " + tablePrefix + "users u "
+ "JOIN " + tablePrefix + "skills s ON (u.id = s.user_id) " + "JOIN " + tablePrefix + "skills s ON (u.id = s.user_id) "
+ "JOIN " + tablePrefix + "experience e ON (u.id = e.user_id) " + "JOIN " + tablePrefix + "experience e ON (u.id = e.user_id) "
@@ -333,22 +343,17 @@ public final class SQLDatabaseManager implements DatabaseManager {
+ "WHERE u.user = ?"); + "WHERE u.user = ?");
statement.setString(1, playerName); statement.setString(1, playerName);
playerData = readRow(statement); ResultSet result = statement.executeQuery();
if (playerData == null || playerData.size() == 0) { if (result.next()) {
int userId = readId(playerName); try {
PlayerProfile ret = loadFromResult(playerName, result);
// Check if user doesn't exist result.close();
if (userId == 0) { return ret;
return playerData;
} }
catch (SQLException e) {}
// Write missing table rows
writeMissingRows(userId);
// Re-read data
playerData = loadPlayerData(playerName);
} }
result.close();
} }
catch (SQLException ex) { catch (SQLException ex) {
printErrors(ex); printErrors(ex);
@@ -363,108 +368,74 @@ public final class SQLDatabaseManager implements DatabaseManager {
} }
} }
} }
return playerData;
// Problem, nothing was returned
// First, read User Id - this is to check for orphans
int id = readId(playerName);
if (id == -1) {
// There is no such user
if (create) {
newUser(playerName);
return new PlayerProfile(playerName, true);
}
else {
return new PlayerProfile(playerName, false);
}
}
// There is such a user
writeMissingRows(id);
// Retry, and abort on re-failure
return loadPlayerProfile(playerName, false);
} }
public boolean convert(String[] data) throws Exception { public void convertUsers(DatabaseManager destination) {
String playerName = data[0]; PreparedStatement statement = null;
// Check for things we don't want put in the DB try {
if (playerName == null || playerName.equalsIgnoreCase("null") || playerName.length() > 16) { statement = connection.prepareStatement(
return false; "SELECT "
+ "s.taming, s.mining, s.repair, s.woodcutting, s.unarmed, s.herbalism, s.excavation, s.archery, s.swords, s.axes, s.acrobatics, s.fishing, "
+ "e.taming, e.mining, e.repair, e.woodcutting, e.unarmed, e.herbalism, e.excavation, e.archery, e.swords, e.axes, e.acrobatics, e.fishing, "
+ "c.taming, c.mining, c.repair, c.woodcutting, c.unarmed, c.herbalism, c.excavation, c.archery, c.swords, c.axes, c.acrobatics, c.blast_mining, "
+ "h.hudtype, h.mobhealthbar "
+ "FROM " + tablePrefix + "users u "
+ "JOIN " + tablePrefix + "skills s ON (u.id = s.user_id) "
+ "JOIN " + tablePrefix + "experience e ON (u.id = e.user_id) "
+ "JOIN " + tablePrefix + "cooldowns c ON (u.id = c.user_id) "
+ "JOIN " + tablePrefix + "huds h ON (u.id = h.user_id) "
+ "WHERE u.user = ?");
List<String> usernames = getStoredUsers();
ResultSet result = null;
for (String playerName : usernames) {
statement.setString(1, playerName);
try {
result = statement.executeQuery();
result.next();
destination.saveUser(loadFromResult(playerName, result));
result.close();
}
catch (SQLException e) {
// Ignore
}
}
}
catch (SQLException e) {
printErrors(e);
}
finally {
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
// Ignore
}
}
} }
String mining = (data.length > 1) ? data[1] : null;
String woodcutting = (data.length > 5) ? data[5] : null;
String repair = (data.length > 7) ? data[7] : null;
String unarmed = (data.length > 8) ? data[8] : null;
String herbalism = (data.length > 9) ? data[9] : null;
String excavation = (data.length > 10) ? data[10] : null;
String archery = (data.length > 11) ? data[11] : null;
String swords = (data.length > 12) ? data[12] : null;
String axes = (data.length > 13) ? data[13] : null;
String acrobatics = (data.length > 14) ? data[14] : null;
String taming = (data.length > 24) ? data[24] : null;
String fishing = (data.length > 34) ? data[34] : null;
String miningXP = (data.length > 4) ? data[4] : null;
String woodCuttingXP = (data.length > 6) ? data[6] : null;;
String repairXP = (data.length > 15) ? data[15] : null;
String unarmedXP = (data.length > 16) ? data[16] : null;
String herbalismXP = (data.length > 17) ? data[17] : null;
String excavationXP = (data.length > 18) ? data[18] : null;
String archeryXP = (data.length > 19) ? data[19] : null;
String swordsXP = (data.length > 20) ? data[20] : null;
String axesXP = (data.length > 21) ? data[21] : null;
String acrobaticsXP = (data.length > 22) ? data[22] : null;
String tamingXP = (data.length > 25) ? data[25] : null;
String fishingXP = (data.length > 35) ? data[35] : null;
String superBreakerCooldown = (data.length > 32) ? data[32] : null;
String treeFellerCooldown = (data.length > 28) ? data[28] : null;
String berserkCooldown = (data.length > 26) ? data[26] : null;
String greenTerraCooldown = (data.length > 29) ? data[29] : null;
String gigaDrillBreakerCooldown = (data.length > 27) ? data[27] : null;
String serratedStrikesCooldown = (data.length > 30) ? data[30] : null;
String skullSplitterCooldown = (data.length > 31) ? data[31] : null;
String blastMiningCooldown = (data.length > 36) ? data[36] : null;
String hudType = (data.length > 33) ? data[33] : null;
String mobHealthbarType = (data.length > 38 ? data[38] : null);
long lastLogin = mcMMO.p.getServer().getOfflinePlayer(playerName).getLastPlayed();
int id = readId(playerName); // Check to see if the user is in the DB
// Create the user if they don't exist
if (id == 0) {
newUser(playerName);
id = readId(playerName);
}
saveLogin(id, lastLogin);
saveIntegers(
"UPDATE " + tablePrefix + "skills SET "
+ " taming = ?, mining = ?, repair = ?, woodcutting = ?"
+ ", unarmed = ?, herbalism = ?, excavation = ?"
+ ", archery = ?, swords = ?, axes = ?, acrobatics = ?"
+ ", fishing = ? WHERE user_id = ?",
StringUtils.getInt(taming), StringUtils.getInt(mining),
StringUtils.getInt(repair), StringUtils.getInt(woodcutting),
StringUtils.getInt(unarmed), StringUtils.getInt(herbalism),
StringUtils.getInt(excavation), StringUtils.getInt(archery),
StringUtils.getInt(swords), StringUtils.getInt(axes),
StringUtils.getInt(acrobatics), StringUtils.getInt(fishing),
id);
saveIntegers(
"UPDATE " + tablePrefix + "experience SET "
+ " taming = ?, mining = ?, repair = ?, woodcutting = ?"
+ ", unarmed = ?, herbalism = ?, excavation = ?"
+ ", archery = ?, swords = ?, axes = ?, acrobatics = ?"
+ ", fishing = ? WHERE user_id = ?",
StringUtils.getInt(tamingXP), StringUtils.getInt(miningXP),
StringUtils.getInt(repairXP), StringUtils.getInt(woodCuttingXP),
StringUtils.getInt(unarmedXP), StringUtils.getInt(herbalismXP),
StringUtils.getInt(excavationXP), StringUtils.getInt(archeryXP),
StringUtils.getInt(swordsXP), StringUtils.getInt(axesXP),
StringUtils.getInt(acrobaticsXP), StringUtils.getInt(fishingXP),
id);
saveLongs(
"UPDATE " + tablePrefix + "cooldowns SET "
+ " taming = ?, mining = ?, repair = ?, woodcutting = ?"
+ ", unarmed = ?, herbalism = ?, excavation = ?"
+ ", archery = ?, swords = ?, axes = ?, acrobatics = ?"
+ ", blast_mining = ? WHERE user_id = ?",
id,
StringUtils.getLong(null), StringUtils.getLong(superBreakerCooldown),
StringUtils.getLong(null), StringUtils.getInt(treeFellerCooldown),
StringUtils.getLong(berserkCooldown), StringUtils.getLong(greenTerraCooldown),
StringUtils.getLong(gigaDrillBreakerCooldown), StringUtils.getLong(null),
StringUtils.getLong(serratedStrikesCooldown), StringUtils.getLong(skullSplitterCooldown),
StringUtils.getLong(null), StringUtils.getLong(blastMiningCooldown));
saveHuds(id, hudType, mobHealthbarType);
return true;
} }
/** /**
* Check connection status and re-establish if dead or stale. * Check connection status and re-establish if dead or stale.
* *
@@ -555,9 +526,35 @@ public final class SQLDatabaseManager implements DatabaseManager {
return false; return false;
} }
public List<String> getStoredUsers() {
ArrayList<String> users = new ArrayList<String>();
Statement stmt = null;
try {
stmt = connection.createStatement();
ResultSet result = stmt.executeQuery("SELECT user FROM " + tablePrefix + "users");
while (result.next()) {
users.add(result.getString("user"));
}
result.close();
}
catch (SQLException e) {
printErrors(e);
}
finally {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
// Ignore
}
}
}
return users;
}
/** /**
* Attempt to connect to the mySQL database. * Attempt to connect to the mySQL database.
*/ */
private void connect() { private void connect() {
connectionString = "jdbc:mysql://" + Config.getInstance().getMySQLServerName() + ":" + Config.getInstance().getMySQLServerPort() + "/" + Config.getInstance().getMySQLDatabaseName(); connectionString = "jdbc:mysql://" + Config.getInstance().getMySQLServerName() + ":" + Config.getInstance().getMySQLServerPort() + "/" + Config.getInstance().getMySQLDatabaseName();
@@ -592,9 +589,9 @@ public final class SQLDatabaseManager implements DatabaseManager {
} }
/** /**
* Attempt to create the database structure. * Checks that the database structure is present and correct
*/ */
private void createStructure() { private void checkStructure() {
write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "users` (" write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "users` ("
+ "`id` int(10) unsigned NOT NULL AUTO_INCREMENT," + "`id` int(10) unsigned NOT NULL AUTO_INCREMENT,"
+ "`user` varchar(40) NOT NULL," + "`user` varchar(40) NOT NULL,"
@@ -662,10 +659,10 @@ public final class SQLDatabaseManager implements DatabaseManager {
} }
/** /**
* Check database structure for missing values. * Check database structure for missing values.
* *
* @param update Type of data to check updates for * @param update Type of data to check updates for
*/ */
private void checkDatabaseStructure(DatabaseUpdateType update) { private void checkDatabaseStructure(DatabaseUpdateType update) {
String sql = ""; String sql = "";
@@ -793,11 +790,11 @@ public final class SQLDatabaseManager implements DatabaseManager {
} }
/** /**
* Attempt to write the SQL query. * Attempt to write the SQL query.
* *
* @param sql Query to write. * @param sql Query to write.
* @return true if the query was successfully written, false otherwise. * @return true if the query was successfully written, false otherwise.
*/ */
private boolean write(String sql) { private boolean write(String sql) {
if (!checkConnected()) { if (!checkConnected()) {
return false; return false;
@@ -828,11 +825,11 @@ public final class SQLDatabaseManager implements DatabaseManager {
} }
/** /**
* Returns the number of rows affected by either a DELETE or UPDATE query * Returns the number of rows affected by either a DELETE or UPDATE query
* *
* @param sql SQL query to execute * @param sql SQL query to execute
* @return the number of rows affected * @return the number of rows affected
*/ */
private int update(String sql) { private int update(String sql) {
int rows = 0; int rows = 0;
@@ -862,11 +859,11 @@ public final class SQLDatabaseManager implements DatabaseManager {
} }
/** /**
* Read SQL query. * Read SQL query.
* *
* @param sql SQL query to read * @param sql SQL query to read
* @return the rows in this SQL query * @return the rows in this SQL query
*/ */
private HashMap<Integer, ArrayList<String>> read(String sql) { private HashMap<Integer, ArrayList<String>> read(String sql) {
HashMap<Integer, ArrayList<String>> rows = new HashMap<Integer, ArrayList<String>>(); HashMap<Integer, ArrayList<String>> rows = new HashMap<Integer, ArrayList<String>>();
@@ -906,47 +903,14 @@ public final class SQLDatabaseManager implements DatabaseManager {
return rows; return rows;
} }
private ArrayList<String> readRow(PreparedStatement statement) {
ArrayList<String> playerData = new ArrayList<String>();
if (checkConnected()) {
ResultSet resultSet = null;
try {
resultSet = statement.executeQuery();
if (resultSet.next()) {
for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
playerData.add(resultSet.getString(i));
}
}
}
catch (SQLException ex) {
printErrors(ex);
}
finally {
if (statement != null) {
try {
statement.close();
}
catch (SQLException e) {
// Ignore
}
}
}
}
return playerData;
}
/** /**
* Get the Integer. Only return first row / first field. * Get the Integer. Only return first row / first field.
* *
* @param sql SQL query to execute * @param sql SQL query to execute
* @return the value in the first row / first field * @return the value in the first row / first field
*/ */
private int readInt(PreparedStatement statement) { private int readInt(PreparedStatement statement) {
int result = 0; int result = -1;
if (checkConnected()) { if (checkConnected()) {
ResultSet resultSet = null; ResultSet resultSet = null;
@@ -1078,8 +1042,14 @@ public final class SQLDatabaseManager implements DatabaseManager {
} }
} }
/**
* Retrieve the database id for a player
*
* @param playerName The name of the user to retrieve the id for
* @return the requested id or -1 if not found
*/
private int readId(String playerName) { private int readId(String playerName) {
int id = 0; int id = -1;
try { try {
PreparedStatement statement = connection.prepareStatement("SELECT id FROM " + tablePrefix + "users WHERE user = ?"); PreparedStatement statement = connection.prepareStatement("SELECT id FROM " + tablePrefix + "users WHERE user = ?");
@@ -1142,6 +1112,74 @@ public final class SQLDatabaseManager implements DatabaseManager {
} }
} }
private PlayerProfile loadFromResult(String playerName, ResultSet result) throws SQLException {
Map<SkillType, Integer> skills = new HashMap<SkillType, Integer>(); // Skill & Level
Map<SkillType, Float> skillsXp = new HashMap<SkillType, Float>(); // Skill & XP
Map<AbilityType, Integer> skillsDATS = new HashMap<AbilityType, Integer>(); // Ability & Cooldown
HudType hudType;
MobHealthbarType mobHealthbarType;
final int OFFSET_SKILLS = 0; // TODO update these numbers when the query changes (a new skill is added)
final int OFFSET_XP = 12;
final int OFFSET_DATS = 24;
final int OFFSET_OTHER = 36;
skills.put(SkillType.TAMING, result.getInt(OFFSET_SKILLS + 1));
skills.put(SkillType.MINING, result.getInt(OFFSET_SKILLS + 2));
skills.put(SkillType.REPAIR, result.getInt(OFFSET_SKILLS + 3));
skills.put(SkillType.WOODCUTTING, result.getInt(OFFSET_SKILLS + 4));
skills.put(SkillType.UNARMED, result.getInt(OFFSET_SKILLS + 5));
skills.put(SkillType.HERBALISM, result.getInt(OFFSET_SKILLS + 6));
skills.put(SkillType.EXCAVATION, result.getInt(OFFSET_SKILLS + 7));
skills.put(SkillType.ARCHERY, result.getInt(OFFSET_SKILLS + 8));
skills.put(SkillType.SWORDS, result.getInt(OFFSET_SKILLS + 9));
skills.put(SkillType.AXES, result.getInt(OFFSET_SKILLS + 10));
skills.put(SkillType.ACROBATICS, result.getInt(OFFSET_SKILLS + 11));
skills.put(SkillType.FISHING, result.getInt(OFFSET_SKILLS + 12));
skillsXp.put(SkillType.TAMING, result.getFloat(OFFSET_XP + 1));
skillsXp.put(SkillType.MINING, result.getFloat(OFFSET_XP + 2));
skillsXp.put(SkillType.REPAIR, result.getFloat(OFFSET_XP + 3));
skillsXp.put(SkillType.WOODCUTTING, result.getFloat(OFFSET_XP + 4));
skillsXp.put(SkillType.UNARMED, result.getFloat(OFFSET_XP + 5));
skillsXp.put(SkillType.HERBALISM, result.getFloat(OFFSET_XP + 6));
skillsXp.put(SkillType.EXCAVATION, result.getFloat(OFFSET_XP + 7));
skillsXp.put(SkillType.ARCHERY, result.getFloat(OFFSET_XP + 8));
skillsXp.put(SkillType.SWORDS, result.getFloat(OFFSET_XP + 9));
skillsXp.put(SkillType.AXES, result.getFloat(OFFSET_XP + 10));
skillsXp.put(SkillType.ACROBATICS, result.getFloat(OFFSET_XP + 11));
skillsXp.put(SkillType.FISHING, result.getFloat(OFFSET_XP + 12));
// Taming - Unused - result.getInt(OFFSET_DATS + 1)
skillsDATS.put(AbilityType.SUPER_BREAKER, result.getInt(OFFSET_DATS + 2));
// Repair - Unused - result.getInt(OFFSET_DATS + 3)
skillsDATS.put(AbilityType.TREE_FELLER, result.getInt(OFFSET_DATS + 4));
skillsDATS.put(AbilityType.BERSERK, result.getInt(OFFSET_DATS + 5));
skillsDATS.put(AbilityType.GREEN_TERRA, result.getInt(OFFSET_DATS + 6));
skillsDATS.put(AbilityType.GIGA_DRILL_BREAKER, result.getInt(OFFSET_DATS + 7));
// Archery - Unused - result.getInt(OFFSET_DATS + 8)
skillsDATS.put(AbilityType.SERRATED_STRIKES, result.getInt(OFFSET_DATS + 9));
skillsDATS.put(AbilityType.SKULL_SPLITTER, result.getInt(OFFSET_DATS + 10));
// Acrobatics - Unused - result.getInt(OFFSET_DATS + 11)
skillsDATS.put(AbilityType.BLAST_MINING, result.getInt(OFFSET_DATS + 12));
try {
hudType = HudType.valueOf(result.getString(OFFSET_OTHER + 1));
}
catch (Exception e) {
hudType = HudType.STANDARD; // Shouldn't happen unless database is being tampered with
}
try {
mobHealthbarType = MobHealthbarType.valueOf(result.getString(OFFSET_OTHER + 2));
}
catch (Exception e) {
mobHealthbarType = Config.getInstance().getMobHealthbarDefault();
}
return new PlayerProfile(playerName, skills, skillsXp, skillsDATS, hudType, mobHealthbarType);
}
private void printErrors(SQLException ex) { private void printErrors(SQLException ex) {
mcMMO.p.getLogger().severe("SQLException: " + ex.getMessage()); mcMMO.p.getLogger().severe("SQLException: " + ex.getMessage());
mcMMO.p.getLogger().severe("SQLState: " + ex.getSQLState()); mcMMO.p.getLogger().severe("SQLState: " + ex.getSQLState());

View File

@@ -89,10 +89,14 @@ public class McMMOPlayer {
private boolean isUsingUnarmed; private boolean isUsingUnarmed;
public McMMOPlayer(Player player) { public McMMOPlayer(Player player) {
this(player, mcMMO.getDatabaseManager().loadPlayerProfile(player.getName(), true));
}
public McMMOPlayer(Player player, PlayerProfile profile) {
String playerName = player.getName(); String playerName = player.getName();
this.player = player; this.player = player;
profile = new PlayerProfile(playerName, true); this.profile = profile;
party = PartyManager.getPlayerParty(playerName); party = PartyManager.getPlayerParty(playerName);
/* /*

View File

@@ -1,7 +1,6 @@
package com.gmail.nossr50.datatypes.player; package com.gmail.nossr50.datatypes.player;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -32,7 +31,7 @@ public class PlayerProfile {
private final Map<SkillType, Float> skillsXp = new HashMap<SkillType, Float>(); // Skill & XP private final Map<SkillType, Float> skillsXp = new HashMap<SkillType, Float>(); // Skill & XP
private final Map<AbilityType, Integer> skillsDATS = new HashMap<AbilityType, Integer>(); // Ability & Cooldown private final Map<AbilityType, Integer> skillsDATS = new HashMap<AbilityType, Integer>(); // Ability & Cooldown
public PlayerProfile(String playerName, boolean addNew) { public PlayerProfile(String playerName) {
this.playerName = playerName; this.playerName = playerName;
hudType = mcMMO.isSpoutEnabled() ? SpoutConfig.getInstance().getDefaultHudType() : HudType.DISABLED; hudType = mcMMO.isSpoutEnabled() ? SpoutConfig.getInstance().getDefaultHudType() : HudType.DISABLED;
@@ -46,11 +45,27 @@ public class PlayerProfile {
skills.put(skillType, 0); skills.put(skillType, 0);
skillsXp.put(skillType, 0F); skillsXp.put(skillType, 0F);
} }
}
if (!loadPlayer() && addNew) { public PlayerProfile(String playerName, boolean isLoaded) {
mcMMO.getDatabaseManager().newUser(playerName); this(playerName);
loaded = true; this.loaded = isLoaded;
} }
/**
* Calling this constructor is considered loading the profile.
*/
public PlayerProfile(String playerName, Map<SkillType, Integer> argSkills, Map<SkillType, Float> argSkillsXp, Map<AbilityType, Integer> argSkillsDats, HudType hudType, MobHealthbarType mobHealthbarType) {
this(playerName, true);
this.hudType = hudType;
this.mobHealthbarType = mobHealthbarType;
this.skills.putAll(argSkills);
this.skillsXp.putAll(argSkillsXp);
this.skillsDATS.putAll(argSkillsDats);
loaded = true;
} }
public void save() { public void save() {
@@ -254,68 +269,4 @@ public class PlayerProfile {
return sum / parents.size(); return sum / parents.size();
} }
private boolean loadPlayer() {
List<String> playerData = mcMMO.getDatabaseManager().loadPlayerData(playerName);
if (playerData == null || playerData.isEmpty()) {
return false;
}
skills.put(SkillType.TAMING, Integer.valueOf(playerData.get(0)));
skills.put(SkillType.MINING, Integer.valueOf(playerData.get(1)));
skills.put(SkillType.REPAIR, Integer.valueOf(playerData.get(2)));
skills.put(SkillType.WOODCUTTING, Integer.valueOf(playerData.get(3)));
skills.put(SkillType.UNARMED, Integer.valueOf(playerData.get(4)));
skills.put(SkillType.HERBALISM, Integer.valueOf(playerData.get(5)));
skills.put(SkillType.EXCAVATION, Integer.valueOf(playerData.get(6)));
skills.put(SkillType.ARCHERY, Integer.valueOf(playerData.get(7)));
skills.put(SkillType.SWORDS, Integer.valueOf(playerData.get(8)));
skills.put(SkillType.AXES, Integer.valueOf(playerData.get(9)));
skills.put(SkillType.ACROBATICS, Integer.valueOf(playerData.get(10)));
skills.put(SkillType.FISHING, Integer.valueOf(playerData.get(11)));
skillsXp.put(SkillType.TAMING, (float) Integer.valueOf(playerData.get(12)));
skillsXp.put(SkillType.MINING, (float) Integer.valueOf(playerData.get(13)));
skillsXp.put(SkillType.REPAIR, (float) Integer.valueOf(playerData.get(14)));
skillsXp.put(SkillType.WOODCUTTING, (float) Integer.valueOf(playerData.get(15)));
skillsXp.put(SkillType.UNARMED, (float) Integer.valueOf(playerData.get(16)));
skillsXp.put(SkillType.HERBALISM, (float) Integer.valueOf(playerData.get(17)));
skillsXp.put(SkillType.EXCAVATION, (float) Integer.valueOf(playerData.get(18)));
skillsXp.put(SkillType.ARCHERY, (float) Integer.valueOf(playerData.get(19)));
skillsXp.put(SkillType.SWORDS, (float) Integer.valueOf(playerData.get(20)));
skillsXp.put(SkillType.AXES, (float) Integer.valueOf(playerData.get(21)));
skillsXp.put(SkillType.ACROBATICS, (float) Integer.valueOf(playerData.get(22)));
skillsXp.put(SkillType.FISHING, (float) Integer.valueOf(playerData.get(23)));
// Taming 24 - Unused
skillsDATS.put(AbilityType.SUPER_BREAKER, Integer.valueOf(playerData.get(25)));
// Repair 26 - Unused
skillsDATS.put(AbilityType.TREE_FELLER, Integer.valueOf(playerData.get(27)));
skillsDATS.put(AbilityType.BERSERK, Integer.valueOf(playerData.get(28)));
skillsDATS.put(AbilityType.GREEN_TERRA, Integer.valueOf(playerData.get(29)));
skillsDATS.put(AbilityType.GIGA_DRILL_BREAKER, Integer.valueOf(playerData.get(30)));
// Archery 31 - Unused
skillsDATS.put(AbilityType.SERRATED_STRIKES, Integer.valueOf(playerData.get(32)));
skillsDATS.put(AbilityType.SKULL_SPLITTER, Integer.valueOf(playerData.get(33)));
// Acrobatics 34 - Unused
skillsDATS.put(AbilityType.BLAST_MINING, Integer.valueOf(playerData.get(35)));
try {
hudType = HudType.valueOf(playerData.get(36));
}
catch (Exception e) {
hudType = HudType.STANDARD; // Shouldn't happen unless database is being tampered with
}
try {
mobHealthbarType = MobHealthbarType.valueOf(playerData.get(37));
}
catch (Exception e) {
mobHealthbarType = Config.getInstance().getMobHealthbarDefault();
}
loaded = true;
return true;
}
} }

View File

@@ -338,7 +338,7 @@ public class BlockListener implements Listener {
event.setInstaBreak(true); event.setInstaBreak(true);
player.playSound(block.getLocation(), Sound.ITEM_PICKUP, Misc.POP_VOLUME, Misc.getPopPitch()); player.playSound(block.getLocation(), Sound.ITEM_PICKUP, Misc.POP_VOLUME, Misc.getPopPitch());
} }
else if (Permissions.blockCracker(player) && SkillUtils.triggerCheck(player, block, AbilityType.BLOCK_CRACKER)) { else if (mcMMOPlayer.getUnarmedManager().canUseBlockCracker() && SkillUtils.triggerCheck(player, block, AbilityType.BLOCK_CRACKER)) {
if (mcMMOPlayer.getUnarmedManager().blockCrackerCheck(blockState)) { if (mcMMOPlayer.getUnarmedManager().blockCrackerCheck(blockState)) {
blockState.update(); blockState.update();
} }

View File

@@ -15,12 +15,14 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
import org.bukkit.event.player.PlayerChangedWorldEvent; import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.event.player.PlayerFishEvent; import org.bukkit.event.player.PlayerFishEvent;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerPickupItemEvent; import org.bukkit.event.player.PlayerPickupItemEvent;
import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.event.player.PlayerRespawnEvent;
@@ -36,6 +38,7 @@ import com.gmail.nossr50.datatypes.party.Party;
import com.gmail.nossr50.datatypes.player.McMMOPlayer; import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.skills.AbilityType; import com.gmail.nossr50.datatypes.skills.AbilityType;
import com.gmail.nossr50.datatypes.skills.SkillType; import com.gmail.nossr50.datatypes.skills.SkillType;
import com.gmail.nossr50.events.fake.FakeBlockBreakEvent;
import com.gmail.nossr50.locale.LocaleLoader; import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.party.ShareHandler; import com.gmail.nossr50.party.ShareHandler;
import com.gmail.nossr50.runnables.skills.BleedTimerTask; import com.gmail.nossr50.runnables.skills.BleedTimerTask;
@@ -192,6 +195,13 @@ public class PlayerListener implements Listener {
Block block = event.getPlayer().getTargetBlock(null, 100); Block block = event.getPlayer().getTargetBlock(null, 100);
if (fishingManager.canIceFish(block)) { if (fishingManager.canIceFish(block)) {
FakeBlockBreakEvent blockBreakEvent = new FakeBlockBreakEvent(block, player);
mcMMO.p.getServer().getPluginManager().callEvent(blockBreakEvent);
if (blockBreakEvent.isCancelled()) {
return;
}
event.setCancelled(true); event.setCancelled(true);
fishingManager.iceFishing(hook, block); fishingManager.iceFishing(hook, block);
} }
@@ -256,6 +266,24 @@ public class PlayerListener implements Listener {
BleedTimerTask.bleedOut(player); // Bleed it out BleedTimerTask.bleedOut(player); // Bleed it out
} }
/**
* Start user data prefetch.
*/
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onLoginStart(PlayerLoginEvent event) {
UserManager.prefetchUserData(event.getPlayer().getName());
}
/**
* Cancel user data prefetch if a plugin kicks them.
*/
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = false)
public void onLoginComplete(PlayerLoginEvent event) {
if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) {
UserManager.discardPrefetch(event.getPlayer().getName());
}
}
/** /**
* Monitor PlayerJoin events. * Monitor PlayerJoin events.
* *

View File

@@ -35,6 +35,7 @@ import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.metrics.MetricsManager; import com.gmail.nossr50.metrics.MetricsManager;
import com.gmail.nossr50.party.PartyManager; import com.gmail.nossr50.party.PartyManager;
import com.gmail.nossr50.runnables.SaveTimerTask; import com.gmail.nossr50.runnables.SaveTimerTask;
import com.gmail.nossr50.runnables.UpdateCheckerTask;
import com.gmail.nossr50.runnables.database.UserPurgeTask; import com.gmail.nossr50.runnables.database.UserPurgeTask;
import com.gmail.nossr50.runnables.party.PartyAutoKickTask; import com.gmail.nossr50.runnables.party.PartyAutoKickTask;
import com.gmail.nossr50.runnables.skills.BleedTimerTask; import com.gmail.nossr50.runnables.skills.BleedTimerTask;
@@ -47,7 +48,6 @@ import com.gmail.nossr50.util.ChimaeraWing;
import com.gmail.nossr50.util.LogFilter; import com.gmail.nossr50.util.LogFilter;
import com.gmail.nossr50.util.Misc; import com.gmail.nossr50.util.Misc;
import com.gmail.nossr50.util.Permissions; import com.gmail.nossr50.util.Permissions;
import com.gmail.nossr50.util.UpdateChecker;
import com.gmail.nossr50.util.blockmeta.chunkmeta.ChunkManager; import com.gmail.nossr50.util.blockmeta.chunkmeta.ChunkManager;
import com.gmail.nossr50.util.blockmeta.chunkmeta.ChunkManagerFactory; import com.gmail.nossr50.util.blockmeta.chunkmeta.ChunkManagerFactory;
import com.gmail.nossr50.util.commands.CommandRegistrationManager; import com.gmail.nossr50.util.commands.CommandRegistrationManager;
@@ -266,13 +266,11 @@ public class mcMMO extends JavaPlugin {
return; return;
} }
try { getServer().getScheduler().runTaskAsynchronously(this, new UpdateCheckerTask());
updateAvailable = UpdateChecker.updateAvailable(); }
}
catch (Exception e) {
updateAvailable = false;
}
public void updateCheckerCallback(boolean updateAvailable) {
this.updateAvailable = updateAvailable;
if (updateAvailable) { if (updateAvailable) {
getLogger().info(LocaleLoader.getString("UpdateChecker.outdated")); getLogger().info(LocaleLoader.getString("UpdateChecker.outdated"));
getLogger().info(LocaleLoader.getString("UpdateChecker.newavailable")); getLogger().info(LocaleLoader.getString("UpdateChecker.newavailable"));

View File

@@ -3,6 +3,7 @@ package com.gmail.nossr50.runnables;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
@SuppressWarnings("deprecation")
public class PlayerUpdateInventoryTask extends BukkitRunnable { public class PlayerUpdateInventoryTask extends BukkitRunnable {
private Player player; private Player player;

View File

@@ -0,0 +1,19 @@
package com.gmail.nossr50.runnables;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.UpdateChecker;
/**
* Async task
*/
public class UpdateCheckerTask implements Runnable {
@Override
public void run() {
try {
mcMMO.p.updateCheckerCallback(UpdateChecker.updateAvailable());
}
catch (Exception e) {
mcMMO.p.updateCheckerCallback(false);
}
}
}

View File

@@ -0,0 +1,41 @@
package com.gmail.nossr50.runnables.database;
import java.util.logging.Level;
import org.bukkit.command.CommandSender;
import org.bukkit.scheduler.BukkitRunnable;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.database.DatabaseManager;
import com.gmail.nossr50.locale.LocaleLoader;
public class ConversionTask extends BukkitRunnable {
private final DatabaseManager sourceDb;
private final CommandSender sender;
private final String message;
public ConversionTask(DatabaseManager from, CommandSender sendback, String oldType, String newType) {
sourceDb = from;
sender = sendback;
message = LocaleLoader.getString("Commands.mmoupdate.Finish", oldType, newType);
}
@Override
public void run() {
sourceDb.convertUsers(mcMMO.getDatabaseManager());
// Announce completeness
mcMMO.p.getServer().getScheduler().runTask(mcMMO.p, new CompleteAnnouncement());
}
public class CompleteAnnouncement implements Runnable {
@Override
public void run() {
try {
sender.sendMessage(message);
} catch (Exception e) {
mcMMO.p.getLogger().log(Level.WARNING, "Exception sending database conversion completion message to " + sender.getName(), e);
}
}
}
}

View File

@@ -1,37 +0,0 @@
package com.gmail.nossr50.runnables.database;
import java.io.BufferedReader;
import java.io.FileReader;
import org.bukkit.scheduler.BukkitRunnable;
import com.gmail.nossr50.mcMMO;
public class SQLConversionTask extends BukkitRunnable {
@Override
public void run() {
String location = mcMMO.getUsersFilePath();
try {
BufferedReader in = new BufferedReader(new FileReader(location));
String line = "";
int converted = 0;
while ((line = in.readLine()) != null) {
// Find if the line contains the player we want.
String[] playerData = line.split(":");
if (mcMMO.getDatabaseManager().convert(playerData)) {
converted++;
}
}
mcMMO.p.getLogger().info("MySQL Updated from users file, " + converted + " items added/updated to MySQL DB");
in.close();
}
catch (Exception e) {
mcMMO.p.getLogger().severe("Exception while reading " + location + " (Are you sure you formatted it correctly?)" + e.toString());
}
}
}

View File

@@ -0,0 +1,19 @@
package com.gmail.nossr50.runnables.player;
import java.util.concurrent.Callable;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.datatypes.player.PlayerProfile;
public class PlayerProfileLoader implements Callable<PlayerProfile> {
private final String playerName;
public PlayerProfileLoader(String player) {
this.playerName = player;
}
@Override
public PlayerProfile call() {
return mcMMO.getDatabaseManager().loadPlayerProfile(playerName, true);
}
}

View File

@@ -44,6 +44,10 @@ public final class MobHealthbarUtils {
return; return;
} }
if (isBoss(target)) {
return;
}
PlayerProfile profile = UserManager.getPlayer(player).getProfile(); PlayerProfile profile = UserManager.getPlayer(player).getProfile();
if (profile.getMobHealthbarType() == null) { if (profile.getMobHealthbarType() == null) {
@@ -149,4 +153,21 @@ public final class MobHealthbarUtils {
return healthbar; return healthbar;
} }
/**
* Check if a given LivingEntity is a boss.
*
* @param livingEntity The {@link LivingEntity} of the livingEntity to check
* @return true if the livingEntity is a boss, false otherwise
*/
public static boolean isBoss(LivingEntity livingEntity) {
switch (livingEntity.getType()) {
case ENDER_DRAGON:
case WITHER:
return true;
default:
return ModUtils.isCustomBossEntity(livingEntity);
}
}
} }

View File

@@ -291,4 +291,15 @@ public final class ModUtils {
return false; return false;
} }
/**
* Check if a custom entity is a boss.
*
* @param entity The entity to check
* @return true if the entity represents a boss, false otherwise
*/
public static boolean isCustomBossEntity(Entity entity) {
//TODO: Finish this method
return false;
}
} }

View File

@@ -19,6 +19,7 @@ import com.gmail.nossr50.commands.chat.AdminChatCommand;
import com.gmail.nossr50.commands.chat.PartyChatCommand; import com.gmail.nossr50.commands.chat.PartyChatCommand;
import com.gmail.nossr50.commands.database.McpurgeCommand; import com.gmail.nossr50.commands.database.McpurgeCommand;
import com.gmail.nossr50.commands.database.McremoveCommand; import com.gmail.nossr50.commands.database.McremoveCommand;
import com.gmail.nossr50.commands.database.MmoshowdbCommand;
import com.gmail.nossr50.commands.database.MmoupdateCommand; import com.gmail.nossr50.commands.database.MmoupdateCommand;
import com.gmail.nossr50.commands.experience.AddlevelsCommand; import com.gmail.nossr50.commands.experience.AddlevelsCommand;
import com.gmail.nossr50.commands.experience.AddxpCommand; import com.gmail.nossr50.commands.experience.AddxpCommand;
@@ -276,11 +277,20 @@ public final class CommandRegistrationManager {
PluginCommand command = mcMMO.p.getCommand("mmoupdate"); PluginCommand command = mcMMO.p.getCommand("mmoupdate");
command.setDescription(LocaleLoader.getString("Commands.Description.mmoupdate")); command.setDescription(LocaleLoader.getString("Commands.Description.mmoupdate"));
command.setPermission("mcmmo.commands.mmoupdate"); command.setPermission("mcmmo.commands.mmoupdate");
command.setPermissionMessage(permissionsMessage); command.setPermissionMessage(LocaleLoader.getString("Commands.mmoupdate.OpOnly"));
command.setUsage(LocaleLoader.getString("Commands.Usage.0", "mmoupdate")); command.setUsage(LocaleLoader.getString("Commands.Usage.1", "mmoupdate", "<confirm|flatfile|sql|" + LocaleLoader.getString("Commands.Usage.FullClassName") + ">"));
command.setExecutor(new MmoupdateCommand()); command.setExecutor(new MmoupdateCommand());
} }
private static void registerMmoshowdbCommand() {
PluginCommand command = mcMMO.p.getCommand("mmoshowdb");
command.setDescription(LocaleLoader.getString("Commands.Description.mmoshowdb"));
command.setPermission("mcmmo.commands.mmoshowdb");
command.setPermissionMessage(permissionsMessage);
command.setUsage(LocaleLoader.getString("Commands.Usage.0", "mmoshowdb"));
command.setExecutor(new MmoshowdbCommand());
}
private static void registerAdminChatCommand() { private static void registerAdminChatCommand() {
PluginCommand command = mcMMO.p.getCommand("adminchat"); PluginCommand command = mcMMO.p.getCommand("adminchat");
command.setDescription(LocaleLoader.getString("Commands.Description.adminchat")); command.setDescription(LocaleLoader.getString("Commands.Description.adminchat"));
@@ -421,6 +431,7 @@ public final class CommandRegistrationManager {
registerMcpurgeCommand(); registerMcpurgeCommand();
registerMcremoveCommand(); registerMcremoveCommand();
registerMmoupdateCommand(); registerMmoupdateCommand();
registerMmoshowdbCommand();
// Experience Commands // Experience Commands
registerAddlevelsCommand(); registerAddlevelsCommand();

View File

@@ -1,22 +1,55 @@
package com.gmail.nossr50.util.player; package com.gmail.nossr50.util.player;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.datatypes.player.McMMOPlayer; import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.player.PlayerProfile;
import com.gmail.nossr50.runnables.player.PlayerProfileLoader;
public final class UserManager { public final class UserManager {
private final static Map<String, McMMOPlayer> players = new HashMap<String, McMMOPlayer>(); private final static Map<String, McMMOPlayer> players = new HashMap<String, McMMOPlayer>();
private final static Map<String, Future<PlayerProfile>> loadTasks = new HashMap<String, Future<PlayerProfile>>();
private final static ExecutorService loadExecutor = Executors.newCachedThreadPool();
private UserManager() {}; private UserManager() {};
/** /**
* Add a new user. * Asynchronously pre-fetch information about the player. This is intended
* to expedite the PlayerJoinEvent.
*
* @param playerName The player name
*/
public static void prefetchUserData(String playerName) {
loadTasks.put(playerName, loadExecutor.submit(new PlayerProfileLoader(playerName)));
}
/**
* Discard the information from the prefetch - for example, due to the
* user being banned.
*
* @param playerName The player name
*/
public static void discardPrefetch(String playerName) {
Future<PlayerProfile> oldTask = loadTasks.remove(playerName);
if (oldTask != null) {
oldTask.cancel(false);
}
}
/**
* Add a new user. If the prefetched player information is available, it
* will be used.
* *
* @param player The player to create a user record for * @param player The player to create a user record for
* @return the player's {@link McMMOPlayer} object * @return the player's {@link McMMOPlayer} object
@@ -29,7 +62,22 @@ public final class UserManager {
mcMMOPlayer.setPlayer(player); // The player object is different on each reconnection and must be updated mcMMOPlayer.setPlayer(player); // The player object is different on each reconnection and must be updated
} }
else { else {
Future<PlayerProfile> task = loadTasks.remove(playerName);
if (task != null && !task.isCancelled()) {
try {
mcMMOPlayer = new McMMOPlayer(player, task.get());
// TODO copy any additional post-processing here
players.put(playerName, mcMMOPlayer);
return mcMMOPlayer;
}
catch (ExecutionException e) {
}
catch (InterruptedException e) {
}
}
// Did not return - load on main thread
mcMMOPlayer = new McMMOPlayer(player); mcMMOPlayer = new McMMOPlayer(player);
// (start post-processing that must be copied above)
players.put(playerName, mcMMOPlayer); players.put(playerName, mcMMOPlayer);
} }
@@ -43,12 +91,14 @@ public final class UserManager {
*/ */
public static void remove(String playerName) { public static void remove(String playerName) {
players.remove(playerName); players.remove(playerName);
discardPrefetch(playerName);
} }
/** /**
* Clear all users. * Clear all users.
*/ */
public static void clearAll() { public static void clearAll() {
discardAllPrefetch();
players.clear(); players.clear();
} }
@@ -56,11 +106,23 @@ public final class UserManager {
* Save all users. * Save all users.
*/ */
public static void saveAll() { public static void saveAll() {
discardAllPrefetch();
for (McMMOPlayer mcMMOPlayer : players.values()) { for (McMMOPlayer mcMMOPlayer : players.values()) {
mcMMOPlayer.getProfile().save(); mcMMOPlayer.getProfile().save();
} }
} }
/**
* Discard / cancel all data prefetching.
*/
public static void discardAllPrefetch() {
Iterator<Future<PlayerProfile>> taskIter = loadTasks.values().iterator();
while (taskIter.hasNext()) {
taskIter.next().cancel(false);
taskIter.remove();
}
}
public static Map<String, McMMOPlayer> getPlayers() { public static Map<String, McMMOPlayer> getPlayers() {
return players; return players;
} }

View File

@@ -18,6 +18,7 @@ import com.gmail.nossr50.datatypes.database.PlayerStat;
import com.gmail.nossr50.datatypes.player.McMMOPlayer; import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.player.PlayerProfile; import com.gmail.nossr50.datatypes.player.PlayerProfile;
import com.gmail.nossr50.datatypes.skills.SkillType; import com.gmail.nossr50.datatypes.skills.SkillType;
import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.runnables.scoreboards.ScoreboardChangeTask; import com.gmail.nossr50.runnables.scoreboards.ScoreboardChangeTask;
import com.gmail.nossr50.util.Misc; import com.gmail.nossr50.util.Misc;
import com.gmail.nossr50.util.Permissions; import com.gmail.nossr50.util.Permissions;
@@ -28,10 +29,16 @@ public class ScoreboardManager {
private static final Map<String, Scoreboard> PLAYER_SCOREBOARDS = new HashMap<String, Scoreboard>(); private static final Map<String, Scoreboard> PLAYER_SCOREBOARDS = new HashMap<String, Scoreboard>();
private static final Scoreboard GLOBAL_STATS_SCOREBOARD = mcMMO.p.getServer().getScoreboardManager().getNewScoreboard(); private static final Scoreboard GLOBAL_STATS_SCOREBOARD = mcMMO.p.getServer().getScoreboardManager().getNewScoreboard();
private final static String PLAYER_STATS_HEADER = "mcMMO Stats"; private final static String PLAYER_STATS_HEADER = LocaleLoader.getString("Scoreboard.Header.PlayerStats");
private final static String PLAYER_RANK_HEADER = "mcMMO Rankings"; private final static String PLAYER_RANK_HEADER = LocaleLoader.getString("Scoreboard.Header.PlayerRank");
private final static String PLAYER_INSPECT_HEADER = "mcMMO Stats: "; private final static String PLAYER_INSPECT_HEADER = LocaleLoader.getString("Scoreboard.Header.PlayerInspect");
private final static String POWER_LEVEL_HEADER = "Power Level"; private final static String POWER_LEVEL_HEADER = LocaleLoader.getString("Scoreboard.Header.PowerLevel");
private final static String POWER_LEVEL = LocaleLoader.getString("Scoreboard.Misc.PowerLevel");
private final static String LEVEL = LocaleLoader.getString("Scoreboard.Misc.Level");
private final static String CURRENT_XP = LocaleLoader.getString("Scoreboard.Misc.CurrentXP");
private final static String REMAINING_XP = LocaleLoader.getString("Scoreboard.Misc.RemainingXP");
private final static String OVERALL = LocaleLoader.getString("Scoreboard.Misc.Overall");
private final static List<String> SCOREBOARD_TASKS = new ArrayList<String>(); private final static List<String> SCOREBOARD_TASKS = new ArrayList<String>();
@@ -158,7 +165,7 @@ public class ScoreboardManager {
} }
Objective newObjective = GLOBAL_STATS_SCOREBOARD.registerNewObjective(skillName, "dummy"); Objective newObjective = GLOBAL_STATS_SCOREBOARD.registerNewObjective(skillName, "dummy");
newObjective.setDisplayName(ChatColor.GOLD + (skillName.equalsIgnoreCase("all") ? "Power Level" : SkillUtils.getSkillName(SkillType.getSkill(skillName)))); newObjective.setDisplayName(ChatColor.GOLD + (skillName.equalsIgnoreCase("all") ? POWER_LEVEL : SkillUtils.getSkillName(SkillType.getSkill(skillName))));
updateGlobalStatsScores(player, newObjective, skillName, pageNumber); updateGlobalStatsScores(player, newObjective, skillName, pageNumber);
changeScoreboard(player, oldScoreboard, GLOBAL_STATS_SCOREBOARD, Config.getInstance().getMctopScoreboardTime()); changeScoreboard(player, oldScoreboard, GLOBAL_STATS_SCOREBOARD, Config.getInstance().getMctopScoreboardTime());
@@ -168,9 +175,9 @@ public class ScoreboardManager {
Server server = mcMMO.p.getServer(); Server server = mcMMO.p.getServer();
int currentXP = profile.getSkillXpLevel(skill); int currentXP = profile.getSkillXpLevel(skill);
objective.getScore(server.getOfflinePlayer("Level")).setScore(profile.getSkillLevel(skill)); objective.getScore(server.getOfflinePlayer(LEVEL)).setScore(profile.getSkillLevel(skill));
objective.getScore(server.getOfflinePlayer("Current XP")).setScore(currentXP); objective.getScore(server.getOfflinePlayer(CURRENT_XP)).setScore(currentXP);
objective.getScore(server.getOfflinePlayer("Remaining XP")).setScore(profile.getXpToLevel(skill) - currentXP); objective.getScore(server.getOfflinePlayer(REMAINING_XP)).setScore(profile.getXpToLevel(skill) - currentXP);
objective.setDisplaySlot(DisplaySlot.SIDEBAR); objective.setDisplaySlot(DisplaySlot.SIDEBAR);
} }
@@ -188,7 +195,7 @@ public class ScoreboardManager {
objective.getScore(server.getOfflinePlayer(SkillUtils.getSkillName(skill))).setScore(profile.getSkillLevel(skill)); objective.getScore(server.getOfflinePlayer(SkillUtils.getSkillName(skill))).setScore(profile.getSkillLevel(skill));
} }
objective.getScore(server.getOfflinePlayer(ChatColor.GOLD + "Power Level")).setScore(mcMMOPlayer.getPowerLevel()); objective.getScore(server.getOfflinePlayer(ChatColor.GOLD + POWER_LEVEL)).setScore(mcMMOPlayer.getPowerLevel());
objective.setDisplaySlot(DisplaySlot.SIDEBAR); objective.setDisplaySlot(DisplaySlot.SIDEBAR);
} }
@@ -214,7 +221,7 @@ public class ScoreboardManager {
rank = skills.get("ALL"); rank = skills.get("ALL");
if (rank != null) { if (rank != null) {
objective.getScore(server.getOfflinePlayer(ChatColor.GOLD + "Overall")).setScore(rank); objective.getScore(server.getOfflinePlayer(ChatColor.GOLD + OVERALL)).setScore(rank);
} }
objective.setDisplaySlot(DisplaySlot.SIDEBAR); objective.setDisplaySlot(DisplaySlot.SIDEBAR);
@@ -237,7 +244,7 @@ public class ScoreboardManager {
rank = skills.get("ALL"); rank = skills.get("ALL");
if (rank != null) { if (rank != null) {
objective.getScore(server.getOfflinePlayer(ChatColor.GOLD + "Overall")).setScore(rank); objective.getScore(server.getOfflinePlayer(ChatColor.GOLD + OVERALL)).setScore(rank);
} }
objective.setDisplayName(PLAYER_RANK_HEADER + ": " + targetName); objective.setDisplayName(PLAYER_RANK_HEADER + ": " + targetName);
@@ -261,7 +268,7 @@ public class ScoreboardManager {
powerLevel += skillLevel; powerLevel += skillLevel;
} }
objective.getScore(server.getOfflinePlayer(ChatColor.GOLD + "Power Level")).setScore(powerLevel); objective.getScore(server.getOfflinePlayer(ChatColor.GOLD + POWER_LEVEL)).setScore(powerLevel);
objective.setDisplayName(PLAYER_INSPECT_HEADER + target.getName()); objective.setDisplayName(PLAYER_INSPECT_HEADER + target.getName());
objective.setDisplaySlot(DisplaySlot.SIDEBAR); objective.setDisplaySlot(DisplaySlot.SIDEBAR);
} }
@@ -277,7 +284,7 @@ public class ScoreboardManager {
powerLevel += skillLevel; powerLevel += skillLevel;
} }
objective.getScore(server.getOfflinePlayer(ChatColor.GOLD + "Power Level")).setScore(powerLevel); objective.getScore(server.getOfflinePlayer(ChatColor.GOLD + POWER_LEVEL)).setScore(powerLevel);
objective.setDisplayName(PLAYER_INSPECT_HEADER + targetProfile.getPlayerName()); objective.setDisplayName(PLAYER_INSPECT_HEADER + targetProfile.getPlayerName());
} }

View File

@@ -442,8 +442,11 @@ Commands.mmoedit=[player] <skill> <newvalue> [[RED]] - Modify target
Commands.mmoedit.AllSkills.1=[[GREEN]]Your level in all skills was set to {0}! Commands.mmoedit.AllSkills.1=[[GREEN]]Your level in all skills was set to {0}!
Commands.mmoedit.Modified.1=[[GREEN]]Your level in {0} was set to {1}! Commands.mmoedit.Modified.1=[[GREEN]]Your level in {0} was set to {1}!
Commands.mmoedit.Modified.2=[[RED]]{0} has been modified for {1}. Commands.mmoedit.Modified.2=[[RED]]{0} has been modified for {1}.
Commands.mmoupdate.Start=[[GRAY]]Starting conversion... Commands.mmoupdate.Same=[[RED]]You are already using the {0} database!
Commands.mmoupdate.Finish=[[GREEN]]Conversion finished! Commands.mmoupdate.InvalidType=[[RED]]{0} is not a valid database type.
Commands.mmoupdate.Start=[[GRAY]]Starting conversion from {0} to {1}...
Commands.mmoupdate.Finish=[[GRAY]]Database migration complete; the {1} database now has all data from the {0} database.
Commands.mmoshowdb=[[YELLOW]]The currently used database is [[GREEN]]{0}
Commands.ModDescription=[[RED]]- Read brief mod description Commands.ModDescription=[[RED]]- Read brief mod description
Commands.NoConsole=This command does not support console usage. Commands.NoConsole=This command does not support console usage.
Commands.Notifications.Off=Ability notifications toggled [[RED]]off Commands.Notifications.Off=Ability notifications toggled [[RED]]off
@@ -505,6 +508,7 @@ Commands.Usage.0=[[RED]]Proper usage is /{0}
Commands.Usage.1=[[RED]]Proper usage is /{0} {1} Commands.Usage.1=[[RED]]Proper usage is /{0} {1}
Commands.Usage.2=[[RED]]Proper usage is /{0} {1} {2} Commands.Usage.2=[[RED]]Proper usage is /{0} {1} {2}
Commands.Usage.3=[[RED]]Proper usage is /{0} {1} {2} {3} Commands.Usage.3=[[RED]]Proper usage is /{0} {1} {2} {3}
Commands.Usage.FullClassName=classname
Commands.Usage.Level=level Commands.Usage.Level=level
Commands.Usage.Message=message Commands.Usage.Message=message
Commands.Usage.Page=page Commands.Usage.Page=page
@@ -751,7 +755,8 @@ Commands.Description.mcremove=Remove a user from the mcMMO database
Commands.Description.mcstats=Show your mcMMO levels and XP Commands.Description.mcstats=Show your mcMMO levels and XP
Commands.Description.mctop=Show mcMMO leader boards Commands.Description.mctop=Show mcMMO leader boards
Commands.Description.mmoedit=Edit mcMMO levels for a user Commands.Description.mmoedit=Edit mcMMO levels for a user
Commands.Description.mmoupdate=Convert mcMMO database from Flatfile to MySQL Commands.Description.mmoupdate=Migrate mcMMO database from an old database into the current one
Commands.Description.mmoshowdb=Show the name of the current database type (for later use with /mmoupdate)
Commands.Description.party=Control various mcMMO party settings Commands.Description.party=Control various mcMMO party settings
Commands.Description.partychat=Toggle mcMMO party chat on/off or send party chat messages Commands.Description.partychat=Toggle mcMMO party chat on/off or send party chat messages
Commands.Description.ptp=Teleport to an mcMMO party member Commands.Description.ptp=Teleport to an mcMMO party member
@@ -764,3 +769,14 @@ Commands.Description.xprate=Modify the mcMMO XP rate or start an mcMMO XP event
#UPDATE CHECKER #UPDATE CHECKER
UpdateChecker.outdated=You are using an outdated version of mcMMO! UpdateChecker.outdated=You are using an outdated version of mcMMO!
UpdateChecker.newavailable=There is a new version available on BukkitDev. UpdateChecker.newavailable=There is a new version available on BukkitDev.
#SCOREBOARD HEADERS
Scoreboard.Header.PlayerStats=mcMMO Stats
Scoreboard.Header.PlayerRank=mcMMO Rankings
Scoreboard.Header.PlayerInspect=mcMMO Stats:
Scoreboard.Header.PowerLevel=Power Level
Scoreboard.Misc.PowerLevel=Power Level
Scoreboard.Misc.Level=Level
Scoreboard.Misc.CurrentXP=Current XP
Scoreboard.Misc.RemainingXP=Remaining XP
Scoreboard.Misc.Overall=Overall

View File

@@ -58,7 +58,9 @@ commands:
inspect: inspect:
description: View detailed mcMMO info on another player description: View detailed mcMMO info on another player
mmoupdate: mmoupdate:
description: Convert from Flat File to MySQL description: Migrate mcMMO database from an old database type to the current
mmoshowdb:
description: Show the name of the current database type (for later use with /mmoupdate)
partychat: partychat:
aliases: [pc, p] aliases: [pc, p]
description: Toggle Party chat or send party chat messages description: Toggle Party chat or send party chat messages
@@ -717,6 +719,7 @@ permissions:
mcmmo.commands.mmoedit: true mcmmo.commands.mmoedit: true
mcmmo.commands.mmoedit.others: true mcmmo.commands.mmoedit.others: true
mcmmo.commands.mmoupdate: true mcmmo.commands.mmoupdate: true
mcmmo.commands.mmoshowdb: true
mcmmo.commands.ptp.world.all: true mcmmo.commands.ptp.world.all: true
mcmmo.commands.skillreset.all: true mcmmo.commands.skillreset.all: true
mcmmo.commands.vampirism.all: true mcmmo.commands.vampirism.all: true
@@ -908,6 +911,8 @@ permissions:
description: Allows access to the mmoedit command for other players description: Allows access to the mmoedit command for other players
mcmmo.commands.mmoupdate: mcmmo.commands.mmoupdate:
description: Allows access to the mmoupdate command description: Allows access to the mmoupdate command
mcmmo.commands.mmoshowdb:
description: Allows access to the mmoshowdb command
mcmmo.commands.mobhealth: mcmmo.commands.mobhealth:
default: true default: true
description: Allows access to the mobhealth command description: Allows access to the mobhealth command