diff --git a/.gitignore b/.gitignore
index 137105071..f7298d092 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,4 +46,4 @@ dependency-reduced-pom.xml
/Essentials/userdata/testplayer1.yml
/Essentials/usermap.csv
/Essentials/userdata
-/EssentialsGroupManager/
+/EssentialsGroupManager/target/
diff --git a/EssentialsGroupManager/pom.xml b/EssentialsGroupManager/pom.xml
new file mode 100644
index 000000000..0c742f58e
--- /dev/null
+++ b/EssentialsGroupManager/pom.xml
@@ -0,0 +1,44 @@
+
+
+ 4.0.0
+
+ org.anjocaido
+ EssentialsGroupManager
+ 1.0-SNAPSHOT
+
+
+ net.ess3
+ EssentialsXParent
+ 2.0.1
+
+
+
+
+ GPLv3
+ http://www.gnu.org/copyleft/gpl.html
+
+
+
+
+
+ net.milkbowl.vault
+ VaultAPI
+ 1.5
+ provided
+
+
+
+
+
+ spigot-repo
+ https://hub.spigotmc.org/nexus/content/groups/public/
+
+
+ vault-repo
+ http://nexus.theyeticave.net/content/repositories/pub_releases
+
+
+
+
\ No newline at end of file
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/GMConfiguration.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/GMConfiguration.java
new file mode 100644
index 000000000..19515b36c
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/GMConfiguration.java
@@ -0,0 +1,211 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.anjocaido.groupmanager;
+
+import org.anjocaido.groupmanager.utils.Tasks;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
+import org.yaml.snakeyaml.reader.UnicodeReader;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+
+/**
+ * @author gabrielcouto
+ */
+public class GMConfiguration {
+
+ private boolean allowCommandBlocks = false;
+ private boolean opOverride = true;
+ private boolean toggleValidate = true;
+ private Integer saveInterval = 10;
+ private Integer backupDuration = 24;
+ private String loggerLevel = "OFF";
+ private Map mirrorsMap;
+
+
+ private GroupManager plugin;
+ private Map GMconfig;
+
+ public GMConfiguration(GroupManager plugin) {
+
+ this.plugin = plugin;
+
+ /*
+ * Set defaults
+ */
+ allowCommandBlocks = false;
+ opOverride = true;
+ toggleValidate = true;
+ saveInterval = 10;
+ backupDuration = 24;
+ loggerLevel = "OFF";
+
+ load();
+ }
+
+ @SuppressWarnings("unchecked")
+ public void load() {
+
+ if (!plugin.getDataFolder().exists()) {
+ plugin.getDataFolder().mkdirs();
+ }
+
+ File configFile = new File(plugin.getDataFolder(), "config.yml");
+
+ if (!configFile.exists()) {
+ try {
+ Tasks.copy(plugin.getResourceAsStream("config.yml"), configFile);
+ } catch (IOException ex) {
+ GroupManager.logger.log(Level.SEVERE, "Error creating a new config.yml", ex);
+ }
+ }
+
+ Yaml configYAML = new Yaml(new SafeConstructor());
+
+ try {
+ FileInputStream configInputStream = new FileInputStream(configFile);
+ GMconfig = (Map) configYAML.load(new UnicodeReader(configInputStream));
+ configInputStream.close();
+
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("The following file couldn't pass on Parser.\n" + configFile.getPath(), ex);
+ }
+
+ /*
+ * Read our config settings and store them for reading later.
+ */
+ try {
+ Map config = getElement("config", getElement("settings", GMconfig));
+
+ try {
+ allowCommandBlocks = (Boolean) config.get("allow_commandblocks");
+ } catch (Exception ex) {
+ GroupManager.logger.log(Level.SEVERE, "Missing or corrupt 'allow_commandblocks' node. Using default settings", ex);
+ }
+
+ try {
+ opOverride = (Boolean) config.get("opOverrides");
+ } catch (Exception ex) {
+ GroupManager.logger.log(Level.SEVERE, "Missing or corrupt 'opOverrides' node. Using default settings", ex);
+ }
+
+ try {
+ toggleValidate = (Boolean) config.get("validate_toggle");
+ } catch (Exception ex) {
+ GroupManager.logger.log(Level.SEVERE, "Missing or corrupt 'validate_toggle' node. Using default settings", ex);
+ }
+
+ /*
+ * data node for save/backup timers.
+ */
+ try {
+ Map save = getElement("save", getElement("data", getElement("settings", GMconfig)));
+
+ try {
+ saveInterval = (Integer) save.get("minutes");
+ } catch (Exception ex) {
+ GroupManager.logger.log(Level.SEVERE, "Missing or corrupt 'minutes' node. Using default setting", ex);
+ }
+
+ try {
+ backupDuration = (Integer) save.get("hours");
+ } catch (Exception ex) {
+ GroupManager.logger.log(Level.SEVERE, "Missing or corrupt 'hours' node. Using default setting", ex);
+ }
+
+ } catch (Exception ex) {
+ GroupManager.logger.log(Level.SEVERE, "Missing or corrupt 'data' node. Using default settings", ex);
+ }
+
+
+ Object level = ((Map) getElement("settings", GMconfig).get("logging")).get("level");
+ if (level instanceof String) {
+ loggerLevel = (String) level;
+ }
+
+ /*
+ * Store our mirrors map for parsing later.
+ */
+ mirrorsMap = (Map) ((Map) GMconfig.get("settings")).get("mirrors");
+
+ if (mirrorsMap == null) {
+ throw new Exception();
+ }
+
+ } catch (Exception ex) {
+ /*
+ * Flag the error and use defaults
+ */
+ GroupManager.logger.log(Level.SEVERE, "There are errors in your config.yml. Using default settings", ex);
+
+ mirrorsMap = new HashMap();
+ }
+ // Setup defaults
+ adjustLoggerLevel();
+ plugin.setValidateOnlinePlayer(isToggleValidate());
+ }
+
+ @SuppressWarnings("unchecked")
+ private Map getElement(String element, Map map) {
+
+ if (!map.containsKey(element)) {
+ throw new IllegalArgumentException("The config.yml has no '" + element + ".\n");
+ }
+
+ return (Map) map.get(element);
+
+ }
+
+ public boolean isAllowCommandBlocks() {
+
+ return allowCommandBlocks;
+ }
+
+ public boolean isOpOverride() {
+
+ return opOverride;
+ }
+
+ public boolean isToggleValidate() {
+
+ return toggleValidate;
+ }
+
+ public Integer getSaveInterval() {
+
+ return saveInterval;
+ }
+
+ public Integer getBackupDuration() {
+
+ return backupDuration;
+ }
+
+ public void adjustLoggerLevel() {
+
+ try {
+ GroupManager.logger.setLevel(Level.parse(loggerLevel));
+ return;
+ } catch (Exception e) {
+ }
+
+ GroupManager.logger.setLevel(Level.INFO);
+ }
+
+ public Map getMirrorsMap() {
+
+ if (!mirrorsMap.isEmpty()) {
+ return mirrorsMap;
+ }
+ return null;
+
+ }
+
+}
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/GlobalGroups.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/GlobalGroups.java
new file mode 100644
index 000000000..5989229fa
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/GlobalGroups.java
@@ -0,0 +1,486 @@
+package org.anjocaido.groupmanager;
+
+import org.anjocaido.groupmanager.data.Group;
+import org.anjocaido.groupmanager.events.GMGroupEvent;
+import org.anjocaido.groupmanager.utils.PermissionCheckResult;
+import org.anjocaido.groupmanager.utils.Tasks;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
+import org.yaml.snakeyaml.reader.UnicodeReader;
+
+import java.io.*;
+import java.util.*;
+import java.util.logging.Level;
+
+/**
+ * @author ElgarL
+ */
+public class GlobalGroups {
+
+ private GroupManager plugin;
+ //private Yaml GGroups;
+
+ private final Map groups = Collections.synchronizedMap(new HashMap());
+
+ protected long timeStampGroups = 0;
+ protected boolean haveGroupsChanged = false;
+ protected File GlobalGroupsFile = null;
+
+ public GlobalGroups(GroupManager plugin) {
+
+ this.plugin = plugin;
+ load();
+ }
+
+ /**
+ * @return the haveGroupsChanged
+ */
+ public boolean haveGroupsChanged() {
+
+ if (this.haveGroupsChanged) {
+ return true;
+ }
+ synchronized (groups) {
+ for (Group g : groups.values()) {
+ if (g.isChanged()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @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
+ */
+ public void setGroupsChanged(boolean haveGroupsChanged) {
+
+ this.haveGroupsChanged = haveGroupsChanged;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void load() {
+
+ Yaml GGroupYAML = new Yaml(new SafeConstructor());
+ Map GGroups;
+
+ GroupManager.setLoaded(false);
+
+ // READ globalGroups FILE
+ if (GlobalGroupsFile == null) {
+ GlobalGroupsFile = new File(plugin.getDataFolder(), "globalgroups.yml");
+ }
+
+ if (!GlobalGroupsFile.exists()) {
+ try {
+ // Create a new file if it doesn't exist.
+ Tasks.copy(plugin.getResourceAsStream("globalgroups.yml"), GlobalGroupsFile);
+ } catch (IOException ex) {
+ GroupManager.logger.log(Level.SEVERE, null, ex);
+ }
+ }
+
+ /*
+ * Load the YAML file.
+ */
+ try {
+ FileInputStream groupsInputStream = new FileInputStream(GlobalGroupsFile);
+ GGroups = (Map) GGroupYAML.load(new UnicodeReader(groupsInputStream));
+ groupsInputStream.close();
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("The following file couldn't pass on Parser.\n" + GlobalGroupsFile.getPath(), ex);
+ }
+
+ // Clear out old groups
+ resetGlobalGroups();
+
+ if (!GGroups.keySet().isEmpty()) {
+ // Read all global groups
+ Map allGroups = new HashMap();
+
+ try {
+ allGroups = (Map) GGroups.get("groups");
+ } catch (Exception ex) {
+ // ex.printStackTrace();
+ throw new IllegalArgumentException("Your " + GlobalGroupsFile.getPath() + " file is invalid. See console for details.", ex);
+ }
+
+ // Load each groups permissions list.
+ if (allGroups != null) {
+
+ Iterator groupItr = allGroups.keySet().iterator();
+ String groupName;
+ Integer groupCount = 0;
+
+ /*
+ * loop each group entry
+ * and read it's data.
+ */
+ while (groupItr.hasNext()) {
+ try {
+ groupCount++;
+ // Attempt to fetch the next group name.
+ groupName = groupItr.next();
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Invalid group name for GlobalGroup entry (" + groupCount + ") in file: " + GlobalGroupsFile.getPath(), ex);
+ }
+
+ /*
+ * Create a new group with this name.
+ */
+ Group newGroup = new Group(groupName.toLowerCase());
+ Object element;
+
+ // Permission nodes
+ try {
+ element = ((Map) allGroups.get(groupName)).get("permissions");
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("The GlobalGroup ' " + groupName + "' is formatted incorrectly: ", ex);
+ }
+
+ if (element != null) {
+ if (element instanceof List) {
+ try {
+ for (String node : (List) element) {
+ if ((node != null) && !node.isEmpty()) {
+ newGroup.addPermission(node);
+ }
+ }
+ } catch (ClassCastException ex) {
+ throw new IllegalArgumentException("Invalid permission node for global group: " + groupName, ex);
+ }
+ } else if (element instanceof String) {
+ if ((element != null) && !((String) element).isEmpty()) {
+ newGroup.addPermission((String) element);
+ }
+ } else {
+ throw new IllegalArgumentException("Unknown type of permission node for global group: " + groupName);
+ }
+ }
+
+// // Info nodes
+// try {
+// element = ((Map)allGroups.get(groupName)).get("info");
+// } catch ( Exception ex) {
+// throw new IllegalArgumentException("The GlobalGroup ' " + groupName + "' is formatted incorrectly: ", ex);
+// }
+//
+// if (element != null)
+// if (element instanceof MemorySection) {
+// Map vars = new HashMap();
+// for (String key : ((MemorySection) element).getKeys(false)) {
+// vars.put(key, ((MemorySection) element).get(key));
+// }
+// newGroup.setVariables(vars);
+// } else
+// throw new IllegalArgumentException("Unknown type of info node for global group: " + groupName);
+
+ // Push a new group
+ addGroup(newGroup);
+ }
+ }
+
+ removeGroupsChangedFlag();
+ }
+
+ setTimeStampGroups(GlobalGroupsFile.lastModified());
+ GroupManager.setLoaded(true);
+ // GlobalGroupsFile = null;
+ }
+
+ /**
+ * Write the globalgroups.yml file
+ */
+
+ public void writeGroups(boolean overwrite) {
+
+ // File GlobalGroupsFile = new File(plugin.getDataFolder(), "globalgroups.yml");
+
+ if (haveGroupsChanged()) {
+ if (overwrite || (!overwrite && (getTimeStampGroups() >= GlobalGroupsFile.lastModified()))) {
+ Map root = new HashMap();
+
+ Map groupsMap = new HashMap();
+ root.put("groups", groupsMap);
+ synchronized (groups) {
+ for (String groupKey : groups.keySet()) {
+ Group group = groups.get(groupKey);
+
+ // Group header
+ Map aGroupMap = new HashMap();
+ groupsMap.put(group.getName(), aGroupMap);
+
+// // Info nodes
+// Map infoMap = new HashMap();
+// 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
+ */
+ public void addGroup(Group groupToAdd) {
+
+ // Create a new group if it already exists
+ if (hasGroup(groupToAdd.getName())) {
+ groupToAdd = groupToAdd.clone();
+ removeGroup(groupToAdd.getName());
+ }
+
+ newGroup(groupToAdd);
+ haveGroupsChanged = true;
+ if (GroupManager.isLoaded()) {
+ GroupManager.getGMEventHandler().callEvent(groupToAdd, GMGroupEvent.Action.GROUP_ADDED);
+ }
+ }
+
+ /**
+ * Creates a new group if it doesn't already exist.
+ *
+ * @param newGroup
+ */
+ public Group newGroup(Group newGroup) {
+
+ // Push a new group
+ if (!groups.containsKey(newGroup.getName().toLowerCase())) {
+ groups.put(newGroup.getName().toLowerCase(), newGroup);
+ this.setGroupsChanged(true);
+ return newGroup;
+ }
+ return null;
+ }
+
+ /**
+ * Delete a group if it exist.
+ *
+ * @param groupName
+ */
+ public boolean removeGroup(String groupName) {
+
+ // Push a new group
+ if (groups.containsKey(groupName.toLowerCase())) {
+ groups.remove(groupName.toLowerCase());
+ this.setGroupsChanged(true);
+ if (GroupManager.isLoaded()) {
+ GroupManager.getGMEventHandler().callEvent(groupName.toLowerCase(), GMGroupEvent.Action.GROUP_REMOVED);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the Global Group exists in the globalgroups.yml
+ *
+ * @param groupName
+ *
+ * @return true if the group exists
+ */
+ public boolean hasGroup(String groupName) {
+
+ return groups.containsKey(groupName.toLowerCase());
+ }
+
+ /**
+ * Returns true if the group has the correct permission node.
+ *
+ * @param groupName
+ * @param permissionNode
+ *
+ * @return true if node exists
+ */
+ public boolean hasPermission(String groupName, String permissionNode) {
+
+ if (!hasGroup(groupName)) {
+ return false;
+ }
+
+ return groups.get(groupName.toLowerCase()).hasSamePermissionNode(permissionNode);
+
+ }
+
+ /**
+ * Returns a PermissionCheckResult of the permission node for the group to
+ * be tested against.
+ *
+ * @param groupName
+ * @param permissionNode
+ *
+ * @return PermissionCheckResult object
+ */
+ public PermissionCheckResult checkPermission(String groupName, String permissionNode) {
+
+ PermissionCheckResult result = new PermissionCheckResult();
+ result.askedPermission = permissionNode;
+ result.resultType = PermissionCheckResult.Type.NOTFOUND;
+
+ if (!hasGroup(groupName)) {
+ return result;
+ }
+
+ Group tempGroup = groups.get(groupName.toLowerCase());
+
+ if (tempGroup.hasSamePermissionNode(permissionNode)) {
+ result.resultType = PermissionCheckResult.Type.FOUND;
+ }
+ if (tempGroup.hasSamePermissionNode("-" + permissionNode)) {
+ result.resultType = PermissionCheckResult.Type.NEGATION;
+ }
+ if (tempGroup.hasSamePermissionNode("+" + permissionNode)) {
+ result.resultType = PermissionCheckResult.Type.EXCEPTION;
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a List of all permission nodes for this group null if none
+ *
+ * @param groupName
+ *
+ * @return List of all group names
+ */
+ public List getGroupsPermissions(String groupName) {
+
+ if (!hasGroup(groupName)) {
+ return null;
+ }
+
+ return groups.get(groupName.toLowerCase()).getPermissionList();
+ }
+
+ /**
+ * Returns a Set of all global group names.
+ *
+ * @return Set containing all group names.
+ */
+ /*public Set getGlobalGroups() {
+
+ return groups.keySet();
+ }*/
+
+ /**
+ * Resets GlobalGroups.
+ */
+ public void resetGlobalGroups() {
+ this.groups.clear();
+ }
+
+ /**
+ * @return a collection of the groups
+ */
+ public Group[] getGroupList() {
+ synchronized (groups) {
+ return groups.values().toArray(new Group[0]);
+ }
+ }
+
+ /**
+ * Returns the Global Group or null if it doesn't exist.
+ *
+ * @param groupName
+ *
+ * @return Group object
+ */
+ public Group getGroup(String groupName) {
+
+ if (!hasGroup(groupName)) {
+ return null;
+ }
+
+ return groups.get(groupName.toLowerCase());
+
+ }
+
+ /**
+ * @return the globalGroupsFile
+ */
+ public File getGlobalGroupsFile() {
+
+ return GlobalGroupsFile;
+ }
+
+ /**
+ *
+ */
+ public void removeGroupsChangedFlag() {
+
+ setGroupsChanged(false);
+ synchronized (groups) {
+ for (Group g : groups.values()) {
+ g.flagAsSaved();
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/GroupManager.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/GroupManager.java
new file mode 100644
index 000000000..9baaee3fd
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/GroupManager.java
@@ -0,0 +1,2294 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.anjocaido.groupmanager;
+
+import org.anjocaido.groupmanager.Tasks.BukkitPermsUpdateTask;
+import org.anjocaido.groupmanager.data.Group;
+import org.anjocaido.groupmanager.data.User;
+import org.anjocaido.groupmanager.data.Variables;
+import org.anjocaido.groupmanager.dataholder.OverloadedWorldHolder;
+import org.anjocaido.groupmanager.dataholder.worlds.WorldsHolder;
+import org.anjocaido.groupmanager.events.GMSystemEvent;
+import org.anjocaido.groupmanager.events.GMWorldListener;
+import org.anjocaido.groupmanager.events.GroupManagerEventHandler;
+import org.anjocaido.groupmanager.metrics.Metrics;
+import org.anjocaido.groupmanager.permissions.AnjoPermissionsHandler;
+import org.anjocaido.groupmanager.permissions.BukkitPermissions;
+import org.anjocaido.groupmanager.utils.GMLoggerHandler;
+import org.anjocaido.groupmanager.utils.GroupManagerPermissions;
+import org.anjocaido.groupmanager.utils.PermissionCheckResult;
+import org.anjocaido.groupmanager.utils.Tasks;
+import org.bukkit.Bukkit;
+import org.bukkit.ChatColor;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.block.Block;
+import org.bukkit.command.BlockCommandSender;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.PluginDescriptionFile;
+import org.bukkit.plugin.ServicePriority;
+import org.bukkit.plugin.java.JavaPlugin;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * @author gabrielcouto, ElgarL
+ */
+public class GroupManager extends JavaPlugin {
+
+ private File backupFolder;
+ private Runnable commiter;
+ private ScheduledThreadPoolExecutor scheduler;
+ private Map> overloadedUsers = new HashMap>();
+ private Map selectedWorlds = new HashMap();
+ private WorldsHolder worldsHolder;
+ private boolean validateOnlinePlayer = true;
+
+ private static boolean isLoaded = false;
+ protected GMConfiguration config;
+
+ protected static GlobalGroups globalGroups;
+
+ private GMLoggerHandler ch;
+
+ private static GroupManagerEventHandler GMEventHandler;
+ public static BukkitPermissions BukkitPermissions;
+ private static GMWorldListener WorldEvents;
+ public static final Logger logger = Logger.getLogger(GroupManager.class.getName());
+
+ // PERMISSIONS FOR COMMAND BEING LOADED
+ private OverloadedWorldHolder dataHolder = null;
+ private AnjoPermissionsHandler permissionHandler = null;
+
+ private String lastError = "";
+
+ @Override
+ public void onDisable() {
+
+ onDisable(false);
+ }
+
+ @Override
+ public void onEnable() {
+
+ /*
+ * Register Metrics
+ */
+ try {
+ Metrics metrics = new Metrics(this);
+ metrics.start();
+ } catch (IOException e) {
+ System.err.println("[GroupManager] Error setting up metrics");
+ }
+
+ /*
+ * Initialize the event handler
+ */
+ setGMEventHandler(new GroupManagerEventHandler(this));
+ onEnable(false);
+ }
+
+ public void onDisable(boolean restarting) {
+
+ setLoaded(false);
+
+ if (!restarting) {
+ // Unregister this service if we are shutting down.
+ this.getServer().getServicesManager().unregister(this.worldsHolder);
+ }
+
+ disableScheduler(); // Shutdown before we save, so it doesn't interfere.
+ if (worldsHolder != null) {
+ try {
+ worldsHolder.saveChanges(false);
+ } catch (IllegalStateException ex) {
+ GroupManager.logger.log(Level.WARNING, ex.getMessage());
+ }
+ }
+
+
+ // Remove all attachments before clearing
+ if (BukkitPermissions != null) {
+ BukkitPermissions.removeAllAttachments();
+ }
+
+ if (!restarting) {
+
+ if (WorldEvents != null) {
+ WorldEvents = null;
+ }
+
+ BukkitPermissions = null;
+
+ }
+
+ // EXAMPLE: Custom code, here we just output some info so we can check that all is well
+ PluginDescriptionFile pdfFile = this.getDescription();
+ System.out.println(pdfFile.getName() + " version " + pdfFile.getVersion() + " is disabled!");
+
+ if (!restarting) {
+ GroupManager.logger.removeHandler(ch);
+ }
+ }
+
+ public void onEnable(boolean restarting) {
+
+ try {
+ /*
+ * reset local variables.
+ */
+ overloadedUsers = new HashMap>();
+ selectedWorlds = new HashMap();
+ lastError = "";
+
+ /*
+ * Setup our logger if we are not restarting.
+ */
+ if (!restarting) {
+ GroupManager.logger.setUseParentHandlers(false);
+ ch = new GMLoggerHandler();
+ GroupManager.logger.addHandler(ch);
+ }
+ GroupManager.logger.setLevel(Level.ALL);
+
+ // Create the backup folder, if it doesn't exist.
+ prepareFileFields();
+ // Load the config.yml
+ prepareConfig();
+ // Load the global groups
+ globalGroups = new GlobalGroups(this);
+
+ /*
+ * Configure the worlds holder.
+ */
+ if (!restarting) {
+ worldsHolder = new WorldsHolder(this);
+ } else {
+ worldsHolder.resetWorldsHolder();
+ }
+
+ /*
+ * This should NEVER happen. No idea why it's still here.
+ */
+ PluginDescriptionFile pdfFile = this.getDescription();
+ if (worldsHolder == null) {
+ GroupManager.logger.severe("Can't enable " + pdfFile.getName() + " version " + pdfFile.getVersion() + ", bad loading!");
+ this.getServer().getPluginManager().disablePlugin(this);
+ throw new IllegalStateException("An error ocurred while loading GroupManager");
+ }
+
+ /*
+ * Prevent our registered events from triggering
+ * updates as we are not fully loaded.
+ */
+ setLoaded(false);
+
+ /*
+ * Initialize the world listener and bukkit permissions
+ * to handle events if this is a fresh start
+ *
+ * else
+ *
+ * Reset bukkit perms.
+ */
+ if (!restarting) {
+ WorldEvents = new GMWorldListener(this);
+ BukkitPermissions = new BukkitPermissions(this);
+ } else {
+ BukkitPermissions.reset();
+ }
+
+ /*
+ * Start the scheduler for data saving.
+ */
+ enableScheduler();
+
+ /*
+ * Schedule a Bukkit Permissions update for 1 tick later.
+ * All plugins will be loaded by then
+ */
+
+ if (getServer().getScheduler().scheduleSyncDelayedTask(this, new BukkitPermsUpdateTask(), 1) == -1) {
+ GroupManager.logger.severe("Could not schedule superperms Update.");
+ /*
+ * Flag that we are now loaded and should start processing events.
+ */
+ setLoaded(true);
+ }
+
+ System.out.println(pdfFile.getName() + " version " + pdfFile.getVersion() + " is enabled!");
+
+ // Register as a service
+ if (!restarting) {
+ this.getServer().getServicesManager().register(WorldsHolder.class, this.worldsHolder, this, ServicePriority.Lowest);
+ }
+
+ } catch (Exception ex) {
+
+ /*
+ * Store the error and write to the log.
+ */
+ saveErrorLog(ex);
+
+ /*
+ * Throw an error so Bukkit knows about it.
+ */
+ throw new IllegalArgumentException(ex.getMessage(), ex);
+
+ }
+ }
+
+ /**
+ * Write an error.log
+ *
+ * @param ex
+ */
+ private void saveErrorLog(Exception ex) {
+
+ if (!getDataFolder().exists()) {
+ getDataFolder().mkdirs();
+ }
+
+ lastError = ex.getMessage();
+
+ GroupManager.logger.severe("===================================================");
+ GroupManager.logger.severe("= ERROR REPORT START - " + this.getDescription().getVersion() + " =");
+ GroupManager.logger.severe("===================================================");
+ GroupManager.logger.severe("=== PLEASE COPY AND PASTE THE ERROR.LOG FROM THE ==");
+ GroupManager.logger.severe("= GROUPMANAGER FOLDER TO AN ESSENTIALS DEVELOPER =");
+ GroupManager.logger.severe("===================================================");
+ GroupManager.logger.severe(lastError);
+ GroupManager.logger.severe("===================================================");
+ GroupManager.logger.severe("= ERROR REPORT ENDED =");
+ GroupManager.logger.severe("===================================================");
+
+ // Append this error to the error log.
+ try {
+ String error = "=============================== GM ERROR LOG ===============================\n";
+ error += "= ERROR REPORT START - " + this.getDescription().getVersion() + " =\n\n";
+
+ error += Tasks.getStackTraceAsString(ex);
+ error += "\n============================================================================\n";
+
+ Tasks.appendStringToFile(error, (getDataFolder() + System.getProperty("file.separator") + "ERROR.LOG"));
+ } catch (IOException e) {
+ // Failed to write file.
+ e.printStackTrace();
+ }
+
+ }
+
+ /**
+ * @return the validateOnlinePlayer
+ */
+ public boolean isValidateOnlinePlayer() {
+
+ return validateOnlinePlayer;
+ }
+
+ /**
+ * @param validateOnlinePlayer the validateOnlinePlayer to set
+ */
+ public void setValidateOnlinePlayer(boolean validateOnlinePlayer) {
+
+ this.validateOnlinePlayer = validateOnlinePlayer;
+ }
+
+ public static boolean isLoaded() {
+
+ return isLoaded;
+ }
+
+ public static void setLoaded(boolean isLoaded) {
+
+ GroupManager.isLoaded = isLoaded;
+ }
+
+ public InputStream getResourceAsStream(String fileName) {
+
+ return this.getClassLoader().getResourceAsStream(fileName);
+ }
+
+ private void prepareFileFields() {
+
+ backupFolder = new File(this.getDataFolder(), "backup");
+ if (!backupFolder.exists()) {
+ getBackupFolder().mkdirs();
+ }
+ }
+
+ private void prepareConfig() {
+
+ config = new GMConfiguration(this);
+ }
+
+ public void enableScheduler() {
+
+ if (worldsHolder != null) {
+ disableScheduler();
+ commiter = new Runnable() {
+
+ public void run() {
+
+ try {
+ if (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);
+ long minutes = (long) getGMConfig().getSaveInterval();
+ if (minutes > 0) {
+ scheduler.scheduleAtFixedRate(commiter, minutes, minutes, TimeUnit.MINUTES);
+ GroupManager.logger.info("Scheduled Data Saving is set for every " + minutes + " minutes!");
+ } else {
+ GroupManager.logger.info("Scheduled Data Saving is Disabled!");
+ }
+
+ GroupManager.logger.info("Backups will be retained for " + getGMConfig().getBackupDuration() + " hours!");
+ }
+ }
+
+ public void disableScheduler() {
+
+ if (scheduler != null) {
+ try {
+ scheduler.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
+ scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
+ scheduler.shutdown();
+ } catch (Exception e) {
+ }
+ scheduler = null;
+ GroupManager.logger.info("Scheduled Data Saving is disabled!");
+ }
+ }
+
+ public WorldsHolder getWorldsHolder() {
+
+ return worldsHolder;
+ }
+
+ /**
+ * Called when a command registered by this plugin is received.
+ *
+ * @param sender
+ * @param cmd
+ * @param args
+ */
+ @Override
+ public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) {
+
+ boolean playerCanDo = false;
+ boolean isConsole = false;
+ Player senderPlayer = null, targetPlayer = null;
+ Group senderGroup = null;
+ User senderUser = null;
+ boolean isOpOverride = config.isOpOverride();
+ boolean isAllowCommandBlocks = config.isAllowCommandBlocks();
+
+ // PREVENT GM COMMANDS BEING USED ON COMMANDBLOCKS
+ if (sender instanceof BlockCommandSender && !isAllowCommandBlocks) {
+ Block block = ((BlockCommandSender) sender).getBlock();
+ GroupManager.logger.warning(ChatColor.RED + "GM Commands can not be called from CommandBlocks");
+ GroupManager.logger.warning(ChatColor.RED + "Location: " + ChatColor.GREEN + block.getWorld().getName() + ", " + block.getX() + ", " + block.getY() + ", " + block.getZ());
+ return true;
+ }
+
+ // DETERMINING PLAYER INFORMATION
+ if (sender instanceof Player) {
+ senderPlayer = (Player) sender;
+
+ if (!lastError.isEmpty() && !commandLabel.equalsIgnoreCase("manload")) {
+ sender.sendMessage(ChatColor.RED + "All commands are locked due to an error. " + ChatColor.BOLD + "" + ChatColor.UNDERLINE + "Check plugins/groupmanager/error.log or console" + ChatColor.RESET + "" + ChatColor.RED + " and then try a '/manload'.");
+ return true;
+ }
+
+ senderUser = worldsHolder.getWorldData(senderPlayer).getUser(senderPlayer.getUniqueId().toString());
+ senderGroup = senderUser.getGroup();
+ isOpOverride = (isOpOverride && (senderPlayer.isOp() || worldsHolder.getWorldPermissions(senderPlayer).has(senderPlayer, "groupmanager.op")));
+
+ if (isOpOverride || worldsHolder.getWorldPermissions(senderPlayer).has(senderPlayer, "groupmanager." + cmd.getName())) {
+ playerCanDo = true;
+ }
+ } else {
+
+ if (!lastError.isEmpty() && !commandLabel.equalsIgnoreCase("manload")) {
+ sender.sendMessage(ChatColor.RED + "All commands are locked due to an error. " + ChatColor.BOLD + "" + ChatColor.UNDERLINE + "Check plugins/groupmanager/error.log or console" + ChatColor.RESET + "" + ChatColor.RED + " and then try a '/manload'.");
+ return true;
+ }
+
+ isConsole = true;
+ }
+
+ // PERMISSIONS FOR COMMAND BEING LOADED
+ dataHolder = null;
+ permissionHandler = null;
+
+ if (senderPlayer != null) {
+ dataHolder = worldsHolder.getWorldData(senderPlayer);
+ }
+
+ String selectedWorld = selectedWorlds.get(sender.getName());
+ if (selectedWorld != null) {
+ dataHolder = worldsHolder.getWorldData(selectedWorld);
+ }
+
+ if (dataHolder != null) {
+ permissionHandler = dataHolder.getPermissionsHandler();
+ }
+
+ // VARIABLES USED IN COMMANDS
+
+ int count;
+ PermissionCheckResult permissionResult = null;
+ ArrayList removeList = null;
+ String auxString = null;
+ List match = null;
+ User auxUser = null;
+ Group auxGroup = null;
+ Group auxGroup2 = null;
+
+ GroupManagerPermissions execCmd = null;
+ try {
+ execCmd = GroupManagerPermissions.valueOf(cmd.getName());
+ } catch (Exception e) {
+ // this error happened once with someone. now im prepared... i think
+ GroupManager.logger.severe("===================================================");
+ GroupManager.logger.severe("= ERROR REPORT START =");
+ GroupManager.logger.severe("===================================================");
+ GroupManager.logger.severe("= COPY AND PASTE THIS TO A GROUPMANAGER DEVELOPER =");
+ GroupManager.logger.severe("===================================================");
+ GroupManager.logger.severe(this.getDescription().getName());
+ GroupManager.logger.severe(this.getDescription().getVersion());
+ GroupManager.logger.severe("An error occured while trying to execute command:");
+ GroupManager.logger.severe(cmd.getName());
+ GroupManager.logger.severe("With " + args.length + " arguments:");
+ for (String ar : args) {
+ GroupManager.logger.severe(ar);
+ }
+ GroupManager.logger.severe("The field '" + cmd.getName() + "' was not found in enum.");
+ GroupManager.logger.severe("And could not be parsed.");
+ GroupManager.logger.severe("FIELDS FOUND IN ENUM:");
+ for (GroupManagerPermissions val : GroupManagerPermissions.values()) {
+ GroupManager.logger.severe(val.name());
+ }
+ GroupManager.logger.severe("===================================================");
+ GroupManager.logger.severe("= ERROR REPORT ENDED =");
+ GroupManager.logger.severe("===================================================");
+ sender.sendMessage("An error occurred. Ask the admin to take a look at the console.");
+ }
+
+ if (isConsole || playerCanDo) {
+ switch (execCmd) {
+ case manuadd:
+
+ // Validating arguments
+ if ((args.length != 2) && (args.length != 3)) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manuadd | optional [world])");
+ return true;
+ }
+
+ // Select the relevant world (if specified)
+ if (args.length == 3) {
+ dataHolder = worldsHolder.getWorldData(args[2]);
+ permissionHandler = dataHolder.getPermissionsHandler();
+ }
+
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+
+ if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) {
+ return false;
+ }
+
+ if (match != null) {
+ auxUser = dataHolder.getUser(match.get(0));
+ } else {
+ auxUser = dataHolder.getUser(args[0]);
+ }
+ auxGroup = dataHolder.getGroup(args[1]);
+ if (auxGroup == null) {
+ sender.sendMessage(ChatColor.RED + "'" + args[1] + "' Group doesnt exist!");
+ return false;
+ }
+ if (auxGroup.isGlobal()) {
+ sender.sendMessage(ChatColor.RED + "Players may not be members of GlobalGroups directly.");
+ return false;
+ }
+
+ // Validating permissions
+ if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getUUID(), senderGroup.getName()) : false)) {
+ sender.sendMessage(ChatColor.RED + "Can't modify a player with the same permissions as you, or higher.");
+ return true;
+ }
+ if (!isConsole && !isOpOverride && (permissionHandler.hasGroupInInheritance(auxGroup, senderGroup.getName()))) {
+ sender.sendMessage(ChatColor.RED + "The destination group can't be the same as yours, or higher.");
+ return true;
+ }
+ if (!isConsole && !isOpOverride && (!permissionHandler.inGroup(senderUser.getUUID(), auxUser.getGroupName()) || !permissionHandler.inGroup(senderUser.getUUID(), auxGroup.getName()))) {
+ sender.sendMessage(ChatColor.RED + "You can't modify a player involving a group that you don't inherit.");
+ return true;
+ }
+
+ // Seems OK
+ auxUser.setGroup(auxGroup);
+ if (!sender.hasPermission("groupmanager.notify.other") || (isConsole)) {
+ sender.sendMessage(ChatColor.YELLOW + "You changed player '" + auxUser.getLastName() + "' group to '" + auxGroup.getName() + "' in world '" + dataHolder.getName() + "'.");
+ }
+
+ return true;
+
+ case manudel:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 1) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manudel )");
+ return true;
+ }
+ if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) {
+ return false;
+ }
+
+ if (match != null) {
+ auxUser = dataHolder.getUser(match.get(0));
+ } else {
+ auxUser = dataHolder.getUser(args[0]);
+ }
+ // Validating permission
+ if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getUUID(), senderGroup.getName()) : false)) {
+ sender.sendMessage(ChatColor.RED + "You can't modify a player with same permissions as you, or higher.");
+ return true;
+ }
+ // Seems OK
+ dataHolder.removeUser(auxUser.getUUID());
+ sender.sendMessage(ChatColor.YELLOW + "You changed player '" + auxUser.getLastName() + "' to default settings.");
+
+ // If the player is online, this will create new data for the user.
+ targetPlayer = this.getServer().getPlayer(auxUser.getLastName());
+ if (targetPlayer != null) {
+ BukkitPermissions.updatePermissions(targetPlayer);
+ }
+
+ return true;
+
+ case manuaddsub:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ sender.sendMessage(ChatColor.RED + "Couldn't retrieve your world. World selection is needed.");
+ sender.sendMessage(ChatColor.RED + "Use /manselect ");
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 2) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manuaddsub )");
+ return true;
+ }
+ if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) {
+ return false;
+ }
+
+ if (match != null) {
+ auxUser = dataHolder.getUser(match.get(0));
+ } else {
+ auxUser = dataHolder.getUser(args[0]);
+ }
+ auxGroup = dataHolder.getGroup(args[1]);
+ if (auxGroup == null) {
+ sender.sendMessage(ChatColor.RED + "'" + args[1] + "' Group doesnt exist!");
+ return true;
+ }
+ // Validating permission
+ if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getUUID(), senderGroup.getName()) : false)) {
+ sender.sendMessage(ChatColor.RED + "You can't modify a player with same permissions as you, or higher.");
+ return true;
+ }
+ if (!isConsole && !isOpOverride && (permissionHandler.hasGroupInInheritance(auxGroup, senderGroup.getName()))) {
+ sender.sendMessage(ChatColor.RED + "The sub-group can't be the same as yours, or higher.");
+ return true;
+ }
+ if (!isConsole && !isOpOverride && (!permissionHandler.inGroup(senderUser.getUUID(), auxUser.getGroupName()) || !permissionHandler.inGroup(senderUser.getUUID(), auxGroup.getName()))) {
+ sender.sendMessage(ChatColor.RED + "You can't modify a player involving a group that you don't inherit.");
+ return true;
+ }
+ // Seems OK
+ if (auxUser.addSubGroup(auxGroup)) {
+ sender.sendMessage(ChatColor.YELLOW + "You added subgroup '" + auxGroup.getName() + "' to player '" + auxUser.getLastName() + "'.");
+ } else {
+ sender.sendMessage(ChatColor.RED + "The subgroup '" + auxGroup.getName() + "' is already available to '" + auxUser.getLastName() + "'.");
+ }
+
+ return true;
+
+ case manudelsub:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 2) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manudelsub )");
+ return true;
+ }
+ if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) {
+ return false;
+ }
+
+ if (match != null) {
+ auxUser = dataHolder.getUser(match.get(0));
+ } else {
+ auxUser = dataHolder.getUser(args[0]);
+ }
+ auxGroup = dataHolder.getGroup(args[1]);
+ if (auxGroup == null) {
+ sender.sendMessage(ChatColor.RED + "'" + args[1] + "' Group doesnt exist!");
+ return true;
+ }
+
+ // Validating permission
+ if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getUUID(), senderGroup.getName()) : false)) {
+ sender.sendMessage(ChatColor.RED + "You can't modify a player with same permissions as you, or higher.");
+ return true;
+ }
+ // Seems OK
+ auxUser.removeSubGroup(auxGroup);
+ sender.sendMessage(ChatColor.YELLOW + "You removed subgroup '" + auxGroup.getName() + "' from player '" + auxUser.getLastName() + "' list.");
+
+ // targetPlayer = this.getServer().getPlayer(auxUser.getName());
+ // if (targetPlayer != null)
+ // BukkitPermissions.updatePermissions(targetPlayer);
+
+ return true;
+
+ case mangadd:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 1) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangadd )");
+ return true;
+ }
+ auxGroup = dataHolder.getGroup(args[0]);
+ if (auxGroup != null) {
+ sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group already exists!");
+ return true;
+ }
+ // Seems OK
+ auxGroup = dataHolder.createGroup(args[0]);
+ sender.sendMessage(ChatColor.YELLOW + "You created a group named: " + auxGroup.getName());
+
+ return true;
+
+ case mangdel:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 1) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangdel )");
+ return false;
+ }
+ auxGroup = dataHolder.getGroup(args[0]);
+ if (auxGroup == null) {
+ sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!");
+ return true;
+ }
+ // Seems OK
+ dataHolder.removeGroup(auxGroup.getName());
+ sender.sendMessage(ChatColor.YELLOW + "You deleted a group named " + auxGroup.getName() + ", it's users are default group now.");
+
+ BukkitPermissions.updateAllPlayers();
+
+ return true;
+
+ case manuaddp:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length < 2) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manuaddp [permission2] [permission3]...)");
+ return true;
+ }
+
+ if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) {
+ return false;
+ }
+
+ if (match != null) {
+ auxUser = dataHolder.getUser(match.get(0));
+ } else {
+ auxUser = dataHolder.getUser(args[0]);
+ }
+
+ // Validating your permissions
+ if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getUUID(), senderGroup.getName()) : false)) {
+ sender.sendMessage(ChatColor.RED + "Can't modify player with same group than you, or higher.");
+ return true;
+ }
+
+ for (int i = 1; i < args.length; i++) {
+ auxString = args[i].replace("'", "");
+
+ permissionResult = permissionHandler.checkFullUserPermission(senderUser, auxString);
+ if (!isConsole && !isOpOverride && (permissionResult.resultType.equals(PermissionCheckResult.Type.NOTFOUND) || permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION))) {
+ sender.sendMessage(ChatColor.RED + "You can't add a permission you don't have: '" + auxString + "'");
+ continue;
+ }
+ // Validating permissions of user
+ permissionResult = permissionHandler.checkUserOnlyPermission(auxUser, auxString);
+ if (checkPermissionExists(sender, auxString, permissionResult, "user")) {
+ continue;
+ }
+ // Seems Ok
+ auxUser.addPermission(auxString);
+ sender.sendMessage(ChatColor.YELLOW + "You added '" + auxString + "' to player '" + auxUser.getLastName() + "' permissions.");
+ }
+
+
+ targetPlayer = this.getServer().getPlayer(auxUser.getLastName());
+ if (targetPlayer != null) {
+ BukkitPermissions.updatePermissions(targetPlayer);
+ }
+
+ return true;
+
+ case manudelp:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length < 2) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manudelp [permission2] [permission3]...)");
+ return true;
+ }
+
+ if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) {
+ return false;
+ }
+
+ if (match != null) {
+ auxUser = dataHolder.getUser(match.get(0));
+ } else {
+ auxUser = dataHolder.getUser(args[0]);
+ }
+
+ for (int i = 1; i < args.length; i++) {
+ auxString = args[i].replace("'", "");
+
+ if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getUUID(), senderGroup.getName()) : false)) {
+ sender.sendMessage(ChatColor.RED + "You can't modify a player with same group as you, or higher.");
+ continue;
+ }
+ // Validating your permissions
+ permissionResult = permissionHandler.checkFullUserPermission(senderUser, auxString);
+ if (!isConsole && !isOpOverride && (permissionResult.resultType.equals(PermissionCheckResult.Type.NOTFOUND) || permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION))) {
+ sender.sendMessage(ChatColor.RED + "You can't remove a permission you don't have: '" + auxString + "'");
+ continue;
+ }
+ // Validating permissions of user
+ permissionResult = permissionHandler.checkUserOnlyPermission(auxUser, auxString);
+ if (permissionResult.resultType.equals(PermissionCheckResult.Type.NOTFOUND)) {
+ sender.sendMessage(ChatColor.RED + "The user doesn't have direct access to that permission: '" + auxString + "'");
+ continue;
+ }
+ if (!auxUser.hasSamePermissionNode(auxString)) {
+ sender.sendMessage(ChatColor.RED + "This permission node doesn't match any node.");
+ sender.sendMessage(ChatColor.RED + "But might match node: " + permissionResult.accessLevel);
+ continue;
+ }
+ auxUser.removePermission(auxString);
+ sender.sendMessage(ChatColor.YELLOW + "You removed '" + auxString + "' from player '" + auxUser.getLastName() + "' permissions.");
+ }
+ // Seems OK
+
+ targetPlayer = this.getServer().getPlayer(auxUser.getLastName());
+ if (targetPlayer != null) {
+ BukkitPermissions.updatePermissions(targetPlayer);
+ }
+
+ return true;
+
+ case manuclearp:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 1) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manuclearp )");
+ return true;
+ }
+
+ if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) {
+ return false;
+ }
+
+ if (match != null) {
+ auxUser = dataHolder.getUser(match.get(0));
+ } else {
+ auxUser = dataHolder.getUser(args[0]);
+ }
+ // Validating your permissions
+ if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getUUID(), senderGroup.getName()) : false)) {
+ sender.sendMessage(ChatColor.RED + "You can't modify a player with same group as you, or higher.");
+ return true;
+ }
+ for (String perm : auxUser.getPermissionList()) {
+ permissionResult = permissionHandler.checkFullUserPermission(senderUser, perm);
+ if (!isConsole && !isOpOverride && (permissionResult.resultType.equals(PermissionCheckResult.Type.NOTFOUND) || permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION))) {
+ sender.sendMessage(ChatColor.RED + "You can't remove a permission you don't have: '" + perm + "'.");
+ } else {
+ auxUser.removePermission(perm);
+ }
+ }
+ sender.sendMessage(ChatColor.YELLOW + "You removed all permissions from player '" + auxUser.getLastName() + "'.");
+
+ targetPlayer = this.getServer().getPlayer(auxUser.getLastName());
+ if (targetPlayer != null) {
+ BukkitPermissions.updatePermissions(targetPlayer);
+ }
+
+ return true;
+
+ case manulistp:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if ((args.length == 0) || (args.length > 2)) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manulistp (+))");
+ return true;
+ }
+
+ if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) {
+ return false;
+ }
+
+ if (match != null) {
+ auxUser = dataHolder.getUser(match.get(0));
+ } else {
+ auxUser = dataHolder.getUser(args[0]);
+ }
+ // Validating permission
+ // Seems OK
+ auxString = "";
+ for (String perm : auxUser.getPermissionList()) {
+ auxString += perm + ", ";
+ }
+ if (auxString.lastIndexOf(",") > 0) {
+ auxString = auxString.substring(0, auxString.lastIndexOf(","));
+ sender.sendMessage(ChatColor.YELLOW + "The player '" + auxUser.getLastName() + "' has following permissions: " + ChatColor.WHITE + auxString);
+ sender.sendMessage(ChatColor.YELLOW + "And all permissions from group: " + auxUser.getGroupName());
+ auxString = "";
+ for (String subGroup : auxUser.subGroupListStringCopy()) {
+ auxString += subGroup + ", ";
+ }
+ if (auxString.lastIndexOf(",") > 0) {
+ auxString = auxString.substring(0, auxString.lastIndexOf(","));
+ sender.sendMessage(ChatColor.YELLOW + "And all permissions from subgroups: " + auxString);
+ }
+ } else {
+ sender.sendMessage(ChatColor.YELLOW + "The player '" + auxUser.getLastName() + "' has no specific permissions.");
+ sender.sendMessage(ChatColor.YELLOW + "Only all permissions from group: " + auxUser.getGroupName());
+ auxString = "";
+ for (String subGroup : auxUser.subGroupListStringCopy()) {
+ auxString += subGroup + ", ";
+ }
+ if (auxString.lastIndexOf(",") > 0) {
+ auxString = auxString.substring(0, auxString.lastIndexOf(","));
+ sender.sendMessage(ChatColor.YELLOW + "And all permissions from subgroups: " + auxString);
+ }
+ }
+
+ // bukkit perms
+ if ((args.length == 2) && (args[1].equalsIgnoreCase("+"))) {
+ targetPlayer = this.getServer().getPlayer(auxUser.getLastName());
+ if (targetPlayer != null) {
+ sender.sendMessage(ChatColor.YELLOW + "Superperms reports: ");
+ for (String line : BukkitPermissions.listPerms(targetPlayer)) {
+ sender.sendMessage(ChatColor.YELLOW + line);
+ }
+
+ }
+ }
+
+ return true;
+
+ case manucheckp:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 2) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manucheckp )");
+ return true;
+ }
+
+ auxString = args[1].replace("'", "");
+
+ if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) {
+ return false;
+ }
+
+ if (match != null) {
+ auxUser = dataHolder.getUser(match.get(0));
+ } else {
+ auxUser = dataHolder.getUser(args[0]);
+ }
+ targetPlayer = this.getServer().getPlayer(auxUser.getLastName());
+ // Validating permission
+ permissionResult = permissionHandler.checkFullGMPermission(auxUser, auxString, false);
+
+ if (permissionResult.resultType.equals(PermissionCheckResult.Type.NOTFOUND)) {
+ // No permissions found in GM so fall through and check Bukkit.
+ sender.sendMessage(ChatColor.YELLOW + "The player doesn't have access to that permission");
+
+ } else {
+ // This permission was found in groupmanager.
+ if (permissionResult.owner instanceof User) {
+ if (permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION)) {
+ sender.sendMessage(ChatColor.YELLOW + "The user has directly a negation node for that permission.");
+ } else if (permissionResult.resultType.equals(PermissionCheckResult.Type.EXCEPTION)) {
+ sender.sendMessage(ChatColor.YELLOW + "The user has directly an Exception node for that permission.");
+ } else {
+ sender.sendMessage(ChatColor.YELLOW + "The user has directly this permission.");
+ }
+ sender.sendMessage(ChatColor.YELLOW + "Permission Node: " + permissionResult.accessLevel);
+ } else if (permissionResult.owner instanceof Group) {
+ if (permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION)) {
+ sender.sendMessage(ChatColor.YELLOW + "The user inherits a negation permission from group: " + permissionResult.owner.getLastName());
+ } else if (permissionResult.resultType.equals(PermissionCheckResult.Type.EXCEPTION)) {
+ sender.sendMessage(ChatColor.YELLOW + "The user inherits an Exception permission from group: " + permissionResult.owner.getLastName());
+ } else {
+ sender.sendMessage(ChatColor.YELLOW + "The user inherits the permission from group: " + permissionResult.owner.getLastName());
+ }
+ sender.sendMessage(ChatColor.YELLOW + "Permission Node: " + permissionResult.accessLevel);
+ }
+ }
+
+ // superperms
+ if (targetPlayer != null) {
+ sender.sendMessage(ChatColor.YELLOW + "SuperPerms reports Node: " + targetPlayer.hasPermission(args[1]) + ((!targetPlayer.hasPermission(args[1]) && targetPlayer.isPermissionSet(args[1])) ? " (Negated)" : ""));
+ }
+
+ return true;
+
+ case mangaddp:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length < 2) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangaddp [permission2] [permission3]...)");
+ return true;
+ }
+
+ auxGroup = dataHolder.getGroup(args[0]);
+ if (auxGroup == null) {
+ sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!");
+ return false;
+ }
+
+ for (int i = 1; i < args.length; i++) {
+ auxString = args[i].replace("'", "");
+
+ // Validating your permissions
+ permissionResult = permissionHandler.checkFullUserPermission(senderUser, auxString);
+ if (!isConsole && !isOpOverride && (permissionResult.resultType.equals(PermissionCheckResult.Type.NOTFOUND) || permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION))) {
+ sender.sendMessage(ChatColor.RED + "You can't add a permission you don't have: '" + auxString + "'");
+ continue;
+ }
+ // Validating permissions of user
+ permissionResult = permissionHandler.checkGroupOnlyPermission(auxGroup, auxString);
+ if (checkPermissionExists(sender, auxString, permissionResult, "group")) {
+ continue;
+ }
+ // Seems OK
+ auxGroup.addPermission(auxString);
+ sender.sendMessage(ChatColor.YELLOW + "You added '" + auxString + "' to group '" + auxGroup.getName() + "' permissions.");
+ }
+
+ BukkitPermissions.updateAllPlayers();
+
+ return true;
+
+ case mangdelp:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length < 2) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangdelp [permission2] [permission3]...)");
+ return true;
+ }
+
+ auxGroup = dataHolder.getGroup(args[0]);
+ if (auxGroup == null) {
+ sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!");
+ return true;
+ }
+ for (int i = 1; i < args.length; i++) {
+ auxString = args[i].replace("'", "");
+
+ // Validating your permissions
+ permissionResult = permissionHandler.checkFullUserPermission(senderUser, auxString);
+ if (!isConsole && !isOpOverride && (permissionResult.resultType.equals(PermissionCheckResult.Type.NOTFOUND) || permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION))) {
+ sender.sendMessage(ChatColor.RED + "Can't remove a permission you don't have: '" + auxString + "'");
+ continue;
+ }
+ // Validating permissions of user
+ permissionResult = permissionHandler.checkGroupOnlyPermission(auxGroup, auxString);
+ if (permissionResult.resultType.equals(PermissionCheckResult.Type.NOTFOUND)) {
+ sender.sendMessage(ChatColor.YELLOW + "The group doesn't have direct access to that permission: '" + auxString + "'");
+ continue;
+ }
+ if (!auxGroup.hasSamePermissionNode(auxString)) {
+ sender.sendMessage(ChatColor.RED + "This permission node doesn't match any node.");
+ sender.sendMessage(ChatColor.RED + "But might match node: " + permissionResult.accessLevel);
+ continue;
+ }
+ // Seems OK
+ auxGroup.removePermission(auxString);
+ sender.sendMessage(ChatColor.YELLOW + "You removed '" + auxString + "' from group '" + auxGroup.getName() + "' permissions.");
+ }
+
+ BukkitPermissions.updateAllPlayers();
+
+ return true;
+
+ case mangclearp:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 1) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangclearp )");
+ return true;
+ }
+
+ auxGroup = dataHolder.getGroup(args[0]);
+ if (auxGroup == null) {
+ sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!");
+ return true;
+ }
+
+ for (String perm : auxGroup.getPermissionList()) {
+ permissionResult = permissionHandler.checkFullUserPermission(senderUser, perm);
+ if (!isConsole && !isOpOverride && (permissionResult.resultType.equals(PermissionCheckResult.Type.NOTFOUND) || permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION))) {
+ sender.sendMessage(ChatColor.RED + "Can't remove a permission you don't have: '" + perm + "'.");
+ } else {
+ auxGroup.removePermission(perm);
+ }
+ }
+ sender.sendMessage(ChatColor.YELLOW + "You removed all permissions from group '" + auxGroup.getName() + "'.");
+
+ BukkitPermissions.updateAllPlayers();
+
+ return true;
+
+ case manglistp:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 1) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manglistp )");
+ return true;
+ }
+ auxGroup = dataHolder.getGroup(args[0]);
+ if (auxGroup == null) {
+ sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!");
+ return true;
+ }
+ // Validating permission
+
+ // Seems OK
+ auxString = "";
+ for (String perm : auxGroup.getPermissionList()) {
+ auxString += perm + ", ";
+ }
+ if (auxString.lastIndexOf(",") > 0) {
+ auxString = auxString.substring(0, auxString.lastIndexOf(","));
+ sender.sendMessage(ChatColor.YELLOW + "The group '" + auxGroup.getName() + "' has following permissions: " + ChatColor.WHITE + auxString);
+ auxString = "";
+ for (String grp : auxGroup.getInherits()) {
+ auxString += grp + ", ";
+ }
+ if (auxString.lastIndexOf(",") > 0) {
+ auxString = auxString.substring(0, auxString.lastIndexOf(","));
+ sender.sendMessage(ChatColor.YELLOW + "And all permissions from groups: " + auxString);
+ }
+
+ } else {
+ sender.sendMessage(ChatColor.YELLOW + "The group '" + auxGroup.getName() + "' has no specific permissions.");
+ auxString = "";
+ for (String grp : auxGroup.getInherits()) {
+ auxString += grp + ", ";
+ }
+ if (auxString.lastIndexOf(",") > 0) {
+ auxString = auxString.substring(0, auxString.lastIndexOf(","));
+ sender.sendMessage(ChatColor.YELLOW + "Only all permissions from groups: " + auxString);
+ }
+
+ }
+ return true;
+
+ case mangcheckp:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 2) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangcheckp )");
+ return true;
+ }
+
+ auxString = args[1];
+ if (auxString.startsWith("'") && auxString.endsWith("'")) {
+ auxString = auxString.substring(1, auxString.length() - 1);
+ }
+
+ auxGroup = dataHolder.getGroup(args[0]);
+ if (auxGroup == null) {
+ sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!");
+ return true;
+ }
+ // Validating permission
+ permissionResult = permissionHandler.checkGroupPermissionWithInheritance(auxGroup, auxString);
+ if (permissionResult.resultType.equals(PermissionCheckResult.Type.NOTFOUND)) {
+ sender.sendMessage(ChatColor.YELLOW + "The group doesn't have access to that permission");
+ return true;
+ }
+ // Seems OK
+ // auxString = permissionHandler.checkUserOnlyPermission(auxUser, args[1]);
+ if (permissionResult.owner instanceof Group) {
+ if (permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION)) {
+ sender.sendMessage(ChatColor.YELLOW + "The group inherits the negation permission from group: " + permissionResult.owner.getLastName());
+ } else if (permissionResult.resultType.equals(PermissionCheckResult.Type.EXCEPTION)) {
+ sender.sendMessage(ChatColor.YELLOW + "The group inherits an Exception permission from group: " + permissionResult.owner.getLastName());
+ } else {
+ sender.sendMessage(ChatColor.YELLOW + "The group inherits the permission from group: " + permissionResult.owner.getLastName());
+ }
+ sender.sendMessage(ChatColor.YELLOW + "Permission Node: " + permissionResult.accessLevel);
+
+ }
+ return true;
+
+ case mangaddi:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 2) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangaddi )");
+ return true;
+ }
+ auxGroup = dataHolder.getGroup(args[0]);
+ if (auxGroup == null) {
+ sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!");
+ return true;
+ }
+ auxGroup2 = dataHolder.getGroup(args[1]);
+ if (auxGroup2 == null) {
+ sender.sendMessage(ChatColor.RED + "'" + args[1] + "' Group doesnt exist!");
+ return true;
+ }
+ if (auxGroup.isGlobal()) {
+ sender.sendMessage(ChatColor.RED + "GlobalGroups do NOT support inheritance.");
+ return true;
+ }
+
+ // Validating permission
+ if (permissionHandler.hasGroupInInheritance(auxGroup, auxGroup2.getName())) {
+ sender.sendMessage(ChatColor.RED + "Group " + auxGroup.getName() + " already inherits " + auxGroup2.getName() + " (might not be directly)");
+ return true;
+ }
+ // Seems OK
+ auxGroup.addInherits(auxGroup2);
+ sender.sendMessage(ChatColor.YELLOW + "Group " + auxGroup2.getName() + " is now in " + auxGroup.getName() + " inheritance list.");
+
+ BukkitPermissions.updateAllPlayers();
+
+ return true;
+
+ case mangdeli:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 2) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangdeli )");
+ return true;
+ }
+ auxGroup = dataHolder.getGroup(args[0]);
+ if (auxGroup == null) {
+ sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!");
+ return true;
+ }
+ auxGroup2 = dataHolder.getGroup(args[1]);
+ if (auxGroup2 == null) {
+ sender.sendMessage(ChatColor.RED + "'" + args[1] + "' Group doesnt exist!");
+ return true;
+ }
+ if (auxGroup.isGlobal()) {
+ sender.sendMessage(ChatColor.RED + "GlobalGroups do NOT support inheritance.");
+ return true;
+ }
+
+ // Validating permission
+ if (!permissionHandler.hasGroupInInheritance(auxGroup, auxGroup2.getName())) {
+ sender.sendMessage(ChatColor.RED + "Group " + auxGroup.getName() + " does not inherit " + auxGroup2.getName() + ".");
+ return true;
+ }
+ if (!auxGroup.getInherits().contains(auxGroup2.getName())) {
+ sender.sendMessage(ChatColor.RED + "Group " + auxGroup.getName() + " does not inherit " + auxGroup2.getName() + " directly.");
+ return true;
+ }
+ // Seems OK
+ auxGroup.removeInherits(auxGroup2.getName());
+ sender.sendMessage(ChatColor.YELLOW + "Group " + auxGroup2.getName() + " was removed from " + auxGroup.getName() + " inheritance list.");
+
+ BukkitPermissions.updateAllPlayers();
+
+ return true;
+
+ case manuaddv:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length < 3) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manuaddv )");
+ return true;
+ }
+ if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) {
+ return false;
+ }
+
+ if (match != null) {
+ auxUser = dataHolder.getUser(match.get(0));
+ } else {
+ auxUser = dataHolder.getUser(args[0]);
+ }
+ // Validating permission
+ // Seems OK
+ auxString = "";
+ for (int i = 2; i < args.length; i++) {
+ auxString += args[i];
+ if ((i + 1) < args.length) {
+ auxString += " ";
+ }
+ }
+ auxString = auxString.replace("'", "");
+ auxUser.getVariables().addVar(args[1], Variables.parseVariableValue(auxString));
+ sender.sendMessage(ChatColor.YELLOW + "Variable " + ChatColor.GOLD + args[1] + ChatColor.YELLOW + ":'" + ChatColor.GREEN + auxString + ChatColor.YELLOW + "' added to the user " + auxUser.getLastName());
+
+ return true;
+
+ case manudelv:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 2) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manudelv )");
+ return true;
+ }
+ if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) {
+ return false;
+ }
+
+ if (match != null) {
+ auxUser = dataHolder.getUser(match.get(0));
+ } else {
+ auxUser = dataHolder.getUser(args[0]);
+ }
+ // Validating permission
+ if (!auxUser.getVariables().hasVar(args[1])) {
+ sender.sendMessage(ChatColor.RED + "The user doesn't have directly that variable!");
+ return true;
+ }
+ // Seems OK
+ auxUser.getVariables().removeVar(args[1]);
+ sender.sendMessage(ChatColor.YELLOW + "Variable " + ChatColor.GOLD + args[1] + ChatColor.YELLOW + " removed from the user " + ChatColor.GREEN + auxUser.getLastName());
+
+ return true;
+
+ case manulistv:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 1) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manulistv )");
+ return true;
+ }
+ if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) {
+ return false;
+ }
+ if (match != null) {
+ auxUser = dataHolder.getUser(match.get(0));
+ } else {
+ auxUser = dataHolder.getUser(args[0]);
+ }
+ // Validating permission
+ // Seems OK
+ auxString = "";
+ for (String varKey : auxUser.getVariables().getVarKeyList()) {
+ Object o = auxUser.getVariables().getVarObject(varKey);
+ auxString += ChatColor.GOLD + varKey + ChatColor.WHITE + ":'" + ChatColor.GREEN + o.toString() + ChatColor.WHITE + "', ";
+ }
+ if (auxString.lastIndexOf(",") > 0) {
+ auxString = auxString.substring(0, auxString.lastIndexOf(","));
+ }
+ sender.sendMessage(ChatColor.YELLOW + "Variables of user " + auxUser.getLastName() + ": ");
+ sender.sendMessage(auxString + ".");
+ sender.sendMessage(ChatColor.YELLOW + "Plus all variables from group: " + auxUser.getGroupName());
+
+ return true;
+
+ case manucheckv:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 2) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manucheckv )");
+ return true;
+ }
+ if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) {
+ return false;
+ }
+ if (match != null) {
+ auxUser = dataHolder.getUser(match.get(0));
+ } else {
+ auxUser = dataHolder.getUser(args[0]);
+ }
+ // Validating permission
+ auxGroup = auxUser.getGroup();
+ auxGroup2 = permissionHandler.nextGroupWithVariable(auxGroup, args[1]);
+
+ if (!auxUser.getVariables().hasVar(args[1])) {
+ // Check sub groups
+ if (!auxUser.isSubGroupsEmpty() && auxGroup2 == null) {
+ for (Group subGroup : auxUser.subGroupListCopy()) {
+ auxGroup2 = permissionHandler.nextGroupWithVariable(subGroup, args[1]);
+ if (auxGroup2 != null) {
+ continue;
+ }
+ }
+ }
+ if (auxGroup2 == null) {
+ sender.sendMessage(ChatColor.YELLOW + "The user doesn't have access to that variable!");
+ return true;
+ }
+ }
+ // Seems OK
+ if (auxUser.getVariables().hasVar(auxString)) {
+ sender.sendMessage(ChatColor.YELLOW + "The value of variable '" + ChatColor.GOLD + args[1] + ChatColor.YELLOW + "' is: '" + ChatColor.GREEN + auxUser.getVariables().getVarObject(args[1]).toString() + ChatColor.WHITE + "'");
+ sender.sendMessage(ChatColor.YELLOW + "This user own directly the variable");
+ }
+ sender.sendMessage(ChatColor.YELLOW + "The value of variable '" + ChatColor.GOLD + args[1] + ChatColor.YELLOW + "' is: '" + ChatColor.GREEN + auxGroup2.getVariables().getVarObject(args[1]).toString() + ChatColor.WHITE + "'");
+ if (!auxGroup.equals(auxGroup2)) {
+ sender.sendMessage(ChatColor.YELLOW + "And the value was inherited from group: " + ChatColor.GREEN + auxGroup2.getName());
+ }
+
+ return true;
+
+ case mangaddv:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length < 3) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangaddv )");
+ return true;
+ }
+ auxGroup = dataHolder.getGroup(args[0]);
+ if (auxGroup == null) {
+ sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!");
+ return true;
+ }
+ if (auxGroup.isGlobal()) {
+ sender.sendMessage(ChatColor.RED + "GlobalGroups do NOT support Info Nodes.");
+ return true;
+ }
+ // Validating permission
+ // Seems OK
+ auxString = "";
+ for (int i = 2; i < args.length; i++) {
+ auxString += args[i];
+ if ((i + 1) < args.length) {
+ auxString += " ";
+ }
+ }
+
+ auxString = auxString.replace("'", "");
+ auxGroup.getVariables().addVar(args[1], Variables.parseVariableValue(auxString));
+ sender.sendMessage(ChatColor.YELLOW + "Variable " + ChatColor.GOLD + args[1] + ChatColor.YELLOW + ":'" + ChatColor.GREEN + auxString + ChatColor.YELLOW + "' added to the group " + auxGroup.getName());
+
+ return true;
+
+ case mangdelv:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 2) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangdelv )");
+ return true;
+ }
+ auxGroup = dataHolder.getGroup(args[0]);
+ if (auxGroup == null) {
+ sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!");
+ return true;
+ }
+ if (auxGroup.isGlobal()) {
+ sender.sendMessage(ChatColor.RED + "GlobalGroups do NOT support Info Nodes.");
+ return true;
+ }
+ // Validating permission
+ if (!auxGroup.getVariables().hasVar(args[1])) {
+ sender.sendMessage(ChatColor.RED + "The group doesn't have directly that variable!");
+ return true;
+ }
+ // Seems OK
+ auxGroup.getVariables().removeVar(args[1]);
+ sender.sendMessage(ChatColor.YELLOW + "Variable " + ChatColor.GOLD + args[1] + ChatColor.YELLOW + " removed from the group " + ChatColor.GREEN + auxGroup.getName());
+
+ return true;
+
+ case manglistv:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 1) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manglistv )");
+ return true;
+ }
+ auxGroup = dataHolder.getGroup(args[0]);
+ if (auxGroup == null) {
+ sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!");
+ return true;
+ }
+ if (auxGroup.isGlobal()) {
+ sender.sendMessage(ChatColor.RED + "GlobalGroups do NOT support Info Nodes.");
+ return true;
+ }
+ // Validating permission
+ // Seems OK
+ auxString = "";
+ for (String varKey : auxGroup.getVariables().getVarKeyList()) {
+ Object o = auxGroup.getVariables().getVarObject(varKey);
+ auxString += ChatColor.GOLD + varKey + ChatColor.WHITE + ":'" + ChatColor.GREEN + o.toString() + ChatColor.WHITE + "', ";
+ }
+ if (auxString.lastIndexOf(",") > 0) {
+ auxString = auxString.substring(0, auxString.lastIndexOf(","));
+ }
+ sender.sendMessage(ChatColor.YELLOW + "Variables of group " + auxGroup.getName() + ": ");
+ sender.sendMessage(auxString + ".");
+ auxString = "";
+ for (String grp : auxGroup.getInherits()) {
+ auxString += grp + ", ";
+ }
+ if (auxString.lastIndexOf(",") > 0) {
+ auxString = auxString.substring(0, auxString.lastIndexOf(","));
+ sender.sendMessage(ChatColor.YELLOW + "Plus all variables from groups: " + auxString);
+ }
+
+ return true;
+
+ case mangcheckv:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 2) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangcheckv )");
+ return true;
+ }
+ auxGroup = dataHolder.getGroup(args[0]);
+ if (auxGroup == null) {
+ sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!");
+ return true;
+ }
+ if (auxGroup.isGlobal()) {
+ sender.sendMessage(ChatColor.RED + "GlobalGroups do NOT support Info Nodes.");
+ return true;
+ }
+ // Validating permission
+ auxGroup2 = permissionHandler.nextGroupWithVariable(auxGroup, args[1]);
+ if (auxGroup2 == null) {
+ sender.sendMessage(ChatColor.RED + "The group doesn't have access to that variable!");
+ }
+ // Seems OK
+ sender.sendMessage(ChatColor.YELLOW + "The value of variable '" + ChatColor.GOLD + args[1] + ChatColor.YELLOW + "' is: '" + ChatColor.GREEN + auxGroup2.getVariables().getVarObject(args[1]).toString() + ChatColor.WHITE + "'");
+ if (!auxGroup.equals(auxGroup2)) {
+ sender.sendMessage(ChatColor.YELLOW + "And the value was inherited from group: " + ChatColor.GREEN + auxGroup2.getName());
+ }
+
+ return true;
+
+ case manwhois:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 1) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manwhois )");
+ return true;
+ }
+ if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) {
+ return false;
+ }
+ if (match != null) {
+ auxUser = dataHolder.getUser(match.get(0));
+ } else {
+ auxUser = dataHolder.getUser(args[0]);
+ }
+ // Seems OK
+ sender.sendMessage(ChatColor.YELLOW + "Name: " + ChatColor.GREEN + auxUser.getLastName());
+ sender.sendMessage(ChatColor.YELLOW + "Group: " + ChatColor.GREEN + auxUser.getGroup().getName());
+ // Compile a list of subgroups
+ auxString = "";
+ for (String subGroup : auxUser.subGroupListStringCopy()) {
+ auxString += subGroup + ", ";
+ }
+ if (auxString.lastIndexOf(",") > 0) {
+ auxString = auxString.substring(0, auxString.lastIndexOf(","));
+ sender.sendMessage(ChatColor.YELLOW + "subgroups: " + auxString);
+ }
+
+ sender.sendMessage(ChatColor.YELLOW + "Overloaded: " + ChatColor.GREEN + dataHolder.isOverloaded(auxUser.getUUID()));
+ auxGroup = dataHolder.surpassOverload(auxUser.getUUID()).getGroup();
+ if (!auxGroup.equals(auxUser.getGroup())) {
+ sender.sendMessage(ChatColor.YELLOW + "Original Group: " + ChatColor.GREEN + auxGroup.getName());
+ }
+ // victim.permissions.add(args[1]);
+ return true;
+
+ case tempadd:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 1) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/tempadd )");
+ return true;
+ }
+ if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) {
+ return false;
+ }
+ if (match != null) {
+ auxUser = dataHolder.getUser(match.get(0));
+ } else {
+ auxUser = dataHolder.getUser(args[0]);
+ }
+ // Validating permission
+ if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getUUID(), senderGroup.getName()) : false)) {
+ sender.sendMessage(ChatColor.RED + "Can't modify player with same permissions than you, or higher.");
+ return true;
+ }
+ // Seems OK
+ if (overloadedUsers.get(dataHolder.getName().toLowerCase()) == null) {
+ overloadedUsers.put(dataHolder.getName().toLowerCase(), new ArrayList());
+ }
+ dataHolder.overloadUser(auxUser.getUUID());
+ overloadedUsers.get(dataHolder.getName().toLowerCase()).add(dataHolder.getUser(auxUser.getUUID()));
+ sender.sendMessage(ChatColor.YELLOW + "Player set to overload mode!");
+
+ return true;
+
+ case tempdel:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 1) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/tempdel )");
+ return true;
+ }
+ if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) {
+ return false;
+ }
+ if (match != null) {
+ auxUser = dataHolder.getUser(match.get(0));
+ } else {
+ auxUser = dataHolder.getUser(args[0]);
+ }
+ // Validating permission
+ if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getUUID(), senderGroup.getName()) : false)) {
+ sender.sendMessage(ChatColor.RED + "You can't modify a player with same permissions as you, or higher.");
+ return true;
+ }
+ // Seems OK
+ if (overloadedUsers.get(dataHolder.getName().toLowerCase()) == null) {
+ overloadedUsers.put(dataHolder.getName().toLowerCase(), new ArrayList());
+ }
+ dataHolder.removeOverload(auxUser.getUUID());
+ if (overloadedUsers.get(dataHolder.getName().toLowerCase()).contains(auxUser)) {
+ overloadedUsers.get(dataHolder.getName().toLowerCase()).remove(auxUser);
+ }
+ sender.sendMessage(ChatColor.YELLOW + "Player overload mode is now disabled.");
+
+ return true;
+
+ case templist:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // WORKING
+ auxString = "";
+ removeList = new ArrayList();
+ count = 0;
+ for (User u : overloadedUsers.get(dataHolder.getName().toLowerCase())) {
+ if (!dataHolder.isOverloaded(u.getUUID())) {
+ removeList.add(u);
+ } else {
+ auxString += u.getLastName() + ", ";
+ count++;
+ }
+ }
+ if (count == 0) {
+ sender.sendMessage(ChatColor.YELLOW + "There are no users in overload mode.");
+ return true;
+ }
+ auxString = auxString.substring(0, auxString.lastIndexOf(","));
+ if (overloadedUsers.get(dataHolder.getName().toLowerCase()) == null) {
+ overloadedUsers.put(dataHolder.getName().toLowerCase(), new ArrayList());
+ }
+ overloadedUsers.get(dataHolder.getName().toLowerCase()).removeAll(removeList);
+ sender.sendMessage(ChatColor.YELLOW + " " + count + " Users in overload mode: " + ChatColor.WHITE + auxString);
+
+ return true;
+
+ case tempdelall:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // WORKING
+ removeList = new ArrayList();
+ count = 0;
+ for (User u : overloadedUsers.get(dataHolder.getName().toLowerCase())) {
+ if (dataHolder.isOverloaded(u.getUUID())) {
+ dataHolder.removeOverload(u.getUUID());
+ count++;
+ }
+ }
+ if (count == 0) {
+ sender.sendMessage(ChatColor.YELLOW + "There are no users in overload mode.");
+ return true;
+ }
+ if (overloadedUsers.get(dataHolder.getName().toLowerCase()) == null) {
+ overloadedUsers.put(dataHolder.getName().toLowerCase(), new ArrayList());
+ }
+ overloadedUsers.get(dataHolder.getName().toLowerCase()).clear();
+ sender.sendMessage(ChatColor.YELLOW + " " + count + "All users in overload mode are now normal again.");
+
+ return true;
+
+ case mansave:
+
+ boolean forced = false;
+
+ if ((args.length == 1) && (args[0].equalsIgnoreCase("force"))) {
+ forced = true;
+ }
+
+ try {
+ worldsHolder.saveChanges(forced);
+ sender.sendMessage(ChatColor.YELLOW + "All changes were saved.");
+ } catch (IllegalStateException ex) {
+ sender.sendMessage(ChatColor.RED + ex.getMessage());
+ }
+ return true;
+
+ case manload:
+
+ /**
+ * Attempt to reload a specific world
+ */
+ if (args.length > 0) {
+
+ if (!lastError.isEmpty()) {
+ sender.sendMessage(ChatColor.RED + "All commands are locked due to an error. " + ChatColor.BOLD + "" + ChatColor.UNDERLINE + "Check plugins/groupmanager/error.log or console" + ChatColor.RESET + "" + ChatColor.RED + " and then try a '/manload'.");
+ return true;
+ }
+
+ auxString = "";
+ for (int i = 0; i < args.length; i++) {
+ auxString += args[i];
+ if ((i + 1) < args.length) {
+ auxString += " ";
+ }
+ }
+
+ isLoaded = false; // Disable Bukkit Perms update and event triggers
+
+ globalGroups.load();
+ worldsHolder.loadWorld(auxString);
+
+ sender.sendMessage("The request to reload world '" + auxString + "' was attempted.");
+
+ isLoaded = true;
+
+ BukkitPermissions.reset();
+
+ } else {
+
+ /**
+ * Reload all settings and data as no world was specified.
+ */
+
+ /*
+ * Attempting a fresh load.
+ */
+ onDisable(true);
+ onEnable(true);
+
+ sender.sendMessage("All settings and worlds were reloaded!");
+ }
+
+ /**
+ * Fire an event as none will have been triggered in the reload.
+ */
+ if (GroupManager.isLoaded()) {
+ GroupManager.getGMEventHandler().callEvent(GMSystemEvent.Action.RELOADED);
+ }
+
+ return true;
+
+ case listgroups:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // WORKING
+ auxString = "";
+ String auxString2 = "";
+ for (Group g : dataHolder.getGroupList()) {
+ auxString += g.getName() + ", ";
+ }
+ for (Group g : getGlobalGroups().getGroupList()) {
+ auxString2 += g.getName() + ", ";
+ }
+ if (auxString.lastIndexOf(",") > 0) {
+ auxString = auxString.substring(0, auxString.lastIndexOf(","));
+ }
+ if (auxString2.lastIndexOf(",") > 0) {
+ auxString2 = auxString2.substring(0, auxString2.lastIndexOf(","));
+ }
+ sender.sendMessage(ChatColor.YELLOW + "Groups Available: " + ChatColor.WHITE + auxString);
+ sender.sendMessage(ChatColor.YELLOW + "GlobalGroups Available: " + ChatColor.WHITE + auxString2);
+
+ return true;
+
+ case manpromote:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 2) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manpromote )");
+ return true;
+ }
+ if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) {
+ return false;
+ }
+ if (match != null) {
+ auxUser = dataHolder.getUser(match.get(0));
+ } else {
+ auxUser = dataHolder.getUser(args[0]);
+ }
+ auxGroup = dataHolder.getGroup(args[1]);
+ if (auxGroup == null) {
+ sender.sendMessage(ChatColor.RED + "'" + args[1] + "' Group doesnt exist!");
+ return true;
+ }
+ if (auxGroup.isGlobal()) {
+ sender.sendMessage(ChatColor.RED + "Players may not be members of GlobalGroups directly.");
+ return true;
+ }
+ // Validating permission
+ if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getUUID(), senderGroup.getName()) : false)) {
+ sender.sendMessage(ChatColor.RED + "You can't modify a player with same permissions as you, or higher.");
+ return true;
+ }
+ if (!isConsole && !isOpOverride && (permissionHandler.hasGroupInInheritance(auxGroup, senderGroup.getName()))) {
+ sender.sendMessage(ChatColor.RED + "The destination group can't be the same as yours, or higher.");
+ return true;
+ }
+ if (!isConsole && !isOpOverride && (!permissionHandler.inGroup(senderUser.getUUID(), auxUser.getGroupName()) || !permissionHandler.inGroup(senderUser.getUUID(), auxGroup.getName()))) {
+ sender.sendMessage(ChatColor.RED + "You can't modify a player involving a group that you don't inherit.");
+ return true;
+ }
+ if (!permissionHandler.hasGroupInInheritance(auxUser.getGroup(), auxGroup.getName()) && !permissionHandler.hasGroupInInheritance(auxGroup, auxUser.getGroupName())) {
+ sender.sendMessage(ChatColor.RED + "You can't modify a player using groups with different heritage line.");
+ return true;
+ }
+ if (!permissionHandler.hasGroupInInheritance(auxGroup, auxUser.getGroupName())) {
+ sender.sendMessage(ChatColor.RED + "The new group must be a higher rank.");
+ return true;
+ }
+ // Seems OK
+ auxUser.setGroup(auxGroup);
+ if (!sender.hasPermission("groupmanager.notify.other") || (isConsole)) {
+ sender.sendMessage(ChatColor.YELLOW + "You changed " + auxUser.getLastName() + " group to " + auxGroup.getName() + ".");
+ }
+
+ return true;
+
+ case mandemote:
+ // Validating state of sender
+ if (dataHolder == null || permissionHandler == null) {
+ if (!setDefaultWorldHandler(sender)) {
+ return true;
+ }
+ }
+ // Validating arguments
+ if (args.length != 2) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mandemote )");
+ return true;
+ }
+ if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) {
+ return false;
+ }
+ if (match != null) {
+ auxUser = dataHolder.getUser(match.get(0));
+ } else {
+ auxUser = dataHolder.getUser(args[0]);
+ }
+ auxGroup = dataHolder.getGroup(args[1]);
+ if (auxGroup == null) {
+ sender.sendMessage(ChatColor.RED + "'" + args[1] + "' Group doesnt exist!");
+ return true;
+ }
+ if (auxGroup.isGlobal()) {
+ sender.sendMessage(ChatColor.RED + "Players may not be members of GlobalGroups directly.");
+ return true;
+ }
+ // Validating permission
+ if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getUUID(), senderGroup.getName()) : false)) {
+ sender.sendMessage(ChatColor.RED + "You can't modify a player with same permissions as you, or higher.");
+ return true;
+ }
+ if (!isConsole && !isOpOverride && (permissionHandler.hasGroupInInheritance(auxGroup, senderGroup.getName()))) {
+ sender.sendMessage(ChatColor.RED + "The destination group can't be the same as yours, or higher.");
+ return true;
+ }
+ if (!isConsole && !isOpOverride && (!permissionHandler.inGroup(senderUser.getUUID(), auxUser.getGroupName()) || !permissionHandler.inGroup(senderUser.getUUID(), auxGroup.getName()))) {
+ sender.sendMessage(ChatColor.RED + "You can't modify a player involving a group that you don't inherit.");
+ return true;
+ }
+ if (!permissionHandler.hasGroupInInheritance(auxUser.getGroup(), auxGroup.getName()) && !permissionHandler.hasGroupInInheritance(auxGroup, auxUser.getGroupName())) {
+ sender.sendMessage(ChatColor.RED + "You can't modify a player using groups with different inheritage line.");
+ return true;
+ }
+ if (permissionHandler.hasGroupInInheritance(auxGroup, auxUser.getGroupName())) {
+ sender.sendMessage(ChatColor.RED + "The new group must be a lower rank.");
+ return true;
+ }
+ // Seems OK
+ auxUser.setGroup(auxGroup);
+ if (!sender.hasPermission("groupmanager.notify.other") || (isConsole)) {
+ sender.sendMessage(ChatColor.YELLOW + "You changed " + auxUser.getLastName() + " group to " + auxGroup.getName() + ".");
+ }
+
+ return true;
+
+ case mantogglevalidate:
+ validateOnlinePlayer = !validateOnlinePlayer;
+ sender.sendMessage(ChatColor.YELLOW + "Validate if player is online, now set to: " + Boolean.toString(validateOnlinePlayer));
+ if (!validateOnlinePlayer) {
+ sender.sendMessage(ChatColor.GOLD + "From now on you can edit players that are not connected... BUT:");
+ sender.sendMessage(ChatColor.LIGHT_PURPLE + "From now on you should type the whole name of the player, correctly.");
+ }
+ return true;
+ case mantogglesave:
+ if (scheduler == null) {
+ enableScheduler();
+ sender.sendMessage(ChatColor.YELLOW + "The auto-saving is enabled!");
+ } else {
+ disableScheduler();
+ sender.sendMessage(ChatColor.YELLOW + "The auto-saving is disabled!");
+ }
+ return true;
+ case manworld:
+ auxString = selectedWorlds.get(sender.getName());
+ if (auxString != null) {
+ sender.sendMessage(ChatColor.YELLOW + "You have the world '" + dataHolder.getName() + "' in your selection.");
+ } else {
+ if (dataHolder == null) {
+ sender.sendMessage(ChatColor.YELLOW + "There is no world selected. And no world is available now.");
+ } else {
+ sender.sendMessage(ChatColor.YELLOW + "You don't have a world in your selection..");
+ sender.sendMessage(ChatColor.YELLOW + "Working with the direct world where your player is.");
+ sender.sendMessage(ChatColor.YELLOW + "Your world now uses permissions of world name: '" + dataHolder.getName() + "' ");
+ }
+ }
+
+ return true;
+
+ case manselect:
+ if (args.length < 1) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manselect )");
+ sender.sendMessage(ChatColor.YELLOW + "Worlds available: ");
+ ArrayList worlds = worldsHolder.allWorldsDataList();
+ auxString = "";
+ for (int i = 0; i < worlds.size(); i++) {
+ auxString += worlds.get(i).getName();
+ if ((i + 1) < worlds.size()) {
+ auxString += ", ";
+ }
+ }
+ sender.sendMessage(ChatColor.YELLOW + auxString);
+ return false;
+ }
+ auxString = "";
+ for (int i = 0; i < args.length; i++) {
+ if (args[i] == null) {
+ logger.warning("Bukkit gave invalid arguments array! Cmd: " + cmd.getName() + " args.length: " + args.length);
+ return false;
+ }
+ auxString += args[i];
+ if (i < (args.length - 1)) {
+ auxString += " ";
+ }
+ }
+ dataHolder = worldsHolder.getWorldData(auxString);
+ permissionHandler = dataHolder.getPermissionsHandler();
+ selectedWorlds.put(sender.getName(), dataHolder.getName());
+ sender.sendMessage(ChatColor.YELLOW + "You have selected world '" + dataHolder.getName() + "'.");
+
+ return true;
+
+ case manclear:
+ if (args.length != 0) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count!");
+ return false;
+ }
+ selectedWorlds.remove(sender.getName());
+ sender.sendMessage(ChatColor.YELLOW + "You have removed your world selection. Working with current world(if possible).");
+
+ return true;
+
+ case mancheckw:
+ if (args.length < 1) {
+ sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mancheckw )");
+ sender.sendMessage(ChatColor.YELLOW + "Worlds available: ");
+ ArrayList worlds = worldsHolder.allWorldsDataList();
+ auxString = "";
+ for (int i = 0; i < worlds.size(); i++) {
+ auxString += worlds.get(i).getName();
+ if ((i + 1) < worlds.size()) {
+ auxString += ", ";
+ }
+ }
+ sender.sendMessage(ChatColor.YELLOW + auxString);
+ return false;
+ }
+
+ auxString = "";
+ for (int i = 0; i < args.length; i++) {
+ if (args[i] == null) {
+ logger.warning("Bukkit gave invalid arguments array! Cmd: " + cmd.getName() + " args.length: " + args.length);
+ return false;
+ }
+ auxString += args[i];
+ if (i < (args.length - 1)) {
+ auxString += " ";
+ }
+ }
+ dataHolder = worldsHolder.getWorldData(auxString);
+
+ sender.sendMessage(ChatColor.YELLOW + "You have selected world '" + dataHolder.getName() + "'.");
+ sender.sendMessage(ChatColor.YELLOW + "This world is using the following data files..");
+ sender.sendMessage(ChatColor.YELLOW + "Groups:" + ChatColor.GREEN + " " + dataHolder.getGroupsFile().getAbsolutePath());
+ sender.sendMessage(ChatColor.YELLOW + "Users:" + ChatColor.GREEN + " " + dataHolder.getUsersFile().getAbsolutePath());
+
+ return true;
+
+ default:
+ break;
+ }
+ }
+ sender.sendMessage(ChatColor.RED + "You are not allowed to use that command.");
+ return true;
+ }
+
+ /**
+ * Checks if a permission exists and of a lower or same priority.
+ */
+ private boolean checkPermissionExists(CommandSender sender, String newPerm, PermissionCheckResult oldPerm, String type) {
+
+
+ if (newPerm.startsWith("+")) {
+ if (oldPerm.resultType.equals(PermissionCheckResult.Type.EXCEPTION)) {
+ sender.sendMessage(ChatColor.RED + "The " + type + " already has direct access to that permission.");
+ sender.sendMessage(ChatColor.RED + "Node: " + oldPerm.accessLevel);
+ return true;
+ }
+ } else if (newPerm.startsWith("-")) {
+ if (oldPerm.resultType.equals(PermissionCheckResult.Type.EXCEPTION)) {
+ sender.sendMessage(ChatColor.RED + "The " + type + " already has an exception for this node.");
+ sender.sendMessage(ChatColor.RED + "Node: " + oldPerm.accessLevel);
+ return true;
+ } else if (oldPerm.resultType.equals(PermissionCheckResult.Type.NEGATION)) {
+ sender.sendMessage(ChatColor.RED + "The " + type + " already has a matching negated node.");
+ sender.sendMessage(ChatColor.RED + "Node: " + oldPerm.accessLevel);
+ return true;
+ }
+ } else {
+ if (oldPerm.resultType.equals(PermissionCheckResult.Type.EXCEPTION)) {
+ sender.sendMessage(ChatColor.RED + "The " + type + " already has an exception for this node.");
+ sender.sendMessage(ChatColor.RED + "Node: " + oldPerm.accessLevel);
+ } else if (oldPerm.resultType.equals(PermissionCheckResult.Type.NEGATION)) {
+ sender.sendMessage(ChatColor.RED + "The " + type + " already has a matching negated node.");
+ sender.sendMessage(ChatColor.RED + "Node: " + oldPerm.accessLevel);
+ } else if (oldPerm.resultType.equals(PermissionCheckResult.Type.FOUND)) {
+ sender.sendMessage(ChatColor.RED + "The " + type + " already has direct access to that permission.");
+ sender.sendMessage(ChatColor.RED + "Node: " + oldPerm.accessLevel);
+
+ // Since not all plugins define wildcard permissions, allow setting the permission anyway if the permissions dont match exactly.
+ return (oldPerm.accessLevel.equalsIgnoreCase(newPerm));
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Sets up the default world for use.
+ */
+ private boolean setDefaultWorldHandler(CommandSender sender) {
+
+ dataHolder = worldsHolder.getWorldData(worldsHolder.getDefaultWorld().getName());
+ permissionHandler = dataHolder.getPermissionsHandler();
+
+ if ((dataHolder != null) && (permissionHandler != null)) {
+ selectedWorlds.put(sender.getName(), dataHolder.getName());
+ sender.sendMessage(ChatColor.RED + "Couldn't retrieve your world. Default world '" + worldsHolder.getDefaultWorld().getName() + "' selected.");
+ return true;
+ }
+
+ sender.sendMessage(ChatColor.RED + "Couldn't retrieve your world. World selection is needed.");
+ sender.sendMessage(ChatColor.RED + "Use /manselect ");
+ return false;
+
+ }
+
+ /**
+ * Send confirmation of a group change. using permission nodes...
+ *
+ * groupmanager.notify.self groupmanager.notify.other
+ *
+ * @param name
+ * @param msg
+ */
+ public static void notify(String name, String msg) {
+
+ Player player = Bukkit.getServer().getPlayerExact(name);
+
+ for (Player test : Bukkit.getServer().getOnlinePlayers()) {
+ if (!test.equals(player)) {
+ if (test.hasPermission("groupmanager.notify.other")) {
+ test.sendMessage(ChatColor.YELLOW + name + " was" + msg);
+ }
+ } else if ((player != null) && ((player.hasPermission("groupmanager.notify.self")) || (player.hasPermission("groupmanager.notify.other")))) {
+ player.sendMessage(ChatColor.YELLOW + "You were" + msg);
+ }
+ }
+
+ }
+
+ /**
+ * Load a List of players matching the name given. If none online, check
+ * Offline.
+ *
+ * @param playerName, sender
+ *
+ * @return true if a single match is found
+ */
+ private List validatePlayer(String playerName, CommandSender sender) {
+
+ List players = new ArrayList();
+ List match = new ArrayList();
+
+ players = this.getServer().matchPlayer(playerName);
+ if (players.isEmpty()) {
+ // Check for an offline player (exact match).
+ if (Arrays.asList(this.getServer().getOfflinePlayers()).contains(Bukkit.getOfflinePlayer(playerName))) {
+ match.add(Bukkit.getOfflinePlayer(playerName).getName()); //.getUniqueId().toString());
+ } else {
+ // look for partial matches
+ for (OfflinePlayer offline : this.getServer().getOfflinePlayers()) {
+ if (offline.getName().toLowerCase().startsWith(playerName.toLowerCase())) {
+ match.add(offline.getName()); //.getUniqueId().toString());
+ }
+ }
+ }
+
+ } else {
+ for (Player player : players) {
+ match.add(player.getUniqueId().toString());
+ }
+ }
+
+ if (match.isEmpty()) {
+ sender.sendMessage(ChatColor.RED + "Player not found!");
+ return null;
+ } else if (match.size() > 1) {
+ sender.sendMessage(ChatColor.RED + "Too many matches found! (" + match.toString() + ")");
+ return null;
+ }
+
+ return match;
+
+ }
+
+ /**
+ * @return the config
+ */
+ public GMConfiguration getGMConfig() {
+
+ return config;
+ }
+
+ /**
+ * @return the backupFolder
+ */
+ public File getBackupFolder() {
+
+ return backupFolder;
+ }
+
+ public static GlobalGroups getGlobalGroups() {
+
+ return globalGroups;
+
+ }
+
+ public static GroupManagerEventHandler getGMEventHandler() {
+
+ return GMEventHandler;
+ }
+
+ public static void setGMEventHandler(GroupManagerEventHandler gMEventHandler) {
+
+ GMEventHandler = gMEventHandler;
+ }
+}
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/Tasks/BukkitPermsUpdateTask.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/Tasks/BukkitPermsUpdateTask.java
new file mode 100644
index 000000000..6db4b20e9
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/Tasks/BukkitPermsUpdateTask.java
@@ -0,0 +1,29 @@
+package org.anjocaido.groupmanager.Tasks;
+
+import org.anjocaido.groupmanager.GroupManager;
+
+/*
+ *
+ * Created by ElgarL
+ */
+
+public class BukkitPermsUpdateTask implements Runnable {
+
+ public BukkitPermsUpdateTask() {
+
+ super();
+ }
+
+ @Override
+ public void run() {
+
+ // Signal loaded and update BukkitPermissions.
+ GroupManager.setLoaded(true);
+ GroupManager.BukkitPermissions.collectPermissions();
+ GroupManager.BukkitPermissions.updateAllPlayers();
+
+ GroupManager.logger.info("Bukkit Permissions Updated!");
+
+ }
+
+}
\ No newline at end of file
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/data/DataUnit.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/data/DataUnit.java
new file mode 100644
index 000000000..c93135a0f
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/data/DataUnit.java
@@ -0,0 +1,214 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.anjocaido.groupmanager.data;
+
+import org.anjocaido.groupmanager.GroupManager;
+import org.anjocaido.groupmanager.dataholder.WorldDataHolder;
+import org.anjocaido.groupmanager.utils.StringPermissionComparator;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author gabrielcouto
+ */
+public abstract class DataUnit {
+
+ private WorldDataHolder dataSource;
+ private String uUID;
+ private String lastName;
+ private boolean changed, sorted = false;
+ private List permissions = Collections.unmodifiableList(Collections.emptyList());
+
+ public DataUnit(WorldDataHolder dataSource, String name) {
+
+ this.dataSource = dataSource;
+ this.uUID = name;
+ }
+
+ public DataUnit(String name) {
+
+ this.uUID = name;
+ }
+
+ /**
+ * Every group is matched only by their names and DataSources names.
+ *
+ * @param o
+ *
+ * @return true if they are equal. false if not.
+ */
+ @Override
+ public boolean equals(Object o) {
+
+ if (o instanceof DataUnit) {
+ DataUnit go = (DataUnit) o;
+ if (this.getUUID().equalsIgnoreCase(go.getUUID())) {
+ // Global Group match.
+ if (this.dataSource == null && go.getDataSource() == null) {
+ return true;
+ }
+ // This is a global group, the object to test isn't.
+ if (this.dataSource == null && go.getDataSource() != null) {
+ return false;
+ }
+ // This is not a global group, but the object to test is.
+ if (this.dataSource != null && go.getDataSource() == null) {
+ return false;
+ }
+ // Match on group name and world name.
+ if (this.dataSource.getName().equalsIgnoreCase(go.getDataSource().getName())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+
+ int hash = 5;
+ hash = 71 * hash + (this.uUID != null ? this.uUID.toLowerCase().hashCode() : 0);
+ return hash;
+ }
+
+ /**
+ * Set the data source to point to a different worldDataHolder
+ *
+ * @param source
+ */
+ public void setDataSource(WorldDataHolder source) {
+
+ this.dataSource = source;
+ }
+
+ /**
+ * Get the current worldDataHolder this object is pointing to
+ *
+ * @return the dataSource
+ */
+ public WorldDataHolder getDataSource() {
+
+ return dataSource;
+ }
+
+ public String getUUID() {
+
+ return uUID;
+ }
+
+ public String getLastName() {
+
+ if (uUID.length() < 36) {
+ return this.uUID;
+ }
+
+ return this.lastName;
+ }
+
+ public void setLastName(String lastName) {
+
+ if (!lastName.equals(this.lastName)) {
+
+ this.lastName = lastName;
+ dataSource.putUUIDLookup(lastName, uUID);
+
+ changed = true;
+
+ }
+
+ }
+
+ public void flagAsChanged() {
+
+ WorldDataHolder testSource = getDataSource();
+ String source = "";
+
+ if (testSource == null) {
+ source = "GlobalGroups";
+ } else {
+ source = testSource.getName();
+ }
+
+ GroupManager.logger.finest("DataSource: " + source + " - DataUnit: " + getUUID() + " flagged as changed!");
+ // for(StackTraceElement st: Thread.currentThread().getStackTrace()){
+ // GroupManager.logger.finest(st.toString());
+ // }
+ sorted = false;
+ changed = true;
+ }
+
+ public boolean isChanged() {
+
+ return changed;
+ }
+
+ public void flagAsSaved() {
+
+ WorldDataHolder testSource = getDataSource();
+ String source = "";
+
+ if (testSource == null) {
+ source = "GlobalGroups";
+ } else {
+ source = testSource.getName();
+ }
+
+ GroupManager.logger.finest("DataSource: " + source + " - DataUnit: " + getUUID() + " flagged as saved!");
+ changed = false;
+ }
+
+ public boolean hasSamePermissionNode(String permission) {
+
+ return permissions.contains(permission);
+ }
+
+ public void addPermission(String permission) {
+
+ if (!hasSamePermissionNode(permission)) {
+ List clone = new ArrayList(permissions);
+ clone.add(permission);
+ permissions = Collections.unmodifiableList(clone);
+ }
+ flagAsChanged();
+ }
+
+ public boolean removePermission(String permission) {
+
+ flagAsChanged();
+ List clone = new ArrayList(permissions);
+ boolean ret = clone.remove(permission);
+ permissions = Collections.unmodifiableList(clone);
+ return ret;
+ }
+
+ /**
+ * Use this only to list permissions.
+ * You can't edit the permissions using the returned ArrayList instance
+ *
+ * @return a copy of the permission list
+ */
+ public List getPermissionList() {
+ sortPermissions();
+ return permissions;
+ }
+
+ public boolean isSorted() {
+
+ return this.sorted;
+ }
+
+ public void sortPermissions() {
+
+ if (!isSorted()) {
+ List clone = new ArrayList(permissions);
+ Collections.sort(clone, StringPermissionComparator.getInstance());
+ permissions = Collections.unmodifiableList(clone);
+ sorted = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/data/Group.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/data/Group.java
new file mode 100644
index 000000000..254868bc5
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/data/Group.java
@@ -0,0 +1,196 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.anjocaido.groupmanager.data;
+
+import org.anjocaido.groupmanager.GroupManager;
+import org.anjocaido.groupmanager.dataholder.WorldDataHolder;
+import org.anjocaido.groupmanager.events.GMGroupEvent.Action;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author gabrielcouto/ElgarL
+ */
+public class Group extends DataUnit implements Cloneable {
+
+ /**
+ * The group it inherits DIRECTLY!
+ */
+ private List inherits = Collections.unmodifiableList(Collections.emptyList());
+ /**
+ * This one holds the fields in INFO node.
+ * like prefix = 'c'
+ * or build = false
+ */
+ private GroupVariables variables = new GroupVariables(this);
+
+ /**
+ * Constructor for individual World Groups.
+ *
+ * @param name
+ */
+ public Group(WorldDataHolder source, String name) {
+
+ super(source, name);
+ }
+
+ /**
+ * Constructor for Global Groups.
+ *
+ * @param name
+ */
+ public Group(String name) {
+
+ super(name);
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName() {
+
+ return this.getUUID();
+ }
+
+ /**
+ * Is this a GlobalGroup
+ *
+ * @return true if this is a global group
+ */
+ public boolean isGlobal() {
+
+ return (getDataSource() == null);
+ }
+
+ /**
+ * Clone this group
+ *
+ * @return a clone of this group
+ */
+ @Override
+ public Group clone() {
+
+ Group clone;
+
+ if (isGlobal()) {
+ clone = new Group(this.getName());
+ } else {
+ clone = new Group(getDataSource(), this.getName());
+ clone.inherits = this.getInherits().isEmpty() ? Collections.unmodifiableList(Collections.emptyList()) : Collections.unmodifiableList(new ArrayList(this.getInherits()));
+ }
+
+ for (String perm : this.getPermissionList()) {
+ clone.addPermission(perm);
+ }
+ clone.variables = ((GroupVariables) variables).clone(clone);
+ //clone.flagAsChanged();
+ return clone;
+ }
+
+ /**
+ * Use this to deliver a group from a different dataSource to another
+ *
+ * @param dataSource
+ *
+ * @return Null or Clone
+ */
+ public Group clone(WorldDataHolder dataSource) {
+
+ if (dataSource.groupExists(this.getName())) {
+ return null;
+ }
+
+ Group clone = dataSource.createGroup(this.getName());
+
+ // Don't add inheritance for GlobalGroups
+ if (!isGlobal()) {
+ clone.inherits = this.getInherits().isEmpty() ? Collections.unmodifiableList(Collections.emptyList()) : Collections.unmodifiableList(new ArrayList(this.getInherits()));
+ }
+ for (String perm : this.getPermissionList()) {
+ clone.addPermission(perm);
+ }
+ clone.variables = variables.clone(clone);
+ clone.flagAsChanged(); //use this to make the new dataSource save the new group
+ return clone;
+ }
+
+ /**
+ * an unmodifiable list of inherits list
+ * You can't manage the list by here
+ * Lol... version 0.6 had a problem because this.
+ *
+ * @return the inherits
+ */
+ public List getInherits() {
+ return inherits;
+ }
+
+ /**
+ * @param inherit the inherits to set
+ */
+ public void addInherits(Group inherit) {
+
+ if (!isGlobal()) {
+ if (!this.getDataSource().groupExists(inherit.getName())) {
+ getDataSource().addGroup(inherit);
+ }
+ if (!inherits.contains(inherit.getName().toLowerCase())) {
+ List clone = new ArrayList(inherits);
+ clone.add(inherit.getName().toLowerCase());
+ inherits = Collections.unmodifiableList(clone);
+ }
+ flagAsChanged();
+ if (GroupManager.isLoaded()) {
+ GroupManager.BukkitPermissions.updateAllPlayers();
+ GroupManager.getGMEventHandler().callEvent(this, Action.GROUP_INHERITANCE_CHANGED);
+ }
+ }
+ }
+
+ public boolean removeInherits(String inherit) {
+
+ if (!isGlobal()) {
+ if (this.inherits.contains(inherit.toLowerCase())) {
+ List clone = new ArrayList(inherits);
+ clone.remove(inherit.toLowerCase());
+ inherits = Collections.unmodifiableList(clone);
+ flagAsChanged();
+ GroupManager.getGMEventHandler().callEvent(this, Action.GROUP_INHERITANCE_CHANGED);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return the variables
+ */
+ public GroupVariables getVariables() {
+
+ return variables;
+ }
+
+ /**
+ * @param varList
+ */
+ public void setVariables(Map varList) {
+
+ if (!isGlobal()) {
+ GroupVariables temp = new GroupVariables(this, varList);
+ variables.clearVars();
+ for (String key : temp.getVarKeyList()) {
+ variables.addVar(key, temp.getVarObject(key));
+ }
+ flagAsChanged();
+ if (GroupManager.isLoaded()) {
+ GroupManager.BukkitPermissions.updateAllPlayers();
+ GroupManager.getGMEventHandler().callEvent(this, Action.GROUP_INFO_CHANGED);
+ }
+ }
+ }
+}
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/data/GroupVariables.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/data/GroupVariables.java
new file mode 100644
index 000000000..b71cfe6dd
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/data/GroupVariables.java
@@ -0,0 +1,96 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.anjocaido.groupmanager.data;
+
+import java.util.Map;
+
+/**
+ * @author gabrielcouto
+ */
+public class GroupVariables extends Variables implements Cloneable {
+
+ private Group owner;
+
+ public GroupVariables(Group owner) {
+
+ super(owner);
+ this.owner = owner;
+ addVar("prefix", "");
+ addVar("suffix", "");
+ addVar("build", false);
+ }
+
+ public GroupVariables(Group owner, Map varList) {
+
+ super(owner);
+ variables.clear();
+ variables.putAll(varList);
+ if (variables.get("prefix") == null) {
+ variables.put("prefix", "");
+ owner.flagAsChanged();
+ }
+ //thisGrp.prefix = infoNode.get("prefix").toString();
+
+ if (variables.get("suffix") == null) {
+ variables.put("suffix", "");
+ owner.flagAsChanged();
+ }
+ //thisGrp.suffix = infoNode.get("suffix").toString();
+
+ if (variables.get("build") == null) {
+ variables.put("build", false);
+ owner.flagAsChanged();
+ }
+ this.owner = owner;
+ }
+
+ /**
+ * A clone of all vars here.
+ *
+ * @return GroupVariables clone
+ */
+ protected GroupVariables clone(Group newOwner) {
+
+ GroupVariables clone = new GroupVariables(newOwner);
+ synchronized (variables) {
+ for (String key : variables.keySet()) {
+ clone.variables.put(key, variables.get(key));
+ }
+ }
+ newOwner.flagAsChanged();
+ return clone;
+ }
+
+ /**
+ * Remove a var from the list
+ *
+ * @param name
+ */
+ @Override
+ public void removeVar(String name) {
+
+ try {
+ this.variables.remove(name);
+ } catch (Exception e) {
+ }
+ if (name.equals("prefix")) {
+ addVar("prefix", "");
+ } else if (name.equals("suffix")) {
+ addVar("suffix", "");
+ } else if (name.equals("build")) {
+ addVar("build", false);
+ }
+ owner.flagAsChanged();
+ }
+
+ /**
+ * @return the owner
+ */
+ @Override
+ public Group getOwner() {
+
+ return owner;
+ }
+}
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/data/User.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/data/User.java
new file mode 100644
index 000000000..6abfc702c
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/data/User.java
@@ -0,0 +1,327 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.anjocaido.groupmanager.data;
+
+//import com.sun.org.apache.bcel.internal.generic.AALOAD;
+
+import org.anjocaido.groupmanager.GroupManager;
+import org.anjocaido.groupmanager.dataholder.WorldDataHolder;
+import org.anjocaido.groupmanager.events.GMUserEvent.Action;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author gabrielcouto/ElgarL
+ */
+public class User extends DataUnit implements Cloneable {
+
+ /**
+ *
+ */
+ private String group = null;
+ private final List subGroups = Collections.synchronizedList(new ArrayList());
+ /**
+ * This one holds the fields in INFO node. like prefix = 'c' or build =
+ * false
+ */
+ private UserVariables variables = new UserVariables(this);
+ private transient Player bukkitPlayer = null;
+
+ /**
+ * @param name
+ */
+ public User(WorldDataHolder source, String name) {
+
+ super(source, name);
+ this.group = source.getDefaultGroup().getName();
+ }
+
+ /**
+ * @return User clone
+ */
+ @Override
+ public User clone() {
+
+ User clone = new User(getDataSource(), this.getLastName());
+ clone.group = this.group;
+
+ // Clone all subgroups.
+ clone.subGroups.addAll(this.subGroupListStringCopy());
+
+ for (String perm : this.getPermissionList()) {
+ clone.addPermission(perm);
+ }
+ // clone.variables = this.variables.clone();
+ // clone.flagAsChanged();
+ return clone;
+ }
+
+ /**
+ * Use this to deliver a user from one WorldDataHolder to another
+ *
+ * @param dataSource
+ *
+ * @return null if given dataSource already contains the same user
+ */
+ public User clone(WorldDataHolder dataSource) {
+
+ if (dataSource.isUserDeclared(this.getUUID())) {
+ return null;
+ }
+
+ User clone = dataSource.createUser(this.getUUID());
+
+ if (dataSource.getGroup(group) == null) {
+ clone.setGroup(dataSource.getDefaultGroup());
+ } else {
+ clone.setGroup(dataSource.getGroup(this.getGroupName()));
+ }
+
+ // Clone all subgroups.
+ clone.subGroups.addAll(this.subGroupListStringCopy());
+
+ for (String perm : this.getPermissionList()) {
+ clone.addPermission(perm);
+ }
+
+ clone.variables = this.variables.clone(this);
+ clone.flagAsChanged();
+ return clone;
+ }
+
+ public User clone(String uUID, String CurrentName) {
+
+ User clone = this.getDataSource().createUser(uUID);
+
+ clone.setLastName(CurrentName);
+
+ // Set the group silently.
+ clone.setGroup(this.getDataSource().getGroup(this.getGroupName()), false);
+
+ // Clone all subgroups.
+ clone.subGroups.addAll(this.subGroupListStringCopy());
+
+ for (String perm : this.getPermissionList()) {
+ clone.addPermission(perm);
+ }
+
+ clone.variables = this.variables.clone(this);
+ clone.flagAsChanged();
+
+ return clone;
+ }
+
+ public Group getGroup() {
+
+ Group result = getDataSource().getGroup(group);
+ if (result == null) {
+ this.setGroup(getDataSource().getDefaultGroup());
+ result = getDataSource().getDefaultGroup();
+ }
+ return result;
+ }
+
+ /**
+ * @return the group
+ */
+ public String getGroupName() {
+
+ Group result = getDataSource().getGroup(group);
+ if (result == null) {
+ group = getDataSource().getDefaultGroup().getName();
+ }
+ return group;
+ }
+
+ /**
+ * Place holder to let people know to stop using this method.
+ *
+ * @return a string containing the players last known name.
+ *
+ * @deprecated use {@link #getLastName()} and {@link #getUUID()}.
+ */
+ @Deprecated
+ public String getName() {
+
+ return this.getLastName();
+
+ }
+
+
+ /**
+ * @param group the group to set
+ */
+ public void setGroup(Group group) {
+
+ setGroup(group, true);
+ }
+
+ /**
+ * @param group the group to set
+ * @param updatePerms if we are to trigger a superperms update.
+ */
+ public void setGroup(Group group, Boolean updatePerms) {
+
+ if (!this.getDataSource().groupExists(group.getName())) {
+ getDataSource().addGroup(group);
+ }
+ group = getDataSource().getGroup(group.getName());
+ String oldGroup = this.group;
+ this.group = group.getName();
+ flagAsChanged();
+ if (GroupManager.isLoaded()) {
+ if (!GroupManager.BukkitPermissions.isPlayer_join() && (updatePerms)) {
+ GroupManager.BukkitPermissions.updatePlayer(getBukkitPlayer());
+ }
+
+ // Do we notify of the group change?
+ String defaultGroupName = getDataSource().getDefaultGroup().getName();
+ // if we were not in the default group
+ // or we were in the default group and the move is to a different
+ // group.
+ boolean notify = (!oldGroup.equalsIgnoreCase(defaultGroupName)) || ((oldGroup.equalsIgnoreCase(defaultGroupName)) && (!this.group.equalsIgnoreCase(defaultGroupName)));
+
+ if (notify) {
+ GroupManager.notify(this.getLastName(), String.format(" moved to the group %s in %s.", group.getName(), this.getDataSource().getName()));
+ }
+
+ if (updatePerms) {
+ GroupManager.getGMEventHandler().callEvent(this, Action.USER_GROUP_CHANGED);
+ }
+ }
+ }
+
+ public boolean addSubGroup(Group subGroup) {
+
+ // Don't allow adding a subgroup if it's already set as the primary.
+ if (this.group.equalsIgnoreCase(subGroup.getName())) {
+ return false;
+ }
+ // User already has this subgroup
+ if (containsSubGroup(subGroup)) {
+ return false;
+ }
+
+ // If the group doesn't exists add it
+ if (!this.getDataSource().groupExists(subGroup.getName())) {
+ getDataSource().addGroup(subGroup);
+ }
+
+ subGroups.add(subGroup.getName());
+ flagAsChanged();
+ if (GroupManager.isLoaded()) {
+ if (!GroupManager.BukkitPermissions.isPlayer_join()) {
+ GroupManager.BukkitPermissions.updatePlayer(getBukkitPlayer());
+ }
+ GroupManager.getGMEventHandler().callEvent(this, Action.USER_SUBGROUP_CHANGED);
+ }
+ return true;
+
+ //subGroup = getDataSource().getGroup(subGroup.getName());
+ //removeSubGroup(subGroup);
+ //subGroups.add(subGroup.getName());
+ }
+
+ public int subGroupsSize() {
+
+ return subGroups.size();
+ }
+
+ public boolean isSubGroupsEmpty() {
+
+ return subGroups.isEmpty();
+ }
+
+ public boolean containsSubGroup(Group subGroup) {
+
+ return subGroups.contains(subGroup.getName());
+ }
+
+ public boolean removeSubGroup(Group subGroup) {
+
+ try {
+ if (subGroups.remove(subGroup.getName())) {
+ flagAsChanged();
+ if (GroupManager.isLoaded()) {
+ if (!GroupManager.BukkitPermissions.isPlayer_join()) {
+ GroupManager.BukkitPermissions.updatePlayer(getBukkitPlayer());
+ }
+ }
+ GroupManager.getGMEventHandler().callEvent(this, Action.USER_SUBGROUP_CHANGED);
+ return true;
+ }
+ } catch (Exception e) {
+ }
+ return false;
+ }
+
+ public ArrayList subGroupListCopy() {
+
+ ArrayList val = new ArrayList();
+ synchronized (subGroups) {
+ for (String gstr : subGroups) {
+ Group g = getDataSource().getGroup(gstr);
+ if (g == null) {
+ removeSubGroup(g);
+ continue;
+ }
+ val.add(g);
+ }
+ }
+ return val;
+ }
+
+ public ArrayList subGroupListStringCopy() {
+ synchronized (subGroups) {
+ return new ArrayList(subGroups);
+ }
+ }
+
+ /**
+ * @return the variables
+ */
+ public UserVariables getVariables() {
+
+ return variables;
+ }
+
+ /**
+ * @param varList
+ */
+ public void setVariables(Map varList) {
+
+ //UserVariables temp = new UserVariables(this, varList);
+ variables.clearVars();
+ for (String key : varList.keySet()) {
+ variables.addVar(key, varList.get(key));
+ }
+ flagAsChanged();
+ if (GroupManager.isLoaded()) {
+ //if (!GroupManager.BukkitPermissions.isPlayer_join())
+ // GroupManager.BukkitPermissions.updatePlayer(this.getName());
+ GroupManager.getGMEventHandler().callEvent(this, Action.USER_INFO_CHANGED);
+ }
+ }
+
+
+ public User updatePlayer(Player player) {
+
+ bukkitPlayer = player;
+ return this;
+ }
+
+ public Player getBukkitPlayer() {
+
+ if (bukkitPlayer == null) {
+ bukkitPlayer = Bukkit.getPlayer(this.getLastName());
+ }
+ return bukkitPlayer;
+ }
+}
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/data/UserVariables.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/data/UserVariables.java
new file mode 100644
index 000000000..b799d45e5
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/data/UserVariables.java
@@ -0,0 +1,55 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.anjocaido.groupmanager.data;
+
+import java.util.Map;
+
+/**
+ * @author gabrielcouto
+ */
+public class UserVariables extends Variables {
+
+ private User owner;
+
+ public UserVariables(User owner) {
+
+ super(owner);
+ this.owner = owner;
+ }
+
+ public UserVariables(User owner, Map varList) {
+
+ super(owner);
+ this.variables.clear();
+ this.variables.putAll(varList);
+ this.owner = owner;
+ }
+
+ /**
+ * A clone of all vars here.
+ *
+ * @return UserVariables clone
+ */
+ protected UserVariables clone(User newOwner) {
+
+ UserVariables clone = new UserVariables(newOwner);
+ synchronized (variables) {
+ for (String key : variables.keySet()) {
+ clone.variables.put(key, variables.get(key));
+ }
+ }
+ newOwner.flagAsChanged();
+ return clone;
+ }
+
+ /**
+ * @return the owner
+ */
+ @Override
+ public User getOwner() {
+
+ return owner;
+ }
+}
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/data/Variables.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/data/Variables.java
new file mode 100644
index 000000000..416ba11a0
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/data/Variables.java
@@ -0,0 +1,212 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.anjocaido.groupmanager.data;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A class that holds variables of a user/group.
+ * In groups, it holds the contents of INFO node.
+ * Like:
+ * prefix
+ * suffix
+ * build
+ *
+ * @author gabrielcouto
+ */
+public abstract class Variables implements Cloneable {
+
+ private DataUnit owner;
+ protected final Map variables = Collections.synchronizedMap(new HashMap());
+
+ public Variables(DataUnit owner) {
+
+ this.owner = owner;
+ }
+
+ /**
+ * Add var to the the INFO node.
+ * examples:
+ * addVar("build",true);
+ * addVar("prefix","c");
+ *
+ * @param name key name of the var
+ * @param o the object value of the var
+ */
+ public void addVar(String name, Object o) {
+
+ if (o == null) {
+ return;
+ }
+ if (variables.containsKey(name)) {
+ variables.remove(name);
+ }
+ variables.put(name, o);
+ owner.flagAsChanged();
+ }
+
+ /**
+ * Returns the object inside the var
+ *
+ * @param name
+ *
+ * @return a Object if exists. null if doesn't exists
+ */
+ public Object getVarObject(String name) {
+
+ return variables.get(name);
+ }
+
+ /**
+ * Get the String value for the given var name
+ *
+ * @param name the var key name
+ *
+ * @return "" if null. or the toString() value of object
+ */
+ public String getVarString(String name) {
+
+ Object o = variables.get(name);
+ try {
+ return o == null ? "" : o.toString();
+ } catch (Exception e) {
+ return "";
+ }
+ }
+
+ /**
+ * @param name
+ *
+ * @return false if null. or a Boolean.parseBoolean of the string
+ */
+ public Boolean getVarBoolean(String name) {
+
+ Object o = variables.get(name);
+ try {
+ return o == null ? false : Boolean.parseBoolean(o.toString());
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ /**
+ * @param name
+ *
+ * @return -1 if null. or a parseInt of the string
+ */
+ public Integer getVarInteger(String name) {
+
+ Object o = variables.get(name);
+ try {
+ return o == null ? -1 : Integer.parseInt(o.toString());
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ /**
+ * @param name
+ *
+ * @return -1 if null. or a parseDouble of the string
+ */
+ public Double getVarDouble(String name) {
+
+ Object o = variables.get(name);
+ try {
+ return o == null ? -1.0D : Double.parseDouble(o.toString());
+ } catch (Exception e) {
+ return -1.0D;
+ }
+ }
+
+ /**
+ * All variable keys this is holding
+ *
+ * @return Set of all variable names.
+ */
+ public String[] getVarKeyList() {
+ synchronized (variables) {
+ return variables.keySet().toArray(new String[0]);
+ }
+ }
+
+ /**
+ * verify is a var exists
+ *
+ * @param name the key name of the var
+ *
+ * @return true if that var exists
+ */
+ public boolean hasVar(String name) {
+
+ return variables.containsKey(name);
+ }
+
+ /**
+ * Returns the quantity of vars this is holding
+ *
+ * @return the number of vars
+ */
+ public int getSize() {
+
+ return variables.size();
+ }
+
+ /**
+ * Remove a var from the list
+ *
+ * @param name
+ */
+ public void removeVar(String name) {
+
+ try {
+ variables.remove(name);
+ } catch (Exception e) {
+ }
+ owner.flagAsChanged();
+ }
+
+ public static Object parseVariableValue(String value) {
+
+ try {
+ Integer i = Integer.parseInt(value);
+ return i;
+ } catch (NumberFormatException e) {
+ }
+ try {
+ Double d = Double.parseDouble(value);
+ return d;
+ } catch (NumberFormatException e) {
+ }
+ if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("yes") || value.equalsIgnoreCase("on")) {
+ return true;
+ } else if (value.equalsIgnoreCase("false") || value.equalsIgnoreCase("no") || value.equalsIgnoreCase("off")) {
+ return false;
+ }
+ return value;
+
+ }
+
+ public void clearVars() {
+
+ variables.clear();
+ owner.flagAsChanged();
+ }
+
+ /**
+ * @return the owner
+ */
+ public DataUnit getOwner() {
+
+ return owner;
+ }
+
+ public boolean isEmpty() {
+
+ return variables.isEmpty();
+ }
+}
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/dataholder/GroupsDataHolder.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/dataholder/GroupsDataHolder.java
new file mode 100644
index 000000000..1ae4d06bc
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/dataholder/GroupsDataHolder.java
@@ -0,0 +1,131 @@
+package org.anjocaido.groupmanager.dataholder;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.anjocaido.groupmanager.data.Group;
+
+/**
+ * This container holds all Groups loaded from the relevant groupsFile.
+ *
+ * @author ElgarL
+ *
+ */
+public class GroupsDataHolder {
+
+ private WorldDataHolder dataSource;
+ private Group defaultGroup = null;
+ private File groupsFile;
+ private boolean haveGroupsChanged = false;
+ private long timeStampGroups = 0;
+
+ /**
+ * The actual groups holder
+ */
+ private final Map groups = Collections.synchronizedMap(new HashMap());
+
+ /**
+ * Constructor
+ */
+ protected GroupsDataHolder() {
+
+ }
+
+ public void setDataSource(WorldDataHolder dataSource) {
+
+ this.dataSource = dataSource;
+ //push this data source to the users, so they pull the correct groups data.
+ synchronized(groups) {
+ for (Group group : groups.values())
+ group.setDataSource(this.dataSource);
+ }
+ }
+
+ public WorldDataHolder getDataSource() {
+
+ return this.dataSource;
+ }
+
+ /**
+ * @return the defaultGroup
+ */
+ public Group getDefaultGroup() {
+
+ return defaultGroup;
+ }
+
+ /**
+ * @param defaultGroup the defaultGroup to set
+ */
+ public void setDefaultGroup(Group defaultGroup) {
+
+ this.defaultGroup = defaultGroup;
+ }
+
+ /**
+ * Note: Iteration over this object has to be synchronized!
+ * @return the groups
+ */
+ public Map getGroups() {
+
+ return groups;
+ }
+
+ /**
+ * Resets the Groups
+ */
+ public void resetGroups() {
+ this.groups.clear();
+ }
+
+ /**
+ * @return the groupsFile
+ */
+ public File getGroupsFile() {
+
+ return groupsFile;
+ }
+
+ /**
+ * @param groupsFile the groupsFile to set
+ */
+ public void setGroupsFile(File groupsFile) {
+
+ this.groupsFile = groupsFile;
+ }
+
+ /**
+ * @return the haveGroupsChanged
+ */
+ public boolean HaveGroupsChanged() {
+
+ return haveGroupsChanged;
+ }
+
+ /**
+ * @param haveGroupsChanged the haveGroupsChanged to set
+ */
+ public void setGroupsChanged(boolean haveGroupsChanged) {
+
+ this.haveGroupsChanged = haveGroupsChanged;
+ }
+
+ /**
+ * @return the timeStampGroups
+ */
+ public long getTimeStampGroups() {
+
+ return timeStampGroups;
+ }
+
+ /**
+ * @param timeStampGroups the timeStampGroups to set
+ */
+ public void setTimeStampGroups(long timeStampGroups) {
+
+ this.timeStampGroups = timeStampGroups;
+ }
+
+}
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/dataholder/OverloadedWorldHolder.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/dataholder/OverloadedWorldHolder.java
new file mode 100644
index 000000000..ca9392c9c
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/dataholder/OverloadedWorldHolder.java
@@ -0,0 +1,217 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.anjocaido.groupmanager.dataholder;
+
+import org.anjocaido.groupmanager.data.User;
+
+import java.util.*;
+
+/**
+ *
+ * @author gabrielcouto
+ */
+public class OverloadedWorldHolder extends WorldDataHolder {
+
+ /**
+ *
+ */
+ protected final Map overloadedUsers = Collections.synchronizedMap(new HashMap());
+
+ /**
+ *
+ * @param ph
+ */
+ public OverloadedWorldHolder(WorldDataHolder ph) {
+
+ super(ph.getName());
+ this.setGroupsFile(ph.getGroupsFile());
+ this.setUsersFile(ph.getUsersFile());
+ this.groups = ph.groups;
+ this.users = ph.users;
+ }
+
+ /**
+ *
+ * @param userName
+ * @return user object or a new user if none exists.
+ */
+ @Override
+ public User getUser(String userId) {
+
+ //OVERLOADED CODE
+ String userNameLowered = userId.toLowerCase();
+ if (overloadedUsers.containsKey(userNameLowered)) {
+ return overloadedUsers.get(userNameLowered);
+ }
+ //END CODE
+
+ return super.getUser(userId);
+ }
+
+ /**
+ *
+ * @param theUser
+ */
+ @Override
+ public void addUser(User theUser) {
+
+ if (theUser.getDataSource() != this) {
+ theUser = theUser.clone(this);
+ }
+ if (theUser == null) {
+ return;
+ }
+ if ((theUser.getGroup() == null) || (!getGroups().containsKey(theUser.getGroupName().toLowerCase()))) {
+ theUser.setGroup(getDefaultGroup());
+ }
+ //OVERLOADED CODE
+ if (overloadedUsers.containsKey(theUser.getUUID().toLowerCase())) {
+ overloadedUsers.remove(theUser.getUUID().toLowerCase());
+ overloadedUsers.put(theUser.getUUID().toLowerCase(), theUser);
+ return;
+ }
+ //END CODE
+ removeUser(theUser.getUUID());
+ getUsers().put(theUser.getUUID().toLowerCase(), theUser);
+ setUsersChanged(true);
+ }
+
+ /**
+ *
+ * @param userId
+ * @return true if removed/false if not found.
+ */
+ @Override
+ public boolean removeUser(String userId) {
+
+ //OVERLOADED CODE
+ if (overloadedUsers.containsKey(userId.toLowerCase())) {
+ overloadedUsers.remove(userId.toLowerCase());
+ return true;
+ }
+ //END CODE
+ if (getUsers().containsKey(userId.toLowerCase())) {
+ getUsers().remove(userId.toLowerCase());
+ setUsersChanged(true);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean removeGroup(String groupName) {
+
+ if (groupName.equals(getDefaultGroup())) {
+ return false;
+ }
+ synchronized(getGroups()) {
+ for (String key : getGroups().keySet()) {
+ if (groupName.equalsIgnoreCase(key)) {
+ getGroups().remove(key);
+ synchronized(getUsers()) {
+ for (String userKey : getUsers().keySet()) {
+ User user = getUsers().get(userKey);
+ if (user.getGroupName().equalsIgnoreCase(key)) {
+ user.setGroup(getDefaultGroup());
+ }
+
+ }
+ }
+ //OVERLOADED CODE
+ synchronized(overloadedUsers) {
+ for (String userKey : overloadedUsers.keySet()) {
+ User user = overloadedUsers.get(userKey);
+ if (user.getGroupName().equalsIgnoreCase(key)) {
+ user.setGroup(getDefaultGroup());
+ }
+
+ }
+ }
+ //END OVERLOAD
+ setGroupsChanged(true);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ *
+ * @return Collection of all users
+ */
+ @Override
+ public Collection getUserList() {
+
+ Collection overloadedList = new ArrayList();
+ synchronized(getUsers()) {
+ Collection normalList = getUsers().values();
+ for (User u : normalList) {
+ if (overloadedUsers.containsKey(u.getUUID().toLowerCase())) {
+ overloadedList.add(overloadedUsers.get(u.getUUID().toLowerCase()));
+ } else {
+ overloadedList.add(u);
+ }
+ }
+ }
+ return overloadedList;
+ }
+
+ /**
+ *
+ * @param userId
+ * @return true if user is overloaded.
+ */
+ public boolean isOverloaded(String userId) {
+
+ return overloadedUsers.containsKey(userId.toLowerCase());
+ }
+
+ /**
+ *
+ * @param userId
+ */
+ public void overloadUser(String userId) {
+
+ if (!isOverloaded(userId)) {
+ User theUser = getUser(userId);
+ theUser = theUser.clone();
+ if (overloadedUsers.containsKey(theUser.getUUID().toLowerCase())) {
+ overloadedUsers.remove(theUser.getUUID().toLowerCase());
+ }
+ overloadedUsers.put(theUser.getUUID().toLowerCase(), theUser);
+ }
+ }
+
+ /**
+ *
+ * @param userId
+ */
+ public void removeOverload(String userId) {
+
+ User theUser = getUser(userId);
+ overloadedUsers.remove(theUser.getUUID().toLowerCase());
+ }
+
+ /**
+ * Gets the user in normal state. Surpassing the overload state.
+ * It doesn't affect permissions. But it enables plugins change the
+ * actual user permissions even in overload mode.
+ *
+ * @param userId
+ * @return user object
+ */
+ public User surpassOverload(String userId) {
+
+ if (!isOverloaded(userId)) {
+ return getUser(userId);
+ }
+ if (getUsers().containsKey(userId.toLowerCase())) {
+ return getUsers().get(userId.toLowerCase());
+ }
+ User newUser = createUser(userId);
+ return newUser;
+ }
+}
\ No newline at end of file
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/dataholder/UsersDataHolder.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/dataholder/UsersDataHolder.java
new file mode 100644
index 000000000..2d1cdf011
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/dataholder/UsersDataHolder.java
@@ -0,0 +1,115 @@
+package org.anjocaido.groupmanager.dataholder;
+
+import org.anjocaido.groupmanager.data.User;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This container holds all Users loaded from the relevant usersFile.
+ *
+ * @author ElgarL
+ */
+public class UsersDataHolder {
+
+ private WorldDataHolder dataSource;
+ private File usersFile;
+ private boolean haveUsersChanged = false;
+ private long timeStampUsers = 0;
+
+ /**
+ * The actual groups holder
+ */
+ private final Map users = Collections.synchronizedMap(new HashMap());
+
+ /**
+ * Constructor
+ */
+ protected UsersDataHolder() {
+
+ }
+
+ public void setDataSource(WorldDataHolder dataSource) {
+
+ this.dataSource = dataSource;
+ //push this data source to the users, so they pull the correct groups data.
+ synchronized (users) {
+ for (User user : users.values()) {
+ user.setDataSource(this.dataSource);
+ }
+ }
+ }
+
+ /**
+ * Note: Iteration over this object has to be synchronized!
+ *
+ * @return the users
+ */
+ public Map getUsers() {
+
+ return users;
+ }
+
+ public WorldDataHolder getDataSource() {
+
+ return this.dataSource;
+ }
+
+ /**
+ * Resets the Users
+ */
+ public void resetUsers() {
+ this.users.clear();
+ }
+
+ /**
+ * @return the usersFile
+ */
+ public File getUsersFile() {
+
+ return usersFile;
+ }
+
+ /**
+ * @param usersFile the usersFile to set
+ */
+ public void setUsersFile(File usersFile) {
+
+ this.usersFile = usersFile;
+ }
+
+ /**
+ * @return the haveUsersChanged
+ */
+ public boolean HaveUsersChanged() {
+
+ return haveUsersChanged;
+ }
+
+ /**
+ * @param haveUsersChanged the haveUsersChanged to set
+ */
+ public void setUsersChanged(boolean haveUsersChanged) {
+
+ this.haveUsersChanged = haveUsersChanged;
+ }
+
+ /**
+ * @return the timeStampUsers
+ */
+ public long getTimeStampUsers() {
+
+ return timeStampUsers;
+ }
+
+ /**
+ * @param timeStampUsers the timeStampUsers to set
+ */
+ public void setTimeStampUsers(long timeStampUsers) {
+
+ this.timeStampUsers = timeStampUsers;
+ }
+
+}
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/dataholder/WorldDataHolder.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/dataholder/WorldDataHolder.java
new file mode 100644
index 000000000..8589a32d2
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/dataholder/WorldDataHolder.java
@@ -0,0 +1,1558 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.anjocaido.groupmanager.dataholder;
+
+import org.anjocaido.groupmanager.GroupManager;
+import org.anjocaido.groupmanager.data.Group;
+import org.anjocaido.groupmanager.data.User;
+import org.anjocaido.groupmanager.events.GMGroupEvent;
+import org.anjocaido.groupmanager.events.GMSystemEvent;
+import org.anjocaido.groupmanager.events.GMUserEvent;
+import org.anjocaido.groupmanager.events.GMUserEvent.Action;
+import org.anjocaido.groupmanager.permissions.AnjoPermissionsHandler;
+import org.bukkit.Server;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.PluginManager;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
+import org.yaml.snakeyaml.reader.UnicodeReader;
+
+import java.io.*;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * One instance of this should exist per world/mirror it contains all functions
+ * to manage these data sets and points to the relevant users and groups
+ * objects.
+ *
+ * @author gabrielcouto, ElgarL
+ */
+public class WorldDataHolder {
+
+ /**
+ * World name
+ */
+ protected String name;
+ /**
+ * The actual groups holder
+ */
+ protected GroupsDataHolder groups = new GroupsDataHolder();
+ /**
+ * The actual users holder
+ */
+ protected UsersDataHolder users = new UsersDataHolder();
+
+ /**
+ * List of UUID's associated with this user name.
+ */
+ protected static Map> nameToUUIDLookup = new TreeMap>();
+ /**
+ *
+ */
+ protected AnjoPermissionsHandler permissionsHandler;
+
+ /**
+ * Prevent direct instantiation
+ *
+ * @param worldName
+ */
+ public WorldDataHolder(String worldName) {
+
+ name = worldName;
+ }
+
+ /**
+ * The main constructor for a new WorldDataHolder
+ *
+ * @param worldName
+ * @param groups
+ * @param users
+ */
+ public WorldDataHolder(String worldName, GroupsDataHolder groups, UsersDataHolder users) {
+
+ this.name = worldName;
+ this.groups = groups;
+ this.users = users;
+
+ // this.defaultGroup = defaultGroup;
+ }
+
+ /**
+ * update the dataSource to point to this object.
+ *
+ * This should be called whenever a set of world data is fetched.
+ */
+ public void updateDataSource() {
+
+ this.groups.setDataSource(this);
+ this.users.setDataSource(this);
+ }
+
+ /**
+ * Search for a user. If it doesn't exist, create a new one with default group.
+ *
+ * If this is called passing a player name with mantogglevalidate off
+ * it can return the wrong user object (offline/online UUID match).
+ *
+ * @param userId the UUID String or name of the user
+ *
+ * @return class that manage that user permission
+ */
+ public User getUser(String userId) {
+
+ if (getUsers().containsKey(userId.toLowerCase())) {
+ return getUsers().get(userId.toLowerCase());
+ }
+
+ // Legacy name matching
+ if ((userId.length() < 36) && nameToUUIDLookup.containsKey(userId.toLowerCase())) {
+
+ // Search for a name to UUID match
+ for (String uid : getUUIDLookup(userId.toLowerCase())) {
+
+ User user = getUsers().get(uid.toLowerCase());
+
+ if ((user != null) && user.getLastName().equalsIgnoreCase(userId)) {
+ return user;
+ }
+ }
+
+ }
+
+ if (!nameToUUIDLookup.containsKey(userId.toLowerCase())) {
+ GroupManager.logger.fine("ERROR: No lookup for: " + userId);
+ }
+
+ // No user account found so create a new one.
+ User newUser = createUser(userId);
+ return newUser;
+ }
+
+ /**
+ * *** Internal GM use only ***
+ * This is called when a player joins to update/add their UUID.
+ *
+ * @param uUID the player objects UUID.
+ * @param currentName the name they have just logged in with.
+ *
+ * @return the user object for this player.
+ */
+ public User getUser(String uUID, String currentName) {
+
+ // Check for a UUID account
+ User user = getUsers().get(uUID.toLowerCase());
+
+ if (user != null) {
+
+ GroupManager.logger.fine("User record found for UUID: " + uUID + ":" + currentName);
+ user.setLastName(currentName);
+ return user;
+
+ }
+
+ // Search for a LastName match
+ user = getUsers().get(currentName.toLowerCase());
+
+ if ((user != null) && user.getLastName().equalsIgnoreCase(currentName) && user.getUUID().equalsIgnoreCase(user.getLastName())) {
+
+ // Clone this user so we can set it's uUID
+ User usr = user.clone(uUID, currentName);
+
+ // Delete it and replace with the new clone.
+ this.removeUser(user.getUUID().toLowerCase());
+ this.addUser(usr);
+
+ GroupManager.logger.fine("Updating User record for UUID: " + uUID + ":" + currentName);
+
+ return getUsers().get(uUID.toLowerCase());
+ }
+
+ if (user != null) {
+ GroupManager.logger.fine("User record found but UUID mismatch for: " + currentName);
+ }
+
+ // No user account found so create a new one.
+ User newUser = createUser(uUID.toLowerCase());
+ newUser.setLastName(currentName);
+
+ GroupManager.logger.fine("New User record created: " + uUID + ":" + currentName);
+
+ return newUser;
+ }
+
+ /**
+ * Add a user to the list. If it already exists, overwrite the old.
+ *
+ * @param theUser the user you want to add to the permission list
+ */
+ public void addUser(User theUser) {
+
+ if (theUser.getDataSource() != this) {
+ theUser = theUser.clone(this);
+ }
+ if (theUser == null) {
+ return;
+ }
+ if ((theUser.getGroup() == null)) {
+ theUser.setGroup(groups.getDefaultGroup());
+ }
+ removeUser(theUser.getUUID().toLowerCase());
+ getUsers().put(theUser.getUUID().toLowerCase(), theUser);
+
+ // Store for name to UUID lookups.
+ //putUUIDLookup(theUser.getLastName(), theUser.getUUID().toLowerCase());
+
+ setUsersChanged(true);
+ if (GroupManager.isLoaded()) {
+ GroupManager.getGMEventHandler().callEvent(theUser, Action.USER_ADDED);
+ }
+ }
+
+ /**
+ * Removes the user from the list. (he might become a default user)
+ *
+ * @param userId the UUID or username for the user to remove
+ *
+ * @return true if it had something to remove
+ */
+ public boolean removeUser(String userId) {
+
+ if (getUsers().containsKey(userId.toLowerCase())) {
+
+ User user = getUser(userId.toLowerCase());
+
+ // Remove the name to UUID lookup for this user object.
+ removeUUIDLookup(user.getLastName().toLowerCase(), user.getUUID());
+
+ getUsers().remove(userId.toLowerCase());
+
+ setUsersChanged(true);
+
+ if (GroupManager.isLoaded()) {
+ GroupManager.getGMEventHandler().callEvent(userId, GMUserEvent.Action.USER_REMOVED);
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @param userId
+ *
+ * @return true if we have data for this player.
+ */
+ public boolean isUserDeclared(String userId) {
+
+ return getUsers().containsKey(userId.toLowerCase());
+ }
+
+ /**
+ * Change the default group of the file.
+ *
+ * @param group the group you want make default.
+ */
+ public void setDefaultGroup(Group group) {
+
+ if (!getGroups().containsKey(group.getName().toLowerCase()) || (group.getDataSource() != this)) {
+ addGroup(group);
+ }
+ groups.setDefaultGroup(getGroup(group.getName()));
+ setGroupsChanged(true);
+ if (GroupManager.isLoaded()) {
+ GroupManager.getGMEventHandler().callEvent(GMSystemEvent.Action.DEFAULT_GROUP_CHANGED);
+ }
+ }
+
+ /**
+ * Returns the default group of the file
+ *
+ * @return the default group
+ */
+ public Group getDefaultGroup() {
+
+ return groups.getDefaultGroup();
+ }
+
+ /**
+ * Returns a group of the given name
+ *
+ * @param groupName the name of the group
+ *
+ * @return a group if it is found. null if not found.
+ */
+ public Group getGroup(String groupName) {
+
+ if (groupName.toLowerCase().startsWith("g:")) {
+ return GroupManager.getGlobalGroups().getGroup(groupName);
+ } else {
+ return getGroups().get(groupName.toLowerCase());
+ }
+ }
+
+ /**
+ * Check if a group exists. Its the same of getGroup, but check if it is
+ * null.
+ *
+ * @param groupName the name of the group
+ *
+ * @return true if exists. false if not.
+ */
+ public boolean groupExists(String groupName) {
+
+ if (groupName.toLowerCase().startsWith("g:")) {
+ return GroupManager.getGlobalGroups().hasGroup(groupName);
+ } else {
+ return getGroups().containsKey(groupName.toLowerCase());
+ }
+ }
+
+ /**
+ * Add a group to the list
+ *
+ * @param groupToAdd
+ */
+ public void addGroup(Group groupToAdd) {
+
+ if (groupToAdd.getName().toLowerCase().startsWith("g:")) {
+ GroupManager.getGlobalGroups().addGroup(groupToAdd);
+ GroupManager.getGMEventHandler().callEvent(groupToAdd, GMGroupEvent.Action.GROUP_ADDED);
+ return;
+ }
+
+ if (groupToAdd.getDataSource() != this) {
+ groupToAdd = groupToAdd.clone(this);
+ }
+ removeGroup(groupToAdd.getName());
+ getGroups().put(groupToAdd.getName().toLowerCase(), groupToAdd);
+ setGroupsChanged(true);
+ if (GroupManager.isLoaded()) {
+ GroupManager.getGMEventHandler().callEvent(groupToAdd, GMGroupEvent.Action.GROUP_ADDED);
+ }
+ }
+
+ /**
+ * Remove the group from the list
+ *
+ * @param groupName
+ *
+ * @return true if had something to remove. false the group was default or
+ * non-existant
+ */
+ public boolean removeGroup(String groupName) {
+
+ if (groupName.toLowerCase().startsWith("g:")) {
+ return GroupManager.getGlobalGroups().removeGroup(groupName);
+ }
+
+ if (getDefaultGroup() != null && groupName.equalsIgnoreCase(getDefaultGroup().getName())) {
+ return false;
+ }
+ if (getGroups().containsKey(groupName.toLowerCase())) {
+ getGroups().remove(groupName.toLowerCase());
+ setGroupsChanged(true);
+ if (GroupManager.isLoaded()) {
+ GroupManager.getGMEventHandler().callEvent(groupName.toLowerCase(), GMGroupEvent.Action.GROUP_REMOVED);
+ }
+ return true;
+ }
+ return false;
+
+ }
+
+ /**
+ * Creates a new User with the given name and adds it to this holder.
+ *
+ * @param userId the UUID or username you want
+ *
+ * @return null if user already exists. or new User
+ */
+ public User createUser(String userId) {
+
+ if (getUsers().containsKey(userId.toLowerCase())) {
+ return null;
+ }
+ User newUser = new User(this, userId);
+ newUser.setGroup(groups.getDefaultGroup(), false);
+ addUser(newUser);
+ setUsersChanged(true);
+ return newUser;
+ }
+
+ /**
+ * Creates a new Group with the given name and adds it to this holder
+ *
+ * @param groupName the groupname you want
+ *
+ * @return null if group already exists. or new Group
+ */
+ public Group createGroup(String groupName) {
+
+ if (groupName.toLowerCase().startsWith("g:")) {
+ Group newGroup = new Group(groupName);
+ return GroupManager.getGlobalGroups().newGroup(newGroup);
+ }
+
+ if (getGroups().containsKey(groupName.toLowerCase())) {
+ return null;
+ }
+
+ Group newGroup = new Group(this, groupName);
+ addGroup(newGroup);
+ setGroupsChanged(true);
+ return newGroup;
+ }
+
+ /**
+ * @return a collection of the groups
+ */
+ public Collection getGroupList() {
+
+ synchronized (getGroups()) {
+ return new ArrayList(getGroups().values());
+ }
+ }
+
+ /**
+ * @return a collection of the users
+ */
+ public Collection getUserList() {
+
+ synchronized (getUsers()) {
+ return new ArrayList(getUsers().values());
+ }
+ }
+
+ /**
+ * reads the file again
+ */
+ public void reload() {
+
+ try {
+ reloadGroups();
+ reloadUsers();
+ } catch (Exception ex) {
+ Logger.getLogger(WorldDataHolder.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+
+ /**
+ * Refresh Group data from file
+ */
+ public void reloadGroups() {
+
+ GroupManager.setLoaded(false);
+ try {
+ // temporary holder in case the load fails.
+ WorldDataHolder ph = new WorldDataHolder(this.getName());
+
+ loadGroups(ph, getGroupsFile());
+ // transfer new data
+ resetGroups();
+ for (Group tempGroup : ph.getGroupList()) {
+ tempGroup.clone(this);
+ }
+ this.setDefaultGroup(getGroup(ph.getDefaultGroup().getName()));
+ this.removeGroupsChangedFlag();
+ this.setTimeStampGroups(getGroupsFile().lastModified());
+
+ ph = null;
+ } catch (Exception ex) {
+ Logger.getLogger(WorldDataHolder.class.getName()).log(Level.WARNING, null, ex);
+ }
+ GroupManager.setLoaded(true);
+ GroupManager.getGMEventHandler().callEvent(GMSystemEvent.Action.RELOADED);
+ }
+
+ /**
+ * Refresh Users data from file
+ */
+ public void reloadUsers() {
+
+ GroupManager.setLoaded(false);
+ try {
+ // temporary holder in case the load fails.
+ WorldDataHolder ph = new WorldDataHolder(this.getName());
+ // copy groups for reference
+ for (Group tempGroup : this.getGroupList()) {
+ tempGroup.clone(ph);
+ }
+ // setup the default group before loading user data.
+ ph.setDefaultGroup(ph.getGroup(getDefaultGroup().getName()));
+ loadUsers(ph, getUsersFile());
+ // transfer new data
+ resetUsers();
+ for (User tempUser : ph.getUserList()) {
+ tempUser.clone(this);
+ }
+ this.removeUsersChangedFlag();
+ this.setTimeStampUsers(getUsersFile().lastModified());
+
+ ph = null;
+ } catch (Exception ex) {
+ Logger.getLogger(WorldDataHolder.class.getName()).log(Level.WARNING, null, ex);
+ }
+ GroupManager.setLoaded(true);
+ GroupManager.getGMEventHandler().callEvent(GMSystemEvent.Action.RELOADED);
+ }
+
+ public void loadGroups(File groupsFile) {
+
+ GroupManager.setLoaded(false);
+ try {
+ setGroupsFile(groupsFile);
+ loadGroups(this, groupsFile);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ throw new IllegalArgumentException("The file which should contain groups does not exist!\n" + groupsFile.getPath());
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new IllegalArgumentException("Error accessing the groups file!\n" + groupsFile.getPath());
+ }
+
+ GroupManager.setLoaded(true);
+ }
+
+ public void loadUsers(File usersFile) {
+
+ GroupManager.setLoaded(false);
+ try {
+ setUsersFile(usersFile);
+ loadUsers(this, usersFile);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ throw new IllegalArgumentException("The file which should contain users does not exist!\n" + usersFile.getPath());
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new IllegalArgumentException("Error accessing the users file!\n" + usersFile.getPath());
+ }
+
+ GroupManager.setLoaded(true);
+ }
+
+ /**
+ * Returns a NEW data holder containing data read from the files
+ *
+ * @param worldName
+ * @param groupsFile
+ * @param usersFile
+ *
+ * @throws FileNotFoundException
+ * @throws IOException
+ */
+ public static WorldDataHolder load(String worldName, File groupsFile, File usersFile) throws FileNotFoundException, IOException {
+
+ WorldDataHolder ph = new WorldDataHolder(worldName);
+
+ GroupManager.setLoaded(false);
+ if (groupsFile != null) {
+ loadGroups(ph, groupsFile);
+ }
+ if (usersFile != null) {
+ loadUsers(ph, usersFile);
+ }
+ GroupManager.setLoaded(true);
+
+ return ph;
+ }
+
+ /**
+ * Updates the WorldDataHolder from the Groups file
+ *
+ * @param ph
+ * @param groupsFile
+ *
+ * @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 groupsRootDataNode;
+
+ if (!groupsFile.exists()) {
+ throw new IllegalArgumentException("The file which should contain groups does not exist!\n" + groupsFile.getPath());
+ }
+ FileInputStream groupsInputStream = new FileInputStream(groupsFile);
+ try {
+ groupsRootDataNode = (Map) yamlGroups.load(new UnicodeReader(groupsInputStream));
+ if (groupsRootDataNode == null) {
+ throw new NullPointerException();
+ }
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("The following file couldn't pass on Parser.\n" + groupsFile.getPath(), ex);
+ } finally {
+ groupsInputStream.close();
+ }
+
+ // PROCESS GROUPS FILE
+
+ Map> inheritance = new HashMap>();
+ Map allGroupsNode = null;
+
+ /*
+ * Fetch all groups under the 'groups' entry.
+ */
+ try {
+ allGroupsNode = (Map) groupsRootDataNode.get("groups");
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Your " + groupsFile.getPath() + " file is invalid. See console for details.", ex);
+ }
+
+ if (allGroupsNode == null) {
+ throw new IllegalArgumentException("You have no groups in " + groupsFile.getPath() + ".");
+ }
+
+ Iterator groupItr = allGroupsNode.keySet().iterator();
+ String groupKey;
+ Integer groupCount = 0;
+
+ /*
+ * loop each group entry and process it's data.
+ */
+ while (groupItr.hasNext()) {
+
+ try {
+ groupCount++;
+ // Attempt to fetch the next group name.
+ groupKey = groupItr.next();
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Invalid group name for group entry (" + groupCount + ") in file: " + groupsFile.getPath(), ex);
+ }
+
+ /*
+ * Fetch this groups child nodes
+ */
+ Map thisGroupNode = null;
+
+ try {
+ thisGroupNode = (Map) allGroupsNode.get(groupKey);
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Invalid child nodes for group '" + groupKey + "' in file: " + groupsFile.getPath(), ex);
+ }
+
+ /*
+ * Create a new group with this name in the assigned data source.
+ */
+ Group thisGrp = ph.createGroup(groupKey);
+
+ if (thisGrp == null) {
+ throw new IllegalArgumentException("I think this Group was declared more than once: " + groupKey + " in file: " + groupsFile.getPath());
+ }
+
+ // DEFAULT NODE
+
+ Object nodeData = null;
+ try {
+ nodeData = thisGroupNode.get("default");
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Bad format found in 'permissions' for group: " + groupKey + " in file: " + groupsFile.getPath());
+ }
+
+ if (nodeData == null) {
+ /*
+ * If no 'default' node is found do nothing.
+ */
+ } else if ((Boolean.parseBoolean(nodeData.toString()))) {
+ /*
+ * Set this as the default group. Warn if some other group has
+ * already claimed that position.
+ */
+ if (ph.getDefaultGroup() != null) {
+ GroupManager.logger.warning("The group '" + thisGrp.getName() + "' is claiming to be default where '" + ph.getDefaultGroup().getName() + "' already was.");
+ GroupManager.logger.warning("Overriding first default request in file: " + groupsFile.getPath());
+ }
+ ph.setDefaultGroup(thisGrp);
+ }
+
+ // PERMISSIONS NODE
+
+ nodeData = null;
+ try {
+ nodeData = thisGroupNode.get("permissions");
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Bad format found in 'permissions' for '" + groupKey + "' in file: " + groupsFile.getPath());
+ }
+
+ if (nodeData == null) {
+ /*
+ * If no permissions node is found, or it's empty do nothing.
+ */
+ } else {
+ /*
+ * There is a permission list Which seems to hold some data
+ */
+ if (nodeData instanceof List) {
+ /*
+ * Check each entry and add it as a new permission.
+ */
+ try {
+ for (Object o : ((List) nodeData)) {
+ try {
+ /*
+ * Only add this permission if it's not empty.
+ */
+ if (!o.toString().isEmpty()) {
+ thisGrp.addPermission(o.toString());
+ }
+
+ } catch (NullPointerException ex) {
+ // Ignore this entry as it's null. It can be
+ // safely dropped
+ }
+ }
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Invalid formatting found in 'permissions' section for group: " + thisGrp.getName() + " in file: " + groupsFile.getPath(), ex);
+ }
+
+ } else if (nodeData instanceof String) {
+ /*
+ * Only add this permission if it's not empty.
+ */
+ if (!nodeData.toString().isEmpty()) {
+ thisGrp.addPermission((String) nodeData);
+ }
+
+ } else {
+ throw new IllegalArgumentException("Unknown type of 'permissions' node(Should be String or List) for group: " + thisGrp.getName() + " in file: " + groupsFile.getPath());
+ }
+ /*
+ * Sort all permissions so they are in the correct order for
+ * checking.
+ */
+ thisGrp.sortPermissions();
+ }
+
+ // INFO NODE
+
+ nodeData = null;
+ try {
+ nodeData = thisGroupNode.get("info");
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Bad format found in 'info' section for group: " + groupKey + " in file: " + groupsFile.getPath());
+ }
+
+ if (nodeData == null) {
+ /*
+ * No info section was found, so leave all variables as
+ * defaults.
+ */
+ GroupManager.logger.warning("The group '" + thisGrp.getName() + "' has no 'info' section!");
+ GroupManager.logger.warning("Using default values: " + groupsFile.getPath());
+
+ } else if (nodeData instanceof Map) {
+ try {
+ if (nodeData != null) {
+ thisGrp.setVariables((Map) nodeData);
+ }
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Invalid formatting found in 'info' section for group: " + thisGrp.getName() + " in file: " + groupsFile.getPath(), ex);
+ }
+
+ } else {
+ throw new IllegalArgumentException("Unknown entry found in 'info' section for group: " + thisGrp.getName() + " in file: " + groupsFile.getPath());
+ }
+
+ // INHERITANCE NODE
+
+ nodeData = null;
+ try {
+ nodeData = thisGroupNode.get("inheritance");
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Bad format found in 'inheritance' section for group: " + groupKey + " in file: " + groupsFile.getPath());
+ }
+
+ if (nodeData == null || nodeData instanceof List) {
+ if (nodeData == null) {
+ /*
+ * If no inheritance node is found, or it's empty do
+ * nothing.
+ */
+ } else if (nodeData instanceof List) {
+
+ try {
+ for (String grp : (List) nodeData) {
+ if (inheritance.get(groupKey) == null) {
+ inheritance.put(groupKey, new ArrayList());
+ }
+ inheritance.get(groupKey).add(grp);
+ }
+
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Invalid formatting found in 'inheritance' section for group: " + thisGrp.getName() + " in file: " + groupsFile.getPath(), ex);
+ }
+
+ }
+ } else {
+ throw new IllegalArgumentException("Unknown entry found in 'inheritance' section for group: " + thisGrp.getName() + " in file: " + groupsFile.getPath());
+ }
+
+ // END GROUP
+
+ }
+
+ if (ph.getDefaultGroup() == null) {
+ throw new IllegalArgumentException("There was no Default Group declared in file: " + groupsFile.getPath());
+ }
+
+ /*
+ * Build the inheritance map and recored any errors
+ */
+ for (String group : inheritance.keySet()) {
+ List inheritedList = inheritance.get(group);
+ Group thisGroup = ph.getGroup(group);
+ if (thisGroup != null) {
+ for (String inheritedKey : inheritedList) {
+ if (inheritedKey != null) {
+ Group inheritedGroup = ph.getGroup(inheritedKey);
+ if (inheritedGroup != null) {
+ thisGroup.addInherits(inheritedGroup);
+ } else {
+ GroupManager.logger.warning("Inherited group '" + inheritedKey + "' not found for group " + thisGroup.getName() + ". Ignoring entry in file: " + groupsFile.getPath());
+ }
+ }
+ }
+ }
+ }
+
+ ph.removeGroupsChangedFlag();
+ // Update the LastModified time.
+ ph.setGroupsFile(groupsFile);
+ ph.setTimeStampGroups(groupsFile.lastModified());
+
+ // return ph;
+ }
+
+ /**
+ * Updates the WorldDataHolder from the Users file
+ *
+ * @param ph
+ * @param usersFile
+ *
+ * @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 usersRootDataNode;
+ if (!usersFile.exists()) {
+ throw new IllegalArgumentException("The file which should contain users does not exist!\n" + usersFile.getPath());
+ }
+ FileInputStream usersInputStream = new FileInputStream(usersFile);
+ try {
+ usersRootDataNode = (Map) yamlUsers.load(new UnicodeReader(usersInputStream));
+ if (usersRootDataNode == null) {
+ throw new NullPointerException();
+ }
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("The following file couldn't pass on Parser.\n" + usersFile.getPath(), ex);
+ } finally {
+ usersInputStream.close();
+ }
+
+ // PROCESS USERS FILE
+
+ Map allUsersNode = null;
+
+ /*
+ * Fetch all child nodes under the 'users' entry.
+ */
+ try {
+ allUsersNode = (Map) usersRootDataNode.get("users");
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Your " + usersFile.getPath() + " file is invalid. See console for details.", ex);
+ }
+
+ // Load users if the file is NOT empty
+
+ if (allUsersNode != null) {
+
+ Iterator usersItr = allUsersNode.keySet().iterator();
+ String usersKey;
+ Object node;
+ Integer userCount = 0;
+
+ while (usersItr.hasNext()) {
+ try {
+ userCount++;
+ // Attempt to fetch the next user name.
+ node = usersItr.next();
+ if (node instanceof Integer) {
+ usersKey = Integer.toString((Integer) node);
+ } else {
+ usersKey = node.toString();
+ }
+
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Invalid node type for user entry (" + userCount + ") in file: " + usersFile.getPath(), ex);
+ }
+
+ Map thisUserNode = null;
+ try {
+ thisUserNode = (Map) allUsersNode.get(node);
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Bad format found for user: " + usersKey + " in file: " + usersFile.getPath());
+ }
+
+ User thisUser = ph.createUser(usersKey);
+ if (thisUser == null) {
+ throw new IllegalArgumentException("I think this user was declared more than once: " + usersKey + " in file: " + usersFile.getPath());
+ }
+
+ // LASTNAME NODES
+
+ Object nodeData = null;
+ try {
+
+ nodeData = thisUserNode.get("lastname");
+
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Bad format found in 'lastname' for user: " + usersKey + " in file: " + usersFile.getPath());
+ }
+
+ if ((nodeData != null) && (nodeData instanceof String)) {
+
+ thisUser.setLastName((String) nodeData);
+
+ }
+
+ // USER PERMISSIONS NODES
+
+ nodeData = null;
+ try {
+ nodeData = thisUserNode.get("permissions");
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Bad format found in 'permissions' for user: " + usersKey + " in file: " + usersFile.getPath());
+ }
+
+ if (nodeData == null) {
+ /*
+ * If no permissions node is found, or it's empty do
+ * nothing.
+ */
+ } else {
+ try {
+ if (nodeData instanceof List) {
+ for (Object o : ((List) nodeData)) {
+ /*
+ * Only add this permission if it's not empty
+ */
+ if (!o.toString().isEmpty()) {
+ thisUser.addPermission(o.toString());
+ }
+ }
+ } else if (nodeData instanceof String) {
+
+ /*
+ * Only add this permission if it's not empty
+ */
+ if (!nodeData.toString().isEmpty()) {
+ thisUser.addPermission(nodeData.toString());
+ }
+
+ }
+ } catch (NullPointerException e) {
+ // Ignore this entry as it's null.
+ }
+ thisUser.sortPermissions();
+ }
+
+ // USER INFO NODE
+
+ nodeData = null;
+ try {
+ nodeData = thisUserNode.get("info");
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Bad format found in 'info' section for user: " + usersKey + " in file: " + usersFile.getPath());
+ }
+
+ if (nodeData == null) {
+ /*
+ * If no info node is found, or it's empty do nothing.
+ */
+ } else if (nodeData instanceof Map) {
+ thisUser.setVariables((Map) nodeData);
+
+ } else {
+ throw new IllegalArgumentException("Unknown entry found in 'info' section for user: " + thisUser.getLastName() + " in file: " + usersFile.getPath());
+ }
+
+ // END INFO NODE
+
+ // PRIMARY GROUP
+
+ nodeData = null;
+ try {
+ nodeData = thisUserNode.get("group");
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Bad format found in 'group' section for user: " + usersKey + " in file: " + usersFile.getPath());
+ }
+
+ if (nodeData != null) {
+ Group hisGroup = ph.getGroup(nodeData.toString());
+ if (hisGroup == null) {
+ GroupManager.logger.warning("There is no group " + thisUserNode.get("group").toString() + ", as stated for player " + thisUser.getLastName() + ": Set to '" + ph.getDefaultGroup().getName() + "' for file: " + usersFile.getPath());
+ hisGroup = ph.getDefaultGroup();
+ }
+ thisUser.setGroup(hisGroup);
+ } else {
+ thisUser.setGroup(ph.getDefaultGroup());
+ }
+
+ // SUBGROUPS NODES
+
+ nodeData = null;
+ try {
+ nodeData = thisUserNode.get("subgroups");
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Bad format found in 'subgroups' for user: " + usersKey + " in file: " + usersFile.getPath());
+ }
+
+ if (nodeData == null) {
+ /*
+ * If no subgroups node is found, or it's empty do nothing.
+ */
+ } else if (nodeData instanceof List) {
+ for (Object o : ((List) nodeData)) {
+ if (o == null) {
+ GroupManager.logger.warning("Invalid Subgroup data for user: " + thisUser.getLastName() + ". Ignoring entry in file: " + usersFile.getPath());
+ } else {
+ Group subGrp = ph.getGroup(o.toString());
+ if (subGrp != null) {
+ thisUser.addSubGroup(subGrp);
+ } else {
+ GroupManager.logger.warning("Subgroup '" + o.toString() + "' not found for user: " + thisUser.getLastName() + ". Ignoring entry in file: " + usersFile.getPath());
+ }
+ }
+ }
+ } else if (nodeData instanceof String) {
+ Group subGrp = ph.getGroup(nodeData.toString());
+ if (subGrp != null) {
+ thisUser.addSubGroup(subGrp);
+ } else {
+ GroupManager.logger.warning("Subgroup '" + nodeData.toString() + "' not found for user: " + thisUser.getLastName() + ". Ignoring entry in file: " + usersFile.getPath());
+ }
+ }
+ }
+ }
+
+ ph.removeUsersChangedFlag();
+ // Update the LastModified time.
+ ph.setUsersFile(usersFile);
+ ph.setTimeStampUsers(usersFile.lastModified());
+ }
+
+ /**
+ * Write a dataHolder in a specified file
+ *
+ * @param ph
+ * @param groupsFile
+ */
+ public static void writeGroups(WorldDataHolder ph, File groupsFile) {
+
+ Map root = new HashMap();
+
+ Map groupsMap = new HashMap();
+
+ root.put("groups", groupsMap);
+ synchronized (ph.getGroups()) {
+ for (String groupKey : ph.getGroups().keySet()) {
+ Group group = ph.getGroups().get(groupKey);
+
+ Map aGroupMap = new HashMap();
+ groupsMap.put(group.getName(), aGroupMap);
+
+ if (ph.getDefaultGroup() == null) {
+ GroupManager.logger.severe("There is no default group for world: " + ph.getName());
+ }
+ aGroupMap.put("default", group.equals(ph.getDefaultGroup()));
+
+ Map infoMap = new HashMap();
+ aGroupMap.put("info", infoMap);
+
+ for (String infoKey : group.getVariables().getVarKeyList()) {
+ infoMap.put(infoKey, group.getVariables().getVarObject(infoKey));
+ }
+
+ aGroupMap.put("inheritance", group.getInherits());
+
+ aGroupMap.put("permissions", group.getPermissionList());
+ }
+ }
+
+ if (!root.isEmpty()) {
+ DumperOptions opt = new DumperOptions();
+ opt.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+ final Yaml yaml = new Yaml(opt);
+ try {
+ OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(groupsFile), "UTF-8");
+
+ String newLine = System.getProperty("line.separator");
+
+ out.write("# Group inheritance" + newLine);
+ out.write("#" + newLine);
+ out.write("# Any inherited groups prefixed with a g: are global groups" + newLine);
+ out.write("# and are inherited from the GlobalGroups.yml." + newLine);
+ out.write("#" + newLine);
+ out.write("# Groups without the g: prefix are groups local to this world" + newLine);
+ out.write("# and are defined in the this groups.yml file." + newLine);
+ out.write("#" + newLine);
+ out.write("# Local group inheritances define your promotion tree when using 'manpromote/mandemote'" + newLine);
+ out.write(newLine);
+
+ yaml.dump(root, out);
+ out.close();
+ } catch (UnsupportedEncodingException ex) {
+ } catch (FileNotFoundException ex) {
+ } catch (IOException e) {
+ }
+ }
+
+ // Update the LastModified time.
+ ph.setGroupsFile(groupsFile);
+ ph.setTimeStampGroups(groupsFile.lastModified());
+ ph.removeGroupsChangedFlag();
+
+ if (GroupManager.isLoaded()) {
+ GroupManager.getGMEventHandler().callEvent(GMSystemEvent.Action.SAVED);
+ }
+
+ /*
+ * FileWriter tx = null; try { tx = new FileWriter(groupsFile, false);
+ * tx.write(yaml.dump(root)); tx.flush(); } catch (Exception e) { }
+ * finally { try { tx.close(); } catch (IOException ex) { } }
+ */
+ }
+
+ /**
+ * Write a dataHolder in a specified file
+ *
+ * @param ph
+ * @param usersFile
+ */
+ public static void writeUsers(WorldDataHolder ph, File usersFile) {
+
+ Map root = new HashMap();
+ LinkedHashMap usersMap = new LinkedHashMap();
+
+ root.put("users", usersMap);
+ synchronized (ph.getUsers()) {
+
+ // A sorted list of users.
+ for (String userKey : new TreeSet(ph.getUsers().keySet())) {
+ User user = ph.getUsers().get(userKey);
+ if ((user.getGroup() == null || user.getGroup().equals(ph.getDefaultGroup())) && user.getPermissionList().isEmpty() && user.getVariables().isEmpty() && user.isSubGroupsEmpty()) {
+ continue;
+ }
+
+ LinkedHashMap aUserMap = new LinkedHashMap();
+ usersMap.put(user.getUUID(), aUserMap);
+
+ if (!user.getUUID().equalsIgnoreCase(user.getLastName())) {
+ aUserMap.put("lastname", user.getLastName());
+ }
+
+ // GROUP NODE
+ if (user.getGroup() == null) {
+ aUserMap.put("group", ph.getDefaultGroup().getName());
+ } else {
+ aUserMap.put("group", user.getGroup().getName());
+ }
+
+ // SUBGROUPS NODE
+ aUserMap.put("subgroups", user.subGroupListStringCopy());
+
+ // PERMISSIONS NODE
+ aUserMap.put("permissions", user.getPermissionList());
+
+ // USER INFO NODE - BETA
+ if (user.getVariables().getSize() > 0) {
+ Map infoMap = new HashMap();
+ aUserMap.put("info", infoMap);
+ for (String infoKey : user.getVariables().getVarKeyList()) {
+ infoMap.put(infoKey, user.getVariables().getVarObject(infoKey));
+ }
+ }
+ // END USER INFO NODE - BETA
+
+ }
+ }
+
+ if (!root.isEmpty()) {
+ DumperOptions opt = new DumperOptions();
+ opt.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+ final Yaml yaml = new Yaml(opt);
+ try {
+ OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(usersFile), "UTF-8");
+ yaml.dump(root, out);
+ out.close();
+ } catch (UnsupportedEncodingException ex) {
+ } catch (FileNotFoundException ex) {
+ } catch (IOException e) {
+ }
+ }
+
+ // Update the LastModified time.
+ ph.setUsersFile(usersFile);
+ ph.setTimeStampUsers(usersFile.lastModified());
+ ph.removeUsersChangedFlag();
+
+ if (GroupManager.isLoaded()) {
+ GroupManager.getGMEventHandler().callEvent(GMSystemEvent.Action.SAVED);
+ }
+
+ /*
+ * FileWriter tx = null; try { tx = new FileWriter(usersFile, false);
+ * tx.write(yaml.dump(root)); tx.flush(); } catch (Exception e) { }
+ * finally { try { tx.close(); } catch (IOException ex) { } }
+ */
+ }
+
+ /**
+ * Don't use this. Unless you want to make this plugin to interact with
+ * original Nijikokun Permissions This method is supposed to make the
+ * original one reload the file, and propagate the changes made here.
+ *
+ * Prefer to use the AnjoCaido's fake version of Nijikokun's Permission
+ * plugin. The AnjoCaido's Permission can propagate the changes made on this
+ * plugin instantly, without need to save the file.
+ *
+ * @param server the server that holds the plugin
+ *
+ * @deprecated it is not used anymore... unless if you use original
+ * Permissions
+ */
+ @Deprecated
+ public static void reloadOldPlugins(Server server) {
+
+ // Only reload permissions
+ PluginManager pm = server.getPluginManager();
+ Plugin[] plugins = pm.getPlugins();
+ for (int i = 0; i < plugins.length; i++) {
+ // plugins[i].getConfiguration().load();
+ try {
+ plugins[i].getClass().getMethod("setupPermissions").invoke(plugins[i]);
+ } catch (Exception ex) {
+ continue;
+ }
+ }
+ }
+
+ /**
+ * @return the permissionsHandler
+ */
+ public AnjoPermissionsHandler getPermissionsHandler() {
+
+ if (permissionsHandler == null) {
+ permissionsHandler = new AnjoPermissionsHandler(this);
+ }
+ return permissionsHandler;
+ }
+
+ /**
+ * @param haveUsersChanged the haveUsersChanged to set
+ */
+ public void setUsersChanged(boolean haveUsersChanged) {
+
+ users.setUsersChanged(haveUsersChanged);
+ }
+
+ /**
+ * @return true if any user data has changed
+ */
+ public boolean haveUsersChanged() {
+
+ if (users.HaveUsersChanged()) {
+ return true;
+ }
+ synchronized (users.getUsers()) {
+ for (User u : users.getUsers().values()) {
+ if (u.isChanged()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @param setGroupsChanged the haveGroupsChanged to set
+ */
+ public void setGroupsChanged(boolean setGroupsChanged) {
+
+ groups.setGroupsChanged(setGroupsChanged);
+ }
+
+ /**
+ * @return true if any group data has changed.
+ */
+ public boolean haveGroupsChanged() {
+
+ if (groups.HaveGroupsChanged()) {
+ return true;
+ }
+ synchronized (groups.getGroups()) {
+ for (Group g : groups.getGroups().values()) {
+ if (g.isChanged()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ *
+ */
+ public void removeUsersChangedFlag() {
+
+ setUsersChanged(false);
+ synchronized (getUsers()) {
+ for (User u : getUsers().values()) {
+ u.flagAsSaved();
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ public void removeGroupsChangedFlag() {
+
+ setGroupsChanged(false);
+ synchronized (getGroups()) {
+ for (Group g : getGroups().values()) {
+ g.flagAsSaved();
+ }
+ }
+ }
+
+ /**
+ * @return the usersFile
+ */
+ public File getUsersFile() {
+
+ return users.getUsersFile();
+ }
+
+ /**
+ * @param file the usersFile to set
+ */
+ public void setUsersFile(File file) {
+
+ users.setUsersFile(file);
+ }
+
+ /**
+ * @return the groupsFile
+ */
+ public File getGroupsFile() {
+
+ return groups.getGroupsFile();
+ }
+
+ /**
+ * @param file the groupsFile to set
+ */
+ public void setGroupsFile(File file) {
+
+ groups.setGroupsFile(file);
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName() {
+
+ return name;
+ }
+
+ /**
+ * Resets Groups.
+ */
+ public void resetGroups() {
+
+ // setDefaultGroup(null);
+ groups.resetGroups();
+ }
+
+ /**
+ * Resets Users
+ */
+ public void resetUsers() {
+
+ users.resetUsers();
+ this.clearUUIDLookup();
+ }
+
+ /**
+ * Note: Iteration over this object has to be synchronized!
+ *
+ * @return the groups
+ */
+ public Map getGroups() {
+
+ return groups.getGroups();
+ }
+
+ /**
+ * Note: Iteration over this object has to be synchronized!
+ *
+ * @return the users
+ */
+ public Map getUsers() {
+
+ return users.getUsers();
+ }
+
+ /**
+ * @return the groups
+ */
+ public GroupsDataHolder getGroupsObject() {
+
+ return groups;
+ }
+
+ /**
+ * @param groupsDataHolder the GroupsDataHolder to set
+ */
+ public void setGroupsObject(GroupsDataHolder groupsDataHolder) {
+
+ groups = groupsDataHolder;
+ }
+
+ /**
+ * @return the users
+ */
+ public UsersDataHolder getUsersObject() {
+
+ return users;
+ }
+
+ /**
+ * @param usersDataHolder the UsersDataHolder to set
+ */
+ public void setUsersObject(UsersDataHolder usersDataHolder) {
+
+ users = usersDataHolder;
+ }
+
+ /**
+ * @return the timeStampGroups
+ */
+ public long getTimeStampGroups() {
+
+ return groups.getTimeStampGroups();
+ }
+
+ /**
+ * @return the timeStampUsers
+ */
+ public long getTimeStampUsers() {
+
+ return users.getTimeStampUsers();
+ }
+
+ /**
+ * @param timeStampGroups the timeStampGroups to set
+ */
+ protected void setTimeStampGroups(long timeStampGroups) {
+
+ groups.setTimeStampGroups(timeStampGroups);
+ }
+
+ /**
+ * @param timeStampUsers the timeStampUsers to set
+ */
+ protected void setTimeStampUsers(long timeStampUsers) {
+
+ users.setTimeStampUsers(timeStampUsers);
+ }
+
+ public void setTimeStamps() {
+
+ if (getGroupsFile() != null) {
+ setTimeStampGroups(getGroupsFile().lastModified());
+ }
+ if (getUsersFile() != null) {
+ setTimeStampUsers(getUsersFile().lastModified());
+ }
+ }
+
+ /** Name to UUID lookups **/
+
+ /**
+ * Add a new name to UUID lookup.
+ *
+ * @param name the User name key to index on.
+ * @param UUID the User object UUID (same as name if there is no UUID).
+ */
+ public void putUUIDLookup(String name, String UUID) {
+
+ Set lookup = getUUIDLookup(name.toLowerCase());
+
+ if (lookup == null) {
+ lookup = new TreeSet();
+ }
+
+ lookup.add(UUID);
+
+ nameToUUIDLookup.put(name.toLowerCase(), lookup);
+ }
+
+ /**
+ * Delete a name lookup.
+ * Allows for multiple UUID's assigned to a single name (offline/online)
+ *
+ * @param name
+ * @param UUID
+ */
+ public void removeUUIDLookup(String name, String UUID) {
+
+ if (nameToUUIDLookup.containsKey(name.toLowerCase())) {
+
+ Set lookup = getUUIDLookup(name.toLowerCase());
+
+ lookup.remove(UUID);
+
+ if (lookup.isEmpty()) {
+ nameToUUIDLookup.remove(name.toLowerCase());
+ return;
+ }
+
+ nameToUUIDLookup.put(name.toLowerCase(), lookup);
+
+ }
+
+ }
+
+ /**
+ * @param name
+ *
+ * @return a Set of strings containing the User objects UUID (or name if they don't have a UUID)
+ */
+ public Set getUUIDLookup(String name) {
+
+ return nameToUUIDLookup.get(name.toLowerCase());
+ }
+
+ /**
+ * Reset the UUID Lookup cache
+ */
+ protected void clearUUIDLookup() {
+
+ nameToUUIDLookup.clear();
+ }
+
+}
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java
new file mode 100644
index 000000000..ca2be61c9
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java
@@ -0,0 +1,815 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.anjocaido.groupmanager.dataholder.worlds;
+
+import org.anjocaido.groupmanager.GroupManager;
+import org.anjocaido.groupmanager.dataholder.OverloadedWorldHolder;
+import org.anjocaido.groupmanager.dataholder.WorldDataHolder;
+import org.anjocaido.groupmanager.permissions.AnjoPermissionsHandler;
+import org.anjocaido.groupmanager.utils.Tasks;
+import org.bukkit.World;
+import org.bukkit.entity.Player;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author gabrielcouto
+ */
+public class WorldsHolder {
+
+ /**
+ * Map with instances of loaded worlds.
+ */
+ private Map worldsData = new HashMap();
+
+ /**
+ * Map of mirrors:
+ * The key is the mirror.
+ * The object is the mirrored.
+ *
+ * Mirror shows the same data of mirrored.
+ */
+ private Map mirrorsGroup = new HashMap();
+ private Map mirrorsUser = new HashMap();
+
+ private String serverDefaultWorldName;
+ private GroupManager plugin;
+ private File worldsFolder;
+
+ /**
+ * @param plugin
+ */
+ public WorldsHolder(GroupManager plugin) {
+
+ this.plugin = plugin;
+ resetWorldsHolder();
+ }
+
+ /**
+ * @return the mirrorsGroup
+ */
+ public Map getMirrorsGroup() {
+
+ return mirrorsGroup;
+ }
+
+
+ /**
+ * @return the mirrorsUser
+ */
+ public Map getMirrorsUser() {
+
+ return mirrorsUser;
+ }
+
+ public boolean isWorldKnown(String name) {
+
+ return worldsData.containsKey(name.toLowerCase());
+ }
+
+ public void resetWorldsHolder() {
+
+ worldsData = new HashMap();
+ mirrorsGroup = new HashMap();
+ mirrorsUser = new HashMap();
+
+ // Setup folders and check files exist for the primary world
+ verifyFirstRun();
+ initialLoad();
+ if (serverDefaultWorldName == null) {
+ throw new IllegalStateException("There is no default group! OMG!");
+ }
+ }
+
+ private void initialLoad() {
+
+ // load the initial world
+ initialWorldLoading();
+ // Configure and load any mirrors and additional worlds as defined in config.yml
+ mirrorSetUp();
+ // search the worlds folder for any manually created worlds (not listed in config.yml)
+ loadAllSearchedWorlds();
+ }
+
+ private void initialWorldLoading() {
+
+ //Load the default world
+ loadWorld(serverDefaultWorldName);
+ //defaultWorld = getUpdatedWorldData(serverDefaultWorldName);
+ }
+
+ private void loadAllSearchedWorlds() {
+
+ /*
+ * Read all known worlds from Bukkit Create the data files if they don't
+ * already exist, and they are not mirrored.
+ */
+ for (World world : plugin.getServer().getWorlds()) {
+ GroupManager.logger.log(Level.FINE, "Checking data for " + world.getName() + ".");
+ if ((!worldsData.containsKey(world.getName().toLowerCase())) && ((!mirrorsGroup.containsKey(world.getName().toLowerCase())) || (!mirrorsUser.containsKey(world.getName().toLowerCase())))) {
+
+ if (worldsData.containsKey("all_unnamed_worlds")) {
+
+ String usersMirror = mirrorsUser.get("all_unnamed_worlds");
+ String groupsMirror = mirrorsGroup.get("all_unnamed_worlds");
+
+ if (usersMirror != null) {
+ mirrorsUser.put(world.getName().toLowerCase(), usersMirror);
+ }
+
+ if (groupsMirror != null) {
+ mirrorsGroup.put(world.getName().toLowerCase(), groupsMirror);
+ }
+
+ }
+
+ GroupManager.logger.log(Level.FINE, "Creating folders for " + world.getName() + ".");
+ setupWorldFolder(world.getName());
+ }
+ }
+ /*
+ * Loop over all folders within the worlds folder and attempt to load
+ * the world data
+ */
+ for (File folder : worldsFolder.listFiles()) {
+ if (folder.isDirectory() && !folder.getName().startsWith(".")) {
+ GroupManager.logger.info("World Found: " + folder.getName());
+
+ /*
+ * don't load any worlds which are already loaded or fully
+ * mirrored worlds that don't need data.
+ */
+ if (!worldsData.containsKey(folder.getName().toLowerCase()) && ((!mirrorsGroup.containsKey(folder.getName().toLowerCase())) || (!mirrorsUser.containsKey(folder.getName().toLowerCase())))) {
+ /*
+ * Call setupWorldFolder to check case sensitivity and
+ * convert to lower case, before we attempt to load this
+ * world.
+ */
+ setupWorldFolder(folder.getName());
+ loadWorld(folder.getName().toLowerCase());
+ }
+
+ }
+ }
+ }
+
+ @SuppressWarnings("rawtypes")
+ public void mirrorSetUp() {
+
+ mirrorsGroup.clear();
+ mirrorsUser.clear();
+ Map mirrorsMap = plugin.getGMConfig().getMirrorsMap();
+
+ HashSet mirroredWorlds = new HashSet();
+
+ if (mirrorsMap != null) {
+ for (String source : mirrorsMap.keySet()) {
+ // Make sure all non mirrored worlds have a set of data files.
+ setupWorldFolder(source);
+ // Load the world data
+ if (!worldsData.containsKey(source.toLowerCase())) {
+ loadWorld(source);
+ }
+
+ if (mirrorsMap.get(source) instanceof ArrayList) {
+ ArrayList mirrorList = (ArrayList) mirrorsMap.get(source);
+
+ // These worlds fully mirror their parent
+ for (Object o : mirrorList) {
+ String world = o.toString().toLowerCase();
+ if (!world.equalsIgnoreCase(serverDefaultWorldName)) {
+ try {
+ mirrorsGroup.remove(world);
+ mirrorsUser.remove(world);
+ } catch (Exception e) {
+ }
+ mirrorsGroup.put(world, getWorldData(source).getName());
+ mirrorsUser.put(world, getWorldData(source).getName());
+
+ // Track this world so we can create a datasource for it later
+ mirroredWorlds.add(o.toString());
+
+ } else {
+ GroupManager.logger.log(Level.WARNING, "Mirroring error with " + o.toString() + ". Recursive loop detected!");
+ }
+ }
+ } else if (mirrorsMap.get(source) instanceof Map) {
+ Map subSection = (Map) mirrorsMap.get(source);
+
+ for (Object key : subSection.keySet()) {
+
+ if (!((String) key).equalsIgnoreCase(serverDefaultWorldName)) {
+
+ if (subSection.get(key) instanceof ArrayList) {
+ ArrayList mirrorList = (ArrayList) subSection.get(key);
+
+ // These worlds have defined mirroring
+ for (Object o : mirrorList) {
+ String type = o.toString().toLowerCase();
+ try {
+ if (type.equals("groups")) {
+ mirrorsGroup.remove(((String) key).toLowerCase());
+ }
+
+ if (type.equals("users")) {
+ mirrorsUser.remove(((String) key).toLowerCase());
+ }
+
+ } catch (Exception e) {
+ }
+ if (type.equals("groups")) {
+ mirrorsGroup.put(((String) key).toLowerCase(), getWorldData(source).getName());
+ GroupManager.logger.log(Level.FINE, "Adding groups mirror for " + key + ".");
+ }
+
+ if (type.equals("users")) {
+ mirrorsUser.put(((String) key).toLowerCase(), getWorldData(source).getName());
+ GroupManager.logger.log(Level.FINE, "Adding users mirror for " + key + ".");
+ }
+ }
+
+ // Track this world so we can create a datasource for it later
+ mirroredWorlds.add((String) key);
+
+ } else {
+ throw new IllegalStateException("Unknown mirroring format for " + (String) key);
+ }
+
+ } else {
+ GroupManager.logger.log(Level.WARNING, "Mirroring error with " + (String) key + ". Recursive loop detected!");
+ }
+
+ }
+ }
+ }
+
+ // Create a datasource for any worlds not already loaded
+ for (String world : mirroredWorlds) {
+ if (!worldsData.containsKey(world.toLowerCase())) {
+ GroupManager.logger.log(Level.FINE, "No data for " + world + ".");
+ setupWorldFolder(world);
+ loadWorld(world, true);
+ }
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ public void reloadAll() {
+
+ // Load global groups
+ GroupManager.getGlobalGroups().load();
+
+ ArrayList alreadyDone = new ArrayList();
+ for (WorldDataHolder w : worldsData.values()) {
+ if (alreadyDone.contains(w)) {
+ continue;
+ }
+ if (!mirrorsGroup.containsKey(w.getName().toLowerCase())) {
+ w.reloadGroups();
+ }
+ if (!mirrorsUser.containsKey(w.getName().toLowerCase())) {
+ w.reloadUsers();
+ }
+
+ alreadyDone.add(w);
+ }
+
+ }
+
+ /**
+ * @param worldName
+ */
+ public void reloadWorld(String worldName) {
+
+ if (!mirrorsGroup.containsKey(worldName.toLowerCase())) {
+ getWorldData(worldName).reloadGroups();
+ }
+ if (!mirrorsUser.containsKey(worldName.toLowerCase())) {
+ getWorldData(worldName).reloadUsers();
+ }
+ }
+
+ /**
+ * Wrapper to retain backwards compatibility
+ * (call this function to auto overwrite files)
+ */
+ public void saveChanges() {
+
+ saveChanges(true);
+ }
+
+ /**
+ *
+ */
+ public boolean saveChanges(boolean overwrite) {
+
+ boolean changed = false;
+ ArrayList alreadyDone = new ArrayList();
+ Tasks.removeOldFiles(plugin, plugin.getBackupFolder());
+
+ // Write Global Groups
+ if (GroupManager.getGlobalGroups().haveGroupsChanged()) {
+ 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();
+ }
+ }
+
+ for (OverloadedWorldHolder w : worldsData.values()) {
+ if (alreadyDone.contains(w)) {
+ continue;
+ }
+ if (w == null) {
+ GroupManager.logger.severe("WHAT HAPPENED?");
+ continue;
+ }
+ if (!mirrorsGroup.containsKey(w.getName().toLowerCase())) {
+ if (w.haveGroupsChanged()) {
+ if (overwrite || (!overwrite && (w.getTimeStampGroups() >= w.getGroupsFile().lastModified()))) {
+ // Backup Groups file
+ backupFile(w, true);
+
+ WorldDataHolder.writeGroups(w, w.getGroupsFile());
+ changed = true;
+ //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 Groups file
+ backupFile(w, true);
+ w.reloadGroups();
+ changed = true;
+ }
+ }
+ }
+ if (!mirrorsUser.containsKey(w.getName().toLowerCase())) {
+ if (w.haveUsersChanged()) {
+ if (overwrite || (!overwrite && (w.getTimeStampUsers() >= w.getUsersFile().lastModified()))) {
+ // Backup Users file
+ backupFile(w, false);
+
+ WorldDataHolder.writeUsers(w, w.getUsersFile());
+ changed = true;
+ //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();
+ changed = true;
+ }
+ }
+ }
+ alreadyDone.add(w);
+ }
+ return changed;
+ }
+
+ /**
+ * 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);
+ }
+ }
+
+ /**
+ * Returns the dataHolder for the given world.
+ * If the world is not on the worlds list, returns the default world
+ * holder.
+ *
+ * Mirrors return their parent world data.
+ * If no mirroring data it returns the default world.
+ *
+ * @param worldName
+ *
+ * @return OverloadedWorldHolder
+ */
+ public OverloadedWorldHolder getWorldData(String worldName) {
+
+ String worldNameLowered = worldName.toLowerCase();
+
+ // Find this worlds data
+ if (worldsData.containsKey(worldNameLowered)) {
+ return getUpdatedWorldData(worldNameLowered);
+ }
+
+ // Oddly no data source was found for this world so attempt to return the global mirror.
+ if (worldsData.containsKey("all_unnamed_worlds")) {
+ GroupManager.logger.finest("Requested world " + worldName + " not found or badly mirrored. Returning all_unnamed_worlds world...");
+ return getUpdatedWorldData("all_unnamed_worlds");
+ }
+
+ // Oddly no data source or global mirror was found for this world so return the default.
+ GroupManager.logger.finest("Requested world " + worldName + " not found or badly mirrored. Returning default world...");
+ return getDefaultWorld();
+ }
+
+ /**
+ * Get the requested world data and update it's dataSource to be relevant
+ * for this world
+ *
+ * @param worldName
+ *
+ * @return updated world holder
+ */
+ private OverloadedWorldHolder getUpdatedWorldData(String worldName) {
+
+ String worldNameLowered = worldName.toLowerCase();
+
+ if (worldsData.containsKey(worldNameLowered)) {
+ OverloadedWorldHolder data = worldsData.get(worldNameLowered);
+ data.updateDataSource();
+ return data;
+ }
+ return null;
+
+ }
+
+ /**
+ * Do a matching of playerName, if its found only one player, do
+ * getWorldData(player)
+ *
+ * @param playerName
+ *
+ * @return null if matching returned no player, or more than one.
+ */
+ public OverloadedWorldHolder getWorldDataByPlayerName(String playerName) {
+
+ List matchPlayer = plugin.getServer().matchPlayer(playerName);
+ if (matchPlayer.size() == 1) {
+ return getWorldData(matchPlayer.get(0));
+ }
+ return null;
+ }
+
+ /**
+ * Retrieves the field player.getWorld().getName() and do
+ * getWorld(worldName)
+ *
+ * @param player
+ *
+ * @return OverloadedWorldHolder
+ */
+ public OverloadedWorldHolder getWorldData(Player player) {
+
+ return getWorldData(player.getWorld().getName());
+ }
+
+ /**
+ * It does getWorld(worldName).getPermissionsHandler()
+ *
+ * @param worldName
+ *
+ * @return AnjoPermissionsHandler
+ */
+ public AnjoPermissionsHandler getWorldPermissions(String worldName) {
+
+ return getWorldData(worldName).getPermissionsHandler();
+ }
+
+ /**
+ * Returns the PermissionsHandler for this player data
+ *
+ * @param player
+ *
+ * @return AnjoPermissionsHandler
+ */
+ public AnjoPermissionsHandler getWorldPermissions(Player player) {
+
+ return getWorldData(player).getPermissionsHandler();
+ }
+
+ /**
+ * Id does getWorldDataByPlayerName(playerName).
+ * If it doesnt return null, it will return result.getPermissionsHandler()
+ *
+ * @param playerName
+ *
+ * @return null if the player matching gone wrong.
+ */
+ public AnjoPermissionsHandler getWorldPermissionsByPlayerName(String playerName) {
+
+ WorldDataHolder dh = getWorldDataByPlayerName(playerName);
+ if (dh != null) {
+ return dh.getPermissionsHandler();
+ }
+ return null;
+ }
+
+ private void verifyFirstRun() {
+
+ /* Do not use the folder name if this
+ * is a Bukkit Forge server.
+ */
+ if (plugin.getServer().getName().equalsIgnoreCase("BukkitForge")) {
+ serverDefaultWorldName = "overworld";
+
+ } else {
+ Properties server = new Properties();
+ try {
+ server.load(new FileInputStream(new File("server.properties")));
+ serverDefaultWorldName = server.getProperty("level-name").toLowerCase();
+ } catch (IOException ex) {
+ GroupManager.logger.log(Level.SEVERE, null, ex);
+ }
+ }
+ setupWorldFolder(serverDefaultWorldName);
+
+ }
+
+ public void setupWorldFolder(String worldName) {
+
+ String worldNameLowered = worldName.toLowerCase();
+
+ worldsFolder = new File(plugin.getDataFolder(), "worlds");
+ if (!worldsFolder.exists()) {
+ worldsFolder.mkdirs();
+ }
+
+ File defaultWorldFolder = new File(worldsFolder, worldNameLowered);
+ if ((!defaultWorldFolder.exists()) && ((!mirrorsGroup.containsKey(worldNameLowered))) || (!mirrorsUser.containsKey(worldNameLowered))) {
+
+ /*
+ * check and convert all old case sensitive folders to lower case
+ */
+ File casedWorldFolder = new File(worldsFolder, worldName);
+ if ((casedWorldFolder.exists()) && (casedWorldFolder.getName().toLowerCase().equals(worldNameLowered))) {
+ /*
+ * Rename the old folder to the new lower cased format
+ */
+ casedWorldFolder.renameTo(new File(worldsFolder, worldNameLowered));
+ } else {
+ /*
+ * Else we just create the folder
+ */
+ defaultWorldFolder.mkdirs();
+ }
+ }
+ if (defaultWorldFolder.exists()) {
+ if (!mirrorsGroup.containsKey(worldNameLowered)) {
+ File groupsFile = new File(defaultWorldFolder, "groups.yml");
+ if (!groupsFile.exists() || groupsFile.length() == 0) {
+
+ InputStream template = plugin.getResourceAsStream("groups.yml");
+ try {
+ Tasks.copy(template, groupsFile);
+ } catch (IOException ex) {
+ GroupManager.logger.log(Level.SEVERE, null, ex);
+ }
+ }
+ }
+
+ if (!mirrorsUser.containsKey(worldNameLowered)) {
+ File usersFile = new File(defaultWorldFolder, "users.yml");
+ if (!usersFile.exists() || usersFile.length() == 0) {
+
+ InputStream template = plugin.getResourceAsStream("users.yml");
+ try {
+ Tasks.copy(template, usersFile);
+ } catch (IOException ex) {
+ GroupManager.logger.log(Level.SEVERE, null, ex);
+ }
+
+ }
+ }
+ }
+ }
+
+ /**
+ * Copies the specified world data to another world
+ *
+ * @param fromWorld
+ * @param toWorld
+ *
+ * @return true if successfully copied.
+ */
+ public boolean cloneWorld(String fromWorld, String toWorld) {
+
+ File fromWorldFolder = new File(worldsFolder, fromWorld.toLowerCase());
+ File toWorldFolder = new File(worldsFolder, toWorld.toLowerCase());
+ if (toWorldFolder.exists() || !fromWorldFolder.exists()) {
+ return false;
+ }
+ File fromWorldGroups = new File(fromWorldFolder, "groups.yml");
+ File fromWorldUsers = new File(fromWorldFolder, "users.yml");
+ if (!fromWorldGroups.exists() || !fromWorldUsers.exists()) {
+ return false;
+ }
+ File toWorldGroups = new File(toWorldFolder, "groups.yml");
+ File toWorldUsers = new File(toWorldFolder, "users.yml");
+ toWorldFolder.mkdirs();
+ try {
+ Tasks.copy(fromWorldGroups, toWorldGroups);
+ Tasks.copy(fromWorldUsers, toWorldUsers);
+ } catch (IOException ex) {
+ Logger.getLogger(WorldsHolder.class.getName()).log(Level.SEVERE, null, ex);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Wrapper for LoadWorld(String,Boolean) for backwards compatibility
+ *
+ * Load a world from file.
+ * If it already been loaded, summon reload method from dataHolder.
+ *
+ * @param worldName
+ */
+ public void loadWorld(String worldName) {
+
+ loadWorld(worldName, false);
+ }
+
+ /**
+ * Load a world from file.
+ * If it already been loaded, summon reload method from dataHolder.
+ *
+ * @param worldName
+ */
+ public void loadWorld(String worldName, Boolean isMirror) {
+
+ String worldNameLowered = worldName.toLowerCase();
+
+ if (worldsData.containsKey(worldNameLowered)) {
+ worldsData.get(worldNameLowered).reload();
+ return;
+ }
+ GroupManager.logger.finest("Trying to load world " + worldName + "...");
+ File thisWorldFolder = new File(worldsFolder, worldNameLowered);
+ if ((isMirror) || (thisWorldFolder.exists() && thisWorldFolder.isDirectory())) {
+
+ // Setup file handles, if not mirrored
+ File groupsFile = (mirrorsGroup.containsKey(worldNameLowered)) ? null : new File(thisWorldFolder, "groups.yml");
+ File usersFile = (mirrorsUser.containsKey(worldNameLowered)) ? null : new File(thisWorldFolder, "users.yml");
+
+ if ((groupsFile != null) && (!groupsFile.exists())) {
+ throw new IllegalArgumentException("Groups file for world '" + worldName + "' doesnt exist: " + groupsFile.getPath());
+ }
+ if ((usersFile != null) && (!usersFile.exists())) {
+ throw new IllegalArgumentException("Users file for world '" + worldName + "' doesnt exist: " + usersFile.getPath());
+ }
+
+ WorldDataHolder tempHolder = new WorldDataHolder(worldNameLowered);
+
+ // Map the group object for any mirror
+ if (mirrorsGroup.containsKey(worldNameLowered)) {
+ tempHolder.setGroupsObject(this.getWorldData(mirrorsGroup.get(worldNameLowered)).getGroupsObject());
+ } else {
+ tempHolder.loadGroups(groupsFile);
+ }
+
+ // Map the user object for any mirror
+ if (mirrorsUser.containsKey(worldNameLowered)) {
+ tempHolder.setUsersObject(this.getWorldData(mirrorsUser.get(worldNameLowered)).getUsersObject());
+ } else {
+ tempHolder.loadUsers(usersFile);
+ }
+
+ OverloadedWorldHolder thisWorldData = new OverloadedWorldHolder(tempHolder);
+
+ // null the object so we don't keep file handles open where we shouldn't
+ tempHolder = null;
+
+ // 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(worldNameLowered, thisWorldData);
+ return;
+ }
+
+ //GroupManager.logger.severe("Failed to load world " + worldName + "...");
+ }
+ }
+
+ /**
+ * Tells if the such world has been mapped.
+ *
+ * It will return true if world is a mirror.
+ *
+ * @param worldName
+ *
+ * @return true if world is loaded or mirrored. false if not listed
+ */
+ public boolean isInList(String worldName) {
+
+ if (worldsData.containsKey(worldName.toLowerCase()) || mirrorsGroup.containsKey(worldName.toLowerCase()) || mirrorsUser.containsKey(worldName.toLowerCase())) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Verify if world has it's own file permissions.
+ *
+ * @param worldName
+ *
+ * @return true if it has its own holder. false if not.
+ */
+ public boolean hasOwnData(String worldName) {
+
+ if (worldsData.containsKey(worldName.toLowerCase()) && (!mirrorsGroup.containsKey(worldName.toLowerCase()) || !mirrorsUser.containsKey(worldName.toLowerCase()))) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @return the defaultWorld
+ */
+ public OverloadedWorldHolder getDefaultWorld() {
+
+ return getUpdatedWorldData(serverDefaultWorldName);
+ }
+
+ /**
+ * Returns all physically loaded worlds which have at least one of their own
+ * data sets for users or groups which isn't an identical mirror.
+ *
+ * @return ArrayList of all loaded worlds
+ */
+ public ArrayList allWorldsDataList() {
+
+ ArrayList list = new ArrayList();
+
+ for (String world : worldsData.keySet()) {
+
+ if (!world.equalsIgnoreCase("all_unnamed_worlds")) {
+
+ // Fetch the relevant world object
+ OverloadedWorldHolder data = getWorldData(world);
+
+ if (!list.contains(data)) {
+
+ String worldNameLowered = data.getName().toLowerCase();
+ String usersMirror = mirrorsUser.get(worldNameLowered);
+ String groupsMirror = mirrorsGroup.get(worldNameLowered);
+
+ // is users mirrored?
+ if (usersMirror != null) {
+
+ // If both are mirrored
+ if (groupsMirror != null) {
+
+ // if the data sources are the same, return the parent
+ if (usersMirror == groupsMirror) {
+ data = getWorldData(usersMirror.toLowerCase());
+
+ // Only add the parent if it's not already listed.
+ if (!list.contains(data)) {
+ list.add(data);
+ }
+
+ continue;
+ }
+ // Both data sources are mirrors, but they are from different parents
+ // so fall through to add the actual data object.
+ }
+ // Groups isn't a mirror so fall through to add this this worlds data source
+ }
+
+ // users isn't mirrored so we need to add this worlds data source
+ list.add(data);
+ }
+ }
+ }
+ return list;
+ }
+}
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/events/GMGroupEvent.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/events/GMGroupEvent.java
new file mode 100644
index 000000000..9bd85fa37
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/events/GMGroupEvent.java
@@ -0,0 +1,88 @@
+package org.anjocaido.groupmanager.events;
+
+import org.anjocaido.groupmanager.GroupManager;
+import org.anjocaido.groupmanager.data.Group;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+
+/**
+ * @author ElgarL
+ */
+public class GMGroupEvent extends Event {
+
+ /**
+ *
+ */
+ private static final HandlerList handlers = new HandlerList();
+
+ @Override
+ public HandlerList getHandlers() {
+
+ return handlers;
+ }
+
+ public static HandlerList getHandlerList() {
+
+ return handlers;
+ }
+
+ //////////////////////////////
+
+ protected Group group;
+
+ protected String groupName;
+
+ protected Action action;
+
+ public GMGroupEvent(Group group, Action action) {
+
+ super();
+
+ this.group = group;
+ this.action = action;
+ this.groupName = group.getName();
+ }
+
+ public GMGroupEvent(String groupName, Action action) {
+
+ super();
+
+ this.groupName = groupName;
+ this.action = action;
+ }
+
+ public Action getAction() {
+
+ return this.action;
+ }
+
+ public Group getGroup() {
+
+ return group;
+ }
+
+ public String getGroupName() {
+
+ return groupName;
+ }
+
+ public enum Action {
+ GROUP_PERMISSIONS_CHANGED, GROUP_INHERITANCE_CHANGED, GROUP_INFO_CHANGED, GROUP_ADDED, GROUP_REMOVED,
+ }
+
+ public void schedule(final GMGroupEvent event) {
+
+ synchronized (GroupManager.getGMEventHandler().getServer()) {
+ if (GroupManager.getGMEventHandler().getServer().getScheduler().scheduleSyncDelayedTask(GroupManager.getGMEventHandler().getPlugin(), new Runnable() {
+
+ @Override
+ public void run() {
+
+ GroupManager.getGMEventHandler().getServer().getPluginManager().callEvent(event);
+ }
+ }, 1) == -1) {
+ GroupManager.logger.warning("Could not schedule GM Event.");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/events/GMSystemEvent.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/events/GMSystemEvent.java
new file mode 100644
index 000000000..0a30725f6
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/events/GMSystemEvent.java
@@ -0,0 +1,63 @@
+package org.anjocaido.groupmanager.events;
+
+import org.anjocaido.groupmanager.GroupManager;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+
+/**
+ * @author ElgarL
+ */
+public class GMSystemEvent extends Event {
+
+ /**
+ *
+ */
+ private static final HandlerList handlers = new HandlerList();
+
+ @Override
+ public HandlerList getHandlers() {
+
+ return handlers;
+ }
+
+ public static HandlerList getHandlerList() {
+
+ return handlers;
+ }
+
+ //////////////////////////////
+
+ protected Action action;
+
+ public GMSystemEvent(Action action) {
+
+ super();
+
+ this.action = action;
+ }
+
+ public Action getAction() {
+
+ return this.action;
+ }
+
+ public enum Action {
+ RELOADED, SAVED, DEFAULT_GROUP_CHANGED, VALIDATE_TOGGLE,
+ }
+
+ public void schedule(final GMSystemEvent event) {
+
+ synchronized (GroupManager.getGMEventHandler().getServer()) {
+ if (GroupManager.getGMEventHandler().getServer().getScheduler().scheduleSyncDelayedTask(GroupManager.getGMEventHandler().getPlugin(), new Runnable() {
+
+ @Override
+ public void run() {
+
+ GroupManager.getGMEventHandler().getServer().getPluginManager().callEvent(event);
+ }
+ }, 1) == -1) {
+ GroupManager.logger.warning("Could not schedule GM Event.");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/events/GMUserEvent.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/events/GMUserEvent.java
new file mode 100644
index 000000000..d333e85d6
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/events/GMUserEvent.java
@@ -0,0 +1,88 @@
+package org.anjocaido.groupmanager.events;
+
+import org.anjocaido.groupmanager.GroupManager;
+import org.anjocaido.groupmanager.data.User;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+
+/**
+ * @author ElgarL
+ */
+public class GMUserEvent extends Event {
+
+ /**
+ *
+ */
+ private static final HandlerList handlers = new HandlerList();
+
+ @Override
+ public HandlerList getHandlers() {
+
+ return handlers;
+ }
+
+ public static HandlerList getHandlerList() {
+
+ return handlers;
+ }
+
+ //////////////////////////////
+
+ protected User user;
+
+ protected String userName;
+
+ protected Action action;
+
+ public GMUserEvent(User user, Action action) {
+
+ super();
+
+ this.user = user;
+ this.action = action;
+ this.userName = user.getLastName();
+ }
+
+ public GMUserEvent(String userName, Action action) {
+
+ super();
+
+ this.userName = userName;
+ this.action = action;
+ }
+
+ public Action getAction() {
+
+ return this.action;
+ }
+
+ public User getUser() {
+
+ return user;
+ }
+
+ public String getUserName() {
+
+ return userName;
+ }
+
+ public enum Action {
+ USER_PERMISSIONS_CHANGED, USER_INHERITANCE_CHANGED, USER_INFO_CHANGED, USER_GROUP_CHANGED, USER_SUBGROUP_CHANGED, USER_ADDED, USER_REMOVED,
+ }
+
+ public void schedule(final GMUserEvent event) {
+
+ synchronized (GroupManager.getGMEventHandler().getServer()) {
+ if (GroupManager.getGMEventHandler().getServer().getScheduler().scheduleSyncDelayedTask(GroupManager.getGMEventHandler().getPlugin(), new Runnable() {
+
+ @Override
+ public void run() {
+
+ GroupManager.getGMEventHandler().getServer().getPluginManager().callEvent(event);
+ }
+ }, 1) == -1) {
+ GroupManager.logger.warning("Could not schedule GM Event.");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/events/GMWorldListener.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/events/GMWorldListener.java
new file mode 100644
index 000000000..f60c24580
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/events/GMWorldListener.java
@@ -0,0 +1,64 @@
+package org.anjocaido.groupmanager.events;
+
+import org.anjocaido.groupmanager.GroupManager;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.world.WorldInitEvent;
+
+/**
+ * @author ElgarL
+ *
+ * Handle new world creation from other plugins
+ */
+public class GMWorldListener implements Listener {
+
+ private final GroupManager plugin;
+
+ public GMWorldListener(GroupManager instance) {
+
+ plugin = instance;
+ registerEvents();
+ }
+
+ private void registerEvents() {
+
+ plugin.getServer().getPluginManager().registerEvents(this, plugin);
+ }
+
+ @EventHandler(priority = EventPriority.LOWEST)
+ public void onWorldInit(WorldInitEvent event) {
+
+ String worldName = event.getWorld().getName();
+
+ if (GroupManager.isLoaded() && !plugin.getWorldsHolder().isInList(worldName)) {
+ GroupManager.logger.info("New world detected...");
+ GroupManager.logger.info("Creating data for: " + worldName);
+
+ if (plugin.getWorldsHolder().isWorldKnown("all_unnamed_worlds")) {
+
+ String usersMirror = plugin.getWorldsHolder().getMirrorsUser().get("all_unnamed_worlds");
+ String groupsMirror = plugin.getWorldsHolder().getMirrorsGroup().get("all_unnamed_worlds");
+
+ if (usersMirror != null) {
+ plugin.getWorldsHolder().getMirrorsUser().put(worldName.toLowerCase(), usersMirror);
+ }
+
+ if (groupsMirror != null) {
+ plugin.getWorldsHolder().getMirrorsGroup().put(worldName.toLowerCase(), groupsMirror);
+ }
+
+ }
+
+ plugin.getWorldsHolder().setupWorldFolder(worldName);
+ plugin.getWorldsHolder().loadWorld(worldName);
+
+
+ if (plugin.getWorldsHolder().isInList(worldName)) {
+ GroupManager.logger.info("Don't forget to configure/mirror this world in config.yml.");
+ } else {
+ GroupManager.logger.severe("Failed to configure this world.");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/events/GroupManagerEventHandler.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/events/GroupManagerEventHandler.java
new file mode 100644
index 000000000..f8363527c
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/events/GroupManagerEventHandler.java
@@ -0,0 +1,83 @@
+package org.anjocaido.groupmanager.events;
+
+import org.anjocaido.groupmanager.GroupManager;
+import org.anjocaido.groupmanager.data.Group;
+import org.anjocaido.groupmanager.data.User;
+import org.bukkit.Server;
+
+/**
+ * @author ElgarL
+ *
+ * Handles all Event generation.
+ */
+public class GroupManagerEventHandler {
+
+ private final Server server;
+ private final GroupManager plugin;
+
+
+ public GroupManagerEventHandler(GroupManager plugin) {
+
+ this.plugin = plugin;
+ this.server = plugin.getServer();
+
+ }
+
+ protected void callEvent(GMGroupEvent event) {
+
+ event.schedule(event);
+ }
+
+ protected void callEvent(GMUserEvent event) {
+
+ event.schedule(event);
+ }
+
+ protected void callEvent(GMSystemEvent event) {
+
+ event.schedule(event);
+ }
+
+ public void callEvent(Group group, GMGroupEvent.Action action) {
+
+ callEvent(new GMGroupEvent(group, action));
+ }
+
+ public void callEvent(String groupName, GMGroupEvent.Action action) {
+
+ callEvent(new GMGroupEvent(groupName, action));
+ }
+
+ public void callEvent(User user, GMUserEvent.Action action) {
+
+ callEvent(new GMUserEvent(user, action));
+ }
+
+ public void callEvent(String userName, GMUserEvent.Action action) {
+
+ callEvent(new GMUserEvent(userName, action));
+ }
+
+ public void callEvent(GMSystemEvent.Action action) {
+
+ callEvent(new GMSystemEvent(action));
+ }
+
+ /**
+ * @return the plugin
+ */
+ public GroupManager getPlugin() {
+
+ return plugin;
+ }
+
+ /**
+ * @return the server
+ */
+ public Server getServer() {
+
+ return server;
+ }
+
+
+}
\ No newline at end of file
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/metrics/Metrics.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/metrics/Metrics.java
new file mode 100644
index 000000000..6fcbd4a46
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/metrics/Metrics.java
@@ -0,0 +1,756 @@
+/*
+ * Copyright 2011-2013 Tyler Blair. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and contributors and should not be interpreted as representing official policies,
+ * either expressed or implied, of anybody else.
+ */
+package org.anjocaido.groupmanager.metrics;
+
+import org.bukkit.Bukkit;
+import org.bukkit.configuration.InvalidConfigurationException;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.PluginDescriptionFile;
+import org.bukkit.scheduler.BukkitTask;
+
+import java.io.*;
+import java.net.Proxy;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLEncoder;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.zip.GZIPOutputStream;
+
+public class Metrics {
+
+ /**
+ * The current revision number
+ */
+ private final static int REVISION = 7;
+
+ /**
+ * The base url of the metrics domain
+ */
+ private static final String BASE_URL = "http://report.mcstats.org";
+
+ /**
+ * The url used to report a server's status
+ */
+ private static final String REPORT_URL = "/plugin/%s";
+
+ /**
+ * Interval of time to ping (in minutes)
+ */
+ private static final int PING_INTERVAL = 15;
+
+ /**
+ * The plugin this metrics submits for
+ */
+ private final Plugin plugin;
+
+ /**
+ * All of the custom graphs to submit to metrics
+ */
+ private final Set graphs = Collections.synchronizedSet(new HashSet());
+
+ /**
+ * The plugin configuration file
+ */
+ private final YamlConfiguration configuration;
+
+ /**
+ * The plugin configuration file
+ */
+ private final File configurationFile;
+
+ /**
+ * Unique server id
+ */
+ private final String guid;
+
+ /**
+ * Debug mode
+ */
+ private final boolean debug;
+
+ /**
+ * Lock for synchronization
+ */
+ private final Object optOutLock = new Object();
+
+ /**
+ * The scheduled task
+ */
+ private volatile BukkitTask task = null;
+
+ public Metrics(final Plugin plugin) throws IOException {
+ if (plugin == null) {
+ throw new IllegalArgumentException("Plugin cannot be null");
+ }
+
+ this.plugin = plugin;
+
+ // load the config
+ configurationFile = getConfigFile();
+ configuration = YamlConfiguration.loadConfiguration(configurationFile);
+
+ // add some defaults
+ configuration.addDefault("opt-out", false);
+ configuration.addDefault("guid", UUID.randomUUID().toString());
+ configuration.addDefault("debug", false);
+
+ // Do we need to create the file?
+ if (configuration.get("guid", null) == null) {
+ configuration.options().header("http://mcstats.org").copyDefaults(true);
+ configuration.save(configurationFile);
+ }
+
+ // Load the guid then
+ guid = configuration.getString("guid");
+ debug = configuration.getBoolean("debug", false);
+ }
+
+ /**
+ * Construct and create a Graph that can be used to separate specific plotters to their own graphs on the metrics
+ * website. Plotters can be added to the graph object returned.
+ *
+ * @param name The name of the graph
+ *
+ * @return Graph object created. Will never return NULL under normal circumstances unless bad parameters are given
+ */
+ public Graph createGraph(final String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("Graph name cannot be null");
+ }
+
+ // Construct the graph object
+ final Graph graph = new Graph(name);
+
+ // Now we can add our graph
+ graphs.add(graph);
+
+ // and return back
+ return graph;
+ }
+
+ /**
+ * Add a Graph object to BukkitMetrics that represents data for the plugin that should be sent to the backend
+ *
+ * @param graph The name of the graph
+ */
+ public void addGraph(final Graph graph) {
+ if (graph == null) {
+ throw new IllegalArgumentException("Graph cannot be null");
+ }
+
+ graphs.add(graph);
+ }
+
+ /**
+ * Start measuring statistics. This will immediately create an async repeating task as the plugin and send the
+ * initial data to the metrics backend, and then after that it will post in increments of PING_INTERVAL * 1200
+ * ticks.
+ *
+ * @return True if statistics measuring is running, otherwise false.
+ */
+ public boolean start() {
+ synchronized (optOutLock) {
+ // Did we opt out?
+ if (isOptOut()) {
+ return false;
+ }
+
+ // Is metrics already running?
+ if (task != null) {
+ return true;
+ }
+
+ // Begin hitting the server with glorious data
+ task = plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, new Runnable() {
+
+ private boolean firstPost = true;
+
+ public void run() {
+ try {
+ // This has to be synchronized or it can collide with the disable method.
+ synchronized (optOutLock) {
+ // Disable Task, if it is running and the server owner decided to opt-out
+ if (isOptOut() && task != null) {
+ task.cancel();
+ task = null;
+ // Tell all plotters to stop gathering information.
+ for (Graph graph : graphs) {
+ graph.onOptOut();
+ }
+ }
+ }
+
+ // We use the inverse of firstPost because if it is the first time we are posting,
+ // it is not a interval ping, so it evaluates to FALSE
+ // Each time thereafter it will evaluate to TRUE, i.e PING!
+ postPlugin(!firstPost);
+
+ // After the first post we set firstPost to false
+ // Each post thereafter will be a ping
+ firstPost = false;
+ } catch (IOException e) {
+ if (debug) {
+ Bukkit.getLogger().log(Level.INFO, "[Metrics] " + e.getMessage());
+ }
+ }
+ }
+ }, 0, PING_INTERVAL * 1200);
+
+ return true;
+ }
+ }
+
+ /**
+ * Has the server owner denied plugin metrics?
+ *
+ * @return true if metrics should be opted out of it
+ */
+ public boolean isOptOut() {
+ synchronized (optOutLock) {
+ try {
+ // Reload the metrics file
+ configuration.load(getConfigFile());
+ } catch (IOException ex) {
+ if (debug) {
+ Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage());
+ }
+ return true;
+ } catch (InvalidConfigurationException ex) {
+ if (debug) {
+ Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage());
+ }
+ return true;
+ }
+ return configuration.getBoolean("opt-out", false);
+ }
+ }
+
+ /**
+ * Enables metrics for the server by setting "opt-out" to false in the config file and starting the metrics task.
+ *
+ * @throws java.io.IOException
+ */
+ public void enable() throws IOException {
+ // This has to be synchronized or it can collide with the check in the task.
+ synchronized (optOutLock) {
+ // Check if the server owner has already set opt-out, if not, set it.
+ if (isOptOut()) {
+ configuration.set("opt-out", false);
+ configuration.save(configurationFile);
+ }
+
+ // Enable Task, if it is not running
+ if (task == null) {
+ start();
+ }
+ }
+ }
+
+ /**
+ * Disables metrics for the server by setting "opt-out" to true in the config file and canceling the metrics task.
+ *
+ * @throws java.io.IOException
+ */
+ public void disable() throws IOException {
+ // This has to be synchronized or it can collide with the check in the task.
+ synchronized (optOutLock) {
+ // Check if the server owner has already set opt-out, if not, set it.
+ if (!isOptOut()) {
+ configuration.set("opt-out", true);
+ configuration.save(configurationFile);
+ }
+
+ // Disable Task, if it is running
+ if (task != null) {
+ task.cancel();
+ task = null;
+ }
+ }
+ }
+
+ /**
+ * Gets the File object of the config file that should be used to store data such as the GUID and opt-out status
+ *
+ * @return the File object for the config file
+ */
+ public File getConfigFile() {
+ // I believe the easiest way to get the base folder (e.g craftbukkit set via -P) for plugins to use
+ // is to abuse the plugin object we already have
+ // plugin.getDataFolder() => base/plugins/PluginA/
+ // pluginsFolder => base/plugins/
+ // The base is not necessarily relative to the startup directory.
+ File pluginsFolder = plugin.getDataFolder().getParentFile();
+
+ // return => base/plugins/PluginMetrics/config.yml
+ return new File(new File(pluginsFolder, "PluginMetrics"), "config.yml");
+ }
+
+ /**
+ * Generic method that posts a plugin to the metrics website
+ */
+ private void postPlugin(final boolean isPing) throws IOException {
+ // Server software specific section
+ PluginDescriptionFile description = plugin.getDescription();
+ String pluginName = description.getName();
+ boolean onlineMode = Bukkit.getServer().getOnlineMode(); // TRUE if online mode is enabled
+ String pluginVersion = description.getVersion();
+ String serverVersion = Bukkit.getVersion();
+ int playersOnline = Bukkit.getServer().getOnlinePlayers().size();
+
+ // END server software specific section -- all code below does not use any code outside of this class / Java
+
+ // Construct the post data
+ StringBuilder json = new StringBuilder(1024);
+ json.append('{');
+
+ // The plugin's description file containg all of the plugin data such as name, version, author, etc
+ appendJSONPair(json, "guid", guid);
+ appendJSONPair(json, "plugin_version", pluginVersion);
+ appendJSONPair(json, "server_version", serverVersion);
+ appendJSONPair(json, "players_online", Integer.toString(playersOnline));
+
+ // New data as of R6
+ String osname = System.getProperty("os.name");
+ String osarch = System.getProperty("os.arch");
+ String osversion = System.getProperty("os.version");
+ String java_version = System.getProperty("java.version");
+ int coreCount = Runtime.getRuntime().availableProcessors();
+
+ // normalize os arch .. amd64 -> x86_64
+ if (osarch.equals("amd64")) {
+ osarch = "x86_64";
+ }
+
+ appendJSONPair(json, "osname", osname);
+ appendJSONPair(json, "osarch", osarch);
+ appendJSONPair(json, "osversion", osversion);
+ appendJSONPair(json, "cores", Integer.toString(coreCount));
+ appendJSONPair(json, "auth_mode", onlineMode ? "1" : "0");
+ appendJSONPair(json, "java_version", java_version);
+
+ // If we're pinging, append it
+ if (isPing) {
+ appendJSONPair(json, "ping", "1");
+ }
+
+ if (graphs.size() > 0) {
+ synchronized (graphs) {
+ json.append(',');
+ json.append('"');
+ json.append("graphs");
+ json.append('"');
+ json.append(':');
+ json.append('{');
+
+ boolean firstGraph = true;
+
+ final Iterator iter = graphs.iterator();
+
+ while (iter.hasNext()) {
+ Graph graph = iter.next();
+
+ StringBuilder graphJson = new StringBuilder();
+ graphJson.append('{');
+
+ for (Plotter plotter : graph.getPlotters()) {
+ appendJSONPair(graphJson, plotter.getColumnName(), Integer.toString(plotter.getValue()));
+ }
+
+ graphJson.append('}');
+
+ if (!firstGraph) {
+ json.append(',');
+ }
+
+ json.append(escapeJSON(graph.getName()));
+ json.append(':');
+ json.append(graphJson);
+
+ firstGraph = false;
+ }
+
+ json.append('}');
+ }
+ }
+
+ // close json
+ json.append('}');
+
+ // Create the url
+ URL url = new URL(BASE_URL + String.format(REPORT_URL, urlEncode(pluginName)));
+
+ // Connect to the website
+ URLConnection connection;
+
+ // Mineshafter creates a socks proxy, so we can safely bypass it
+ // It does not reroute POST requests so we need to go around it
+ if (isMineshafterPresent()) {
+ connection = url.openConnection(Proxy.NO_PROXY);
+ } else {
+ connection = url.openConnection();
+ }
+
+
+ byte[] uncompressed = json.toString().getBytes();
+ byte[] compressed = gzip(json.toString());
+
+ // Headers
+ connection.addRequestProperty("User-Agent", "MCStats/" + REVISION);
+ connection.addRequestProperty("Content-Type", "application/json");
+ connection.addRequestProperty("Content-Encoding", "gzip");
+ connection.addRequestProperty("Content-Length", Integer.toString(compressed.length));
+ connection.addRequestProperty("Accept", "application/json");
+ connection.addRequestProperty("Connection", "close");
+
+ connection.setDoOutput(true);
+
+ if (debug) {
+ System.out.println("[Metrics] Prepared request for " + pluginName + " uncompressed=" + uncompressed.length + " compressed=" + compressed.length);
+ }
+
+ // Write the data
+ OutputStream os = connection.getOutputStream();
+ os.write(compressed);
+ os.flush();
+
+ // Now read the response
+ final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+ String response = reader.readLine();
+
+ // close resources
+ os.close();
+ reader.close();
+
+ if (response == null || response.startsWith("ERR") || response.startsWith("7")) {
+ if (response == null) {
+ response = "null";
+ } else if (response.startsWith("7")) {
+ response = response.substring(response.startsWith("7,") ? 2 : 1);
+ }
+
+ throw new IOException(response);
+ } else {
+ // Is this the first update this hour?
+ if (response.equals("1") || response.contains("This is your first update this hour")) {
+ synchronized (graphs) {
+ final Iterator iter = graphs.iterator();
+
+ while (iter.hasNext()) {
+ final Graph graph = iter.next();
+
+ for (Plotter plotter : graph.getPlotters()) {
+ plotter.reset();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * GZip compress a string of bytes
+ *
+ * @param input
+ *
+ * @return
+ */
+ public static byte[] gzip(String input) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ GZIPOutputStream gzos = null;
+
+ try {
+ gzos = new GZIPOutputStream(baos);
+ gzos.write(input.getBytes("UTF-8"));
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ if (gzos != null) {
+ try {
+ gzos.close();
+ } catch (IOException ignore) {
+ }
+ }
+ }
+
+ return baos.toByteArray();
+ }
+
+ /**
+ * Check if mineshafter is present. If it is, we need to bypass it to send POST requests
+ *
+ * @return true if mineshafter is installed on the server
+ */
+ private boolean isMineshafterPresent() {
+ try {
+ Class.forName("mineshafter.MineServer");
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ /**
+ * Appends a json encoded key/value pair to the given string builder.
+ *
+ * @param json
+ * @param key
+ * @param value
+ *
+ * @throws UnsupportedEncodingException
+ */
+ private static void appendJSONPair(StringBuilder json, String key, String value) throws UnsupportedEncodingException {
+ boolean isValueNumeric = false;
+
+ try {
+ if (value.equals("0") || !value.endsWith("0")) {
+ Double.parseDouble(value);
+ isValueNumeric = true;
+ }
+ } catch (NumberFormatException e) {
+ isValueNumeric = false;
+ }
+
+ if (json.charAt(json.length() - 1) != '{') {
+ json.append(',');
+ }
+
+ json.append(escapeJSON(key));
+ json.append(':');
+
+ if (isValueNumeric) {
+ json.append(value);
+ } else {
+ json.append(escapeJSON(value));
+ }
+ }
+
+ /**
+ * Escape a string to create a valid JSON string
+ *
+ * @param text
+ *
+ * @return
+ */
+ private static String escapeJSON(String text) {
+ StringBuilder builder = new StringBuilder();
+
+ builder.append('"');
+ for (int index = 0; index < text.length(); index++) {
+ char chr = text.charAt(index);
+
+ switch (chr) {
+ case '"':
+ case '\\':
+ builder.append('\\');
+ builder.append(chr);
+ break;
+ case '\b':
+ builder.append("\\b");
+ break;
+ case '\t':
+ builder.append("\\t");
+ break;
+ case '\n':
+ builder.append("\\n");
+ break;
+ case '\r':
+ builder.append("\\r");
+ break;
+ default:
+ if (chr < ' ') {
+ String t = "000" + Integer.toHexString(chr);
+ builder.append("\\u" + t.substring(t.length() - 4));
+ } else {
+ builder.append(chr);
+ }
+ break;
+ }
+ }
+ builder.append('"');
+
+ return builder.toString();
+ }
+
+ /**
+ * Encode text as UTF-8
+ *
+ * @param text the text to encode
+ *
+ * @return the encoded text, as UTF-8
+ */
+ private static String urlEncode(final String text) throws UnsupportedEncodingException {
+ return URLEncoder.encode(text, "UTF-8");
+ }
+
+ /**
+ * Represents a custom graph on the website
+ */
+ public static class Graph {
+
+ /**
+ * The graph's name, alphanumeric and spaces only :) If it does not comply to the above when submitted, it is
+ * rejected
+ */
+ private final String name;
+
+ /**
+ * The set of plotters that are contained within this graph
+ */
+ private final Set plotters = new LinkedHashSet();
+
+ private Graph(final String name) {
+ this.name = name;
+ }
+
+ /**
+ * Gets the graph's name
+ *
+ * @return the Graph's name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Add a plotter to the graph, which will be used to plot entries
+ *
+ * @param plotter the plotter to add to the graph
+ */
+ public void addPlotter(final Plotter plotter) {
+ plotters.add(plotter);
+ }
+
+ /**
+ * Remove a plotter from the graph
+ *
+ * @param plotter the plotter to remove from the graph
+ */
+ public void removePlotter(final Plotter plotter) {
+ plotters.remove(plotter);
+ }
+
+ /**
+ * Gets an unmodifiable set of the plotter objects in the graph
+ *
+ * @return an unmodifiable {@link java.util.Set} of the plotter objects
+ */
+ public Set getPlotters() {
+ return Collections.unmodifiableSet(plotters);
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ if (!(object instanceof Graph)) {
+ return false;
+ }
+
+ final Graph graph = (Graph) object;
+ return graph.name.equals(name);
+ }
+
+ /**
+ * Called when the server owner decides to opt-out of BukkitMetrics while the server is running.
+ */
+ protected void onOptOut() {
+ }
+ }
+
+ /**
+ * Interface used to collect custom data for a plugin
+ */
+ public static abstract class Plotter {
+
+ /**
+ * The plot's name
+ */
+ private final String name;
+
+ /**
+ * Construct a plotter with the default plot name
+ */
+ public Plotter() {
+ this("Default");
+ }
+
+ /**
+ * Construct a plotter with a specific plot name
+ *
+ * @param name the name of the plotter to use, which will show up on the website
+ */
+ public Plotter(final String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the current value for the plotted point. Since this function defers to an external function it may or
+ * may
+ * not return immediately thus cannot be guaranteed to be thread friendly or safe. This function can be called
+ * from any thread so care should be taken when accessing resources that need to be synchronized.
+ *
+ * @return the current value for the point to be plotted.
+ */
+ public abstract int getValue();
+
+ /**
+ * Get the column name for the plotted point
+ *
+ * @return the plotted point's column name
+ */
+ public String getColumnName() {
+ return name;
+ }
+
+ /**
+ * Called after the website graphs have been updated
+ */
+ public void reset() {
+ }
+
+ @Override
+ public int hashCode() {
+ return getColumnName().hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ if (!(object instanceof Plotter)) {
+ return false;
+ }
+
+ final Plotter plotter = (Plotter) object;
+ return plotter.name.equals(name) && plotter.getValue() == getValue();
+ }
+ }
+}
\ No newline at end of file
diff --git a/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/permissions/AnjoPermissionsHandler.java b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/permissions/AnjoPermissionsHandler.java
new file mode 100644
index 000000000..9e3530276
--- /dev/null
+++ b/EssentialsGroupManager/src/main/java/org/anjocaido/groupmanager/permissions/AnjoPermissionsHandler.java
@@ -0,0 +1,1403 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.anjocaido.groupmanager.permissions;
+
+import org.anjocaido.groupmanager.GroupManager;
+import org.anjocaido.groupmanager.data.Group;
+import org.anjocaido.groupmanager.data.User;
+import org.anjocaido.groupmanager.dataholder.WorldDataHolder;
+import org.anjocaido.groupmanager.utils.PermissionCheckResult;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+
+import java.util.*;
+
+/**
+ * Everything here maintains the model created by Nijikokun
+ *
+ * But implemented to use GroupManager system. Which provides instant changes,
+ * without file access.
+ *
+ * It holds permissions only for one single world.
+ *
+ * @author gabrielcouto, ElgarL
+ */
+public class AnjoPermissionsHandler extends PermissionsReaderInterface {
+
+ WorldDataHolder ph = null;
+
+ /**
+ * It needs a WorldDataHolder to work with.
+ *
+ * @param holder
+ */
+ public AnjoPermissionsHandler(WorldDataHolder holder) {
+
+ ph = holder;
+ }
+
+ /**
+ * A short name method, for permission method.
+ *
+ * @param player
+ * @param permission
+ *
+ * @return true if the player has the permission
+ */
+ @Override
+ public boolean has(Player player, String permission) {
+
+ return permission(player, permission);
+ }
+
+ /**
+ * Checks if a player can use that permission node.
+ *
+ * @param player
+ * @param permission
+ *
+ * @return true if the player has the permission
+ */
+ @Override
+ public boolean permission(Player player, String permission) {
+
+ return checkUserPermission(ph.getUser(player.getUniqueId().toString()).updatePlayer(player), permission);
+ }
+
+ /**
+ * Checks if a player can use that permission node.
+ *
+ * @param playerName
+ * @param permission
+ *
+ * @return true if the player has the permission
+ */
+ public boolean permission(String playerName, String permission) {
+
+ return checkUserPermission(ph.getUser(playerName), permission);
+ }
+
+ /**
+ * Returns the name of the group of that player name.
+ *
+ * @param userName
+ *
+ * @return String of players group name.
+ */
+ @Override
+ public String getGroup(String userName) {
+
+ return ph.getUser(userName).getGroup().getName();
+ }
+
+ /**
+ * Returns All permissions (including inheritance and sub groups) for the
+ * player, including child nodes from Bukkit.
+ *
+ * @param userName
+ *
+ * @return List of all players permissions.
+ */
+ @Override
+ public List getAllPlayersPermissions(String userName) {
+
+ List perms = new ArrayList();
+
+ perms.addAll(getAllPlayersPermissions(userName, true));
+
+ return perms;
+ }
+
+ /**
+ * Returns All permissions (including inheritance and sub groups) for the
+ * player. With or without Bukkit child nodes.
+ *
+ * @param userName
+ *
+ * @return Set of all players permissions.
+ */
+ @Override
+ public Set getAllPlayersPermissions(String userName, Boolean includeChildren) {
+
+ Set playerPermArray = new LinkedHashSet();
+ Set overrides = new LinkedHashSet();
+
+ // Add the players own permissions.
+ playerPermArray.addAll(populatePerms(ph.getUser(userName).getPermissionList(), includeChildren));
+
+ ArrayList alreadyProcessed = new ArrayList();
+
+ // fetch all group permissions
+ for (String group : getGroups(userName)) {
+ // Don't process a group more than once.
+ if (!alreadyProcessed.contains(group)) {
+ alreadyProcessed.add(group);
+
+ Set groupPermArray = new LinkedHashSet();
+
+ if (group.startsWith("g:") && GroupManager.getGlobalGroups().hasGroup(group)) {
+ // GlobalGroups
+ groupPermArray = populatePerms(GroupManager.getGlobalGroups().getGroupsPermissions(group), includeChildren);
+
+ } else {
+ // World Groups
+ groupPermArray = populatePerms(ph.getGroup(group).getPermissionList(), includeChildren);
+ }
+
+ // Add all group permissions, unless negated by earlier permissions.
+ for (String perm : groupPermArray) {
+ boolean negated = (perm.startsWith("-"));
+
+ // Overridden (Exception) permission defeats negation.
+ if (perm.startsWith("+")) {
+ overrides.add(perm.substring(1));
+ continue;
+ }
+
+ // Perm doesn't already exists and there is no negation for it
+ // or It's a negated perm where a normal perm doesn't exists (don't allow inheritance to negate higher perms)
+ if ((!negated && !playerPermArray.contains(perm) && !wildcardNegation(playerPermArray, perm)) || (negated && !playerPermArray.contains(perm.substring(1)) && !wildcardNegation(playerPermArray, perm.substring(1)))) {
+ playerPermArray.add(perm);
+ }
+
+ }
+ }
+
+ }
+
+ // Process overridden permissions
+
+ Iterator