GroupManager/src/main/java/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java

693 lines
29 KiB
Java

/*
* 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<String, OverloadedWorldHolder> worldsData = new HashMap<String, OverloadedWorldHolder>();
/**
* Map of mirrors: <nonExistingWorldName, existingAndLoadedWorldName>
* The key is the mirror.
* The object is the mirrored.
*
* Mirror shows the same data of mirrored.
*/
private Map<String, String> mirrorsGroup = new HashMap<String, String>();
private Map<String, String> mirrorsUser = new HashMap<String, String>();
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<OverloadedWorldHolder> of all loaded worlds
*/
public ArrayList<OverloadedWorldHolder> allWorldsDataList() {
final ArrayList<OverloadedWorldHolder> list = new ArrayList<OverloadedWorldHolder>();
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<String, String> getMirrorsGroup() {
return mirrorsGroup;
}
/**
* @return the mirrorsUser
*/
public Map<String, String> 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<Player> 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<String, Object> mirrorsMap = plugin.getGMConfig().getMirrorsMap();
final HashSet<String> mirroredWorlds = new HashSet<String>();
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<WorldDataHolder> alreadyDone = new ArrayList<WorldDataHolder>();
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<String, OverloadedWorldHolder>();
mirrorsGroup = new HashMap<String, String>();
mirrorsUser = new HashMap<String, String>();
// 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<WorldDataHolder> alreadyDone = new ArrayList<WorldDataHolder>();
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);
}
}