diff options
5 files changed, 399 insertions, 85 deletions
diff --git a/EssentialsGroupManager/src/Changelog.txt b/EssentialsGroupManager/src/Changelog.txt index 5264a88b9..b0d68e24c 100644 --- a/EssentialsGroupManager/src/Changelog.txt +++ b/EssentialsGroupManager/src/Changelog.txt @@ -64,4 +64,8 @@ v 1.5: - Added checking of subgroups for Info nodes.
- Expanded 'canUserBuild()' to include inheritance and subgroups.
- Added a config.yml setting of 'validate_toggle' for those who prefer 'mantogglevalidate' to always be off.
- - Prevent setting 'minutes' in the config to zero causing an error.
\ No newline at end of file + - Prevent setting 'minutes' in the config to zero causing an error.
+ - GM will now check to see if it's data files have been changed at each scheduled save.
+ If the files have been altered (on disc) it will reload, so long as the in-memory data hasn't changed.
+ If the files on Disc have changed AND there have been changes to it's in-memory data it will show a warning.
+ You then MUST issue a '/mansave force' to overwrite the disc files, or a '/manload' to overwrite the memory data.
\ No newline at end of file diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GlobalGroups.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GlobalGroups.java index a7d0dd920..1114bef95 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GlobalGroups.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GlobalGroups.java @@ -33,10 +33,10 @@ public class GlobalGroups { private YamlConfiguration GGroups;
private Map<String, Group> groups;
- /**
- *
- */
+
+ protected long timeStampGroups = 0;
protected boolean haveGroupsChanged = false;
+ protected File GlobalGroupsFile = null;
public GlobalGroups(GroupManager plugin) {
this.plugin = plugin;
@@ -59,6 +59,19 @@ public class GlobalGroups { }
/**
+ * @return the timeStampGroups
+ */
+ public long getTimeStampGroups() {
+ return timeStampGroups;
+ }
+ /**
+ * @param timeStampGroups the timeStampGroups to set
+ */
+ protected void setTimeStampGroups(long timeStampGroups) {
+ this.timeStampGroups = timeStampGroups;
+ }
+
+ /**
* @param haveGroupsChanged
* the haveGroupsChanged to set
*/
@@ -71,9 +84,12 @@ public class GlobalGroups { GGroups = new YamlConfiguration();
groups = new HashMap<String, Group>();
+
+ GroupManager.setLoaded(false);
// READ globalGroups FILE
- File GlobalGroupsFile = new File(plugin.getDataFolder(), "globalgroups.yml");
+ if (GlobalGroupsFile == null)
+ GlobalGroupsFile = new File(plugin.getDataFolder(), "globalgroups.yml");
if (!GlobalGroupsFile.exists()) {
try {
@@ -129,7 +145,10 @@ public class GlobalGroups { addGroup(newGroup);
}
- GlobalGroupsFile = null;
+ removeGroupsChangedFlag();
+ setTimeStampGroups(GlobalGroupsFile.lastModified());
+ GroupManager.setLoaded(true);
+ //GlobalGroupsFile = null;
}
@@ -137,47 +156,79 @@ public class GlobalGroups { * Write the globalgroups.yml file
*/
- public void writeGroups() {
-
- File GlobalGroupsFile = new File(plugin.getDataFolder(), "globalgroups.yml");
-
- Map<String, Object> root = new HashMap<String, Object>();
-
- Map<String, Object> groupsMap = new HashMap<String, Object>();
- root.put("groups", groupsMap);
- for (String groupKey : groups.keySet()) {
- Group group = groups.get(groupKey);
-
- // Group header
- Map<String, Object> aGroupMap = new HashMap<String, Object>();
- groupsMap.put(group.getName(), aGroupMap);
-
- // Info nodes
- Map<String, Object> infoMap = new HashMap<String, Object>();
- aGroupMap.put("info", infoMap);
-
- for (String infoKey : group.getVariables().getVarKeyList()) {
- infoMap.put(infoKey, group.getVariables().getVarObject(infoKey));
- }
-
- // Permission nodes
- aGroupMap.put("permissions", group.getPermissionList());
- }
-
- if (!root.isEmpty()) {
- DumperOptions opt = new DumperOptions();
- opt.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
- final Yaml yaml = new Yaml(opt);
- try {
- yaml.dump(root, new OutputStreamWriter(new FileOutputStream(GlobalGroupsFile), "UTF-8"));
- } catch (UnsupportedEncodingException ex) {
- } catch (FileNotFoundException ex) {
- }
- }
+ public void writeGroups(boolean overwrite) {
+
+ //File GlobalGroupsFile = new File(plugin.getDataFolder(), "globalgroups.yml");
+
+ if (haveGroupsChanged()) {
+ if (overwrite || (!overwrite && (getTimeStampGroups() >= GlobalGroupsFile.lastModified()))) {
+ Map<String, Object> root = new HashMap<String, Object>();
+
+ Map<String, Object> groupsMap = new HashMap<String, Object>();
+ root.put("groups", groupsMap);
+ for (String groupKey : groups.keySet()) {
+ Group group = groups.get(groupKey);
+
+ // Group header
+ Map<String, Object> aGroupMap = new HashMap<String, Object>();
+ groupsMap.put(group.getName(), aGroupMap);
+
+ // Info nodes
+ Map<String, Object> infoMap = new HashMap<String, Object>();
+ aGroupMap.put("info", infoMap);
+
+ for (String infoKey : group.getVariables().getVarKeyList()) {
+ infoMap.put(infoKey, group.getVariables().getVarObject(infoKey));
+ }
+
+ // Permission nodes
+ aGroupMap.put("permissions", group.getPermissionList());
+ }
+
+ if (!root.isEmpty()) {
+ DumperOptions opt = new DumperOptions();
+ opt.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+ final Yaml yaml = new Yaml(opt);
+ try {
+ yaml.dump(root, new OutputStreamWriter(new FileOutputStream(GlobalGroupsFile), "UTF-8"));
+ } catch (UnsupportedEncodingException ex) {
+ } catch (FileNotFoundException ex) {
+ }
+ }
+ setTimeStampGroups(GlobalGroupsFile.lastModified());
+ } else {
+ // Newer file found.
+ GroupManager.logger.log(Level.WARNING, "Newer GlobalGroups file found, but we have local changes!");
+ throw new IllegalStateException("Unable to save unless you issue a '/mansave force'");
+ }
+ removeGroupsChangedFlag();
+ } else {
+ //Check for newer file as no local changes.
+ if (getTimeStampGroups() < GlobalGroupsFile.lastModified()) {
+ System.out.print("Newer GlobalGroups file found (Loading changes)!");
+ // Backup GlobalGroups file
+ backupFile();
+ load();
+ }
+ }
}
/**
+ * Backup the BlobalGroups file
+ * @param w
+ */
+ private void backupFile() {
+
+ File backupFile = new File(plugin.getBackupFolder(), "bkp_ggroups_" + Tasks.getDateString() + ".yml");
+ try {
+ Tasks.copy(GlobalGroupsFile, backupFile);
+ } catch (IOException ex) {
+ GroupManager.logger.log(Level.SEVERE, null, ex);
+ }
+ }
+
+ /**
* Adds a group, or replaces an existing one.
*
* @param groupToAdd
@@ -322,4 +373,21 @@ public class GlobalGroups { }
+ /**
+ * @return the globalGroupsFile
+ */
+ public File getGlobalGroupsFile() {
+ return GlobalGroupsFile;
+ }
+
+ /**
+ *
+ */
+ public void removeGroupsChangedFlag() {
+ setGroupsChanged(false);
+ for (Group g : groups.values()) {
+ g.flagAsSaved();
+ }
+ }
+
}
\ No newline at end of file diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java index ff77a76a4..8964c1cd1 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java @@ -90,7 +90,11 @@ public class GroupManager extends JavaPlugin { disableScheduler(); // Shutdown before we save, so it doesn't interfere. if (worldsHolder != null) { - worldsHolder.saveChanges(); + try { + worldsHolder.saveChanges(false); + } catch (IllegalStateException ex) { + GroupManager.logger.log(Level.WARNING, ex.getMessage()); + } } WorldEvents = null; @@ -177,7 +181,12 @@ public class GroupManager extends JavaPlugin { @Override public void run() { - worldsHolder.saveChanges(); + try { + worldsHolder.saveChanges(false); + GroupManager.logger.log(Level.INFO, " Data files refreshed."); + } catch (IllegalStateException ex) { + GroupManager.logger.log(Level.WARNING, ex.getMessage()); + } } }; scheduler = new ScheduledThreadPoolExecutor(1); @@ -1504,9 +1513,20 @@ public class GroupManager extends JavaPlugin { return true; case mansave: - worldsHolder.saveChanges(); - sender.sendMessage(ChatColor.YELLOW + " The changes were saved."); + + boolean forced = false; + + if ((args.length == 1) && (args[0].equalsIgnoreCase("force"))) + forced = true; + + try { + worldsHolder.saveChanges(forced); + sender.sendMessage(ChatColor.YELLOW + " The changes were saved."); + } catch (IllegalStateException ex) { + sender.sendMessage(ChatColor.RED + ex.getMessage()); + } return true; + case manload: // THIS CASE DONT NEED SENDER if (args.length > 0) { diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/WorldDataHolder.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/WorldDataHolder.java index a9f7e3f6e..d71fc64c0 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/WorldDataHolder.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/WorldDataHolder.java @@ -45,20 +45,23 @@ public class WorldDataHolder { * The actual groups holder */ protected Map<String, Group> groups = new HashMap<String, Group>(); - /** + /** * The actual users holder */ protected Map<String, User> users = new HashMap<String, User>(); - /** + + /** * Points to the default group */ protected Group defaultGroup = null; + /** * The file, which this class loads/save data from/to * @deprecated */ @Deprecated protected File f; + /** * */ @@ -79,8 +82,17 @@ public class WorldDataHolder { * */ protected boolean haveGroupsChanged = false; - /** + * + */ + protected long timeStampGroups = 0; + /** + * + */ + protected long timeStampUsers = 0; + + + /** * Prevent direct instantiation * @param worldName */ @@ -302,14 +314,34 @@ public class WorldDataHolder { */ public void reload() { try { - WorldDataHolder ph = load(this.getName(), getGroupsFile(), getUsersFile()); - this.defaultGroup = ph.defaultGroup; - this.groups = ph.groups; - this.users = ph.users; + reloadGroups(); + reloadUsers(); } catch (Exception ex) { Logger.getLogger(WorldDataHolder.class.getName()).log(Level.SEVERE, null, ex); } } + + public void reloadGroups() { + GroupManager.setLoaded(false); + try { + resetGroups(); + loadGroups(this, getGroupsFile()); + } catch (Exception ex) { + Logger.getLogger(WorldDataHolder.class.getName()).log(Level.SEVERE, null, ex); + } + GroupManager.setLoaded(true); + } + + public void reloadUsers() { + GroupManager.setLoaded(false); + try { + resetUsers(); + loadUsers(this, getUsersFile()); + } catch (Exception ex) { + Logger.getLogger(WorldDataHolder.class.getName()).log(Level.SEVERE, null, ex); + } + GroupManager.setLoaded(true); + } /** * Save by yourself! @@ -469,7 +501,8 @@ public class WorldDataHolder { } /** - * Returns a data holder for the given file + * Returns a NEW data holder containing data read from the files + * * @param worldName * @param groupsFile * @param usersFile @@ -477,18 +510,57 @@ public class WorldDataHolder { * @throws FileNotFoundException * @throws IOException */ - @SuppressWarnings({"rawtypes", "unchecked"}) public static WorldDataHolder load(String worldName, File groupsFile, File usersFile) throws FileNotFoundException, IOException { - WorldDataHolder ph = new WorldDataHolder(worldName); - ph.groupsFile = groupsFile; - ph.usersFile = usersFile; + WorldDataHolder ph = new WorldDataHolder(worldName); + + GroupManager.setLoaded(false); + loadGroups(ph, groupsFile); + loadUsers(ph, usersFile); + GroupManager.setLoaded(true); + + return ph; + } + + /** + * Updates the WorldDataHolder from the files + * + * @param ph + * @param groupsFile + * @param usersFile + * @return + * @throws FileNotFoundException + * @throws IOException + */ + public static WorldDataHolder Update(WorldDataHolder ph, File groupsFile, File usersFile) throws FileNotFoundException, IOException { + + GroupManager.setLoaded(false); + ph.resetGroups(); + loadGroups(ph, groupsFile); + ph.resetUsers(); + loadUsers(ph, usersFile); + GroupManager.setLoaded(true); + + return ph; + } + + /** + * Updates the WorldDataHolder from the Groups file + * + * @param worldName + * @param groupsFile + * @return + * @throws FileNotFoundException + * @throws IOException + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + protected static void loadGroups(WorldDataHolder ph, File groupsFile) throws FileNotFoundException, IOException { //READ GROUPS FILE Yaml yamlGroups = new Yaml(new SafeConstructor()); Map<String, Object> groupsRootDataNode; if (!groupsFile.exists()) { - throw new IllegalArgumentException("The file which should contain permissions does not exist!\n" + groupsFile.getPath()); + throw new IllegalArgumentException("The file which should contain groups does not exist!\n" + groupsFile.getPath()); } FileInputStream groupsInputStream = new FileInputStream(groupsFile); try { @@ -584,13 +656,32 @@ public class WorldDataHolder { } } } + + ph.removeGroupsChangedFlag(); + // Update the LastModified time. + ph.groupsFile = groupsFile; + ph.setTimeStamps(); - + //return ph; + } + + /** + * Updates the WorldDataHolder from the Users file + * + * @param worldName + * @param usersFile + * @return + * @throws FileNotFoundException + * @throws IOException + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + protected static void loadUsers(WorldDataHolder ph, File usersFile) throws FileNotFoundException, IOException { + //READ USERS FILE Yaml yamlUsers = new Yaml(new SafeConstructor()); Map<String, Object> usersRootDataNode; - if (!groupsFile.exists()) { - throw new IllegalArgumentException("The file which should contain permissions does not exist!\n" + usersFile.getPath()); + if (!usersFile.exists()) { + throw new IllegalArgumentException("The file which should contain users does not exist!\n" + usersFile.getPath()); } FileInputStream usersInputStream = new FileInputStream(usersFile); try { @@ -609,7 +700,7 @@ public class WorldDataHolder { // Stop loading if the file is empty if (allUsersNode == null) - return ph; + return ; for (String usersKey : allUsersNode.keySet()) { Map<String, Object> thisUserNode = (Map<String, Object>) allUsersNode.get(usersKey); @@ -672,7 +763,13 @@ public class WorldDataHolder { thisUser.setGroup(ph.defaultGroup); } } - return ph; + + ph.removeUsersChangedFlag(); + // Update the LastModified time. + ph.usersFile = usersFile; + ph.setTimeStamps(); + + //return ph; } /** @@ -805,6 +902,11 @@ public class WorldDataHolder { } catch (FileNotFoundException ex) { } } + + // Update the LastModified time. + ph.groupsFile = groupsFile; + ph.setTimeStampGroups(groupsFile.lastModified()); + ph.removeGroupsChangedFlag(); /*FileWriter tx = null; try { @@ -870,6 +972,12 @@ public class WorldDataHolder { } catch (FileNotFoundException ex) { } } + + // Update the LastModified time. + ph.usersFile = usersFile; + ph.setTimeStampUsers(usersFile.lastModified()); + ph.removeUsersChangedFlag(); + /*FileWriter tx = null; try { tx = new FileWriter(usersFile, false); @@ -992,4 +1100,64 @@ public class WorldDataHolder { public String getName() { return name; } + + /** + * Resets Groups. + */ + public void resetGroups() { + this.defaultGroup = null; + this.groups = new HashMap<String, Group>(); + } + /** + * Resets Users + */ + public void resetUsers() { + this.users = new HashMap<String, User>(); + } + + /** + * @return the groups + */ + public Map<String, Group> getGroups() { + return groups; + } + /** + * @return the users + */ + public Map<String, User> getUsers() { + return users; + } + + /** + * @return the timeStampGroups + */ + public long getTimeStampGroups() { + return timeStampGroups; + } + /** + * @return the timeStampUsers + */ + public long getTimeStampUsers() { + return timeStampUsers; + } + + /** + * @param timeStampGroups the timeStampGroups to set + */ + protected void setTimeStampGroups(long timeStampGroups) { + this.timeStampGroups = timeStampGroups; + } + /** + * @param timeStampUsers the timeStampUsers to set + */ + protected void setTimeStampUsers(long timeStampUsers) { + this.timeStampUsers = timeStampUsers; + } + + public void setTimeStamps() { + if (groupsFile != null) + setTimeStampGroups(groupsFile.lastModified()); + if (usersFile != null) + setTimeStampUsers(usersFile.lastModified()); + } } diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java index f5b55a25a..8ea8e28c9 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java @@ -160,47 +160,97 @@ public class WorldsHolder { public void reloadWorld(String worldName) { getWorldData(worldName).reload(); } + + /** + * Wrapper to retain backwards compatibility + * (call this function to auto overwrite files) + */ + public void saveChanges() { + saveChanges(true); + } /** * */ - public void saveChanges() { + public void saveChanges(boolean overwrite) { ArrayList<WorldDataHolder> alreadyDone = new ArrayList<WorldDataHolder>(); + Tasks.removeOldFiles(plugin, plugin.getBackupFolder()); + for (OverloadedWorldHolder w : worldsData.values()) { if (alreadyDone.contains(w)) { continue; } - Tasks.removeOldFiles(plugin, plugin.getBackupFolder()); if (w == null) { GroupManager.logger.severe("WHAT HAPPENED?"); continue; } if (w.haveGroupsChanged()) { - //String groupsFolderName = w.getGroupsFile().getParentFile().getName(); - File backupGroups = new File(plugin.getBackupFolder(), "bkp_" + w.getName() + "_g_" + Tasks.getDateString() + ".yml"); - try { - Tasks.copy(w.getGroupsFile(), backupGroups); - } catch (IOException ex) { - GroupManager.logger.log(Level.SEVERE, null, ex); - } - WorldDataHolder.writeGroups(w, w.getGroupsFile()); - w.removeGroupsChangedFlag(); + if (overwrite || (!overwrite && (w.getTimeStampGroups() >= w.getGroupsFile().lastModified()))) { + // Backup Groups file + backupFile(w,true); + + WorldDataHolder.writeGroups(w, w.getGroupsFile()); + //w.removeGroupsChangedFlag(); + } else { + // Newer file found. + GroupManager.logger.log(Level.WARNING, "Newer Groups file found for " + w.getName() + ", but we have local changes!"); + throw new IllegalStateException("Unable to save unless you issue a '/mansave force'"); + } + } else { + //Check for newer file as no local changes. + if (w.getTimeStampGroups() < w.getGroupsFile().lastModified()) { + System.out.print("Newer Groups file found (Loading changes)!"); + // Backup Users file + backupFile(w,false); + w.reloadGroups(); + } } if (w.haveUsersChanged()) { - File backupUsers = new File(plugin.getBackupFolder(), "bkp_" + w.getName() + "_u_" + Tasks.getDateString() + ".yml"); - try { - Tasks.copy(w.getUsersFile(), backupUsers); - } catch (IOException ex) { - GroupManager.logger.log(Level.SEVERE, null, ex); - } - WorldDataHolder.writeUsers(w, w.getUsersFile()); - w.removeUsersChangedFlag(); + if (overwrite || (!overwrite && (w.getTimeStampUsers() >= w.getUsersFile().lastModified()))) { + // Backup Users file + backupFile(w,false); + + WorldDataHolder.writeUsers(w, w.getUsersFile()); + //w.removeUsersChangedFlag(); + } else { + // Newer file found. + GroupManager.logger.log(Level.WARNING, "Newer Users file found for " + w.getName() + ", but we have local changes!"); + throw new IllegalStateException("Unable to save unless you issue a '/mansave force'"); + } + } else { + //Check for newer file as no local changes. + if (w.getTimeStampUsers() < w.getUsersFile().lastModified()) { + System.out.print("Newer Users file found (Loading changes)!"); + // Backup Users file + backupFile(w,false); + w.reloadUsers(); + } } alreadyDone.add(w); } // Write Global Groups if (GroupManager.getGlobalGroups().haveGroupsChanged()) { - GroupManager.getGlobalGroups().writeGroups(); + GroupManager.getGlobalGroups().writeGroups(overwrite); + } else { + if (GroupManager.getGlobalGroups().getTimeStampGroups() < GroupManager.getGlobalGroups().getGlobalGroupsFile().lastModified()) { + System.out.print("Newer GlobalGroups file found (Loading changes)!"); + GroupManager.getGlobalGroups().load(); + } + } + } + + /** + * Backup the Groups/Users file + * @param w + * @param groups + */ + private void backupFile(OverloadedWorldHolder w, Boolean groups) { + + File backupFile = new File(plugin.getBackupFolder(), "bkp_" + w.getName() + (groups ? "_g_" : "_u_") + Tasks.getDateString() + ".yml"); + try { + Tasks.copy((groups ? w.getGroupsFile() : w.getUsersFile()), backupFile); + } catch (IOException ex) { + GroupManager.logger.log(Level.SEVERE, null, ex); } } @@ -407,6 +457,10 @@ public class WorldsHolder { } try { OverloadedWorldHolder thisWorldData = new OverloadedWorldHolder(WorldDataHolder.load(worldName, groupsFile, usersFile)); + + // Set the file TimeStamps as it will be default from the initial load. + thisWorldData.setTimeStamps(); + if (thisWorldData != null) { GroupManager.logger.finest("Successful load of world " + worldName + "..."); worldsData.put(worldName.toLowerCase(), thisWorldData); |