This commit is contained in:
Ryan Hamshire 2012-08-29 19:24:33 -07:00
parent bd215673c0
commit 008f313d09
10 changed files with 408 additions and 104 deletions

View File

@ -2,7 +2,7 @@ name: GriefPrevention
main: me.ryanhamshire.GriefPrevention.GriefPrevention
softdepend: [Vault, Multiverse-Core, My Worlds]
dev-url: http://dev.bukkit.org/server-mods/grief-prevention
version: 6.1
version: 6.3
commands:
abandonclaim:
description: Deletes a claim.

View File

@ -352,7 +352,7 @@ public class BlockEventHandler implements Listener
}
//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()))
else if(GriefPrevention.instance.config_claims_warnOnBuildOutside && !this.trashBlocks.contains(block.getType()) && GriefPrevention.instance.claimsEnabledForWorld(block.getWorld()) && playerData.claims.size() > 0)
{
if(--playerData.unclaimedBlockPlacementsUntilWarning <= 0)
{
@ -601,7 +601,8 @@ public class BlockEventHandler implements Listener
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)
Material materialDispensed = dispenseEvent.getItem().getType();
if((materialDispensed == Material.WATER_BUCKET || materialDispensed == Material.LAVA_BUCKET) && GriefPrevention.instance.config_blockWildernessWaterBuckets && toClaim == null)
{
dispenseEvent.setCancelled(true);
return;

View File

@ -121,6 +121,9 @@ public class Claim
//don't do it for very large claims
if(this.getArea() > 10000) return;
//don't do it when surface fluids are allowed to be dumped
if(!GriefPrevention.instance.config_blockWildernessWaterBuckets) return;
Location lesser = this.getLesserBoundaryCorner();
Location greater = this.getGreaterBoundaryCorner();
@ -430,9 +433,6 @@ public class Claim
//access permission check
public String allowAccess(Player player)
{
//everyone always has access to admin claims
if(this.isAdminClaim()) return null;
//following a siege where the defender lost, the claim will allow everyone access for a time
if(this.doorsOpen) return null;
@ -471,9 +471,6 @@ public class Claim
return GriefPrevention.instance.dataStore.getMessage(Messages.NoContainersSiege, siegeData.attacker.getName());
}
//containers are always accessible in admin claims
if(this.isAdminClaim()) return null;
//owner and administrators in ignoreclaims mode have access
if(this.ownerName.equals(player.getName()) || GriefPrevention.instance.dataStore.getPlayerData(player.getName()).ignoreClaims) return null;

View File

@ -100,6 +100,13 @@ public abstract class DataStore
{
claim.removeSurfaceFluids(null);
this.deleteClaim(claim);
//if in a creative mode world, delete the claim
if(GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()))
{
GriefPrevention.instance.restoreClaim(claim, 0);
}
GriefPrevention.AddLogEntry(" " + playerName + "'s new player claim expired.");
}
}
@ -776,8 +783,16 @@ public abstract class DataStore
//delete them one by one
for(int i = 0; i < claimsToDelete.size(); i++)
{
claimsToDelete.get(i).removeSurfaceFluids(null);
this.deleteClaim(claimsToDelete.get(i));
Claim claim = claimsToDelete.get(i);
claim.removeSurfaceFluids(null);
this.deleteClaim(claim);
//if in a creative mode world, delete the claim
if(GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()))
{
GriefPrevention.instance.restoreClaim(claim, 0);
}
}
}
@ -996,7 +1011,8 @@ public abstract class DataStore
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);
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.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);
//load the config file
FileConfiguration config = YamlConfiguration.loadConfiguration(new File(messagesFilePath));

View File

@ -41,6 +41,7 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
import org.bukkit.event.entity.EntityBreakDoorEvent;
import org.bukkit.event.entity.EntityChangeBlockEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
@ -75,6 +76,13 @@ class EntityEventHandler implements Listener
}
}
//don't allow zombies to break down doors
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onZombieBreakDoor(EntityBreakDoorEvent event)
{
if(!GriefPrevention.instance.config_zombiesBreakDoors) event.setCancelled(true);
}
//don't allow entities to trample crops
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onEntityInteract(EntityInteractEvent event)

View File

@ -80,6 +80,7 @@ public class GriefPrevention extends JavaPlugin
public boolean config_claims_creationRequiresPermission; //whether creating claims with the shovel requires a permission
public int config_claims_claimsExtendIntoGroundDistance; //how far below the shoveled block a new claim will reach
public int config_claims_minSize; //minimum width and height for non-admin claims
public boolean config_claims_allowUnclaimInCreative; //whether players may unclaim land (resize or abandon) in creative mode
public boolean config_claims_noBuildOutsideClaims; //whether players can build in survival worlds outside their claimed areas
@ -127,6 +128,7 @@ public class GriefPrevention extends JavaPlugin
public boolean config_endermenMoveBlocks; //whether or not endermen may move blocks around
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<Integer> config_mods_accessTrustIds; //list of block IDs which should require /accesstrust for player interaction
public List<Integer> config_mods_containerTrustIds; //list of block IDs which should require /containertrust for player interaction
@ -245,7 +247,8 @@ public class GriefPrevention extends JavaPlugin
this.config_claims_expirationDays = config.getInt("GriefPrevention.Claims.IdleLimitDays", 0);
this.config_claims_trappedCooldownHours = config.getInt("GriefPrevention.Claims.TrappedCommandCooldownHours", 8);
this.config_claims_noBuildOutsideClaims = config.getBoolean("GriefPrevention.Claims.NoSurvivalBuildingOutsideClaims", false);
this.config_claims_warnOnBuildOutside = config.getBoolean("GriefPrevention.Claims.WarnWhenBuildingOutsideClaims");
this.config_claims_warnOnBuildOutside = config.getBoolean("GriefPrevention.Claims.WarnWhenBuildingOutsideClaims", true);
this.config_claims_allowUnclaimInCreative = config.getBoolean("GriefPrevention.Claims.AllowUnclaimingCreativeModeLand", true);
this.config_spam_enabled = config.getBoolean("GriefPrevention.Spam.Enabled", true);
this.config_spam_loginCooldownMinutes = config.getInt("GriefPrevention.Spam.LoginCooldownMinutes", 2);
@ -283,6 +286,7 @@ public class GriefPrevention extends JavaPlugin
this.config_endermenMoveBlocks = config.getBoolean("GriefPrevention.EndermenMoveBlocks", false);
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<Integer>();
@ -423,6 +427,7 @@ public class GriefPrevention extends JavaPlugin
config.set("GriefPrevention.Claims.ModificationTool", this.config_claims_modificationTool.name());
config.set("GriefPrevention.Claims.NoSurvivalBuildingOutsideClaims", this.config_claims_noBuildOutsideClaims);
config.set("GriefPrevention.Claims.WarnWhenBuildingOutsideClaims", this.config_claims_warnOnBuildOutside);
config.set("GriefPrevention.Claims.AllowUnclaimingCreativeModeLand", this.config_claims_allowUnclaimInCreative);
config.set("GriefPrevention.Spam.Enabled", this.config_spam_enabled);
config.set("GriefPrevention.Spam.LoginCooldownMinutes", this.config_spam_loginCooldownMinutes);
@ -463,6 +468,7 @@ public class GriefPrevention extends JavaPlugin
config.set("GriefPrevention.EndermenMoveBlocks", this.config_endermenMoveBlocks);
config.set("GriefPrevention.CreaturesTrampleCrops", this.config_creaturesTrampleCrops);
config.set("GriefPrevention.HardModeZombiesBreakDoors", this.config_zombiesBreakDoors);
config.set("GriefPrevention.Database.URL", databaseUrl);
config.set("GriefPrevention.Database.UserName", databaseUserName);
@ -657,7 +663,7 @@ public class GriefPrevention extends JavaPlugin
{
if(args.length != 0) return false;
if(creativeRulesApply(player.getLocation()))
if(!GriefPrevention.instance.config_claims_allowUnclaimInCreative && creativeRulesApply(player.getLocation()))
{
GriefPrevention.sendMessage(player, TextMode.Err, Messages.NoCreativeUnClaim);
return true;
@ -1235,6 +1241,13 @@ public class GriefPrevention extends JavaPlugin
{
claim.removeSurfaceFluids(null);
this.dataStore.deleteClaim(claim);
//if in a creative mode world, delete the claim
if(GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()))
{
GriefPrevention.instance.restoreClaim(claim, 0);
}
GriefPrevention.sendMessage(player, TextMode.Success, Messages.DeleteSuccess);
GriefPrevention.AddLogEntry(player.getName() + " deleted " + claim.getOwnerName() + "'s claim at " + GriefPrevention.getfriendlyLocationString(claim.getLesserBoundaryCorner()));
@ -1633,7 +1646,7 @@ public class GriefPrevention extends JavaPlugin
}
//don't allow abandon of creative mode claims
else if(this.creativeRulesApply(player.getLocation()))
else if(!GriefPrevention.instance.config_claims_allowUnclaimInCreative && this.creativeRulesApply(player.getLocation()))
{
GriefPrevention.sendMessage(player, TextMode.Err, Messages.NoCreativeUnClaim);
}
@ -1658,6 +1671,14 @@ public class GriefPrevention extends JavaPlugin
claim.removeSurfaceFluids(null);
this.dataStore.deleteClaim(claim);
//if in a creative mode world, restore the claim area
if(GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()))
{
GriefPrevention.AddLogEntry(player.getName() + " abandoned a claim @ " + GriefPrevention.getfriendlyLocationString(claim.getLesserBoundaryCorner()));
GriefPrevention.sendMessage(player, TextMode.Warn, Messages.UnclaimCleanupWarning);
GriefPrevention.instance.restoreClaim(claim, 20L * 60 * 2);
}
//tell the player how many claim blocks he has left
int remainingBlocks = playerData.getRemainingClaimBlocks();
GriefPrevention.sendMessage(player, TextMode.Success, Messages.AbandonSuccess, String.valueOf(remainingBlocks));
@ -1694,7 +1715,7 @@ public class GriefPrevention extends JavaPlugin
else
{
otherPlayer = this.resolvePlayer(recipientName);
if(otherPlayer == null && !recipientName.equals("public"))
if(otherPlayer == null && !recipientName.equals("public") && !recipientName.equals("all"))
{
GriefPrevention.sendMessage(player, TextMode.Err, Messages.PlayerNotFound);
return;
@ -2268,4 +2289,55 @@ public class GriefPrevention extends JavaPlugin
return claim.allowBreak(player, location.getBlock().getType());
}
}
//restores nature in multiple chunks, as described by a claim instance
//this restores all chunks which have ANY number of claim blocks from this claim in them
//if the claim is still active (in the data store), then the claimed blocks will not be changed (only the area bordering the claim)
public void restoreClaim(Claim claim, long delayInTicks)
{
//admin claims aren't automatically cleaned up when deleted or abandoned
if(claim.isAdminClaim()) return;
//it's too expensive to do this for huge claims
if(claim.getArea() > 10000) return;
Chunk lesserChunk = claim.getLesserBoundaryCorner().getChunk();
Chunk greaterChunk = claim.getGreaterBoundaryCorner().getChunk();
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);
this.restoreChunk(chunk, chunk.getWorld().getSeaLevel() - 15, false, delayInTicks, null);
}
}
public void restoreChunk(Chunk chunk, int miny, boolean aggressiveMode, long delayInTicks, Player playerReceivingVisualization)
{
//build a snapshot of this chunk, including 1 block boundary outside of the chunk all the way around
int maxHeight = chunk.getWorld().getMaxHeight();
BlockSnapshot[][][] snapshots = new BlockSnapshot[18][maxHeight][18];
Block startBlock = chunk.getBlock(0, 0, 0);
Location startLocation = new Location(chunk.getWorld(), startBlock.getX() - 1, 0, startBlock.getZ() - 1);
for(int x = 0; x < snapshots.length; x++)
{
for(int z = 0; z < snapshots[0][0].length; z++)
{
for(int y = 0; y < snapshots[0].length; y++)
{
Block block = chunk.getWorld().getBlockAt(startLocation.getBlockX() + x, startLocation.getBlockY() + y, startLocation.getBlockZ() + z);
snapshots[x][y][z] = new BlockSnapshot(block.getLocation(), block.getTypeId(), block.getData());
}
}
}
//create task to process those data in another thread
Location lesserBoundaryCorner = chunk.getBlock(0, 0, 0).getLocation();
Location greaterBoundaryCorner = chunk.getBlock(15, 0, 15).getLocation();
//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(), chunk.getWorld().getBiome(lesserBoundaryCorner.getBlockX(), lesserBoundaryCorner.getBlockZ()), lesserBoundaryCorner, greaterBoundaryCorner, chunk.getWorld().getSeaLevel() + 1, aggressiveMode, GriefPrevention.instance.creativeRulesApply(lesserBoundaryCorner), playerReceivingVisualization);
GriefPrevention.instance.getServer().getScheduler().scheduleAsyncDelayedTask(GriefPrevention.instance, task, delayInTicks);
}
}

View File

@ -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
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
}

View File

@ -807,10 +807,11 @@ class PlayerEventHandler implements Listener
}
//if the bucket is being used in a claim, allow for dumping lava closer to other players
Claim claim = this.dataStore.getClaimAt(block.getLocation(), false, null);
PlayerData playerData = this.dataStore.getPlayerData(player.getName());
Claim claim = this.dataStore.getClaimAt(block.getLocation(), false, playerData.lastClaim);
if(claim != null)
{
minLavaDistance = 3;
minLavaDistance = 3;
}
//otherwise no wilderness dumping (unless underground) in worlds where claims are enabled
@ -900,6 +901,25 @@ class PlayerEventHandler implements Listener
Material clickedBlockType = clickedBlock.getType();
//apply rules for putting out fires (requires build permission)
PlayerData playerData = this.dataStore.getPlayerData(player.getName());
if(event.getClickedBlock() != null && event.getClickedBlock().getRelative(event.getBlockFace()).getType() == Material.FIRE)
{
Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), false, playerData.lastClaim);
if(claim != null)
{
playerData.lastClaim = claim;
String noBuildReason = claim.allowBuild(player);
if(noBuildReason != null)
{
event.setCancelled(true);
GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason);
return;
}
}
}
//apply rules for containers and crafting blocks
if( GriefPrevention.instance.config_claims_preventTheft && (
event.getAction() == Action.RIGHT_CLICK_BLOCK && (
@ -913,7 +933,6 @@ class PlayerEventHandler implements Listener
GriefPrevention.instance.config_mods_containerTrustIds.contains(clickedBlock.getTypeId()))))
{
//block container use while under siege, so players can't hide items from attackers
PlayerData playerData = this.dataStore.getPlayerData(player.getName());
if(playerData.siegeData != null)
{
GriefPrevention.sendMessage(player, TextMode.Err, Messages.SiegeNoContainers);
@ -930,9 +949,11 @@ class PlayerEventHandler implements Listener
}
//otherwise check permissions for the claim the player is in
Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), false, null);
Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), false, playerData.lastClaim);
if(claim != null)
{
playerData.lastClaim = claim;
String noContainersReason = claim.allowContainers(player);
if(noContainersReason != null)
{
@ -956,9 +977,11 @@ class PlayerEventHandler implements Listener
(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, playerData.lastClaim);
if(claim != null)
{
playerData.lastClaim = claim;
String noAccessReason = claim.allowAccess(player);
if(noAccessReason != null)
{
@ -972,9 +995,11 @@ 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())))
{
Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), false, null);
Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), false, playerData.lastClaim);
if(claim != null)
{
playerData.lastClaim = claim;
String noAccessReason = claim.allowAccess(player);
if(noAccessReason != null)
{
@ -996,7 +1021,7 @@ class PlayerEventHandler implements Listener
//apply rule for note blocks and repeaters
else if(clickedBlockType == Material.NOTE_BLOCK || clickedBlockType == Material.DIODE_BLOCK_ON || clickedBlockType == Material.DIODE_BLOCK_OFF)
{
Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), false, null);
Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), false, playerData.lastClaim);
if(claim != null)
{
String noBuildReason = claim.allowBuild(player);
@ -1045,7 +1070,6 @@ class PlayerEventHandler implements Listener
}
//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;
@ -1070,7 +1094,7 @@ class PlayerEventHandler implements Listener
return;
}
Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), false /*ignore height*/, null);
Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), false /*ignore height*/, playerData.lastClaim);
//no claim case
if(claim == null)
@ -1082,6 +1106,7 @@ class PlayerEventHandler implements Listener
//claim case
else
{
playerData.lastClaim = claim;
GriefPrevention.sendMessage(player, TextMode.Info, Messages.BlockClaimed, claim.getOwnerName());
//visualize boundary
@ -1116,8 +1141,6 @@ class PlayerEventHandler implements Listener
//if it's a golden shovel
else if(materialInHand != GriefPrevention.instance.config_claims_modificationTool) return;
PlayerData playerData = this.dataStore.getPlayerData(player.getName());
//disable golden shovel while under siege
if(playerData.siegeData != null)
{
@ -1152,24 +1175,7 @@ class PlayerEventHandler implements Listener
//figure out which chunk to repair
Chunk chunk = player.getWorld().getChunkAt(clickedBlock.getLocation());
//build a snapshot of this chunk, including 1 block boundary outside of the chunk all the way around
int maxHeight = chunk.getWorld().getMaxHeight();
BlockSnapshot[][][] snapshots = new BlockSnapshot[18][maxHeight][18];
Block startBlock = chunk.getBlock(0, 0, 0);
Location startLocation = new Location(chunk.getWorld(), startBlock.getX() - 1, 0, startBlock.getZ() - 1);
for(int x = 0; x < snapshots.length; x++)
{
for(int z = 0; z < snapshots[0][0].length; z++)
{
for(int y = 0; y < snapshots[0].length; y++)
{
Block block = chunk.getWorld().getBlockAt(startLocation.getBlockX() + x, startLocation.getBlockY() + y, startLocation.getBlockZ() + z);
snapshots[x][y][z] = new BlockSnapshot(block.getLocation(), block.getTypeId(), block.getData());
}
}
}
//create task to process those data in another thread
//start the repair process
//set boundaries for processing
int miny = clickedBlock.getY();
@ -1183,13 +1189,7 @@ class PlayerEventHandler implements Listener
}
}
Location lesserBoundaryCorner = chunk.getBlock(0, 0, 0).getLocation();
Location greaterBoundaryCorner = chunk.getBlock(15, 0, 15).getLocation();
//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(), chunk.getWorld().getBiome(lesserBoundaryCorner.getBlockX(), lesserBoundaryCorner.getBlockZ()), lesserBoundaryCorner, greaterBoundaryCorner, chunk.getWorld().getSeaLevel(), playerData.shovelMode == ShovelMode.RestoreNatureAggressive, player);
GriefPrevention.instance.getServer().getScheduler().scheduleAsyncDelayedTask(GriefPrevention.instance, task);
GriefPrevention.instance.restoreChunk(chunk, miny, playerData.shovelMode == ShovelMode.RestoreNatureAggressive, 0, player);
return;
}
@ -1209,11 +1209,11 @@ class PlayerEventHandler implements Listener
}
else
{
allowedFillBlocks.add(Material.GRASS);
allowedFillBlocks.add(Material.DIRT);
allowedFillBlocks.add(Material.STONE);
allowedFillBlocks.add(Material.SAND);
allowedFillBlocks.add(Material.SANDSTONE);
allowedFillBlocks.add(Material.DIRT);
allowedFillBlocks.add(Material.GRASS);
allowedFillBlocks.add(Material.ICE);
}
@ -1236,6 +1236,29 @@ class PlayerEventHandler implements Listener
Location location = new Location(centerBlock.getWorld(), x, centerBlock.getY(), z);
if(location.distance(centerBlock.getLocation()) > playerData.fillRadius) continue;
//default fill block is initially the first from the allowed fill blocks list above
Material defaultFiller = allowedFillBlocks.get(0);
//prefer to use the block the player clicked on, if it's an acceptable fill block
if(allowedFillBlocks.contains(centerBlock.getType()))
{
defaultFiller = centerBlock.getType();
}
//if the player clicks on water, try to sink through the water to find something underneath that's useful for a filler
else if(centerBlock.getType() == Material.WATER || centerBlock.getType() == Material.STATIONARY_WATER)
{
Block block = centerBlock.getWorld().getBlockAt(centerBlock.getLocation());
while(!allowedFillBlocks.contains(block.getType()) && block.getY() > centerBlock.getY() - 10)
{
block = block.getRelative(BlockFace.DOWN);
}
if(allowedFillBlocks.contains(block.getType()))
{
defaultFiller = block.getType();
}
}
//fill bottom to top
for(int y = minHeight; y <= maxHeight; y++)
{
@ -1252,41 +1275,43 @@ class PlayerEventHandler implements Listener
//only replace air, spilling water, snow, long grass
if(block.getType() == Material.AIR || block.getType() == Material.SNOW || (block.getType() == Material.STATIONARY_WATER && block.getData() != 0) || block.getType() == Material.LONG_GRASS)
{
//look to neighbors for an appropriate fill block
Block eastBlock = block.getRelative(BlockFace.EAST);
Block westBlock = block.getRelative(BlockFace.WEST);
Block northBlock = block.getRelative(BlockFace.NORTH);
Block southBlock = block.getRelative(BlockFace.SOUTH);
Block underBlock = block.getRelative(BlockFace.DOWN);
//first, check lateral neighbors (ideally, want to keep natural layers)
if(allowedFillBlocks.contains(eastBlock.getType()))
//if the top level, always use the default filler picked above
if(y == maxHeight)
{
block.setType(eastBlock.getType());
}
else if(allowedFillBlocks.contains(westBlock.getType()))
{
block.setType(westBlock.getType());
}
else if(allowedFillBlocks.contains(northBlock.getType()))
{
block.setType(northBlock.getType());
}
else if(allowedFillBlocks.contains(southBlock.getType()))
{
block.setType(southBlock.getType());
block.setType(defaultFiller);
}
//then check underneath
else if(allowedFillBlocks.contains(underBlock.getType()))
{
block.setType(underBlock.getType());
}
//if all else fails, use the first material listed in the acceptable fill blocks above
//otherwise look to neighbors for an appropriate fill block
else
{
block.setType(allowedFillBlocks.get(0));
Block eastBlock = block.getRelative(BlockFace.EAST);
Block westBlock = block.getRelative(BlockFace.WEST);
Block northBlock = block.getRelative(BlockFace.NORTH);
Block southBlock = block.getRelative(BlockFace.SOUTH);
//first, check lateral neighbors (ideally, want to keep natural layers)
if(allowedFillBlocks.contains(eastBlock.getType()))
{
block.setType(eastBlock.getType());
}
else if(allowedFillBlocks.contains(westBlock.getType()))
{
block.setType(westBlock.getType());
}
else if(allowedFillBlocks.contains(northBlock.getType()))
{
block.setType(northBlock.getType());
}
else if(allowedFillBlocks.contains(southBlock.getType()))
{
block.setType(southBlock.getType());
}
//if all else fails, use the default filler selected above
else
{
block.setType(defaultFiller);
}
}
}
}
@ -1380,6 +1405,7 @@ class PlayerEventHandler implements Listener
//rule1: in creative mode, top-level claims can't be moved or resized smaller.
//rule2: in any mode, shrinking a claim removes any surface fluids
Claim oldClaim = playerData.claimResizing;
boolean smaller = false;
if(oldClaim.parent == null)
{
//temporary claim instance, just for checking contains()
@ -1391,8 +1417,10 @@ class PlayerEventHandler implements Listener
//if the new claim is smaller
if(!newClaim.contains(oldClaim.getLesserBoundaryCorner(), true, false) || !newClaim.contains(oldClaim.getGreaterBoundaryCorner(), true, false))
{
smaller = true;
//enforce creative mode rule
if(!player.hasPermission("griefprevention.deleteclaims") && GriefPrevention.instance.creativeRulesApply(player.getLocation()))
if(!GriefPrevention.instance.config_claims_allowUnclaimInCreative && !player.hasPermission("griefprevention.deleteclaims") && GriefPrevention.instance.creativeRulesApply(player.getLocation()))
{
GriefPrevention.sendMessage(player, TextMode.Err, Messages.NoCreativeUnClaim);
return;
@ -1419,6 +1447,14 @@ class PlayerEventHandler implements Listener
GriefPrevention.AddLogEntry(playerName + " resized " + playerData.claimResizing.getOwnerName() + "'s claim at " + GriefPrevention.getfriendlyLocationString(playerData.claimResizing.lesserBoundaryCorner) + ".");
}
//if in a creative mode world and shrinking an existing claim, restore any unclaimed area
if(smaller && GriefPrevention.instance.creativeRulesApply(oldClaim.getLesserBoundaryCorner()))
{
GriefPrevention.sendMessage(player, TextMode.Warn, Messages.UnclaimCleanupWarning);
GriefPrevention.instance.restoreClaim(oldClaim, 20L * 60 * 2); //2 minutes
GriefPrevention.AddLogEntry(player.getName() + " shrank a claim @ " + GriefPrevention.getfriendlyLocationString(playerData.claimResizing.getLesserBoundaryCorner()));
}
//clean up
playerData.claimResizing = null;
playerData.lastShovelLocation = null;

View File

@ -23,6 +23,7 @@ import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Animals;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
@ -88,7 +89,7 @@ class RestoreNatureExecutionTask implements Runnable
for(int i = 0; i < entities.length; i++)
{
Entity entity = entities[i];
if(!(entity instanceof Player))
if(!(entity instanceof Player || entity instanceof Animals))
{
entity.remove();
}
@ -101,8 +102,11 @@ class RestoreNatureExecutionTask implements Runnable
}
//show visualization to player
Claim claim = new Claim(lesserCorner, greaterCorner, "", new String[] {}, new String[] {}, new String[] {}, new String[] {}, null);
Visualization visualization = Visualization.FromClaim(claim, player.getLocation().getBlockY(), VisualizationType.RestoreNature, player.getLocation());
Visualization.Apply(player, visualization);
if(player != null)
{
Claim claim = new Claim(lesserCorner, greaterCorner, "", new String[] {}, new String[] {}, new String[] {}, new String[] {}, null);
Visualization visualization = Visualization.FromClaim(claim, player.getLocation().getBlockY(), VisualizationType.RestoreNature, player.getLocation());
Visualization.Apply(player, visualization);
}
}
}

View File

@ -42,6 +42,7 @@ class RestoreNatureProcessingTask implements Runnable
private Location greaterBoundaryCorner;
private Player player; //absolutely must not be accessed. not thread safe.
private Biome biome;
private boolean creativeMode;
private int seaLevel;
private boolean aggressiveMode;
@ -49,7 +50,7 @@ class RestoreNatureProcessingTask implements Runnable
private ArrayList<Integer> notAllowedToHang; //natural blocks which don't naturally hang in their air
private ArrayList<Integer> playerBlocks; //a "complete" list of player-placed blocks. MUST BE MAINTAINED as patches introduce more
public RestoreNatureProcessingTask(BlockSnapshot[][][] snapshots, int miny, Environment environment, Biome biome, Location lesserBoundaryCorner, Location greaterBoundaryCorner, int seaLevel, boolean aggressiveMode, Player player)
public RestoreNatureProcessingTask(BlockSnapshot[][][] snapshots, int miny, Environment environment, Biome biome, Location lesserBoundaryCorner, Location greaterBoundaryCorner, int seaLevel, boolean aggressiveMode, boolean creativeMode, Player player)
{
this.snapshots = snapshots;
this.miny = miny;
@ -60,6 +61,7 @@ class RestoreNatureProcessingTask implements Runnable
this.seaLevel = seaLevel;
this.aggressiveMode = aggressiveMode;
this.player = player;
this.creativeMode = creativeMode;
this.notAllowedToHang = new ArrayList<Integer>();
this.notAllowedToHang.add(Material.DIRT.getId());
@ -150,6 +152,9 @@ class RestoreNatureProcessingTask implements Runnable
this.playerBlocks.add(Material.DIODE_BLOCK_ON.getId());
this.playerBlocks.add(Material.WEB.getId());
this.playerBlocks.add(Material.SPONGE.getId());
this.playerBlocks.add(Material.GRAVEL.getId());
this.playerBlocks.add(Material.EMERALD_BLOCK.getId());
this.playerBlocks.add(Material.SANDSTONE.getId());
//these are unnatural in the standard world, but not in the nether
if(this.environment != Environment.NETHER)
@ -169,25 +174,33 @@ class RestoreNatureProcessingTask implements Runnable
}
//these are unnatural in sandy biomes, but not elsewhere
if(this.biome == Biome.DESERT || this.biome == Biome.DESERT_HILLS || this.biome == Biome.BEACH || this.aggressiveMode)
if(this.biome == Biome.DESERT || this.biome == Biome.DESERT_HILLS || this.biome == Biome.BEACH || this.environment != Environment.NORMAL || this.aggressiveMode)
{
this.playerBlocks.add(Material.LEAVES.getId());
this.playerBlocks.add(Material.LOG.getId());
}
//in aggressive mode, also treat these blocks as user placed, to be removed
//in aggressive or creative world mode, also treat these blocks as user placed, to be removed
//this is helpful in the few cases where griefers intentionally use natural blocks to grief,
//like a single-block tower of iron ore or a giant penis constructed with logs
if(this.aggressiveMode)
//like a single-block tower of iron ore or a giant penis constructed with melons
if(this.aggressiveMode || this.creativeMode)
{
this.playerBlocks.add(Material.IRON_ORE.getId());
this.playerBlocks.add(Material.PUMPKIN.getId());
this.playerBlocks.add(Material.PUMPKIN_STEM.getId());
this.playerBlocks.add(Material.IRON_ORE.getId());
this.playerBlocks.add(Material.GOLD_ORE.getId());
this.playerBlocks.add(Material.DIAMOND_ORE.getId());
this.playerBlocks.add(Material.MELON_BLOCK.getId());
this.playerBlocks.add(Material.MELON_STEM.getId());
this.playerBlocks.add(Material.BEDROCK.getId());
this.playerBlocks.add(Material.GRAVEL.getId());
this.playerBlocks.add(Material.SANDSTONE.getId());
this.playerBlocks.add(Material.COAL_ORE.getId());
this.playerBlocks.add(Material.PUMPKIN.getId());
this.playerBlocks.add(Material.PUMPKIN_STEM.getId());
this.playerBlocks.add(Material.MELON.getId());
}
if(this.aggressiveMode)
{
this.playerBlocks.add(Material.LEAVES.getId());
this.playerBlocks.add(Material.VINE.getId());
}
}
@ -196,18 +209,24 @@ class RestoreNatureProcessingTask implements Runnable
{
//order is important!
//remove sandstone which appears to be unnatural
this.removeSandstone();
//remove any blocks which are definitely player placed
this.removePlayerBlocks();
//remove natural blocks which are unnaturally hanging in the air
this.removeHanging();
//reduce large outcroppings of stone, sandstone
this.reduceStone();
//reduce logs, except in jungle biomes
this.reduceLogs();
//remove natural blocks which are unnaturally stacked high
this.removeWallsAndTowers();
//cover surface stone and gravel with sand or grass, as the biome requires
this.coverSurfaceStone();
//fill unnatural thin trenches and single-block potholes
this.fillHolesAndTrenches();
@ -217,11 +236,161 @@ class RestoreNatureProcessingTask implements Runnable
//remove water/lava above sea level
this.removeDumpedFluids();
//cover over any gaping holes in creative mode worlds
if(this.creativeMode && this.environment == Environment.NORMAL)
{
this.fillBigHoles();
}
//cover surface stone and gravel with sand or grass, as the biome requires
this.coverSurfaceStone();
//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 fillBigHoles()
{
for(int x = 1; x < snapshots.length - 1; x++)
{
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][z].typeId == Material.LAVA.getId() || (this.snapshots[x][this.seaLevel][z].typeId == Material.WATER.getId() || this.snapshots[x][this.seaLevel][z].data != 0))
{
this.snapshots[x][this.seaLevel - 3][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][z].typeId == Material.LAVA.getId() || (this.snapshots[x][this.seaLevel][z].typeId == Material.WATER.getId() || this.snapshots[x][this.seaLevel][z].data != 0))
{
this.snapshots[x][this.seaLevel - 4][z].typeId = Material.STONE.getId();
}
}
}
}
//converts sandstone adjacent to sand to sand, and any other sandstone to air
private void removeSandstone()
{
for(int x = 1; x < snapshots.length - 1; x++)
{
for(int z = 1; z < snapshots[0][0].length - 1; z++)
{
for(int y = snapshots[0].length - 2; y > miny; y--)
{
if(snapshots[x][y][z].typeId != Material.SANDSTONE.getId()) continue;
BlockSnapshot leftBlock = this.snapshots[x + 1][y][z];
BlockSnapshot rightBlock = this.snapshots[x - 1][y][z];
BlockSnapshot upBlock = this.snapshots[x][y][z + 1];
BlockSnapshot downBlock = this.snapshots[x][y][z - 1];
BlockSnapshot underBlock = this.snapshots[x][y - 1][z];
BlockSnapshot aboveBlock = this.snapshots[x][y + 1][z];
//skip blocks which may cause a cave-in
if(aboveBlock.typeId == Material.SAND.getId() && underBlock.typeId == Material.AIR.getId()) continue;
//count adjacent non-air/non-leaf blocks
if( leftBlock.typeId == Material.SAND.getId() ||
rightBlock.typeId == Material.SAND.getId() ||
upBlock.typeId == Material.SAND.getId() ||
downBlock.typeId == Material.SAND.getId() ||
aboveBlock.typeId == Material.SAND.getId() ||
underBlock.typeId == Material.SAND.getId())
{
snapshots[x][y][z].typeId = Material.SAND.getId();
}
else
{
snapshots[x][y][z].typeId = Material.AIR.getId();
}
}
}
}
}
private void reduceStone()
{
for(int x = 1; x < snapshots.length - 1; x++)
{
for(int z = 1; z < snapshots[0][0].length - 1; z++)
{
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()))
{
BlockSnapshot leftBlock = this.snapshots[x + 1][thisy][z];
BlockSnapshot rightBlock = this.snapshots[x - 1][thisy][z];
BlockSnapshot upBlock = this.snapshots[x][thisy][z + 1];
BlockSnapshot downBlock = this.snapshots[x][thisy][z - 1];
//count adjacent non-air/non-leaf blocks
byte adjacentBlockCount = 0;
if(leftBlock.typeId != Material.AIR.getId() && leftBlock.typeId != Material.LEAVES.getId() && leftBlock.typeId != Material.VINE.getId())
{
adjacentBlockCount++;
}
if(rightBlock.typeId != Material.AIR.getId() && rightBlock.typeId != Material.LEAVES.getId() && rightBlock.typeId != Material.VINE.getId())
{
adjacentBlockCount++;
}
if(downBlock.typeId != Material.AIR.getId() && downBlock.typeId != Material.LEAVES.getId() && downBlock.typeId != Material.VINE.getId())
{
adjacentBlockCount++;
}
if(upBlock.typeId != Material.AIR.getId() && upBlock.typeId != Material.LEAVES.getId() && upBlock.typeId != Material.VINE.getId())
{
adjacentBlockCount++;
}
if(adjacentBlockCount < 3)
{
this.snapshots[x][thisy][z].typeId = Material.AIR.getId();
}
thisy--;
}
}
}
}
private void reduceLogs()
{
boolean jungleBiome = this.biome == Biome.JUNGLE || this.biome == Biome.JUNGLE_HILLS;
//scan all blocks above sea level
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 - 2; y < snapshots[0].length; y++)
{
BlockSnapshot block = snapshots[x][y][z];
//skip non-logs
if(block.typeId != Material.LOG.getId()) continue;
//if in jungle biome, skip jungle logs
if(jungleBiome && block.data == 3) continue;
//examine adjacent blocks for logs
BlockSnapshot leftBlock = this.snapshots[x + 1][y][z];
BlockSnapshot rightBlock = this.snapshots[x - 1][y][z];
BlockSnapshot upBlock = this.snapshots[x][y][z + 1];
BlockSnapshot downBlock = this.snapshots[x][y][z - 1];
//if any, remove the log
if(leftBlock.typeId == Material.LOG.getId() || rightBlock.typeId == Material.LOG.getId() || upBlock.typeId == Material.LOG.getId() || downBlock.typeId == Material.LOG.getId())
{
this.snapshots[x][y][z].typeId = Material.AIR.getId();
}
}
}
}
}
private void removePlayerBlocks()
{
int miny = this.miny;
@ -332,7 +501,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())
if(block.typeId == Material.STONE.getId() || block.typeId == Material.GRAVEL.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)
{
@ -516,6 +685,7 @@ class RestoreNatureProcessingTask implements Runnable
{
BlockSnapshot block = this.snapshots[x][y][z];
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))