/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package org.anjocaido.groupmanager.dataholder.worlds; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; 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; /** * * @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 final GroupManager plugin; private File worldsFolder; /** * * @param plugin */ public WorldsHolder(final GroupManager plugin) { this.plugin = plugin; resetWorldsHolder(); } /** * 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() { final ArrayList list = new ArrayList(); for (final String world : worldsData.keySet()) { if (!world.equalsIgnoreCase("all_unnamed_worlds")) { // Fetch the relevant world object OverloadedWorldHolder data = getWorldData(world); if (!list.contains(data)) { final String worldNameLowered = data.getName().toLowerCase(); final String usersMirror = mirrorsUser.get(worldNameLowered); final 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; } /** * Copies the specified world data to another world * * @param fromWorld * @param toWorld * @return true if successfully copied. */ public boolean cloneWorld(final String fromWorld, final String toWorld) { final File fromWorldFolder = new File(worldsFolder, fromWorld.toLowerCase()); final File toWorldFolder = new File(worldsFolder, toWorld.toLowerCase()); if (toWorldFolder.exists() || !fromWorldFolder.exists()) { return false; } final File fromWorldGroups = new File(fromWorldFolder, "groups.yml"); final File fromWorldUsers = new File(fromWorldFolder, "users.yml"); if (!fromWorldGroups.exists() || !fromWorldUsers.exists()) { return false; } final File toWorldGroups = new File(toWorldFolder, "groups.yml"); final File toWorldUsers = new File(toWorldFolder, "users.yml"); toWorldFolder.mkdirs(); try { Tasks.copy(fromWorldGroups, toWorldGroups); Tasks.copy(fromWorldUsers, toWorldUsers); } catch (final IOException ex) { Logger.getLogger(WorldsHolder.class.getName()).log(Level.SEVERE, null, ex); return false; } return true; } /** * @return the defaultWorld */ public OverloadedWorldHolder getDefaultWorld() { return getUpdatedWorldData(serverDefaultWorldName); } /** * @return the mirrorsGroup */ public Map getMirrorsGroup() { return mirrorsGroup; } /** * @return the mirrorsUser */ public Map getMirrorsUser() { return mirrorsUser; } /** * Retrieves the field player.getWorld().getName() and do * getWorld(worldName) * * @param player * @return OverloadedWorldHolder */ public OverloadedWorldHolder getWorldData(final Player player) { return getWorldData(player.getWorld().getName()); } /** * 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(final String worldName) { final 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("加载世界 " + worldName + " 时未找到或配置错误. 返回 all_unnamed_worlds 配置..."); return getUpdatedWorldData("all_unnamed_worlds"); } // Oddly no data source or global mirror was found for this world so return the default. GroupManager.logger.finest("加载世界 " + worldName + " 时未找到或配置错误. 返回默认世界..."); return getDefaultWorld(); } /** * 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(final String playerName) { final List matchPlayer = plugin.getServer().matchPlayer(playerName); if (matchPlayer.size() == 1) { return getWorldData(matchPlayer.get(0)); } return null; } /** * Returns the PermissionsHandler for this player data * * @param player * @return AnjoPermissionsHandler */ public AnjoPermissionsHandler getWorldPermissions(final Player player) { return getWorldData(player).getPermissionsHandler(); } /** * It does getWorld(worldName).getPermissionsHandler() * * @param worldName * @return AnjoPermissionsHandler */ public AnjoPermissionsHandler getWorldPermissions(final String worldName) { return getWorldData(worldName).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(final String playerName) { final WorldDataHolder dh = getWorldDataByPlayerName(playerName); if (dh != null) { return dh.getPermissionsHandler(); } return null; } /** * 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(final String worldName) { if (worldsData.containsKey(worldName.toLowerCase()) && (!mirrorsGroup.containsKey(worldName.toLowerCase()) || !mirrorsUser.containsKey(worldName.toLowerCase()))) { return true; } return false; } /** * 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(final String worldName) { if (worldsData.containsKey(worldName.toLowerCase()) || mirrorsGroup.containsKey(worldName.toLowerCase()) || mirrorsUser.containsKey(worldName.toLowerCase())) { return true; } return false; } public boolean isWorldKnown(final String name) { return worldsData.containsKey(name.toLowerCase()); } /** * 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(final 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(final String worldName, final Boolean isMirror) { final String worldNameLowered = worldName.toLowerCase(); if (worldsData.containsKey(worldNameLowered)) { worldsData.get(worldNameLowered).reload(); return; } GroupManager.logger.finest("Trying to load world " + worldName + "..."); final File thisWorldFolder = new File(worldsFolder, worldNameLowered); if ((isMirror) || (thisWorldFolder.exists() && thisWorldFolder.isDirectory())) { // Setup file handles, if not mirrored final File groupsFile = (mirrorsGroup.containsKey(worldNameLowered)) ? null : new File(thisWorldFolder, "groups.yml"); final File usersFile = (mirrorsUser.containsKey(worldNameLowered)) ? null : new File(thisWorldFolder, "users.yml"); if ((groupsFile != null) && (!groupsFile.exists())) { throw new IllegalArgumentException("世界 '" + worldName + "' 的组配置文件未找到: " + groupsFile.getPath()); } if ((usersFile != null) && (!usersFile.exists())) { throw new IllegalArgumentException("世界 '" + worldName + "' 的用户配置文件未找到: " + 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); final 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("成功载入世界 " + worldName + "..."); worldsData.put(worldNameLowered, thisWorldData); return; } // GroupManager.logger.severe("Failed to load world " + worldName + "..."); } } @SuppressWarnings("rawtypes") public void mirrorSetUp() { mirrorsGroup.clear(); mirrorsUser.clear(); final Map mirrorsMap = plugin.getGMConfig().getMirrorsMap(); final HashSet mirroredWorlds = new HashSet(); if (mirrorsMap != null) { for (final 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) { final ArrayList mirrorList = (ArrayList) mirrorsMap.get(source); // These worlds fully mirror their parent for (final Object o : mirrorList) { final String world = o.toString().toLowerCase(); if (!world.equalsIgnoreCase(serverDefaultWorldName)) { try { mirrorsGroup.remove(world); mirrorsUser.remove(world); } catch (final 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, "镜像错误 " + o.toString() + ". 检测到递归循环!"); } } else if (mirrorsMap.get(source) instanceof Map) { final Map subSection = (Map) mirrorsMap.get(source); for (final Object key : subSection.keySet()) { if (!((String) key).equalsIgnoreCase(serverDefaultWorldName)) { if (subSection.get(key) instanceof ArrayList) { final ArrayList mirrorList = (ArrayList) subSection.get(key); // These worlds have defined mirroring for (final Object o : mirrorList) { final 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 (final Exception e) { } if (type.equals("groups")) { mirrorsGroup.put(((String) key).toLowerCase(), getWorldData(source).getName()); GroupManager.logger.log(Level.FINE, "添加组镜像 " + key + "."); } if (type.equals("users")) { mirrorsUser.put(((String) key).toLowerCase(), getWorldData(source).getName()); GroupManager.logger.log(Level.FINE, "添加用户镜像 " + key + "."); } } // Track this world so we can create a datasource for it later mirroredWorlds.add((String) key); } else throw new IllegalStateException("位置的镜像格式 " + (String) key); } else { GroupManager.logger.log(Level.WARNING, "镜像错误 " + (String) key + ". 检测到递归循环!"); } } } } // Create a datasource for any worlds not already loaded for (final String world : mirroredWorlds) { if (!worldsData.containsKey(world.toLowerCase())) { GroupManager.logger.log(Level.FINE, "世界 " + world + " 没有数据."); setupWorldFolder(world); loadWorld(world, true); } } } } /** * */ public void reloadAll() { // Load global groups GroupManager.getGlobalGroups().load(); final ArrayList alreadyDone = new ArrayList(); for (final 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(final String worldName) { if (!mirrorsGroup.containsKey(worldName.toLowerCase())) getWorldData(worldName).reloadGroups(); if (!mirrorsUser.containsKey(worldName.toLowerCase())) getWorldData(worldName).reloadUsers(); } 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("当前没有默认组! OMG!"); } /** * Wrapper to retain backwards compatibility * (call this function to auto overwrite files) */ public void saveChanges() { saveChanges(true); } /** * */ public boolean saveChanges(final boolean overwrite) { boolean changed = false; final 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("发现新的全局组配置 (载入数据)!"); GroupManager.getGlobalGroups().load(); } } for (final OverloadedWorldHolder w : worldsData.values()) { if (alreadyDone.contains(w)) { continue; } if (w == null) { GroupManager.logger.severe("发生了什么!?"); 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("发现新的组配置 (载入数据)!"); // 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("发现新的用户配置 (载入数据)!"); // Backup Users file backupFile(w, false); w.reloadUsers(); changed = true; } } alreadyDone.add(w); } return changed; } public void setupWorldFolder(final String worldName) { final String worldNameLowered = worldName.toLowerCase(); worldsFolder = new File(plugin.getDataFolder(), "worlds"); if (!worldsFolder.exists()) { worldsFolder.mkdirs(); } final 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 */ final 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)) { final File groupsFile = new File(defaultWorldFolder, "groups.yml"); if (!groupsFile.exists() || groupsFile.length() == 0) { final InputStream template = plugin.getResourceAsStream("groups.yml"); try { Tasks.copy(template, groupsFile); } catch (final IOException ex) { GroupManager.logger.log(Level.SEVERE, null, ex); } } } if (!mirrorsUser.containsKey(worldNameLowered)) { final File usersFile = new File(defaultWorldFolder, "users.yml"); if (!usersFile.exists() || usersFile.length() == 0) { final InputStream template = plugin.getResourceAsStream("users.yml"); try { Tasks.copy(template, usersFile); } catch (final IOException ex) { GroupManager.logger.log(Level.SEVERE, null, ex); } } } } } /** * Backup the Groups/Users file * * @param w * @param groups */ private void backupFile(final OverloadedWorldHolder w, final Boolean groups) { final 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 (final IOException ex) { GroupManager.logger.log(Level.SEVERE, null, ex); } } /** * 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(final String worldName) { final String worldNameLowered = worldName.toLowerCase(); if (worldsData.containsKey(worldNameLowered)) { final OverloadedWorldHolder data = worldsData.get(worldNameLowered); data.updateDataSource(); return data; } return null; } 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 (final World world : plugin.getServer().getWorlds()) { GroupManager.logger.log(Level.FINE, "检查世界 " + world.getName() + " 的数据."); if ((!worldsData.containsKey(world.getName().toLowerCase())) && ((!mirrorsGroup.containsKey(world.getName().toLowerCase())) || (!mirrorsUser.containsKey(world.getName().toLowerCase())))) { if (worldsData.containsKey("all_unnamed_worlds")) { final String usersMirror = mirrorsUser.get("all_unnamed_worlds"); final 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, "创建世界 " + world.getName() + " 的数据."); setupWorldFolder(world.getName()); } } /* * Loop over all folders within the worlds folder and attempt to load * the world data */ for (final File folder : worldsFolder.listFiles()) { if (folder.isDirectory() && !folder.getName().startsWith(".")) { GroupManager.logger.info("载入世界数据: " + 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()); } } } } 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 { final Properties server = new Properties(); try { server.load(new FileInputStream(new File("server.properties"))); serverDefaultWorldName = server.getProperty("level-name").toLowerCase(); } catch (final IOException ex) { GroupManager.logger.log(Level.SEVERE, null, ex); } } setupWorldFolder(serverDefaultWorldName); } }