From 540746ad4b8287e8ea35bf4dbc5e6d348dc462eb Mon Sep 17 00:00:00 2001 From: Ryan Hamshire Date: Tue, 22 May 2012 20:31:42 -0700 Subject: [PATCH] 3.8 --- plugin.yml | 6 +- .../GriefPrevention/BlockEventHandler.java | 68 +++++---- .../ryanhamshire/GriefPrevention/Claim.java | 53 +++++++ .../GriefPrevention/DataStore.java | 26 +++- .../GriefPrevention/EntityEventHandler.java | 78 ++++++++-- .../GriefPrevention/GriefPrevention.java | 136 +++++++++++++++++- .../GriefPrevention/PlayerEventHandler.java | 136 ++++++++++++++---- 7 files changed, 415 insertions(+), 88 deletions(-) diff --git a/plugin.yml b/plugin.yml index fd328de..dd71b3d 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,7 +1,7 @@ name: GriefPrevention main: me.ryanhamshire.GriefPrevention.GriefPrevention softdepend: [Vault, Multiverse-Core] -version: 3.6 +version: 3.8 commands: abandonclaim: description: Deletes a claim. @@ -108,6 +108,7 @@ permissions: griefprevention.deleteclaims: true griefprevention.spam: true griefprevention.lava: true + griefprevention.eavesdrop: true griefprevention.restorenature: description: Grants permission to use /RestoreNature. default: op @@ -128,4 +129,7 @@ permissions: default: op griefprevention.lava: description: Grants permission to place lava near the surface and outside of claims. + default: op + griefprevention.eavesdrop: + description: Allows a player to see whispered chat messages (/tell). default: op \ No newline at end of file diff --git a/src/me/ryanhamshire/GriefPrevention/BlockEventHandler.java b/src/me/ryanhamshire/GriefPrevention/BlockEventHandler.java index 19ce469..d69830f 100644 --- a/src/me/ryanhamshire/GriefPrevention/BlockEventHandler.java +++ b/src/me/ryanhamshire/GriefPrevention/BlockEventHandler.java @@ -144,34 +144,27 @@ public class BlockEventHandler implements Listener { Player player = breakEvent.getPlayer(); Block block = breakEvent.getBlock(); + + //make sure the player is allowed to break at the location + String noBuildReason = GriefPrevention.instance.allowBreak(player, block.getLocation()); + if(noBuildReason != null) + { + GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason); + breakEvent.setCancelled(true); + return; + } + PlayerData playerData = this.dataStore.getPlayerData(player.getName()); Claim claim = this.dataStore.getClaimAt(block.getLocation(), true, playerData.lastClaim); //if there's a claim here if(claim != null) { - //cache the claim for later reference - playerData.lastClaim = claim; - - //check permissions - String noBuildReason = claim.allowBreak(player, block.getType()); - - //if permission to break and breaking UNDER the claim + //if breaking UNDER the claim if(block.getY() < claim.lesserBoundaryCorner.getBlockY()) { - if(noBuildReason == null) - { - //extend the claim downward beyond the breakage point - this.dataStore.extendClaim(claim, claim.getLesserBoundaryCorner().getBlockY() - GriefPrevention.instance.config_claims_claimsExtendIntoGroundDistance); - } - } - - //otherwise if not allowed to break blocks here, tell the player why - else if(noBuildReason != null) - { - breakEvent.setCancelled(true); - GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason); - return; + //extend the claim downward beyond the breakage point + this.dataStore.extendClaim(claim, claim.getLesserBoundaryCorner().getBlockY() - GriefPrevention.instance.config_claims_claimsExtendIntoGroundDistance); } } @@ -210,30 +203,26 @@ public class BlockEventHandler implements Listener } } + //make sure the player is allowed to build at the location + String noBuildReason = GriefPrevention.instance.allowBuild(player, block.getLocation()); + if(noBuildReason != null) + { + GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason); + placeEvent.setCancelled(true); + return; + } + //if the block is being placed within an existing claim PlayerData playerData = this.dataStore.getPlayerData(player.getName()); Claim claim = this.dataStore.getClaimAt(block.getLocation(), true, playerData.lastClaim); if(claim != null) { - playerData.lastClaim = claim; - String noBuildReason = claim.allowBuild(player); - //if the player has permission for the claim and he's placing UNDER the claim if(block.getY() < claim.lesserBoundaryCorner.getBlockY()) { - if(noBuildReason == null) - { - //extend the claim downward - this.dataStore.extendClaim(claim, claim.getLesserBoundaryCorner().getBlockY() - GriefPrevention.instance.config_claims_claimsExtendIntoGroundDistance); - } + //extend the claim downward + this.dataStore.extendClaim(claim, claim.getLesserBoundaryCorner().getBlockY() - GriefPrevention.instance.config_claims_claimsExtendIntoGroundDistance); } - - //otherwise if he doesn't have permission, tell him why - else if(noBuildReason != null) - { - placeEvent.setCancelled(true); - GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason); - } } //FEATURE: automatically create a claim when a player who has no claims places a chest @@ -304,7 +293,7 @@ public class BlockEventHandler implements Listener //blocks "pushing" other players' blocks around (pistons) @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onBlockPistonExtend (BlockPistonExtendEvent event) + public void onBlockPistonExtend (BlockPistonExtendEvent event) { //who owns the piston, if anyone? String pistonClaimOwnerName = "_"; @@ -390,6 +379,13 @@ public class BlockEventHandler implements Listener Block toBlock = spreadEvent.getToBlock(); Claim toClaim = this.dataStore.getClaimAt(toBlock.getLocation(), false, null); + //if in a creative world, block any spread into the wilderness + if(GriefPrevention.instance.creativeRulesApply(toBlock.getLocation()) && toClaim == null) + { + spreadEvent.setCancelled(true); + return; + } + //if spreading into a claim if(toClaim != null) { diff --git a/src/me/ryanhamshire/GriefPrevention/Claim.java b/src/me/ryanhamshire/GriefPrevention/Claim.java index 7ee52ce..4f9edd7 100644 --- a/src/me/ryanhamshire/GriefPrevention/Claim.java +++ b/src/me/ryanhamshire/GriefPrevention/Claim.java @@ -26,6 +26,7 @@ import java.util.Iterator; import java.util.Map; import org.bukkit.*; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; //represents a player claim @@ -182,6 +183,9 @@ public class Claim //all of these return NULL when a player has permission, or a String error message when the player doesn't have permission public String allowEdit(Player player) { + //if we don't know who's asking, always say no (i've been told some mods can make this happen somehow) + if(player == null) return ""; + //special cases... //admin claims need adminclaims permission only. @@ -219,6 +223,9 @@ public class Claim //build permission check public String allowBuild(Player player) { + //if we don't know who's asking, always say no (i've been told some mods can make this happen somehow) + if(player == null) return ""; + //when a player tries to build in a claim, if he's under siege, the siege may extend to include the new claim GriefPrevention.instance.dataStore.tryExtendSiege(player, this); @@ -263,6 +270,9 @@ public class Claim //break permission check public String allowBreak(Player player, Material material) { + //if we don't know who's asking, always say no (i've been told some mods can make this happen somehow) + if(player == null) return ""; + //if under siege, some blocks will be breakable if(this.siegeData != null) { @@ -301,6 +311,9 @@ public class Claim //access permission check public String allowAccess(Player player) { + //if we don't know who's asking, always say no (i've been told some mods can make this happen somehow) + if(player == null) return ""; + //everyone always has access to admin claims if(this.isAdminClaim()) return null; @@ -329,6 +342,9 @@ public class Claim //inventory permission check public String allowContainers(Player player) { + //if we don't know who's asking, always say no (i've been told some mods can make this happen somehow) + if(player == null) return ""; + //trying to access inventory in a claim may extend an existing siege to include this claim GriefPrevention.instance.dataStore.tryExtendSiege(player, this); @@ -363,6 +379,9 @@ public class Claim //grant permission check, relatively simple public String allowGrantPermission(Player player) { + //if we don't know who's asking, always say no (i've been told some mods can make this happen somehow) + if(player == null) return ""; + //anyone who can modify the claim, or who's explicitly in the managers (/PermissionTrust) list can do this if(this.allowEdit(player) == null || this.managers.contains(player.getName())) return null; @@ -543,6 +562,40 @@ public class Claim return false; } + //whether more entities may be added to a claim + public String allowMoreEntities() + { + if(this.parent != null) return this.parent.allowMoreEntities(); + + //this rule only applies to creative mode worlds + if(!GriefPrevention.instance.creativeRulesApply(this.getLesserBoundaryCorner())) return null; + + //determine maximum allowable entity count, based on claim size + int maxEntities = this.getArea() / 50; + if(maxEntities == 0) return "This claim isn't big enough for that. Try enlarging it."; + + //count current entities (ignoring players) + Chunk lesserChunk = this.getLesserBoundaryCorner().getChunk(); + Chunk greaterChunk = this.getGreaterBoundaryCorner().getChunk(); + + int totalEntities = 0; + for(int x = lesserChunk.getX(); x <= greaterChunk.getX(); x++) + for(int z = lesserChunk.getZ(); z <= greaterChunk.getZ(); z++) + { + Chunk chunk = lesserChunk.getWorld().getChunkAt(x, z); + Entity [] entities = chunk.getEntities(); + for(int i = 0; i < entities.length; i++) + { + Entity entity = entities[i]; + if(!(entity instanceof Player) && this.contains(entity.getLocation(), false, false)) totalEntities++; + } + } + + if(totalEntities > maxEntities) return "This claim has too many entities already. Try enlarging the claim or removing some animals, monsters, or minecarts."; + + return null; + } + //implements a strict ordering of claims, used to keep the claims collection sorted for faster searching boolean greaterThan(Claim otherClaim) { diff --git a/src/me/ryanhamshire/GriefPrevention/DataStore.java b/src/me/ryanhamshire/GriefPrevention/DataStore.java index 8ee5cb2..6111c80 100644 --- a/src/me/ryanhamshire/GriefPrevention/DataStore.java +++ b/src/me/ryanhamshire/GriefPrevention/DataStore.java @@ -140,6 +140,9 @@ public class DataStore else { Claim subdivision = new Claim(lesserBoundaryCorner, greaterBoundaryCorner, "--subdivision--", builderNames, containerNames, accessorNames, managerNames); + + //make sure there are no other subdivisions overlapping this one + subdivision.modifiedDate = new Date(files[i].lastModified()); subdivision.parent = topLevelClaim; topLevelClaim.children.add(subdivision); @@ -509,8 +512,10 @@ public class DataStore //convert that to a number and store it playerData.bonusClaimBlocks = Integer.parseInt(bonusBlocksString); - //fourth line is a double-semicolon-delimited list of claims - String claimsString = inStream.readLine(); + //fourth line is a double-semicolon-delimited list of claims, which is currently ignored + //String claimsString = inStream.readLine(); + inStream.readLine(); + /* if(claimsString != null && claimsString.length() > 0) { String [] claimsStrings = claimsString.split(";;"); @@ -522,7 +527,7 @@ public class DataStore String claimID = claimsStrings[i]; if(claimID != null) { - Claim claim = this.getClaimAt(this.locationFromString(claimID), true /*ignore height*/, null); + Claim claim = this.getClaimAt(this.locationFromString(claimID), true, null); //if the referenced claim exists, add it to the player data instance for later reference if(claim != null) @@ -544,6 +549,17 @@ public class DataStore this.savePlayerData(playerName, playerData); } } + */ + + //find all the claims belonging to this player and note them for future reference + for(int i = 0; i < this.claims.size(); i++) + { + Claim claim = this.claims.get(i); + if(claim.ownerName.equals(playerName)) + { + playerData.claims.add(claim); + } + } inStream.close(); } @@ -1044,14 +1060,14 @@ public class DataStore } //deletes all claims owned by a player - public void deleteClaimsForPlayer(String playerName) + public void deleteClaimsForPlayer(String playerName, boolean deleteCreativeClaims) { //make a list of the player's claims ArrayList claimsToDelete = new ArrayList(); for(int i = 0; i < this.claims.size(); i++) { Claim claim = this.claims.get(i); - if(claim.ownerName.equals(playerName)) + if(claim.ownerName.equals(playerName) && (deleteCreativeClaims || !GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()))) claimsToDelete.add(claim); } diff --git a/src/me/ryanhamshire/GriefPrevention/EntityEventHandler.java b/src/me/ryanhamshire/GriefPrevention/EntityEventHandler.java index e9858da..0239777 100644 --- a/src/me/ryanhamshire/GriefPrevention/EntityEventHandler.java +++ b/src/me/ryanhamshire/GriefPrevention/EntityEventHandler.java @@ -35,11 +35,14 @@ import org.bukkit.entity.Vehicle; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; import org.bukkit.event.entity.EntityChangeBlockEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.ItemSpawnEvent; import org.bukkit.event.painting.PaintingBreakByEntityEvent; import org.bukkit.event.painting.PaintingBreakEvent; import org.bukkit.event.painting.PaintingPlaceEvent; @@ -73,6 +76,12 @@ class EntityEventHandler implements Listener } } + //special rule for creative worlds: explosions don't destroy anything + if(GriefPrevention.instance.creativeRulesApply(entity.getLocation())) + { + blocks.clear(); + } + //FEATURE: creating an explosion near a claim doesn't damage any of the claimed blocks Claim claim = null; @@ -96,14 +105,59 @@ class EntityEventHandler implements Listener } } + //when an item spawns... + @EventHandler + public void onItemSpawn(ItemSpawnEvent event) + { + //if in a creative world, cancel the event (don't drop items on the ground) + if(GriefPrevention.instance.creativeRulesApply(event.getLocation())) + { + event.setCancelled(true); + } + } + + //when a creature spawns... + @EventHandler + public void onEntitySpawn(CreatureSpawnEvent event) + { + LivingEntity entity = event.getEntity(); + + //these rules apply only to creative worlds + if(!GriefPrevention.instance.creativeRulesApply(entity.getLocation())) return; + + //chicken eggs and breeding could potentially make a mess in the wilderness, once griefers get involved + SpawnReason reason = event.getSpawnReason(); + if(reason == SpawnReason.EGG || reason == SpawnReason.BREEDING) + { + event.setCancelled(true); + return; + } + + //otherwise, just apply the limit on total entities per claim + Claim claim = this.dataStore.getClaimAt(event.getLocation(), false, null); + if(claim != null && claim.allowMoreEntities() != null) + { + event.setCancelled(true); + return; + } + } + //when an entity dies... @EventHandler public void onEntityDeath(EntityDeathEvent event) { + LivingEntity entity = event.getEntity(); + + //special rule for creative worlds: killed entities don't drop items or experience orbs + if(GriefPrevention.instance.creativeRulesApply(entity.getLocation())) + { + event.setDroppedExp(0); + event.getDrops().clear(); + } + //FEATURE: when a player is involved in a siege (attacker or defender role) //his death will end the siege - LivingEntity entity = event.getEntity(); if(!(entity instanceof Player)) return; //only tracking players Player player = (Player)entity; @@ -153,10 +207,6 @@ class EntityEventHandler implements Listener PaintingBreakByEntityEvent entityEvent = (PaintingBreakByEntityEvent)event; - //which claim is the painting in? - Claim claim = this.dataStore.getClaimAt(event.getPainting().getLocation(), false, null); - if(claim == null) return; - //who is removing it? Entity remover = entityEvent.getRemover(); @@ -166,15 +216,19 @@ class EntityEventHandler implements Listener event.setCancelled(true); return; } + + //make sure the player has build permission here + Claim claim = this.dataStore.getClaimAt(event.getPainting().getLocation(), false, null); + if(claim == null) return; //if the player doesn't have build permission, don't allow the breakage Player playerRemover = (Player)entityEvent.getRemover(); - String noBuildReason = claim.allowBuild(playerRemover); + String noBuildReason = GriefPrevention.instance.allowBuild(playerRemover, event.getPainting().getLocation()); if(noBuildReason != null) { event.setCancelled(true); GriefPrevention.sendMessage(playerRemover, TextMode.Err, noBuildReason); - } + } } //when a painting is placed... @@ -183,17 +237,13 @@ class EntityEventHandler implements Listener { //FEATURE: similar to above, placing a painting requires build permission in the claim - //which claim is the painting in? - Claim claim = this.dataStore.getClaimAt(event.getBlock().getLocation(), false, null); - if(claim == null) return; - - //if the player doesn't have permission, don't allow the placement - String noBuildReason = claim.allowBuild(event.getPlayer()); + //if the player doesn't have permission, don't allow the placement + String noBuildReason = GriefPrevention.instance.allowBuild(event.getPlayer(), event.getPainting().getLocation()); if(noBuildReason != null) { event.setCancelled(true); GriefPrevention.sendMessage(event.getPlayer(), TextMode.Err, noBuildReason); - } + } } //when an entity is damaged diff --git a/src/me/ryanhamshire/GriefPrevention/GriefPrevention.java b/src/me/ryanhamshire/GriefPrevention/GriefPrevention.java index 20240b8..05e24cc 100644 --- a/src/me/ryanhamshire/GriefPrevention/GriefPrevention.java +++ b/src/me/ryanhamshire/GriefPrevention/GriefPrevention.java @@ -30,6 +30,7 @@ import net.milkbowl.vault.economy.Economy; import org.bukkit.ChatColor; import org.bukkit.Chunk; +import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.OfflinePlayer; @@ -59,6 +60,7 @@ public class GriefPrevention extends JavaPlugin //configuration variables, loaded/saved from a config.yml public ArrayList config_claims_enabledWorlds; //list of worlds where players can create GriefPrevention claims + public ArrayList config_claims_enabledCreativeWorlds; //list of worlds where additional creative mode anti-grief rules apply public boolean config_claims_preventTheft; //whether containers and crafting blocks are protectable public boolean config_claims_preventButtonsSwitches; //whether buttons and switches are protectable @@ -103,6 +105,7 @@ public class GriefPrevention extends JavaPlugin public boolean config_fireDestroys; //whether fire destroys blocks outside of claims public boolean config_addItemsToClaimedChests; //whether players may add items to claimed chests by left-clicking them + public boolean config_eavesdrop; //whether whispered messages will be visible to administrators //reference to the economy plugin, if economy integration is enabled public static Economy economy = null; @@ -159,6 +162,41 @@ public class GriefPrevention extends JavaPlugin } } + //default creative claim world names + List defaultCreativeWorldNames = new ArrayList(); + + //if default game mode for the server is creative, creative rules will apply to all worlds unless the config specifies otherwise + if(this.getServer().getDefaultGameMode() == GameMode.CREATIVE) + { + for(int i = 0; i < defaultClaimsWorldNames.size(); i++) + { + defaultCreativeWorldNames.add(defaultClaimsWorldNames.get(i)); + } + } + + //get creative world names from the config file + List creativeClaimsEnabledWorldNames = config.getStringList("GriefPrevention.Claims.CreativeRulesWorlds"); + if(creativeClaimsEnabledWorldNames == null || creativeClaimsEnabledWorldNames.size() == 0) + { + creativeClaimsEnabledWorldNames = defaultCreativeWorldNames; + } + + //validate that list + this.config_claims_enabledCreativeWorlds = new ArrayList(); + for(int i = 0; i < creativeClaimsEnabledWorldNames.size(); i++) + { + String worldName = creativeClaimsEnabledWorldNames.get(i); + World world = this.getServer().getWorld(worldName); + if(world == null) + { + AddLogEntry("Error: Claims Configuration: There's no world named \"" + worldName + "\". Please update your config.yml."); + } + else + { + this.config_claims_enabledCreativeWorlds.add(world); + } + } + this.config_claims_preventTheft = config.getBoolean("GriefPrevention.Claims.PreventTheft", true); this.config_claims_preventButtonsSwitches = config.getBoolean("GriefPrevention.Claims.PreventButtonsSwitches", true); this.config_claims_initialBlocks = config.getInt("GriefPrevention.Claims.InitialBlocks", 100); @@ -196,8 +234,9 @@ public class GriefPrevention extends JavaPlugin this.config_fireDestroys = config.getBoolean("GriefPrevention.FireDestroys", false); this.config_addItemsToClaimedChests = config.getBoolean("GriefPrevention.AddItemsToClaimedChests", true); + this.config_eavesdrop = config.getBoolean("GriefPrevention.EavesdropEnabled", false); - //default for claims worlds list + //default for siege worlds list ArrayList defaultSiegeWorldNames = new ArrayList(); //get siege world names from the config file @@ -270,6 +309,7 @@ public class GriefPrevention extends JavaPlugin } config.set("GriefPrevention.Claims.Worlds", claimsEnabledWorldNames); + config.set("GriefPrevention.Claims.CreativeRulesWorlds", creativeClaimsEnabledWorldNames); config.set("GriefPrevention.Claims.PreventTheft", this.config_claims_preventTheft); config.set("GriefPrevention.Claims.PreventButtonsSwitches", this.config_claims_preventButtonsSwitches); config.set("GriefPrevention.Claims.InitialBlocks", this.config_claims_initialBlocks); @@ -307,6 +347,7 @@ public class GriefPrevention extends JavaPlugin config.set("GriefPrevention.FireDestroys", this.config_fireDestroys); config.set("GriefPrevention.AddItemsToClaimedChests", this.config_addItemsToClaimedChests); + config.set("GriefPrevention.EavesdropEnabled", this.config_eavesdrop); config.set("GriefPrevention.Siege.Worlds", siegeEnabledWorldNames); config.set("GriefPrevention.Siege.BreakableBlocks", breakableBlocksList); @@ -403,7 +444,7 @@ public class GriefPrevention extends JavaPlugin //abandonclaim if(cmd.getName().equalsIgnoreCase("abandonclaim") && player != null) { - this.abandonClaimHandler(player, false); + return this.abandonClaimHandler(player, false); } //abandontoplevelclaim @@ -435,6 +476,14 @@ public class GriefPrevention extends JavaPlugin //abandonallclaims else if(cmd.getName().equalsIgnoreCase("abandonallclaims") && player != null) { + if(args.length != 0) return false; + + if(creativeRulesApply(player.getLocation())) + { + GriefPrevention.sendMessage(player, TextMode.Err, "Creative mode claims can't be abandoned."); + return true; + } + //count claims PlayerData playerData = this.dataStore.getPlayerData(player.getName()); int originalClaimCount = playerData.claims.size(); @@ -447,11 +496,11 @@ public class GriefPrevention extends JavaPlugin } //delete them - this.dataStore.deleteClaimsForPlayer(player.getName()); + this.dataStore.deleteClaimsForPlayer(player.getName(), false); //inform the player int remainingBlocks = playerData.getRemainingClaimBlocks(); - GriefPrevention.sendMessage(player, TextMode.Success, originalClaimCount + " claims abandoned. You now have " + String.valueOf(remainingBlocks) + " available claim blocks."); + GriefPrevention.sendMessage(player, TextMode.Success, "Claims abandoned. You now have " + String.valueOf(remainingBlocks) + " available claim blocks."); //revert any current visualization Visualization.Revert(player); @@ -960,7 +1009,7 @@ public class GriefPrevention extends JavaPlugin } //delete all that player's claims - this.dataStore.deleteClaimsForPlayer(otherPlayer.getName()); + this.dataStore.deleteClaimsForPlayer(otherPlayer.getName(), true); GriefPrevention.sendMessage(player, TextMode.Success, "Deleted all of " + otherPlayer.getName() + "'s claims."); @@ -980,7 +1029,7 @@ public class GriefPrevention extends JavaPlugin } //delete all admin claims - this.dataStore.deleteClaimsForPlayer(""); //empty string for owner name indicates an administrative claim + this.dataStore.deleteClaimsForPlayer("", true); //empty string for owner name indicates an administrative claim GriefPrevention.sendMessage(player, TextMode.Success, "Deleted all administrative claims."); @@ -1194,7 +1243,12 @@ public class GriefPrevention extends JavaPlugin if(claim == null) { GriefPrevention.sendMessage(player, TextMode.Instr, "Stand in the claim you want to delete, or consider /AbandonAllClaims."); - } + } + + else if(this.creativeRulesApply(player.getLocation())) + { + GriefPrevention.sendMessage(player, TextMode.Err, "Creative-mode claims can't be abandoned."); + } //verify ownership else if(claim.allowEdit(player) != null) @@ -1673,4 +1727,72 @@ public class GriefPrevention extends JavaPlugin { player.sendMessage(color + message); } + + //determines whether creative anti-grief rules apply at a location + boolean creativeRulesApply(Location location) + { + return this.config_claims_enabledCreativeWorlds.contains(location.getWorld()); + } + + public String allowBuild(Player player, Location location) + { + PlayerData playerData = this.dataStore.getPlayerData(player.getName()); + Claim claim = this.dataStore.getClaimAt(location, false, playerData.lastClaim); + + //wilderness rules + if(claim == null) + { + //no building in the wilderness in creative mode + if(this.creativeRulesApply(location)) + { + //exception: administrators in ignore claims mode + if(playerData.ignoreClaims) return null; + + return "You can't build here. Use the golden shovel to claim some land first."; + } + + //but it's fine in survival mode + else + { + //cache the claim for later reference + playerData.lastClaim = claim; + + return null; + } + } + + //if not in the wilderness, then apply claim rules (permissions, etc) + return claim.allowBuild(player); + } + + public String allowBreak(Player player, Location location) + { + PlayerData playerData = this.dataStore.getPlayerData(player.getName()); + Claim claim = this.dataStore.getClaimAt(location, false, playerData.lastClaim); + + //wilderness rules + if(claim == null) + { + //no building in the wilderness in creative mode + if(this.creativeRulesApply(location)) + { + //exception: administrators in ignore claims mode + if(playerData.ignoreClaims) return null; + + return "You can't build here. Use the golden shovel to claim some land first."; + } + + //but it's fine in survival mode + else + { + //cache the claim for later reference + playerData.lastClaim = claim; + + return null; + } + } + + //if not in the wilderness, then apply claim rules (permissions, etc) + return claim.allowBreak(player, location.getBlock().getType()); + } } \ No newline at end of file diff --git a/src/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java b/src/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java index e442da5..64a04b8 100644 --- a/src/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java +++ b/src/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.bukkit.ChatColor; import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; @@ -111,7 +112,7 @@ class PlayerEventHandler implements Listener long millisecondsSinceLastMessage = (new Date()).getTime() - playerData.lastMessageTimestamp.getTime(); //if the message came too close to the last one - if(millisecondsSinceLastMessage < 3000) + if(millisecondsSinceLastMessage < 2000) { //increment the spam counter playerData.spamCount++; @@ -122,6 +123,7 @@ class PlayerEventHandler implements Listener if(message.equals(playerData.lastMessage)) { playerData.spamCount++; + event.setCancelled(true); spam = true; } @@ -164,7 +166,11 @@ class PlayerEventHandler implements Listener //kick player.kickPlayer(GriefPrevention.instance.config_spam_banMessage); - } + } + else + { + player.kickPlayer(""); + } } //cancel any messages while at or above the third spam level and issue warnings @@ -197,6 +203,31 @@ class PlayerEventHandler implements Listener //if the slash command used is in the list of monitored commands, treat it like a chat message (see above) String [] args = event.getMessage().split(" "); if(GriefPrevention.instance.config_spam_monitorSlashCommands.contains(args[0])) this.onPlayerChat(event); + + if(GriefPrevention.instance.config_eavesdrop && args[0].equalsIgnoreCase("/tell") && !event.getPlayer().hasPermission("griefprevention.eavesdrop") && args.length > 2) + { + StringBuilder logMessageBuilder = new StringBuilder(); + logMessageBuilder.append("[").append(event.getPlayer().getName()).append(" > ").append(args[1]).append("] "); + + for(int i = 2; i < args.length; i++) + { + logMessageBuilder.append(args[i]).append(" "); + } + + String logMessage = logMessageBuilder.toString(); + + GriefPrevention.AddLogEntry(logMessage.toString()); + + Player [] players = GriefPrevention.instance.getServer().getOnlinePlayers(); + for(int i = 0; i < players.length; i++) + { + Player player = players[i]; + if(player.hasPermission("griefprevention.eavesdrop") && !player.getName().equalsIgnoreCase(args[1])) + { + player.sendMessage(ChatColor.GRAY + logMessage); + } + } + } } //when a player attempts to join the server... @@ -300,6 +331,14 @@ class PlayerEventHandler implements Listener public void onPlayerDropItem(PlayerDropItemEvent event) { Player player = event.getPlayer(); + + //in creative worlds, dropping items is blocked + if(GriefPrevention.instance.creativeRulesApply(player.getLocation())) + { + event.setCancelled(true); + return; + } + PlayerData playerData = this.dataStore.getPlayerData(player.getName()); //FEATURE: players under siege or in PvP combat, can't throw items on the ground to hide @@ -494,18 +533,19 @@ class PlayerEventHandler implements Listener Block block = bucketEvent.getBlockClicked().getRelative(bucketEvent.getBlockFace()); int minLavaDistance = 10; + //make sure the player is allowed to build at the location + String noBuildReason = GriefPrevention.instance.allowBuild(player, block.getLocation()); + if(noBuildReason != null) + { + GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason); + bucketEvent.setCancelled(true); + return; + } + //if the bucket is being used in a claim Claim claim = this.dataStore.getClaimAt(block.getLocation(), false, null); if(claim != null) { - //the player must have build permission to use it - if(claim.allowBuild(player) != null) - { - bucketEvent.setCancelled(true); - GriefPrevention.sendMessage(player, TextMode.Err, "You don't have " + claim.getOwnerName() + "'s permission to use your bucket here."); - return; - } - //the claim must be at least an hour old long now = Calendar.getInstance().getTimeInMillis(); long lastModified = claim.modifiedDate.getTime(); @@ -526,6 +566,7 @@ class PlayerEventHandler implements Listener { GriefPrevention.sendMessage(player, TextMode.Err, "You may only dump lava inside your claim(s) or underground."); bucketEvent.setCancelled(true); + return; } } @@ -557,15 +598,14 @@ class PlayerEventHandler implements Listener Player player = bucketEvent.getPlayer(); Block block = bucketEvent.getBlockClicked(); - Claim claim = this.dataStore.getClaimAt(block.getLocation(), false, null); - if(claim != null) + //make sure the player is allowed to build at the location + String noBuildReason = GriefPrevention.instance.allowBuild(player, block.getLocation()); + if(noBuildReason != null) { - if(claim.allowBuild(player) != null) - { - bucketEvent.setCancelled(true); - GriefPrevention.sendMessage(player, TextMode.Err, "You don't have permission to use your bucket here."); - } - } + GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason); + bucketEvent.setCancelled(true); + return; + } } //when a player interacts with the world @@ -681,14 +721,39 @@ class PlayerEventHandler implements Listener //if it's bonemeal, check for build permission (ink sac == bone meal, must be a Bukkit bug?) if(materialInHand == Material.INK_SACK) { - Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), false, null); + String noBuildReason = GriefPrevention.instance.allowBuild(player, clickedBlock.getLocation()); + if(noBuildReason != null) + { + GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason); + event.setCancelled(true); + } + + return; + } + + //if it's a spawn egg or minecart and this is a creative world, apply special rules + else if((materialInHand == Material.MONSTER_EGG || materialInHand == Material.MINECART || materialInHand == Material.POWERED_MINECART || materialInHand == Material.STORAGE_MINECART) && GriefPrevention.instance.creativeRulesApply(clickedBlock.getLocation())) + { + //player needs build permission at this location + String noBuildReason = GriefPrevention.instance.allowBuild(player, clickedBlock.getLocation()); + if(noBuildReason != null) + { + GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason); + event.setCancelled(true); + return; + } + + //enforce limit on total number of entities in this claim + PlayerData playerData = this.dataStore.getPlayerData(player.getName()); + Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), false, playerData.lastClaim); if(claim == null) return; - String noBuildReason = claim.allowBuild(player); - if(claim != null && noBuildReason != null) + String noEntitiesReason = claim.allowMoreEntities(); + if(noEntitiesReason != null) { - player.sendMessage(noBuildReason); - event.setCancelled(true); + GriefPrevention.sendMessage(player, TextMode.Err, noEntitiesReason); + event.setCancelled(true); + return; } return; @@ -885,7 +950,7 @@ class PlayerEventHandler implements Listener { int newArea = newWidth * newHeight; int blocksRemainingAfter = playerData.getRemainingClaimBlocks() + playerData.claimResizing.getArea() - newArea; - + if(blocksRemainingAfter < 0) { GriefPrevention.sendMessage(player, TextMode.Err, "You don't have enough blocks for this size. You need " + Math.abs(blocksRemainingAfter) + " more."); @@ -894,7 +959,28 @@ class PlayerEventHandler implements Listener } } - //ask the datastore to try and resize the claim + //in creative mode, top-level claims can't be moved or resized smaller. + //to check this, verifying the old claim's corners are inside the new claim's boundaries. + if(!player.hasPermission("griefprevention.deleteclaims") && GriefPrevention.instance.creativeRulesApply(player.getLocation()) && playerData.claimResizing.parent == null) + { + Claim oldClaim = playerData.claimResizing; + + //temporary claim instance, just for checking contains() + Claim newClaim = new Claim( + new Location(oldClaim.getLesserBoundaryCorner().getWorld(), newx1, newy1, newz1), + new Location(oldClaim.getLesserBoundaryCorner().getWorld(), newx2, newy2, newz2), + "", new String[]{}, new String[]{}, new String[]{}, new String[]{}); + + //both greater and lesser boundary corners of the old claim must be inside the new claim + if(!newClaim.contains(oldClaim.getLesserBoundaryCorner(), true, false) || !newClaim.contains(oldClaim.getGreaterBoundaryCorner(), true, false)) + { + //otherwise, show an error message and stop here + GriefPrevention.sendMessage(player, TextMode.Err, "You can't un-claim creative mode land. You can only make this claim larger or create additional claims."); + return; + } + } + + //ask the datastore to try and resize the claim, this checks for conflicts with other claims CreateClaimResult result = GriefPrevention.instance.dataStore.resizeClaim(playerData.claimResizing, newx1, newx2, newy1, newy2, newz1, newz2); if(result.succeeded)