From 6632af96df4735109db1e8170d1441f755a6fc3e Mon Sep 17 00:00:00 2001 From: Ryan Hamshire Date: Sat, 6 Oct 2012 10:52:17 -0700 Subject: [PATCH] 6.7 --- plugin.yml | 6 +- .../GriefPrevention/BlockEventHandler.java | 53 ++++++- .../CleanupUnusedClaimsTask.java | 5 +- .../GriefPrevention/DataStore.java | 5 +- .../DeliverClaimBlocksTask.java | 7 +- .../GriefPrevention/EntityEventHandler.java | 8 +- .../GriefPrevention/GriefPrevention.java | 140 ++++++++++++++++-- .../GriefPrevention/MaterialCollection.java | 56 +++++++ .../GriefPrevention/MaterialInfo.java | 75 ++++++++++ .../GriefPrevention/Messages.java | 2 +- .../GriefPrevention/PlayerEventHandler.java | 25 +++- .../RestoreNatureProcessingTask.java | 48 ++++-- 12 files changed, 388 insertions(+), 42 deletions(-) create mode 100644 src/me/ryanhamshire/GriefPrevention/MaterialCollection.java create mode 100644 src/me/ryanhamshire/GriefPrevention/MaterialInfo.java diff --git a/plugin.yml b/plugin.yml index 8bc20dd..835e070 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,8 +1,8 @@ name: GriefPrevention main: me.ryanhamshire.GriefPrevention.GriefPrevention -softdepend: [Vault, Multiverse-Core, My Worlds] +softdepend: [Vault, Multiverse-Core, My Worlds, MystCraft, Transporter] dev-url: http://dev.bukkit.org/server-mods/grief-prevention -version: 6.4 +version: 6.7 commands: abandonclaim: description: Deletes a claim. @@ -19,7 +19,7 @@ commands: trust: description: Grants a player full access to your claim(s). usage: /Trust Graants a player permission to build. See also /UnTrust, /ContainerTrust, /AccessTrust, and /PermissionTrust. - aliases: t + aliases: tr permission: griefprevention.claims untrust: description: Revokes a player's access to your claim(s). diff --git a/src/me/ryanhamshire/GriefPrevention/BlockEventHandler.java b/src/me/ryanhamshire/GriefPrevention/BlockEventHandler.java index 7dacfa1..bab6be2 100644 --- a/src/me/ryanhamshire/GriefPrevention/BlockEventHandler.java +++ b/src/me/ryanhamshire/GriefPrevention/BlockEventHandler.java @@ -25,6 +25,7 @@ import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.OfflinePlayer; +import org.bukkit.World.Environment; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; @@ -261,6 +262,12 @@ public class BlockEventHandler implements Listener Claim claim = this.dataStore.getClaimAt(block.getLocation(), true, playerData.lastClaim); if(claim != null) { + //warn about TNT not destroying claimed blocks + if(block.getType() == Material.TNT) + { + GriefPrevention.sendMessage(player, TextMode.Warn, Messages.NoTNTDamageClaims); + } + //if the player has permission for the claim and he's placing UNDER the claim if(block.getY() < claim.lesserBoundaryCorner.getBlockY() && claim.allowBuild(player) == null) { @@ -338,7 +345,7 @@ public class BlockEventHandler implements Listener } //FEATURE: limit wilderness tree planting to grass, or dirt with more blocks beneath it - else if(block.getType() == Material.SAPLING && GriefPrevention.instance.config_blockSkyTrees) + else if(block.getType() == Material.SAPLING && GriefPrevention.instance.config_blockSkyTrees && GriefPrevention.instance.claimsEnabledForWorld(player.getWorld())) { Block earthBlock = placeEvent.getBlockAgainst(); if(earthBlock.getType() != Material.GRASS) @@ -366,6 +373,14 @@ public class BlockEventHandler implements Listener } } } + + //warn players when they place TNT above sea level, since it doesn't destroy blocks there + if( GriefPrevention.instance.config_blockSurfaceOtherExplosions && block.getType() == Material.TNT && + block.getWorld().getEnvironment() != Environment.NETHER && + block.getY() > block.getWorld().getSeaLevel() - 5) + { + GriefPrevention.sendMessage(player, TextMode.Warn, Messages.NoTNTDamageAboveSeaLevel); + } } //blocks "pushing" other players' blocks around (pistons) @@ -499,14 +514,40 @@ public class BlockEventHandler implements Listener @EventHandler(priority = EventPriority.LOWEST) public void onBlockIgnite (BlockIgniteEvent igniteEvent) { - if(igniteEvent.getCause() != IgniteCause.FLINT_AND_STEEL && !GriefPrevention.instance.config_fireSpreads) igniteEvent.setCancelled(true); + if(!GriefPrevention.instance.config_fireSpreads && + igniteEvent.getBlock().getWorld().getEnvironment() == Environment.NORMAL && + igniteEvent.getCause() != IgniteCause.FLINT_AND_STEEL && + igniteEvent.getCause() != IgniteCause.LIGHTNING && + igniteEvent.getCause() != IgniteCause.LAVA) + { + igniteEvent.setCancelled(true); + } } //fire doesn't spread unless configured to, but other blocks still do (mushrooms and vines, for example) @EventHandler(priority = EventPriority.LOWEST) public void onBlockSpread (BlockSpreadEvent spreadEvent) { - if(spreadEvent.getSource().getType() == Material.FIRE && !GriefPrevention.instance.config_fireSpreads) spreadEvent.setCancelled(true); + if(spreadEvent.getSource().getType() != Material.FIRE) return; + + if(!GriefPrevention.instance.config_fireSpreads) + { + spreadEvent.setCancelled(true); + return; + } + + //never spread into a claimed area, regardless of settings + if(this.dataStore.getClaimAt(spreadEvent.getBlock().getLocation(), false, null) != null) + { + spreadEvent.setCancelled(true); + + //if the source of the spread is not fire on netherrack, put out that source fire to save cpu cycles + Block source = spreadEvent.getSource(); + if(source.getType() == Material.FIRE && source.getRelative(BlockFace.DOWN).getType() != Material.NETHERRACK) + { + source.setType(Material.AIR); + } + } } //blocks are not destroyed by fire, unless configured to do so @@ -516,6 +557,7 @@ public class BlockEventHandler implements Listener if(!GriefPrevention.instance.config_fireDestroys) { burnEvent.setCancelled(true); + return; } //never burn claimed blocks, regardless of settings @@ -530,6 +572,9 @@ public class BlockEventHandler implements Listener @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) public void onBlockFromTo (BlockFromToEvent spreadEvent) { + //don't track fluid movement in worlds where claims are not enabled + if(!GriefPrevention.instance.config_claims_enabledWorlds.contains(spreadEvent.getBlock().getWorld())) return; + //always allow fluids to flow straight down if(spreadEvent.getFace() == BlockFace.DOWN) return; @@ -602,7 +647,7 @@ public class BlockEventHandler implements Listener //into wilderness is NOT OK when surface buckets are limited Material materialDispensed = dispenseEvent.getItem().getType(); - if((materialDispensed == Material.WATER_BUCKET || materialDispensed == Material.LAVA_BUCKET) && GriefPrevention.instance.config_blockWildernessWaterBuckets && toClaim == null) + if((materialDispensed == Material.WATER_BUCKET || materialDispensed == Material.LAVA_BUCKET) && GriefPrevention.instance.config_blockWildernessWaterBuckets && GriefPrevention.instance.claimsEnabledForWorld(fromBlock.getWorld()) && toClaim == null) { dispenseEvent.setCancelled(true); return; diff --git a/src/me/ryanhamshire/GriefPrevention/CleanupUnusedClaimsTask.java b/src/me/ryanhamshire/GriefPrevention/CleanupUnusedClaimsTask.java index a5f925a..5b35872 100644 --- a/src/me/ryanhamshire/GriefPrevention/CleanupUnusedClaimsTask.java +++ b/src/me/ryanhamshire/GriefPrevention/CleanupUnusedClaimsTask.java @@ -166,7 +166,10 @@ class CleanupUnusedClaimsTask implements Runnable } //toss that player data out of the cache, it's probably not needed in memory right now - GriefPrevention.instance.dataStore.clearCachedPlayerData(claim.ownerName); + if(!GriefPrevention.instance.getServer().getOfflinePlayer(claim.ownerName).isOnline()) + { + GriefPrevention.instance.dataStore.clearCachedPlayerData(claim.ownerName); + } //since we're potentially loading a lot of chunks to scan parts of the world where there are no players currently playing, be mindful of memory usage //unfortunately, java/minecraft don't do a good job of clearing unused memory, leading to out of memory errors from this type of world scanning diff --git a/src/me/ryanhamshire/GriefPrevention/DataStore.java b/src/me/ryanhamshire/GriefPrevention/DataStore.java index 0471e5f..8ca3e30 100644 --- a/src/me/ryanhamshire/GriefPrevention/DataStore.java +++ b/src/me/ryanhamshire/GriefPrevention/DataStore.java @@ -956,7 +956,10 @@ public abstract class DataStore this.addDefault(defaults, Messages.TrappedWontWorkHere, "Sorry, unable to find a safe location to teleport you to. Contact an admin, or consider /kill if you don't want to wait.", null); this.addDefault(defaults, Messages.CommandBannedInPvP, "You can't use that command while in PvP combat.", null); this.addDefault(defaults, Messages.UnclaimCleanupWarning, "The land you've unclaimed may be changed by other players or cleaned up by administrators. If you've built something there you want to keep, you should reclaim it.", null); - this.addDefault(defaults, Messages.BuySellNotConfigured, "Sorry, buying anhd selling claim blocks is disabled.", null); + this.addDefault(defaults, Messages.BuySellNotConfigured, "Sorry, buying anhd selling claim blocks is disabled.", null); + this.addDefault(defaults, Messages.NoTeleportPvPCombat, "You can't teleport while fighting another player.", null); + this.addDefault(defaults, Messages.NoTNTDamageAboveSeaLevel, "Warning: TNT will not destroy blocks above sea level.", null); + this.addDefault(defaults, Messages.NoTNTDamageClaims, "Warning: TNT will not destroy claimed blocks.", null); //load the config file FileConfiguration config = YamlConfiguration.loadConfiguration(new File(messagesFilePath)); diff --git a/src/me/ryanhamshire/GriefPrevention/DeliverClaimBlocksTask.java b/src/me/ryanhamshire/GriefPrevention/DeliverClaimBlocksTask.java index 92e4aea..de1bb7f 100644 --- a/src/me/ryanhamshire/GriefPrevention/DeliverClaimBlocksTask.java +++ b/src/me/ryanhamshire/GriefPrevention/DeliverClaimBlocksTask.java @@ -33,7 +33,7 @@ class DeliverClaimBlocksTask implements Runnable //ensure players get at least 1 block (if accrual is totally disabled, this task won't even be scheduled) int accruedBlocks = GriefPrevention.instance.config_claims_blocksAccruedPerHour / 12; - if(accruedBlocks == 0) accruedBlocks = 1; + if(accruedBlocks < 0) accruedBlocks = 1; //for each online player for(int i = 0; i < players.length; i++) @@ -48,6 +48,11 @@ class DeliverClaimBlocksTask implements Runnable //if he's not in a vehicle and has moved at least three blocks since the last check if(!player.isInsideVehicle() && (lastLocation == null || lastLocation.distanceSquared(player.getLocation()) >= 9)) { + //if player is over accrued limit, accrued limit was probably reduced in config file AFTER he accrued + //in that case, leave his blocks where they are + if(playerData.accruedClaimBlocks > GriefPrevention.instance.config_claims_maxAccruedBlocks) continue; + + //add blocks playerData.accruedClaimBlocks += accruedBlocks; //respect limits diff --git a/src/me/ryanhamshire/GriefPrevention/EntityEventHandler.java b/src/me/ryanhamshire/GriefPrevention/EntityEventHandler.java index 18b83a9..43e64a8 100644 --- a/src/me/ryanhamshire/GriefPrevention/EntityEventHandler.java +++ b/src/me/ryanhamshire/GriefPrevention/EntityEventHandler.java @@ -102,12 +102,12 @@ class EntityEventHandler implements Listener //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( location.getWorld().getEnvironment() == Environment.NORMAL && ((isCreeper && GriefPrevention.instance.config_blockSurfaceCreeperExplosions) || (!isCreeper && GriefPrevention.instance.config_blockSurfaceOtherExplosions))) + if( location.getWorld().getEnvironment() == Environment.NORMAL && GriefPrevention.instance.config_claims_enabledWorlds.contains(location.getWorld()) && ((isCreeper && GriefPrevention.instance.config_blockSurfaceCreeperExplosions) || (!isCreeper && GriefPrevention.instance.config_blockSurfaceOtherExplosions))) { for(int i = 0; i < blocks.size(); i++) { Block block = blocks.get(i); - if(GriefPrevention.instance.config_mods_explodableIds.contains(block.getTypeId())) continue; + if(GriefPrevention.instance.config_mods_explodableIds.Contains(new MaterialInfo(block.getTypeId(), block.getData(), null))) continue; if(block.getLocation().getBlockY() > location.getWorld().getSeaLevel() - 7) { @@ -122,7 +122,7 @@ class EntityEventHandler implements Listener for(int i = 0; i < blocks.size(); i++) { Block block = blocks.get(i); - if(GriefPrevention.instance.config_mods_explodableIds.contains(block.getTypeId())) continue; + if(GriefPrevention.instance.config_mods_explodableIds.Contains(new MaterialInfo(block.getTypeId(), block.getData(), null))) continue; blocks.remove(i--); } @@ -135,7 +135,7 @@ class EntityEventHandler implements Listener Block block = blocks.get(i); if(block.getType() == Material.AIR) continue; //if it's air, we don't care - if(GriefPrevention.instance.config_mods_explodableIds.contains(block.getTypeId())) continue; + if(GriefPrevention.instance.config_mods_explodableIds.Contains(new MaterialInfo(block.getTypeId(), block.getData(), null))) continue; claim = this.dataStore.getClaimAt(block.getLocation(), false, claim); //if the block is claimed, remove it from the list of destroyed blocks diff --git a/src/me/ryanhamshire/GriefPrevention/GriefPrevention.java b/src/me/ryanhamshire/GriefPrevention/GriefPrevention.java index 897cace..af44f57 100644 --- a/src/me/ryanhamshire/GriefPrevention/GriefPrevention.java +++ b/src/me/ryanhamshire/GriefPrevention/GriefPrevention.java @@ -22,6 +22,7 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; +import java.util.HashMap; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.logging.Logger; @@ -130,13 +131,15 @@ public class GriefPrevention extends JavaPlugin public boolean config_creaturesTrampleCrops; //whether or not non-player entities may trample crops public boolean config_zombiesBreakDoors; //whether or not hard-mode zombies may break down wooden doors - public List config_mods_accessTrustIds; //list of block IDs which should require /accesstrust for player interaction - public List config_mods_containerTrustIds; //list of block IDs which should require /containertrust for player interaction + public MaterialCollection config_mods_accessTrustIds; //list of block IDs which should require /accesstrust for player interaction + public MaterialCollection config_mods_containerTrustIds; //list of block IDs which should require /containertrust for player interaction public List config_mods_ignoreClaimsAccounts; //list of player names which ALWAYS ignore claims - public List config_mods_explodableIds; //list of block IDs which can be destroyed by explosions, even in claimed areas + public MaterialCollection 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 + public HashMap config_seaLevelOverride; //override for sea level, because bukkit doesn't report the right value for all situations + //reference to the economy plugin, if economy integration is enabled public static Economy economy = null; @@ -230,6 +233,15 @@ public class GriefPrevention extends JavaPlugin } } + //sea level + this.config_seaLevelOverride = new HashMap(); + for(int i = 0; i < worlds.size(); i++) + { + int seaLevelOverride = config.getInt("GriefPrevention.SeaLevelOverrides." + worlds.get(i).getName(), -1); + config.set("GriefPrevention.SeaLevelOverrides." + worlds.get(i).getName(), seaLevelOverride); + this.config_seaLevelOverride.put(worlds.get(i).getName(), seaLevelOverride); + } + this.config_claims_preventTheft = config.getBoolean("GriefPrevention.Claims.PreventTheft", true); this.config_claims_protectCreatures = config.getBoolean("GriefPrevention.Claims.ProtectCreatures", true); this.config_claims_preventButtonsSwitches = config.getBoolean("GriefPrevention.Claims.PreventButtonsSwitches", true); @@ -288,17 +300,74 @@ public class GriefPrevention extends JavaPlugin this.config_creaturesTrampleCrops = config.getBoolean("GriefPrevention.CreaturesTrampleCrops", false); this.config_zombiesBreakDoors = config.getBoolean("GriefPrevention.HardModeZombiesBreakDoors", false); - this.config_mods_accessTrustIds = config.getIntegerList("GriefPrevention.Mods.BlockIdsRequiringAccessTrust"); - if(this.config_mods_accessTrustIds == null) this.config_mods_accessTrustIds = new ArrayList(); - - this.config_mods_containerTrustIds = config.getIntegerList("GriefPrevention.Mods.BlockIdsRequiringContainerTrust"); - if(this.config_mods_containerTrustIds == null) this.config_mods_containerTrustIds = new ArrayList(); - this.config_mods_ignoreClaimsAccounts = config.getStringList("GriefPrevention.Mods.PlayersIgnoringAllClaims"); + if(this.config_mods_ignoreClaimsAccounts == null) this.config_mods_ignoreClaimsAccounts = new ArrayList(); - this.config_mods_explodableIds = config.getIntegerList("GriefPrevention.Mods.BlockIdsExplodable"); - if(this.config_mods_explodableIds == null) this.config_mods_explodableIds = new ArrayList(); + this.config_mods_accessTrustIds = new MaterialCollection(); + List accessTrustStrings = config.getStringList("GriefPrevention.Mods.BlockIdsRequiringAccessTrust"); + + //default values for access trust mod blocks + if(accessTrustStrings == null || accessTrustStrings.size() == 0) + { + //none by default + } + + this.parseMaterialListFromConfig(accessTrustStrings, this.config_mods_accessTrustIds); + + this.config_mods_containerTrustIds = new MaterialCollection(); + List containerTrustStrings = config.getStringList("GriefPrevention.Mods.BlockIdsRequiringContainerTrust"); + + //default values for container trust mod blocks + if(containerTrustStrings == null || containerTrustStrings.size() == 0) + { + containerTrustStrings.add(new MaterialInfo(227, "Battery Box").toString()); + containerTrustStrings.add(new MaterialInfo(130, "Transmutation Tablet").toString()); + containerTrustStrings.add(new MaterialInfo(128, "Alchemical Chest and Energy Condenser").toString()); + containerTrustStrings.add(new MaterialInfo(181, "Various Chests").toString()); + containerTrustStrings.add(new MaterialInfo(178, "Ender Chest").toString()); + containerTrustStrings.add(new MaterialInfo(150, "Various BuildCraft Gadgets").toString()); + containerTrustStrings.add(new MaterialInfo(155, "Filler").toString()); + containerTrustStrings.add(new MaterialInfo(157, "Builder").toString()); + containerTrustStrings.add(new MaterialInfo(158, "Template Drawing Table").toString()); + containerTrustStrings.add(new MaterialInfo(126, "Various EE Gadgets").toString()); + containerTrustStrings.add(new MaterialInfo(138, "Various RedPower Gadgets").toString()); + containerTrustStrings.add(new MaterialInfo(137, "BuildCraft Project Table and Furnaces").toString()); + containerTrustStrings.add(new MaterialInfo(250, "Various IC2 Machines").toString()); + containerTrustStrings.add(new MaterialInfo(161, "BuildCraft Engines").toString()); + containerTrustStrings.add(new MaterialInfo(169, "Automatic Crafting Table").toString()); + containerTrustStrings.add(new MaterialInfo(177, "Wireless Components").toString()); + containerTrustStrings.add(new MaterialInfo(183, "Solar Arrays").toString()); + containerTrustStrings.add(new MaterialInfo(187, "Charging Benches").toString()); + containerTrustStrings.add(new MaterialInfo(188, "More IC2 Machines").toString()); + containerTrustStrings.add(new MaterialInfo(190, "Generators, Fabricators, Strainers").toString()); + containerTrustStrings.add(new MaterialInfo(194, "More Gadgets").toString()); + containerTrustStrings.add(new MaterialInfo(207, "Computer").toString()); + containerTrustStrings.add(new MaterialInfo(208, "Computer Peripherals").toString()); + containerTrustStrings.add(new MaterialInfo(246, "IC2 Generators").toString()); + containerTrustStrings.add(new MaterialInfo(24303, "Teleport Pipe").toString()); + containerTrustStrings.add(new MaterialInfo(24304, "Waterproof Teleport Pipe").toString()); + containerTrustStrings.add(new MaterialInfo(24305, "Power Teleport Pipe").toString()); + containerTrustStrings.add(new MaterialInfo(4311, "Diamond Sorting Pipe").toString()); + containerTrustStrings.add(new MaterialInfo(216, "Turtle").toString()); + + } + + //parse the strings from the config file + this.parseMaterialListFromConfig(containerTrustStrings, this.config_mods_containerTrustIds); + + this.config_mods_explodableIds = new MaterialCollection(); + List explodableStrings = config.getStringList("GriefPrevention.Mods.BlockIdsExplodable"); + + //default values for explodable mod blocks + if(explodableStrings == null || explodableStrings.size() == 0) + { + explodableStrings.add(new MaterialInfo(161, "BuildCraft Engines").toString()); + explodableStrings.add(new MaterialInfo(246, (byte)5 ,"Nuclear Reactor").toString()); + } + + //parse the strings from the config file + this.parseMaterialListFromConfig(explodableStrings, this.config_mods_explodableIds); //default for claim investigation tool String investigationToolMaterialName = Material.STICK.name(); @@ -478,6 +547,9 @@ public class GriefPrevention extends JavaPlugin config.set("GriefPrevention.Mods.BlockIdsRequiringContainerTrust", this.config_mods_containerTrustIds); config.set("GriefPrevention.Mods.BlockIdsExplodable", this.config_mods_explodableIds); config.set("GriefPrevention.Mods.PlayersIgnoringAllClaims", this.config_mods_ignoreClaimsAccounts); + config.set("GriefPrevention.Mods.BlockIdsRequiringAccessTrust", accessTrustStrings); + config.set("GriefPrevention.Mods.BlockIdsRequiringContainerTrust", containerTrustStrings); + config.set("GriefPrevention.Mods.BlockIdsExplodable", explodableStrings); try { @@ -2349,7 +2421,51 @@ public class GriefPrevention extends JavaPlugin //create task //when done processing, this task will create a main thread task to actually update the world with processing results - RestoreNatureProcessingTask task = new RestoreNatureProcessingTask(snapshots, miny, chunk.getWorld().getEnvironment(), lesserBoundaryCorner.getBlock().getBiome(), lesserBoundaryCorner, greaterBoundaryCorner, chunk.getWorld().getSeaLevel() + 1, aggressiveMode, GriefPrevention.instance.creativeRulesApply(lesserBoundaryCorner), playerReceivingVisualization); + RestoreNatureProcessingTask task = new RestoreNatureProcessingTask(snapshots, miny, chunk.getWorld().getEnvironment(), lesserBoundaryCorner.getBlock().getBiome(), lesserBoundaryCorner, greaterBoundaryCorner, chunk.getWorld().getSeaLevel(), aggressiveMode, GriefPrevention.instance.creativeRulesApply(lesserBoundaryCorner), playerReceivingVisualization); GriefPrevention.instance.getServer().getScheduler().scheduleAsyncDelayedTask(GriefPrevention.instance, task, delayInTicks); } + + private void parseMaterialListFromConfig(List stringsToParse, MaterialCollection materialCollection) + { + materialCollection.clear(); + + //for each string in the list + for(int i = 0; i < stringsToParse.size(); i++) + { + //try to parse the string value into a material info + MaterialInfo materialInfo = MaterialInfo.fromString(stringsToParse.get(i)); + + //null value returned indicates an error parsing the string from the config file + if(materialInfo == null) + { + //show error in log + GriefPrevention.AddLogEntry("ERROR: Unable to read a material entry from the config file. Please update your config.yml."); + + //update string, which will go out to config file to help user find the error entry + if(!stringsToParse.get(i).contains("can't")) + { + stringsToParse.set(i, stringsToParse.get(i) + " <-- can't understand this entry, see BukkitDev documentation"); + } + } + + //otherwise store the valid entry in config data + else + { + materialCollection.Add(materialInfo); + } + } + } + + public int getSeaLevel(World world) + { + Integer overrideValue = this.config_seaLevelOverride.get(world.getName()); + if(overrideValue == null || overrideValue == -1) + { + return world.getSeaLevel(); + } + else + { + return overrideValue; + } + } } \ No newline at end of file diff --git a/src/me/ryanhamshire/GriefPrevention/MaterialCollection.java b/src/me/ryanhamshire/GriefPrevention/MaterialCollection.java new file mode 100644 index 0000000..b437c9f --- /dev/null +++ b/src/me/ryanhamshire/GriefPrevention/MaterialCollection.java @@ -0,0 +1,56 @@ +package me.ryanhamshire.GriefPrevention; + +import java.util.ArrayList; + +//ordered list of material info objects, for fast searching +public class MaterialCollection +{ + ArrayList materials = new ArrayList(); + + void Add(MaterialInfo material) + { + int i; + for(i = 0; i < this.materials.size() && this.materials.get(i).typeID <= material.typeID; i++); + this.materials.add(i, material); + } + + boolean Contains(MaterialInfo material) + { + for(int i = 0; i < this.materials.size() ; i++) + { + MaterialInfo thisMaterial = this.materials.get(i); + if(material.typeID == thisMaterial.typeID && (thisMaterial.allDataValues || material.data == thisMaterial.data)) + { + return true; + } + else if(thisMaterial.typeID > material.typeID) + { + return false; + } + } + + return false; + } + + @Override + public String toString() + { + StringBuilder stringBuilder = new StringBuilder(); + for(int i = 0; i < this.materials.size(); i++) + { + stringBuilder.append(this.materials.get(i).toString() + " "); + } + + return stringBuilder.toString(); + } + + public int size() + { + return this.materials.size(); + } + + public void clear() + { + this.materials.clear(); + } +} diff --git a/src/me/ryanhamshire/GriefPrevention/MaterialInfo.java b/src/me/ryanhamshire/GriefPrevention/MaterialInfo.java new file mode 100644 index 0000000..e6f80a6 --- /dev/null +++ b/src/me/ryanhamshire/GriefPrevention/MaterialInfo.java @@ -0,0 +1,75 @@ +package me.ryanhamshire.GriefPrevention; + +//represents a material or collection of materials +public class MaterialInfo +{ + int typeID; + byte data; + boolean allDataValues; + String description; + + public MaterialInfo(int typeID, byte data, String description) + { + this.typeID = typeID; + this.data = data; + this.allDataValues = false; + this.description = description; + } + + public MaterialInfo(int typeID, String description) + { + this.typeID = typeID; + this.data = 0; + this.allDataValues = true; + this.description = description; + } + + private MaterialInfo(int typeID, byte data, boolean allDataValues, String description) + { + this.typeID = typeID; + this.data = data; + this.allDataValues = allDataValues; + this.description = description; + } + + @Override + public String toString() + { + String returnValue = String.valueOf(this.typeID) + ":" + (this.allDataValues?"*":String.valueOf(this.data)); + if(this.description != null) returnValue += ":" + this.description; + + return returnValue; + } + + public static MaterialInfo fromString(String string) + { + if(string == null || string.isEmpty()) return null; + + String [] parts = string.split(":"); + if(parts.length < 3) return null; + + try + { + int typeID = Integer.parseInt(parts[0]); + + byte data; + boolean allDataValues; + if(parts[1].equals("*")) + { + allDataValues = true; + data = 0; + } + else + { + allDataValues = false; + data = Byte.parseByte(parts[1]); + } + + return new MaterialInfo(typeID, data, allDataValues, parts[2]); + } + catch(NumberFormatException exception) + { + return null; + } + } +} diff --git a/src/me/ryanhamshire/GriefPrevention/Messages.java b/src/me/ryanhamshire/GriefPrevention/Messages.java index 6343adb..1e30ebb 100644 --- a/src/me/ryanhamshire/GriefPrevention/Messages.java +++ b/src/me/ryanhamshire/GriefPrevention/Messages.java @@ -2,5 +2,5 @@ package me.ryanhamshire.GriefPrevention; 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, BuildingOutsideClaims, TrappedWontWorkHere, CommandBannedInPvP, UnclaimCleanupWarning, BuySellNotConfigured + 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, TrappedWontWorkHere, CommandBannedInPvP, UnclaimCleanupWarning, BuySellNotConfigured, NoTeleportPvPCombat, NoTNTDamageAboveSeaLevel, NoTNTDamageClaims } diff --git a/src/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java b/src/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java index d4d6337..ed09c8b 100644 --- a/src/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java +++ b/src/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java @@ -77,6 +77,12 @@ class PlayerEventHandler implements Listener void onPlayerChat (AsyncPlayerChatEvent event) { Player player = event.getPlayer(); + if(!player.isOnline()) + { + event.setCancelled(true); + return; + } + String message = event.getMessage(); event.setCancelled(this.handlePlayerChat(player, message, event)); @@ -636,6 +642,14 @@ class PlayerEventHandler implements Listener Player player = event.getPlayer(); + PlayerData playerData = this.dataStore.getPlayerData(player.getName()); + if(playerData.inPvpCombat()) + { + GriefPrevention.sendMessage(player, TextMode.Err, Messages.NoTeleportPvPCombat); + event.setCancelled(true); + return; + } + Location source = event.getFrom(); Claim sourceClaim = this.dataStore.getClaimAt(source, false, null); if(sourceClaim != null && sourceClaim.siegeData != null) @@ -778,8 +792,11 @@ class PlayerEventHandler implements Listener playerData.claimResizing = null; //give the player his available claim blocks count and claiming instructions, but only if he keeps the shovel equipped for a minimum time, to avoid mouse wheel spam - EquipShovelProcessingTask task = new EquipShovelProcessingTask(player); - GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, task, 15L); //15L is approx. 3/4 of a second + if(GriefPrevention.instance.claimsEnabledForWorld(player.getWorld())) + { + EquipShovelProcessingTask task = new EquipShovelProcessingTask(player); + GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, task, 15L); //15L is approx. 3/4 of a second + } } } @@ -946,7 +963,7 @@ class PlayerEventHandler implements Listener clickedBlockType == Material.BREWING_STAND || clickedBlockType == Material.JUKEBOX || clickedBlockType == Material.ENCHANTMENT_TABLE || - GriefPrevention.instance.config_mods_containerTrustIds.contains(clickedBlock.getTypeId())))) + GriefPrevention.instance.config_mods_containerTrustIds.Contains(new MaterialInfo(clickedBlock.getTypeId(), clickedBlock.getData(), null))))) { //block container use while under siege, so players can't hide items from attackers if(playerData.siegeData != null) @@ -1009,7 +1026,7 @@ class PlayerEventHandler implements Listener } //otherwise apply rules for buttons and switches - else if(GriefPrevention.instance.config_claims_preventButtonsSwitches && (clickedBlockType == null || clickedBlockType == Material.STONE_BUTTON || clickedBlockType == Material.LEVER || GriefPrevention.instance.config_mods_accessTrustIds.contains(clickedBlock.getTypeId()))) + else if(GriefPrevention.instance.config_claims_preventButtonsSwitches && (clickedBlockType == null || clickedBlockType == Material.STONE_BUTTON || clickedBlockType == Material.LEVER || GriefPrevention.instance.config_mods_accessTrustIds.Contains(new MaterialInfo(clickedBlock.getTypeId(), clickedBlock.getData(), null)))) { Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), false, playerData.lastClaim); if(claim != null) diff --git a/src/me/ryanhamshire/GriefPrevention/RestoreNatureProcessingTask.java b/src/me/ryanhamshire/GriefPrevention/RestoreNatureProcessingTask.java index 0457fee..2e222fe 100644 --- a/src/me/ryanhamshire/GriefPrevention/RestoreNatureProcessingTask.java +++ b/src/me/ryanhamshire/GriefPrevention/RestoreNatureProcessingTask.java @@ -99,7 +99,6 @@ class RestoreNatureProcessingTask implements Runnable { this.playerBlocks.add(Material.LEAVES.getId()); this.playerBlocks.add(Material.LOG.getId()); - this.playerBlocks.add(Material.LEAVES.getId()); this.playerBlocks.add(Material.VINE.getId()); } } @@ -145,11 +144,37 @@ class RestoreNatureProcessingTask implements Runnable //cover surface stone and gravel with sand or grass, as the biome requires this.coverSurfaceStone(); + //reset leaves to NOT player placed so that they may decay as usual + this.resetLeaves(); + //schedule main thread task to apply the result to the world RestoreNatureExecutionTask task = new RestoreNatureExecutionTask(this.snapshots, this.miny, this.lesserBoundaryCorner, this.greaterBoundaryCorner, this.player); GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, task); } + private void resetLeaves() + { + for(int x = 1; x < snapshots.length - 1; x++) + { + for(int z = 1; z < snapshots[0][0].length - 1; z++) + { + for(int y = this.seaLevel - 1; y < snapshots[0].length; y++) + { + //note: see minecraft wiki data values for leaves + BlockSnapshot block = snapshots[x][y][z]; + if(block.typeId == Material.LEAVES.getId()) + { + //clear "player placed" bit + block.data = (byte)(block.data & ~(1<<2)); + + //set the "check for natural decay" bit + block.data = (byte)(block.data | (1<<3)); + } + } + } + } + } + private void fillBigHoles() { for(int x = 1; x < snapshots.length - 1; x++) @@ -157,15 +182,15 @@ class RestoreNatureProcessingTask implements Runnable for(int z = 1; z < snapshots[0][0].length - 1; z++) { //replace air, lava, or running water at sea level with stone - if(this.snapshots[x][this.seaLevel - 3][z].typeId == Material.AIR.getId() || this.snapshots[x][this.seaLevel - 3][z].typeId == Material.LAVA.getId() || (this.snapshots[x][this.seaLevel - 3][z].typeId == Material.WATER.getId() || this.snapshots[x][this.seaLevel - 3][z].data != 0)) + if(this.snapshots[x][this.seaLevel - 2][z].typeId == Material.AIR.getId() || this.snapshots[x][this.seaLevel - 2][z].typeId == Material.LAVA.getId() || (this.snapshots[x][this.seaLevel - 2][z].typeId == Material.WATER.getId() || this.snapshots[x][this.seaLevel - 2][z].data != 0)) { - this.snapshots[x][this.seaLevel - 3][z].typeId = Material.STONE.getId(); + this.snapshots[x][this.seaLevel - 2][z].typeId = Material.STONE.getId(); } //do the same for one layer beneath that (because a future restoration step may convert surface stone to sand, which falls down) - if(this.snapshots[x][this.seaLevel - 4][z].typeId == Material.AIR.getId() || this.snapshots[x][this.seaLevel - 4][z].typeId == Material.LAVA.getId() || (this.snapshots[x][this.seaLevel - 4][z].typeId == Material.WATER.getId() || this.snapshots[x][this.seaLevel - 4][z].data != 0)) + if(this.snapshots[x][this.seaLevel - 3][z].typeId == Material.AIR.getId() || this.snapshots[x][this.seaLevel - 3][z].typeId == Material.LAVA.getId() || (this.snapshots[x][this.seaLevel - 3][z].typeId == Material.WATER.getId() || this.snapshots[x][this.seaLevel - 3][z].data != 0)) { - this.snapshots[x][this.seaLevel - 4][z].typeId = Material.STONE.getId(); + this.snapshots[x][this.seaLevel - 3][z].typeId = Material.STONE.getId(); } } } @@ -219,7 +244,7 @@ class RestoreNatureProcessingTask implements Runnable { int thisy = this.highestY(x, z, true); - while(thisy > this.seaLevel - 2 && (this.snapshots[x][thisy][z].typeId == Material.STONE.getId() || this.snapshots[x][thisy][z].typeId == Material.SANDSTONE.getId())) + while(thisy > this.seaLevel - 1 && (this.snapshots[x][thisy][z].typeId == Material.STONE.getId() || this.snapshots[x][thisy][z].typeId == Material.SANDSTONE.getId())) { BlockSnapshot leftBlock = this.snapshots[x + 1][thisy][z]; BlockSnapshot rightBlock = this.snapshots[x - 1][thisy][z]; @@ -265,7 +290,7 @@ class RestoreNatureProcessingTask implements Runnable { for(int z = 1; z < snapshots[0][0].length - 1; z++) { - for(int y = this.seaLevel - 2; y < snapshots[0].length; y++) + for(int y = this.seaLevel - 1; y < snapshots[0].length; y++) { BlockSnapshot block = snapshots[x][y][z]; @@ -401,7 +426,7 @@ class RestoreNatureProcessingTask implements Runnable int y = this.highestY(x, z, true); BlockSnapshot block = snapshots[x][y][z]; - if(block.typeId == Material.STONE.getId() || block.typeId == Material.GRAVEL.getId() || block.typeId == Material.DIRT.getId() || block.typeId == Material.SANDSTONE.getId()) + if(block.typeId == Material.STONE.getId() || block.typeId == Material.GRAVEL.getId() || block.typeId == Material.SOIL.getId() || block.typeId == Material.DIRT.getId() || block.typeId == Material.SANDSTONE.getId()) { if(this.biome == Biome.DESERT || this.biome == Biome.DESERT_HILLS || this.biome == Biome.BEACH) { @@ -587,8 +612,10 @@ class RestoreNatureProcessingTask implements Runnable if(block.typeId != Material.AIR.getId() && !(ignoreLeaves && block.typeId == Material.SNOW.getId()) && !(ignoreLeaves && block.typeId == Material.LEAVES.getId()) && - !(block.typeId == Material.STATIONARY_WATER.getId() && block.data != 0) && - !(block.typeId == Material.STATIONARY_LAVA.getId() && block.data != 0)) + !(block.typeId == Material.STATIONARY_WATER.getId()) && + !(block.typeId == Material.WATER.getId()) && + !(block.typeId == Material.LAVA.getId()) && + !(block.typeId == Material.STATIONARY_LAVA.getId())) { return y; } @@ -635,7 +662,6 @@ class RestoreNatureProcessingTask implements Runnable playerBlocks.add(Material.REDSTONE_WIRE.getId()); playerBlocks.add(Material.DIAMOND_BLOCK.getId()); playerBlocks.add(Material.WORKBENCH.getId()); - playerBlocks.add(Material.SOIL.getId()); playerBlocks.add(Material.FURNACE.getId()); playerBlocks.add(Material.BURNING_FURNACE.getId()); playerBlocks.add(Material.WOODEN_DOOR.getId());