reformat code

- Lots of tabs to spaces going on
- That's a lot of changes!

#63
This commit is contained in:
RoboMWM 2020-06-08 21:57:55 -07:00
parent bc05440617
commit 40f554d386
63 changed files with 12745 additions and 12750 deletions

View File

@ -1,13 +1,13 @@
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import java.util.ArrayList;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChunkSnapshot; import org.bukkit.ChunkSnapshot;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World.Environment; import org.bukkit.World.Environment;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import java.util.ArrayList;
//automatically extends a claim downward based on block types detected //automatically extends a claim downward based on block types detected
class AutoExtendClaimTask implements Runnable class AutoExtendClaimTask implements Runnable
{ {
@ -26,7 +26,7 @@ class AutoExtendClaimTask implements Runnable
public void run() public void run()
{ {
int newY = this.getLowestBuiltY(); int newY = this.getLowestBuiltY();
if(newY < this.claim.getLesserBoundaryCorner().getBlockY()) if (newY < this.claim.getLesserBoundaryCorner().getBlockY())
{ {
Bukkit.getScheduler().runTask(GriefPrevention.instance, new ExecuteExtendClaimTask(claim, newY)); Bukkit.getScheduler().runTask(GriefPrevention.instance, new ExecuteExtendClaimTask(claim, newY));
} }
@ -35,73 +35,73 @@ class AutoExtendClaimTask implements Runnable
private int getLowestBuiltY() private int getLowestBuiltY()
{ {
int y = this.claim.getLesserBoundaryCorner().getBlockY(); int y = this.claim.getLesserBoundaryCorner().getBlockY();
if(this.yTooSmall(y)) return y; if (this.yTooSmall(y)) return y;
try try
{ {
for(ChunkSnapshot chunk : this.chunks) for (ChunkSnapshot chunk : this.chunks)
{ {
Biome biome = chunk.getBiome(0, 0); Biome biome = chunk.getBiome(0, 0);
ArrayList<Material> playerBlockIDs = RestoreNatureProcessingTask.getPlayerBlocks(this.worldType, biome); ArrayList<Material> playerBlockIDs = RestoreNatureProcessingTask.getPlayerBlocks(this.worldType, biome);
boolean ychanged = true; boolean ychanged = true;
while(!this.yTooSmall(y) && ychanged) while (!this.yTooSmall(y) && ychanged)
{ {
ychanged = false; ychanged = false;
for(int x = 0; x < 16; x++) for (int x = 0; x < 16; x++)
{ {
for(int z = 0; z < 16; z++) for (int z = 0; z < 16; z++)
{ {
Material blockType = chunk.getBlockType(x, y, z); Material blockType = chunk.getBlockType(x, y, z);
while(!this.yTooSmall(y) && playerBlockIDs.contains(blockType)) while (!this.yTooSmall(y) && playerBlockIDs.contains(blockType))
{ {
ychanged = true; ychanged = true;
blockType = chunk.getBlockType(x, --y, z); blockType = chunk.getBlockType(x, --y, z);
} }
if(this.yTooSmall(y)) return y; if (this.yTooSmall(y)) return y;
} }
} }
} }
if(this.yTooSmall(y)) return y; if (this.yTooSmall(y)) return y;
} }
} }
catch (NoSuchMethodError e) catch (NoSuchMethodError e)
{ {
GriefPrevention.instance.getLogger().severe("You are running an outdated build of Craftbukkit/Spigot/Paper. Please update."); GriefPrevention.instance.getLogger().severe("You are running an outdated build of Craftbukkit/Spigot/Paper. Please update.");
for(ChunkSnapshot chunk : this.chunks) for (ChunkSnapshot chunk : this.chunks)
{ {
Biome biome = chunk.getBiome(0, 0); Biome biome = chunk.getBiome(0, 0);
ArrayList<Material> playerBlockIDs = RestoreNatureProcessingTask.getPlayerBlocks(this.worldType, biome); ArrayList<Material> playerBlockIDs = RestoreNatureProcessingTask.getPlayerBlocks(this.worldType, biome);
boolean ychanged = true; boolean ychanged = true;
while(!this.yTooSmall(y) && ychanged) while (!this.yTooSmall(y) && ychanged)
{ {
ychanged = false; ychanged = false;
for(int x = 0; x < 16; x++) for (int x = 0; x < 16; x++)
{ {
for(int z = 0; z < 16; z++) for (int z = 0; z < 16; z++)
{ {
Material blockType = chunk.getBlockType(x, y, z); Material blockType = chunk.getBlockType(x, y, z);
while(!this.yTooSmall(y) && playerBlockIDs.contains(blockType)) while (!this.yTooSmall(y) && playerBlockIDs.contains(blockType))
{ {
ychanged = true; ychanged = true;
blockType = chunk.getBlockType(x, --y, z); blockType = chunk.getBlockType(x, --y, z);
} }
if(this.yTooSmall(y)) return y; if (this.yTooSmall(y)) return y;
} }
} }
} }
if(this.yTooSmall(y)) return y; if (this.yTooSmall(y)) return y;
} }
} }
return y; return y;
} }
@ -109,7 +109,7 @@ class AutoExtendClaimTask implements Runnable
{ {
return y == 0 || y <= GriefPrevention.instance.config_claims_maxDepth; return y == 0 || y <= GriefPrevention.instance.config_claims_maxDepth;
} }
//runs in the main execution thread, where it can safely change claims and save those changes //runs in the main execution thread, where it can safely change claims and save those changes
private class ExecuteExtendClaimTask implements Runnable private class ExecuteExtendClaimTask implements Runnable
{ {

View File

@ -24,16 +24,16 @@ import org.bukkit.block.data.BlockData;
//basically, just a few data points from a block conveniently encapsulated in a class //basically, just a few data points from a block conveniently encapsulated in a class
//this is used only by the RestoreNature code //this is used only by the RestoreNature code
public class BlockSnapshot public class BlockSnapshot
{ {
public Location location; public Location location;
public Material typeId; public Material typeId;
public BlockData data; public BlockData data;
public BlockSnapshot(Location location, Material typeId, BlockData data) public BlockSnapshot(Location location, Material typeId, BlockData data)
{ {
this.location = location; this.location = location;
this.typeId = typeId; this.typeId = typeId;
this.data = data; this.data = data;
} }
} }

View File

@ -15,25 +15,25 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
//sends a message to all online players //sends a message to all online players
//used to send delayed messages, for example a quit message after the player has been gone a while //used to send delayed messages, for example a quit message after the player has been gone a while
class BroadcastMessageTask implements Runnable class BroadcastMessageTask implements Runnable
{ {
private String message; private String message;
public BroadcastMessageTask(String message) public BroadcastMessageTask(String message)
{ {
this.message = message; this.message = message;
} }
@Override @Override
public void run() public void run()
{ {
Bukkit.getServer().broadcastMessage(this.message); Bukkit.getServer().broadcastMessage(this.message);
} }
} }

View File

@ -15,13 +15,10 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
@ -31,30 +28,30 @@ import org.bukkit.scheduler.BukkitRunnable;
//if that happens, we detect the problem and send them back through the portal. //if that happens, we detect the problem and send them back through the portal.
class CheckForPortalTrapTask extends BukkitRunnable class CheckForPortalTrapTask extends BukkitRunnable
{ {
GriefPrevention instance; GriefPrevention instance;
//player who recently teleported via nether portal //player who recently teleported via nether portal
private Player player; private Player player;
//where to send the player back to if he hasn't left the portal frame //where to send the player back to if he hasn't left the portal frame
private Location returnLocation; private Location returnLocation;
public CheckForPortalTrapTask(Player player, GriefPrevention plugin, Location locationToReturn) public CheckForPortalTrapTask(Player player, GriefPrevention plugin, Location locationToReturn)
{ {
this.player = player; this.player = player;
this.instance = plugin; this.instance = plugin;
this.returnLocation = locationToReturn; this.returnLocation = locationToReturn;
player.setMetadata("GP_PORTALRESCUE", new FixedMetadataValue(instance, locationToReturn)); player.setMetadata("GP_PORTALRESCUE", new FixedMetadataValue(instance, locationToReturn));
} }
@Override @Override
public void run() public void run()
{ {
if(player.isOnline() && player.getPortalCooldown() >= 10 && player.hasMetadata("GP_PORTALRESCUE")) if (player.isOnline() && player.getPortalCooldown() >= 10 && player.hasMetadata("GP_PORTALRESCUE"))
{ {
instance.AddLogEntry("Rescued " + player.getName() + " from a nether portal.\nTeleported from " + player.getLocation().toString() + " to " + returnLocation.toString(), CustomLogEntryTypes.Debug); instance.AddLogEntry("Rescued " + player.getName() + " from a nether portal.\nTeleported from " + player.getLocation().toString() + " to " + returnLocation.toString(), CustomLogEntryTypes.Debug);
player.teleport(returnLocation); player.teleport(returnLocation);
player.removeMetadata("GP_PORTALRESCUE", instance); player.removeMetadata("GP_PORTALRESCUE", instance);
} }
instance.portalReturnTaskMap.remove(player.getUniqueId()); instance.portalReturnTaskMap.remove(player.getUniqueId());
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -15,26 +15,26 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
//basic enum stuff //basic enum stuff
public enum ClaimPermission public enum ClaimPermission
{ {
Build, Build,
Inventory, Inventory,
Access; Access;
/** /**
* Check if a ClaimPermission is granted by another ClaimPermission. * Check if a ClaimPermission is granted by another ClaimPermission.
* *
* @param other the ClaimPermission to compare against * @param other the ClaimPermission to compare against
* @return true if this ClaimPermission is equal or lesser than the provided ClaimPermission * @return true if this ClaimPermission is equal or lesser than the provided ClaimPermission
*/ */
public boolean isGrantedBy(ClaimPermission other) public boolean isGrantedBy(ClaimPermission other)
{ {
// As this uses declaration order to compare, if trust levels are reordered this method must be rewritten. // As this uses declaration order to compare, if trust levels are reordered this method must be rewritten.
return other != null && other.ordinal() <= this.ordinal(); return other != null && other.ordinal() <= this.ordinal();
} }
} }

View File

@ -15,8 +15,8 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
@ -26,40 +26,40 @@ import java.util.UUID;
//asynchronously loads player data without caching it in the datastore, then //asynchronously loads player data without caching it in the datastore, then
//passes those data to a claim cleanup task which might decide to delete a claim for inactivity //passes those data to a claim cleanup task which might decide to delete a claim for inactivity
class CleanupUnusedClaimPreTask implements Runnable class CleanupUnusedClaimPreTask implements Runnable
{ {
private UUID ownerID = null; private UUID ownerID = null;
CleanupUnusedClaimPreTask(UUID uuid)
{
this.ownerID = uuid;
}
@Override
public void run()
{
//get the data
PlayerData ownerData = GriefPrevention.instance.dataStore.getPlayerDataFromStorage(ownerID);
OfflinePlayer ownerInfo = Bukkit.getServer().getOfflinePlayer(ownerID);
GriefPrevention.AddLogEntry("Looking for expired claims. Checking data for " + ownerID.toString(), CustomLogEntryTypes.Debug, true); CleanupUnusedClaimPreTask(UUID uuid)
{
//expiration code uses last logout timestamp to decide whether to expire claims this.ownerID = uuid;
//don't expire claims for online players }
if(ownerInfo.isOnline())
{ @Override
GriefPrevention.AddLogEntry("Player is online. Ignoring.", CustomLogEntryTypes.Debug, true); public void run()
return; {
} //get the data
if(ownerInfo.getLastPlayed() <= 0) PlayerData ownerData = GriefPrevention.instance.dataStore.getPlayerDataFromStorage(ownerID);
{ OfflinePlayer ownerInfo = Bukkit.getServer().getOfflinePlayer(ownerID);
GriefPrevention.AddLogEntry("Player is new or not in the server's cached userdata. Ignoring. getLastPlayed = " + ownerInfo.getLastPlayed(), CustomLogEntryTypes.Debug, true);
return; GriefPrevention.AddLogEntry("Looking for expired claims. Checking data for " + ownerID.toString(), CustomLogEntryTypes.Debug, true);
}
//expiration code uses last logout timestamp to decide whether to expire claims
//skip claims belonging to exempted players based on block totals in config //don't expire claims for online players
int bonusBlocks = ownerData.getBonusClaimBlocks(); if (ownerInfo.isOnline())
if(bonusBlocks >= GriefPrevention.instance.config_claims_expirationExemptionBonusBlocks || bonusBlocks + ownerData.getAccruedClaimBlocks() >= GriefPrevention.instance.config_claims_expirationExemptionTotalBlocks) {
GriefPrevention.AddLogEntry("Player is online. Ignoring.", CustomLogEntryTypes.Debug, true);
return;
}
if (ownerInfo.getLastPlayed() <= 0)
{
GriefPrevention.AddLogEntry("Player is new or not in the server's cached userdata. Ignoring. getLastPlayed = " + ownerInfo.getLastPlayed(), CustomLogEntryTypes.Debug, true);
return;
}
//skip claims belonging to exempted players based on block totals in config
int bonusBlocks = ownerData.getBonusClaimBlocks();
if (bonusBlocks >= GriefPrevention.instance.config_claims_expirationExemptionBonusBlocks || bonusBlocks + ownerData.getAccruedClaimBlocks() >= GriefPrevention.instance.config_claims_expirationExemptionTotalBlocks)
{ {
GriefPrevention.AddLogEntry("Player exempt from claim expiration based on claim block counts vs. config file settings.", CustomLogEntryTypes.Debug, true); GriefPrevention.AddLogEntry("Player exempt from claim expiration based on claim block counts vs. config file settings.", CustomLogEntryTypes.Debug, true);
return; return;
@ -67,22 +67,22 @@ class CleanupUnusedClaimPreTask implements Runnable
Claim claimToExpire = null; Claim claimToExpire = null;
for (Claim claim : GriefPrevention.instance.dataStore.getClaims()) for (Claim claim : GriefPrevention.instance.dataStore.getClaims())
{ {
if (ownerID.equals(claim.ownerID)) if (ownerID.equals(claim.ownerID))
{ {
claimToExpire = claim; claimToExpire = claim;
break; break;
} }
} }
if (claimToExpire == null) if (claimToExpire == null)
{ {
GriefPrevention.AddLogEntry("Unable to find a claim to expire for " + ownerID.toString(), CustomLogEntryTypes.Debug, false); GriefPrevention.AddLogEntry("Unable to find a claim to expire for " + ownerID.toString(), CustomLogEntryTypes.Debug, false);
return; return;
} }
//pass it back to the main server thread, where it's safe to delete a claim if needed //pass it back to the main server thread, where it's safe to delete a claim if needed
Bukkit.getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, new CleanupUnusedClaimTask(claimToExpire, ownerData, ownerInfo), 1L); Bukkit.getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, new CleanupUnusedClaimTask(claimToExpire, ownerData, ownerInfo), 1L);
} }
} }

View File

@ -15,134 +15,131 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import me.ryanhamshire.GriefPrevention.events.ClaimExpirationEvent;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.Vector; import java.util.Vector;
import org.bukkit.Bukkit; class CleanupUnusedClaimTask implements Runnable
import org.bukkit.OfflinePlayer; {
import me.ryanhamshire.GriefPrevention.events.ClaimExpirationEvent;
class CleanupUnusedClaimTask implements Runnable
{
Claim claim; Claim claim;
PlayerData ownerData; PlayerData ownerData;
OfflinePlayer ownerInfo; OfflinePlayer ownerInfo;
CleanupUnusedClaimTask(Claim claim, PlayerData ownerData, OfflinePlayer ownerInfo)
{
this.claim = claim;
this.ownerData = ownerData;
this.ownerInfo = ownerInfo;
}
@Override
public void run()
{
CleanupUnusedClaimTask(Claim claim, PlayerData ownerData, OfflinePlayer ownerInfo)
//determine area of the default chest claim {
int areaOfDefaultClaim = 0; this.claim = claim;
if(GriefPrevention.instance.config_claims_automaticClaimsForNewPlayersRadius >= 0) this.ownerData = ownerData;
{ this.ownerInfo = ownerInfo;
areaOfDefaultClaim = (int)Math.pow(GriefPrevention.instance.config_claims_automaticClaimsForNewPlayersRadius * 2 + 1, 2); }
}
@Override
//if this claim is a chest claim and those are set to expire public void run()
if(claim.getArea() <= areaOfDefaultClaim && GriefPrevention.instance.config_claims_chestClaimExpirationDays > 0) {
{
//determine area of the default chest claim
int areaOfDefaultClaim = 0;
if (GriefPrevention.instance.config_claims_automaticClaimsForNewPlayersRadius >= 0)
{
areaOfDefaultClaim = (int) Math.pow(GriefPrevention.instance.config_claims_automaticClaimsForNewPlayersRadius * 2 + 1, 2);
}
//if this claim is a chest claim and those are set to expire
if (claim.getArea() <= areaOfDefaultClaim && GriefPrevention.instance.config_claims_chestClaimExpirationDays > 0)
{
//if the owner has been gone at least a week, and if he has ONLY the new player claim, it will be removed //if the owner has been gone at least a week, and if he has ONLY the new player claim, it will be removed
Calendar sevenDaysAgo = Calendar.getInstance(); Calendar sevenDaysAgo = Calendar.getInstance();
sevenDaysAgo.add(Calendar.DATE, -GriefPrevention.instance.config_claims_chestClaimExpirationDays); sevenDaysAgo.add(Calendar.DATE, -GriefPrevention.instance.config_claims_chestClaimExpirationDays);
boolean newPlayerClaimsExpired = sevenDaysAgo.getTime().after(new Date(ownerInfo.getLastPlayed())); boolean newPlayerClaimsExpired = sevenDaysAgo.getTime().after(new Date(ownerInfo.getLastPlayed()));
if(newPlayerClaimsExpired && ownerData.getClaims().size() == 1) if (newPlayerClaimsExpired && ownerData.getClaims().size() == 1)
{ {
if (expireEventCanceled()) if (expireEventCanceled())
return; return;
claim.removeSurfaceFluids(null); claim.removeSurfaceFluids(null);
GriefPrevention.instance.dataStore.deleteClaim(claim, true, true); GriefPrevention.instance.dataStore.deleteClaim(claim, true, true);
//if configured to do so, restore the land to natural
if(GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()) || GriefPrevention.instance.config_claims_survivalAutoNatureRestoration)
{
GriefPrevention.instance.restoreClaim(claim, 0);
}
GriefPrevention.AddLogEntry(" " + claim.getOwnerName() + "'s new player claim expired.", CustomLogEntryTypes.AdminActivity);
}
}
//if configured to always remove claims after some inactivity period without exceptions...
else if(GriefPrevention.instance.config_claims_expirationDays > 0)
{
Calendar earliestPermissibleLastLogin = Calendar.getInstance();
earliestPermissibleLastLogin.add(Calendar.DATE, -GriefPrevention.instance.config_claims_expirationDays);
if(earliestPermissibleLastLogin.getTime().after(new Date(ownerInfo.getLastPlayed())))
{
if (expireEventCanceled())
return;
//make a copy of this player's claim list
Vector<Claim> claims = new Vector<Claim>();
for(int i = 0; i < ownerData.getClaims().size(); i++)
{
claims.add(ownerData.getClaims().get(i));
}
//delete them
GriefPrevention.instance.dataStore.deleteClaimsForPlayer(claim.ownerID, true);
GriefPrevention.AddLogEntry(" All of " + claim.getOwnerName() + "'s claims have expired.", CustomLogEntryTypes.AdminActivity);
for(int i = 0; i < claims.size(); i++)
{
//if configured to do so, restore the land to natural
if(GriefPrevention.instance.creativeRulesApply(claims.get(i).getLesserBoundaryCorner()) || GriefPrevention.instance.config_claims_survivalAutoNatureRestoration)
{
GriefPrevention.instance.restoreClaim(claims.get(i), 0);
}
}
}
}
else if(GriefPrevention.instance.config_claims_unusedClaimExpirationDays > 0 && GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()))
{
//avoid scanning large claims and administrative claims
if(claim.isAdminClaim() || claim.getWidth() > 25 || claim.getHeight() > 25) return;
//otherwise scan the claim content
int minInvestment = 400;
long investmentScore = claim.getPlayerInvestmentScore();
if(investmentScore < minInvestment)
{
//if the owner has been gone at least a week, and if he has ONLY the new player claim, it will be removed
Calendar sevenDaysAgo = Calendar.getInstance();
sevenDaysAgo.add(Calendar.DATE, -GriefPrevention.instance.config_claims_unusedClaimExpirationDays);
boolean claimExpired = sevenDaysAgo.getTime().after(new Date(ownerInfo.getLastPlayed()));
if(claimExpired)
{
if (expireEventCanceled())
return;
GriefPrevention.instance.dataStore.deleteClaim(claim, true, true);
GriefPrevention.AddLogEntry("Removed " + claim.getOwnerName() + "'s unused claim @ " + GriefPrevention.getfriendlyLocationString(claim.getLesserBoundaryCorner()), CustomLogEntryTypes.AdminActivity);
//restore the claim area to natural state
GriefPrevention.instance.restoreClaim(claim, 0);
}
}
}
}
public boolean expireEventCanceled() //if configured to do so, restore the land to natural
{ if (GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()) || GriefPrevention.instance.config_claims_survivalAutoNatureRestoration)
//see if any other plugins don't want this claim deleted {
ClaimExpirationEvent event = new ClaimExpirationEvent(this.claim); GriefPrevention.instance.restoreClaim(claim, 0);
Bukkit.getPluginManager().callEvent(event); }
return event.isCancelled();
} GriefPrevention.AddLogEntry(" " + claim.getOwnerName() + "'s new player claim expired.", CustomLogEntryTypes.AdminActivity);
}
}
//if configured to always remove claims after some inactivity period without exceptions...
else if (GriefPrevention.instance.config_claims_expirationDays > 0)
{
Calendar earliestPermissibleLastLogin = Calendar.getInstance();
earliestPermissibleLastLogin.add(Calendar.DATE, -GriefPrevention.instance.config_claims_expirationDays);
if (earliestPermissibleLastLogin.getTime().after(new Date(ownerInfo.getLastPlayed())))
{
if (expireEventCanceled())
return;
//make a copy of this player's claim list
Vector<Claim> claims = new Vector<Claim>();
for (int i = 0; i < ownerData.getClaims().size(); i++)
{
claims.add(ownerData.getClaims().get(i));
}
//delete them
GriefPrevention.instance.dataStore.deleteClaimsForPlayer(claim.ownerID, true);
GriefPrevention.AddLogEntry(" All of " + claim.getOwnerName() + "'s claims have expired.", CustomLogEntryTypes.AdminActivity);
for (int i = 0; i < claims.size(); i++)
{
//if configured to do so, restore the land to natural
if (GriefPrevention.instance.creativeRulesApply(claims.get(i).getLesserBoundaryCorner()) || GriefPrevention.instance.config_claims_survivalAutoNatureRestoration)
{
GriefPrevention.instance.restoreClaim(claims.get(i), 0);
}
}
}
} else if (GriefPrevention.instance.config_claims_unusedClaimExpirationDays > 0 && GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()))
{
//avoid scanning large claims and administrative claims
if (claim.isAdminClaim() || claim.getWidth() > 25 || claim.getHeight() > 25) return;
//otherwise scan the claim content
int minInvestment = 400;
long investmentScore = claim.getPlayerInvestmentScore();
if (investmentScore < minInvestment)
{
//if the owner has been gone at least a week, and if he has ONLY the new player claim, it will be removed
Calendar sevenDaysAgo = Calendar.getInstance();
sevenDaysAgo.add(Calendar.DATE, -GriefPrevention.instance.config_claims_unusedClaimExpirationDays);
boolean claimExpired = sevenDaysAgo.getTime().after(new Date(ownerInfo.getLastPlayed()));
if (claimExpired)
{
if (expireEventCanceled())
return;
GriefPrevention.instance.dataStore.deleteClaim(claim, true, true);
GriefPrevention.AddLogEntry("Removed " + claim.getOwnerName() + "'s unused claim @ " + GriefPrevention.getfriendlyLocationString(claim.getLesserBoundaryCorner()), CustomLogEntryTypes.AdminActivity);
//restore the claim area to natural state
GriefPrevention.instance.restoreClaim(claim, 0);
}
}
}
}
public boolean expireEventCanceled()
{
//see if any other plugins don't want this claim deleted
ClaimExpirationEvent event = new ClaimExpirationEvent(this.claim);
Bukkit.getPluginManager().callEvent(event);
return event.isCancelled();
}
} }

View File

@ -18,12 +18,12 @@
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
public class CreateClaimResult public class CreateClaimResult
{ {
//whether or not the creation succeeded (it would fail if the new claim overlapped another existing claim) //whether or not the creation succeeded (it would fail if the new claim overlapped another existing claim)
public boolean succeeded; public boolean succeeded;
//when succeeded, this is a reference to the new claim //when succeeded, this is a reference to the new claim
//when failed, this is a reference to the pre-existing, conflicting claim //when failed, this is a reference to the pre-existing, conflicting claim
public Claim claim; public Claim claim;
} }

View File

@ -18,6 +18,9 @@
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import com.google.common.io.Files;
import org.bukkit.scheduler.BukkitScheduler;
import java.io.File; import java.io.File;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@ -26,32 +29,28 @@ import java.util.Date;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.bukkit.scheduler.BukkitScheduler;
import com.google.common.io.Files;
class CustomLogger class CustomLogger
{ {
private final SimpleDateFormat timestampFormat = new SimpleDateFormat("HH:mm"); private final SimpleDateFormat timestampFormat = new SimpleDateFormat("HH:mm");
private final SimpleDateFormat filenameFormat = new SimpleDateFormat("yyyy_MM_dd"); private final SimpleDateFormat filenameFormat = new SimpleDateFormat("yyyy_MM_dd");
private final String logFolderPath = DataStore.dataLayerFolderPath + File.separator + "Logs"; private final String logFolderPath = DataStore.dataLayerFolderPath + File.separator + "Logs";
private final int secondsBetweenWrites = 300; private final int secondsBetweenWrites = 300;
//stringbuilder is not thread safe, stringbuffer is //stringbuilder is not thread safe, stringbuffer is
private StringBuffer queuedEntries = new StringBuffer(); private StringBuffer queuedEntries = new StringBuffer();
CustomLogger() CustomLogger()
{ {
//ensure log folder exists //ensure log folder exists
File logFolder = new File(this.logFolderPath); File logFolder = new File(this.logFolderPath);
logFolder.mkdirs(); logFolder.mkdirs();
//delete any outdated log files immediately //delete any outdated log files immediately
this.DeleteExpiredLogs(); this.DeleteExpiredLogs();
//unless disabled, schedule recurring tasks //unless disabled, schedule recurring tasks
int daysToKeepLogs = GriefPrevention.instance.config_logs_daysToKeep; int daysToKeepLogs = GriefPrevention.instance.config_logs_daysToKeep;
if(daysToKeepLogs > 0) if (daysToKeepLogs > 0)
{ {
BukkitScheduler scheduler = GriefPrevention.instance.getServer().getScheduler(); BukkitScheduler scheduler = GriefPrevention.instance.getServer().getScheduler();
final long ticksPerSecond = 20L; final long ticksPerSecond = 20L;
@ -60,33 +59,38 @@ class CustomLogger
scheduler.runTaskTimerAsynchronously(GriefPrevention.instance, new ExpiredLogRemover(), ticksPerDay, ticksPerDay); scheduler.runTaskTimerAsynchronously(GriefPrevention.instance, new ExpiredLogRemover(), ticksPerDay, ticksPerDay);
} }
} }
private static final Pattern inlineFormatterPattern = Pattern.compile("§."); private static final Pattern inlineFormatterPattern = Pattern.compile("§.");
void AddEntry(String entry, CustomLogEntryTypes entryType) void AddEntry(String entry, CustomLogEntryTypes entryType)
{ {
//if disabled, do nothing //if disabled, do nothing
int daysToKeepLogs = GriefPrevention.instance.config_logs_daysToKeep; int daysToKeepLogs = GriefPrevention.instance.config_logs_daysToKeep;
if(daysToKeepLogs == 0) return; if (daysToKeepLogs == 0) return;
//if entry type is not enabled, do nothing //if entry type is not enabled, do nothing
if(!this.isEnabledType(entryType)) return; if (!this.isEnabledType(entryType)) return;
//otherwise write to the in-memory buffer, after removing formatters //otherwise write to the in-memory buffer, after removing formatters
Matcher matcher = inlineFormatterPattern.matcher(entry); Matcher matcher = inlineFormatterPattern.matcher(entry);
entry = matcher.replaceAll(""); entry = matcher.replaceAll("");
String timestamp = this.timestampFormat.format(new Date()); String timestamp = this.timestampFormat.format(new Date());
this.queuedEntries.append(timestamp + " " + entry + "\n"); this.queuedEntries.append(timestamp + " " + entry + "\n");
} }
private boolean isEnabledType(CustomLogEntryTypes entryType) private boolean isEnabledType(CustomLogEntryTypes entryType)
{ {
if(entryType == CustomLogEntryTypes.Exception) return true; if (entryType == CustomLogEntryTypes.Exception) return true;
if(entryType == CustomLogEntryTypes.SocialActivity && !GriefPrevention.instance.config_logs_socialEnabled) return false; if (entryType == CustomLogEntryTypes.SocialActivity && !GriefPrevention.instance.config_logs_socialEnabled)
if(entryType == CustomLogEntryTypes.SuspiciousActivity && !GriefPrevention.instance.config_logs_suspiciousEnabled) return false; return false;
if(entryType == CustomLogEntryTypes.AdminActivity && !GriefPrevention.instance.config_logs_adminEnabled) return false; if (entryType == CustomLogEntryTypes.SuspiciousActivity && !GriefPrevention.instance.config_logs_suspiciousEnabled)
if(entryType == CustomLogEntryTypes.Debug && !GriefPrevention.instance.config_logs_debugEnabled) return false; return false;
if(entryType == CustomLogEntryTypes.MutedChat && !GriefPrevention.instance.config_logs_mutedChatEnabled) return false; if (entryType == CustomLogEntryTypes.AdminActivity && !GriefPrevention.instance.config_logs_adminEnabled)
return false;
if (entryType == CustomLogEntryTypes.Debug && !GriefPrevention.instance.config_logs_debugEnabled) return false;
if (entryType == CustomLogEntryTypes.MutedChat && !GriefPrevention.instance.config_logs_mutedChatEnabled)
return false;
return true; return true;
} }
@ -95,73 +99,73 @@ class CustomLogger
try try
{ {
//if nothing to write, stop here //if nothing to write, stop here
if(this.queuedEntries.length() == 0) return; if (this.queuedEntries.length() == 0) return;
//determine filename based on date //determine filename based on date
String filename = this.filenameFormat.format(new Date()) + ".log"; String filename = this.filenameFormat.format(new Date()) + ".log";
String filepath = this.logFolderPath + File.separator + filename; String filepath = this.logFolderPath + File.separator + filename;
File logFile = new File(filepath); File logFile = new File(filepath);
//dump content //dump content
Files.append(this.queuedEntries.toString(), logFile, Charset.forName("UTF-8")); Files.append(this.queuedEntries.toString(), logFile, Charset.forName("UTF-8"));
//in case of a failure to write the above due to exception, //in case of a failure to write the above due to exception,
//the unwritten entries will remain the buffer for the next write to retry //the unwritten entries will remain the buffer for the next write to retry
this.queuedEntries.setLength(0); this.queuedEntries.setLength(0);
} }
catch(Exception e) catch (Exception e)
{ {
e.printStackTrace(); e.printStackTrace();
} }
} }
private void DeleteExpiredLogs() private void DeleteExpiredLogs()
{ {
try try
{ {
//get list of log files //get list of log files
File logFolder = new File(this.logFolderPath); File logFolder = new File(this.logFolderPath);
File [] files = logFolder.listFiles(); File[] files = logFolder.listFiles();
//delete any created before x days ago //delete any created before x days ago
int daysToKeepLogs = GriefPrevention.instance.config_logs_daysToKeep; int daysToKeepLogs = GriefPrevention.instance.config_logs_daysToKeep;
Calendar expirationBoundary = Calendar.getInstance(); Calendar expirationBoundary = Calendar.getInstance();
expirationBoundary.add(Calendar.DATE, -daysToKeepLogs); expirationBoundary.add(Calendar.DATE, -daysToKeepLogs);
for(int i = 0; i < files.length; i++) for (int i = 0; i < files.length; i++)
{ {
File file = files[i]; File file = files[i];
if(file.isDirectory()) continue; //skip any folders if (file.isDirectory()) continue; //skip any folders
String filename = file.getName().replace(".log", ""); String filename = file.getName().replace(".log", "");
String [] dateParts = filename.split("_"); //format is yyyy_MM_dd String[] dateParts = filename.split("_"); //format is yyyy_MM_dd
if(dateParts.length != 3) continue; if (dateParts.length != 3) continue;
try try
{ {
int year = Integer.parseInt(dateParts[0]); int year = Integer.parseInt(dateParts[0]);
int month = Integer.parseInt(dateParts[1]) - 1; int month = Integer.parseInt(dateParts[1]) - 1;
int day = Integer.parseInt(dateParts[2]); int day = Integer.parseInt(dateParts[2]);
Calendar filedate = Calendar.getInstance(); Calendar filedate = Calendar.getInstance();
filedate.set(year, month, day); filedate.set(year, month, day);
if(filedate.before(expirationBoundary)) if (filedate.before(expirationBoundary))
{ {
file.delete(); file.delete();
} }
} }
catch(NumberFormatException e) catch (NumberFormatException e)
{ {
//throw this away - effectively ignoring any files without the correct filename format //throw this away - effectively ignoring any files without the correct filename format
GriefPrevention.AddLogEntry("Ignoring an unexpected file in the abridged logs folder: " + file.getName(), CustomLogEntryTypes.Debug, true); GriefPrevention.AddLogEntry("Ignoring an unexpected file in the abridged logs folder: " + file.getName(), CustomLogEntryTypes.Debug, true);
} }
} }
} }
catch(Exception e) catch (Exception e)
{ {
e.printStackTrace(); e.printStackTrace();
} }
} }
//transfers the internal buffer to a log file //transfers the internal buffer to a log file
private class EntryWriter implements Runnable private class EntryWriter implements Runnable
{ {
@ -171,7 +175,7 @@ class CustomLogger
WriteEntries(); WriteEntries();
} }
} }
private class ExpiredLogRemover implements Runnable private class ExpiredLogRemover implements Runnable
{ {
@Override @Override

View File

@ -18,16 +18,16 @@
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
public class CustomizableMessage public class CustomizableMessage
{ {
public Messages id; public Messages id;
public String text; public String text;
public String notes; public String notes;
public CustomizableMessage(Messages id, String text, String notes) public CustomizableMessage(Messages id, String text, String notes)
{ {
this.id = id; this.id = id;
this.text = text; this.text = text;
this.notes = notes; this.notes = notes;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -16,13 +16,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import java.util.Collection;
import me.ryanhamshire.GriefPrevention.events.AccrueClaimBlocksEvent; import me.ryanhamshire.GriefPrevention.events.AccrueClaimBlocksEvent;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.Collection;
//FEATURE: give players claim blocks for playing, as long as they're not away from their computer //FEATURE: give players claim blocks for playing, as long as they're not away from their computer
//runs every 5 minutes in the main thread, grants blocks per hour / 12 to each online player who appears to be actively playing //runs every 5 minutes in the main thread, grants blocks per hour / 12 to each online player who appears to be actively playing
@ -43,13 +43,13 @@ class DeliverClaimBlocksTask implements Runnable
public void run() public void run()
{ {
//if no player specified, this task will create a player-specific task for each online player, scheduled one tick apart //if no player specified, this task will create a player-specific task for each online player, scheduled one tick apart
if(this.player == null) if (this.player == null)
{ {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Collection<Player> players = (Collection<Player>)GriefPrevention.instance.getServer().getOnlinePlayers(); Collection<Player> players = (Collection<Player>) GriefPrevention.instance.getServer().getOnlinePlayers();
long i = 0; long i = 0;
for(Player onlinePlayer : players) for (Player onlinePlayer : players)
{ {
DeliverClaimBlocksTask newTask = new DeliverClaimBlocksTask(onlinePlayer, instance); DeliverClaimBlocksTask newTask = new DeliverClaimBlocksTask(onlinePlayer, instance);
instance.getServer().getScheduler().scheduleSyncDelayedTask(instance, newTask, i++); instance.getServer().getScheduler().scheduleSyncDelayedTask(instance, newTask, i++);
@ -59,7 +59,7 @@ class DeliverClaimBlocksTask implements Runnable
} }
//deliver claim blocks to the specified player //deliver claim blocks to the specified player
if(!this.player.isOnline()) if (!this.player.isOnline())
{ {
return; //player is not online to receive claim blocks return; //player is not online to receive claim blocks
} }
@ -76,7 +76,7 @@ class DeliverClaimBlocksTask implements Runnable
isIdle = player.isInsideVehicle() || player.getLocation().getBlock().isLiquid() || isIdle = player.isInsideVehicle() || player.getLocation().getBlock().isLiquid() ||
!(playerData.lastAfkCheckLocation == null || playerData.lastAfkCheckLocation.distanceSquared(player.getLocation()) > idleThresholdSquared); !(playerData.lastAfkCheckLocation == null || playerData.lastAfkCheckLocation.distanceSquared(player.getLocation()) > idleThresholdSquared);
} }
catch(IllegalArgumentException ignore) //can't measure distance when to/from are different worlds catch (IllegalArgumentException ignore) //can't measure distance when to/from are different worlds
{ {
} }
@ -119,7 +119,7 @@ class DeliverClaimBlocksTask implements Runnable
//many other operations will cause this player's data to save, including his eventual logout //many other operations will cause this player's data to save, including his eventual logout
//dataStore.savePlayerData(player.getUniqueIdentifier(), playerData); //dataStore.savePlayerData(player.getUniqueIdentifier(), playerData);
} }
catch(Exception e) catch (Exception e)
{ {
GriefPrevention.AddLogEntry("Problem delivering claim blocks to player " + player.getName() + ":"); GriefPrevention.AddLogEntry("Problem delivering claim blocks to player " + player.getName() + ":");
e.printStackTrace(); e.printStackTrace();

View File

@ -32,119 +32,116 @@ import java.util.List;
//this main thread task revisits the location of a partially chopped tree from several minutes ago //this main thread task revisits the location of a partially chopped tree from several minutes ago
//if any part of the tree is still there and nothing else has been built in its place, remove the remaining parts //if any part of the tree is still there and nothing else has been built in its place, remove the remaining parts
class EntityCleanupTask implements Runnable class EntityCleanupTask implements Runnable
{ {
//where to start cleaning in the list of entities //where to start cleaning in the list of entities
private double percentageStart; private double percentageStart;
public EntityCleanupTask(double percentageStart) public EntityCleanupTask(double percentageStart)
{ {
this.percentageStart = percentageStart; this.percentageStart = percentageStart;
} }
@Override @Override
public void run() public void run()
{ {
ArrayList<World> worlds = new ArrayList<World>(); ArrayList<World> worlds = new ArrayList<World>();
for(World world : GriefPrevention.instance.getServer().getWorlds()) for (World world : GriefPrevention.instance.getServer().getWorlds())
{ {
if(GriefPrevention.instance.config_claims_worldModes.get(world) == ClaimsMode.Creative) if (GriefPrevention.instance.config_claims_worldModes.get(world) == ClaimsMode.Creative)
{ {
worlds.add(world); worlds.add(world);
} }
} }
for(int i = 0; i < worlds.size(); i++) for (int i = 0; i < worlds.size(); i++)
{ {
World world = worlds.get(i); World world = worlds.get(i);
List<Entity> entities = world.getEntities(); List<Entity> entities = world.getEntities();
//starting and stopping point. each execution of the task scans 10% of the server's (loaded) entities //starting and stopping point. each execution of the task scans 10% of the server's (loaded) entities
int j = (int)(entities.size() * this.percentageStart); int j = (int) (entities.size() * this.percentageStart);
int k = (int)(entities.size() * (this.percentageStart + .1)); int k = (int) (entities.size() * (this.percentageStart + .1));
Claim cachedClaim = null; Claim cachedClaim = null;
for(; j < entities.size() && j < k; j++) for (; j < entities.size() && j < k; j++)
{ {
Entity entity = entities.get(j); Entity entity = entities.get(j);
boolean remove = false; boolean remove = false;
if(entity instanceof Boat) //boats must be occupied if (entity instanceof Boat) //boats must be occupied
{ {
Boat boat = (Boat)entity; Boat boat = (Boat) entity;
if(boat.isEmpty()) remove = true; if (boat.isEmpty()) remove = true;
} } else if (entity instanceof Vehicle)
{
else if(entity instanceof Vehicle) Vehicle vehicle = (Vehicle) entity;
{
Vehicle vehicle = (Vehicle)entity; //minecarts in motion must be occupied by a player
if (vehicle.getVelocity().lengthSquared() != 0)
//minecarts in motion must be occupied by a player {
if(vehicle.getVelocity().lengthSquared() != 0) if (vehicle.isEmpty() || !(vehicle.getPassenger() instanceof Player))
{ {
if(vehicle.isEmpty() || !(vehicle.getPassenger() instanceof Player)) remove = true;
{ }
remove = true; }
}
} //stationary carts must be on rails
else
//stationary carts must be on rails {
else Material material = world.getBlockAt(vehicle.getLocation()).getType();
{ if (material != Material.RAIL && material != Material.POWERED_RAIL && material != Material.DETECTOR_RAIL)
Material material = world.getBlockAt(vehicle.getLocation()).getType(); {
if(material != Material.RAIL && material != Material.POWERED_RAIL && material != Material.DETECTOR_RAIL) remove = true;
{ }
remove = true; }
} }
}
} //all non-player entities must be in claims
else if (!(entity instanceof Player))
//all non-player entities must be in claims {
else if(!(entity instanceof Player)) Claim claim = GriefPrevention.instance.dataStore.getClaimAt(entity.getLocation(), false, cachedClaim);
{ if (claim != null)
Claim claim = GriefPrevention.instance.dataStore.getClaimAt(entity.getLocation(), false, cachedClaim); {
if(claim != null) cachedClaim = claim;
{ } else
cachedClaim = claim; {
} remove = true;
else }
{ }
remove = true;
} if (remove)
} {
GriefPrevention.AddLogEntry("Removing entity " + entity.getType().name() + " @ " + entity.getLocation(), CustomLogEntryTypes.Debug, true);
if(remove) entity.remove();
{ }
GriefPrevention.AddLogEntry("Removing entity " + entity.getType().name() + " @ " + entity.getLocation(), CustomLogEntryTypes.Debug, true); }
entity.remove(); }
}
} //starting and stopping point. each execution of the task scans 5% of the server's claims
} List<Claim> claims = GriefPrevention.instance.dataStore.claims;
int j = (int) (claims.size() * this.percentageStart);
//starting and stopping point. each execution of the task scans 5% of the server's claims int k = (int) (claims.size() * (this.percentageStart + .05));
List<Claim> claims = GriefPrevention.instance.dataStore.claims; for (; j < claims.size() && j < k; j++)
int j = (int)(claims.size() * this.percentageStart); {
int k = (int)(claims.size() * (this.percentageStart + .05)); Claim claim = claims.get(j);
for(; j < claims.size() && j < k; j++)
{ //if it's a creative mode claim
Claim claim = claims.get(j); if (GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()))
{
//if it's a creative mode claim //check its entity count and remove any extras
if(GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner())) claim.allowMoreEntities(true);
{ }
//check its entity count and remove any extras }
claim.allowMoreEntities(true);
} //schedule the next run of this task, in 3 minutes (20L is approximately 1 second)
} double nextRunPercentageStart = this.percentageStart + .05;
if (nextRunPercentageStart > .99)
//schedule the next run of this task, in 3 minutes (20L is approximately 1 second) {
double nextRunPercentageStart = this.percentageStart + .05; nextRunPercentageStart = 0;
if(nextRunPercentageStart > .99) }
{
nextRunPercentageStart = 0; EntityCleanupTask task = new EntityCleanupTask(nextRunPercentageStart);
} GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, task, 20L * 60 * 1);
}
EntityCleanupTask task = new EntityCleanupTask(nextRunPercentageStart);
GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, task, 20L * 60 * 1);
}
} }

View File

@ -15,7 +15,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -24,55 +24,55 @@ import org.bukkit.inventory.EquipmentSlot;
//tells a player about how many claim blocks he has, etc //tells a player about how many claim blocks he has, etc
//implemented as a task so that it can be delayed //implemented as a task so that it can be delayed
//otherwise, it's spammy when players mouse-wheel past the shovel in their hot bars //otherwise, it's spammy when players mouse-wheel past the shovel in their hot bars
class EquipShovelProcessingTask implements Runnable class EquipShovelProcessingTask implements Runnable
{ {
//player data //player data
private Player player; private Player player;
public EquipShovelProcessingTask(Player player) public EquipShovelProcessingTask(Player player)
{ {
this.player = player; this.player = player;
} }
@Override @Override
public void run() public void run()
{ {
//if he's not holding the golden shovel anymore, do nothing //if he's not holding the golden shovel anymore, do nothing
if(GriefPrevention.instance.getItemInHand(player, EquipmentSlot.HAND).getType() != GriefPrevention.instance.config_claims_modificationTool) return; if (GriefPrevention.instance.getItemInHand(player, EquipmentSlot.HAND).getType() != GriefPrevention.instance.config_claims_modificationTool)
return;
PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId());
PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId());
//reset any work he might have been doing //reset any work he might have been doing
playerData.lastShovelLocation = null; playerData.lastShovelLocation = null;
playerData.claimResizing = null; playerData.claimResizing = null;
//always reset to basic claims mode //always reset to basic claims mode
if(playerData.shovelMode != ShovelMode.Basic) if (playerData.shovelMode != ShovelMode.Basic)
{ {
playerData.shovelMode = ShovelMode.Basic; playerData.shovelMode = ShovelMode.Basic;
GriefPrevention.sendMessage(player, TextMode.Info, Messages.ShovelBasicClaimMode); GriefPrevention.sendMessage(player, TextMode.Info, Messages.ShovelBasicClaimMode);
} }
//tell him how many claim blocks he has available //tell him how many claim blocks he has available
int remainingBlocks = playerData.getRemainingClaimBlocks(); int remainingBlocks = playerData.getRemainingClaimBlocks();
GriefPrevention.sendMessage(player, TextMode.Instr, Messages.RemainingBlocks, String.valueOf(remainingBlocks)); GriefPrevention.sendMessage(player, TextMode.Instr, Messages.RemainingBlocks, String.valueOf(remainingBlocks));
//link to a video demo of land claiming, based on world type //link to a video demo of land claiming, based on world type
if(GriefPrevention.instance.creativeRulesApply(player.getLocation())) if (GriefPrevention.instance.creativeRulesApply(player.getLocation()))
{ {
GriefPrevention.sendMessage(player, TextMode.Instr, Messages.CreativeBasicsVideo2, DataStore.CREATIVE_VIDEO_URL); GriefPrevention.sendMessage(player, TextMode.Instr, Messages.CreativeBasicsVideo2, DataStore.CREATIVE_VIDEO_URL);
} } else if (GriefPrevention.instance.claimsEnabledForWorld(player.getLocation().getWorld()))
else if(GriefPrevention.instance.claimsEnabledForWorld(player.getLocation().getWorld())) {
{ GriefPrevention.sendMessage(player, TextMode.Instr, Messages.SurvivalBasicsVideo2, DataStore.SURVIVAL_VIDEO_URL);
GriefPrevention.sendMessage(player, TextMode.Instr, Messages.SurvivalBasicsVideo2, DataStore.SURVIVAL_VIDEO_URL); }
}
//if standing in a claim owned by the player, visualize it
//if standing in a claim owned by the player, visualize it Claim claim = GriefPrevention.instance.dataStore.getClaimAt(player.getLocation(), true, playerData.lastClaim);
Claim claim = GriefPrevention.instance.dataStore.getClaimAt(player.getLocation(), true, playerData.lastClaim); if (claim != null && claim.allowEdit(player) == null)
if(claim != null && claim.allowEdit(player) == null) {
{ playerData.lastClaim = claim;
playerData.lastClaim = claim; Visualization.Apply(player, Visualization.FromClaim(claim, player.getEyeLocation().getBlockY(), VisualizationType.Claim, player.getLocation()));
Visualization.Apply(player, Visualization.FromClaim(claim, player.getEyeLocation().getBlockY(), VisualizationType.Claim, player.getLocation())); }
} }
}
} }

View File

@ -31,47 +31,49 @@ import java.util.stream.Collectors;
//...because the player has been gone a REALLY long time, and that expiration has been configured in config.yml //...because the player has been gone a REALLY long time, and that expiration has been configured in config.yml
//runs every 1 minute in the main thread //runs every 1 minute in the main thread
class FindUnusedClaimsTask implements Runnable class FindUnusedClaimsTask implements Runnable
{ {
private List<UUID> claimOwnerUUIDs; private List<UUID> claimOwnerUUIDs;
private Iterator<UUID> claimOwnerIterator; private Iterator<UUID> claimOwnerIterator;
FindUnusedClaimsTask()
{
refreshUUIDs();
}
@Override
public void run()
{
//don't do anything when there are no claims
if(claimOwnerUUIDs.isEmpty()) return;
//wrap search around to beginning FindUnusedClaimsTask()
if(!claimOwnerIterator.hasNext()) {
{ refreshUUIDs();
refreshUUIDs(); }
return;
}
GriefPrevention.instance.getServer().getScheduler().runTaskAsynchronously(GriefPrevention.instance, new CleanupUnusedClaimPreTask(claimOwnerIterator.next()));
}
public void refreshUUIDs() { @Override
// Fetch owner UUIDs from list of claims public void run()
claimOwnerUUIDs = GriefPrevention.instance.dataStore.claims.stream().map(claim -> claim.ownerID) {
.distinct().filter(Objects::nonNull).collect(Collectors.toList()); //don't do anything when there are no claims
if (claimOwnerUUIDs.isEmpty()) return;
if (!claimOwnerUUIDs.isEmpty()) { //wrap search around to beginning
// Randomize order if (!claimOwnerIterator.hasNext())
Collections.shuffle(claimOwnerUUIDs); {
} refreshUUIDs();
return;
}
GriefPrevention.AddLogEntry("The following UUIDs own a claim and will be checked for inactivity in the following order:", CustomLogEntryTypes.Debug, true); GriefPrevention.instance.getServer().getScheduler().runTaskAsynchronously(GriefPrevention.instance, new CleanupUnusedClaimPreTask(claimOwnerIterator.next()));
}
for (UUID uuid : claimOwnerUUIDs) public void refreshUUIDs()
GriefPrevention.AddLogEntry(uuid.toString(), CustomLogEntryTypes.Debug, true); {
// Fetch owner UUIDs from list of claims
claimOwnerUUIDs = GriefPrevention.instance.dataStore.claims.stream().map(claim -> claim.ownerID)
.distinct().filter(Objects::nonNull).collect(Collectors.toList());
claimOwnerIterator = claimOwnerUUIDs.iterator(); if (!claimOwnerUUIDs.isEmpty())
} {
// Randomize order
Collections.shuffle(claimOwnerUUIDs);
}
GriefPrevention.AddLogEntry("The following UUIDs own a claim and will be checked for inactivity in the following order:", CustomLogEntryTypes.Debug, true);
for (UUID uuid : claimOwnerUUIDs)
GriefPrevention.AddLogEntry(uuid.toString(), CustomLogEntryTypes.Debug, true);
claimOwnerIterator = claimOwnerUUIDs.iterator();
}
} }

View File

@ -1,51 +1,51 @@
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import com.google.common.io.Files;
import java.io.File; import java.io.File;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import com.google.common.io.Files;
//loads ignore data from file into a hash map //loads ignore data from file into a hash map
class IgnoreLoaderThread extends Thread class IgnoreLoaderThread extends Thread
{ {
private UUID playerToLoad; private UUID playerToLoad;
private ConcurrentHashMap<UUID, Boolean> destinationMap; private ConcurrentHashMap<UUID, Boolean> destinationMap;
IgnoreLoaderThread(UUID playerToLoad, ConcurrentHashMap<UUID, Boolean> destinationMap) IgnoreLoaderThread(UUID playerToLoad, ConcurrentHashMap<UUID, Boolean> destinationMap)
{ {
this.playerToLoad = playerToLoad; this.playerToLoad = playerToLoad;
this.destinationMap = destinationMap; this.destinationMap = destinationMap;
this.setPriority(MIN_PRIORITY); this.setPriority(MIN_PRIORITY);
} }
@Override @Override
public void run() public void run()
{ {
File ignoreFile = new File(DataStore.playerDataFolderPath + File.separator + this.playerToLoad + ".ignore"); File ignoreFile = new File(DataStore.playerDataFolderPath + File.separator + this.playerToLoad + ".ignore");
//if the file doesn't exist, there's nothing to do here //if the file doesn't exist, there's nothing to do here
if(!ignoreFile.exists()) return; if (!ignoreFile.exists()) return;
boolean needRetry = false; boolean needRetry = false;
int retriesRemaining = 5; int retriesRemaining = 5;
Exception latestException = null; Exception latestException = null;
do do
{ {
try try
{ {
needRetry = false; needRetry = false;
//read the file content and immediately close it //read the file content and immediately close it
List<String> lines = Files.readLines(ignoreFile, Charset.forName("UTF-8")); List<String> lines = Files.readLines(ignoreFile, Charset.forName("UTF-8"));
//each line is one ignore. asterisks indicate administrative ignores //each line is one ignore. asterisks indicate administrative ignores
for(String line : lines) for (String line : lines)
{ {
boolean adminIgnore = false; boolean adminIgnore = false;
if(line.startsWith("*")) if (line.startsWith("*"))
{ {
adminIgnore = true; adminIgnore = true;
line = line.substring(1); line = line.substring(1);
@ -55,28 +55,28 @@ class IgnoreLoaderThread extends Thread
UUID ignoredUUID = UUID.fromString(line); UUID ignoredUUID = UUID.fromString(line);
this.destinationMap.put(ignoredUUID, adminIgnore); this.destinationMap.put(ignoredUUID, adminIgnore);
} }
catch(IllegalArgumentException e){} //if a bad UUID, ignore the line catch (IllegalArgumentException e) {} //if a bad UUID, ignore the line
} }
} }
//if there's any problem with the file's content, retry up to 5 times with 5 milliseconds between //if there's any problem with the file's content, retry up to 5 times with 5 milliseconds between
catch(Exception e) catch (Exception e)
{ {
latestException = e; latestException = e;
needRetry = true; needRetry = true;
retriesRemaining--; retriesRemaining--;
} }
try try
{ {
if(needRetry) Thread.sleep(5); if (needRetry) Thread.sleep(5);
} }
catch(InterruptedException exception) {} catch (InterruptedException exception) {}
}while(needRetry && retriesRemaining >= 0); } while (needRetry && retriesRemaining >= 0);
//if last attempt failed, log information about the problem //if last attempt failed, log information about the problem
if(needRetry) if (needRetry)
{ {
GriefPrevention.AddLogEntry("Retry attempts exhausted. Unable to load ignore data for player \"" + playerToLoad.toString() + "\": " + latestException.toString()); GriefPrevention.AddLogEntry("Retry attempts exhausted. Unable to load ignore data for player \"" + playerToLoad.toString() + "\": " + latestException.toString());
latestException.printStackTrace(); latestException.printStackTrace();

View File

@ -22,14 +22,14 @@ import java.net.InetAddress;
public class IpBanInfo public class IpBanInfo
{ {
InetAddress address; InetAddress address;
long expirationTimestamp; long expirationTimestamp;
String bannedAccountName; String bannedAccountName;
IpBanInfo(InetAddress address, long expirationTimestamp, String bannedAccountName) IpBanInfo(InetAddress address, long expirationTimestamp, String bannedAccountName)
{ {
this.address = address; this.address = address;
this.expirationTimestamp = expirationTimestamp; this.expirationTimestamp = expirationTimestamp;
this.bannedAccountName = bannedAccountName; this.bannedAccountName = bannedAccountName;
} }
} }

View File

@ -24,31 +24,31 @@ import java.util.Set;
//ordered list of material info objects, for fast searching //ordered list of material info objects, for fast searching
public class MaterialCollection public class MaterialCollection
{ {
Set<MaterialInfo> materials = new HashSet<MaterialInfo>(); Set<MaterialInfo> materials = new HashSet<MaterialInfo>();
void Add(MaterialInfo material)
{
this.materials.add(material);
}
boolean Contains(MaterialInfo material)
{
return this.materials.contains(material);
}
@Override
public String toString()
{
return materials.toString();
}
public int size()
{
return this.materials.size();
}
public void clear() void Add(MaterialInfo material)
{ {
this.materials.clear(); this.materials.add(material);
} }
boolean Contains(MaterialInfo material)
{
return this.materials.contains(material);
}
@Override
public String toString()
{
return materials.toString();
}
public int size()
{
return this.materials.size();
}
public void clear()
{
this.materials.clear();
}
} }

View File

@ -24,73 +24,72 @@ import org.bukkit.Material;
public class MaterialInfo public class MaterialInfo
{ {
Material typeID; Material typeID;
byte data; byte data;
boolean allDataValues; boolean allDataValues;
String description; String description;
public MaterialInfo(Material typeID, byte data, String description) public MaterialInfo(Material typeID, byte data, String description)
{ {
this.typeID = typeID; this.typeID = typeID;
this.data = data; this.data = data;
this.allDataValues = false; this.allDataValues = false;
this.description = description; this.description = description;
} }
public MaterialInfo(Material typeID, String description) public MaterialInfo(Material typeID, String description)
{ {
this.typeID = typeID; this.typeID = typeID;
this.data = 0; this.data = 0;
this.allDataValues = true; this.allDataValues = true;
this.description = description; this.description = description;
} }
private MaterialInfo(Material typeID, byte data, boolean allDataValues, String description) private MaterialInfo(Material typeID, byte data, boolean allDataValues, String description)
{ {
this.typeID = typeID; this.typeID = typeID;
this.data = data; this.data = data;
this.allDataValues = allDataValues; this.allDataValues = allDataValues;
this.description = description; this.description = description;
} }
@Override @Override
public String toString() public String toString()
{ {
String returnValue = String.valueOf(this.typeID) + ":" + (this.allDataValues?"*":String.valueOf(this.data)); String returnValue = String.valueOf(this.typeID) + ":" + (this.allDataValues ? "*" : String.valueOf(this.data));
if(this.description != null) returnValue += ":" + this.description; if (this.description != null) returnValue += ":" + this.description;
return returnValue; return returnValue;
} }
public static MaterialInfo fromString(String string) public static MaterialInfo fromString(String string)
{ {
if(string == null || string.isEmpty()) return null; if (string == null || string.isEmpty()) return null;
String [] parts = string.split(":"); String[] parts = string.split(":");
if(parts.length < 3) return null; if (parts.length < 3) return null;
try try
{ {
Material typeID = Material.matchMaterial(parts[0]); Material typeID = Material.matchMaterial(parts[0]);
byte data; byte data;
boolean allDataValues; boolean allDataValues;
if(parts[1].equals("*")) if (parts[1].equals("*"))
{ {
allDataValues = true; allDataValues = true;
data = 0; data = 0;
} } else
else {
{ allDataValues = false;
allDataValues = false; data = Byte.parseByte(parts[1]);
data = Byte.parseByte(parts[1]); }
}
return new MaterialInfo(typeID, data, allDataValues, parts[2]);
return new MaterialInfo(typeID, data, allDataValues, parts[2]); }
} catch (NumberFormatException exception)
catch(NumberFormatException exception) {
{ return null;
return null; }
} }
}
} }

View File

@ -1,16 +1,17 @@
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import java.util.UUID;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import java.util.UUID;
class PendingItemProtection class PendingItemProtection
{ {
public Location location; public Location location;
public UUID owner; public UUID owner;
long expirationTimestamp; long expirationTimestamp;
ItemStack itemStack; ItemStack itemStack;
public PendingItemProtection(Location location, UUID owner, long expirationTimestamp, ItemStack itemStack) public PendingItemProtection(Location location, UUID owner, long expirationTimestamp, ItemStack itemStack)
{ {
this.location = location; this.location = location;

View File

@ -17,184 +17,176 @@
*/ */
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date;
import java.util.UUID; import java.util.UUID;
import java.util.Vector; import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import me.ryanhamshire.GriefPrevention.Claim;
import me.ryanhamshire.GriefPrevention.GriefPrevention;
import me.ryanhamshire.GriefPrevention.ShovelMode;
import me.ryanhamshire.GriefPrevention.SiegeData;
import me.ryanhamshire.GriefPrevention.Visualization;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
//holds all of GriefPrevention's player-tied data //holds all of GriefPrevention's player-tied data
public class PlayerData public class PlayerData
{ {
//the player's ID //the player's ID
public UUID playerID; public UUID playerID;
//the player's claims
private Vector<Claim> claims = null;
//how many claim blocks the player has earned via play time
private Integer accruedClaimBlocks = null;
//temporary holding area to avoid opening data files too early
private int newlyAccruedClaimBlocks = 0;
//where this player was the last time we checked on him for earning claim blocks
public Location lastAfkCheckLocation = null;
//how many claim blocks the player has been gifted by admins, or purchased via economy integration
private Integer bonusClaimBlocks = null;
//what "mode" the shovel is in determines what it will do when it's used
public ShovelMode shovelMode = ShovelMode.Basic;
//radius for restore nature fill mode
int fillRadius = 0;
//last place the player used the shovel, useful in creating and resizing claims,
//because the player must use the shovel twice in those instances
public Location lastShovelLocation = null;
//the claim this player is currently resizing
public Claim claimResizing = null;
//the claim this player is currently subdividing
public Claim claimSubdividing = null;
//whether or not the player has a pending /trapped rescue
public boolean pendingTrapped = false;
//whether this player was recently warned about building outside land claims
boolean warnedAboutBuildingOutsideClaims = false;
//timestamp when last siege ended (where this player was the defender)
long lastSiegeEndTimeStamp = 0;
//whether the player was kicked (set and used during logout)
boolean wasKicked = false;
//visualization
public Visualization currentVisualization = null;
//anti-camping pvp protection
public boolean pvpImmune = false;
public long lastSpawn = 0;
//ignore claims mode
public boolean ignoreClaims = false;
//the last claim this player was in, that we know of
public Claim lastClaim = null;
//siege
public SiegeData siegeData = null;
//pvp
public long lastPvpTimestamp = 0;
public String lastPvpPlayer = "";
//safety confirmation for deleting multi-subdivision claims
public boolean warnedAboutMajorDeletion = false;
public InetAddress ipAddress; //the player's claims
private Vector<Claim> claims = null;
//how many claim blocks the player has earned via play time
private Integer accruedClaimBlocks = null;
//temporary holding area to avoid opening data files too early
private int newlyAccruedClaimBlocks = 0;
//where this player was the last time we checked on him for earning claim blocks
public Location lastAfkCheckLocation = null;
//how many claim blocks the player has been gifted by admins, or purchased via economy integration
private Integer bonusClaimBlocks = null;
//what "mode" the shovel is in determines what it will do when it's used
public ShovelMode shovelMode = ShovelMode.Basic;
//radius for restore nature fill mode
int fillRadius = 0;
//last place the player used the shovel, useful in creating and resizing claims,
//because the player must use the shovel twice in those instances
public Location lastShovelLocation = null;
//the claim this player is currently resizing
public Claim claimResizing = null;
//the claim this player is currently subdividing
public Claim claimSubdividing = null;
//whether or not the player has a pending /trapped rescue
public boolean pendingTrapped = false;
//whether this player was recently warned about building outside land claims
boolean warnedAboutBuildingOutsideClaims = false;
//timestamp when last siege ended (where this player was the defender)
long lastSiegeEndTimeStamp = 0;
//whether the player was kicked (set and used during logout)
boolean wasKicked = false;
//visualization
public Visualization currentVisualization = null;
//anti-camping pvp protection
public boolean pvpImmune = false;
public long lastSpawn = 0;
//ignore claims mode
public boolean ignoreClaims = false;
//the last claim this player was in, that we know of
public Claim lastClaim = null;
//siege
public SiegeData siegeData = null;
//pvp
public long lastPvpTimestamp = 0;
public String lastPvpPlayer = "";
//safety confirmation for deleting multi-subdivision claims
public boolean warnedAboutMajorDeletion = false;
public InetAddress ipAddress;
//for addons to set per-player claim limits. Any negative value will use config's value //for addons to set per-player claim limits. Any negative value will use config's value
private int AccruedClaimBlocksLimit = -1; private int AccruedClaimBlocksLimit = -1;
//whether or not this player has received a message about unlocking death drops since his last death //whether or not this player has received a message about unlocking death drops since his last death
boolean receivedDropUnlockAdvertisement = false; boolean receivedDropUnlockAdvertisement = false;
//whether or not this player's dropped items (on death) are unlocked for other players to pick up //whether or not this player's dropped items (on death) are unlocked for other players to pick up
boolean dropsAreUnlocked = false; boolean dropsAreUnlocked = false;
//message to send to player after he respawns //message to send to player after he respawns
String messageOnRespawn = null; String messageOnRespawn = null;
//player which a pet will be given to when it's right-clicked //player which a pet will be given to when it's right-clicked
OfflinePlayer petGiveawayRecipient = null; OfflinePlayer petGiveawayRecipient = null;
//timestamp for last "you're building outside your land claims" message //timestamp for last "you're building outside your land claims" message
Long buildWarningTimestamp = null; Long buildWarningTimestamp = null;
//spot where a player can't talk, used to mute new players until they've moved a little //spot where a player can't talk, used to mute new players until they've moved a little
//this is an anti-bot strategy. //this is an anti-bot strategy.
Location noChatLocation = null; Location noChatLocation = null;
//ignore list //ignore list
//true means invisible (admin-forced ignore), false means player-created ignore //true means invisible (admin-forced ignore), false means player-created ignore
public ConcurrentHashMap<UUID, Boolean> ignoredPlayers = new ConcurrentHashMap<UUID, Boolean>(); public ConcurrentHashMap<UUID, Boolean> ignoredPlayers = new ConcurrentHashMap<UUID, Boolean>();
public boolean ignoreListChanged = false; public boolean ignoreListChanged = false;
//profanity warning, once per play session //profanity warning, once per play session
boolean profanityWarned = false; boolean profanityWarned = false;
//whether or not this player is "in" pvp combat //whether or not this player is "in" pvp combat
public boolean inPvpCombat() public boolean inPvpCombat()
{ {
if(this.lastPvpTimestamp == 0) return false; if (this.lastPvpTimestamp == 0) return false;
long now = Calendar.getInstance().getTimeInMillis(); long now = Calendar.getInstance().getTimeInMillis();
long elapsed = now - this.lastPvpTimestamp; long elapsed = now - this.lastPvpTimestamp;
if(elapsed > GriefPrevention.instance.config_pvp_combatTimeoutSeconds * 1000) //X seconds if (elapsed > GriefPrevention.instance.config_pvp_combatTimeoutSeconds * 1000) //X seconds
{ {
this.lastPvpTimestamp = 0; this.lastPvpTimestamp = 0;
return false; return false;
} }
return true; return true;
} }
//the number of claim blocks a player has available for claiming land //the number of claim blocks a player has available for claiming land
public int getRemainingClaimBlocks() public int getRemainingClaimBlocks()
{ {
int remainingBlocks = this.getAccruedClaimBlocks() + this.getBonusClaimBlocks() + GriefPrevention.instance.dataStore.getGroupBonusBlocks(this.playerID); int remainingBlocks = this.getAccruedClaimBlocks() + this.getBonusClaimBlocks() + GriefPrevention.instance.dataStore.getGroupBonusBlocks(this.playerID);
for(int i = 0; i < this.getClaims().size(); i++) for (int i = 0; i < this.getClaims().size(); i++)
{ {
Claim claim = this.getClaims().get(i); Claim claim = this.getClaims().get(i);
remainingBlocks -= claim.getArea(); remainingBlocks -= claim.getArea();
} }
return remainingBlocks; return remainingBlocks;
} }
//don't load data from secondary storage until it's needed //don't load data from secondary storage until it's needed
public synchronized int getAccruedClaimBlocks() public synchronized int getAccruedClaimBlocks()
{ {
if(this.accruedClaimBlocks == null) this.loadDataFromSecondaryStorage(); if (this.accruedClaimBlocks == null) this.loadDataFromSecondaryStorage();
//update claim blocks with any he has accrued during his current play session //update claim blocks with any he has accrued during his current play session
if(this.newlyAccruedClaimBlocks > 0) if (this.newlyAccruedClaimBlocks > 0)
{ {
int accruedLimit = this.getAccruedClaimBlocksLimit(); int accruedLimit = this.getAccruedClaimBlocksLimit();
//if over the limit before adding blocks, leave it as-is, because the limit may have changed AFTER he accrued the blocks //if over the limit before adding blocks, leave it as-is, because the limit may have changed AFTER he accrued the blocks
if(this.accruedClaimBlocks < accruedLimit) if (this.accruedClaimBlocks < accruedLimit)
{ {
//move any in the holding area //move any in the holding area
int newTotal = this.accruedClaimBlocks + this.newlyAccruedClaimBlocks; int newTotal = this.accruedClaimBlocks + this.newlyAccruedClaimBlocks;
//respect limits //respect limits
this.accruedClaimBlocks = Math.min(newTotal, accruedLimit); this.accruedClaimBlocks = Math.min(newTotal, accruedLimit);
} }
this.newlyAccruedClaimBlocks = 0; this.newlyAccruedClaimBlocks = 0;
return this.accruedClaimBlocks; return this.accruedClaimBlocks;
} }
return accruedClaimBlocks; return accruedClaimBlocks;
} }
public void setAccruedClaimBlocks(Integer accruedClaimBlocks) public void setAccruedClaimBlocks(Integer accruedClaimBlocks)
@ -205,7 +197,7 @@ public class PlayerData
public int getBonusClaimBlocks() public int getBonusClaimBlocks()
{ {
if(this.bonusClaimBlocks == null) this.loadDataFromSecondaryStorage(); if (this.bonusClaimBlocks == null) this.loadDataFromSecondaryStorage();
return bonusClaimBlocks; return bonusClaimBlocks;
} }
@ -213,89 +205,87 @@ public class PlayerData
{ {
this.bonusClaimBlocks = bonusClaimBlocks; this.bonusClaimBlocks = bonusClaimBlocks;
} }
private void loadDataFromSecondaryStorage() private void loadDataFromSecondaryStorage()
{ {
//reach out to secondary storage to get any data there //reach out to secondary storage to get any data there
PlayerData storageData = GriefPrevention.instance.dataStore.getPlayerDataFromStorage(this.playerID); PlayerData storageData = GriefPrevention.instance.dataStore.getPlayerDataFromStorage(this.playerID);
if(this.accruedClaimBlocks == null) if (this.accruedClaimBlocks == null)
{ {
if(storageData.accruedClaimBlocks != null) if (storageData.accruedClaimBlocks != null)
{ {
this.accruedClaimBlocks = storageData.accruedClaimBlocks; this.accruedClaimBlocks = storageData.accruedClaimBlocks;
//ensure at least minimum accrued are accrued (in case of settings changes to increase initial amount) //ensure at least minimum accrued are accrued (in case of settings changes to increase initial amount)
if(GriefPrevention.instance.config_advanced_fixNegativeClaimblockAmounts && (this.accruedClaimBlocks < GriefPrevention.instance.config_claims_initialBlocks)) if (GriefPrevention.instance.config_advanced_fixNegativeClaimblockAmounts && (this.accruedClaimBlocks < GriefPrevention.instance.config_claims_initialBlocks))
{ {
this.accruedClaimBlocks = GriefPrevention.instance.config_claims_initialBlocks; this.accruedClaimBlocks = GriefPrevention.instance.config_claims_initialBlocks;
} }
} } else
else
{ {
this.accruedClaimBlocks = GriefPrevention.instance.config_claims_initialBlocks; this.accruedClaimBlocks = GriefPrevention.instance.config_claims_initialBlocks;
} }
} }
if(this.bonusClaimBlocks == null) if (this.bonusClaimBlocks == null)
{ {
if(storageData.bonusClaimBlocks != null) if (storageData.bonusClaimBlocks != null)
{ {
this.bonusClaimBlocks = storageData.bonusClaimBlocks; this.bonusClaimBlocks = storageData.bonusClaimBlocks;
} } else
else
{ {
this.bonusClaimBlocks = 0; this.bonusClaimBlocks = 0;
} }
} }
} }
public Vector<Claim> getClaims() public Vector<Claim> getClaims()
{ {
if(this.claims == null) if (this.claims == null)
{ {
this.claims = new Vector<Claim>(); this.claims = new Vector<Claim>();
//find all the claims belonging to this player and note them for future reference //find all the claims belonging to this player and note them for future reference
DataStore dataStore = GriefPrevention.instance.dataStore; DataStore dataStore = GriefPrevention.instance.dataStore;
int totalClaimsArea = 0; int totalClaimsArea = 0;
for(int i = 0; i < dataStore.claims.size(); i++) for (int i = 0; i < dataStore.claims.size(); i++)
{ {
Claim claim = dataStore.claims.get(i); Claim claim = dataStore.claims.get(i);
if(!claim.inDataStore) if (!claim.inDataStore)
{ {
dataStore.claims.remove(i--); dataStore.claims.remove(i--);
continue; continue;
} }
if(playerID.equals(claim.ownerID)) if (playerID.equals(claim.ownerID))
{ {
this.claims.add(claim); this.claims.add(claim);
totalClaimsArea += claim.getArea(); totalClaimsArea += claim.getArea();
} }
} }
//ensure player has claim blocks for his claims, and at least the minimum accrued //ensure player has claim blocks for his claims, and at least the minimum accrued
this.loadDataFromSecondaryStorage(); this.loadDataFromSecondaryStorage();
//if total claimed area is more than total blocks available //if total claimed area is more than total blocks available
int totalBlocks = this.accruedClaimBlocks + this.getBonusClaimBlocks() + GriefPrevention.instance.dataStore.getGroupBonusBlocks(this.playerID); int totalBlocks = this.accruedClaimBlocks + this.getBonusClaimBlocks() + GriefPrevention.instance.dataStore.getGroupBonusBlocks(this.playerID);
if(GriefPrevention.instance.config_advanced_fixNegativeClaimblockAmounts && totalBlocks < totalClaimsArea) if (GriefPrevention.instance.config_advanced_fixNegativeClaimblockAmounts && totalBlocks < totalClaimsArea)
{ {
OfflinePlayer player = GriefPrevention.instance.getServer().getOfflinePlayer(this.playerID); OfflinePlayer player = GriefPrevention.instance.getServer().getOfflinePlayer(this.playerID);
GriefPrevention.AddLogEntry(player.getName() + " has more claimed land than blocks available. Adding blocks to fix.", CustomLogEntryTypes.Debug, true); GriefPrevention.AddLogEntry(player.getName() + " has more claimed land than blocks available. Adding blocks to fix.", CustomLogEntryTypes.Debug, true);
GriefPrevention.AddLogEntry(player.getName() + " Accrued blocks: " + this.getAccruedClaimBlocks() + " Bonus blocks: " + this.getBonusClaimBlocks(), CustomLogEntryTypes.Debug, true); GriefPrevention.AddLogEntry(player.getName() + " Accrued blocks: " + this.getAccruedClaimBlocks() + " Bonus blocks: " + this.getBonusClaimBlocks(), CustomLogEntryTypes.Debug, true);
GriefPrevention.AddLogEntry("Total blocks: " + totalBlocks + " Total claimed area: " + totalClaimsArea, CustomLogEntryTypes.Debug, true); GriefPrevention.AddLogEntry("Total blocks: " + totalBlocks + " Total claimed area: " + totalClaimsArea, CustomLogEntryTypes.Debug, true);
for(Claim claim : this.claims) for (Claim claim : this.claims)
{ {
if(!claim.inDataStore) continue; if (!claim.inDataStore) continue;
GriefPrevention.AddLogEntry( GriefPrevention.AddLogEntry(
GriefPrevention.getfriendlyLocationString(claim.getLesserBoundaryCorner()) + " // " GriefPrevention.getfriendlyLocationString(claim.getLesserBoundaryCorner()) + " // "
+ GriefPrevention.getfriendlyLocationString(claim.getGreaterBoundaryCorner()) + " = " + GriefPrevention.getfriendlyLocationString(claim.getGreaterBoundaryCorner()) + " = "
+ claim.getArea() + claim.getArea()
, CustomLogEntryTypes.Debug, true); , CustomLogEntryTypes.Debug, true);
} }
//try to fix it by adding to accrued blocks //try to fix it by adding to accrued blocks
this.accruedClaimBlocks = totalClaimsArea; //Set accrued blocks to equal total claims this.accruedClaimBlocks = totalClaimsArea; //Set accrued blocks to equal total claims
int accruedLimit = this.getAccruedClaimBlocksLimit(); int accruedLimit = this.getAccruedClaimBlocksLimit();
@ -307,7 +297,7 @@ public class PlayerData
GriefPrevention.AddLogEntry("New total blocks: " + totalBlocks, CustomLogEntryTypes.Debug, true); GriefPrevention.AddLogEntry("New total blocks: " + totalBlocks, CustomLogEntryTypes.Debug, true);
//if that didn't fix it, then make up the difference with bonus blocks //if that didn't fix it, then make up the difference with bonus blocks
if(totalBlocks < totalClaimsArea) if (totalBlocks < totalClaimsArea)
{ {
int bonusBlocksToAdd = totalClaimsArea - totalBlocks; int bonusBlocksToAdd = totalClaimsArea - totalBlocks;
this.bonusClaimBlocks += bonusBlocksToAdd; this.bonusClaimBlocks += bonusBlocksToAdd;
@ -320,18 +310,18 @@ public class PlayerData
GriefPrevention.AddLogEntry("Remaining claim blocks to use: " + this.getRemainingClaimBlocks() + " (should be 0)", CustomLogEntryTypes.Debug, true); GriefPrevention.AddLogEntry("Remaining claim blocks to use: " + this.getRemainingClaimBlocks() + " (should be 0)", CustomLogEntryTypes.Debug, true);
} }
} }
for(int i = 0; i < this.claims.size(); i++) for (int i = 0; i < this.claims.size(); i++)
{ {
if(!claims.get(i).inDataStore) if (!claims.get(i).inDataStore)
{ {
claims.remove(i--); claims.remove(i--);
} }
} }
return claims; return claims;
} }
//Limit can be changed by addons //Limit can be changed by addons
public int getAccruedClaimBlocksLimit() public int getAccruedClaimBlocksLimit()
{ {

View File

@ -15,7 +15,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import me.ryanhamshire.GriefPrevention.events.PlayerKickBanEvent; import me.ryanhamshire.GriefPrevention.events.PlayerKickBanEvent;
@ -25,47 +25,46 @@ import org.bukkit.entity.Player;
//kicks or bans a player //kicks or bans a player
//need a task for this because async threads (like the chat event handlers) can't kick or ban. //need a task for this because async threads (like the chat event handlers) can't kick or ban.
//but they CAN schedule a task to run in the main thread to do that job //but they CAN schedule a task to run in the main thread to do that job
class PlayerKickBanTask implements Runnable class PlayerKickBanTask implements Runnable
{ {
//player to kick or ban //player to kick or ban
private Player player; private Player player;
//message to send player.
private String reason;
//source of ban
private String source;
//whether to ban
private boolean ban;
public PlayerKickBanTask(Player player, String reason, String source, boolean ban)
{
this.player = player;
this.reason = reason;
this.source = source;
this.ban = ban;
}
@Override
public void run()
{
PlayerKickBanEvent kickBanEvent = new PlayerKickBanEvent(player, reason, source, ban);
Bukkit.getPluginManager().callEvent(kickBanEvent);
if (kickBanEvent.isCancelled()) //message to send player.
{ private String reason;
return; // cancelled by a plugin
}
if(this.ban) //source of ban
{ private String source;
//ban
GriefPrevention.banPlayer(this.player, this.reason, this.source); //whether to ban
} private boolean ban;
else if(this.player.isOnline())
{ public PlayerKickBanTask(Player player, String reason, String source, boolean ban)
this.player.kickPlayer(this.reason); {
} this.player = player;
} this.reason = reason;
this.source = source;
this.ban = ban;
}
@Override
public void run()
{
PlayerKickBanEvent kickBanEvent = new PlayerKickBanEvent(player, reason, source, ban);
Bukkit.getPluginManager().callEvent(kickBanEvent);
if (kickBanEvent.isCancelled())
{
return; // cancelled by a plugin
}
if (this.ban)
{
//ban
GriefPrevention.banPlayer(this.player, this.reason, this.source);
} else if (this.player.isOnline())
{
this.player.kickPlayer(this.reason);
}
}
} }

View File

@ -15,7 +15,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import org.bukkit.Location; import org.bukkit.Location;
@ -24,52 +24,51 @@ import org.bukkit.entity.Player;
//tries to rescue a trapped player from a claim where he doesn't have permission to save himself //tries to rescue a trapped player from a claim where he doesn't have permission to save himself
//related to the /trapped slash command //related to the /trapped slash command
//this does run in the main thread, so it's okay to make non-thread-safe calls //this does run in the main thread, so it's okay to make non-thread-safe calls
class PlayerRescueTask implements Runnable class PlayerRescueTask implements Runnable
{ {
//original location where /trapped was used //original location where /trapped was used
private Location location; private Location location;
//rescue destination, may be decided at instantiation or at execution //rescue destination, may be decided at instantiation or at execution
private Location destination; private Location destination;
//player data //player data
private Player player; private Player player;
public PlayerRescueTask(Player player, Location location, Location destination) public PlayerRescueTask(Player player, Location location, Location destination)
{ {
this.player = player; this.player = player;
this.location = location; this.location = location;
this.destination = destination; this.destination = destination;
} }
@Override @Override
public void run() public void run()
{ {
//if he logged out, don't do anything //if he logged out, don't do anything
if(!player.isOnline()) return; if (!player.isOnline()) return;
//he no longer has a pending /trapped slash command, so he can try to use it again now //he no longer has a pending /trapped slash command, so he can try to use it again now
PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId()); PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId());
playerData.pendingTrapped = false; playerData.pendingTrapped = false;
//if the player moved three or more blocks from where he used /trapped, admonish him and don't save him //if the player moved three or more blocks from where he used /trapped, admonish him and don't save him
if(player.getLocation().distance(this.location) > 3) if (player.getLocation().distance(this.location) > 3)
{ {
GriefPrevention.sendMessage(player, TextMode.Err, Messages.RescueAbortedMoved); GriefPrevention.sendMessage(player, TextMode.Err, Messages.RescueAbortedMoved);
return; return;
} }
//otherwise find a place to teleport him //otherwise find a place to teleport him
if(this.destination == null) if (this.destination == null)
{ {
this.destination = GriefPrevention.instance.ejectPlayer(this.player); this.destination = GriefPrevention.instance.ejectPlayer(this.player);
} } else
else {
{ player.teleport(this.destination);
player.teleport(this.destination); }
}
//log entry, in case admins want to investigate the "trap"
//log entry, in case admins want to investigate the "trap" GriefPrevention.AddLogEntry("Rescued trapped player " + player.getName() + " from " + GriefPrevention.getfriendlyLocationString(this.location) + " to " + GriefPrevention.getfriendlyLocationString(this.destination) + ".");
GriefPrevention.AddLogEntry("Rescued trapped player " + player.getName() + " from " + GriefPrevention.getfriendlyLocationString(this.location) + " to " + GriefPrevention.getfriendlyLocationString(this.destination) + "."); }
}
} }

View File

@ -15,41 +15,40 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
//sends a message to a player //sends a message to a player
//used to send delayed messages, for example help text triggered by a player's chat //used to send delayed messages, for example help text triggered by a player's chat
class PvPImmunityValidationTask implements Runnable class PvPImmunityValidationTask implements Runnable
{ {
private Player player; private Player player;
public PvPImmunityValidationTask(Player player)
{
this.player = player;
}
@Override public PvPImmunityValidationTask(Player player)
public void run() {
{ this.player = player;
if(!player.isOnline()) return; }
PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId()); @Override
if(!playerData.pvpImmune) return; public void run()
{
//check the player's inventory for anything if (!player.isOnline()) return;
if(!GriefPrevention.isInventoryEmpty(player))
{ PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId());
//if found, cancel invulnerability and notify if (!playerData.pvpImmune) return;
playerData.pvpImmune = false;
GriefPrevention.sendMessage(player, TextMode.Warn, Messages.PvPImmunityEnd); //check the player's inventory for anything
} if (!GriefPrevention.isInventoryEmpty(player))
else {
{ //if found, cancel invulnerability and notify
//otherwise check again in one minute playerData.pvpImmune = false;
GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, this, 1200L); GriefPrevention.sendMessage(player, TextMode.Warn, Messages.PvPImmunityEnd);
} } else
} {
//otherwise check again in one minute
GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, this, 1200L);
}
}
} }

View File

@ -15,10 +15,8 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.ryanhamshire.GriefPrevention;
import java.util.ArrayList; package me.ryanhamshire.GriefPrevention;
import org.bukkit.Chunk; import org.bukkit.Chunk;
import org.bukkit.Location; import org.bukkit.Location;
@ -30,101 +28,103 @@ import org.bukkit.entity.Entity;
import org.bukkit.entity.Hanging; import org.bukkit.entity.Hanging;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.ArrayList;
//this main thread task takes the output from the RestoreNatureProcessingTask\ //this main thread task takes the output from the RestoreNatureProcessingTask\
//and updates the world accordingly //and updates the world accordingly
class RestoreNatureExecutionTask implements Runnable class RestoreNatureExecutionTask implements Runnable
{ {
//results from processing thread //results from processing thread
//will be applied to the world //will be applied to the world
private BlockSnapshot[][][] snapshots; private BlockSnapshot[][][] snapshots;
//boundaries for changes
private int miny;
private Location lesserCorner;
private Location greaterCorner;
//player who should be notified about the result (will see a visualization when the restoration is complete)
private Player player;
public RestoreNatureExecutionTask(BlockSnapshot[][][] snapshots, int miny, Location lesserCorner, Location greaterCorner, Player player) //boundaries for changes
{ private int miny;
this.snapshots = snapshots; private Location lesserCorner;
this.miny = miny; private Location greaterCorner;
this.lesserCorner = lesserCorner;
this.greaterCorner = greaterCorner; //player who should be notified about the result (will see a visualization when the restoration is complete)
this.player = player; private Player player;
}
public RestoreNatureExecutionTask(BlockSnapshot[][][] snapshots, int miny, Location lesserCorner, Location greaterCorner, Player player)
@SuppressWarnings("deprecation") {
@Override this.snapshots = snapshots;
public void run() this.miny = miny;
{ this.lesserCorner = lesserCorner;
//apply changes to the world, but ONLY to unclaimed blocks this.greaterCorner = greaterCorner;
//note that the edge of the results is not applied (the 1-block-wide band around the outside of the chunk) this.player = player;
//those data were sent to the processing thread for referernce purposes, but aren't part of the area selected for restoration }
Claim cachedClaim = null;
for(int x = 1; x < this.snapshots.length - 1; x++) @SuppressWarnings("deprecation")
{ @Override
for(int z = 1; z < this.snapshots[0][0].length - 1; z++) public void run()
{ {
for(int y = this.miny; y < this.snapshots[0].length; y++) //apply changes to the world, but ONLY to unclaimed blocks
{ //note that the edge of the results is not applied (the 1-block-wide band around the outside of the chunk)
BlockSnapshot blockUpdate = this.snapshots[x][y][z]; //those data were sent to the processing thread for referernce purposes, but aren't part of the area selected for restoration
Block currentBlock = blockUpdate.location.getBlock(); Claim cachedClaim = null;
if(blockUpdate.typeId != currentBlock.getType()|| !blockUpdate.data.equals(currentBlock.getBlockData())) for (int x = 1; x < this.snapshots.length - 1; x++)
{ {
Claim claim = GriefPrevention.instance.dataStore.getClaimAt(blockUpdate.location, false, cachedClaim); for (int z = 1; z < this.snapshots[0][0].length - 1; z++)
if(claim != null) {
{ for (int y = this.miny; y < this.snapshots[0].length; y++)
cachedClaim = claim; {
break; BlockSnapshot blockUpdate = this.snapshots[x][y][z];
} Block currentBlock = blockUpdate.location.getBlock();
if (blockUpdate.typeId != currentBlock.getType() || !blockUpdate.data.equals(currentBlock.getBlockData()))
try {
{ Claim claim = GriefPrevention.instance.dataStore.getClaimAt(blockUpdate.location, false, cachedClaim);
currentBlock.setType(blockUpdate.typeId, false); if (claim != null)
// currentBlock.setBlockData(blockUpdate.data, false); {
} cachedClaim = claim;
catch(IllegalArgumentException e) break;
{ }
//just don't update this block and continue trying to update other blocks
} try
} {
} currentBlock.setType(blockUpdate.typeId, false);
} // currentBlock.setBlockData(blockUpdate.data, false);
} }
catch (IllegalArgumentException e)
//clean up any entities in the chunk, ensure no players are suffocated {
Chunk chunk = this.lesserCorner.getChunk(); //just don't update this block and continue trying to update other blocks
Entity [] entities = chunk.getEntities(); }
for(int i = 0; i < entities.length; i++) }
{ }
Entity entity = entities[i]; }
if(!(entity instanceof Player || entity instanceof Animals)) }
{
//hanging entities (paintings, item frames) are protected when they're in land claims //clean up any entities in the chunk, ensure no players are suffocated
if(!(entity instanceof Hanging) || GriefPrevention.instance.dataStore.getClaimAt(entity.getLocation(), false, null) == null) Chunk chunk = this.lesserCorner.getChunk();
{ Entity[] entities = chunk.getEntities();
//everything else is removed for (int i = 0; i < entities.length; i++)
entity.remove(); {
} Entity entity = entities[i];
} if (!(entity instanceof Player || entity instanceof Animals))
{
//for players, always ensure there's air where the player is standing //hanging entities (paintings, item frames) are protected when they're in land claims
else if (!(entity instanceof Hanging) || GriefPrevention.instance.dataStore.getClaimAt(entity.getLocation(), false, null) == null)
{ {
Block feetBlock = entity.getLocation().getBlock(); //everything else is removed
feetBlock.setType(Material.AIR); entity.remove();
feetBlock.getRelative(BlockFace.UP).setType(Material.AIR); }
} }
}
//for players, always ensure there's air where the player is standing
//show visualization to player who started the restoration else
if(player != null) {
{ Block feetBlock = entity.getLocation().getBlock();
Claim claim = new Claim(lesserCorner, greaterCorner, null, new ArrayList<String>(), new ArrayList<String>(), new ArrayList<String>(), new ArrayList<String>(), null); feetBlock.setType(Material.AIR);
Visualization visualization = Visualization.FromClaim(claim, player.getLocation().getBlockY(), VisualizationType.RestoreNature, player.getLocation()); feetBlock.getRelative(BlockFace.UP).setType(Material.AIR);
Visualization.Apply(player, visualization); }
} }
}
//show visualization to player who started the restoration
if (player != 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

@ -15,44 +15,44 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.ryanhamshire.GriefPrevention;
import java.util.Collection; package me.ryanhamshire.GriefPrevention;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.Collection;
//secures a claim after a siege looting window has closed //secures a claim after a siege looting window has closed
class SecureClaimTask implements Runnable class SecureClaimTask implements Runnable
{ {
private SiegeData siegeData; private SiegeData siegeData;
public SecureClaimTask(SiegeData siegeData) public SecureClaimTask(SiegeData siegeData)
{ {
this.siegeData = siegeData; this.siegeData = siegeData;
} }
@Override @Override
public void run() public void run()
{ {
//for each claim involved in this siege //for each claim involved in this siege
for(int i = 0; i < this.siegeData.claims.size(); i++) for (int i = 0; i < this.siegeData.claims.size(); i++)
{ {
//lock the doors //lock the doors
Claim claim = this.siegeData.claims.get(i); Claim claim = this.siegeData.claims.get(i);
claim.doorsOpen = false; claim.doorsOpen = false;
//eject bad guys //eject bad guys
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Collection<Player> onlinePlayers = (Collection<Player>)GriefPrevention.instance.getServer().getOnlinePlayers(); Collection<Player> onlinePlayers = (Collection<Player>) GriefPrevention.instance.getServer().getOnlinePlayers();
for(Player player : onlinePlayers) for (Player player : onlinePlayers)
{ {
if(claim.contains(player.getLocation(), false, false) && claim.allowAccess(player) != null) if (claim.contains(player.getLocation(), false, false) && claim.allowAccess(player) != null)
{ {
GriefPrevention.sendMessage(player, TextMode.Err, Messages.SiegeDoorsLockedEjection); GriefPrevention.sendMessage(player, TextMode.Err, Messages.SiegeDoorsLockedEjection);
GriefPrevention.instance.ejectPlayer(player); GriefPrevention.instance.ejectPlayer(player);
} }
} }
} }
} }
} }

View File

@ -15,7 +15,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
@ -23,39 +23,39 @@ import org.bukkit.entity.Player;
//sends a message to a player //sends a message to a player
//used to send delayed messages, for example help text triggered by a player's chat //used to send delayed messages, for example help text triggered by a player's chat
class SendPlayerMessageTask implements Runnable class SendPlayerMessageTask implements Runnable
{ {
private Player player; private Player player;
private ChatColor color; private ChatColor color;
private String message; private String message;
public SendPlayerMessageTask(Player player, ChatColor color, String message)
{
this.player = player;
this.color = color;
this.message = message;
}
@Override public SendPlayerMessageTask(Player player, ChatColor color, String message)
public void run() {
{ this.player = player;
if(player == null) this.color = color;
{ this.message = message;
GriefPrevention.AddLogEntry(color + message); }
return;
} @Override
public void run()
//if the player is dead, save it for after his respawn {
if(this.player.isDead()) if (player == null)
{ {
PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(this.player.getUniqueId()); GriefPrevention.AddLogEntry(color + message);
playerData.messageOnRespawn = this.color + this.message; return;
} }
//otherwise send it immediately //if the player is dead, save it for after his respawn
else if (this.player.isDead())
{ {
GriefPrevention.sendMessage(this.player, this.color, this.message); PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(this.player.getUniqueId());
} playerData.messageOnRespawn = this.color + this.message;
} }
//otherwise send it immediately
else
{
GriefPrevention.sendMessage(this.player, this.color, this.message);
}
}
} }

View File

@ -15,16 +15,16 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
//enumeration for golden shovel modes //enumeration for golden shovel modes
public enum ShovelMode public enum ShovelMode
{ {
Basic, Basic,
Admin, Admin,
Subdivide, Subdivide,
RestoreNature, RestoreNature,
RestoreNatureAggressive, RestoreNatureAggressive,
RestoreNatureFill RestoreNatureFill
} }

View File

@ -22,89 +22,89 @@ import org.bukkit.entity.Player;
//checks to see whether or not a siege should end based on the locations of the players //checks to see whether or not a siege should end based on the locations of the players
//for example, defender escaped or attacker gave up and left //for example, defender escaped or attacker gave up and left
class SiegeCheckupTask implements Runnable class SiegeCheckupTask implements Runnable
{ {
private SiegeData siegeData; private SiegeData siegeData;
public SiegeCheckupTask(SiegeData siegeData) public SiegeCheckupTask(SiegeData siegeData)
{ {
this.siegeData = siegeData; this.siegeData = siegeData;
} }
@Override @Override
public void run() public void run()
{ {
DataStore dataStore = GriefPrevention.instance.dataStore; DataStore dataStore = GriefPrevention.instance.dataStore;
Player defender = this.siegeData.defender; Player defender = this.siegeData.defender;
Player attacker = this.siegeData.attacker; Player attacker = this.siegeData.attacker;
//where is the defender? //where is the defender?
Claim defenderClaim = dataStore.getClaimAt(defender.getLocation(), false, null); Claim defenderClaim = dataStore.getClaimAt(defender.getLocation(), false, null);
//if this is a new claim and he has some permission there, extend the siege to include it //if this is a new claim and he has some permission there, extend the siege to include it
if(defenderClaim != null) if (defenderClaim != null)
{ {
String noAccessReason = defenderClaim.allowAccess(defender); String noAccessReason = defenderClaim.allowAccess(defender);
if(defenderClaim.canSiege(defender) && noAccessReason == null) if (defenderClaim.canSiege(defender) && noAccessReason == null)
{ {
this.siegeData.claims.add(defenderClaim); this.siegeData.claims.add(defenderClaim);
defenderClaim.siegeData = this.siegeData; defenderClaim.siegeData = this.siegeData;
} }
} }
//determine who's close enough to the siege area to be considered "still here" //determine who's close enough to the siege area to be considered "still here"
boolean attackerRemains = this.playerRemains(attacker); boolean attackerRemains = this.playerRemains(attacker);
boolean defenderRemains = this.playerRemains(defender); boolean defenderRemains = this.playerRemains(defender);
//if they're both here, just plan to come check again later //if they're both here, just plan to come check again later
if(attackerRemains && defenderRemains) if (attackerRemains && defenderRemains)
{ {
this.scheduleAnotherCheck(); this.scheduleAnotherCheck();
} }
//otherwise attacker wins if the defender runs away //otherwise attacker wins if the defender runs away
else if(attackerRemains && !defenderRemains) else if (attackerRemains && !defenderRemains)
{ {
dataStore.endSiege(this.siegeData, attacker.getName(), defender.getName(), null); dataStore.endSiege(this.siegeData, attacker.getName(), defender.getName(), null);
} }
//or defender wins if the attacker leaves //or defender wins if the attacker leaves
else if(!attackerRemains && defenderRemains) else if (!attackerRemains && defenderRemains)
{ {
dataStore.endSiege(this.siegeData, defender.getName(), attacker.getName(), null); dataStore.endSiege(this.siegeData, defender.getName(), attacker.getName(), null);
} }
//if they both left, but are still close together, the battle continues (check again later) //if they both left, but are still close together, the battle continues (check again later)
else if(attacker.getWorld().equals(defender.getWorld()) && attacker.getLocation().distanceSquared(defender.getLocation()) < 2500) //50-block radius for chasing else if (attacker.getWorld().equals(defender.getWorld()) && attacker.getLocation().distanceSquared(defender.getLocation()) < 2500) //50-block radius for chasing
{ {
this.scheduleAnotherCheck(); this.scheduleAnotherCheck();
} }
//otherwise they both left and aren't close to each other, so call the attacker the winner (defender escaped, possibly after a chase) //otherwise they both left and aren't close to each other, so call the attacker the winner (defender escaped, possibly after a chase)
else else
{ {
dataStore.endSiege(this.siegeData, attacker.getName(), defender.getName(), null); dataStore.endSiege(this.siegeData, attacker.getName(), defender.getName(), null);
} }
} }
//a player has to be within 25 blocks of the edge of a besieged claim to be considered still in the fight //a player has to be within 25 blocks of the edge of a besieged claim to be considered still in the fight
private boolean playerRemains(Player player) private boolean playerRemains(Player player)
{ {
for(int i = 0; i < this.siegeData.claims.size(); i++) for (int i = 0; i < this.siegeData.claims.size(); i++)
{ {
Claim claim = this.siegeData.claims.get(i); Claim claim = this.siegeData.claims.get(i);
if(claim.isNear(player.getLocation(), 25)) if (claim.isNear(player.getLocation(), 25))
{ {
return true; return true;
} }
} }
return false; return false;
} }
//schedules another checkup later //schedules another checkup later
private void scheduleAnotherCheck() private void scheduleAnotherCheck()
{ {
this.siegeData.checkupTaskID = GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, this, 20L * 30); this.siegeData.checkupTaskID = GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, this, 20L * 30);
} }
} }

View File

@ -18,23 +18,23 @@
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import java.util.ArrayList;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.ArrayList;
//information about an ongoing siege //information about an ongoing siege
public class SiegeData public class SiegeData
{ {
public Player defender; public Player defender;
public Player attacker; public Player attacker;
public ArrayList<Claim> claims; public ArrayList<Claim> claims;
public int checkupTaskID; public int checkupTaskID;
public SiegeData(Player attacker, Player defender, Claim claim) public SiegeData(Player attacker, Player defender, Claim claim)
{ {
this.defender = defender; this.defender = defender;
this.attacker = attacker; this.attacker = attacker;
this.claims = new ArrayList<Claim>(); this.claims = new ArrayList<Claim>();
this.claims.add(claim); this.claims.add(claim);
} }
} }

View File

@ -9,206 +9,204 @@ class SpamDetector
//last chat message shown and its timestamp, regardless of who sent it //last chat message shown and its timestamp, regardless of who sent it
private String lastChatMessage = ""; private String lastChatMessage = "";
private long lastChatMessageTimestamp = 0; private long lastChatMessageTimestamp = 0;
//number of identical chat messages in a row //number of identical chat messages in a row
private int duplicateMessageCount = 0; private int duplicateMessageCount = 0;
//data for individual chatters //data for individual chatters
ConcurrentHashMap<UUID, ChatterData> dataStore = new ConcurrentHashMap<UUID, ChatterData>(); ConcurrentHashMap<UUID, ChatterData> dataStore = new ConcurrentHashMap<UUID, ChatterData>();
private ChatterData getChatterData(UUID chatterID) private ChatterData getChatterData(UUID chatterID)
{ {
ChatterData data = this.dataStore.get(chatterID); ChatterData data = this.dataStore.get(chatterID);
if(data == null) if (data == null)
{ {
data = new ChatterData(); data = new ChatterData();
this.dataStore.put(chatterID, data); this.dataStore.put(chatterID, data);
} }
return data; return data;
} }
SpamAnalysisResult AnalyzeMessage(UUID chatterID, String message, long timestamp) SpamAnalysisResult AnalyzeMessage(UUID chatterID, String message, long timestamp)
{ {
SpamAnalysisResult result = new SpamAnalysisResult(); SpamAnalysisResult result = new SpamAnalysisResult();
result.finalMessage = message; result.finalMessage = message;
//remedy any CAPS SPAM, exception for very short messages which could be emoticons like =D or XD //remedy any CAPS SPAM, exception for very short messages which could be emoticons like =D or XD
if(message.length() > 4 && this.stringsAreSimilar(message.toUpperCase(), message)) if (message.length() > 4 && this.stringsAreSimilar(message.toUpperCase(), message))
{ {
message = message.toLowerCase(); message = message.toLowerCase();
result.finalMessage = message; result.finalMessage = message;
} }
boolean spam = false; boolean spam = false;
ChatterData chatterData = this.getChatterData(chatterID); ChatterData chatterData = this.getChatterData(chatterID);
//mute if total volume of text from this player is too high //mute if total volume of text from this player is too high
if(message.length() > 50 && chatterData.getTotalRecentLength(timestamp) > 200) if (message.length() > 50 && chatterData.getTotalRecentLength(timestamp) > 200)
{ {
spam = true; spam = true;
result.muteReason = "too much chat sent in 10 seconds"; result.muteReason = "too much chat sent in 10 seconds";
chatterData.spamLevel++; chatterData.spamLevel++;
} }
//always mute an exact match to the last chat message //always mute an exact match to the last chat message
if(result.finalMessage.equals(this.lastChatMessage) && timestamp - this.lastChatMessageTimestamp < 2000) if (result.finalMessage.equals(this.lastChatMessage) && timestamp - this.lastChatMessageTimestamp < 2000)
{ {
chatterData.spamLevel += ++this.duplicateMessageCount; chatterData.spamLevel += ++this.duplicateMessageCount;
spam = true; spam = true;
result.muteReason = "repeat message"; result.muteReason = "repeat message";
} } else
else
{ {
this.lastChatMessage = message; this.lastChatMessage = message;
this.lastChatMessageTimestamp = timestamp; this.lastChatMessageTimestamp = timestamp;
this.duplicateMessageCount = 0; this.duplicateMessageCount = 0;
} }
//check message content and timing //check message content and timing
long millisecondsSinceLastMessage = timestamp - chatterData.lastMessageTimestamp; long millisecondsSinceLastMessage = timestamp - chatterData.lastMessageTimestamp;
//if the message came too close to the last one //if the message came too close to the last one
if(millisecondsSinceLastMessage < 1500) if (millisecondsSinceLastMessage < 1500)
{ {
//increment the spam counter //increment the spam counter
chatterData.spamLevel++; chatterData.spamLevel++;
spam = true; spam = true;
} }
//if it's exactly the same as the last message from the same player and within 30 seconds //if it's exactly the same as the last message from the same player and within 30 seconds
if(result.muteReason == null && millisecondsSinceLastMessage < 30000 && result.finalMessage.equalsIgnoreCase(chatterData.lastMessage)) if (result.muteReason == null && millisecondsSinceLastMessage < 30000 && result.finalMessage.equalsIgnoreCase(chatterData.lastMessage))
{ {
chatterData.spamLevel++; chatterData.spamLevel++;
spam = true; spam = true;
result.muteReason = "repeat message"; result.muteReason = "repeat message";
} }
//if it's very similar to the last message from the same player and within 10 seconds of that message //if it's very similar to the last message from the same player and within 10 seconds of that message
if(result.muteReason == null && millisecondsSinceLastMessage < 10000 && this.stringsAreSimilar(message.toLowerCase(), chatterData.lastMessage.toLowerCase())) if (result.muteReason == null && millisecondsSinceLastMessage < 10000 && this.stringsAreSimilar(message.toLowerCase(), chatterData.lastMessage.toLowerCase()))
{ {
chatterData.spamLevel++; chatterData.spamLevel++;
spam = true; spam = true;
if(chatterData.spamLevel > 2) if (chatterData.spamLevel > 2)
{ {
result.muteReason = "similar message"; result.muteReason = "similar message";
} }
} }
//if the message was mostly non-alpha-numerics or doesn't include much whitespace, consider it a spam (probably ansi art or random text gibberish) //if the message was mostly non-alpha-numerics or doesn't include much whitespace, consider it a spam (probably ansi art or random text gibberish)
if(result.muteReason == null && message.length() > 5) if (result.muteReason == null && message.length() > 5)
{ {
int symbolsCount = 0; int symbolsCount = 0;
int whitespaceCount = 0; int whitespaceCount = 0;
for(int i = 0; i < message.length(); i++) for (int i = 0; i < message.length(); i++)
{ {
char character = message.charAt(i); char character = message.charAt(i);
if(!(Character.isLetterOrDigit(character))) if (!(Character.isLetterOrDigit(character)))
{ {
symbolsCount++; symbolsCount++;
} }
if(Character.isWhitespace(character)) if (Character.isWhitespace(character))
{ {
whitespaceCount++; whitespaceCount++;
} }
} }
if(symbolsCount > message.length() / 2 || (message.length() > 15 && whitespaceCount < message.length() / 10)) if (symbolsCount > message.length() / 2 || (message.length() > 15 && whitespaceCount < message.length() / 10))
{ {
spam = true; spam = true;
if(chatterData.spamLevel > 0) result.muteReason = "gibberish"; if (chatterData.spamLevel > 0) result.muteReason = "gibberish";
chatterData.spamLevel++; chatterData.spamLevel++;
} }
} }
//very short messages close together are spam //very short messages close together are spam
if(result.muteReason == null && message.length() < 5 && millisecondsSinceLastMessage < 3000) if (result.muteReason == null && message.length() < 5 && millisecondsSinceLastMessage < 3000)
{ {
spam = true; spam = true;
chatterData.spamLevel++; chatterData.spamLevel++;
} }
//if the message was determined to be a spam, consider taking action //if the message was determined to be a spam, consider taking action
if(spam) if (spam)
{ {
//anything above level 8 for a player which has received a warning... kick or if enabled, ban //anything above level 8 for a player which has received a warning... kick or if enabled, ban
if(chatterData.spamLevel > 8 && chatterData.spamWarned) if (chatterData.spamLevel > 8 && chatterData.spamWarned)
{ {
result.shouldBanChatter = true; result.shouldBanChatter = true;
} } else if (chatterData.spamLevel >= 4)
else if(chatterData.spamLevel >= 4)
{ {
if(!chatterData.spamWarned) if (!chatterData.spamWarned)
{ {
chatterData.spamWarned = true; chatterData.spamWarned = true;
result.shouldWarnChatter = true; result.shouldWarnChatter = true;
} }
if(result.muteReason == null) if (result.muteReason == null)
{ {
result.muteReason = "too-frequent text"; result.muteReason = "too-frequent text";
} }
} }
} }
//otherwise if not a spam, reduce the spam level for this player //otherwise if not a spam, reduce the spam level for this player
else else
{ {
chatterData.spamLevel = 0; chatterData.spamLevel = 0;
chatterData.spamWarned = false; chatterData.spamWarned = false;
} }
chatterData.AddMessage(message, timestamp); chatterData.AddMessage(message, timestamp);
return result; return result;
} }
//if two strings are 75% identical, they're too close to follow each other in the chat //if two strings are 75% identical, they're too close to follow each other in the chat
private boolean stringsAreSimilar(String message, String lastMessage) private boolean stringsAreSimilar(String message, String lastMessage)
{ {
//ignore differences in only punctuation and whitespace //ignore differences in only punctuation and whitespace
message = message.replaceAll("[^\\p{Alpha}]", ""); message = message.replaceAll("[^\\p{Alpha}]", "");
lastMessage = lastMessage.replaceAll("[^\\p{Alpha}]", ""); lastMessage = lastMessage.replaceAll("[^\\p{Alpha}]", "");
//determine which is shorter //determine which is shorter
String shorterString, longerString; String shorterString, longerString;
if(lastMessage.length() < message.length()) if (lastMessage.length() < message.length())
{ {
shorterString = lastMessage; shorterString = lastMessage;
longerString = message; longerString = message;
} } else
else
{ {
shorterString = message; shorterString = message;
longerString = lastMessage; longerString = lastMessage;
} }
if(shorterString.length() <= 5) return shorterString.equals(longerString); if (shorterString.length() <= 5) return shorterString.equals(longerString);
//set similarity tolerance //set similarity tolerance
int maxIdenticalCharacters = longerString.length() - longerString.length() / 4; int maxIdenticalCharacters = longerString.length() - longerString.length() / 4;
//trivial check on length //trivial check on length
if(shorterString.length() < maxIdenticalCharacters) return false; if (shorterString.length() < maxIdenticalCharacters) return false;
//compare forward //compare forward
int identicalCount = 0; int identicalCount = 0;
int i; int i;
for(i = 0; i < shorterString.length(); i++) for (i = 0; i < shorterString.length(); i++)
{ {
if(shorterString.charAt(i) == longerString.charAt(i)) identicalCount++; if (shorterString.charAt(i) == longerString.charAt(i)) identicalCount++;
if(identicalCount > maxIdenticalCharacters) return true; if (identicalCount > maxIdenticalCharacters) return true;
} }
//compare backward //compare backward
int j; int j;
for(j = 0; j < shorterString.length() - i; j++) for (j = 0; j < shorterString.length() - i; j++)
{ {
if(shorterString.charAt(shorterString.length() - j - 1) == longerString.charAt(longerString.length() - j - 1)) identicalCount++; if (shorterString.charAt(shorterString.length() - j - 1) == longerString.charAt(longerString.length() - j - 1))
if(identicalCount > maxIdenticalCharacters) return true; identicalCount++;
if (identicalCount > maxIdenticalCharacters) return true;
} }
return false; return false;
} }
} }
@ -227,17 +225,17 @@ class ChatterData
public long lastMessageTimestamp; //last time the player sent a chat message or used a monitored slash command public long lastMessageTimestamp; //last time the player sent a chat message or used a monitored slash command
public int spamLevel = 0; //number of consecutive "spams" public int spamLevel = 0; //number of consecutive "spams"
public boolean spamWarned = false; //whether the player has received a warning recently public boolean spamWarned = false; //whether the player has received a warning recently
//all recent message lengths and their total //all recent message lengths and their total
private ConcurrentLinkedQueue<LengthTimestampPair> recentMessageLengths = new ConcurrentLinkedQueue<LengthTimestampPair>(); private ConcurrentLinkedQueue<LengthTimestampPair> recentMessageLengths = new ConcurrentLinkedQueue<LengthTimestampPair>();
private int recentTotalLength = 0; private int recentTotalLength = 0;
public void AddMessage(String message, long timestamp) public void AddMessage(String message, long timestamp)
{ {
int length = message.length(); int length = message.length();
this.recentMessageLengths.add(new LengthTimestampPair(length, timestamp)); this.recentMessageLengths.add(new LengthTimestampPair(length, timestamp));
this.recentTotalLength += length; this.recentTotalLength += length;
this.lastMessage = message; this.lastMessage = message;
this.lastMessageTimestamp = timestamp; this.lastMessageTimestamp = timestamp;
} }
@ -245,13 +243,13 @@ class ChatterData
public int getTotalRecentLength(long timestamp) public int getTotalRecentLength(long timestamp)
{ {
LengthTimestampPair oldestPair = this.recentMessageLengths.peek(); LengthTimestampPair oldestPair = this.recentMessageLengths.peek();
while(oldestPair != null && timestamp - oldestPair.timestamp > 10000) while (oldestPair != null && timestamp - oldestPair.timestamp > 10000)
{ {
this.recentMessageLengths.poll(); this.recentMessageLengths.poll();
this.recentTotalLength -= oldestPair.length; this.recentTotalLength -= oldestPair.length;
oldestPair = this.recentMessageLengths.peek(); oldestPair = this.recentMessageLengths.peek();
} }
return this.recentTotalLength; return this.recentTotalLength;
} }
} }
@ -260,7 +258,7 @@ class LengthTimestampPair
{ {
public long timestamp; public long timestamp;
public int length; public int length;
public LengthTimestampPair(int length, long timestamp) public LengthTimestampPair(int length, long timestamp)
{ {
this.length = length; this.length = length;

View File

@ -21,11 +21,11 @@ package me.ryanhamshire.GriefPrevention;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
//just a few constants for chat color codes //just a few constants for chat color codes
public class TextMode public class TextMode
{ {
final static ChatColor Info = ChatColor.AQUA; final static ChatColor Info = ChatColor.AQUA;
final static ChatColor Instr = ChatColor.YELLOW; final static ChatColor Instr = ChatColor.YELLOW;
final static ChatColor Warn = ChatColor.GOLD; final static ChatColor Warn = ChatColor.GOLD;
final static ChatColor Err = ChatColor.RED; final static ChatColor Err = ChatColor.RED;
final static ChatColor Success = ChatColor.GREEN; final static ChatColor Success = ChatColor.GREEN;
} }

View File

@ -7,93 +7,98 @@ import org.bukkit.OfflinePlayer;
import org.json.simple.JSONArray; import org.json.simple.JSONArray;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser; import org.json.simple.parser.JSONParser;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.*; import java.util.HashMap;
import java.util.List;
class UUIDFetcher { import java.util.UUID;
class UUIDFetcher
{
private static int PROFILES_PER_REQUEST = 100; private static int PROFILES_PER_REQUEST = 100;
private static final String PROFILE_URL = "https://api.mojang.com/profiles/minecraft"; private static final String PROFILE_URL = "https://api.mojang.com/profiles/minecraft";
private final JSONParser jsonParser = new JSONParser(); private final JSONParser jsonParser = new JSONParser();
private final List<String> names; private final List<String> names;
private final boolean rateLimiting; private final boolean rateLimiting;
//cache for username -> uuid lookups //cache for username -> uuid lookups
static HashMap<String, UUID> lookupCache; static HashMap<String, UUID> lookupCache;
//record of username -> proper casing updates //record of username -> proper casing updates
static HashMap<String, String> correctedNames; static HashMap<String, String> correctedNames;
public UUIDFetcher(List<String> names, boolean rateLimiting) { public UUIDFetcher(List<String> names, boolean rateLimiting)
{
this.names = names; this.names = names;
this.rateLimiting = rateLimiting; this.rateLimiting = rateLimiting;
} }
public UUIDFetcher(List<String> names) { public UUIDFetcher(List<String> names)
{
this(names, true); this(names, true);
} }
public void call() throws Exception public void call() throws Exception
{ {
if(lookupCache == null) if (lookupCache == null)
{ {
lookupCache = new HashMap<String, UUID>(); lookupCache = new HashMap<String, UUID>();
} }
if(correctedNames == null) if (correctedNames == null)
{ {
correctedNames = new HashMap<String, String>(); correctedNames = new HashMap<String, String>();
} }
GriefPrevention.AddLogEntry("UUID conversion process started. Please be patient - this may take a while."); GriefPrevention.AddLogEntry("UUID conversion process started. Please be patient - this may take a while.");
GriefPrevention.AddLogEntry("Mining your local world data to save calls to Mojang..."); GriefPrevention.AddLogEntry("Mining your local world data to save calls to Mojang...");
OfflinePlayer [] players = GriefPrevention.instance.getServer().getOfflinePlayers(); OfflinePlayer[] players = GriefPrevention.instance.getServer().getOfflinePlayers();
for(OfflinePlayer player : players) for (OfflinePlayer player : players)
{ {
if(player.getName() != null && player.getUniqueId() != null) if (player.getName() != null && player.getUniqueId() != null)
{ {
lookupCache.put(player.getName(), player.getUniqueId()); lookupCache.put(player.getName(), player.getUniqueId());
lookupCache.put(player.getName().toLowerCase(), player.getUniqueId()); lookupCache.put(player.getName().toLowerCase(), player.getUniqueId());
correctedNames.put(player.getName().toLowerCase(), player.getName()); correctedNames.put(player.getName().toLowerCase(), player.getName());
} }
} }
//try to get correct casing from local data //try to get correct casing from local data
GriefPrevention.AddLogEntry("Checking local server data to get correct casing for player names..."); GriefPrevention.AddLogEntry("Checking local server data to get correct casing for player names...");
for(int i = 0; i < names.size(); i++) for (int i = 0; i < names.size(); i++)
{ {
String name = names.get(i); String name = names.get(i);
String correctCasingName = correctedNames.get(name); String correctCasingName = correctedNames.get(name);
if(correctCasingName != null && !name.equals(correctCasingName)) if (correctCasingName != null && !name.equals(correctCasingName))
{ {
GriefPrevention.AddLogEntry(name + " --> " + correctCasingName); GriefPrevention.AddLogEntry(name + " --> " + correctCasingName);
names.set(i, correctCasingName); names.set(i, correctCasingName);
} }
} }
//look for local uuid's first //look for local uuid's first
GriefPrevention.AddLogEntry("Checking local server data for UUIDs already seen..."); GriefPrevention.AddLogEntry("Checking local server data for UUIDs already seen...");
for(int i = 0; i < names.size(); i++) for (int i = 0; i < names.size(); i++)
{ {
String name = names.get(i); String name = names.get(i);
UUID uuid = lookupCache.get(name); UUID uuid = lookupCache.get(name);
if(uuid != null) if (uuid != null)
{ {
GriefPrevention.AddLogEntry(name + " --> " + uuid.toString()); GriefPrevention.AddLogEntry(name + " --> " + uuid.toString());
names.remove(i--); names.remove(i--);
} }
} }
//for online mode, call Mojang to resolve the rest //for online mode, call Mojang to resolve the rest
if(GriefPrevention.instance.getServer().getOnlineMode()) if (GriefPrevention.instance.getServer().getOnlineMode())
{ {
GriefPrevention.AddLogEntry("Calling Mojang to get UUIDs for remaining unresolved players (this is the slowest step)..."); GriefPrevention.AddLogEntry("Calling Mojang to get UUIDs for remaining unresolved players (this is the slowest step)...");
for (int i = 0; i * PROFILES_PER_REQUEST < names.size(); i++) for (int i = 0; i * PROFILES_PER_REQUEST < names.size(); i++)
{ {
boolean retry = false; boolean retry = false;
@ -109,21 +114,21 @@ class UUIDFetcher {
{ {
array = (JSONArray) jsonParser.parse(new InputStreamReader(connection.getInputStream())); array = (JSONArray) jsonParser.parse(new InputStreamReader(connection.getInputStream()));
} }
catch(Exception e) catch (Exception e)
{ {
//in case of error 429 too many requests, pause and then retry later //in case of error 429 too many requests, pause and then retry later
if(e.getMessage().contains("429")) if (e.getMessage().contains("429"))
{ {
retry = true; retry = true;
//if this is the first time we're sending anything, the batch size must be too big //if this is the first time we're sending anything, the batch size must be too big
//try reducing it //try reducing it
if(i == 0 && PROFILES_PER_REQUEST > 1) if (i == 0 && PROFILES_PER_REQUEST > 1)
{ {
GriefPrevention.AddLogEntry("Batch size " + PROFILES_PER_REQUEST + " seems too large. Looking for a workable batch size..."); GriefPrevention.AddLogEntry("Batch size " + PROFILES_PER_REQUEST + " seems too large. Looking for a workable batch size...");
PROFILES_PER_REQUEST = Math.max(PROFILES_PER_REQUEST - 5, 1); PROFILES_PER_REQUEST = Math.max(PROFILES_PER_REQUEST - 5, 1);
} }
//otherwise, keep the batch size which has worked for previous iterations //otherwise, keep the batch size which has worked for previous iterations
//but wait a little while before trying again. //but wait a little while before trying again.
else else
@ -131,15 +136,15 @@ class UUIDFetcher {
GriefPrevention.AddLogEntry("Mojang says we're sending requests too fast. Will retry every 30 seconds until we succeed..."); GriefPrevention.AddLogEntry("Mojang says we're sending requests too fast. Will retry every 30 seconds until we succeed...");
Thread.sleep(30000); Thread.sleep(30000);
} }
} } else
else
{ {
throw e; throw e;
} }
} }
}while(retry); } while (retry);
for (Object profile : array) { for (Object profile : array)
{
JSONObject jsonProfile = (JSONObject) profile; JSONObject jsonProfile = (JSONObject) profile;
String id = (String) jsonProfile.get("id"); String id = (String) jsonProfile.get("id");
String name = (String) jsonProfile.get("name"); String name = (String) jsonProfile.get("name");
@ -148,18 +153,19 @@ class UUIDFetcher {
lookupCache.put(name, uuid); lookupCache.put(name, uuid);
lookupCache.put(name.toLowerCase(), uuid); lookupCache.put(name.toLowerCase(), uuid);
} }
if (rateLimiting) { if (rateLimiting)
{
Thread.sleep(200L); Thread.sleep(200L);
} }
} }
} }
//for offline mode, generate UUIDs for the rest //for offline mode, generate UUIDs for the rest
else else
{ {
GriefPrevention.AddLogEntry("Generating offline mode UUIDs for remaining unresolved players..."); GriefPrevention.AddLogEntry("Generating offline mode UUIDs for remaining unresolved players...");
for(int i = 0; i < names.size(); i++) for (int i = 0; i < names.size(); i++)
{ {
String name = names.get(i); String name = names.get(i);
UUID uuid = java.util.UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8)); UUID uuid = java.util.UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8));
@ -169,15 +175,17 @@ class UUIDFetcher {
} }
} }
} }
private static void writeBody(HttpURLConnection connection, String body) throws Exception { private static void writeBody(HttpURLConnection connection, String body) throws Exception
{
OutputStream stream = connection.getOutputStream(); OutputStream stream = connection.getOutputStream();
stream.write(body.getBytes()); stream.write(body.getBytes());
stream.flush(); stream.flush();
stream.close(); stream.close();
} }
private static HttpURLConnection createConnection() throws Exception { private static HttpURLConnection createConnection() throws Exception
{
URL url = new URL(PROFILE_URL); URL url = new URL(PROFILE_URL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection(); HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST"); connection.setRequestMethod("POST");
@ -187,20 +195,24 @@ class UUIDFetcher {
connection.setDoOutput(true); connection.setDoOutput(true);
return connection; return connection;
} }
private static UUID getUUID(String id) { private static UUID getUUID(String id)
return UUID.fromString(id.substring(0, 8) + "-" + id.substring(8, 12) + "-" + id.substring(12, 16) + "-" + id.substring(16, 20) + "-" +id.substring(20, 32)); {
return UUID.fromString(id.substring(0, 8) + "-" + id.substring(8, 12) + "-" + id.substring(12, 16) + "-" + id.substring(16, 20) + "-" + id.substring(20, 32));
} }
public static byte[] toBytes(UUID uuid) { public static byte[] toBytes(UUID uuid)
{
ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[16]); ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[16]);
byteBuffer.putLong(uuid.getMostSignificantBits()); byteBuffer.putLong(uuid.getMostSignificantBits());
byteBuffer.putLong(uuid.getLeastSignificantBits()); byteBuffer.putLong(uuid.getLeastSignificantBits());
return byteBuffer.array(); return byteBuffer.array();
} }
public static UUID fromBytes(byte[] array) { public static UUID fromBytes(byte[] array)
if (array.length != 16) { {
if (array.length != 16)
{
throw new IllegalArgumentException("Illegal byte array length: " + array.length); throw new IllegalArgumentException("Illegal byte array length: " + array.length);
} }
ByteBuffer byteBuffer = ByteBuffer.wrap(array); ByteBuffer byteBuffer = ByteBuffer.wrap(array);
@ -208,17 +220,17 @@ class UUIDFetcher {
long leastSignificant = byteBuffer.getLong(); long leastSignificant = byteBuffer.getLong();
return new UUID(mostSignificant, leastSignificant); return new UUID(mostSignificant, leastSignificant);
} }
public static UUID getUUIDOf(String name) throws Exception public static UUID getUUIDOf(String name) throws Exception
{ {
UUID result = lookupCache.get(name); UUID result = lookupCache.get(name);
if(result == null) if (result == null)
{ {
//throw up our hands and report the problem in the logs //throw up our hands and report the problem in the logs
//this player will lose his land claim blocks, but claims will stay in place as admin claims //this player will lose his land claim blocks, but claims will stay in place as admin claims
throw new IllegalArgumentException(name); throw new IllegalArgumentException(name);
} }
return result; return result;
} }
} }

View File

@ -18,8 +18,6 @@
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import java.util.ArrayList;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Tag; import org.bukkit.Tag;
@ -30,306 +28,300 @@ import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Lightable; import org.bukkit.block.data.Lightable;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.ArrayList;
//represents a visualization sent to a player //represents a visualization sent to a player
//FEATURE: to show players visually where claim boundaries are, we send them fake block change packets //FEATURE: to show players visually where claim boundaries are, we send them fake block change packets
//the result is that those players see new blocks, but the world hasn't been changed. other players can't see the new blocks, either. //the result is that those players see new blocks, but the world hasn't been changed. other players can't see the new blocks, either.
public class Visualization public class Visualization
{ {
public ArrayList<VisualizationElement> elements = new ArrayList<VisualizationElement>(); public ArrayList<VisualizationElement> elements = new ArrayList<VisualizationElement>();
//sends a visualization to a player
public static void Apply(Player player, Visualization visualization)
{
PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId());
//if he has any current visualization, clear it first
if(playerData.currentVisualization != null)
{
Visualization.Revert(player);
}
//if he's online, create a task to send him the visualization
if(player.isOnline() && visualization.elements.size() > 0 && visualization.elements.get(0).location.getWorld().equals(player.getWorld()))
{
GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, new VisualizationApplicationTask(player, playerData, visualization), 1L);
}
}
//reverts a visualization by sending another block change list, this time with the real world block values
@SuppressWarnings("deprecation")
public static void Revert(Player player)
{
if(!player.isOnline()) return;
PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId());
Visualization visualization = playerData.currentVisualization;
if(playerData.currentVisualization != null)
{
//locality
int minx = player.getLocation().getBlockX() - 100;
int minz = player.getLocation().getBlockZ() - 100;
int maxx = player.getLocation().getBlockX() + 100;
int maxz = player.getLocation().getBlockZ() + 100;
//remove any elements which are too far away //sends a visualization to a player
visualization.removeElementsOutOfRange(visualization.elements, minx, minz, maxx, maxz); public static void Apply(Player player, Visualization visualization)
{
//send real block information for any remaining elements PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId());
for(int i = 0; i < visualization.elements.size(); i++)
{
VisualizationElement element = visualization.elements.get(i);
//check player still in world where visualization exists //if he has any current visualization, clear it first
if(i == 0) if (playerData.currentVisualization != null)
{ {
if(!player.getWorld().equals(element.location.getWorld())) return; Visualization.Revert(player);
} }
player.sendBlockChange(element.location, element.realBlock); //if he's online, create a task to send him the visualization
} if (player.isOnline() && visualization.elements.size() > 0 && visualization.elements.get(0).location.getWorld().equals(player.getWorld()))
{
GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, new VisualizationApplicationTask(player, playerData, visualization), 1L);
}
}
playerData.currentVisualization = null; //reverts a visualization by sending another block change list, this time with the real world block values
} @SuppressWarnings("deprecation")
} public static void Revert(Player player)
{
//convenience method to build a visualization from a claim if (!player.isOnline()) return;
//visualizationType determines the style (gold blocks, silver, red, diamond, etc)
public static Visualization FromClaim(Claim claim, int height, VisualizationType visualizationType, Location locality) PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId());
{
//visualize only top level claims Visualization visualization = playerData.currentVisualization;
if(claim.parent != null)
{ if (playerData.currentVisualization != null)
return FromClaim(claim.parent, height, visualizationType, locality); {
} //locality
int minx = player.getLocation().getBlockX() - 100;
Visualization visualization = new Visualization(); int minz = player.getLocation().getBlockZ() - 100;
int maxx = player.getLocation().getBlockX() + 100;
//add subdivisions first int maxz = player.getLocation().getBlockZ() + 100;
for(int i = 0; i < claim.children.size(); i++)
{ //remove any elements which are too far away
Claim child = claim.children.get(i); visualization.removeElementsOutOfRange(visualization.elements, minx, minz, maxx, maxz);
if(!child.inDataStore) continue;
visualization.addClaimElements(child, height, VisualizationType.Subdivision, locality); //send real block information for any remaining elements
} for (int i = 0; i < visualization.elements.size(); i++)
{
//special visualization for administrative land claims VisualizationElement element = visualization.elements.get(i);
if(claim.isAdminClaim() && visualizationType == VisualizationType.Claim)
//check player still in world where visualization exists
if (i == 0)
{
if (!player.getWorld().equals(element.location.getWorld())) return;
}
player.sendBlockChange(element.location, element.realBlock);
}
playerData.currentVisualization = null;
}
}
//convenience method to build a visualization from a claim
//visualizationType determines the style (gold blocks, silver, red, diamond, etc)
public static Visualization FromClaim(Claim claim, int height, VisualizationType visualizationType, Location locality)
{
//visualize only top level claims
if (claim.parent != null)
{
return FromClaim(claim.parent, height, visualizationType, locality);
}
Visualization visualization = new Visualization();
//add subdivisions first
for (int i = 0; i < claim.children.size(); i++)
{
Claim child = claim.children.get(i);
if (!child.inDataStore) continue;
visualization.addClaimElements(child, height, VisualizationType.Subdivision, locality);
}
//special visualization for administrative land claims
if (claim.isAdminClaim() && visualizationType == VisualizationType.Claim)
{ {
visualizationType = VisualizationType.AdminClaim; visualizationType = VisualizationType.AdminClaim;
} }
//add top level last so that it takes precedence (it shows on top when the child claim boundaries overlap with its boundaries) //add top level last so that it takes precedence (it shows on top when the child claim boundaries overlap with its boundaries)
visualization.addClaimElements(claim, height, visualizationType, locality); visualization.addClaimElements(claim, height, visualizationType, locality);
return visualization; return visualization;
}
//adds a claim's visualization to the current visualization
//handy for combining several visualizations together, as when visualization a top level claim with several subdivisions inside
//locality is a performance consideration. only create visualization blocks for around 100 blocks of the locality
@SuppressWarnings("deprecation")
private void addClaimElements(Claim claim, int height, VisualizationType visualizationType, Location locality)
{
Location smallXsmallZ = claim.getLesserBoundaryCorner();
Location bigXbigZ = claim.getGreaterBoundaryCorner();
World world = smallXsmallZ.getWorld();
boolean waterIsTransparent = locality.getBlock().getType() == Material.WATER;
int smallx = smallXsmallZ.getBlockX();
int smallz = smallXsmallZ.getBlockZ();
int bigx = bigXbigZ.getBlockX();
int bigz = bigXbigZ.getBlockZ();
BlockData cornerBlockData;
BlockData accentBlockData;
ArrayList<VisualizationElement> newElements = new ArrayList<VisualizationElement>();
if(visualizationType == VisualizationType.Claim)
{
cornerBlockData = Material.GLOWSTONE.createBlockData();
accentBlockData = Material.GOLD_BLOCK.createBlockData();
}
else if(visualizationType == VisualizationType.AdminClaim)
{
cornerBlockData = Material.GLOWSTONE.createBlockData();
accentBlockData = Material.PUMPKIN.createBlockData();
}
else if(visualizationType == VisualizationType.Subdivision)
{
cornerBlockData = Material.IRON_BLOCK.createBlockData();
accentBlockData = Material.WHITE_WOOL.createBlockData();
}
else if(visualizationType == VisualizationType.RestoreNature)
{
cornerBlockData = Material.DIAMOND_BLOCK.createBlockData();
accentBlockData = Material.DIAMOND_BLOCK.createBlockData();
}
else
{
cornerBlockData = Material.REDSTONE_ORE.createBlockData();
((Lightable) cornerBlockData).setLit(true);
accentBlockData = Material.NETHERRACK.createBlockData();
}
//initialize visualization elements without Y values and real data
//that will be added later for only the visualization elements within visualization range
//locality
int minx = locality.getBlockX() - 75;
int minz = locality.getBlockZ() - 75;
int maxx = locality.getBlockX() + 75;
int maxz = locality.getBlockZ() + 75;
final int STEP = 10;
//top line
newElements.add(new VisualizationElement(new Location(world, smallx, 0, bigz), cornerBlockData, Material.AIR.createBlockData()));
newElements.add(new VisualizationElement(new Location(world, smallx + 1, 0, bigz), accentBlockData, Material.AIR.createBlockData()));
for(int x = smallx + STEP; x < bigx - STEP / 2; x += STEP)
{
if(x > minx && x < maxx)
newElements.add(new VisualizationElement(new Location(world, x, 0, bigz), accentBlockData, Material.AIR.createBlockData()));
}
newElements.add(new VisualizationElement(new Location(world, bigx - 1, 0, bigz), accentBlockData, Material.AIR.createBlockData()));
//bottom line
newElements.add(new VisualizationElement(new Location(world, smallx + 1, 0, smallz), accentBlockData, Material.AIR.createBlockData()));
for(int x = smallx + STEP; x < bigx - STEP / 2; x += STEP)
{
if(x > minx && x < maxx)
newElements.add(new VisualizationElement(new Location(world, x, 0, smallz), accentBlockData, Material.AIR.createBlockData()));
}
newElements.add(new VisualizationElement(new Location(world, bigx - 1, 0, smallz), accentBlockData, Material.AIR.createBlockData()));
//left line
newElements.add(new VisualizationElement(new Location(world, smallx, 0, smallz), cornerBlockData, Material.AIR.createBlockData()));
newElements.add(new VisualizationElement(new Location(world, smallx, 0, smallz + 1), accentBlockData, Material.AIR.createBlockData()));
for(int z = smallz + STEP; z < bigz - STEP / 2; z += STEP)
{
if(z > minz && z < maxz)
newElements.add(new VisualizationElement(new Location(world, smallx, 0, z), accentBlockData, Material.AIR.createBlockData()));
}
newElements.add(new VisualizationElement(new Location(world, smallx, 0, bigz - 1), accentBlockData, Material.AIR.createBlockData()));
//right line
newElements.add(new VisualizationElement(new Location(world, bigx, 0, smallz), cornerBlockData, Material.AIR.createBlockData()));
newElements.add(new VisualizationElement(new Location(world, bigx, 0, smallz + 1), accentBlockData, Material.AIR.createBlockData()));
for(int z = smallz + STEP; z < bigz - STEP / 2; z += STEP)
{
if(z > minz && z < maxz)
newElements.add(new VisualizationElement(new Location(world, bigx, 0, z), accentBlockData, Material.AIR.createBlockData()));
}
newElements.add(new VisualizationElement(new Location(world, bigx, 0, bigz - 1), accentBlockData, Material.AIR.createBlockData()));
newElements.add(new VisualizationElement(new Location(world, bigx, 0, bigz), cornerBlockData, Material.AIR.createBlockData()));
//remove any out of range elements
this.removeElementsOutOfRange(newElements, minx, minz, maxx, maxz);
//remove any elements outside the claim
for(int i = 0; i < newElements.size(); i++)
{
VisualizationElement element = newElements.get(i);
if(!claim.contains(element.location, true, false))
{
newElements.remove(i--);
}
}
//set Y values and real block information for any remaining visualization blocks
for(VisualizationElement element : newElements)
{
Location tempLocation = element.location;
element.location = getVisibleLocation(tempLocation.getWorld(), tempLocation.getBlockX(), height, tempLocation.getBlockZ(), waterIsTransparent);
height = element.location.getBlockY();
element.realBlock = element.location.getBlock().getBlockData();
}
this.elements.addAll(newElements);
}
//removes any elements which are out of visualization range
private void removeElementsOutOfRange(ArrayList<VisualizationElement> elements, int minx, int minz, int maxx, int maxz)
{
for(int i = 0; i < elements.size(); i++)
{
Location location = elements.get(i).location;
if(location.getX() < minx || location.getX() > maxx || location.getZ() < minz || location.getZ() > maxz)
{
elements.remove(i--);
}
}
} }
//finds a block the player can probably see. this is how visualizations "cling" to the ground or ceiling //adds a claim's visualization to the current visualization
private static Location getVisibleLocation(World world, int x, int y, int z, boolean waterIsTransparent) //handy for combining several visualizations together, as when visualization a top level claim with several subdivisions inside
{ //locality is a performance consideration. only create visualization blocks for around 100 blocks of the locality
Block block = world.getBlockAt(x, y, z); @SuppressWarnings("deprecation")
BlockFace direction = (isTransparent(block, waterIsTransparent)) ? BlockFace.DOWN : BlockFace.UP; private void addClaimElements(Claim claim, int height, VisualizationType visualizationType, Location locality)
{
Location smallXsmallZ = claim.getLesserBoundaryCorner();
Location bigXbigZ = claim.getGreaterBoundaryCorner();
World world = smallXsmallZ.getWorld();
boolean waterIsTransparent = locality.getBlock().getType() == Material.WATER;
while( block.getY() >= 1 && int smallx = smallXsmallZ.getBlockX();
block.getY() < world.getMaxHeight() - 1 && int smallz = smallXsmallZ.getBlockZ();
(!isTransparent(block.getRelative(BlockFace.UP), waterIsTransparent) || isTransparent(block, waterIsTransparent))) int bigx = bigXbigZ.getBlockX();
{ int bigz = bigXbigZ.getBlockZ();
block = block.getRelative(direction);
}
return block.getLocation();
}
//helper method for above. allows visualization blocks to sit underneath partly transparent blocks like grass and fence
private static boolean isTransparent(Block block, boolean waterIsTransparent)
{
Material blockMaterial = block.getType();
//Blacklist
switch (blockMaterial)
{
case SNOW:
return false;
}
//Whitelist TODO: some of this might already be included in isTransparent() BlockData cornerBlockData;
switch (blockMaterial) BlockData accentBlockData;
{
case AIR:
case OAK_FENCE:
case ACACIA_FENCE:
case BIRCH_FENCE:
case DARK_OAK_FENCE:
case JUNGLE_FENCE:
case NETHER_BRICK_FENCE:
case SPRUCE_FENCE:
case OAK_FENCE_GATE:
case ACACIA_FENCE_GATE:
case BIRCH_FENCE_GATE:
case DARK_OAK_FENCE_GATE:
case SPRUCE_FENCE_GATE:
case JUNGLE_FENCE_GATE:
return true;
}
if (Tag.SIGNS.isTagged(blockMaterial) || Tag.WALL_SIGNS.isTagged(blockMaterial)) ArrayList<VisualizationElement> newElements = new ArrayList<VisualizationElement>();
return true;
return (waterIsTransparent && block.getType() == Material.WATER) || if (visualizationType == VisualizationType.Claim)
block.getType().isTransparent(); {
} cornerBlockData = Material.GLOWSTONE.createBlockData();
accentBlockData = Material.GOLD_BLOCK.createBlockData();
} else if (visualizationType == VisualizationType.AdminClaim)
{
cornerBlockData = Material.GLOWSTONE.createBlockData();
accentBlockData = Material.PUMPKIN.createBlockData();
} else if (visualizationType == VisualizationType.Subdivision)
{
cornerBlockData = Material.IRON_BLOCK.createBlockData();
accentBlockData = Material.WHITE_WOOL.createBlockData();
} else if (visualizationType == VisualizationType.RestoreNature)
{
cornerBlockData = Material.DIAMOND_BLOCK.createBlockData();
accentBlockData = Material.DIAMOND_BLOCK.createBlockData();
} else
{
cornerBlockData = Material.REDSTONE_ORE.createBlockData();
((Lightable) cornerBlockData).setLit(true);
accentBlockData = Material.NETHERRACK.createBlockData();
}
//initialize visualization elements without Y values and real data
//that will be added later for only the visualization elements within visualization range
//locality
int minx = locality.getBlockX() - 75;
int minz = locality.getBlockZ() - 75;
int maxx = locality.getBlockX() + 75;
int maxz = locality.getBlockZ() + 75;
final int STEP = 10;
//top line
newElements.add(new VisualizationElement(new Location(world, smallx, 0, bigz), cornerBlockData, Material.AIR.createBlockData()));
newElements.add(new VisualizationElement(new Location(world, smallx + 1, 0, bigz), accentBlockData, Material.AIR.createBlockData()));
for (int x = smallx + STEP; x < bigx - STEP / 2; x += STEP)
{
if (x > minx && x < maxx)
newElements.add(new VisualizationElement(new Location(world, x, 0, bigz), accentBlockData, Material.AIR.createBlockData()));
}
newElements.add(new VisualizationElement(new Location(world, bigx - 1, 0, bigz), accentBlockData, Material.AIR.createBlockData()));
//bottom line
newElements.add(new VisualizationElement(new Location(world, smallx + 1, 0, smallz), accentBlockData, Material.AIR.createBlockData()));
for (int x = smallx + STEP; x < bigx - STEP / 2; x += STEP)
{
if (x > minx && x < maxx)
newElements.add(new VisualizationElement(new Location(world, x, 0, smallz), accentBlockData, Material.AIR.createBlockData()));
}
newElements.add(new VisualizationElement(new Location(world, bigx - 1, 0, smallz), accentBlockData, Material.AIR.createBlockData()));
//left line
newElements.add(new VisualizationElement(new Location(world, smallx, 0, smallz), cornerBlockData, Material.AIR.createBlockData()));
newElements.add(new VisualizationElement(new Location(world, smallx, 0, smallz + 1), accentBlockData, Material.AIR.createBlockData()));
for (int z = smallz + STEP; z < bigz - STEP / 2; z += STEP)
{
if (z > minz && z < maxz)
newElements.add(new VisualizationElement(new Location(world, smallx, 0, z), accentBlockData, Material.AIR.createBlockData()));
}
newElements.add(new VisualizationElement(new Location(world, smallx, 0, bigz - 1), accentBlockData, Material.AIR.createBlockData()));
//right line
newElements.add(new VisualizationElement(new Location(world, bigx, 0, smallz), cornerBlockData, Material.AIR.createBlockData()));
newElements.add(new VisualizationElement(new Location(world, bigx, 0, smallz + 1), accentBlockData, Material.AIR.createBlockData()));
for (int z = smallz + STEP; z < bigz - STEP / 2; z += STEP)
{
if (z > minz && z < maxz)
newElements.add(new VisualizationElement(new Location(world, bigx, 0, z), accentBlockData, Material.AIR.createBlockData()));
}
newElements.add(new VisualizationElement(new Location(world, bigx, 0, bigz - 1), accentBlockData, Material.AIR.createBlockData()));
newElements.add(new VisualizationElement(new Location(world, bigx, 0, bigz), cornerBlockData, Material.AIR.createBlockData()));
//remove any out of range elements
this.removeElementsOutOfRange(newElements, minx, minz, maxx, maxz);
//remove any elements outside the claim
for (int i = 0; i < newElements.size(); i++)
{
VisualizationElement element = newElements.get(i);
if (!claim.contains(element.location, true, false))
{
newElements.remove(i--);
}
}
//set Y values and real block information for any remaining visualization blocks
for (VisualizationElement element : newElements)
{
Location tempLocation = element.location;
element.location = getVisibleLocation(tempLocation.getWorld(), tempLocation.getBlockX(), height, tempLocation.getBlockZ(), waterIsTransparent);
height = element.location.getBlockY();
element.realBlock = element.location.getBlock().getBlockData();
}
this.elements.addAll(newElements);
}
//removes any elements which are out of visualization range
private void removeElementsOutOfRange(ArrayList<VisualizationElement> elements, int minx, int minz, int maxx, int maxz)
{
for (int i = 0; i < elements.size(); i++)
{
Location location = elements.get(i).location;
if (location.getX() < minx || location.getX() > maxx || location.getZ() < minz || location.getZ() > maxz)
{
elements.remove(i--);
}
}
}
//finds a block the player can probably see. this is how visualizations "cling" to the ground or ceiling
private static Location getVisibleLocation(World world, int x, int y, int z, boolean waterIsTransparent)
{
Block block = world.getBlockAt(x, y, z);
BlockFace direction = (isTransparent(block, waterIsTransparent)) ? BlockFace.DOWN : BlockFace.UP;
while (block.getY() >= 1 &&
block.getY() < world.getMaxHeight() - 1 &&
(!isTransparent(block.getRelative(BlockFace.UP), waterIsTransparent) || isTransparent(block, waterIsTransparent)))
{
block = block.getRelative(direction);
}
return block.getLocation();
}
//helper method for above. allows visualization blocks to sit underneath partly transparent blocks like grass and fence
private static boolean isTransparent(Block block, boolean waterIsTransparent)
{
Material blockMaterial = block.getType();
//Blacklist
switch (blockMaterial)
{
case SNOW:
return false;
}
//Whitelist TODO: some of this might already be included in isTransparent()
switch (blockMaterial)
{
case AIR:
case OAK_FENCE:
case ACACIA_FENCE:
case BIRCH_FENCE:
case DARK_OAK_FENCE:
case JUNGLE_FENCE:
case NETHER_BRICK_FENCE:
case SPRUCE_FENCE:
case OAK_FENCE_GATE:
case ACACIA_FENCE_GATE:
case BIRCH_FENCE_GATE:
case DARK_OAK_FENCE_GATE:
case SPRUCE_FENCE_GATE:
case JUNGLE_FENCE_GATE:
return true;
}
if (Tag.SIGNS.isTagged(blockMaterial) || Tag.WALL_SIGNS.isTagged(blockMaterial))
return true;
return (waterIsTransparent && block.getType() == Material.WATER) ||
block.getType().isTransparent();
}
public static Visualization fromClaims(Iterable<Claim> claims, int height, VisualizationType type, Location locality) public static Visualization fromClaims(Iterable<Claim> claims, int height, VisualizationType type, Location locality)
{ {
Visualization visualization = new Visualization(); Visualization visualization = new Visualization();
for(Claim claim : claims) for (Claim claim : claims)
{ {
visualization.addClaimElements(claim, height, type, locality); visualization.addClaimElements(claim, height, type, locality);
} }
return visualization; return visualization;
} }
} }

View File

@ -15,46 +15,46 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
//applies a visualization for a player by sending him block change packets //applies a visualization for a player by sending him block change packets
class VisualizationApplicationTask implements Runnable class VisualizationApplicationTask implements Runnable
{ {
private Visualization visualization; private Visualization visualization;
private Player player; private Player player;
private PlayerData playerData; private PlayerData playerData;
public VisualizationApplicationTask(Player player, PlayerData playerData, Visualization visualization) public VisualizationApplicationTask(Player player, PlayerData playerData, Visualization visualization)
{ {
this.visualization = visualization; this.visualization = visualization;
this.playerData = playerData; this.playerData = playerData;
this.player = player; this.player = player;
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
public void run() public void run()
{ {
//for each element (=block) of the visualization //for each element (=block) of the visualization
for(int i = 0; i < visualization.elements.size(); i++) for (int i = 0; i < visualization.elements.size(); i++)
{ {
VisualizationElement element = visualization.elements.get(i); VisualizationElement element = visualization.elements.get(i);
//send the player a fake block change event //send the player a fake block change event
if(!element.location.getChunk().isLoaded()) continue; //cheap distance check if (!element.location.getChunk().isLoaded()) continue; //cheap distance check
player.sendBlockChange(element.location, element.visualizedBlock); player.sendBlockChange(element.location, element.visualizedBlock);
} }
//remember the visualization applied to this player for later (so it can be inexpensively reverted) //remember the visualization applied to this player for later (so it can be inexpensively reverted)
playerData.currentVisualization = visualization; playerData.currentVisualization = visualization;
//schedule automatic visualization reversion in 60 seconds. //schedule automatic visualization reversion in 60 seconds.
GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask( GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(
GriefPrevention.instance, GriefPrevention.instance,
new VisualizationReversionTask(player, playerData, visualization), new VisualizationReversionTask(player, playerData, visualization),
20L * 60); //60 seconds 20L * 60); //60 seconds
} }
} }

View File

@ -17,21 +17,21 @@
*/ */
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
//represents a "fake" block sent to a player as part of a visualization //represents a "fake" block sent to a player as part of a visualization
public class VisualizationElement public class VisualizationElement
{ {
public Location location; public Location location;
public BlockData visualizedBlock; public BlockData visualizedBlock;
public BlockData realBlock; public BlockData realBlock;
public VisualizationElement(Location location, BlockData visualizedBlock, BlockData realBlock) public VisualizationElement(Location location, BlockData visualizedBlock, BlockData realBlock)
{ {
this.location = location; this.location = location;
this.visualizedBlock = visualizedBlock; this.visualizedBlock = visualizedBlock;
this.realBlock = realBlock; this.realBlock = realBlock;
} }
} }

View File

@ -15,8 +15,8 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import me.ryanhamshire.GriefPrevention.events.VisualizationEvent; import me.ryanhamshire.GriefPrevention.events.VisualizationEvent;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -25,28 +25,28 @@ import org.bukkit.entity.Player;
import java.util.Collections; import java.util.Collections;
//applies a visualization for a player by sending him block change packets //applies a visualization for a player by sending him block change packets
class VisualizationReversionTask implements Runnable class VisualizationReversionTask implements Runnable
{ {
private Visualization visualization; private Visualization visualization;
private Player player; private Player player;
private PlayerData playerData; private PlayerData playerData;
public VisualizationReversionTask(Player player, PlayerData playerData, Visualization visualization) public VisualizationReversionTask(Player player, PlayerData playerData, Visualization visualization)
{ {
this.visualization = visualization; this.visualization = visualization;
this.playerData = playerData; this.playerData = playerData;
this.player = player; this.player = player;
} }
@Override
public void run()
{
//don't do anything if the player's current visualization is different from the one scheduled to revert
if(playerData.currentVisualization != visualization) return;
// alert plugins of a visualization @Override
Bukkit.getPluginManager().callEvent(new VisualizationEvent(player, Collections.<Claim>emptySet())); public void run()
{
Visualization.Revert(player); //don't do anything if the player's current visualization is different from the one scheduled to revert
} if (playerData.currentVisualization != visualization) return;
// alert plugins of a visualization
Bukkit.getPluginManager().callEvent(new VisualizationEvent(player, Collections.<Claim>emptySet()));
Visualization.Revert(player);
}
} }

View File

@ -19,11 +19,11 @@
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
//just an enumeration of the visualization types, which determine what materials will be for the fake blocks //just an enumeration of the visualization types, which determine what materials will be for the fake blocks
public enum VisualizationType public enum VisualizationType
{ {
Claim, Claim,
Subdivision, Subdivision,
ErrorClaim, ErrorClaim,
RestoreNature, RestoreNature,
AdminClaim AdminClaim
} }

View File

@ -10,24 +10,24 @@ import org.bukkit.inventory.meta.BookMeta;
public class WelcomeTask implements Runnable public class WelcomeTask implements Runnable
{ {
private Player player; private Player player;
public WelcomeTask(Player player) public WelcomeTask(Player player)
{ {
this.player = player; this.player = player;
} }
@Override @Override
public void run() public void run()
{ {
//abort if player has logged out since this task was scheduled //abort if player has logged out since this task was scheduled
if(!this.player.isOnline()) return; if (!this.player.isOnline()) return;
//offer advice and a helpful link //offer advice and a helpful link
GriefPrevention.sendMessage(player, TextMode.Instr, Messages.AvoidGriefClaimLand); GriefPrevention.sendMessage(player, TextMode.Instr, Messages.AvoidGriefClaimLand);
GriefPrevention.sendMessage(player, TextMode.Instr, Messages.SurvivalBasicsVideo2, DataStore.SURVIVAL_VIDEO_URL); GriefPrevention.sendMessage(player, TextMode.Instr, Messages.SurvivalBasicsVideo2, DataStore.SURVIVAL_VIDEO_URL);
//give the player a reference book for later //give the player a reference book for later
if(GriefPrevention.instance.config_claims_supplyPlayerManual) if (GriefPrevention.instance.config_claims_supplyPlayerManual)
{ {
ItemFactory factory = Bukkit.getItemFactory(); ItemFactory factory = Bukkit.getItemFactory();
BookMeta meta = (BookMeta) factory.getItemMeta(Material.WRITTEN_BOOK); BookMeta meta = (BookMeta) factory.getItemMeta(Material.WRITTEN_BOOK);
@ -35,43 +35,43 @@ public class WelcomeTask implements Runnable
DataStore datastore = GriefPrevention.instance.dataStore; DataStore datastore = GriefPrevention.instance.dataStore;
meta.setAuthor(datastore.getMessage(Messages.BookAuthor)); meta.setAuthor(datastore.getMessage(Messages.BookAuthor));
meta.setTitle(datastore.getMessage(Messages.BookTitle)); meta.setTitle(datastore.getMessage(Messages.BookTitle));
StringBuilder page1 = new StringBuilder(); StringBuilder page1 = new StringBuilder();
String URL = datastore.getMessage(Messages.BookLink, DataStore.SURVIVAL_VIDEO_URL); String URL = datastore.getMessage(Messages.BookLink, DataStore.SURVIVAL_VIDEO_URL);
String intro = datastore.getMessage(Messages.BookIntro); String intro = datastore.getMessage(Messages.BookIntro);
page1.append(URL).append("\n\n"); page1.append(URL).append("\n\n");
page1.append(intro).append("\n\n"); page1.append(intro).append("\n\n");
String editToolName = GriefPrevention.instance.config_claims_modificationTool.name().replace('_', ' ').toLowerCase(); String editToolName = GriefPrevention.instance.config_claims_modificationTool.name().replace('_', ' ').toLowerCase();
String infoToolName = GriefPrevention.instance.config_claims_investigationTool.name().replace('_', ' ').toLowerCase(); String infoToolName = GriefPrevention.instance.config_claims_investigationTool.name().replace('_', ' ').toLowerCase();
String configClaimTools = datastore.getMessage(Messages.BookTools, editToolName, infoToolName); String configClaimTools = datastore.getMessage(Messages.BookTools, editToolName, infoToolName);
page1.append(configClaimTools); page1.append(configClaimTools);
if(GriefPrevention.instance.config_claims_automaticClaimsForNewPlayersRadius < 0) if (GriefPrevention.instance.config_claims_automaticClaimsForNewPlayersRadius < 0)
{ {
page1.append(datastore.getMessage(Messages.BookDisabledChestClaims)); page1.append(datastore.getMessage(Messages.BookDisabledChestClaims));
} }
StringBuilder page2 = new StringBuilder(datastore.getMessage(Messages.BookUsefulCommands)).append("\n\n"); StringBuilder page2 = new StringBuilder(datastore.getMessage(Messages.BookUsefulCommands)).append("\n\n");
page2.append("/Trust /UnTrust /TrustList\n"); page2.append("/Trust /UnTrust /TrustList\n");
page2.append("/ClaimsList\n"); page2.append("/ClaimsList\n");
page2.append("/AbandonClaim\n\n"); page2.append("/AbandonClaim\n\n");
page2.append("/Claim /ExtendClaim\n"); page2.append("/Claim /ExtendClaim\n");
page2.append("/IgnorePlayer\n\n"); page2.append("/IgnorePlayer\n\n");
page2.append("/SubdivideClaims\n"); page2.append("/SubdivideClaims\n");
page2.append("/AccessTrust\n"); page2.append("/AccessTrust\n");
page2.append("/ContainerTrust\n"); page2.append("/ContainerTrust\n");
page2.append("/PermissionTrust"); page2.append("/PermissionTrust");
meta.setPages(page1.toString(), page2.toString()); meta.setPages(page1.toString(), page2.toString());
ItemStack item = new ItemStack(Material.WRITTEN_BOOK); ItemStack item = new ItemStack(Material.WRITTEN_BOOK);
item.setItemMeta(meta); item.setItemMeta(meta);
player.getInventory().addItem(item); player.getInventory().addItem(item);
} }
} }
} }

View File

@ -6,35 +6,35 @@ import java.util.regex.Pattern;
class WordFinder class WordFinder
{ {
private Pattern pattern; private Pattern pattern;
WordFinder(List<String> wordsToFind) WordFinder(List<String> wordsToFind)
{ {
if(wordsToFind.size() == 0) return; if (wordsToFind.size() == 0) return;
StringBuilder patternBuilder = new StringBuilder(); StringBuilder patternBuilder = new StringBuilder();
for(String word : wordsToFind) for (String word : wordsToFind)
{ {
if(!word.isEmpty() && !word.trim().isEmpty()) if (!word.isEmpty() && !word.trim().isEmpty())
{ {
patternBuilder.append("|(([^\\w]|^)" + Pattern.quote(word) + "([^\\w]|$))"); patternBuilder.append("|(([^\\w]|^)" + Pattern.quote(word) + "([^\\w]|$))");
} }
} }
String patternString = patternBuilder.toString(); String patternString = patternBuilder.toString();
if(patternString.length() > 1) if (patternString.length() > 1)
{ {
//trim extraneous leading pipe (|) //trim extraneous leading pipe (|)
patternString = patternString.substring(1); patternString = patternString.substring(1);
} }
this.pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); this.pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
} }
boolean hasMatch(String input) boolean hasMatch(String input)
{ {
if(this.pattern == null) return false; if (this.pattern == null) return false;
Matcher matcher = this.pattern.matcher(input); Matcher matcher = this.pattern.matcher(input);
return matcher.find(); return matcher.find();
} }

View File

@ -1,19 +1,18 @@
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import com.sk89q.worldguard.WorldGuard; import com.sk89q.worldguard.WorldGuard;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.bukkit.BukkitPlayer; import com.sk89q.worldguard.bukkit.BukkitPlayer;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.internal.permission.RegionPermissionModel; import com.sk89q.worldguard.internal.permission.RegionPermissionModel;
import com.sk89q.worldguard.protection.ApplicableRegionSet; import com.sk89q.worldguard.protection.ApplicableRegionSet;
import com.sk89q.worldguard.protection.flags.Flags; import com.sk89q.worldguard.protection.flags.Flags;
import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
import com.sk89q.worldguard.protection.regions.ProtectedRegion; import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import org.bukkit.Location;
import org.bukkit.entity.Player;
class WorldGuardWrapper class WorldGuardWrapper
{ {
@ -21,7 +20,7 @@ class WorldGuardWrapper
public WorldGuardWrapper() throws ClassNotFoundException public WorldGuardWrapper() throws ClassNotFoundException
{ {
this.worldGuard = (WorldGuardPlugin)GriefPrevention.instance.getServer().getPluginManager().getPlugin("WorldGuard"); this.worldGuard = (WorldGuardPlugin) GriefPrevention.instance.getServer().getPluginManager().getPlugin("WorldGuard");
} }
public boolean canBuild(Location lesserCorner, Location greaterCorner, Player creatingPlayer) public boolean canBuild(Location lesserCorner, Location greaterCorner, Player creatingPlayer)
@ -31,11 +30,11 @@ class WorldGuardWrapper
BukkitPlayer localPlayer = new BukkitPlayer(this.worldGuard, creatingPlayer); BukkitPlayer localPlayer = new BukkitPlayer(this.worldGuard, creatingPlayer);
World world = WorldGuard.getInstance().getPlatform().getMatcher().getWorldByName(lesserCorner.getWorld().getName()); World world = WorldGuard.getInstance().getPlatform().getMatcher().getWorldByName(lesserCorner.getWorld().getName());
if(new RegionPermissionModel(localPlayer).mayIgnoreRegionProtection(world)) return true; if (new RegionPermissionModel(localPlayer).mayIgnoreRegionProtection(world)) return true;
RegionManager manager = WorldGuard.getInstance().getPlatform().getRegionContainer().get(world); RegionManager manager = WorldGuard.getInstance().getPlatform().getRegionContainer().get(world);
if(manager != null) if (manager != null)
{ {
ProtectedCuboidRegion tempRegion = new ProtectedCuboidRegion( ProtectedCuboidRegion tempRegion = new ProtectedCuboidRegion(
"GP_TEMP", "GP_TEMP",
@ -43,8 +42,10 @@ class WorldGuardWrapper
BlockVector3.at(greaterCorner.getX(), world.getMaxY(), greaterCorner.getZ())); BlockVector3.at(greaterCorner.getX(), world.getMaxY(), greaterCorner.getZ()));
ApplicableRegionSet overlaps = manager.getApplicableRegions(tempRegion); ApplicableRegionSet overlaps = manager.getApplicableRegions(tempRegion);
for (ProtectedRegion r : overlaps.getRegions()) { for (ProtectedRegion r : overlaps.getRegions())
if (!manager.getApplicableRegions(r).testState(localPlayer, Flags.BUILD)) { {
if (!manager.getApplicableRegions(r).testState(localPlayer, Flags.BUILD))
{
return false; return false;
} }
} }

View File

@ -14,11 +14,15 @@ public class AccrueClaimBlocksEvent extends Event
{ {
// Custom Event Requirements // Custom Event Requirements
private static final HandlerList handlers = new HandlerList(); private static final HandlerList handlers = new HandlerList();
public static HandlerList getHandlerList() {
public static HandlerList getHandlerList()
{
return handlers; return handlers;
} }
@Override @Override
public HandlerList getHandlers() { public HandlerList getHandlers()
{
return handlers; return handlers;
} }
@ -30,7 +34,6 @@ public class AccrueClaimBlocksEvent extends Event
/** /**
* @param player Player receiving accruals * @param player Player receiving accruals
* @param blocksToAccrue Blocks to accrue * @param blocksToAccrue Blocks to accrue
*
* @deprecated Use {@link #AccrueClaimBlocksEvent(Player, int, boolean)} instead * @deprecated Use {@link #AccrueClaimBlocksEvent(Player, int, boolean)} instead
*/ */
public AccrueClaimBlocksEvent(Player player, int blocksToAccrue) public AccrueClaimBlocksEvent(Player player, int blocksToAccrue)
@ -67,7 +70,8 @@ public class AccrueClaimBlocksEvent extends Event
/** /**
* @return whether the player was detected as idle (used for idle accrual percentage) * @return whether the player was detected as idle (used for idle accrual percentage)
*/ */
public boolean isIdle() { public boolean isIdle()
{
return this.isIdle; return this.isIdle;
} }
@ -78,6 +82,7 @@ public class AccrueClaimBlocksEvent extends Event
/** /**
* Modify the amount of claim blocks to deliver to the player for this 10 minute interval * Modify the amount of claim blocks to deliver to the player for this 10 minute interval
*
* @param blocksToAccrue blocks to deliver * @param blocksToAccrue blocks to deliver
*/ */
public void setBlocksToAccrue(int blocksToAccrue) public void setBlocksToAccrue(int blocksToAccrue)
@ -87,6 +92,7 @@ public class AccrueClaimBlocksEvent extends Event
/** /**
* Similar to setBlocksToAccrue(int), but automatically converting from a per-hour rate value to a 10-minute rate value * Similar to setBlocksToAccrue(int), but automatically converting from a per-hour rate value to a 10-minute rate value
*
* @param blocksToAccruePerHour the per-hour rate of blocks to deliver * @param blocksToAccruePerHour the per-hour rate of blocks to deliver
*/ */

View File

@ -13,11 +13,13 @@ import org.bukkit.event.HandlerList;
* Created by Narimm on 5/08/2018. * Created by Narimm on 5/08/2018.
*/ */
public class ClaimCreatedEvent extends Event implements Cancellable { public class ClaimCreatedEvent extends Event implements Cancellable
{
private static final HandlerList handlers = new HandlerList(); private static final HandlerList handlers = new HandlerList();
public static HandlerList getHandlerList() { public static HandlerList getHandlerList()
{
return handlers; return handlers;
} }
@ -27,23 +29,27 @@ public class ClaimCreatedEvent extends Event implements Cancellable {
private boolean cancelled = false; private boolean cancelled = false;
public ClaimCreatedEvent(Claim claim, CommandSender creator) { public ClaimCreatedEvent(Claim claim, CommandSender creator)
{
this.claim = claim; this.claim = claim;
this.creator = creator; this.creator = creator;
} }
@Override @Override
public HandlerList getHandlers() { public HandlerList getHandlers()
{
return handlers; return handlers;
} }
@Override @Override
public boolean isCancelled() { public boolean isCancelled()
{
return cancelled; return cancelled;
} }
@Override @Override
public void setCancelled(boolean b) { public void setCancelled(boolean b)
{
this.cancelled = b; this.cancelled = b;
} }
@ -52,7 +58,8 @@ public class ClaimCreatedEvent extends Event implements Cancellable {
* *
* @return Claim * @return Claim
*/ */
public Claim getClaim() { public Claim getClaim()
{
return claim; return claim;
} }
@ -61,7 +68,8 @@ public class ClaimCreatedEvent extends Event implements Cancellable {
* *
* @return the CommandSender * @return the CommandSender
*/ */
public CommandSender getCreator() { public CommandSender getCreator()
{
return creator; return creator;
} }
} }

View File

@ -1,43 +1,46 @@
package me.ryanhamshire.GriefPrevention.events; package me.ryanhamshire.GriefPrevention.events;
import me.ryanhamshire.GriefPrevention.Claim; import me.ryanhamshire.GriefPrevention.Claim;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
/** /**
* This event gets called whenever a claim is going to be deleted. This event is * This event gets called whenever a claim is going to be deleted. This event is
* not called when a claim is resized. * not called when a claim is resized.
* *
* @author Tux2 * @author Tux2
*
*/ */
public class ClaimDeletedEvent extends Event{ public class ClaimDeletedEvent extends Event
{
// Custom Event Requirements // Custom Event Requirements
private static final HandlerList handlers = new HandlerList(); private static final HandlerList handlers = new HandlerList();
public static HandlerList getHandlerList() { public static HandlerList getHandlerList()
{
return handlers; return handlers;
} }
private Claim claim; private Claim claim;
public ClaimDeletedEvent(Claim claim) { public ClaimDeletedEvent(Claim claim)
{
this.claim = claim; this.claim = claim;
} }
/** /**
* Gets the claim to be deleted. * Gets the claim to be deleted.
* *
* @return * @return
*/ */
public Claim getClaim() { public Claim getClaim()
{
return claim; return claim;
} }
@Override @Override
public HandlerList getHandlers() { public HandlerList getHandlers()
{
return handlers; return handlers;
} }
} }

View File

@ -1,7 +1,6 @@
package me.ryanhamshire.GriefPrevention.events; package me.ryanhamshire.GriefPrevention.events;
import me.ryanhamshire.GriefPrevention.Claim; import me.ryanhamshire.GriefPrevention.Claim;
import org.bukkit.event.Cancellable; import org.bukkit.event.Cancellable;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
@ -11,7 +10,7 @@ public class ClaimExpirationEvent extends Event implements Cancellable
{ {
private static final HandlerList handlers = new HandlerList(); private static final HandlerList handlers = new HandlerList();
private boolean cancelled = false; private boolean cancelled = false;
public static HandlerList getHandlerList() public static HandlerList getHandlerList()
{ {
return handlers; return handlers;
@ -34,13 +33,13 @@ public class ClaimExpirationEvent extends Event implements Cancellable
{ {
return handlers; return handlers;
} }
@Override @Override
public boolean isCancelled() public boolean isCancelled()
{ {
return this.cancelled; return this.cancelled;
} }
@Override @Override
public void setCancelled(boolean cancelled) public void setCancelled(boolean cancelled)
{ {

View File

@ -11,24 +11,28 @@ import org.bukkit.event.HandlerList;
* a claim has changed. The CommandSender can be null in the event that the modification is called by the plugin itself. * a claim has changed. The CommandSender can be null in the event that the modification is called by the plugin itself.
* Created by Narimm on 5/08/2018. * Created by Narimm on 5/08/2018.
*/ */
public class ClaimModifiedEvent extends Event { public class ClaimModifiedEvent extends Event
{
private static final HandlerList handlers = new HandlerList(); private static final HandlerList handlers = new HandlerList();
public static HandlerList getHandlerList() { public static HandlerList getHandlerList()
{
return handlers; return handlers;
} }
private final Claim claim; private final Claim claim;
private CommandSender modifier; private CommandSender modifier;
public ClaimModifiedEvent(Claim claim, CommandSender modifier) { public ClaimModifiedEvent(Claim claim, CommandSender modifier)
{
this.claim = claim; this.claim = claim;
this.modifier = modifier; this.modifier = modifier;
} }
@Override @Override
public HandlerList getHandlers() { public HandlerList getHandlers()
{
return handlers; return handlers;
} }
@ -37,7 +41,8 @@ public class ClaimModifiedEvent extends Event {
* *
* @return the claim * @return the claim
*/ */
public Claim getClaim() { public Claim getClaim()
{
return claim; return claim;
} }
@ -46,7 +51,8 @@ public class ClaimModifiedEvent extends Event {
* *
* @return the CommandSender or null * @return the CommandSender or null
*/ */
public CommandSender getModifier() { public CommandSender getModifier()
{
return modifier; return modifier;
} }
} }

View File

@ -34,8 +34,8 @@ public class PlayerKickBanEvent extends Event
/** /**
* @param player Player getting kicked and/or banned * @param player Player getting kicked and/or banned
* @param reason Reason message for kick/ban * @param reason Reason message for kick/ban
* @param source What caused the kick/ban * @param source What caused the kick/ban
* @param ban True if player is getting banned * @param ban True if player is getting banned
*/ */
public PlayerKickBanEvent(Player player, String reason, String source, boolean ban) public PlayerKickBanEvent(Player player, String reason, String source, boolean ban)
{ {

View File

@ -10,7 +10,7 @@ public class PreventBlockBreakEvent extends Event implements Cancellable
{ {
private static final HandlerList handlers = new HandlerList(); private static final HandlerList handlers = new HandlerList();
private boolean cancelled = false; private boolean cancelled = false;
private BlockBreakEvent innerEvent; private BlockBreakEvent innerEvent;
public static HandlerList getHandlerList() public static HandlerList getHandlerList()
{ {
@ -21,7 +21,7 @@ public class PreventBlockBreakEvent extends Event implements Cancellable
{ {
this.innerEvent = innerEvent; this.innerEvent = innerEvent;
} }
public BlockBreakEvent getInnerEvent() public BlockBreakEvent getInnerEvent()
{ {
return this.innerEvent; return this.innerEvent;
@ -32,13 +32,13 @@ public class PreventBlockBreakEvent extends Event implements Cancellable
{ {
return handlers; return handlers;
} }
@Override @Override
public boolean isCancelled() public boolean isCancelled()
{ {
return this.cancelled; return this.cancelled;
} }
@Override @Override
public void setCancelled(boolean cancelled) public void setCancelled(boolean cancelled)
{ {

View File

@ -1,7 +1,6 @@
package me.ryanhamshire.GriefPrevention.events; package me.ryanhamshire.GriefPrevention.events;
import me.ryanhamshire.GriefPrevention.Claim; import me.ryanhamshire.GriefPrevention.Claim;
import org.bukkit.event.Cancellable; import org.bukkit.event.Cancellable;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
@ -34,13 +33,13 @@ public class PreventPvPEvent extends Event implements Cancellable
{ {
return handlers; return handlers;
} }
@Override @Override
public boolean isCancelled() public boolean isCancelled()
{ {
return this.cancelled; return this.cancelled;
} }
@Override @Override
public void setCancelled(boolean cancelled) public void setCancelled(boolean cancelled)
{ {

View File

@ -1,7 +1,6 @@
package me.ryanhamshire.GriefPrevention.events; package me.ryanhamshire.GriefPrevention.events;
import me.ryanhamshire.GriefPrevention.Claim; import me.ryanhamshire.GriefPrevention.Claim;
import org.bukkit.event.Cancellable; import org.bukkit.event.Cancellable;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
@ -34,13 +33,13 @@ public class ProtectDeathDropsEvent extends Event implements Cancellable
{ {
return handlers; return handlers;
} }
@Override @Override
public boolean isCancelled() public boolean isCancelled()
{ {
return this.cancelled; return this.cancelled;
} }
@Override @Override
public void setCancelled(boolean cancelled) public void setCancelled(boolean cancelled)
{ {

View File

@ -1,7 +1,6 @@
package me.ryanhamshire.GriefPrevention.events; package me.ryanhamshire.GriefPrevention.events;
import me.ryanhamshire.GriefPrevention.Claim; import me.ryanhamshire.GriefPrevention.Claim;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.event.Cancellable; import org.bukkit.event.Cancellable;
import org.bukkit.event.Event; import org.bukkit.event.Event;
@ -46,13 +45,13 @@ public class SaveTrappedPlayerEvent extends Event implements Cancellable
{ {
return handlers; return handlers;
} }
@Override @Override
public boolean isCancelled() public boolean isCancelled()
{ {
return this.cancelled; return this.cancelled;
} }
@Override @Override
public void setCancelled(boolean cancelled) public void setCancelled(boolean cancelled)
{ {

View File

@ -1,110 +1,119 @@
package me.ryanhamshire.GriefPrevention.events; package me.ryanhamshire.GriefPrevention.events;
import java.util.ArrayList; import me.ryanhamshire.GriefPrevention.Claim;
import java.util.List; import me.ryanhamshire.GriefPrevention.ClaimPermission;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable; import org.bukkit.event.Cancellable;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
import me.ryanhamshire.GriefPrevention.Claim; import java.util.ArrayList;
import me.ryanhamshire.GriefPrevention.ClaimPermission; import java.util.List;
/** /**
* This event is thrown when the trust is changed in one or more claims * This event is thrown when the trust is changed in one or more claims
*
* @author roinujnosde
* *
* @author roinujnosde
*/ */
public class TrustChangedEvent extends Event implements Cancellable { public class TrustChangedEvent extends Event implements Cancellable
{
private static final HandlerList handlers = new HandlerList(); private static final HandlerList handlers = new HandlerList();
private final Player changer; private final Player changer;
private final List<Claim> claims; private final List<Claim> claims;
private final ClaimPermission claimPermission; private final ClaimPermission claimPermission;
private final boolean given; private final boolean given;
private final String identifier; private final String identifier;
private boolean cancelled; private boolean cancelled;
public TrustChangedEvent(Player changer, List<Claim> claims, ClaimPermission claimPermission, boolean given,
String identifier) {
super();
this.changer = changer;
this.claims = claims;
this.claimPermission = claimPermission;
this.given = given;
this.identifier = identifier;
}
public TrustChangedEvent(Player changer, Claim claim, ClaimPermission claimPermission, boolean given, String identifier) { public TrustChangedEvent(Player changer, List<Claim> claims, ClaimPermission claimPermission, boolean given,
this.changer = changer; String identifier)
claims = new ArrayList<>(); {
claims.add(claim); super();
this.claimPermission = claimPermission; this.changer = changer;
this.given = given; this.claims = claims;
this.identifier = identifier; this.claimPermission = claimPermission;
} this.given = given;
this.identifier = identifier;
}
@Override public TrustChangedEvent(Player changer, Claim claim, ClaimPermission claimPermission, boolean given, String identifier)
public HandlerList getHandlers() { {
return handlers; this.changer = changer;
} claims = new ArrayList<>();
claims.add(claim);
/** this.claimPermission = claimPermission;
* Gets who made the change this.given = given;
* this.identifier = identifier;
* @return the changer }
*/
public Player getChanger() {
return changer;
}
/** @Override
* Gets the changed claims public HandlerList getHandlers()
* {
* @return the changed claims return handlers;
*/ }
public List<Claim> getClaims() {
return claims;
}
/** /**
* Gets the claim permission (null if the permission is being taken) * Gets who made the change
* *
* @return the claim permission * @return the changer
*/ */
public ClaimPermission getClaimPermission() { public Player getChanger()
return claimPermission; {
} return changer;
}
/** /**
* Checks if the trust is being given * Gets the changed claims
* *
* @return true if given, false if taken * @return the changed claims
*/ */
public boolean isGiven() { public List<Claim> getClaims()
return given; {
} return claims;
}
/** /**
* Gets the identifier of the receiver of this action * Gets the claim permission (null if the permission is being taken)
* Can be: "public", "all", a UUID, a permission *
* * @return the claim permission
* @return the identifier */
*/ public ClaimPermission getClaimPermission()
public String getIdentifier() { {
return identifier; return claimPermission;
} }
@Override /**
public boolean isCancelled() { * Checks if the trust is being given
return cancelled; *
} * @return true if given, false if taken
*/
public boolean isGiven()
{
return given;
}
@Override /**
public void setCancelled(boolean cancelled) { * Gets the identifier of the receiver of this action
this.cancelled = cancelled; * Can be: "public", "all", a UUID, a permission
} *
* @return the identifier
*/
public String getIdentifier()
{
return identifier;
}
@Override
public boolean isCancelled()
{
return cancelled;
}
@Override
public void setCancelled(boolean cancelled)
{
this.cancelled = cancelled;
}
} }

View File

@ -11,7 +11,8 @@ import java.util.Collections;
/** /**
* Called when GriefPrevention is sending claim visuals to a player * Called when GriefPrevention is sending claim visuals to a player
*/ */
public class VisualizationEvent extends PlayerEvent { public class VisualizationEvent extends PlayerEvent
{
private static final HandlerList handlers = new HandlerList(); private static final HandlerList handlers = new HandlerList();
private final Collection<Claim> claims; private final Collection<Claim> claims;
private final boolean showSubdivides; private final boolean showSubdivides;
@ -22,7 +23,8 @@ public class VisualizationEvent extends PlayerEvent {
* @param player Player receiving visuals * @param player Player receiving visuals
* @param claim The claim being visualized (with subdivides), or null if visuals being removed * @param claim The claim being visualized (with subdivides), or null if visuals being removed
*/ */
public VisualizationEvent(Player player, Claim claim) { public VisualizationEvent(Player player, Claim claim)
{
super(player); super(player);
this.claims = Collections.singleton(claim); this.claims = Collections.singleton(claim);
this.showSubdivides = true; this.showSubdivides = true;
@ -34,7 +36,8 @@ public class VisualizationEvent extends PlayerEvent {
* @param player Player receiving visuals * @param player Player receiving visuals
* @param claims Claims being visualized (without subdivides) * @param claims Claims being visualized (without subdivides)
*/ */
public VisualizationEvent(Player player, Collection<Claim> claims) { public VisualizationEvent(Player player, Collection<Claim> claims)
{
super(player); super(player);
this.claims = claims; this.claims = claims;
this.showSubdivides = false; this.showSubdivides = false;
@ -45,7 +48,8 @@ public class VisualizationEvent extends PlayerEvent {
* *
* @return Claims being visualized * @return Claims being visualized
*/ */
public Collection<Claim> getClaims() { public Collection<Claim> getClaims()
{
return claims; return claims;
} }
@ -54,16 +58,19 @@ public class VisualizationEvent extends PlayerEvent {
* *
* @return True if subdivide claims are being shown * @return True if subdivide claims are being shown
*/ */
public boolean showSubdivides() { public boolean showSubdivides()
{
return showSubdivides; return showSubdivides;
} }
@Override @Override
public HandlerList getHandlers() { public HandlerList getHandlers()
{
return handlers; return handlers;
} }
public static HandlerList getHandlerList() { public static HandlerList getHandlerList()
{
return handlers; return handlers;
} }
} }

View File

@ -1,5 +1,5 @@
/** /**
* * @author Ryan
*/ */
/** /**
* @author Ryan * @author Ryan

View File

@ -14,6 +14,7 @@ import java.util.concurrent.Callable;
public class MetricsHandler public class MetricsHandler
{ {
private Metrics metrics; private Metrics metrics;
public MetricsHandler(GriefPrevention plugin, String dataMode) public MetricsHandler(GriefPrevention plugin, String dataMode)
{ {
metrics = new Metrics(plugin); metrics = new Metrics(plugin);
@ -23,7 +24,7 @@ public class MetricsHandler
addSimplePie("custom_build", plugin.getDescription().getVersion().equals("15.2.2")); addSimplePie("custom_build", plugin.getDescription().getVersion().equals("15.2.2"));
addSimplePie("bukkit_impl", plugin.getServer().getVersion().split("-")[1]); addSimplePie("bukkit_impl", plugin.getServer().getVersion().split("-")[1]);
} }
catch (Throwable ignored){} catch (Throwable ignored) {}
//enums and etc. would be amazing. //enums and etc. would be amazing.