Performance: Claim Lookup

Major perf improvement for claim search.
This commit is contained in:
ryanhamshire 2014-10-04 19:48:03 -07:00
parent 195afef302
commit 04d628b01e
7 changed files with 106 additions and 90 deletions

View File

@ -18,13 +18,6 @@
package me.ryanhamshire.GriefPrevention;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.*;
import org.bukkit.*;
@ -735,25 +728,21 @@ public class Claim
if(maxEntities == 0) return GriefPrevention.instance.dataStore.getMessage(Messages.ClaimTooSmallForEntities);
//count current entities (ignoring players)
Chunk lesserChunk = this.getLesserBoundaryCorner().getChunk();
Chunk greaterChunk = this.getGreaterBoundaryCorner().getChunk();
int totalEntities = 0;
for(int x = lesserChunk.getX(); x <= greaterChunk.getX(); x++)
for(int z = lesserChunk.getZ(); z <= greaterChunk.getZ(); z++)
ArrayList<Chunk> chunks = this.getChunks();
for(Chunk chunk : chunks)
{
Entity [] entities = chunk.getEntities();
for(int i = 0; i < entities.length; i++)
{
Chunk chunk = lesserChunk.getWorld().getChunkAt(x, z);
Entity [] entities = chunk.getEntities();
for(int i = 0; i < entities.length; i++)
Entity entity = entities[i];
if(!(entity instanceof Player) && this.contains(entity.getLocation(), false, false))
{
Entity entity = entities[i];
if(!(entity instanceof Player) && this.contains(entity.getLocation(), false, false))
{
totalEntities++;
if(totalEntities > maxEntities) entity.remove();
}
totalEntities++;
if(totalEntities > maxEntities) entity.remove();
}
}
}
if(totalEntities > maxEntities) return GriefPrevention.instance.dataStore.getMessage(Messages.TooManyEntitiesInClaim);
@ -833,4 +822,23 @@ public class Claim
return (long)score;
}
public ArrayList<Chunk> getChunks()
{
ArrayList<Chunk> chunks = new ArrayList<Chunk>();
World world = this.getLesserBoundaryCorner().getWorld();
Chunk lesserChunk = this.getLesserBoundaryCorner().getChunk();
Chunk greaterChunk = this.getGreaterBoundaryCorner().getChunk();
for(int x = lesserChunk.getX(); x <= greaterChunk.getX(); x++)
{
for(int z = lesserChunk.getZ(); z <= greaterChunk.getZ(); z++)
{
chunks.add(world.getChunkAt(x, z));
}
}
return chunks;
}
}

View File

@ -40,6 +40,7 @@ public abstract class DataStore
//in-memory cache for claim data
ArrayList<Claim> claims = new ArrayList<Claim>();
ConcurrentHashMap<String, ArrayList<Claim>> chunksToClaimsMap = new ConcurrentHashMap<String, ArrayList<Claim>>();
//in-memory cache for messages
private String [] messages;
@ -197,7 +198,7 @@ public abstract class DataStore
}
//adds a claim to the datastore, making it an effective claim
synchronized void addClaim(Claim newClaim)
synchronized void addClaim(Claim newClaim, boolean writeToStorage)
{
//subdivisions are easy
if(newClaim.parent != null)
@ -209,12 +210,21 @@ public abstract class DataStore
}
//add it and mark it as added
int j = 0;
while(j < this.claims.size() && !this.claims.get(j).greaterThan(newClaim)) j++;
if(j < this.claims.size())
this.claims.add(j, newClaim);
else
this.claims.add(this.claims.size(), newClaim);
this.claims.add(newClaim);
ArrayList<Chunk> chunks = newClaim.getChunks();
for(Chunk chunk : chunks)
{
String chunkID = this.getChunkString(chunk);
ArrayList<Claim> claimsInChunk = this.chunksToClaimsMap.get(chunkID);
if(claimsInChunk == null)
{
claimsInChunk = new ArrayList<Claim>();
this.chunksToClaimsMap.put(chunkID, claimsInChunk);
}
claimsInChunk.add(newClaim);
}
newClaim.inDataStore = true;
//except for administrative claims (which have no owner), update the owner's playerData with the new claim
@ -226,7 +236,10 @@ public abstract class DataStore
}
//make sure the claim is saved to disk
this.saveClaim(newClaim);
if(writeToStorage)
{
this.saveClaim(newClaim);
}
}
//turns a location into a string, useful in data storage
@ -360,6 +373,21 @@ public abstract class DataStore
}
break;
}
ArrayList<Chunk> chunks = claim.getChunks();
for(Chunk chunk : chunks)
{
String chunkID = this.getChunkString(chunk);
ArrayList<Claim> claimsInChunk = this.chunksToClaimsMap.get(chunkID);
for(int j = 0; j < claimsInChunk.size(); j++)
{
if(claimsInChunk.get(j).id.equals(claim.id))
{
claimsInChunk.remove(j);
break;
}
}
}
}
//remove from secondary storage
@ -391,40 +419,41 @@ public abstract class DataStore
//check cachedClaim guess first. if it's in the datastore and the location is inside it, we're done
if(cachedClaim != null && cachedClaim.inDataStore && cachedClaim.contains(location, ignoreHeight, true)) return cachedClaim;
//the claims list is ordered by greater boundary corner
//create a temporary "fake" claim in memory for comparison purposes
Claim tempClaim = new Claim();
tempClaim.lesserBoundaryCorner = location;
//find a top level claim
String chunkID = this.getChunkString(location.getChunk());
ArrayList<Claim> claimsInChunk = this.chunksToClaimsMap.get(chunkID);
if(claimsInChunk == null) return null;
//otherwise, search all existing claims until we find the right claim
for(int i = 0; i < this.claims.size(); i++)
for(Claim claim : claimsInChunk)
{
Claim claim = this.claims.get(i);
//if we reach a claim which is greater than the temp claim created above, there's definitely no claim
//in the collection which includes our location
if(claim.greaterThan(tempClaim)) return null;
//find a top level claim
if(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;
}
return claim;
}
if(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;
}
return claim;
}
}
//if no claim found, return null
return null;
}
//creates a claim.
//gets a unique, persistent identifier string for a chunk
private String getChunkString(Chunk chunk)
{
return String.format("%s;%d;%d",
chunk.getWorld().getName(),
chunk.getX(),
chunk.getZ());
}
//creates a claim.
//if the new claim would overlap an existing claim, returns a failure along with a reference to the existing claim
//otherwise, returns a success along with a reference to the new claim
//use ownerName == "" for administrative claims
@ -518,7 +547,7 @@ public abstract class DataStore
}
//otherwise add this new claim to the data store to make it effective
this.addClaim(newClaim);
this.addClaim(newClaim, true);
//then return success along with reference to new claim
result.succeeded = true;
@ -557,7 +586,7 @@ public abstract class DataStore
}
//save changes
this.addClaim(claim);
this.addClaim(claim, true);
}
//starts a siege on a claim
@ -833,7 +862,7 @@ public abstract class DataStore
else
{
//put original claim back
this.addClaim(claim);
this.addClaim(claim, true);
}
return result;

View File

@ -212,13 +212,7 @@ public class DatabaseDataStore extends DataStore
//otherwise, add this claim to the claims collection
else
{
int j = 0;
while(j < this.claims.size() && !this.claims.get(j).greaterThan(topLevelClaim)) j++;
if(j < this.claims.size())
this.claims.add(j, topLevelClaim);
else
this.claims.add(this.claims.size(), topLevelClaim);
topLevelClaim.inDataStore = true;
this.addClaim(topLevelClaim, false);
}
//look for any subdivisions for this claim

View File

@ -265,13 +265,7 @@ public class FlatFileDataStore extends DataStore
else
{
topLevelClaim.modifiedDate = new Date(files[i].lastModified());
int j = 0;
while(j < this.claims.size() && !this.claims.get(j).greaterThan(topLevelClaim)) j++;
if(j < this.claims.size())
this.claims.add(j, topLevelClaim);
else
this.claims.add(this.claims.size(), topLevelClaim);
topLevelClaim.inDataStore = true;
this.addClaim(topLevelClaim, false);
}
}
@ -662,7 +656,7 @@ public class FlatFileDataStore extends DataStore
for(int i = 0; i < this.claims.size(); i++)
{
Claim claim = this.claims.get(i);
databaseStore.addClaim(claim);
databaseStore.addClaim(claim, true);
}
//migrate groups

View File

@ -2660,15 +2660,11 @@ public class GriefPrevention extends JavaPlugin
//it's too expensive to do this for huge claims
if(claim.getArea() > 10000) return;
Chunk lesserChunk = claim.getLesserBoundaryCorner().getChunk();
Chunk greaterChunk = claim.getGreaterBoundaryCorner().getChunk();
for(int x = lesserChunk.getX(); x <= greaterChunk.getX(); x++)
for(int z = lesserChunk.getZ(); z <= greaterChunk.getZ(); z++)
{
Chunk chunk = lesserChunk.getWorld().getChunkAt(x, z);
this.restoreChunk(chunk, this.getSeaLevel(chunk.getWorld()) - 15, false, delayInTicks, null);
}
ArrayList<Chunk> chunks = claim.getChunks();
for(Chunk chunk : chunks)
{
this.restoreChunk(chunk, this.getSeaLevel(chunk.getWorld()) - 15, false, delayInTicks, null);
}
}
public void restoreChunk(Chunk chunk, int miny, boolean aggressiveMode, long delayInTicks, Player playerReceivingVisualization)

View File

@ -1071,6 +1071,7 @@ class PlayerEventHandler implements Listener
if( GriefPrevention.instance.config_claims_preventTheft && (
event.getAction() == Action.RIGHT_CLICK_BLOCK && (
clickedBlock.getState() instanceof InventoryHolder ||
clickedBlock.getType() == Material.ANVIL ||
GriefPrevention.instance.config_mods_containerTrustIds.Contains(new MaterialInfo(clickedBlock.getTypeId(), clickedBlock.getData(), null)))))
{
//block container use while under siege, so players can't hide items from attackers

View File

@ -48,7 +48,7 @@ public class Visualization
//if he's online, create a task to send him the visualization in about half a second
if(player.isOnline())
{
GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, new VisualizationApplicationTask(player, playerData, visualization), 10L);
GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, new VisualizationApplicationTask(player, playerData, visualization), 1L);
}
}
@ -215,13 +215,7 @@ public class Visualization
private static boolean isTransparent(Block block)
{
return ( block.getType() == Material.AIR ||
block.getType() == Material.LONG_GRASS ||
block.getType() == Material.FENCE ||
block.getType() == Material.LEAVES ||
block.getType() == Material.RED_ROSE ||
block.getType() == Material.CHEST ||
block.getType() == Material.TORCH ||
block.getType() == Material.VINE ||
block.getType() == Material.YELLOW_FLOWER );
block.getType().isTransparent());
}
}