Unique IDs for claim subdivisions.

Also performance updates and switch to YAML file format for flat file
data stores.
This commit is contained in:
ryanhamshire 2015-09-12 14:17:00 -07:00
parent 4b3802d189
commit 9c00a47a29
9 changed files with 462 additions and 367 deletions

View File

@ -173,14 +173,14 @@ commands:
permission: griefprevention.ignore
separate:
description: Forces two players to ignore each other in chat.
usage: /Separate
usage: /Separate <player1> <player2>
permission: griefprevention.separate
unseparate:
description: Reverses /separate.
usage: /UnSeparate
usage: /UnSeparate <player1> <player2>
permission: griefprevention.separate
claimbook:
description: Gives a player a replacement land claiming book.
description: Gives a player a manual about claiming land.
usage: /ClaimBook <player>
permission: griefprevention.claimbook
permissions:

View File

@ -186,7 +186,7 @@ public class Claim
}
//main constructor. note that only creating a claim instance does nothing - a claim must be added to the data store to be effective
Claim(Location lesserBoundaryCorner, Location greaterBoundaryCorner, UUID ownerID, String [] builderIDs, String [] containerIds, String [] accessorIDs, String [] managerIDs, Long id)
Claim(Location lesserBoundaryCorner, Location greaterBoundaryCorner, UUID ownerID, List<String> builderIDs, List<String> containerIDs, List<String> accessorIDs, List<String> managerIDs, Long id)
{
//modification date
this.modifiedDate = Calendar.getInstance().getTime();
@ -202,36 +202,32 @@ public class Claim
this.ownerID = ownerID;
//other permissions
for(int i = 0; i < builderIDs.length; i++)
for(String builderID : builderIDs)
{
String builderID = builderIDs[i];
if(builderID != null && !builderID.isEmpty())
{
this.playerIDToClaimPermissionMap.put(builderID, ClaimPermission.Build);
}
}
for(int i = 0; i < containerIds.length; i++)
for(String containerID : containerIDs)
{
String containerID = containerIds[i];
if(containerID != null && !containerID.isEmpty())
{
this.playerIDToClaimPermissionMap.put(containerID, ClaimPermission.Inventory);
}
}
for(int i = 0; i < accessorIDs.length; i++)
for(String accessorID : accessorIDs)
{
String accessorID = accessorIDs[i];
if(accessorID != null && !accessorID.isEmpty())
{
this.playerIDToClaimPermissionMap.put(accessorID, ClaimPermission.Access);
}
}
for(int i = 0; i < managerIDs.length; i++)
for(String managerID : managerIDs)
{
String managerID = managerIDs[i];
if(managerID != null && !managerID.isEmpty())
{
this.managers.add(managerID);
@ -264,7 +260,7 @@ public class Claim
Claim claim = new Claim
(new Location(this.lesserBoundaryCorner.getWorld(), this.lesserBoundaryCorner.getBlockX() - howNear, this.lesserBoundaryCorner.getBlockY(), this.lesserBoundaryCorner.getBlockZ() - howNear),
new Location(this.greaterBoundaryCorner.getWorld(), this.greaterBoundaryCorner.getBlockX() + howNear, this.greaterBoundaryCorner.getBlockY(), this.greaterBoundaryCorner.getBlockZ() + howNear),
null, new String[] {}, new String[] {}, new String[] {}, new String[] {}, null);
null, new ArrayList<String>(), new ArrayList<String>(), new ArrayList<String>(), new ArrayList<String>(), null);
return claim.contains(location, false, true);
}

View File

@ -19,9 +19,6 @@
package me.ryanhamshire.GriefPrevention;
import java.io.*;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
@ -66,7 +63,7 @@ public abstract class DataStore
final static String softMuteFilePath = dataLayerFolderPath + File.separator + "softMute.txt";
//the latest version of the data schema implemented here
protected static final int latestSchemaVersion = 1;
protected static final int latestSchemaVersion = 2;
//reading and writing the schema version to the data store
abstract int getSchemaVersionFromStorage();
@ -129,6 +126,11 @@ public abstract class DataStore
for(Claim claim : this.claims)
{
this.saveClaim(claim);
for(Claim subClaim : claim.children)
{
this.saveClaim(subClaim);
}
}
//clean up any UUID conversion work
@ -363,7 +365,7 @@ public abstract class DataStore
//adds a claim to the datastore, making it an effective claim
synchronized void addClaim(Claim newClaim, boolean writeToStorage)
{
//subdivisions are easy
//subdivisions are added under their parent, not directly to the hash map for direct search
if(newClaim.parent != null)
{
if(!newClaim.parent.children.contains(newClaim))
@ -425,7 +427,7 @@ public abstract class DataStore
}
//turns a location string back into a location
Location locationFromString(String string) throws Exception
Location locationFromString(String string, List<World> validWorlds) throws Exception
{
//split the input string on the space
String [] elements = string.split(locationStringDelimiter);
@ -442,7 +444,16 @@ public abstract class DataStore
String zString = elements[3];
//identify world the claim is in
World world = GriefPrevention.instance.getServer().getWorld(worldName);
World world = null;
for(World w : validWorlds)
{
if(w.getName().equalsIgnoreCase(worldName))
{
world = w;
break;
}
}
if(world == null)
{
throw new Exception("World not found: \"" + worldName + "\"");
@ -459,15 +470,7 @@ public abstract class DataStore
//saves any changes to a claim to secondary storage
synchronized public void saveClaim(Claim claim)
{
//subdivisions don't save to their own files, but instead live in their parent claim's file
//so any attempt to save a subdivision will save its parent (and thus the subdivision)
if(claim.parent != null)
{
this.saveClaim(claim.parent);
return;
}
//otherwise get a unique identifier for the claim which will be used to name the file on disk
//ensure a unique identifier for the claim which will be used to name the file on disk
if(claim.id == null)
{
claim.id = this.nextClaimID;
@ -512,23 +515,20 @@ public abstract class DataStore
synchronized void deleteClaim(Claim claim, boolean fireEvent)
{
//subdivisions are simple - just remove them from their parent claim and save that claim
//delete any children
for(int j = 0; j < claim.children.size(); j++)
{
this.deleteClaim(claim.children.get(j--), true);
}
//subdivisions must also be removed from the parent claim child list
if(claim.parent != null)
{
Claim parentClaim = claim.parent;
parentClaim.children.remove(claim);
claim.inDataStore = false;
this.saveClaim(parentClaim);
return;
}
//delete any children
for(int j = 0; j < claim.children.size(); j++)
{
this.deleteClaim(claim.children.get(j), false);
}
//mark as deleted so any references elsewhere can be ignored
//mark as deleted so any references elsewhere can be ignored
claim.inDataStore = false;
//remove from memory
@ -558,8 +558,8 @@ public abstract class DataStore
//remove from secondary storage
this.deleteClaimFromSecondaryStorage(claim);
//update player data, except for administrative claims, which have no owner
if(!claim.isAdminClaim())
//update player data
if(claim.ownerID != null)
{
PlayerData ownerData = this.getPlayerData(claim.ownerID);
for(int i = 0; i < ownerData.getClaims().size(); i++)
@ -597,14 +597,14 @@ public abstract class DataStore
for(Claim claim : claimsInChunk)
{
if(claim.contains(location, ignoreHeight, false))
if(claim.inDataStore && claim.contains(location, ignoreHeight, false))
{
//when we find a top level claim, if the location is in one of its subdivisions,
//return the SUBDIVISION, not the top level claim
for(int j = 0; j < claim.children.size(); j++)
{
Claim subdivision = claim.children.get(j);
if(subdivision.contains(location, ignoreHeight, false)) return subdivision;
if(subdivision.inDataStore && subdivision.contains(location, ignoreHeight, false)) return subdivision;
}
return claim;
@ -620,7 +620,7 @@ public abstract class DataStore
{
for(Claim claim : this.claims)
{
if(claim.getID() == id) return claim;
if(claim.inDataStore && claim.getID() == id) return claim;
}
return null;
@ -702,10 +702,10 @@ public abstract class DataStore
new Location(world, smallx, smally, smallz),
new Location(world, bigx, bigy, bigz),
ownerID,
new String [] {},
new String [] {},
new String [] {},
new String [] {},
new ArrayList<String>(),
new ArrayList<String>(),
new ArrayList<String>(),
new ArrayList<String>(),
id);
newClaim.parent = parent;
@ -714,7 +714,7 @@ public abstract class DataStore
ArrayList<Claim> claimsToCheck;
if(newClaim.parent != null)
{
claimsToCheck = newClaim.parent.children;
claimsToCheck = newClaim.parent.children;
}
else
{
@ -726,7 +726,7 @@ public abstract class DataStore
Claim otherClaim = claimsToCheck.get(i);
//if we find an existing claim which will be overlapped
if(otherClaim.overlaps(newClaim))
if(otherClaim.id != newClaim.id && otherClaim.inDataStore && otherClaim.overlaps(newClaim))
{
//result = fail, return conflicting claim
result.succeeded = false;
@ -821,27 +821,18 @@ public abstract class DataStore
if(claim.parent != null) claim = claim.parent;
//note any subdivisions
ArrayList<Claim> subdivisions = new ArrayList<Claim>(claim.children);
//delete the claim
this.deleteClaim(claim, false);
//re-create it at the new depth
//adjust to new depth
claim.lesserBoundaryCorner.setY(newDepth);
claim.greaterBoundaryCorner.setY(newDepth);
//re-add the subdivisions (deleteClaim() removed them) with the new depth
for(Claim subdivision : subdivisions)
for(Claim subdivision : claim.children)
{
subdivision.lesserBoundaryCorner.setY(newDepth);
subdivision.greaterBoundaryCorner.setY(newDepth);
subdivision.parent = claim;
this.addClaim(subdivision, false);
this.saveClaim(subdivision);
}
//save changes
this.addClaim(claim, true);
this.saveClaim(claim);
}
//starts a siege on a claim
@ -1084,12 +1075,6 @@ public abstract class DataStore
//see CreateClaim() for details on return value
synchronized public CreateClaimResult resizeClaim(Claim claim, int newx1, int newx2, int newy1, int newy2, int newz1, int newz2, Player resizingPlayer)
{
//note any subdivisions before deleting the claim
ArrayList<Claim> subdivisions = new ArrayList<Claim>(claim.children);
//remove old claim
this.deleteClaim(claim, false);
//try to create this new claim, ignoring the original when checking for overlap
CreateClaimResult result = this.createClaim(claim.getLesserBoundaryCorner().getWorld(), newx1, newx2, newy1, newy2, newz1, newz2, claim.ownerID, claim.parent, claim.id, resizingPlayer);
@ -1118,20 +1103,17 @@ public abstract class DataStore
}
//restore subdivisions
for(Claim subdivision : subdivisions)
for(Claim subdivision : claim.children)
{
subdivision.parent = result.claim;
this.addClaim(subdivision, false);
result.claim.children.add(subdivision);
}
//save those changes
this.saveClaim(result.claim);
}
else
{
//put original claim back
this.addClaim(claim, true);
//make original claim ineffective (it's still in the hash map, so let's make it ignored)
claim.inDataStore = false;
}
return result;
@ -1421,7 +1403,7 @@ public abstract class DataStore
//used in updating the data schema from 0 to 1.
//converts player names in a list to uuids
protected String[] convertNameListToUUIDList(String[] names)
protected List<String> convertNameListToUUIDList(List<String> names)
{
//doesn't apply after schema has been updated to version 1
if(this.getSchemaVersion() >= 1) return names;
@ -1453,14 +1435,7 @@ public abstract class DataStore
}
}
//return final result of conversion
String [] resultArray = new String [resultNames.size()];
for(int i = 0; i < resultNames.size(); i++)
{
resultArray[i] = resultNames.get(i);
}
return resultArray;
return resultNames;
}
abstract void close();

View File

@ -225,6 +225,7 @@ public class DatabaseDataStore extends DataStore
ArrayList<Claim> claimsToRemove = new ArrayList<Claim>();
ArrayList<Claim> subdivisionsToLoad = new ArrayList<Claim>();
List<World> validWorlds = Bukkit.getServer().getWorlds();
while(results.next())
{
@ -242,10 +243,10 @@ public class DatabaseDataStore extends DataStore
try
{
lesserCornerString = results.getString("lessercorner");
lesserBoundaryCorner = this.locationFromString(lesserCornerString);
lesserBoundaryCorner = this.locationFromString(lesserCornerString, validWorlds);
String greaterCornerString = results.getString("greatercorner");
greaterBoundaryCorner = this.locationFromString(greaterCornerString);
greaterBoundaryCorner = this.locationFromString(greaterCornerString, validWorlds);
}
catch(Exception e)
{
@ -293,19 +294,19 @@ public class DatabaseDataStore extends DataStore
}
String buildersString = results.getString("builders");
String [] builderNames = buildersString.split(";");
List<String> builderNames = Arrays.asList(buildersString.split(";"));
builderNames = this.convertNameListToUUIDList(builderNames);
String containersString = results.getString("containers");
String [] containerNames = containersString.split(";");
List<String> containerNames = Arrays.asList(containersString.split(";"));
containerNames = this.convertNameListToUUIDList(containerNames);
String accessorsString = results.getString("accessors");
String [] accessorNames = accessorsString.split(";");
List<String> accessorNames = Arrays.asList(accessorsString.split(";"));
accessorNames = this.convertNameListToUUIDList(accessorNames);
String managersString = results.getString("managers");
String [] managerNames = managersString.split(";");
List<String> managerNames = Arrays.asList(managersString.split(";"));
managerNames = this.convertNameListToUUIDList(managerNames);
Claim claim = new Claim(lesserBoundaryCorner, greaterBoundaryCorner, ownerID, builderNames, containerNames, accessorNames, managerNames, claimID);
@ -356,6 +357,13 @@ public class DatabaseDataStore extends DataStore
this.deleteClaimFromSecondaryStorage(claimsToRemove.get(i));
}
if(this.getSchemaVersion() <= 2)
{
this.refreshDataConnection();
statement = this.databaseConnection.createStatement();
statement.execute("DELETE FROM griefprevention_claimdata WHERE id='-1';");
}
super.initialize();
}
@ -369,15 +377,8 @@ public class DatabaseDataStore extends DataStore
//wipe out any existing data about this claim
this.deleteClaimFromSecondaryStorage(claim);
//write top level claim data to the database
//write claim data to the database
this.writeClaimData(claim);
//for each subdivision
for(int i = 0; i < claim.children.size(); i++)
{
//write the subdivision's data to the database
this.writeClaimData(claim.children.get(i));
}
}
catch(SQLException e)
{
@ -435,23 +436,13 @@ public class DatabaseDataStore extends DataStore
parentId = claim.parent.id;
}
long id;
if(claim.id == null)
{
id = -1;
}
else
{
id = claim.id;
}
try
{
this.refreshDataConnection();
Statement statement = databaseConnection.createStatement();
statement.execute("INSERT INTO griefprevention_claimdata (id, owner, lessercorner, greatercorner, builders, containers, accessors, managers, parentid) VALUES(" +
id + ", '" +
claim.id + ", '" +
owner + "', '" +
lesserCornerString + "', '" +
greaterCornerString + "', '" +
@ -479,11 +470,7 @@ public class DatabaseDataStore extends DataStore
Statement statement = this.databaseConnection.createStatement();
statement.execute("DELETE FROM griefprevention_claimdata WHERE lessercorner='" + this.locationToString(claim.lesserBoundaryCorner) + "' AND greatercorner = '" + this.locationToString(claim.greaterBoundaryCorner) + "';");
if(claim.id != -1)
{
statement.execute("DELETE FROM griefprevention_claimdata WHERE parentid=" + claim.id + ";");
}
statement.execute("DELETE FROM griefprevention_claimdata WHERE id='" + claim.id + "';");
}
catch(SQLException e)
{

View File

@ -24,8 +24,13 @@ import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import org.bukkit.*;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import com.google.common.io.Files;
@ -189,200 +194,374 @@ public class FlatFileDataStore extends DataStore
//get a list of all the files in the claims data folder
files = claimDataFolder.listFiles();
for(int i = 0; i < files.length; i++)
if(this.getSchemaVersion() <= 1)
{
if(files[i].isFile()) //avoids folders
{
//skip any file starting with an underscore, to avoid special files not representing land claims
if(files[i].getName().startsWith("_")) continue;
//the filename is the claim ID. try to parse it
long claimID;
try
{
claimID = Long.parseLong(files[i].getName());
}
//because some older versions used a different file name pattern before claim IDs were introduced,
//those files need to be "converted" by renaming them to a unique ID
catch(Exception e)
{
claimID = this.nextClaimID;
this.incrementNextClaimID();
File newFile = new File(claimDataFolderPath + File.separator + String.valueOf(this.nextClaimID));
files[i].renameTo(newFile);
files[i] = newFile;
}
BufferedReader inStream = null;
try
{
Claim topLevelClaim = null;
inStream = new BufferedReader(new FileReader(files[i].getAbsolutePath()));
String line = inStream.readLine();
while(line != null)
{
//skip any SUB:### lines from previous versions
if(line.toLowerCase().startsWith("sub:"))
{
line = inStream.readLine();
}
//skip any UUID lines from previous versions
Matcher match = uuidpattern.matcher(line.trim());
if(match.find())
{
line = inStream.readLine();
}
//first line is lesser boundary corner location
Location lesserBoundaryCorner = this.locationFromString(line);
//second line is greater boundary corner location
line = inStream.readLine();
Location greaterBoundaryCorner = this.locationFromString(line);
//third line is owner name
line = inStream.readLine();
String ownerName = line;
UUID ownerID = null;
if(ownerName.isEmpty() || ownerName.startsWith("--"))
{
ownerID = null; //administrative land claim or subdivision
}
else if(this.getSchemaVersion() == 0)
{
try
{
ownerID = UUIDFetcher.getUUIDOf(ownerName);
}
catch(Exception ex)
{
GriefPrevention.AddLogEntry("Couldn't resolve this name to a UUID: " + ownerName + ".");
GriefPrevention.AddLogEntry(" Converted land claim to administrative @ " + lesserBoundaryCorner.toString());
}
}
else
{
try
{
ownerID = UUID.fromString(ownerName);
}
catch(Exception ex)
{
GriefPrevention.AddLogEntry("Error - this is not a valid UUID: " + ownerName + ".");
GriefPrevention.AddLogEntry(" Converted land claim to administrative @ " + lesserBoundaryCorner.toString());
}
}
//fourth line is list of builders
line = inStream.readLine();
String [] builderNames = line.split(";");
builderNames = this.convertNameListToUUIDList(builderNames);
//fifth line is list of players who can access containers
line = inStream.readLine();
String [] containerNames = line.split(";");
containerNames = this.convertNameListToUUIDList(containerNames);
//sixth line is list of players who can use buttons and switches
line = inStream.readLine();
String [] accessorNames = line.split(";");
accessorNames = this.convertNameListToUUIDList(accessorNames);
//seventh line is list of players who can grant permissions
line = inStream.readLine();
if(line == null) line = "";
String [] managerNames = line.split(";");
managerNames = this.convertNameListToUUIDList(managerNames);
//skip any remaining extra lines, until the "===" string, indicating the end of this claim or subdivision
line = inStream.readLine();
while(line != null && !line.contains("==="))
line = inStream.readLine();
//build a claim instance from those data
//if this is the first claim loaded from this file, it's the top level claim
if(topLevelClaim == null)
{
//instantiate
topLevelClaim = new Claim(lesserBoundaryCorner, greaterBoundaryCorner, ownerID, builderNames, containerNames, accessorNames, managerNames, claimID);
topLevelClaim.modifiedDate = new Date(files[i].lastModified());
this.addClaim(topLevelClaim, false);
}
//otherwise there's already a top level claim, so this must be a subdivision of that top level claim
else
{
Claim subdivision = new Claim(lesserBoundaryCorner, greaterBoundaryCorner, null, builderNames, containerNames, accessorNames, managerNames, null);
subdivision.modifiedDate = new Date(files[i].lastModified());
subdivision.parent = topLevelClaim;
topLevelClaim.children.add(subdivision);
subdivision.inDataStore = true;
}
//move up to the first line in the next subdivision
line = inStream.readLine();
}
inStream.close();
}
//if there's any problem with the file's content, log an error message and skip it
catch(Exception e)
{
if(e.getMessage().contains("World not found"))
{
inStream.close();
files[i].delete();
}
else
{
StringWriter errors = new StringWriter();
e.printStackTrace(new PrintWriter(errors));
GriefPrevention.AddLogEntry(files[i].getName() + " " + errors.toString(), CustomLogEntryTypes.Exception);
}
}
try
{
if(inStream != null) inStream.close();
}
catch(IOException exception) {}
}
this.loadClaimData_Legacy(files);
}
else
{
this.loadClaimData(files);
}
super.initialize();
}
void loadClaimData_Legacy(File [] files) throws Exception
{
List<World> validWorlds = Bukkit.getServer().getWorlds();
for(int i = 0; i < files.length; i++)
{
if(files[i].isFile()) //avoids folders
{
//skip any file starting with an underscore, to avoid special files not representing land claims
if(files[i].getName().startsWith("_")) continue;
//the filename is the claim ID. try to parse it
long claimID;
try
{
claimID = Long.parseLong(files[i].getName());
}
//because some older versions used a different file name pattern before claim IDs were introduced,
//those files need to be "converted" by renaming them to a unique ID
catch(Exception e)
{
claimID = this.nextClaimID;
this.incrementNextClaimID();
File newFile = new File(claimDataFolderPath + File.separator + String.valueOf(this.nextClaimID));
files[i].renameTo(newFile);
files[i] = newFile;
}
BufferedReader inStream = null;
try
{
Claim topLevelClaim = null;
inStream = new BufferedReader(new FileReader(files[i].getAbsolutePath()));
String line = inStream.readLine();
while(line != null)
{
//skip any SUB:### lines from previous versions
if(line.toLowerCase().startsWith("sub:"))
{
line = inStream.readLine();
}
//skip any UUID lines from previous versions
Matcher match = uuidpattern.matcher(line.trim());
if(match.find())
{
line = inStream.readLine();
}
//first line is lesser boundary corner location
Location lesserBoundaryCorner = this.locationFromString(line, validWorlds);
//second line is greater boundary corner location
line = inStream.readLine();
Location greaterBoundaryCorner = this.locationFromString(line, validWorlds);
//third line is owner name
line = inStream.readLine();
String ownerName = line;
UUID ownerID = null;
if(ownerName.isEmpty() || ownerName.startsWith("--"))
{
ownerID = null; //administrative land claim or subdivision
}
else if(this.getSchemaVersion() == 0)
{
try
{
ownerID = UUIDFetcher.getUUIDOf(ownerName);
}
catch(Exception ex)
{
GriefPrevention.AddLogEntry("Couldn't resolve this name to a UUID: " + ownerName + ".");
GriefPrevention.AddLogEntry(" Converted land claim to administrative @ " + lesserBoundaryCorner.toString());
}
}
else
{
try
{
ownerID = UUID.fromString(ownerName);
}
catch(Exception ex)
{
GriefPrevention.AddLogEntry("Error - this is not a valid UUID: " + ownerName + ".");
GriefPrevention.AddLogEntry(" Converted land claim to administrative @ " + lesserBoundaryCorner.toString());
}
}
//fourth line is list of builders
line = inStream.readLine();
List<String> builderNames = Arrays.asList(line.split(";"));
builderNames = this.convertNameListToUUIDList(builderNames);
//fifth line is list of players who can access containers
line = inStream.readLine();
List<String> containerNames = Arrays.asList(line.split(";"));
containerNames = this.convertNameListToUUIDList(containerNames);
//sixth line is list of players who can use buttons and switches
line = inStream.readLine();
List<String> accessorNames = Arrays.asList(line.split(";"));
accessorNames = this.convertNameListToUUIDList(accessorNames);
//seventh line is list of players who can grant permissions
line = inStream.readLine();
if(line == null) line = "";
List<String> managerNames = Arrays.asList(line.split(";"));
managerNames = this.convertNameListToUUIDList(managerNames);
//skip any remaining extra lines, until the "===" string, indicating the end of this claim or subdivision
line = inStream.readLine();
while(line != null && !line.contains("==="))
line = inStream.readLine();
//build a claim instance from those data
//if this is the first claim loaded from this file, it's the top level claim
if(topLevelClaim == null)
{
//instantiate
topLevelClaim = new Claim(lesserBoundaryCorner, greaterBoundaryCorner, ownerID, builderNames, containerNames, accessorNames, managerNames, claimID);
topLevelClaim.modifiedDate = new Date(files[i].lastModified());
this.addClaim(topLevelClaim, false);
}
//otherwise there's already a top level claim, so this must be a subdivision of that top level claim
else
{
Claim subdivision = new Claim(lesserBoundaryCorner, greaterBoundaryCorner, null, builderNames, containerNames, accessorNames, managerNames, null);
subdivision.modifiedDate = new Date(files[i].lastModified());
subdivision.parent = topLevelClaim;
topLevelClaim.children.add(subdivision);
subdivision.inDataStore = true;
}
//move up to the first line in the next subdivision
line = inStream.readLine();
}
inStream.close();
}
//if there's any problem with the file's content, log an error message and skip it
catch(Exception e)
{
if(e.getMessage().contains("World not found"))
{
inStream.close();
files[i].delete();
}
else
{
StringWriter errors = new StringWriter();
e.printStackTrace(new PrintWriter(errors));
GriefPrevention.AddLogEntry(files[i].getName() + " " + errors.toString(), CustomLogEntryTypes.Exception);
}
}
try
{
if(inStream != null) inStream.close();
}
catch(IOException exception) {}
}
}
}
void loadClaimData(File [] files) throws Exception
{
ConcurrentHashMap<Claim, Long> orphans = new ConcurrentHashMap<Claim, Long>();
for(int i = 0; i < files.length; i++)
{
if(files[i].isFile()) //avoids folders
{
//skip any file starting with an underscore, to avoid special files not representing land claims
if(files[i].getName().startsWith("_")) continue;
//delete any which don't end in .yml
if(!files[i].getName().endsWith(".yml"))
{
files[i].delete();
continue;
}
//the filename is the claim ID. try to parse it
long claimID;
try
{
claimID = Long.parseLong(files[i].getName().split("\\.")[0]);
}
//because some older versions used a different file name pattern before claim IDs were introduced,
//those files need to be "converted" by renaming them to a unique ID
catch(Exception e)
{
claimID = this.nextClaimID;
this.incrementNextClaimID();
File newFile = new File(claimDataFolderPath + File.separator + String.valueOf(this.nextClaimID) + ".yml");
files[i].renameTo(newFile);
files[i] = newFile;
}
try
{
ArrayList<Long> out_parentID = new ArrayList<Long>(); //hacky output parameter
Claim claim = this.loadClaim(files[i], out_parentID, claimID);
if(out_parentID.size() == 0 || out_parentID.get(0) == -1)
{
this.addClaim(claim, false);
}
else
{
orphans.put(claim, out_parentID.get(0));
}
}
//if there's any problem with the file's content, log an error message and skip it
catch(Exception e)
{
if(e.getMessage() != null && e.getMessage().contains("World not found"))
{
files[i].delete();
}
else
{
StringWriter errors = new StringWriter();
e.printStackTrace(new PrintWriter(errors));
GriefPrevention.AddLogEntry(files[i].getName() + " " + errors.toString(), CustomLogEntryTypes.Exception);
}
}
}
}
//link children to parents
for(Claim child : orphans.keySet())
{
Claim parent = this.getClaim(orphans.get(child));
if(parent != null)
{
child.parent = parent;
this.addClaim(child, false);
}
}
}
Claim loadClaim(File file, ArrayList<Long> out_parentID, long claimID) throws IOException, InvalidConfigurationException, Exception
{
List<String> lines = Files.readLines(file, Charset.forName("UTF-8"));
StringBuilder builder = new StringBuilder();
for(String line : lines)
{
builder.append(line).append('\n');
}
return this.loadClaim(builder.toString(), out_parentID, file.lastModified(), claimID, Bukkit.getServer().getWorlds());
}
Claim loadClaim(String input, ArrayList<Long> out_parentID, long lastModifiedDate, long claimID, List<World> validWorlds) throws InvalidConfigurationException, Exception
{
Claim claim = null;
YamlConfiguration yaml = new YamlConfiguration();
yaml.loadFromString(input);
//boundaries
Location lesserBoundaryCorner = this.locationFromString(yaml.getString("Lesser Boundary Corner"), validWorlds);
Location greaterBoundaryCorner = this.locationFromString(yaml.getString("Greater Boundary Corner"), validWorlds);
//owner
String ownerIdentifier = yaml.getString("Owner");
UUID ownerID = null;
if(!ownerIdentifier.isEmpty())
{
try
{
ownerID = UUID.fromString(ownerIdentifier);
}
catch(Exception ex)
{
GriefPrevention.AddLogEntry("Error - this is not a valid UUID: " + ownerIdentifier + ".");
GriefPrevention.AddLogEntry(" Converted land claim to administrative @ " + lesserBoundaryCorner.toString());
}
}
List<String> builders = yaml.getStringList("Builders");
List<String> containers = yaml.getStringList("Containers");
List<String> accessors = yaml.getStringList("Accessors");
List<String> managers = yaml.getStringList("Managers");
out_parentID.add(yaml.getLong("Parent Claim ID", -1L));
//instantiate
claim = new Claim(lesserBoundaryCorner, greaterBoundaryCorner, ownerID, builders, containers, accessors, managers, claimID);
claim.modifiedDate = new Date(lastModifiedDate);
claim.id = claimID;
return claim;
}
String getYamlForClaim(Claim claim)
{
YamlConfiguration yaml = new YamlConfiguration();
//boundaries
yaml.set("Lesser Boundary Corner", this.locationToString(claim.lesserBoundaryCorner));
yaml.set("Greater Boundary Corner", this.locationToString(claim.greaterBoundaryCorner));
//owner
String ownerID = "";
if(claim.ownerID != null) ownerID = claim.ownerID.toString();
yaml.set("Owner", ownerID);
ArrayList<String> builders = new ArrayList<String>();
ArrayList<String> containers = new ArrayList<String>();
ArrayList<String> accessors = new ArrayList<String>();
ArrayList<String> managers = new ArrayList<String>();
claim.getPermissions(builders, containers, accessors, managers);
yaml.set("Builders", builders);
yaml.set("Containers", containers);
yaml.set("Accessors", accessors);
yaml.set("Managers", managers);
Long parentID = -1L;
if(claim.parent != null)
{
parentID = claim.parent.id;
}
yaml.set("Parent Claim ID", parentID);
return yaml.saveToString();
}
@Override
synchronized void writeClaimToStorage(Claim claim)
{
String claimID = String.valueOf(claim.id);
BufferedWriter outStream = null;
String yaml = this.getYamlForClaim(claim);
try
{
//open the claim's file
File claimFile = new File(claimDataFolderPath + File.separator + claimID);
File claimFile = new File(claimDataFolderPath + File.separator + claimID + ".yml");
claimFile.createNewFile();
outStream = new BufferedWriter(new FileWriter(claimFile));
//write top level claim data to the file
this.writeClaimData(claim, outStream);
//for each subdivision
for(int i = 0; i < claim.children.size(); i++)
{
//write the subdivision's data to the file
this.writeClaimData(claim.children.get(i), outStream);
}
Files.write(yaml.getBytes("UTF-8"), claimFile);
}
//if any problem, log it
@ -392,80 +571,16 @@ public class FlatFileDataStore extends DataStore
e.printStackTrace(new PrintWriter(errors));
GriefPrevention.AddLogEntry(claimID + " " + errors.toString(), CustomLogEntryTypes.Exception);
}
//close the file
try
{
if(outStream != null) outStream.close();
}
catch(IOException exception) {}
}
//actually writes claim data to an output stream
synchronized private void writeClaimData(Claim claim, BufferedWriter outStream) throws IOException
{
//first line is lesser boundary corner location
outStream.write(this.locationToString(claim.getLesserBoundaryCorner()));
outStream.newLine();
//second line is greater boundary corner location
outStream.write(this.locationToString(claim.getGreaterBoundaryCorner()));
outStream.newLine();
//third line is owner name
String lineToWrite = "";
if(claim.ownerID != null) lineToWrite = claim.ownerID.toString();
outStream.write(lineToWrite);
outStream.newLine();
ArrayList<String> builders = new ArrayList<String>();
ArrayList<String> containers = new ArrayList<String>();
ArrayList<String> accessors = new ArrayList<String>();
ArrayList<String> managers = new ArrayList<String>();
claim.getPermissions(builders, containers, accessors, managers);
//fourth line is list of players with build permission
for(int i = 0; i < builders.size(); i++)
{
outStream.write(builders.get(i) + ";");
}
outStream.newLine();
//fifth line is list of players with container permission
for(int i = 0; i < containers.size(); i++)
{
outStream.write(containers.get(i) + ";");
}
outStream.newLine();
//sixth line is list of players with access permission
for(int i = 0; i < accessors.size(); i++)
{
outStream.write(accessors.get(i) + ";");
}
outStream.newLine();
//seventh line is list of players who may grant permissions for others
for(int i = 0; i < managers.size(); i++)
{
outStream.write(managers.get(i) + ";");
}
outStream.newLine();
//cap each claim with "=========="
outStream.write("==========");
outStream.newLine();
}
//deletes a top level claim from the file system
//deletes a claim from the file system
@Override
synchronized void deleteClaimFromSecondaryStorage(Claim claim)
{
String claimID = String.valueOf(claim.id);
//remove from disk
File claimFile = new File(claimDataFolderPath + File.separator + claimID);
File claimFile = new File(claimDataFolderPath + File.separator + claimID + ".yml");
if(claimFile.exists() && !claimFile.delete())
{
GriefPrevention.AddLogEntry("Error: Unable to delete claim file \"" + claimFile.getAbsolutePath() + "\".");
@ -671,6 +786,10 @@ public class FlatFileDataStore extends DataStore
{
Claim claim = this.claims.get(i);
databaseStore.addClaim(claim, true);
for(Claim child : claim.children)
{
databaseStore.addClaim(child, true);
}
}
//migrate groups

View File

@ -298,6 +298,11 @@ public class PlayerData
for(int i = 0; i < dataStore.claims.size(); i++)
{
Claim claim = dataStore.claims.get(i);
if(!claim.inDataStore)
{
dataStore.claims.remove(i--);
continue;
}
if(playerID.equals(claim.ownerID))
{
this.claims.add(claim);
@ -317,6 +322,7 @@ public class PlayerData
GriefPrevention.AddLogEntry("Total blocks: " + totalBlocks + " Total claimed area: " + totalClaimsArea, CustomLogEntryTypes.Debug, true);
for(Claim claim : this.claims)
{
if(!claim.inDataStore) continue;
GriefPrevention.AddLogEntry(
GriefPrevention.getfriendlyLocationString(claim.getLesserBoundaryCorner()) + " // "
+ GriefPrevention.getfriendlyLocationString(claim.getGreaterBoundaryCorner()) + " = "
@ -341,6 +347,14 @@ public class PlayerData
}
}
for(int i = 0; i < this.claims.size(); i++)
{
if(!claims.get(i).inDataStore)
{
claims.remove(i--);
}
}
return claims;
}

View File

@ -2130,7 +2130,7 @@ class PlayerEventHandler implements Listener
Claim newClaim = new Claim(
new Location(oldClaim.getLesserBoundaryCorner().getWorld(), newx1, newy1, newz1),
new Location(oldClaim.getLesserBoundaryCorner().getWorld(), newx2, newy2, newz2),
null, new String[]{}, new String[]{}, new String[]{}, new String[]{}, null);
null, new ArrayList<String>(), new ArrayList<String>(), new ArrayList<String>(), new ArrayList<String>(), null);
//if the new claim is smaller
if(!newClaim.contains(oldClaim.getLesserBoundaryCorner(), true, false) || !newClaim.contains(oldClaim.getGreaterBoundaryCorner(), true, false))
@ -2353,7 +2353,7 @@ class PlayerEventHandler implements Listener
GriefPrevention.sendMessage(player, TextMode.Instr, Messages.ClaimStart);
//show him where he's working
Visualization visualization = Visualization.FromClaim(new Claim(clickedBlock.getLocation(), clickedBlock.getLocation(), null, new String[]{}, new String[]{}, new String[]{}, new String[]{}, null), clickedBlock.getY(), VisualizationType.RestoreNature, player.getLocation());
Visualization visualization = Visualization.FromClaim(new Claim(clickedBlock.getLocation(), clickedBlock.getLocation(), null, new ArrayList<String>(), new ArrayList<String>(), new ArrayList<String>(), new ArrayList<String>(), null), clickedBlock.getY(), VisualizationType.RestoreNature, player.getLocation());
Visualization.Apply(player, visualization);
}

View File

@ -18,6 +18,8 @@
package me.ryanhamshire.GriefPrevention;
import java.util.ArrayList;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
@ -112,7 +114,7 @@ class RestoreNatureExecutionTask implements Runnable
//show visualization to player who started the restoration
if(player != null)
{
Claim claim = new Claim(lesserCorner, greaterCorner, null, new String[] {}, new String[] {}, new String[] {}, new String[] {}, null);
Claim claim = new Claim(lesserCorner, greaterCorner, null, new ArrayList<String>(), new ArrayList<String>(), new ArrayList<String>(), new ArrayList<String>(), null);
Visualization visualization = Visualization.FromClaim(claim, player.getLocation().getBlockY(), VisualizationType.RestoreNature, player.getLocation());
Visualization.Apply(player, visualization);
}

View File

@ -105,7 +105,9 @@ public class Visualization
//add subdivisions first
for(int i = 0; i < claim.children.size(); i++)
{
visualization.addClaimElements(claim.children.get(i), height, VisualizationType.Subdivision, locality);
Claim child = claim.children.get(i);
if(!child.inDataStore) continue;
visualization.addClaimElements(child, height, VisualizationType.Subdivision, locality);
}
//special visualization for administrative land claims