Skip to content

Commit

Permalink
Add chunk_id column for faster reinforcement loads + redstonePower op…
Browse files Browse the repository at this point in the history
…timization
  • Loading branch information
erocs committed Nov 11, 2012
1 parent 1fdf82a commit 7006533
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 69 deletions.
7 changes: 1 addition & 6 deletions src/com/untamedears/citadel/dao/CitadelCachingDao.java
Expand Up @@ -78,10 +78,6 @@


public class CitadelCachingDao extends CitadelDao {
public static String MakeChunkId( Chunk chunk ) {
return String.format("%s.%d.%d", chunk.getWorld().getName(), chunk.getX(), chunk.getZ());
}

HashMap<String, ChunkCache> cachesByChunkId;
PriorityQueue<ChunkCache> cachesByTime;
long maxAge;
Expand Down Expand Up @@ -324,8 +320,7 @@ public ChunkCache( CitadelCachingDao dao, Chunk chunk ){
this.dao = dao;
this.chunkId = CitadelCachingDao.MakeChunkId(chunk);
this.pendingDbUpdate = new TreeSet<PlayerReinforcement>();
this.cache = new TreeSet<IReinforcement>(
findReinforcementsInChunk(chunk));
this.cache = dao.findReinforcementsInChunk(chunk);
this.lastAccessed = System.currentTimeMillis();
}

Expand Down
48 changes: 34 additions & 14 deletions src/com/untamedears/citadel/dao/CitadelDao.java
Expand Up @@ -46,6 +46,10 @@ public class CitadelDao extends MyDatabase {
private String sqlLogDirectory;
private boolean sqlEnableLog;

public static String MakeChunkId(Chunk chunk) {
return String.format("%s:%d:%d", chunk.getWorld().getName(), chunk.getX(), chunk.getZ());
}

public CitadelDao(JavaPlugin plugin) {
super(plugin);

Expand Down Expand Up @@ -186,28 +190,24 @@ public IReinforcement findReinforcement(Location location) {
.setParameter("world", location.getWorld().getName())
.findUnique();
}

public Set<IReinforcement> findReinforcementsInChunk(Chunk c){
//The minus ones are intentional. Think about fenceposts if you aren't sure why.
Block minBlock = c.getBlock(0, 0, 0);
int xlo = minBlock.getX();
int zlo = minBlock.getZ();

public TreeSet<IReinforcement> findReinforcementsInChunk(Chunk c){
String chunkId = MakeChunkId(c);
Set<PlayerReinforcement> result = getDatabase()
.createQuery(
PlayerReinforcement.class,
"find reinforcement where x >= :xlo and x <= :xhi and z >= :zlo and z <= :zhi and world = :world")
.setParameter("xlo", xlo)
.setParameter("xhi", xlo+CHUNK_SIZE-1)
.setParameter("zlo", zlo)
.setParameter("zhi", zlo+CHUNK_SIZE-1)
.setParameter("world", c.getWorld().getName())
"find reinforcement where chunk_id = :chunk_id")
.setParameter("chunk_id", chunkId)
.findSet();
// This manually resets each reinforcement DB state. The ORM calls the
// object's property setter methods which incorrectly flags the object
// for SAVE.
for (PlayerReinforcement pr : result) {
pr.setDbAction(DbUpdateAction.NONE);
}
return new TreeSet<IReinforcement>(result);
}

public void moveReinforcements(String from, String target){
SqlUpdate update = getDatabase().createSqlUpdate("UPDATE reinforcement SET name = :target, security_level = 1" +
" WHERE name = :from")
Expand Down Expand Up @@ -286,7 +286,7 @@ public void removeAllModeratorsFromGroup(String groupName){
.setParameter("groupName", groupName);
getDatabase().execute(update);
}

public void updateDatabase(){
//this for when Citadel 2.0 is loaded after an older version of Citadel was previously installed
SqlUpdate createMemberTable = getDatabase().createSqlUpdate
Expand Down Expand Up @@ -316,6 +316,26 @@ public void updateDatabase(){
} catch(PersistenceException e){
//column already exists
}

try {
// The initial add column statement is our indicator if the DB
// needs this reconstruction.
SqlUpdate addReinforcementChunkId = getDatabase().createSqlUpdate(
"ALTER TABLE reinforcement ADD COLUMN chunk_id VARCHAR(255)");
getDatabase().execute(addReinforcementChunkId);

addReinforcementChunkId = getDatabase().createSqlUpdate(
"UPDATE reinforcement SET chunk_id = " +
"CONCAT(world, ':', CONVERT(IF(x >= 0, x, x - 15) DIV 16, CHAR), ':'," +
"CONVERT(IF(z >= 0, z, z - 15) DIV 16, CHAR))");
getDatabase().execute(addReinforcementChunkId);

addReinforcementChunkId = getDatabase().createSqlUpdate(
"ALTER TABLE reinforcement ADD INDEX ix_chunk_id (chunk_id)");
getDatabase().execute(addReinforcementChunkId);
} catch(PersistenceException e){
//column already exists
}
}

protected void prepareDatabaseAdditionalConfig(DataSourceConfig dataSourceConfig, ServerConfig serverConfig) {
Expand Down
10 changes: 10 additions & 0 deletions src/com/untamedears/citadel/entity/PlayerReinforcement.java
Expand Up @@ -37,6 +37,7 @@ public class PlayerReinforcement implements
private int materialId;
private int durability;
private SecurityLevel securityLevel;
private String chunkId;
// @Transient == not persisted in the DB
@Transient private DbUpdateAction dbAction;

Expand All @@ -63,6 +64,7 @@ public PlayerReinforcement(
this.durability = material.getStrength();
this.owner = owner;
this.securityLevel = securityLevel;
this.chunkId = this.id.getChunkId();
this.dbAction = DbUpdateAction.INSERT;
}

Expand Down Expand Up @@ -123,6 +125,14 @@ public void setDurability(int durability) {
this.durability = durability;
}

public String getChunkId() {
return this.chunkId;
}

public void setChunkId(String id) {
this.chunkId = id;
}

public SecurityLevel getSecurityLevel() {
return securityLevel;
}
Expand Down
18 changes: 18 additions & 0 deletions src/com/untamedears/citadel/entity/ReinforcementKey.java
Expand Up @@ -65,6 +65,24 @@ public void setWorld(String world) {
this.world = world;
}

public String getChunkId() {
// See CitadelDao.MakeChunkId
int chunkX;
if (this.x < 0) {
chunkX = (this.x - 15) / 16;
} else {
chunkX = this.x / 16;
}
int chunkZ;
if (this.z < 0) {
chunkZ = (this.z - 15) / 16;
} else {
chunkZ = this.z / 16;
}
return String.format(
"%s:%d:%d", this.world, chunkX, chunkZ);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down
129 changes: 80 additions & 49 deletions src/com/untamedears/citadel/listener/BlockListener.java
Expand Up @@ -34,6 +34,7 @@
import org.bukkit.material.Openable;
import org.bukkit.material.PistonBaseMaterial;
import org.bukkit.Effect;
import org.bukkit.World;

import com.untamedears.citadel.Citadel;
import com.untamedears.citadel.PlacementMode;
Expand Down Expand Up @@ -228,55 +229,85 @@ public void blockPhysics(BlockPhysicsEvent bpe) {

@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void redstonePower(BlockRedstoneEvent bre) {
try {
Block block = bre.getBlock();

if (!(block.getState().getData() instanceof Openable)) return;

Openable openable = (Openable) block.getState().getData();
if (openable.isOpen()) return;

IReinforcement generic_reinforcement = Citadel.getReinforcementManager().getReinforcement(block);
if (generic_reinforcement == null ||
!(generic_reinforcement instanceof PlayerReinforcement))
return;

PlayerReinforcement reinforcement = (PlayerReinforcement)generic_reinforcement;
if (reinforcement.getSecurityLevel() == SecurityLevel.PUBLIC)
return;

//Set<Player> onlinePlayers = new HashSet<Player>(Citadel.getMemberManager().getOnlinePlayers());
Player[] onlinePlayers = Citadel.getPlugin().getServer().getOnlinePlayers();
boolean isAuthorizedPlayerNear = false;
try {
double redstoneDistance = Citadel.getConfigManager().getRedstoneDistance();
for(Player player : onlinePlayers){
if(reinforcement.isAccessible(player)){
Location playerLocation = player.getLocation();
Location blockLocation = block.getLocation();
if(playerLocation.getWorld() == blockLocation.getWorld()){
double distanceSquared = playerLocation.distance(blockLocation);
if(distanceSquared <= redstoneDistance){
isAuthorizedPlayerNear = true;
break;
}
}
}
}
} catch (ConcurrentModificationException e){
Citadel.warning("ConcurrentModificationException at redstonePower() in BlockListener");
}

if (!isAuthorizedPlayerNear) {
Citadel.info("Prevented redstone from opening reinforcement %s at "
+ reinforcement.getBlock().getLocation().toString());
bre.setNewCurrent(bre.getOldCurrent());
}

}
catch(Exception e)
{
Citadel.printStackTrace(e);
// This currently only protects against reinforced openable objects,
// like doors, from being opened by unauthorizied players.
try {
// NewCurrent <= 0 means the redstone wire is turning off, so the
// container is closing. Closing is good so just return. This also
// shaves off some time when dealing with sand generators.
// OldCurrent > 0 means that the wire was already on, thus the
// container was already open by an authorized player. Now it's
// either staying open or closing. Just return.
if (bre.getNewCurrent() <= 0 || bre.getOldCurrent() > 0) {
return;
}
Block block = bre.getBlock();
MaterialData blockData = block.getState().getData();
if (!(blockData instanceof Openable)) {
return;
}
Openable openable = (Openable)blockData;
if (openable.isOpen()) {
return;
}
IReinforcement generic_reinforcement =
Citadel.getReinforcementManager().getReinforcement(block);
if (generic_reinforcement == null ||
!(generic_reinforcement instanceof PlayerReinforcement)) {
return;
}
PlayerReinforcement reinforcement =
(PlayerReinforcement)generic_reinforcement;
if (reinforcement.getSecurityLevel() == SecurityLevel.PUBLIC) {
return;
}
double redstoneDistance = Citadel.getConfigManager().getRedstoneDistance();
Location blockLocation = block.getLocation();
double min_x = blockLocation.getX() - redstoneDistance;
double min_z = blockLocation.getZ() - redstoneDistance;
double max_x = blockLocation.getX() + redstoneDistance;
double max_z = blockLocation.getZ() + redstoneDistance;
World blockWorld = blockLocation.getWorld();
//Set<Player> onlinePlayers = new HashSet<Player>(Citadel.getMemberManager().getOnlinePlayers());
Player[] onlinePlayers = Citadel.getPlugin().getServer().getOnlinePlayers();
boolean isAuthorizedPlayerNear = false;
try {
for (Player player : onlinePlayers) {
if (player.isDead()) {
continue;
}
Location playerLocation = player.getLocation();
double player_x = playerLocation.getX();
double player_z = playerLocation.getZ();
// Simple bounding box check to quickly rule out Players
// before doing the more expensive playerLocation.distance
if (player_x < min_x || player_x > max_x ||
player_z < min_z || player_z > max_z) {
continue;
}
if (playerLocation.getWorld() != blockWorld) {
continue;
}
if (!reinforcement.isAccessible(player)) {
continue;
}
double distanceSquared =
playerLocation.distance(blockLocation);
if (distanceSquared <= redstoneDistance) {
isAuthorizedPlayerNear = true;
break;
}
}
} catch (ConcurrentModificationException e) {
Citadel.warning("ConcurrentModificationException at redstonePower() in BlockListener");
}
if (!isAuthorizedPlayerNear) {
Citadel.info("Prevented redstone from opening reinforcement at "
+ reinforcement.getBlock().getLocation().toString());
bre.setNewCurrent(bre.getOldCurrent());
}
} catch(Exception e) {
Citadel.printStackTrace(e);
}
}
}

0 comments on commit 7006533

Please sign in to comment.