Fix world mirroring so it correctly creates data files and data sources

for partially mirrored worlds.
Fixed world mirroring so it returns the correct data for the
requested world
This commit is contained in:
ElgarL 2012-02-05 16:30:58 +00:00
parent 76ba5caeec
commit e93e50f6d2
7 changed files with 216 additions and 77 deletions

View file

@ -135,4 +135,6 @@ v 1.9:
- Track the 'onPlayerChangeWorld' event as some teleports seem to not be triggering a world move. - Track the 'onPlayerChangeWorld' event as some teleports seem to not be triggering a world move.
- Catch all errors in badly formatted groups. - Catch all errors in badly formatted groups.
- Fix a bug with getWorldData return the main world data for all mirrors, instead of the worlds parent data. - Fix a bug with getWorldData return the main world data for all mirrors, instead of the worlds parent data.
- Prevent getAllPlayersPermissions() processing a group more than once. Improves performance when using complex inheritance structures. - Prevent getAllPlayersPermissions() processing a group more than once. Improves performance when using complex inheritance structures.
- Fix world mirroring so it correctly creates data files and data sources for partially mirrored worlds.
- Fixed world mirroring so it returns the correct data for the requested world

View file

@ -1485,7 +1485,7 @@ public class GroupManager extends JavaPlugin {
try { try {
worldsHolder.saveChanges(forced); worldsHolder.saveChanges(forced);
sender.sendMessage(ChatColor.YELLOW + " The changes were saved."); sender.sendMessage(ChatColor.YELLOW + " All changes were saved.");
} catch (IllegalStateException ex) { } catch (IllegalStateException ex) {
sender.sendMessage(ChatColor.RED + ex.getMessage()); sender.sendMessage(ChatColor.RED + ex.getMessage());
} }
@ -1522,6 +1522,7 @@ public class GroupManager extends JavaPlugin {
} }
// WORKING // WORKING
config.load(); config.load();
globalGroups.load();
worldsHolder.mirrorSetUp(); worldsHolder.mirrorSetUp();
isLoaded = false; isLoaded = false;
@ -1538,7 +1539,7 @@ public class GroupManager extends JavaPlugin {
sender.sendMessage("The request to world '" + auxString + "' was sent."); sender.sendMessage("The request to world '" + auxString + "' was sent.");
} else { } else {
worldsHolder.reloadAll(); worldsHolder.reloadAll();
sender.sendMessage(ChatColor.YELLOW + " The current world was reloaded."); sender.sendMessage(ChatColor.YELLOW + " All worlds were reloaded.");
} }
isLoaded = true; isLoaded = true;

View file

@ -65,6 +65,15 @@ public abstract class DataUnit {
hash = 71 * hash + (this.name != null ? this.name.toLowerCase().hashCode() : 0); hash = 71 * hash + (this.name != null ? this.name.toLowerCase().hashCode() : 0);
return hash; return hash;
} }
/**
* Set the data source to point to a new worldDataHolder
*
* @param source
*/
public void setDataSource(WorldDataHolder source) {
this.dataSource = source;
}
/** /**
* @return the dataSource * @return the dataSource

View file

@ -17,7 +17,8 @@ public class GroupsDataHolder {
/** /**
* Root World name this set of groups is associated with. * Root World name this set of groups is associated with.
*/ */
private String name; //private String name;
private WorldDataHolder dataSource;
private Group defaultGroup = null; private Group defaultGroup = null;
private File groupsFile; private File groupsFile;
private boolean haveGroupsChanged = false; private boolean haveGroupsChanged = false;
@ -33,17 +34,24 @@ public class GroupsDataHolder {
*/ */
protected GroupsDataHolder() { protected GroupsDataHolder() {
} }
protected void setWorldName(String worldName) { public void setDataSource(WorldDataHolder dataSource) {
name = worldName; this.dataSource = dataSource;
//push this data source to the users, so they pull the correct groups data.
for (Group group : groups.values())
group.setDataSource(this.dataSource);
} }
//protected void setWorldName(String worldName) {
// name = worldName;
//}
/** /**
* @return the name * @return the name
*/ */
public String getWorldName() { //public String getWorldName() {
return name; // return name;
} //}
/** /**
* @return the defaultGroup * @return the defaultGroup

View file

@ -17,7 +17,8 @@ public class UsersDataHolder {
/** /**
* Root World name this set of groups is associated with. * Root World name this set of groups is associated with.
*/ */
private String name; //private String name;
private WorldDataHolder dataSource;
private File usersFile; private File usersFile;
private boolean haveUsersChanged = false; private boolean haveUsersChanged = false;
private long timeStampUsers = 0; private long timeStampUsers = 0;
@ -32,20 +33,28 @@ public class UsersDataHolder {
*/ */
protected UsersDataHolder() { protected UsersDataHolder() {
} }
public void setDataSource(WorldDataHolder dataSource) {
this.dataSource = dataSource;
//push this data source to the users, so they pull the correct groups data.
for (User user : users.values())
user.setDataSource(this.dataSource);
}
/** /**
* @param worldName * @param worldName
*/ */
public void setWorldName(String worldName) { //public void setWorldName(String worldName) {
this.name = worldName; // this.name = worldName;
} //}
/** /**
* @return the name * @return the name
*/ */
public String getWorldName() { //public String getWorldName() {
return this.name; // return this.name;
} //}
/** /**
* @return the users * @return the users

View file

@ -80,6 +80,16 @@ public class WorldDataHolder {
//this.defaultGroup = defaultGroup; //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 * Search for a user. If it doesn't exist, create a new one with
@ -465,54 +475,66 @@ public class WorldDataHolder {
} }
//PERMISSIONS NODE //PERMISSIONS NODE
if (thisGroupNode.get("permissions") == null) { try {
thisGroupNode.put("permissions", new ArrayList<String>()); if (thisGroupNode.get("permissions") == null) {
} else { thisGroupNode.put("permissions", new ArrayList<String>());
if (thisGroupNode.get("permissions") instanceof List) {
for (Object o : ((List) thisGroupNode.get("permissions"))) {
try {
thisGrp.addPermission(o.toString());
} catch (NullPointerException e) {
// Ignore this entry as it's null.
//throw new IllegalArgumentException("Invalid permission node in group: " + thisGrp.getName() + " in file: " + groupsFile.getPath());
}
}
} else if (thisGroupNode.get("permissions") instanceof String) {
thisGrp.addPermission((String) thisGroupNode.get("permissions"));
} else { } else {
throw new IllegalArgumentException("Unknown type of permissions node(Should be String or List<String>) for group: " + thisGrp.getName() + " in file: " + groupsFile.getPath()); if (thisGroupNode.get("permissions") instanceof List) {
for (Object o : ((List) thisGroupNode.get("permissions"))) {
try {
thisGrp.addPermission(o.toString());
} catch (NullPointerException e) {
// Ignore this entry as it's null.
//throw new IllegalArgumentException("Invalid permission node in group: " + thisGrp.getName() + " in file: " + groupsFile.getPath());
}
}
} else if (thisGroupNode.get("permissions") instanceof String) {
thisGrp.addPermission((String) thisGroupNode.get("permissions"));
} else {
throw new IllegalArgumentException("Unknown type of permissions node(Should be String or List<String>) for group: " + thisGrp.getName() + " in file: " + groupsFile.getPath());
}
thisGrp.sortPermissions();
} }
thisGrp.sortPermissions(); } catch (Exception e) {
throw new IllegalArgumentException("Invalid formatting found in permissions section for group: " + thisGrp.getName() + " in file: " + groupsFile.getPath());
} }
//INFO NODE //INFO NODE
if (thisGroupNode.get("info") instanceof Map) { try {
Map<String, Object> infoNode = (Map<String, Object>) thisGroupNode.get("info"); if (thisGroupNode.get("info") instanceof Map) {
if (infoNode != null) { Map<String, Object> infoNode = (Map<String, Object>) thisGroupNode.get("info");
thisGrp.setVariables(infoNode); if (infoNode != null) {
} thisGrp.setVariables(infoNode);
} else }
throw new IllegalArgumentException("Unknown entry found in Info section for group: " + thisGrp.getName() + " in file: " + groupsFile.getPath()); } else
throw new IllegalArgumentException("Unknown entry found in Info section for group: " + thisGrp.getName() + " in file: " + groupsFile.getPath());
} catch (Exception e1) {
throw new IllegalArgumentException("Invalid formatting found in info section for group: " + thisGrp.getName() + " in file: " + groupsFile.getPath());
}
//END INFO NODE //END INFO NODE
if (thisGroupNode.get("inheritance") == null || thisGroupNode.get("inheritance") instanceof List) { try {
Object inheritNode = thisGroupNode.get("inheritance"); if (thisGroupNode.get("inheritance") == null || thisGroupNode.get("inheritance") instanceof List) {
if (inheritNode == null) { Object inheritNode = thisGroupNode.get("inheritance");
thisGroupNode.put("inheritance", new ArrayList<String>()); if (inheritNode == null) {
} else if (inheritNode instanceof List) { thisGroupNode.put("inheritance", new ArrayList<String>());
List<String> groupsInh = (List<String>) inheritNode; } else if (inheritNode instanceof List) {
for (String grp : groupsInh) { List<String> groupsInh = (List<String>) inheritNode;
if (inheritance.get(groupKey) == null) { for (String grp : groupsInh) {
List<String> thisInherits = new ArrayList<String>(); if (inheritance.get(groupKey) == null) {
inheritance.put(groupKey, thisInherits); List<String> thisInherits = new ArrayList<String>();
} inheritance.put(groupKey, thisInherits);
inheritance.get(groupKey).add(grp); }
inheritance.get(groupKey).add(grp);
}
} }
}else }
throw new IllegalArgumentException("Unknown entry found in inheritance section for group: " + thisGrp.getName() + " in file: " + groupsFile.getPath()); }else
throw new IllegalArgumentException("Unknown entry found in inheritance section for group: " + thisGrp.getName() + " in file: " + groupsFile.getPath());
} catch (Exception e2) {
throw new IllegalArgumentException("Invalid formatting found in inheritance section for group: " + thisGrp.getName() + " in file: " + groupsFile.getPath());
}
} }
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(); ex.printStackTrace();

View file

@ -10,6 +10,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
@ -45,7 +46,7 @@ public class WorldsHolder {
private Map<String, String> mirrorsGroup = new HashMap<String, String>(); private Map<String, String> mirrorsGroup = new HashMap<String, String>();
private Map<String, String> mirrorsUser = new HashMap<String, String>(); private Map<String, String> mirrorsUser = new HashMap<String, String>();
private OverloadedWorldHolder defaultWorld; //private OverloadedWorldHolder defaultWorld;
private String serverDefaultWorldName; private String serverDefaultWorldName;
private GroupManager plugin; private GroupManager plugin;
private File worldsFolder; private File worldsFolder;
@ -59,7 +60,7 @@ public class WorldsHolder {
// Setup folders and check files exist for the primary world // Setup folders and check files exist for the primary world
verifyFirstRun(); verifyFirstRun();
initialLoad(); initialLoad();
if (defaultWorld == null) { if (serverDefaultWorldName == null) {
throw new IllegalStateException("There is no default group! OMG!"); throw new IllegalStateException("There is no default group! OMG!");
} }
} }
@ -76,7 +77,7 @@ public class WorldsHolder {
private void initialWorldLoading() { private void initialWorldLoading() {
//Load the default world //Load the default world
loadWorld(serverDefaultWorldName); loadWorld(serverDefaultWorldName);
defaultWorld = worldsData.get(serverDefaultWorldName); //defaultWorld = getUpdatedWorldData(serverDefaultWorldName);
} }
private void loadAllSearchedWorlds() { private void loadAllSearchedWorlds() {
@ -117,6 +118,8 @@ public class WorldsHolder {
mirrorsGroup.clear(); mirrorsGroup.clear();
mirrorsUser.clear(); mirrorsUser.clear();
Map<String, Object> mirrorsMap = plugin.getGMConfig().getMirrorsMap(); Map<String, Object> mirrorsMap = plugin.getGMConfig().getMirrorsMap();
HashSet<String> mirroredWorlds = new HashSet<String>();
if (mirrorsMap != null) { if (mirrorsMap != null) {
for (String source : mirrorsMap.keySet()) { for (String source : mirrorsMap.keySet()) {
@ -140,6 +143,10 @@ public class WorldsHolder {
} }
mirrorsGroup.put(world, getWorldData(source).getName()); mirrorsGroup.put(world, getWorldData(source).getName());
mirrorsUser.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 } else
GroupManager.logger.log(Level.WARNING, "Mirroring error with " + o.toString() + ". Recursive loop detected!"); GroupManager.logger.log(Level.WARNING, "Mirroring error with " + o.toString() + ". Recursive loop detected!");
} }
@ -171,11 +178,13 @@ public class WorldsHolder {
if (type.equals("users")) if (type.equals("users"))
mirrorsUser.put(key.toLowerCase(), getWorldData(source).getName()); mirrorsUser.put(key.toLowerCase(), getWorldData(source).getName());
} }
// Track this world so we can create a datasource for it later
mirroredWorlds.add(key);
} else } else
GroupManager.logger.log(Level.WARNING, "Mirroring error with " + key + ". Recursive loop detected!"); GroupManager.logger.log(Level.WARNING, "Mirroring error with " + key + ". Recursive loop detected!");
} else { } else {
throw new IllegalStateException("Unknown mirroring format for " + key); throw new IllegalStateException("Unknown mirroring format for " + key);
} }
@ -183,6 +192,14 @@ public class WorldsHolder {
} }
} }
} }
// Create a datasource for any worlds not already loaded
for (String world : mirroredWorlds){
if (!worldsData.containsKey(world.toLowerCase())) {
setupWorldFolder(world);
loadWorld(world, true);
}
}
} }
} }
@ -329,21 +346,55 @@ public class WorldsHolder {
public OverloadedWorldHolder getWorldData(String worldName) { public OverloadedWorldHolder getWorldData(String worldName) {
String worldNameLowered = worldName.toLowerCase(); String worldNameLowered = worldName.toLowerCase();
// Return this worlds data // Find this worlds data
if (worldsData.containsKey(worldNameLowered)) if (worldsData.containsKey(worldNameLowered)) {
return worldsData.get(worldNameLowered);
String usersMirror = mirrorsUser.get(worldNameLowered);
String groupsMirror = mirrorsGroup.get(worldNameLowered);
if (usersMirror != null) {
// If both are mirrored
if (groupsMirror != null) {
// if the data sources are the same, return the parent
if (usersMirror == groupsMirror)
return getUpdatedWorldData(usersMirror.toLowerCase());
// Both data sources are mirrors, but they are from different parents
// so we return the actual data object.
return getUpdatedWorldData(worldNameLowered);
}
// Groups isn't a mirror so return this this worlds data source
return getUpdatedWorldData(worldNameLowered);
}
// users isn't mirrored so we need to return this worlds data source
return getUpdatedWorldData(worldNameLowered);
}
// If groups mirrored return the parent worlds data // Oddly no data source was found for this world so return the default.
if (mirrorsGroup.containsKey(worldNameLowered))
return worldsData.get(mirrorsGroup.get(worldNameLowered).toLowerCase());
// If users mirrored return the parent worlds data
if (mirrorsUser.containsKey(worldNameLowered))
return worldsData.get(mirrorsUser.get(worldNameLowered).toLowerCase());
GroupManager.logger.finest("Requested world " + worldName + " not found or badly mirrored. Returning default world..."); GroupManager.logger.finest("Requested world " + worldName + " not found or badly mirrored. Returning default world...");
return getDefaultWorld(); 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) {
if (worldsData.containsKey(worldName.toLowerCase())) {
OverloadedWorldHolder data = worldsData.get(worldName.toLowerCase());
data.updateDataSource();
return data;
}
return null;
}
/** /**
* Do a matching of playerName, if its found only one player, do * Do a matching of playerName, if its found only one player, do
@ -487,18 +538,29 @@ public class WorldsHolder {
} }
/** /**
* Wrapper for LoadWorld(String,Boolean) for backwards compatibility
*
* Load a world from file. * Load a world from file.
* If it already been loaded, summon reload method from dataHolder. * If it already been loaded, summon reload method from dataHolder.
* @param worldName * @param worldName
*/ */
public void loadWorld(String 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) {
if (worldsData.containsKey(worldName.toLowerCase())) { if (worldsData.containsKey(worldName.toLowerCase())) {
worldsData.get(worldName.toLowerCase()).reload(); worldsData.get(worldName.toLowerCase()).reload();
return; return;
} }
GroupManager.logger.finest("Trying to load world " + worldName + "..."); GroupManager.logger.finest("Trying to load world " + worldName + "...");
File thisWorldFolder = new File(worldsFolder, worldName); File thisWorldFolder = new File(worldsFolder, worldName);
if (thisWorldFolder.exists() && thisWorldFolder.isDirectory()) { if ((isMirror) || (thisWorldFolder.exists() && thisWorldFolder.isDirectory())) {
// Setup file handles, if not mirrored // Setup file handles, if not mirrored
File groupsFile = (mirrorsGroup.containsKey(worldName.toLowerCase()))? null : new File(thisWorldFolder, "groups.yml"); File groupsFile = (mirrorsGroup.containsKey(worldName.toLowerCase()))? null : new File(thisWorldFolder, "groups.yml");
@ -575,17 +637,43 @@ public class WorldsHolder {
* @return the defaultWorld * @return the defaultWorld
*/ */
public OverloadedWorldHolder getDefaultWorld() { public OverloadedWorldHolder getDefaultWorld() {
return defaultWorld; return getUpdatedWorldData(serverDefaultWorldName);
} }
/** /**
* Returns all physically loaded worlds. * Returns all physically loaded worlds which have at least
* one of their own data sets for users or groups.
*
* @return ArrayList<OverloadedWorldHolder> of all loaded worlds * @return ArrayList<OverloadedWorldHolder> of all loaded worlds
*/ */
public ArrayList<OverloadedWorldHolder> allWorldsDataList() { public ArrayList<OverloadedWorldHolder> allWorldsDataList() {
ArrayList<OverloadedWorldHolder> list = new ArrayList<OverloadedWorldHolder>(); ArrayList<OverloadedWorldHolder> list = new ArrayList<OverloadedWorldHolder>();
for (OverloadedWorldHolder data : worldsData.values()) { for (OverloadedWorldHolder data : worldsData.values()) {
if (!list.contains(data)) { if ((!list.contains(data)) && (!mirrorsGroup.containsKey(data.getName().toLowerCase()) || !mirrorsUser.containsKey(data.getName().toLowerCase()))) {
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) {
if (!list.contains(usersMirror.toLowerCase()))
list.add(worldsData.get(usersMirror.toLowerCase()));
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); list.add(data);
} }
} }