Better flatfile purge performance; sync flatdb access ...

... to avoid data loss
This commit is contained in:
riking 2013-06-23 11:45:30 -07:00
parent c967a327c6
commit c588fe7420

View File

@ -13,6 +13,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import com.gmail.nossr50.mcMMO;
@ -32,6 +33,7 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
private final long UPDATE_WAIT_TIME = 600000L; // 10 minutes
private final File usersFile;
private static final Object fileWritingLock = new Object();
protected FlatfileDatabaseManager() {
usersFile = new File(mcMMO.getUsersFilePath());
@ -48,65 +50,65 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
FileWriter out = null;
String usersFilePath = mcMMO.getUsersFilePath();
// Rationale for doing a file read instead of using the cached values:
// If mulitple users need be removed, this only opens the file once as
// opposed to calling removeUser which opens the file once each time.
try {
in = new BufferedReader(new FileReader(usersFilePath));
StringBuilder writer = new StringBuilder();
String line = "";
// 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(":");
PlayerProfile profile = null;
try {
profile = loadFromLine(character);
} catch (Exception e) {
e.printStackTrace();
}
if (profile == null) continue; // skip malformed lines
while ((line = in.readLine()) != null) {
String[] character = line.split(":");
PlayerProfile profile = null;
try {
profile = loadFromLine(character);
} catch (Exception e) {
e.printStackTrace();
}
if (profile == null) continue; // skip malformed lines
boolean powerless = true;
for (SkillType skill : SkillType.nonChildSkills()) {
if (profile.getSkillLevel(skill) != 0) {
powerless = false;
break;
boolean powerless = true;
for (SkillType skill : SkillType.nonChildSkills()) {
if (profile.getSkillLevel(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]);
}
}
// 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());
}
// 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 {
if (in != null) {
try {
in.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
catch (IOException e) {
mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
}
if (out != null) {
try {
out.close();
finally {
if (in != null) {
try {
in.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
catch (IOException ex) {
ex.printStackTrace();
if (out != null) {
try {
out.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
@ -120,9 +122,61 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
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()))) {
removedPlayers++;
BufferedReader in = null;
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);
}
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 {
if (in != null) {
try {
in.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
if (out != null) {
try {
out.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
@ -136,44 +190,46 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
FileWriter out = null;
String usersFilePath = mcMMO.getUsersFilePath();
try {
in = new BufferedReader(new FileReader(usersFilePath));
StringBuilder writer = new StringBuilder();
String line = "";
synchronized (fileWritingLock) {
try {
in = new BufferedReader(new FileReader(usersFilePath));
StringBuilder writer = new StringBuilder();
String line = "";
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.
if (!worked && line.split(":")[0].equalsIgnoreCase(playerName)) {
mcMMO.p.getLogger().info("User found, removing...");
worked = true;
continue; // Skip the player
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.
if (!worked && line.split(":")[0].equalsIgnoreCase(playerName)) {
mcMMO.p.getLogger().info("User found, removing...");
worked = true;
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());
}
out = new FileWriter(usersFilePath); // Write out the new file
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();
}
catch (Exception e) {
mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
}
if (out != null) {
try {
out.close();
finally {
if (in != null) {
try {
in.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
catch (IOException ex) {
ex.printStackTrace();
if (out != null) {
try {
out.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
@ -190,88 +246,90 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
FileWriter out = null;
String usersFilePath = mcMMO.getUsersFilePath();
try {
// Open the file
in = new BufferedReader(new FileReader(usersFilePath));
StringBuilder writer = new StringBuilder();
String line;
synchronized (fileWritingLock) {
try {
// Open the file
in = new BufferedReader(new FileReader(usersFilePath));
StringBuilder writer = new StringBuilder();
String line;
// While not at the end of the file
while ((line = in.readLine()) != null) {
// 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)) {
writer.append(line).append("\r\n");
}
else {
// Otherwise write the new player information
writer.append(playerName).append(":");
writer.append(profile.getSkillLevel(SkillType.MINING)).append(":");
writer.append(":");
writer.append(":");
writer.append(profile.getSkillXpLevel(SkillType.MINING)).append(":");
writer.append(profile.getSkillLevel(SkillType.WOODCUTTING)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.WOODCUTTING)).append(":");
writer.append(profile.getSkillLevel(SkillType.REPAIR)).append(":");
writer.append(profile.getSkillLevel(SkillType.UNARMED)).append(":");
writer.append(profile.getSkillLevel(SkillType.HERBALISM)).append(":");
writer.append(profile.getSkillLevel(SkillType.EXCAVATION)).append(":");
writer.append(profile.getSkillLevel(SkillType.ARCHERY)).append(":");
writer.append(profile.getSkillLevel(SkillType.SWORDS)).append(":");
writer.append(profile.getSkillLevel(SkillType.AXES)).append(":");
writer.append(profile.getSkillLevel(SkillType.ACROBATICS)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.REPAIR)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.UNARMED)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.HERBALISM)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.EXCAVATION)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.ARCHERY)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.SWORDS)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.AXES)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.ACROBATICS)).append(":");
writer.append(":");
writer.append(profile.getSkillLevel(SkillType.TAMING)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.TAMING)).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.TREE_FELLER)).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.SKULL_SPLITTER)).append(":");
writer.append((int) profile.getSkillDATS(AbilityType.SUPER_BREAKER)).append(":");
HudType hudType = profile.getHudType();
writer.append(hudType == null ? "STANDARD" : hudType.toString()).append(":");
writer.append(profile.getSkillLevel(SkillType.FISHING)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.FISHING)).append(":");
writer.append((int) profile.getSkillDATS(AbilityType.BLAST_MINING)).append(":");
writer.append(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR).append(":");
MobHealthbarType mobHealthbarType = profile.getMobHealthbarType();
writer.append(mobHealthbarType == null ? Config.getInstance().getMobHealthbarDefault().toString() : mobHealthbarType.toString()).append(":");
writer.append("\r\n");
// While not at the end of the file
while ((line = in.readLine()) != null) {
// 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)) {
writer.append(line).append("\r\n");
}
else {
// Otherwise write the new player information
writer.append(playerName).append(":");
writer.append(profile.getSkillLevel(SkillType.MINING)).append(":");
writer.append(":");
writer.append(":");
writer.append(profile.getSkillXpLevel(SkillType.MINING)).append(":");
writer.append(profile.getSkillLevel(SkillType.WOODCUTTING)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.WOODCUTTING)).append(":");
writer.append(profile.getSkillLevel(SkillType.REPAIR)).append(":");
writer.append(profile.getSkillLevel(SkillType.UNARMED)).append(":");
writer.append(profile.getSkillLevel(SkillType.HERBALISM)).append(":");
writer.append(profile.getSkillLevel(SkillType.EXCAVATION)).append(":");
writer.append(profile.getSkillLevel(SkillType.ARCHERY)).append(":");
writer.append(profile.getSkillLevel(SkillType.SWORDS)).append(":");
writer.append(profile.getSkillLevel(SkillType.AXES)).append(":");
writer.append(profile.getSkillLevel(SkillType.ACROBATICS)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.REPAIR)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.UNARMED)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.HERBALISM)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.EXCAVATION)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.ARCHERY)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.SWORDS)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.AXES)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.ACROBATICS)).append(":");
writer.append(":");
writer.append(profile.getSkillLevel(SkillType.TAMING)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.TAMING)).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.TREE_FELLER)).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.SKULL_SPLITTER)).append(":");
writer.append((int) profile.getSkillDATS(AbilityType.SUPER_BREAKER)).append(":");
HudType hudType = profile.getHudType();
writer.append(hudType == null ? "STANDARD" : hudType.toString()).append(":");
writer.append(profile.getSkillLevel(SkillType.FISHING)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.FISHING)).append(":");
writer.append((int) profile.getSkillDATS(AbilityType.BLAST_MINING)).append(":");
writer.append(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR).append(":");
MobHealthbarType mobHealthbarType = profile.getMobHealthbarType();
writer.append(mobHealthbarType == null ? Config.getInstance().getMobHealthbarDefault().toString() : mobHealthbarType.toString()).append(":");
writer.append("\r\n");
}
}
// Write the new file
out = new FileWriter(usersFilePath);
out.write(writer.toString());
}
// Write the new file
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();
}
catch (Exception e) {
e.printStackTrace();
}
if (out != null) {
try {
out.close();
finally {
if (in != null) {
try {
in.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
catch (IOException ex) {
ex.printStackTrace();
if (out != null) {
try {
out.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
@ -300,95 +358,99 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
}
public void newUser(String playerName) {
try {
// Open the file to write the player
BufferedWriter out = new BufferedWriter(new FileWriter(mcMMO.getUsersFilePath(), true));
synchronized (fileWritingLock) {
try {
// Open the file to write the player
BufferedWriter out = new BufferedWriter(new FileWriter(mcMMO.getUsersFilePath(), true));
// Add the player to the end
out.append(playerName).append(":");
out.append("0:"); // Mining
out.append(":");
out.append(":");
out.append("0:"); // Xp
out.append("0:"); // Woodcutting
out.append("0:"); // WoodCuttingXp
out.append("0:"); // Repair
out.append("0:"); // Unarmed
out.append("0:"); // Herbalism
out.append("0:"); // Excavation
out.append("0:"); // Archery
out.append("0:"); // Swords
out.append("0:"); // Axes
out.append("0:"); // Acrobatics
out.append("0:"); // RepairXp
out.append("0:"); // UnarmedXp
out.append("0:"); // HerbalismXp
out.append("0:"); // ExcavationXp
out.append("0:"); // ArcheryXp
out.append("0:"); // SwordsXp
out.append("0:"); // AxesXp
out.append("0:"); // AcrobaticsXp
out.append(":");
out.append("0:"); // Taming
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("STANDARD").append(":"); // HUD
out.append("0:"); // Fishing
out.append("0:"); // FishingXp
out.append("0:"); // Blast Mining
out.append(String.valueOf(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR)).append(":"); // LastLogin
out.append(Config.getInstance().getMobHealthbarDefault().toString()).append(":"); // Mob Healthbar HUD
// Add the player to the end
out.append(playerName).append(":");
out.append("0:"); // Mining
out.append(":");
out.append(":");
out.append("0:"); // Xp
out.append("0:"); // Woodcutting
out.append("0:"); // WoodCuttingXp
out.append("0:"); // Repair
out.append("0:"); // Unarmed
out.append("0:"); // Herbalism
out.append("0:"); // Excavation
out.append("0:"); // Archery
out.append("0:"); // Swords
out.append("0:"); // Axes
out.append("0:"); // Acrobatics
out.append("0:"); // RepairXp
out.append("0:"); // UnarmedXp
out.append("0:"); // HerbalismXp
out.append("0:"); // ExcavationXp
out.append("0:"); // ArcheryXp
out.append("0:"); // SwordsXp
out.append("0:"); // AxesXp
out.append("0:"); // AcrobaticsXp
out.append(":");
out.append("0:"); // Taming
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("STANDARD").append(":"); // HUD
out.append("0:"); // Fishing
out.append("0:"); // FishingXp
out.append("0:"); // Blast Mining
out.append(String.valueOf(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR)).append(":"); // LastLogin
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.close();
}
catch (Exception e) {
e.printStackTrace();
out.newLine();
out.close();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
public PlayerProfile loadPlayerProfile(String playerName, boolean create) {
FileReader file = null;
BufferedReader in = null;
try {
// Open the user file
file = new FileReader(mcMMO.getUsersFilePath());
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;
}
return loadFromLine(character);
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
synchronized (fileWritingLock) {
try {
if (in != null) {
in.close();
// Open the user file
file = new FileReader(mcMMO.getUsersFilePath());
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;
}
return loadFromLine(character);
}
if (file != null) {
file.close();
}
} catch (IOException e) {
}
catch (Exception e) {
e.printStackTrace();
}
finally {
try {
if (in != null) {
in.close();
}
if (file != null) {
file.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
if (create) {
@ -402,37 +464,39 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
FileReader file = null;
BufferedReader in = null;
try {
// Open the user file
file = new FileReader(mcMMO.getUsersFilePath());
in = new BufferedReader(file);
String line;
synchronized (fileWritingLock) {
try {
// Open the user file
file = new FileReader(mcMMO.getUsersFilePath());
in = new BufferedReader(file);
String line;
while ((line = in.readLine()) != null) {
String[] character = line.split(":");
while ((line = in.readLine()) != null) {
String[] character = line.split(":");
try {
destination.saveUser(loadFromLine(character));
}
catch (Exception e) {
e.printStackTrace();
try {
destination.saveUser(loadFromLine(character));
}
catch (Exception e) {
e.printStackTrace();
}
}
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
try {
if (in != null) {
in.close();
}
if (file != null) {
file.close();
}
} catch (IOException e) {
catch (Exception e) {
e.printStackTrace();
}
finally {
try {
if (in != null) {
in.close();
}
if (file != null) {
file.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@ -444,26 +508,29 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
public List<String> getStoredUsers() {
ArrayList<String> users = new ArrayList<String>();
BufferedReader in = null;
try {
// Open the user file
FileReader file = new FileReader(mcMMO.getUsersFilePath());
in = new BufferedReader(file);
String line;
while ((line = in.readLine()) != null) {
String[] character = line.split(":");
users.add(character[0]);
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
synchronized (fileWritingLock) {
try {
in.close();
} catch (IOException e) {
// Open the user file
FileReader file = new FileReader(mcMMO.getUsersFilePath());
in = new BufferedReader(file);
String line;
while ((line = in.readLine()) != null) {
String[] character = line.split(":");
users.add(character[0]);
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return users;
}