From dac2bf90af5162483335a5cf54f8dd408b62ff98 Mon Sep 17 00:00:00 2001 From: Sn0wStorm Date: Thu, 4 Sep 2014 13:06:34 +0200 Subject: [PATCH] Threaded savefile writes --- config.yml | 10 +- src/com/dre/brewery/P.java | 92 +--------- .../brewery/{ => filedata}/ConfigUpdater.java | 4 +- src/com/dre/brewery/filedata/DataSave.java | 160 ++++++++++++++++++ .../brewery/{ => filedata}/DataUpdater.java | 6 +- .../{ => filedata}/LanguageReader.java | 2 +- src/com/dre/brewery/filedata/ReadOldData.java | 36 ++++ src/com/dre/brewery/filedata/WriteData.java | 32 ++++ .../dre/brewery/listeners/WorldListener.java | 3 +- 9 files changed, 252 insertions(+), 93 deletions(-) rename src/com/dre/brewery/{ => filedata}/ConfigUpdater.java (99%) create mode 100644 src/com/dre/brewery/filedata/DataSave.java rename src/com/dre/brewery/{ => filedata}/DataUpdater.java (96%) rename src/com/dre/brewery/{ => filedata}/LanguageReader.java (99%) create mode 100644 src/com/dre/brewery/filedata/ReadOldData.java create mode 100644 src/com/dre/brewery/filedata/WriteData.java diff --git a/config.yml b/config.yml index 0724c7b1..b1bfbf8b 100644 --- a/config.yml +++ b/config.yml @@ -210,14 +210,18 @@ recipes: 10: name: Stale Coffee/Coffee/Strong Coffee ingredients: - - INK_SACK,3/6 - - MILK_BUCKET/1 + - INK_SACK,3/12 + - MILK_BUCKET/2 cookingtime: 2 color: BLACK difficulty: 3 effects: - REGENERATION/1/2-5 - - SPEED/1-2/10-25 + - SPEED/1/30-140 + +# More Recipes ideas: Cachaca, Gin, Whiskey, Tequila, Cider, etc. as well as high quality abbreviations like golden vodka etc. +# I will not add more Recipes to the default config, as they would be public and viewable by users to cheat. +# It is up to the Serveradmin to change and add Recipes, so players cannot cheat from the default config. diff --git a/src/com/dre/brewery/P.java b/src/com/dre/brewery/P.java index c13a7f8c..c79b13bb 100644 --- a/src/com/dre/brewery/P.java +++ b/src/com/dre/brewery/P.java @@ -17,7 +17,6 @@ import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.ConfigurationSection; -import com.dre.brewery.integration.LogBlockBarrel; import org.apache.commons.lang.math.NumberUtils; import org.bukkit.event.HandlerList; import org.bukkit.Bukkit; @@ -30,14 +29,12 @@ import org.mcstats.Metrics; import com.dre.brewery.listeners.*; +import com.dre.brewery.filedata.*; +import com.dre.brewery.integration.LogBlockBarrel; public class P extends JavaPlugin { public static P p; public static boolean debug; - public static int lastBackup = 0; - public static int lastSave = 1; - public static int autosave = 3; - final public static String dataVersion = "1.1"; public static boolean useUUID; // Third Party Enabled @@ -102,7 +99,7 @@ public void onDisable() { p.getServer().getScheduler().cancelTasks(this); // save Data to Disk - saveData(); + DataSave.save(true); // save LanguageReader languageReader.save(); @@ -212,7 +209,7 @@ public void readConfig() { useLB = config.getBoolean("useLogBlock", false) && getServer().getPluginManager().isPluginEnabled("LogBlock"); // various Settings - autosave = config.getInt("autosave", 3); + DataSave.autosave = config.getInt("autosave", 3); debug = config.getBoolean("debug", false); BPlayer.pukeItem = Material.matchMaterial(config.getString("pukeItem", "SOUL_SAND")); BPlayer.hangoverTime = config.getInt("hangoverDays", 0) * 24 * 60; @@ -287,11 +284,11 @@ public void readData() { // Check if data is the newest version String version = data.getString("Version", null); if (version != null) { - if (!version.equals(dataVersion)) { + if (!version.equals(DataSave.dataVersion)) { P.p.log("Data File is being updated..."); new DataUpdater(data, file).update(version); data = YamlConfiguration.loadConfiguration(file); - P.p.log("Data Updated to version: " + dataVersion); + P.p.log("Data Updated to version: " + DataSave.dataVersion); } } @@ -497,77 +494,6 @@ public void loadWorldData(String uuid, World world) { } } - // save all Data - public void saveData() { - long time = System.nanoTime(); - File datafile = new File(p.getDataFolder(), "data.yml"); - - FileConfiguration oldData = YamlConfiguration.loadConfiguration(datafile); - - if (datafile.exists()) { - if (lastBackup > 10) { - datafile.renameTo(new File(p.getDataFolder(), "dataBackup.yml")); - lastBackup = 0; - } else { - lastBackup++; - } - } - - FileConfiguration configFile = new YamlConfiguration(); - - if (!Brew.potions.isEmpty()) { - Brew.save(configFile.createSection("Brew")); - } - - if (!BCauldron.bcauldrons.isEmpty() || oldData.contains("BCauldron")) { - BCauldron.save(configFile.createSection("BCauldron"), oldData.getConfigurationSection("BCauldron")); - } - - if (!Barrel.barrels.isEmpty() || oldData.contains("Barrel")) { - Barrel.save(configFile.createSection("Barrel"), oldData.getConfigurationSection("Barrel")); - } - - if (!BPlayer.isEmpty()) { - BPlayer.save(configFile.createSection("Player")); - } - - if (!Wakeup.wakeups.isEmpty() || oldData.contains("Wakeup")) { - Wakeup.save(configFile.createSection("Wakeup"), oldData.getConfigurationSection("Wakeup")); - } - - saveWorldNames(configFile, oldData.getConfigurationSection("Worlds")); - - configFile.set("Version", dataVersion); - - try { - configFile.save(datafile); - } catch (IOException e) { - e.printStackTrace(); - } - - lastSave = 1; - - time = System.nanoTime() - time; - float ftime = (float) (time / 1000000.0); - p.debugLog("Writing Data to File (" + ftime + "ms)"); - } - - public void saveWorldNames(FileConfiguration root, ConfigurationSection old) { - if (old != null) { - root.set("Worlds", old); - } - for (World world : p.getServer().getWorlds()) { - String worldName = world.getName(); - if (worldName.startsWith("DXL_")) { - worldName = getDxlName(worldName); - root.set("Worlds." + worldName, 0); - } else { - worldName = world.getUID().toString(); - root.set("Worlds." + worldName, world.getName()); - } - } - } - // Utility public int parseInt(String string) { @@ -738,11 +664,7 @@ public void run() { debugLog("Update"); - if (lastSave >= autosave) { - saveData();// save all data - } else { - lastSave++; - } + DataSave.autoSave(); } } diff --git a/src/com/dre/brewery/ConfigUpdater.java b/src/com/dre/brewery/filedata/ConfigUpdater.java similarity index 99% rename from src/com/dre/brewery/ConfigUpdater.java rename to src/com/dre/brewery/filedata/ConfigUpdater.java index 32d86607..af86a573 100644 --- a/src/com/dre/brewery/ConfigUpdater.java +++ b/src/com/dre/brewery/filedata/ConfigUpdater.java @@ -1,4 +1,4 @@ -package com.dre.brewery; +package com.dre.brewery.filedata; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -9,6 +9,8 @@ import java.util.ArrayList; import java.util.Arrays; +import com.dre.brewery.P; + public class ConfigUpdater { private ArrayList config = new ArrayList(); diff --git a/src/com/dre/brewery/filedata/DataSave.java b/src/com/dre/brewery/filedata/DataSave.java new file mode 100644 index 00000000..f491c629 --- /dev/null +++ b/src/com/dre/brewery/filedata/DataSave.java @@ -0,0 +1,160 @@ +package com.dre.brewery.filedata; + + +import java.io.File; + +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.scheduler.BukkitRunnable; + +import com.dre.brewery.BCauldron; +import com.dre.brewery.BPlayer; +import com.dre.brewery.Barrel; +import com.dre.brewery.Brew; +import com.dre.brewery.P; +import com.dre.brewery.Wakeup; + +public class DataSave extends BukkitRunnable { + + public static int lastBackup = 0; + public static int lastSave = 1; + public static int autosave = 3; + final public static String dataVersion = "1.1"; + public static DataSave running; + + public ReadOldData read; + private long time; + public boolean collected = false; + + // Not Thread-Safe! Needs to be run in main thread but uses async Read/Write + public DataSave(ReadOldData read) { + this.read = read; + time = System.currentTimeMillis(); + } + + + @Override + public void run() { + FileConfiguration oldData; + if (read != null) { + if (!read.done) { + // Wait for async thread to load old data + if (System.currentTimeMillis() - time > 30000) { + P.p.errorLog("Old Data took too long to load!"); + cancel(); + return; + } + return; + } + oldData = read.getData(); + } else { + oldData = new YamlConfiguration(); + } + try { + cancel(); + } catch (IllegalStateException e) { + } + + FileConfiguration configFile = new YamlConfiguration(); + + if (!Brew.potions.isEmpty()) { + Brew.save(configFile.createSection("Brew")); + } + + if (!BCauldron.bcauldrons.isEmpty() || oldData.contains("BCauldron")) { + BCauldron.save(configFile.createSection("BCauldron"), oldData.getConfigurationSection("BCauldron")); + } + + if (!Barrel.barrels.isEmpty() || oldData.contains("Barrel")) { + Barrel.save(configFile.createSection("Barrel"), oldData.getConfigurationSection("Barrel")); + } + + if (!BPlayer.isEmpty()) { + BPlayer.save(configFile.createSection("Player")); + } + + if (!Wakeup.wakeups.isEmpty() || oldData.contains("Wakeup")) { + Wakeup.save(configFile.createSection("Wakeup"), oldData.getConfigurationSection("Wakeup")); + } + + saveWorldNames(configFile, oldData.getConfigurationSection("Worlds")); + configFile.set("Version", dataVersion); + + collected = true; + if (P.p.isEnabled()) { + P.p.getServer().getScheduler().runTaskAsynchronously(P.p, new WriteData(configFile)); + } else { + new WriteData(configFile).run(); + } + } + + // Finish the collection of data immediately + public void now() { + if (!read.done) { + read.cancel(); + read.run(); + } + if (!collected) { + cancel(); + run(); + } + } + + + + // Save all data. Takes a boolean whether all data should be collected in instantly + public static void save(boolean collectInstant) { + long time = System.nanoTime(); + if (running != null) { + P.p.log("Another Save was started while a Save was in Progress"); + if (collectInstant) { + running.now(); + } + return; + } + File datafile = new File(P.p.getDataFolder(), "data.yml"); + + if (datafile.exists()) { + ReadOldData read = new ReadOldData(); + if (collectInstant) { + read.run(); + running = new DataSave(read); + running.run(); + } else { + read.runTaskAsynchronously(P.p); + running = new DataSave(read); + running.runTaskTimer(P.p, 1, 2); + } + } else { + running = new DataSave(null); + running.runTask(P.p); + } + P.p.debugLog("saving: " + ((System.nanoTime() - time) / 1000000.0) + "ms"); + } + + public static void autoSave() { + if (lastSave >= autosave) { + save(false);// save all data + } else { + lastSave++; + } + } + + public static void saveWorldNames(FileConfiguration root, ConfigurationSection old) { + if (old != null) { + root.set("Worlds", old); + } + for (World world : P.p.getServer().getWorlds()) { + String worldName = world.getName(); + if (worldName.startsWith("DXL_")) { + worldName = P.p.getDxlName(worldName); + root.set("Worlds." + worldName, 0); + } else { + worldName = world.getUID().toString(); + root.set("Worlds." + worldName, world.getName()); + } + } + } +} diff --git a/src/com/dre/brewery/DataUpdater.java b/src/com/dre/brewery/filedata/DataUpdater.java similarity index 96% rename from src/com/dre/brewery/DataUpdater.java rename to src/com/dre/brewery/filedata/DataUpdater.java index 9eaaf746..52d4db73 100644 --- a/src/com/dre/brewery/DataUpdater.java +++ b/src/com/dre/brewery/filedata/DataUpdater.java @@ -1,4 +1,4 @@ -package com.dre.brewery; +package com.dre.brewery.filedata; import java.io.File; import java.io.IOException; @@ -9,6 +9,8 @@ import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; +import com.dre.brewery.P; + public class DataUpdater { private FileConfiguration data; @@ -40,7 +42,7 @@ public void update(String fromVersion) { @SuppressWarnings("deprecation") public void update10() { - data.set("Version", P.dataVersion); + data.set("Version", DataSave.dataVersion); ConfigurationSection section = data.getConfigurationSection("Ingredients"); try { diff --git a/src/com/dre/brewery/LanguageReader.java b/src/com/dre/brewery/filedata/LanguageReader.java similarity index 99% rename from src/com/dre/brewery/LanguageReader.java rename to src/com/dre/brewery/filedata/LanguageReader.java index 0cb588ab..f8b5d4a9 100644 --- a/src/com/dre/brewery/LanguageReader.java +++ b/src/com/dre/brewery/filedata/LanguageReader.java @@ -1,4 +1,4 @@ -package com.dre.brewery; +package com.dre.brewery.filedata; import java.io.File; import java.io.IOException; diff --git a/src/com/dre/brewery/filedata/ReadOldData.java b/src/com/dre/brewery/filedata/ReadOldData.java new file mode 100644 index 00000000..2396ca20 --- /dev/null +++ b/src/com/dre/brewery/filedata/ReadOldData.java @@ -0,0 +1,36 @@ +package com.dre.brewery.filedata; + + +import java.io.File; + +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.scheduler.BukkitRunnable; + +import com.dre.brewery.P; + +public class ReadOldData extends BukkitRunnable { + + public FileConfiguration data; + public boolean done = false; + + @Override + public void run() { + File datafile = new File(P.p.getDataFolder(), "data.yml"); + data = YamlConfiguration.loadConfiguration(datafile); + + if (DataSave.lastBackup > 10) { + datafile.renameTo(new File(P.p.getDataFolder(), "dataBackup.yml")); + DataSave.lastBackup = 0; + } else { + DataSave.lastBackup++; + } + + done = true; + } + + public FileConfiguration getData() { + return data; + } + +} diff --git a/src/com/dre/brewery/filedata/WriteData.java b/src/com/dre/brewery/filedata/WriteData.java new file mode 100644 index 00000000..c6248515 --- /dev/null +++ b/src/com/dre/brewery/filedata/WriteData.java @@ -0,0 +1,32 @@ +package com.dre.brewery.filedata; + + +import java.io.File; +import java.io.IOException; + +import org.bukkit.configuration.file.FileConfiguration; + +import com.dre.brewery.P; + +public class WriteData implements Runnable { + + private FileConfiguration data; + + public WriteData(FileConfiguration data) { + this.data = data; + } + + @Override + public void run() { + File datafile = new File(P.p.getDataFolder(), "data.yml"); + + try { + data.save(datafile); + } catch (IOException e) { + e.printStackTrace(); + } + + DataSave.lastSave = 1; + DataSave.running = null; + } +} diff --git a/src/com/dre/brewery/listeners/WorldListener.java b/src/com/dre/brewery/listeners/WorldListener.java index 2189dbb9..b2f6979f 100644 --- a/src/com/dre/brewery/listeners/WorldListener.java +++ b/src/com/dre/brewery/listeners/WorldListener.java @@ -10,6 +10,7 @@ import com.dre.brewery.P; import com.dre.brewery.BCauldron; import com.dre.brewery.Barrel; +import com.dre.brewery.filedata.DataSave; public class WorldListener implements Listener { @@ -26,7 +27,7 @@ public void onWorldLoad(WorldLoadEvent event) { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onWorldUnload(WorldUnloadEvent event) { - P.p.saveData(); + DataSave.save(true); Barrel.onUnload(event.getWorld().getName()); BCauldron.onUnload(event.getWorld().getName()); }