This commit is contained in:
Ryan Hamshire 2012-08-07 21:46:31 -07:00
parent 50f572fc2b
commit 452fd7f11a
11 changed files with 273 additions and 81 deletions

View File

@ -1,7 +1,8 @@
name: GriefPrevention name: GriefPrevention
main: me.ryanhamshire.GriefPrevention.GriefPrevention main: me.ryanhamshire.GriefPrevention.GriefPrevention
softdepend: [Vault, Multiverse-Core, My Worlds] softdepend: [Vault, Multiverse-Core, My Worlds]
version: 5.5 dev-url: http://dev.bukkit.org/server-mods/grief-prevention
version: 5.9
commands: commands:
abandonclaim: abandonclaim:
description: Deletes a claim. description: Deletes a claim.
@ -124,7 +125,7 @@ commands:
claimslist: claimslist:
description: Lists information about a player's claim blocks and claims. description: Lists information about a player's claim blocks and claims.
usage: /ClaimsList <player> usage: /ClaimsList <player>
permission: griefprevention.adjustbonusclaimblocks permission: griefprevention.adjustclaimblocks
permissions: permissions:
griefprevention.createclaims: griefprevention.createclaims:
description: Grants permission to create claims. description: Grants permission to create claims.

View File

@ -18,6 +18,7 @@
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.bukkit.GameMode; import org.bukkit.GameMode;
@ -35,6 +36,7 @@ import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockBurnEvent; import org.bukkit.event.block.BlockBurnEvent;
import org.bukkit.event.block.BlockDamageEvent; import org.bukkit.event.block.BlockDamageEvent;
import org.bukkit.event.block.BlockDispenseEvent;
import org.bukkit.event.block.BlockFromToEvent; import org.bukkit.event.block.BlockFromToEvent;
import org.bukkit.event.block.BlockIgniteEvent; import org.bukkit.event.block.BlockIgniteEvent;
import org.bukkit.event.block.BlockIgniteEvent.IgniteCause; import org.bukkit.event.block.BlockIgniteEvent.IgniteCause;
@ -47,6 +49,7 @@ import org.bukkit.event.world.StructureGrowEvent;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory; import org.bukkit.inventory.PlayerInventory;
import org.bukkit.util.Vector;
//event handlers related to blocks //event handlers related to blocks
public class BlockEventHandler implements Listener public class BlockEventHandler implements Listener
@ -54,10 +57,23 @@ public class BlockEventHandler implements Listener
//convenience reference to singleton datastore //convenience reference to singleton datastore
private DataStore dataStore; private DataStore dataStore;
//boring typical constructor private ArrayList<Material> trashBlocks;
//constructor
public BlockEventHandler(DataStore dataStore) public BlockEventHandler(DataStore dataStore)
{ {
this.dataStore = dataStore; this.dataStore = dataStore;
//create the list of blocks which will not trigger a warning when they're placed outside of land claims
this.trashBlocks = new ArrayList<Material>();
this.trashBlocks.add(Material.COBBLESTONE);
this.trashBlocks.add(Material.TORCH);
this.trashBlocks.add(Material.DIRT);
this.trashBlocks.add(Material.SAPLING);
this.trashBlocks.add(Material.GRAVEL);
this.trashBlocks.add(Material.SAND);
this.trashBlocks.add(Material.TNT);
this.trashBlocks.add(Material.WORKBENCH);
} }
//when a block is damaged... //when a block is damaged...
@ -251,6 +267,9 @@ public class BlockEventHandler implements Listener
//extend the claim downward //extend the claim downward
this.dataStore.extendClaim(claim, claim.getLesserBoundaryCorner().getBlockY() - GriefPrevention.instance.config_claims_claimsExtendIntoGroundDistance); this.dataStore.extendClaim(claim, claim.getLesserBoundaryCorner().getBlockY() - GriefPrevention.instance.config_claims_claimsExtendIntoGroundDistance);
} }
//reset the counter for warning the player when he places outside his claims
playerData.unclaimedBlockPlacementsUntilWarning = 1;
} }
//FEATURE: automatically create a claim when a player who has no claims places a chest //FEATURE: automatically create a claim when a player who has no claims places a chest
@ -330,7 +349,23 @@ public class BlockEventHandler implements Listener
placeEvent.setCancelled(true); placeEvent.setCancelled(true);
} }
} }
} }
//FEATURE: warn players when they're placing non-trash blocks outside of their claimed areas
else if(GriefPrevention.instance.config_claims_warnOnBuildOutside && !this.trashBlocks.contains(block.getType()) && GriefPrevention.instance.claimsEnabledForWorld(block.getWorld()))
{
if(--playerData.unclaimedBlockPlacementsUntilWarning <= 0)
{
GriefPrevention.sendMessage(player, TextMode.Warn, Messages.BuildingOutsideClaims);
playerData.unclaimedBlockPlacementsUntilWarning = 15;
if(playerData.lastClaim != null && playerData.lastClaim.allowBuild(player) == null)
{
Visualization visualization = Visualization.FromClaim(playerData.lastClaim, block.getY(), VisualizationType.Claim, player.getLocation());
Visualization.Apply(player, visualization);
}
}
}
} }
//blocks "pushing" other players' blocks around (pistons) //blocks "pushing" other players' blocks around (pistons)
@ -339,8 +374,21 @@ public class BlockEventHandler implements Listener
{ {
List<Block> blocks = event.getBlocks(); List<Block> blocks = event.getBlocks();
//if no blocks moving, then we don't care //if no blocks moving, then only check to make sure we're not pushing into a claim from outside
if(blocks.size() == 0) return; //this avoids pistons breaking non-solids just inside a claim, like torches, doors, and touchplates
if(blocks.size() == 0)
{
Block pistonBlock = event.getBlock();
Block invadedBlock = pistonBlock.getRelative(event.getDirection());
if( this.dataStore.getClaimAt(pistonBlock.getLocation(), false, null) == null &&
this.dataStore.getClaimAt(invadedBlock.getLocation(), false, null) != null)
{
event.setCancelled(true);
}
return;
}
//who owns the piston, if anyone? //who owns the piston, if anyone?
String pistonClaimOwnerName = "_"; String pistonClaimOwnerName = "_";
@ -525,6 +573,50 @@ public class BlockEventHandler implements Listener
} }
} }
//ensures dispensers can't be used to dispense a block(like water or lava) or item across a claim boundary
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onDispense(BlockDispenseEvent dispenseEvent)
{
//from where?
Block fromBlock = dispenseEvent.getBlock();
//to where?
Vector velocity = dispenseEvent.getVelocity();
int xChange = 0;
int zChange = 0;
if(Math.abs(velocity.getX()) > Math.abs(velocity.getZ()))
{
if(velocity.getX() > 0) xChange = 1;
else xChange = -1;
}
else
{
if(velocity.getZ() > 0) zChange = 1;
else zChange = -1;
}
Block toBlock = fromBlock.getRelative(xChange, 0, zChange);
Claim fromClaim = this.dataStore.getClaimAt(fromBlock.getLocation(), false, null);
Claim toClaim = this.dataStore.getClaimAt(toBlock.getLocation(), false, fromClaim);
//into wilderness is NOT OK when surface buckets are limited
if(GriefPrevention.instance.config_blockWildernessWaterBuckets && toClaim == null)
{
dispenseEvent.setCancelled(true);
return;
}
//wilderness to wilderness is OK
if(fromClaim == null && toClaim == null) return;
//within claim is OK
if(fromClaim == toClaim) return;
//everything else is NOT OK
dispenseEvent.setCancelled(true);
}
@EventHandler(ignoreCancelled = true) @EventHandler(ignoreCancelled = true)
public void onTreeGrow (StructureGrowEvent growEvent) public void onTreeGrow (StructureGrowEvent growEvent)
{ {

View File

@ -128,13 +128,13 @@ public abstract class DataStore
} }
//removes cached player data from memory //removes cached player data from memory
void clearCachedPlayerData(String playerName) synchronized void clearCachedPlayerData(String playerName)
{ {
this.playerNameToPlayerDataMap.remove(playerName); this.playerNameToPlayerDataMap.remove(playerName);
} }
//gets the number of bonus blocks a player has from his permissions //gets the number of bonus blocks a player has from his permissions
int getGroupBonusBlocks(String playerName) synchronized int getGroupBonusBlocks(String playerName)
{ {
int bonusBlocks = 0; int bonusBlocks = 0;
Set<String> keys = permissionToBonusBlocksMap.keySet(); Set<String> keys = permissionToBonusBlocksMap.keySet();
@ -153,7 +153,7 @@ public abstract class DataStore
} }
//grants a group (players with a specific permission) bonus claim blocks as long as they're still members of the group //grants a group (players with a specific permission) bonus claim blocks as long as they're still members of the group
public int adjustGroupBonusBlocks(String groupName, int amount) synchronized public int adjustGroupBonusBlocks(String groupName, int amount)
{ {
Integer currentValue = this.permissionToBonusBlocksMap.get(groupName); Integer currentValue = this.permissionToBonusBlocksMap.get(groupName);
if(currentValue == null) currentValue = 0; if(currentValue == null) currentValue = 0;
@ -169,7 +169,7 @@ public abstract class DataStore
abstract void saveGroupBonusBlocks(String groupName, int amount); abstract void saveGroupBonusBlocks(String groupName, int amount);
public void changeClaimOwner(Claim claim, String newOwnerName) throws Exception synchronized public void changeClaimOwner(Claim claim, String newOwnerName) throws Exception
{ {
//if it's a subdivision, throw an exception //if it's a subdivision, throw an exception
if(claim.parent != null) if(claim.parent != null)
@ -207,7 +207,7 @@ public abstract class DataStore
} }
//adds a claim to the datastore, making it an effective claim //adds a claim to the datastore, making it an effective claim
void addClaim(Claim newClaim) synchronized void addClaim(Claim newClaim)
{ {
//subdivisions are easy //subdivisions are easy
if(newClaim.parent != null) if(newClaim.parent != null)
@ -287,7 +287,7 @@ public abstract class DataStore
} }
//saves any changes to a claim to secondary storage //saves any changes to a claim to secondary storage
public void saveClaim(Claim claim) synchronized public void saveClaim(Claim claim)
{ {
//subdivisions don't save to their own files, but instead live in their parent claim's file //subdivisions don't save to their own files, but instead live in their parent claim's file
//so any attempt to save a subdivision will save its parent (and thus the subdivision) //so any attempt to save a subdivision will save its parent (and thus the subdivision)
@ -314,7 +314,7 @@ public abstract class DataStore
//retrieves player data from memory or secondary storage, as necessary //retrieves player data from memory or secondary storage, as necessary
//if the player has never been on the server before, this will return a fresh player data with default values //if the player has never been on the server before, this will return a fresh player data with default values
public PlayerData getPlayerData(String playerName) synchronized public PlayerData getPlayerData(String playerName)
{ {
//first, look in memory //first, look in memory
PlayerData playerData = this.playerNameToPlayerDataMap.get(playerName); PlayerData playerData = this.playerNameToPlayerDataMap.get(playerName);
@ -346,7 +346,7 @@ public abstract class DataStore
abstract PlayerData getPlayerDataFromStorage(String playerName); abstract PlayerData getPlayerDataFromStorage(String playerName);
//deletes a claim or subdivision //deletes a claim or subdivision
public void deleteClaim(Claim claim) synchronized public void deleteClaim(Claim claim)
{ {
//subdivisions are simple - just remove them from their parent claim and save that claim //subdivisions are simple - just remove them from their parent claim and save that claim
if(claim.parent != null) if(claim.parent != null)
@ -396,7 +396,7 @@ public abstract class DataStore
//gets the claim at a specific location //gets the claim at a specific location
//ignoreHeight = TRUE means that a location UNDER an existing claim will return the claim //ignoreHeight = TRUE means that a location UNDER an existing claim will return the claim
//cachedClaim can be NULL, but will help performance if you have a reasonable guess about which claim the location is in //cachedClaim can be NULL, but will help performance if you have a reasonable guess about which claim the location is in
public Claim getClaimAt(Location location, boolean ignoreHeight, Claim cachedClaim) synchronized public Claim getClaimAt(Location location, boolean ignoreHeight, Claim cachedClaim)
{ {
//check cachedClaim guess first. if it's in the datastore and the location is inside it, we're done //check cachedClaim guess first. if it's in the datastore and the location is inside it, we're done
if(cachedClaim != null && cachedClaim.inDataStore && cachedClaim.contains(location, ignoreHeight, true)) return cachedClaim; if(cachedClaim != null && cachedClaim.inDataStore && cachedClaim.contains(location, ignoreHeight, true)) return cachedClaim;
@ -443,7 +443,7 @@ public abstract class DataStore
//does NOT check a player has permission to create a claim, or enough claim blocks. //does NOT check a player has permission to create a claim, or enough claim blocks.
//does NOT check minimum claim size constraints //does NOT check minimum claim size constraints
//does NOT visualize the new claim for any players //does NOT visualize the new claim for any players
public CreateClaimResult createClaim(World world, int x1, int x2, int y1, int y2, int z1, int z2, String ownerName, Claim parent, Long id) synchronized public CreateClaimResult createClaim(World world, int x1, int x2, int y1, int y2, int z1, int z2, String ownerName, Claim parent, Long id)
{ {
CreateClaimResult result = new CreateClaimResult(); CreateClaimResult result = new CreateClaimResult();
@ -541,7 +541,7 @@ public abstract class DataStore
//extends a claim to a new depth //extends a claim to a new depth
//respects the max depth config variable //respects the max depth config variable
public void extendClaim(Claim claim, int newDepth) synchronized public void extendClaim(Claim claim, int newDepth)
{ {
if(newDepth < GriefPrevention.instance.config_claims_maxDepth) newDepth = GriefPrevention.instance.config_claims_maxDepth; if(newDepth < GriefPrevention.instance.config_claims_maxDepth) newDepth = GriefPrevention.instance.config_claims_maxDepth;
@ -567,7 +567,7 @@ public abstract class DataStore
//starts a siege on a claim //starts a siege on a claim
//does NOT check siege cooldowns, see onCooldown() below //does NOT check siege cooldowns, see onCooldown() below
public void startSiege(Player attacker, Player defender, Claim defenderClaim) synchronized public void startSiege(Player attacker, Player defender, Claim defenderClaim)
{ {
//fill-in the necessary SiegeData instance //fill-in the necessary SiegeData instance
SiegeData siegeData = new SiegeData(attacker, defender, defenderClaim); SiegeData siegeData = new SiegeData(attacker, defender, defenderClaim);
@ -586,7 +586,7 @@ public abstract class DataStore
//ends a siege //ends a siege
//either winnerName or loserName can be null, but not both //either winnerName or loserName can be null, but not both
public void endSiege(SiegeData siegeData, String winnerName, String loserName, boolean death) synchronized public void endSiege(SiegeData siegeData, String winnerName, String loserName, boolean death)
{ {
boolean grantAccess = false; boolean grantAccess = false;
@ -704,7 +704,7 @@ public abstract class DataStore
private HashMap<String, Long> siegeCooldownRemaining = new HashMap<String, Long>(); private HashMap<String, Long> siegeCooldownRemaining = new HashMap<String, Long>();
//whether or not a sieger can siege a particular victim or claim, considering only cooldowns //whether or not a sieger can siege a particular victim or claim, considering only cooldowns
public boolean onCooldown(Player attacker, Player defender, Claim defenderClaim) synchronized public boolean onCooldown(Player attacker, Player defender, Claim defenderClaim)
{ {
Long cooldownEnd = null; Long cooldownEnd = null;
@ -740,7 +740,7 @@ public abstract class DataStore
} }
//extend a siege, if it's possible to do so //extend a siege, if it's possible to do so
void tryExtendSiege(Player player, Claim claim) synchronized void tryExtendSiege(Player player, Claim claim)
{ {
PlayerData playerData = this.getPlayerData(player.getName()); PlayerData playerData = this.getPlayerData(player.getName());
@ -762,7 +762,7 @@ public abstract class DataStore
} }
//deletes all claims owned by a player //deletes all claims owned by a player
public void deleteClaimsForPlayer(String playerName, boolean deleteCreativeClaims) synchronized public void deleteClaimsForPlayer(String playerName, boolean deleteCreativeClaims)
{ {
//make a list of the player's claims //make a list of the player's claims
ArrayList<Claim> claimsToDelete = new ArrayList<Claim>(); ArrayList<Claim> claimsToDelete = new ArrayList<Claim>();
@ -783,7 +783,7 @@ public abstract class DataStore
//tries to resize a claim //tries to resize a claim
//see CreateClaim() for details on return value //see CreateClaim() for details on return value
public CreateClaimResult resizeClaim(Claim claim, int newx1, int newx2, int newy1, int newy2, int newz1, int newz2) synchronized public CreateClaimResult resizeClaim(Claim claim, int newx1, int newx2, int newy1, int newy2, int newz1, int newz2)
{ {
//remove old claim //remove old claim
this.deleteClaim(claim); this.deleteClaim(claim);
@ -993,7 +993,8 @@ public abstract class DataStore
this.addDefault(defaults, Messages.UntrustOwnerOnly, "Only {0} can revoke permissions here.", "0: claim owner's name"); this.addDefault(defaults, Messages.UntrustOwnerOnly, "Only {0} can revoke permissions here.", "0: claim owner's name");
this.addDefault(defaults, Messages.HowToClaimRegex, "(^|.*\\W)how\\W.*\\W(claim|protect)(\\W.*|$)", "This is a Java Regular Expression. Look it up before editing! It's used to tell players about the demo video when they ask how to claim land."); this.addDefault(defaults, Messages.HowToClaimRegex, "(^|.*\\W)how\\W.*\\W(claim|protect)(\\W.*|$)", "This is a Java Regular Expression. Look it up before editing! It's used to tell players about the demo video when they ask how to claim land.");
this.addDefault(defaults, Messages.NoBuildOutsideClaims, "You can't build here unless you claim some land first.", null); this.addDefault(defaults, Messages.NoBuildOutsideClaims, "You can't build here unless you claim some land first.", null);
this.addDefault(defaults, Messages.PlayerOfflineTime, " Last login: {0} days ago.", "0: number of full days since last login"); this.addDefault(defaults, Messages.PlayerOfflineTime, " Last login: {0} days ago.", "0: number of full days since last login");
this.addDefault(defaults, Messages.BuildingOutsideClaims, "Other players can undo your work here! Consider claiming this area to protect your work.", null);
//load the config file //load the config file
FileConfiguration config = YamlConfiguration.loadConfiguration(new File(messagesFilePath)); FileConfiguration config = YamlConfiguration.loadConfiguration(new File(messagesFilePath));
@ -1044,7 +1045,7 @@ public abstract class DataStore
defaults.put(id.name(), message); defaults.put(id.name(), message);
} }
public String getMessage(Messages messageID, String... args) synchronized public String getMessage(Messages messageID, String... args)
{ {
String message = messages[messageID.ordinal()]; String message = messages[messageID.ordinal()];

View File

@ -236,7 +236,7 @@ public class DatabaseDataStore extends DataStore
} }
@Override @Override
void writeClaimToStorage(Claim claim) //see datastore.cs. this will ALWAYS be a top level claim synchronized void writeClaimToStorage(Claim claim) //see datastore.cs. this will ALWAYS be a top level claim
{ {
try try
{ {
@ -261,7 +261,7 @@ public class DatabaseDataStore extends DataStore
} }
//actually writes claim data to the database //actually writes claim data to the database
private void writeClaimData(Claim claim) throws SQLException synchronized private void writeClaimData(Claim claim) throws SQLException
{ {
String lesserCornerString = this.locationToString(claim.getLesserBoundaryCorner()); String lesserCornerString = this.locationToString(claim.getLesserBoundaryCorner());
String greaterCornerString = this.locationToString(claim.getGreaterBoundaryCorner()); String greaterCornerString = this.locationToString(claim.getGreaterBoundaryCorner());
@ -342,7 +342,7 @@ public class DatabaseDataStore extends DataStore
//deletes a top level claim from the database //deletes a top level claim from the database
@Override @Override
void deleteClaimFromSecondaryStorage(Claim claim) synchronized void deleteClaimFromSecondaryStorage(Claim claim)
{ {
try try
{ {
@ -358,7 +358,7 @@ public class DatabaseDataStore extends DataStore
} }
@Override @Override
PlayerData getPlayerDataFromStorage(String playerName) synchronized PlayerData getPlayerDataFromStorage(String playerName)
{ {
PlayerData playerData = new PlayerData(); PlayerData playerData = new PlayerData();
playerData.playerName = playerName; playerData.playerName = playerName;
@ -393,7 +393,7 @@ public class DatabaseDataStore extends DataStore
//saves changes to player data. MUST be called after you're done making changes, otherwise a reload will lose them //saves changes to player data. MUST be called after you're done making changes, otherwise a reload will lose them
@Override @Override
public void savePlayerData(String playerName, PlayerData playerData) synchronized public void savePlayerData(String playerName, PlayerData playerData)
{ {
//never save data for the "administrative" account. an empty string for player name indicates administrative account //never save data for the "administrative" account. an empty string for player name indicates administrative account
if(playerName.length() == 0) return; if(playerName.length() == 0) return;
@ -415,13 +415,13 @@ public class DatabaseDataStore extends DataStore
} }
@Override @Override
void incrementNextClaimID() synchronized void incrementNextClaimID()
{ {
this.setNextClaimID(this.nextClaimID + 1); this.setNextClaimID(this.nextClaimID + 1);
} }
//sets the next claim ID. used by incrementNextClaimID() above, and also while migrating data from a flat file data store //sets the next claim ID. used by incrementNextClaimID() above, and also while migrating data from a flat file data store
void setNextClaimID(long nextID) synchronized void setNextClaimID(long nextID)
{ {
this.nextClaimID = nextID; this.nextClaimID = nextID;
@ -440,7 +440,7 @@ public class DatabaseDataStore extends DataStore
//updates the database with a group's bonus blocks //updates the database with a group's bonus blocks
@Override @Override
void saveGroupBonusBlocks(String groupName, int currentValue) synchronized void saveGroupBonusBlocks(String groupName, int currentValue)
{ {
//group bonus blocks are stored in the player data table, with player name = $groupName //group bonus blocks are stored in the player data table, with player name = $groupName
String playerName = "$" + groupName; String playerName = "$" + groupName;
@ -451,7 +451,7 @@ public class DatabaseDataStore extends DataStore
} }
@Override @Override
void close() synchronized void close()
{ {
if(this.databaseConnection != null) if(this.databaseConnection != null)
{ {

View File

@ -27,6 +27,7 @@ import org.bukkit.World.Environment;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.entity.Arrow; import org.bukkit.entity.Arrow;
import org.bukkit.entity.Creature; import org.bukkit.entity.Creature;
import org.bukkit.entity.Creeper;
import org.bukkit.entity.Enderman; import org.bukkit.entity.Enderman;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
@ -92,8 +93,8 @@ class EntityEventHandler implements Listener
Location location = explodeEvent.getLocation(); Location location = explodeEvent.getLocation();
//FEATURE: explosions don't destroy blocks when they explode near or above sea level in standard worlds //FEATURE: explosions don't destroy blocks when they explode near or above sea level in standard worlds
boolean isCreeper = (explodeEvent.getEntity() != null && explodeEvent.getEntity() instanceof Creeper);
if(GriefPrevention.instance.config_blockSurfaceExplosions && location.getWorld().getEnvironment() == Environment.NORMAL) if( location.getWorld().getEnvironment() == Environment.NORMAL && ((isCreeper && GriefPrevention.instance.config_blockSurfaceCreeperExplosions) || (!isCreeper && GriefPrevention.instance.config_blockSurfaceOtherExplosions)))
{ {
for(int i = 0; i < blocks.size(); i++) for(int i = 0; i < blocks.size(); i++)
{ {

View File

@ -257,7 +257,7 @@ public class FlatFileDataStore extends DataStore
} }
@Override @Override
void writeClaimToStorage(Claim claim) synchronized void writeClaimToStorage(Claim claim)
{ {
String claimID = String.valueOf(claim.id); String claimID = String.valueOf(claim.id);
@ -296,7 +296,7 @@ public class FlatFileDataStore extends DataStore
} }
//actually writes claim data to an output stream //actually writes claim data to an output stream
private void writeClaimData(Claim claim, BufferedWriter outStream) throws IOException synchronized private void writeClaimData(Claim claim, BufferedWriter outStream) throws IOException
{ {
//first line is lesser boundary corner location //first line is lesser boundary corner location
outStream.write(this.locationToString(claim.getLesserBoundaryCorner())); outStream.write(this.locationToString(claim.getLesserBoundaryCorner()));
@ -352,7 +352,7 @@ public class FlatFileDataStore extends DataStore
//deletes a top level claim from the file system //deletes a top level claim from the file system
@Override @Override
void deleteClaimFromSecondaryStorage(Claim claim) synchronized void deleteClaimFromSecondaryStorage(Claim claim)
{ {
String claimID = String.valueOf(claim.id); String claimID = String.valueOf(claim.id);
@ -365,7 +365,7 @@ public class FlatFileDataStore extends DataStore
} }
@Override @Override
PlayerData getPlayerDataFromStorage(String playerName) synchronized PlayerData getPlayerDataFromStorage(String playerName)
{ {
File playerFile = new File(playerDataFolderPath + File.separator + playerName); File playerFile = new File(playerDataFolderPath + File.separator + playerName);
@ -439,7 +439,7 @@ public class FlatFileDataStore extends DataStore
//saves changes to player data. MUST be called after you're done making changes, otherwise a reload will lose them //saves changes to player data. MUST be called after you're done making changes, otherwise a reload will lose them
@Override @Override
public void savePlayerData(String playerName, PlayerData playerData) synchronized public void savePlayerData(String playerName, PlayerData playerData)
{ {
//never save data for the "administrative" account. an empty string for claim owner indicates administrative account //never save data for the "administrative" account. an empty string for claim owner indicates administrative account
if(playerName.length() == 0) return; if(playerName.length() == 0) return;
@ -496,7 +496,7 @@ public class FlatFileDataStore extends DataStore
} }
@Override @Override
void incrementNextClaimID() synchronized void incrementNextClaimID()
{ {
//increment in memory //increment in memory
this.nextClaimID++; this.nextClaimID++;
@ -529,7 +529,7 @@ public class FlatFileDataStore extends DataStore
//grants a group (players with a specific permission) bonus claim blocks as long as they're still members of the group //grants a group (players with a specific permission) bonus claim blocks as long as they're still members of the group
@Override @Override
void saveGroupBonusBlocks(String groupName, int currentValue) synchronized void saveGroupBonusBlocks(String groupName, int currentValue)
{ {
//write changes to file to ensure they don't get lost //write changes to file to ensure they don't get lost
BufferedWriter outStream = null; BufferedWriter outStream = null;
@ -562,7 +562,7 @@ public class FlatFileDataStore extends DataStore
catch(IOException exception){} catch(IOException exception){}
} }
void migrateData(DatabaseDataStore databaseStore) synchronized void migrateData(DatabaseDataStore databaseStore)
{ {
//migrate claims //migrate claims
for(int i = 0; i < this.claims.size(); i++) for(int i = 0; i < this.claims.size(); i++)
@ -629,5 +629,5 @@ public class FlatFileDataStore extends DataStore
} }
@Override @Override
void close() { } synchronized void close() { }
} }

View File

@ -66,7 +66,9 @@ public class GriefPrevention extends JavaPlugin
public boolean config_claims_preventTheft; //whether containers and crafting blocks are protectable public boolean config_claims_preventTheft; //whether containers and crafting blocks are protectable
public boolean config_claims_protectCreatures; //whether claimed animals may be injured by players without permission public boolean config_claims_protectCreatures; //whether claimed animals may be injured by players without permission
public boolean config_claims_preventButtonsSwitches; //whether buttons and switches are protectable public boolean config_claims_preventButtonsSwitches; //whether buttons and switches are protectable
public boolean config_claims_lockAllDoors; //whether wooden doors, trap doors, fence gates, etc require permission to use public boolean config_claims_lockWoodenDoors; //whether wooden doors should be locked by default (require /accesstrust)
public boolean config_claims_lockTrapDoors; //whether trap doors should be locked by default (require /accesstrust)
public boolean config_claims_lockFenceGates; //whether fence gates should be locked by default (require /accesstrust)
public int config_claims_initialBlocks; //the number of claim blocks a new player starts with public int config_claims_initialBlocks; //the number of claim blocks a new player starts with
public int config_claims_blocksAccruedPerHour; //how many additional blocks players get each hour of play (can be zero) public int config_claims_blocksAccruedPerHour; //how many additional blocks players get each hour of play (can be zero)
@ -108,7 +110,8 @@ public class GriefPrevention extends JavaPlugin
public double config_economy_claimBlocksPurchaseCost; //cost to purchase a claim block. set to zero to disable purchase. public double config_economy_claimBlocksPurchaseCost; //cost to purchase a claim block. set to zero to disable purchase.
public double config_economy_claimBlocksSellValue; //return on a sold claim block. set to zero to disable sale. public double config_economy_claimBlocksSellValue; //return on a sold claim block. set to zero to disable sale.
public boolean config_blockSurfaceExplosions; //whether creeper/TNT explosions near or above the surface destroy blocks public boolean config_blockSurfaceCreeperExplosions; //whether creeper explosions near or above the surface destroy blocks
public boolean config_blockSurfaceOtherExplosions; //whether non-creeper explosions near or above the surface destroy blocks
public boolean config_blockWildernessWaterBuckets; //whether players can dump water buckets outside their claims public boolean config_blockWildernessWaterBuckets; //whether players can dump water buckets outside their claims
public boolean config_blockSkyTrees; //whether players can build trees on platforms in the sky public boolean config_blockSkyTrees; //whether players can build trees on platforms in the sky
@ -128,6 +131,8 @@ public class GriefPrevention extends JavaPlugin
public List<Integer> config_mods_containerTrustIds; //list of block IDs which should require /containertrust for player interaction public List<Integer> config_mods_containerTrustIds; //list of block IDs which should require /containertrust for player interaction
public List<String> config_mods_ignoreClaimsAccounts; //list of player names which ALWAYS ignore claims public List<String> config_mods_ignoreClaimsAccounts; //list of player names which ALWAYS ignore claims
public List<Integer> config_mods_explodableIds; //list of block IDs which can be destroyed by explosions, even in claimed areas public List<Integer> config_mods_explodableIds; //list of block IDs which can be destroyed by explosions, even in claimed areas
public boolean config_claims_warnOnBuildOutside; //whether players should be warned when they're building in an unclaimed area
//reference to the economy plugin, if economy integration is enabled //reference to the economy plugin, if economy integration is enabled
public static Economy economy = null; public static Economy economy = null;
@ -225,7 +230,9 @@ public class GriefPrevention extends JavaPlugin
this.config_claims_preventTheft = config.getBoolean("GriefPrevention.Claims.PreventTheft", true); this.config_claims_preventTheft = config.getBoolean("GriefPrevention.Claims.PreventTheft", true);
this.config_claims_protectCreatures = config.getBoolean("GriefPrevention.Claims.ProtectCreatures", true); this.config_claims_protectCreatures = config.getBoolean("GriefPrevention.Claims.ProtectCreatures", true);
this.config_claims_preventButtonsSwitches = config.getBoolean("GriefPrevention.Claims.PreventButtonsSwitches", true); this.config_claims_preventButtonsSwitches = config.getBoolean("GriefPrevention.Claims.PreventButtonsSwitches", true);
this.config_claims_lockAllDoors = config.getBoolean("GriefPrevention.Claims.LockAllDoors", false); this.config_claims_lockWoodenDoors = config.getBoolean("GriefPrevention.Claims.LockWoodenDoors", false);
this.config_claims_lockTrapDoors = config.getBoolean("GriefPrevention.Claims.LockTrapDoors", false);
this.config_claims_lockFenceGates = config.getBoolean("GriefPrevention.Claims.LockFenceGates", false);
this.config_claims_initialBlocks = config.getInt("GriefPrevention.Claims.InitialBlocks", 100); this.config_claims_initialBlocks = config.getInt("GriefPrevention.Claims.InitialBlocks", 100);
this.config_claims_blocksAccruedPerHour = config.getInt("GriefPrevention.Claims.BlocksAccruedPerHour", 100); this.config_claims_blocksAccruedPerHour = config.getInt("GriefPrevention.Claims.BlocksAccruedPerHour", 100);
this.config_claims_maxAccruedBlocks = config.getInt("GriefPrevention.Claims.MaxAccruedBlocks", 80000); this.config_claims_maxAccruedBlocks = config.getInt("GriefPrevention.Claims.MaxAccruedBlocks", 80000);
@ -237,6 +244,7 @@ public class GriefPrevention extends JavaPlugin
this.config_claims_expirationDays = config.getInt("GriefPrevention.Claims.IdleLimitDays", 0); this.config_claims_expirationDays = config.getInt("GriefPrevention.Claims.IdleLimitDays", 0);
this.config_claims_trappedCooldownHours = config.getInt("GriefPrevention.Claims.TrappedCommandCooldownHours", 8); this.config_claims_trappedCooldownHours = config.getInt("GriefPrevention.Claims.TrappedCommandCooldownHours", 8);
this.config_claims_noBuildOutsideClaims = config.getBoolean("GriefPrevention.Claims.NoSurvivalBuildingOutsideClaims", false); this.config_claims_noBuildOutsideClaims = config.getBoolean("GriefPrevention.Claims.NoSurvivalBuildingOutsideClaims", false);
this.config_claims_warnOnBuildOutside = config.getBoolean("GriefPrevention.Claims.WarnWhenBuildingOutsideClaims");
this.config_spam_enabled = config.getBoolean("GriefPrevention.Spam.Enabled", true); this.config_spam_enabled = config.getBoolean("GriefPrevention.Spam.Enabled", true);
this.config_spam_loginCooldownMinutes = config.getInt("GriefPrevention.Spam.LoginCooldownMinutes", 2); this.config_spam_loginCooldownMinutes = config.getInt("GriefPrevention.Spam.LoginCooldownMinutes", 2);
@ -257,7 +265,8 @@ public class GriefPrevention extends JavaPlugin
this.config_economy_claimBlocksPurchaseCost = config.getDouble("GriefPrevention.Economy.ClaimBlocksPurchaseCost", 0); this.config_economy_claimBlocksPurchaseCost = config.getDouble("GriefPrevention.Economy.ClaimBlocksPurchaseCost", 0);
this.config_economy_claimBlocksSellValue = config.getDouble("GriefPrevention.Economy.ClaimBlocksSellValue", 0); this.config_economy_claimBlocksSellValue = config.getDouble("GriefPrevention.Economy.ClaimBlocksSellValue", 0);
this.config_blockSurfaceExplosions = config.getBoolean("GriefPrevention.BlockSurfaceExplosions", true); this.config_blockSurfaceCreeperExplosions = config.getBoolean("GriefPrevention.BlockSurfaceCreeperExplosions", true);
this.config_blockSurfaceOtherExplosions = config.getBoolean("GriefPrevention.BlockSurfaceOtherExplosions", true);
this.config_blockWildernessWaterBuckets = config.getBoolean("GriefPrevention.LimitSurfaceWaterBuckets", true); this.config_blockWildernessWaterBuckets = config.getBoolean("GriefPrevention.LimitSurfaceWaterBuckets", true);
this.config_blockSkyTrees = config.getBoolean("GriefPrevention.LimitSkyTrees", true); this.config_blockSkyTrees = config.getBoolean("GriefPrevention.LimitSkyTrees", true);
@ -276,7 +285,7 @@ public class GriefPrevention extends JavaPlugin
this.config_mods_accessTrustIds = config.getIntegerList("GriefPrevention.Mods.BlockIdsRequiringAccessTrust"); this.config_mods_accessTrustIds = config.getIntegerList("GriefPrevention.Mods.BlockIdsRequiringAccessTrust");
if(this.config_mods_accessTrustIds == null) this.config_mods_accessTrustIds = new ArrayList<Integer>(); if(this.config_mods_accessTrustIds == null) this.config_mods_accessTrustIds = new ArrayList<Integer>();
this.config_mods_accessTrustIds = config.getIntegerList("GriefPrevention.Mods.BlockIdsRequiringContainerTrust"); this.config_mods_containerTrustIds = config.getIntegerList("GriefPrevention.Mods.BlockIdsRequiringContainerTrust");
if(this.config_mods_containerTrustIds == null) this.config_mods_containerTrustIds = new ArrayList<Integer>(); if(this.config_mods_containerTrustIds == null) this.config_mods_containerTrustIds = new ArrayList<Integer>();
this.config_mods_ignoreClaimsAccounts = config.getStringList("GriefPrevention.Mods.PlayersIgnoringAllClaims"); this.config_mods_ignoreClaimsAccounts = config.getStringList("GriefPrevention.Mods.PlayersIgnoringAllClaims");
@ -395,7 +404,9 @@ public class GriefPrevention extends JavaPlugin
config.set("GriefPrevention.Claims.PreventTheft", this.config_claims_preventTheft); config.set("GriefPrevention.Claims.PreventTheft", this.config_claims_preventTheft);
config.set("GriefPrevention.Claims.ProtectCreatures", this.config_claims_protectCreatures); config.set("GriefPrevention.Claims.ProtectCreatures", this.config_claims_protectCreatures);
config.set("GriefPrevention.Claims.PreventButtonsSwitches", this.config_claims_preventButtonsSwitches); config.set("GriefPrevention.Claims.PreventButtonsSwitches", this.config_claims_preventButtonsSwitches);
config.set("GriefPrevention.Claims.LockAllDoors", this.config_claims_lockAllDoors); config.set("GriefPrevention.Claims.LockWoodenDoors", this.config_claims_lockWoodenDoors);
config.set("GriefPrevention.Claims.LockTrapDoors", this.config_claims_lockTrapDoors);
config.set("GriefPrevention.Claims.LockFenceGates", this.config_claims_lockFenceGates);
config.set("GriefPrevention.Claims.InitialBlocks", this.config_claims_initialBlocks); config.set("GriefPrevention.Claims.InitialBlocks", this.config_claims_initialBlocks);
config.set("GriefPrevention.Claims.BlocksAccruedPerHour", this.config_claims_blocksAccruedPerHour); config.set("GriefPrevention.Claims.BlocksAccruedPerHour", this.config_claims_blocksAccruedPerHour);
config.set("GriefPrevention.Claims.MaxAccruedBlocks", this.config_claims_maxAccruedBlocks); config.set("GriefPrevention.Claims.MaxAccruedBlocks", this.config_claims_maxAccruedBlocks);
@ -408,7 +419,8 @@ public class GriefPrevention extends JavaPlugin
config.set("GriefPrevention.Claims.TrappedCommandCooldownHours", this.config_claims_trappedCooldownHours); config.set("GriefPrevention.Claims.TrappedCommandCooldownHours", this.config_claims_trappedCooldownHours);
config.set("GriefPrevention.Claims.InvestigationTool", this.config_claims_investigationTool.name()); config.set("GriefPrevention.Claims.InvestigationTool", this.config_claims_investigationTool.name());
config.set("GriefPrevention.Claims.ModificationTool", this.config_claims_modificationTool.name()); config.set("GriefPrevention.Claims.ModificationTool", this.config_claims_modificationTool.name());
config.set("GriefPrevention.Claims.NoSurvivalBuildingOutsideClaims", this.config_claims_noBuildOutsideClaims); config.set("GriefPrevention.Claims.NoSurvivalBuildingOutsideClaims", this.config_claims_noBuildOutsideClaims);
config.set("GriefPrevention.Claims.WarnWhenBuildingOutsideClaims", this.config_claims_warnOnBuildOutside);
config.set("GriefPrevention.Spam.Enabled", this.config_spam_enabled); config.set("GriefPrevention.Spam.Enabled", this.config_spam_enabled);
config.set("GriefPrevention.Spam.LoginCooldownMinutes", this.config_spam_loginCooldownMinutes); config.set("GriefPrevention.Spam.LoginCooldownMinutes", this.config_spam_loginCooldownMinutes);
@ -429,7 +441,8 @@ public class GriefPrevention extends JavaPlugin
config.set("GriefPrevention.Economy.ClaimBlocksPurchaseCost", this.config_economy_claimBlocksPurchaseCost); config.set("GriefPrevention.Economy.ClaimBlocksPurchaseCost", this.config_economy_claimBlocksPurchaseCost);
config.set("GriefPrevention.Economy.ClaimBlocksSellValue", this.config_economy_claimBlocksSellValue); config.set("GriefPrevention.Economy.ClaimBlocksSellValue", this.config_economy_claimBlocksSellValue);
config.set("GriefPrevention.BlockSurfaceExplosions", this.config_blockSurfaceExplosions); config.set("GriefPrevention.BlockSurfaceCreeperExplosions", this.config_blockSurfaceCreeperExplosions);
config.set("GriefPrevention.BlockSurfaceOtherExplosions", this.config_blockSurfaceOtherExplosions);
config.set("GriefPrevention.LimitSurfaceWaterBuckets", this.config_blockWildernessWaterBuckets); config.set("GriefPrevention.LimitSurfaceWaterBuckets", this.config_blockWildernessWaterBuckets);
config.set("GriefPrevention.LimitSkyTrees", this.config_blockSkyTrees); config.set("GriefPrevention.LimitSkyTrees", this.config_blockSkyTrees);
@ -2117,9 +2130,15 @@ public class GriefPrevention extends JavaPlugin
//sends a color-coded message to a player //sends a color-coded message to a player
static void sendMessage(Player player, ChatColor color, Messages messageID, String... args) static void sendMessage(Player player, ChatColor color, Messages messageID, String... args)
{
sendMessage(player, color, messageID, 0, args);
}
//sends a color-coded message to a player
static void sendMessage(Player player, ChatColor color, Messages messageID, long delayInTicks, String... args)
{ {
String message = GriefPrevention.instance.dataStore.getMessage(messageID, args); String message = GriefPrevention.instance.dataStore.getMessage(messageID, args);
sendMessage(player, color, message); sendMessage(player, color, message, delayInTicks);
} }
//sends a color-coded message to a player //sends a color-coded message to a player
@ -2135,6 +2154,19 @@ public class GriefPrevention extends JavaPlugin
} }
} }
static void sendMessage(Player player, ChatColor color, String message, long delayInTicks)
{
SendPlayerMessageTask task = new SendPlayerMessageTask(player, color, message);
if(delayInTicks > 0)
{
GriefPrevention.instance.getServer().getScheduler().scheduleAsyncDelayedTask(GriefPrevention.instance, task, delayInTicks);
}
else
{
task.run();
}
}
//determines whether creative anti-grief rules apply at a location //determines whether creative anti-grief rules apply at a location
boolean creativeRulesApply(Location location) boolean creativeRulesApply(Location location)
{ {

View File

@ -2,5 +2,5 @@ package me.ryanhamshire.GriefPrevention;
public enum Messages public enum Messages
{ {
RespectingClaims, IgnoringClaims, SuccessfulAbandon, RestoreNatureActivate, RestoreNatureAggressiveActivate, FillModeActive, TransferClaimPermission, TransferClaimMissing, TransferClaimAdminOnly, PlayerNotFound, TransferTopLevel, TransferSuccess, TrustListNoClaim, ClearPermsOwnerOnly, UntrustIndividualAllClaims, UntrustEveryoneAllClaims, NoPermissionTrust, ClearPermissionsOneClaim, UntrustIndividualSingleClaim, OnlySellBlocks, BlockPurchaseCost, ClaimBlockLimit, InsufficientFunds, PurchaseConfirmation, OnlyPurchaseBlocks, BlockSaleValue, NotEnoughBlocksForSale, BlockSaleConfirmation, AdminClaimsMode, BasicClaimsMode, SubdivisionMode, SubdivisionDemo, DeleteClaimMissing, DeletionSubdivisionWarning, DeleteSuccess, CantDeleteAdminClaim, DeleteAllSuccess, NoDeletePermission, AllAdminDeleted, AdjustBlocksSuccess, NotTrappedHere, TrappedOnCooldown, RescuePending, NonSiegeWorld, AlreadySieging, NotSiegableThere, SiegeTooFarAway, NoSiegeDefenseless, AlreadyUnderSiegePlayer, AlreadyUnderSiegeArea, NoSiegeAdminClaim, SiegeOnCooldown, SiegeAlert, SiegeConfirmed, AbandonClaimMissing, NotYourClaim, DeleteTopLevelClaim, AbandonSuccess, CantGrantThatPermission, GrantPermissionNoClaim, GrantPermissionConfirmation, ManageUniversalPermissionsInstruction, ManageOneClaimPermissionsInstruction, CollectivePublic, BuildPermission, ContainersPermission, AccessPermission, PermissionsPermission, LocationCurrentClaim, LocationAllClaims, PvPImmunityStart, SiegeNoDrop, DonateItemsInstruction, ChestFull, DonationSuccess, PlayerTooCloseForFire, TooDeepToClaim, ChestClaimConfirmation, AutomaticClaimNotification, TrustCommandAdvertisement, GoldenShovelAdvertisement, UnprotectedChestWarning, ThatPlayerPvPImmune, CantFightWhileImmune, NoDamageClaimedEntity, ShovelBasicClaimMode, RemainingBlocks, CreativeBasicsDemoAdvertisement, SurvivalBasicsDemoAdvertisement, TrappedChatKeyword, TrappedInstructions, PvPNoDrop, SiegeNoTeleport, BesiegedNoTeleport, SiegeNoContainers, PvPNoContainers, PvPImmunityEnd, NoBedPermission, NoWildernessBuckets, NoLavaNearOtherPlayer, TooFarAway, BlockNotClaimed, BlockClaimed, SiegeNoShovel, RestoreNaturePlayerInChunk, NoCreateClaimPermission, ResizeClaimTooSmall, ResizeNeedMoreBlocks, NoCreativeUnClaim, ClaimResizeSuccess, ResizeFailOverlap, ResizeStart, ResizeFailOverlapSubdivision, SubdivisionStart, CreateSubdivisionOverlap, SubdivisionSuccess, CreateClaimFailOverlap, CreateClaimFailOverlapOtherPlayer, ClaimsDisabledWorld, ClaimStart, NewClaimTooSmall, CreateClaimInsufficientBlocks, AbandonClaimAdvertisement, CreateClaimFailOverlapShort, CreateClaimSuccess, SiegeWinDoorsOpen, RescueAbortedMoved, SiegeDoorsLockedEjection, NoModifyDuringSiege, OnlyOwnersModifyClaims, NoBuildUnderSiege, NoBuildPvP, NoBuildPermission, NonSiegeMaterial, NoOwnerBuildUnderSiege, NoAccessPermission, NoContainersSiege, NoContainersPermission, OwnerNameForAdminClaims, ClaimTooSmallForEntities, TooManyEntitiesInClaim, YouHaveNoClaims, ConfirmFluidRemoval, AutoBanNotify, AdjustGroupBlocksSuccess, InvalidPermissionID, UntrustOwnerOnly, HowToClaimRegex, NoBuildOutsideClaims, PlayerOfflineTime RespectingClaims, IgnoringClaims, SuccessfulAbandon, RestoreNatureActivate, RestoreNatureAggressiveActivate, FillModeActive, TransferClaimPermission, TransferClaimMissing, TransferClaimAdminOnly, PlayerNotFound, TransferTopLevel, TransferSuccess, TrustListNoClaim, ClearPermsOwnerOnly, UntrustIndividualAllClaims, UntrustEveryoneAllClaims, NoPermissionTrust, ClearPermissionsOneClaim, UntrustIndividualSingleClaim, OnlySellBlocks, BlockPurchaseCost, ClaimBlockLimit, InsufficientFunds, PurchaseConfirmation, OnlyPurchaseBlocks, BlockSaleValue, NotEnoughBlocksForSale, BlockSaleConfirmation, AdminClaimsMode, BasicClaimsMode, SubdivisionMode, SubdivisionDemo, DeleteClaimMissing, DeletionSubdivisionWarning, DeleteSuccess, CantDeleteAdminClaim, DeleteAllSuccess, NoDeletePermission, AllAdminDeleted, AdjustBlocksSuccess, NotTrappedHere, TrappedOnCooldown, RescuePending, NonSiegeWorld, AlreadySieging, NotSiegableThere, SiegeTooFarAway, NoSiegeDefenseless, AlreadyUnderSiegePlayer, AlreadyUnderSiegeArea, NoSiegeAdminClaim, SiegeOnCooldown, SiegeAlert, SiegeConfirmed, AbandonClaimMissing, NotYourClaim, DeleteTopLevelClaim, AbandonSuccess, CantGrantThatPermission, GrantPermissionNoClaim, GrantPermissionConfirmation, ManageUniversalPermissionsInstruction, ManageOneClaimPermissionsInstruction, CollectivePublic, BuildPermission, ContainersPermission, AccessPermission, PermissionsPermission, LocationCurrentClaim, LocationAllClaims, PvPImmunityStart, SiegeNoDrop, DonateItemsInstruction, ChestFull, DonationSuccess, PlayerTooCloseForFire, TooDeepToClaim, ChestClaimConfirmation, AutomaticClaimNotification, TrustCommandAdvertisement, GoldenShovelAdvertisement, UnprotectedChestWarning, ThatPlayerPvPImmune, CantFightWhileImmune, NoDamageClaimedEntity, ShovelBasicClaimMode, RemainingBlocks, CreativeBasicsDemoAdvertisement, SurvivalBasicsDemoAdvertisement, TrappedChatKeyword, TrappedInstructions, PvPNoDrop, SiegeNoTeleport, BesiegedNoTeleport, SiegeNoContainers, PvPNoContainers, PvPImmunityEnd, NoBedPermission, NoWildernessBuckets, NoLavaNearOtherPlayer, TooFarAway, BlockNotClaimed, BlockClaimed, SiegeNoShovel, RestoreNaturePlayerInChunk, NoCreateClaimPermission, ResizeClaimTooSmall, ResizeNeedMoreBlocks, NoCreativeUnClaim, ClaimResizeSuccess, ResizeFailOverlap, ResizeStart, ResizeFailOverlapSubdivision, SubdivisionStart, CreateSubdivisionOverlap, SubdivisionSuccess, CreateClaimFailOverlap, CreateClaimFailOverlapOtherPlayer, ClaimsDisabledWorld, ClaimStart, NewClaimTooSmall, CreateClaimInsufficientBlocks, AbandonClaimAdvertisement, CreateClaimFailOverlapShort, CreateClaimSuccess, SiegeWinDoorsOpen, RescueAbortedMoved, SiegeDoorsLockedEjection, NoModifyDuringSiege, OnlyOwnersModifyClaims, NoBuildUnderSiege, NoBuildPvP, NoBuildPermission, NonSiegeMaterial, NoOwnerBuildUnderSiege, NoAccessPermission, NoContainersSiege, NoContainersPermission, OwnerNameForAdminClaims, ClaimTooSmallForEntities, TooManyEntitiesInClaim, YouHaveNoClaims, ConfirmFluidRemoval, AutoBanNotify, AdjustGroupBlocksSuccess, InvalidPermissionID, UntrustOwnerOnly, HowToClaimRegex, NoBuildOutsideClaims, PlayerOfflineTime, BuildingOutsideClaims
} }

View File

@ -67,6 +67,9 @@ public class PlayerData
//last place the player damaged a chest //last place the player damaged a chest
public Location lastChestDamageLocation = null; public Location lastChestDamageLocation = null;
//number of blocks placed outside claims before next warning
int unclaimedBlockPlacementsUntilWarning = 1;
//spam //spam
public Date lastLogin; //when the player last logged into the server public Date lastLogin; //when the player last logged into the server
public String lastMessage = ""; //the player's last chat message, or slash command complete with parameters public String lastMessage = ""; //the player's last chat message, or slash command complete with parameters

View File

@ -74,11 +74,17 @@ class PlayerEventHandler implements Listener
//when a player chats, monitor for spam //when a player chats, monitor for spam
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
void onPlayerChat (PlayerChatEvent event) void onPlayerChat (AsyncPlayerChatEvent event)
{ {
Player player = event.getPlayer(); Player player = event.getPlayer();
String message = event.getMessage(); String message = event.getMessage();
event.setCancelled(this.handlePlayerChat(player, message, event));
}
//returns true if the message should be sent, false if it should be muted
private boolean handlePlayerChat(Player player, String message, PlayerEvent event)
{
//FEATURE: automatically educate players about claiming land //FEATURE: automatically educate players about claiming land
//watching for message format how*claim*, and will send a link to the basics video //watching for message format how*claim*, and will send a link to the basics video
if(this.howToClaimPattern == null) if(this.howToClaimPattern == null)
@ -90,11 +96,11 @@ class PlayerEventHandler implements Listener
{ {
if(GriefPrevention.instance.creativeRulesApply(player.getLocation())) if(GriefPrevention.instance.creativeRulesApply(player.getLocation()))
{ {
GriefPrevention.sendMessage(player, TextMode.Info, Messages.CreativeBasicsDemoAdvertisement); GriefPrevention.sendMessage(player, TextMode.Info, Messages.CreativeBasicsDemoAdvertisement, 10L);
} }
else else
{ {
GriefPrevention.sendMessage(player, TextMode.Info, Messages.SurvivalBasicsDemoAdvertisement); GriefPrevention.sendMessage(player, TextMode.Info, Messages.SurvivalBasicsDemoAdvertisement, 10L);
} }
} }
@ -102,23 +108,26 @@ class PlayerEventHandler implements Listener
//check for "trapped" or "stuck" to educate players about the /trapped command //check for "trapped" or "stuck" to educate players about the /trapped command
if(message.contains("trapped") || message.contains("stuck") || message.contains(this.dataStore.getMessage(Messages.TrappedChatKeyword))) if(message.contains("trapped") || message.contains("stuck") || message.contains(this.dataStore.getMessage(Messages.TrappedChatKeyword)))
{ {
GriefPrevention.sendMessage(player, TextMode.Info, Messages.TrappedInstructions); GriefPrevention.sendMessage(player, TextMode.Info, Messages.TrappedInstructions, 10L);
} }
//FEATURE: monitor for chat and command spam //FEATURE: monitor for chat and command spam
if(!GriefPrevention.instance.config_spam_enabled) return; if(!GriefPrevention.instance.config_spam_enabled) return false;
//if the player has permission to spam, don't bother even examining the message //if the player has permission to spam, don't bother even examining the message
if(player.hasPermission("griefprevention.spam")) return; if(player.hasPermission("griefprevention.spam")) return false;
//remedy any CAPS SPAM without bothering to fault the player for it //remedy any CAPS SPAM without bothering to fault the player for it
if(message.length() > 4 && message.toUpperCase().equals(message)) if(message.length() > 4 && this.stringsAreSimilar(message.toUpperCase(), message))
{ {
event.setMessage(message.toLowerCase()); if(event instanceof AsyncPlayerChatEvent)
{
((AsyncPlayerChatEvent)event).setMessage(message.toLowerCase());
}
} }
//where spam is concerned, casing isn't significant //where other types of spam are concerned, casing isn't significant
message = message.toLowerCase(); message = message.toLowerCase();
PlayerData playerData = this.dataStore.getPlayerData(player.getName()); PlayerData playerData = this.dataStore.getPlayerData(player.getName());
@ -137,8 +146,8 @@ class PlayerEventHandler implements Listener
spam = true; spam = true;
} }
//if it's very similar to the last message and less than 15 seconds have passed //if it's very similar to the last message
if(!muted && this.stringsAreSimilar(message, playerData.lastMessage) && millisecondsSinceLastMessage < 15000) if(!muted && this.stringsAreSimilar(message, playerData.lastMessage))
{ {
playerData.spamCount++; playerData.spamCount++;
spam = true; spam = true;
@ -146,10 +155,10 @@ class PlayerEventHandler implements Listener
} }
//filter IP addresses //filter IP addresses
if(!muted && !(event instanceof PlayerCommandPreprocessEvent)) if(!muted)
{ {
Pattern ipAddressPattern = Pattern.compile("\\d{1,4}\\D{1,3}\\d{1,4}\\D{1,3}\\d{1,4}\\D{1,3}\\d{1,4}"); Pattern ipAddressPattern = Pattern.compile("\\d{1,4}\\D{1,3}\\d{1,4}\\D{1,3}\\d{1,4}\\D{1,3}\\d{1,4}");
Matcher matcher = ipAddressPattern.matcher(event.getMessage()); Matcher matcher = ipAddressPattern.matcher(message);
//if it looks like an IP address //if it looks like an IP address
while(matcher.find()) while(matcher.find())
@ -158,7 +167,7 @@ class PlayerEventHandler implements Listener
if(!GriefPrevention.instance.config_spam_allowedIpAddresses.contains(matcher.group())) if(!GriefPrevention.instance.config_spam_allowedIpAddresses.contains(matcher.group()))
{ {
//log entry //log entry
GriefPrevention.AddLogEntry("Muted IP address from " + player.getName() + ": " + event.getMessage()); GriefPrevention.AddLogEntry("Muted IP address from " + player.getName() + ": " + message);
//spam notation //spam notation
playerData.spamCount++; playerData.spamCount++;
@ -201,7 +210,6 @@ class PlayerEventHandler implements Listener
if(!muted && message.length() < 5 && millisecondsSinceLastMessage < 5000) if(!muted && message.length() < 5 && millisecondsSinceLastMessage < 5000)
{ {
spam = true; spam = true;
if(playerData.spamCount > 4) muted = true;
playerData.spamCount++; playerData.spamCount++;
} }
@ -235,7 +243,7 @@ class PlayerEventHandler implements Listener
muted = true; muted = true;
if(!playerData.spamWarned) if(!playerData.spamWarned)
{ {
GriefPrevention.sendMessage(player, TextMode.Warn, GriefPrevention.instance.config_spam_warningMessage); GriefPrevention.sendMessage(player, TextMode.Warn, GriefPrevention.instance.config_spam_warningMessage, 10L);
GriefPrevention.AddLogEntry("Warned " + player.getName() + " about spam penalties."); GriefPrevention.AddLogEntry("Warned " + player.getName() + " about spam penalties.");
playerData.spamWarned = true; playerData.spamWarned = true;
} }
@ -243,14 +251,15 @@ class PlayerEventHandler implements Listener
if(muted) if(muted)
{ {
//cancel the event and make a log entry //make a log entry
//cancelling the event guarantees players don't receive the message
event.setCancelled(true);
GriefPrevention.AddLogEntry("Muted spam from " + player.getName() + ": " + message); GriefPrevention.AddLogEntry("Muted spam from " + player.getName() + ": " + message);
//send a fake message so the player doesn't realize he's muted //send a fake message so the player doesn't realize he's muted
//less information for spammers = less effective spam filter dodging //less information for spammers = less effective spam filter dodging
player.sendMessage("<" + player.getName() + "> " + event.getMessage()); player.sendMessage("<" + player.getName() + "> " + message);
//cancelling the event guarantees other players don't receive the message
return true;
} }
} }
@ -263,7 +272,9 @@ class PlayerEventHandler implements Listener
//in any case, record the timestamp of this message and also its content for next time //in any case, record the timestamp of this message and also its content for next time
playerData.lastMessageTimestamp = new Date(); playerData.lastMessageTimestamp = new Date();
playerData.lastMessage = message; playerData.lastMessage = message;
return false;
} }
//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
@ -345,7 +356,10 @@ class PlayerEventHandler implements Listener
if(!GriefPrevention.instance.config_spam_enabled) return; if(!GriefPrevention.instance.config_spam_enabled) return;
//if the slash command used is in the list of monitored commands, treat it like a chat message (see above) //if the slash command used is in the list of monitored commands, treat it like a chat message (see above)
if(GriefPrevention.instance.config_spam_monitorSlashCommands.contains(args[0])) this.onPlayerChat(event); if(GriefPrevention.instance.config_spam_monitorSlashCommands.contains(args[0]))
{
event.setCancelled(this.handlePlayerChat(event.getPlayer(), event.getMessage(), event));
}
} }
//when a player attempts to join the server... //when a player attempts to join the server...
@ -878,9 +892,11 @@ class PlayerEventHandler implements Listener
//apply rules for containers and crafting blocks //apply rules for containers and crafting blocks
if( GriefPrevention.instance.config_claims_preventTheft && ( if( GriefPrevention.instance.config_claims_preventTheft && (
event.getAction() == Action.RIGHT_CLICK_BLOCK && ( event.getAction() == Action.RIGHT_CLICK_BLOCK && (
clickedBlock.getState() instanceof InventoryHolder || clickedBlock.getState() instanceof InventoryHolder ||
clickedBlockType == Material.BREWING_STAND ||
clickedBlockType == Material.WORKBENCH || clickedBlockType == Material.WORKBENCH ||
clickedBlockType == Material.ENDER_CHEST ||
clickedBlockType == Material.DISPENSER ||
clickedBlockType == Material.BREWING_STAND ||
clickedBlockType == Material.JUKEBOX || clickedBlockType == Material.JUKEBOX ||
clickedBlockType == Material.ENCHANTMENT_TABLE || clickedBlockType == Material.ENCHANTMENT_TABLE ||
GriefPrevention.instance.config_mods_containerTrustIds.contains(clickedBlock.getTypeId())))) GriefPrevention.instance.config_mods_containerTrustIds.contains(clickedBlock.getTypeId()))))
@ -925,7 +941,9 @@ class PlayerEventHandler implements Listener
} }
//otherwise apply rules for doors, if configured that way //otherwise apply rules for doors, if configured that way
else if(GriefPrevention.instance.config_claims_lockAllDoors && (clickedBlockType == Material.WOOD_DOOR || clickedBlockType == Material.WOODEN_DOOR || clickedBlockType == Material.TRAP_DOOR || clickedBlockType == Material.FENCE_GATE)) else if((GriefPrevention.instance.config_claims_lockWoodenDoors && clickedBlockType == Material.WOOD_DOOR) ||
(GriefPrevention.instance.config_claims_lockTrapDoors && clickedBlockType == Material.TRAP_DOOR) ||
(GriefPrevention.instance.config_claims_lockFenceGates && clickedBlockType == Material.FENCE_GATE))
{ {
Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), false, null); Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), false, null);
if(claim != null) if(claim != null)
@ -1346,7 +1364,7 @@ class PlayerEventHandler implements Listener
} }
//make sure player has enough blocks to make up the difference //make sure player has enough blocks to make up the difference
if(!playerData.claimResizing.isAdminClaim()) if(!playerData.claimResizing.isAdminClaim() && player.getName().equals(playerData.claimResizing.getOwnerName()))
{ {
int newArea = newWidth * newHeight; int newArea = newWidth * newHeight;
int blocksRemainingAfter = playerData.getRemainingClaimBlocks() + playerData.claimResizing.getArea() - newArea; int blocksRemainingAfter = playerData.getRemainingClaimBlocks() + playerData.claimResizing.getArea() - newArea;

View File

@ -0,0 +1,44 @@
/*
GriefPrevention Server Plugin for Minecraft
Copyright (C) 2011 Ryan Hamshire
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package me.ryanhamshire.GriefPrevention;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
//sends a message to a player
//used to send delayed messages, for example help text triggered by a player's chat
class SendPlayerMessageTask implements Runnable
{
private Player player;
private ChatColor color;
private String message;
public SendPlayerMessageTask(Player player, ChatColor color, String message)
{
this.player = player;
this.color = color;
this.message = message;
}
@Override
public void run()
{
GriefPrevention.sendMessage(this.player, this.color, this.message);
}
}