This commit is contained in:
Ryan Hamshire 2012-09-10 16:38:48 -07:00
parent 008f313d09
commit 1f6223526b
9 changed files with 435 additions and 197 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.3
version: 6.4
commands:
abandonclaim:
description: Deletes a claim.

View File

@ -39,7 +39,6 @@ public class Claim
{
//two locations, which together define the boundaries of the claim
//note that the upper Y value is always ignored, because claims ALWAYS extend up to the sky
//IF MODIFIED, THE CLAIM DATA FILE'S NAME WILL CHANGE. ANY MODIFICATIONS MUST BE HANDLED VERY CAREFULLY
Location lesserBoundaryCorner;
Location greaterBoundaryCorner;
@ -436,6 +435,12 @@ public class Claim
//following a siege where the defender lost, the claim will allow everyone access for a time
if(this.doorsOpen) return null;
//admin claims need adminclaims permission only.
if(this.isAdminClaim())
{
if(player.hasPermission("griefprevention.adminclaims")) return null;
}
//claim owner and admins in ignoreclaims mode have access
if(this.ownerName.equals(player.getName()) || GriefPrevention.instance.dataStore.getPlayerData(player.getName()).ignoreClaims) return null;
@ -474,6 +479,12 @@ public class Claim
//owner and administrators in ignoreclaims mode have access
if(this.ownerName.equals(player.getName()) || GriefPrevention.instance.dataStore.getPlayerData(player.getName()).ignoreClaims) return null;
//admin claims need adminclaims permission only.
if(this.isAdminClaim())
{
if(player.hasPermission("griefprevention.adminclaims")) return null;
}
//check for explicit individual container or build permission
if(this.hasExplicitPermission(player, ClaimPermission.Inventory)) return null;
if(this.hasExplicitPermission(player, ClaimPermission.Build)) return null;
@ -750,4 +761,61 @@ public class Claim
return thisCorner.getWorld().getName().compareTo(otherCorner.getWorld().getName()) < 0;
}
long getPlayerInvestmentScore()
{
//decide which blocks will be considered player placed
Location lesserBoundaryCorner = this.getLesserBoundaryCorner();
ArrayList<Integer> playerBlocks = RestoreNatureProcessingTask.getPlayerBlocks(lesserBoundaryCorner.getWorld().getEnvironment(), lesserBoundaryCorner.getBlock().getBiome());
//scan the claim for player placed blocks
double score = 0;
boolean creativeMode = GriefPrevention.instance.creativeRulesApply(lesserBoundaryCorner);
for(int x = this.lesserBoundaryCorner.getBlockX(); x <= this.greaterBoundaryCorner.getBlockX(); x++)
{
for(int z = this.lesserBoundaryCorner.getBlockZ(); z <= this.greaterBoundaryCorner.getBlockZ(); z++)
{
int y = this.lesserBoundaryCorner.getBlockY();
for(; y < this.lesserBoundaryCorner.getWorld().getSeaLevel(); y++)
{
Block block = this.lesserBoundaryCorner.getWorld().getBlockAt(x, y, z);
if(playerBlocks.contains(block.getTypeId()))
{
if(block.getType() == Material.CHEST && !creativeMode)
{
score += 10;
}
else
{
score += .2;
}
}
}
for(; y < this.lesserBoundaryCorner.getWorld().getMaxHeight(); y++)
{
Block block = this.lesserBoundaryCorner.getWorld().getBlockAt(x, y, z);
if(playerBlocks.contains(block.getTypeId()))
{
if(block.getType() == Material.CHEST && !creativeMode)
{
score += 10;
}
else if(creativeMode && (block.getType() == Material.LAVA || block.getType() == Material.STATIONARY_LAVA))
{
score -= 10;
}
else
{
score += 1;
}
}
}
}
}
return (long)score;
}
}

View File

@ -0,0 +1,186 @@
/*
GriefPrevention Server Plugin for Minecraft
Copyright (C) 2011 Ryan Hamshire
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package me.ryanhamshire.GriefPrevention;
import java.util.Calendar;
import java.util.Random;
import org.bukkit.Chunk;
import org.bukkit.World;
//FEATURE: automatically remove claims owned by inactive players which:
//...aren't protecting much OR
//...are a free new player claim (and the player has no other claims) OR
//...because the player has been gone a REALLY long time, and that expiration has been configured in config.yml
//runs every 1 minute in the main thread
class CleanupUnusedClaimsTask implements Runnable
{
int nextClaimIndex;
CleanupUnusedClaimsTask()
{
//start scanning in a random spot
if(GriefPrevention.instance.dataStore.claims.size() == 0)
{
this.nextClaimIndex = 0;
}
else
{
Random randomNumberGenerator = new Random();
this.nextClaimIndex = randomNumberGenerator.nextInt(GriefPrevention.instance.dataStore.claims.size());
}
}
@Override
public void run()
{
//don't do anything when there are no claims
if(GriefPrevention.instance.dataStore.claims.size() == 0) return;
//wrap search around to beginning
if(this.nextClaimIndex >= GriefPrevention.instance.dataStore.claims.size()) this.nextClaimIndex = 0;
//decide which claim to check next
Claim claim = GriefPrevention.instance.dataStore.claims.get(this.nextClaimIndex++);
//skip administrative claims
if(claim.isAdminClaim()) return;
//get data for the player, especially last login timestamp
PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(claim.ownerName);
//determine area of the default chest claim
int areaOfDefaultClaim = 0;
if(GriefPrevention.instance.config_claims_automaticClaimsForNewPlayersRadius >= 0)
{
areaOfDefaultClaim = (int)Math.pow(GriefPrevention.instance.config_claims_automaticClaimsForNewPlayersRadius * 2 + 1, 2);
}
//if he's been gone at least a week, if he has ONLY the new player claim, it will be removed
Calendar sevenDaysAgo = Calendar.getInstance();
sevenDaysAgo.add(Calendar.DATE, -7);
boolean newPlayerClaimsExpired = sevenDaysAgo.getTime().after(playerData.lastLogin);
//if only one claim, and the player hasn't played in a week
if(newPlayerClaimsExpired && playerData.claims.size() == 1)
{
//if that's a chest claim, delete it
if(claim.getArea() <= areaOfDefaultClaim)
{
claim.removeSurfaceFluids(null);
GriefPrevention.instance.dataStore.deleteClaim(claim);
//if in a creative mode world, delete the claim
if(GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()))
{
GriefPrevention.instance.restoreClaim(claim, 0);
}
GriefPrevention.AddLogEntry(" " + claim.getOwnerName() + "'s new player claim expired.");
}
}
//if configured to always remove claims after some inactivity period without exceptions...
else if(GriefPrevention.instance.config_claims_expirationDays > 0)
{
Calendar earliestPermissibleLastLogin = Calendar.getInstance();
earliestPermissibleLastLogin.add(Calendar.DATE, -GriefPrevention.instance.config_claims_expirationDays);
if(earliestPermissibleLastLogin.getTime().after(playerData.lastLogin))
{
GriefPrevention.instance.dataStore.deleteClaimsForPlayer(claim.getOwnerName(), true);
GriefPrevention.AddLogEntry(" All of " + claim.getOwnerName() + "'s claims have expired.");
}
}
else
{
//if the player has been gone two weeks, scan claim content to assess player investment
Calendar fourteenDaysAgo = Calendar.getInstance();
fourteenDaysAgo.add(Calendar.DATE, -14);
boolean needsInvestmentScan = fourteenDaysAgo.getTime().after(playerData.lastLogin);
//avoid scanning large claims and administrative claims
if(claim.isAdminClaim() || claim.getWidth() > 25 || claim.getHeight() > 25) return;
//if creative mode or the claim owner has been away a long enough time, scan the claim content
if(needsInvestmentScan || GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()))
{
int minInvestment;
if(GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()))
{
minInvestment = 400;
}
else
{
minInvestment = 200;
}
long investmentScore = claim.getPlayerInvestmentScore();
boolean removeClaim = false;
//in creative mode, a build which is almost entirely lava above sea level will be automatically removed, even if the owner is an active player
//lava above the surface deducts 10 points per block from the investment score
//so 500 blocks of lava without anything built to offset all that potential mess would be cleaned up automatically
if(GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()) && investmentScore < -5000)
{
removeClaim = true;
}
//otherwise, the only way to get a claim automatically removed based on build investment is to be away for two weeks AND not build much of anything
else if(needsInvestmentScan && investmentScore < minInvestment)
{
removeClaim = true;
}
if(removeClaim)
{
GriefPrevention.instance.dataStore.deleteClaim(claim);
GriefPrevention.AddLogEntry("Removed " + claim.getOwnerName() + "'s unused claim @ " + GriefPrevention.getfriendlyLocationString(claim.getLesserBoundaryCorner()));
//if in a creative mode world, restore the claim area
if(GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()))
{
GriefPrevention.instance.restoreClaim(claim, 0);
}
}
}
}
//toss that player data out of the cache, it's probably not needed in memory right now
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
if(this.nextClaimIndex % 20 == 0)
{
World world = claim.getLesserBoundaryCorner().getWorld();
Chunk [] chunks = world.getLoadedChunks();
for(int i = 0; i < chunks.length; i++)
{
Chunk chunk = chunks[i];
chunk.unload(true, true);
}
System.gc();
}
}
}

View File

@ -70,63 +70,6 @@ public abstract class DataStore
GriefPrevention.AddLogEntry(playerNames.size() + " players have staked claims.");
//load each of these players and determine whether his claims should be cleaned up
for(int i = 0; i < playerNames.size(); i++)
{
String playerName = playerNames.get(i);
PlayerData playerData = this.getPlayerData(playerName);
int areaOfDefaultClaim = 0;
//determine area of the default chest claim
if(GriefPrevention.instance.config_claims_automaticClaimsForNewPlayersRadius >= 0)
{
areaOfDefaultClaim = (int)Math.pow(GriefPrevention.instance.config_claims_automaticClaimsForNewPlayersRadius * 2 + 1, 2);
}
//figure out how long the player has been away
Calendar sevenDaysAgo = Calendar.getInstance();
sevenDaysAgo.add(Calendar.DATE, -7);
boolean claimsExpired = sevenDaysAgo.getTime().after(playerData.lastLogin);
//if only one claim, and the player hasn't played in a week
if(claimsExpired && playerData.claims.size() == 1)
{
Claim claim = playerData.claims.get(0);
//if that's a chest claim, delete it
if(claim.getArea() <= areaOfDefaultClaim)
{
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.");
}
}
if(GriefPrevention.instance.config_claims_expirationDays > 0)
{
Calendar earliestPermissibleLastLogin = Calendar.getInstance();
earliestPermissibleLastLogin.add(Calendar.DATE, -GriefPrevention.instance.config_claims_expirationDays);
if(earliestPermissibleLastLogin.getTime().after(playerData.lastLogin))
{
this.deleteClaimsForPlayer(playerName, true);
GriefPrevention.AddLogEntry(" All of " + playerName + "'s claims have expired.");
}
}
//toss that player data out of the cache, it's not needed in memory right now
this.clearCachedPlayerData(playerName);
}
//load up all the messages from messages.yml
this.loadMessages();
@ -1013,6 +956,7 @@ 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);
//load the config file
FileConfiguration config = YamlConfiguration.loadConfiguration(new File(messagesFilePath));

View File

@ -44,30 +44,23 @@ class EquipShovelProcessingTask implements Runnable
PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getName());
//reset any work he might have been doing
playerData.lastShovelLocation = null;
playerData.claimResizing = null;
//always reset to basic claims mode
if(playerData.shovelMode != ShovelMode.Basic)
{
playerData.shovelMode = ShovelMode.Basic;
GriefPrevention.sendMessage(player, TextMode.Info, Messages.ShovelBasicClaimMode);
}
int remainingBlocks = playerData.getRemainingClaimBlocks();
//instruct him in the steps to create a claim
GriefPrevention.sendMessage(player, TextMode.Instr, Messages.RemainingBlocks, String.valueOf(remainingBlocks));
//demo link changes based on game mode
if(GriefPrevention.instance.creativeRulesApply(player.getLocation()))
//if in basic claims mode...
if(playerData.shovelMode == ShovelMode.Basic)
{
GriefPrevention.sendMessage(player, TextMode.Instr, Messages.CreativeBasicsDemoAdvertisement);
}
else
{
GriefPrevention.sendMessage(player, TextMode.Instr, Messages.SurvivalBasicsDemoAdvertisement);
//tell him how many claim blocks he has available
GriefPrevention.sendMessage(player, TextMode.Instr, Messages.RemainingBlocks, String.valueOf(remainingBlocks));
//link to a video demo of land claiming, based on world type
if(GriefPrevention.instance.creativeRulesApply(player.getLocation()))
{
GriefPrevention.sendMessage(player, TextMode.Instr, Messages.CreativeBasicsDemoAdvertisement);
}
else
{
GriefPrevention.sendMessage(player, TextMode.Instr, Messages.SurvivalBasicsDemoAdvertisement);
}
}
}
}

View File

@ -565,6 +565,10 @@ public class GriefPrevention extends JavaPlugin
EntityCleanupTask task = new EntityCleanupTask(0);
this.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, task, 20L);
//start recurring cleanup scan for unused claims belonging to inactive players
CleanupUnusedClaimsTask task2 = new CleanupUnusedClaimsTask();
this.getServer().getScheduler().scheduleSyncRepeatingTask(this, task2, 20L * 60 * 2, 20L * 60 * 5);
//register for events
PluginManager pluginManager = this.getServer().getPluginManager();
@ -614,7 +618,7 @@ public class GriefPrevention extends JavaPlugin
{
GriefPrevention.AddLogEntry("ERROR: Vault was unable to find a supported economy plugin. Either install a Vault-compatible economy plugin, or set both of the economy config variables to zero.");
}
}
}
}
//handles slash commands
@ -1041,7 +1045,11 @@ public class GriefPrevention extends JavaPlugin
else if(cmd.getName().equalsIgnoreCase("buyclaimblocks") && player != null)
{
//if economy is disabled, don't do anything
if(GriefPrevention.economy == null) return true;
if(GriefPrevention.economy == null)
{
GriefPrevention.sendMessage(player, TextMode.Err, Messages.BuySellNotConfigured);
return true;
}
//if purchase disabled, send error message
if(GriefPrevention.instance.config_economy_claimBlocksPurchaseCost == 0)
@ -1122,7 +1130,11 @@ public class GriefPrevention extends JavaPlugin
else if(cmd.getName().equalsIgnoreCase("sellclaimblocks") && player != null)
{
//if economy is disabled, don't do anything
if(GriefPrevention.economy == null) return true;
if(GriefPrevention.economy == null)
{
GriefPrevention.sendMessage(player, TextMode.Err, Messages.BuySellNotConfigured);
return true;
}
//if disabled, error message
if(GriefPrevention.instance.config_economy_claimBlocksSellValue == 0)
@ -2337,7 +2349,7 @@ 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(), chunk.getWorld().getBiome(lesserBoundaryCorner.getBlockX(), lesserBoundaryCorner.getBlockZ()), 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() + 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, UnclaimCleanupWarning
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
}

View File

@ -234,6 +234,8 @@ class PlayerEventHandler implements Listener
{
player.kickPlayer("");
}
return true;
}
//cancel any messages while at or above the third spam level and issue warnings
@ -521,7 +523,7 @@ class PlayerEventHandler implements Listener
PlayerData playerData = this.dataStore.getPlayerData(player.getName());
//if banned, add IP to the temporary IP ban list
if(player.isBanned())
if(player.isBanned() && playerData.ipAddress != null)
{
long now = Calendar.getInstance().getTimeInMillis();
this.tempBannedIps.add(new IpBanInfo(playerData.ipAddress, now + this.MILLISECONDS_IN_DAY, player.getName()));
@ -534,7 +536,7 @@ class PlayerEventHandler implements Listener
}
//make sure his data is all saved - he might have accrued some claim blocks while playing that were not saved immediately
this.dataStore.savePlayerData(player.getName(), playerData);
this.dataStore.savePlayerData(player.getName(), playerData);
this.onPlayerDisconnect(event.getPlayer(), event.getQuitMessage());
}
@ -762,6 +764,20 @@ class PlayerEventHandler implements Listener
ItemStack newItemStack = player.getInventory().getItem(event.getNewSlot());
if(newItemStack != null && newItemStack.getType() == GriefPrevention.instance.config_claims_modificationTool)
{
PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getName());
//always reset to basic claims mode
if(playerData.shovelMode != ShovelMode.Basic)
{
playerData.shovelMode = ShovelMode.Basic;
GriefPrevention.sendMessage(player, TextMode.Info, Messages.ShovelBasicClaimMode);
}
//reset any work he might have been doing
playerData.lastShovelLocation = null;
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
}
@ -878,7 +894,7 @@ class PlayerEventHandler implements Listener
try
{
clickedBlock = event.getClickedBlock(); //null returned here means interacting with air
if(clickedBlock == null)
if(clickedBlock == null || clickedBlock.getType() == Material.SNOW)
{
//try to find a far away non-air block along line of sight
HashSet<Byte> transparentMaterials = new HashSet<Byte>();
@ -1570,7 +1586,7 @@ class PlayerEventHandler implements Listener
{
GriefPrevention.sendMessage(player, TextMode.Err, Messages.CreateClaimFailOverlapOtherPlayer, claim.getOwnerName());
Visualization visualization = Visualization.FromClaim(claim, clickedBlock.getY(), VisualizationType.ErrorClaim, player.getLocation());
Visualization.Apply(player, visualization);
Visualization.Apply(player, visualization);
}
return;
@ -1592,6 +1608,10 @@ class PlayerEventHandler implements Listener
//remember it, and start him on the new claim
playerData.lastShovelLocation = clickedBlock.getLocation();
GriefPrevention.sendMessage(player, TextMode.Instr, Messages.ClaimStart);
//show him where he's working
Visualization visualization = Visualization.FromClaim(new Claim(clickedBlock.getLocation(), clickedBlock.getLocation(), "", new String[]{}, new String[]{}, new String[]{}, new String[]{}, null), clickedBlock.getY(), VisualizationType.RestoreNature, player.getLocation());
Visualization.Apply(player, visualization);
}
//otherwise, he's trying to finish creating a claim by setting the other boundary corner

View File

@ -75,110 +75,8 @@ class RestoreNatureProcessingTask implements Runnable
this.notAllowedToHang.add(Material.STONE.getId());
}
//NOTE on this list. why not make a list of natural blocks?
//answer: better to leave a few player blocks than to remove too many natural blocks. remember we're "restoring nature"
//a few extra player blocks can be manually removed, but it will be impossible to guess exactly which natural materials to use in manual repair of an overzealous block removal
this.playerBlocks = new ArrayList<Integer>();
this.playerBlocks.add(Material.FIRE.getId());
this.playerBlocks.add(Material.BED_BLOCK.getId());
this.playerBlocks.add(Material.WOOD.getId());
this.playerBlocks.add(Material.BOOKSHELF.getId());
this.playerBlocks.add(Material.BREWING_STAND.getId());
this.playerBlocks.add(Material.BRICK.getId());
this.playerBlocks.add(Material.COBBLESTONE.getId());
this.playerBlocks.add(Material.GLASS.getId());
this.playerBlocks.add(Material.LAPIS_BLOCK.getId());
this.playerBlocks.add(Material.DISPENSER.getId());
this.playerBlocks.add(Material.NOTE_BLOCK.getId());
this.playerBlocks.add(Material.POWERED_RAIL.getId());
this.playerBlocks.add(Material.DETECTOR_RAIL.getId());
this.playerBlocks.add(Material.PISTON_STICKY_BASE.getId());
this.playerBlocks.add(Material.PISTON_BASE.getId());
this.playerBlocks.add(Material.PISTON_EXTENSION.getId());
this.playerBlocks.add(Material.WOOL.getId());
this.playerBlocks.add(Material.PISTON_MOVING_PIECE.getId());
this.playerBlocks.add(Material.GOLD_BLOCK.getId());
this.playerBlocks.add(Material.IRON_BLOCK.getId());
this.playerBlocks.add(Material.DOUBLE_STEP.getId());
this.playerBlocks.add(Material.STEP.getId());
this.playerBlocks.add(Material.CROPS.getId());
this.playerBlocks.add(Material.TNT.getId());
this.playerBlocks.add(Material.MOSSY_COBBLESTONE.getId());
this.playerBlocks.add(Material.TORCH.getId());
this.playerBlocks.add(Material.FIRE.getId());
this.playerBlocks.add(Material.WOOD_STAIRS.getId());
this.playerBlocks.add(Material.CHEST.getId());
this.playerBlocks.add(Material.REDSTONE_WIRE.getId());
this.playerBlocks.add(Material.DIAMOND_BLOCK.getId());
this.playerBlocks.add(Material.WORKBENCH.getId());
this.playerBlocks.add(Material.SOIL.getId());
this.playerBlocks.add(Material.FURNACE.getId());
this.playerBlocks.add(Material.BURNING_FURNACE.getId());
this.playerBlocks.add(Material.WOODEN_DOOR.getId());
this.playerBlocks.add(Material.SIGN_POST.getId());
this.playerBlocks.add(Material.LADDER.getId());
this.playerBlocks.add(Material.RAILS.getId());
this.playerBlocks.add(Material.COBBLESTONE_STAIRS.getId());
this.playerBlocks.add(Material.WALL_SIGN.getId());
this.playerBlocks.add(Material.STONE_PLATE.getId());
this.playerBlocks.add(Material.LEVER.getId());
this.playerBlocks.add(Material.IRON_DOOR_BLOCK.getId());
this.playerBlocks.add(Material.WOOD_PLATE.getId());
this.playerBlocks.add(Material.REDSTONE_TORCH_ON.getId());
this.playerBlocks.add(Material.REDSTONE_TORCH_OFF.getId());
this.playerBlocks.add(Material.STONE_BUTTON.getId());
this.playerBlocks.add(Material.SNOW_BLOCK.getId());
this.playerBlocks.add(Material.JUKEBOX.getId());
this.playerBlocks.add(Material.FENCE.getId());
this.playerBlocks.add(Material.PORTAL.getId());
this.playerBlocks.add(Material.JACK_O_LANTERN.getId());
this.playerBlocks.add(Material.CAKE_BLOCK.getId());
this.playerBlocks.add(Material.DIODE_BLOCK_ON.getId());
this.playerBlocks.add(Material.DIODE_BLOCK_OFF.getId());
this.playerBlocks.add(Material.TRAP_DOOR.getId());
this.playerBlocks.add(Material.SMOOTH_BRICK.getId());
this.playerBlocks.add(Material.HUGE_MUSHROOM_1.getId());
this.playerBlocks.add(Material.HUGE_MUSHROOM_2.getId());
this.playerBlocks.add(Material.IRON_FENCE.getId());
this.playerBlocks.add(Material.THIN_GLASS.getId());
this.playerBlocks.add(Material.MELON_STEM.getId());
this.playerBlocks.add(Material.FENCE_GATE.getId());
this.playerBlocks.add(Material.BRICK_STAIRS.getId());
this.playerBlocks.add(Material.SMOOTH_STAIRS.getId());
this.playerBlocks.add(Material.ENCHANTMENT_TABLE.getId());
this.playerBlocks.add(Material.BREWING_STAND.getId());
this.playerBlocks.add(Material.CAULDRON.getId());
this.playerBlocks.add(Material.DIODE_BLOCK_ON.getId());
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)
{
this.playerBlocks.add(Material.NETHERRACK.getId());
this.playerBlocks.add(Material.SOUL_SAND.getId());
this.playerBlocks.add(Material.GLOWSTONE.getId());
this.playerBlocks.add(Material.NETHER_BRICK.getId());
this.playerBlocks.add(Material.NETHER_FENCE.getId());
this.playerBlocks.add(Material.NETHER_BRICK_STAIRS.getId());
}
//these are unnatural in the standard and nether worlds, but not in the end
if(this.environment != Environment.THE_END)
{
this.playerBlocks.add(Material.OBSIDIAN.getId());
}
//these are unnatural in sandy biomes, but not elsewhere
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());
}
this.playerBlocks.addAll(RestoreNatureProcessingTask.getPlayerBlocks(this.environment, this.biome));
//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,
@ -199,6 +97,8 @@ class RestoreNatureProcessingTask implements Runnable
if(this.aggressiveMode)
{
this.playerBlocks.add(Material.LEAVES.getId());
this.playerBlocks.add(Material.LOG.getId());
this.playerBlocks.add(Material.LEAVES.getId());
this.playerBlocks.add(Material.VINE.getId());
}
@ -215,15 +115,15 @@ class RestoreNatureProcessingTask implements Runnable
//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 hanging in the air
this.removeHanging();
//remove natural blocks which are unnaturally stacked high
this.removeWallsAndTowers();
@ -257,13 +157,13 @@ 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][z].typeId == Material.LAVA.getId() || (this.snapshots[x][this.seaLevel][z].typeId == Material.WATER.getId() || this.snapshots[x][this.seaLevel][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 - 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))
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))
{
this.snapshots[x][this.seaLevel - 4][z].typeId = Material.STONE.getId();
}
@ -696,4 +596,119 @@ class RestoreNatureProcessingTask implements Runnable
return y;
}
static ArrayList<Integer> getPlayerBlocks(Environment environment, Biome biome)
{
//NOTE on this list. why not make a list of natural blocks?
//answer: better to leave a few player blocks than to remove too many natural blocks. remember we're "restoring nature"
//a few extra player blocks can be manually removed, but it will be impossible to guess exactly which natural materials to use in manual repair of an overzealous block removal
ArrayList<Integer> playerBlocks = new ArrayList<Integer>();
playerBlocks.add(Material.FIRE.getId());
playerBlocks.add(Material.BED_BLOCK.getId());
playerBlocks.add(Material.WOOD.getId());
playerBlocks.add(Material.BOOKSHELF.getId());
playerBlocks.add(Material.BREWING_STAND.getId());
playerBlocks.add(Material.BRICK.getId());
playerBlocks.add(Material.COBBLESTONE.getId());
playerBlocks.add(Material.GLASS.getId());
playerBlocks.add(Material.LAPIS_BLOCK.getId());
playerBlocks.add(Material.DISPENSER.getId());
playerBlocks.add(Material.NOTE_BLOCK.getId());
playerBlocks.add(Material.POWERED_RAIL.getId());
playerBlocks.add(Material.DETECTOR_RAIL.getId());
playerBlocks.add(Material.PISTON_STICKY_BASE.getId());
playerBlocks.add(Material.PISTON_BASE.getId());
playerBlocks.add(Material.PISTON_EXTENSION.getId());
playerBlocks.add(Material.WOOL.getId());
playerBlocks.add(Material.PISTON_MOVING_PIECE.getId());
playerBlocks.add(Material.GOLD_BLOCK.getId());
playerBlocks.add(Material.IRON_BLOCK.getId());
playerBlocks.add(Material.DOUBLE_STEP.getId());
playerBlocks.add(Material.STEP.getId());
playerBlocks.add(Material.CROPS.getId());
playerBlocks.add(Material.TNT.getId());
playerBlocks.add(Material.MOSSY_COBBLESTONE.getId());
playerBlocks.add(Material.TORCH.getId());
playerBlocks.add(Material.FIRE.getId());
playerBlocks.add(Material.WOOD_STAIRS.getId());
playerBlocks.add(Material.CHEST.getId());
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());
playerBlocks.add(Material.SIGN_POST.getId());
playerBlocks.add(Material.LADDER.getId());
playerBlocks.add(Material.RAILS.getId());
playerBlocks.add(Material.COBBLESTONE_STAIRS.getId());
playerBlocks.add(Material.WALL_SIGN.getId());
playerBlocks.add(Material.STONE_PLATE.getId());
playerBlocks.add(Material.LEVER.getId());
playerBlocks.add(Material.IRON_DOOR_BLOCK.getId());
playerBlocks.add(Material.WOOD_PLATE.getId());
playerBlocks.add(Material.REDSTONE_TORCH_ON.getId());
playerBlocks.add(Material.REDSTONE_TORCH_OFF.getId());
playerBlocks.add(Material.STONE_BUTTON.getId());
playerBlocks.add(Material.SNOW_BLOCK.getId());
playerBlocks.add(Material.JUKEBOX.getId());
playerBlocks.add(Material.FENCE.getId());
playerBlocks.add(Material.PORTAL.getId());
playerBlocks.add(Material.JACK_O_LANTERN.getId());
playerBlocks.add(Material.CAKE_BLOCK.getId());
playerBlocks.add(Material.DIODE_BLOCK_ON.getId());
playerBlocks.add(Material.DIODE_BLOCK_OFF.getId());
playerBlocks.add(Material.TRAP_DOOR.getId());
playerBlocks.add(Material.SMOOTH_BRICK.getId());
playerBlocks.add(Material.HUGE_MUSHROOM_1.getId());
playerBlocks.add(Material.HUGE_MUSHROOM_2.getId());
playerBlocks.add(Material.IRON_FENCE.getId());
playerBlocks.add(Material.THIN_GLASS.getId());
playerBlocks.add(Material.MELON_STEM.getId());
playerBlocks.add(Material.FENCE_GATE.getId());
playerBlocks.add(Material.BRICK_STAIRS.getId());
playerBlocks.add(Material.SMOOTH_STAIRS.getId());
playerBlocks.add(Material.ENCHANTMENT_TABLE.getId());
playerBlocks.add(Material.BREWING_STAND.getId());
playerBlocks.add(Material.CAULDRON.getId());
playerBlocks.add(Material.DIODE_BLOCK_ON.getId());
playerBlocks.add(Material.DIODE_BLOCK_ON.getId());
playerBlocks.add(Material.WEB.getId());
playerBlocks.add(Material.SPONGE.getId());
playerBlocks.add(Material.GRAVEL.getId());
playerBlocks.add(Material.EMERALD_BLOCK.getId());
playerBlocks.add(Material.SANDSTONE.getId());
playerBlocks.add(Material.WOOD_STEP.getId());
playerBlocks.add(Material.WOOD_DOUBLE_STEP.getId());
playerBlocks.add(Material.ENDER_CHEST.getId());
//these are unnatural in the standard world, but not in the nether
if(environment != Environment.NETHER)
{
playerBlocks.add(Material.NETHERRACK.getId());
playerBlocks.add(Material.SOUL_SAND.getId());
playerBlocks.add(Material.GLOWSTONE.getId());
playerBlocks.add(Material.NETHER_BRICK.getId());
playerBlocks.add(Material.NETHER_FENCE.getId());
playerBlocks.add(Material.NETHER_BRICK_STAIRS.getId());
}
//these are unnatural in the standard and nether worlds, but not in the end
if(environment != Environment.THE_END)
{
playerBlocks.add(Material.OBSIDIAN.getId());
playerBlocks.add(Material.ENDER_STONE.getId());
playerBlocks.add(Material.ENDER_PORTAL_FRAME.getId());
}
//these are unnatural in sandy biomes, but not elsewhere
if(biome == Biome.DESERT || biome == Biome.DESERT_HILLS || biome == Biome.BEACH || environment != Environment.NORMAL)
{
playerBlocks.add(Material.LEAVES.getId());
playerBlocks.add(Material.LOG.getId());
}
return playerBlocks;
}
}