This commit is contained in:
Ryan Hamshire 2012-04-16 19:00:21 -07:00
parent e68fd63194
commit 5037847814
9 changed files with 181 additions and 85 deletions

View File

@ -1,17 +1,20 @@
name: GriefPrevention
main: me.ryanhamshire.GriefPrevention.GriefPrevention
softdepend: [Vault]
version: 3.2
version: 3.3.2
commands:
abandonclaim:
description: Deletes a claim.
usage: /abandonclaim
usage: /AbandonClaim
abandontoplevelclaim:
description: Deletes a claim and all its subdivisions.
usage: /AbandonTopLevelClaim
abandonallclaims:
description: Deletes ALL your claims.
usage: /AbandonAllClaims
trust:
description: Grants a player full access to your claim(s).
usage: /Trust <player> See also /UnTrust, /ContainerTrust, /AccessTrust, and /PermissionTrust.
usage: /Trust <player> Graants a player permission to build. See also /UnTrust, /ContainerTrust, /AccessTrust, and /PermissionTrust.
aliases: t
untrust:
description: Revokes a player's access to your claim(s).
@ -19,15 +22,15 @@ commands:
aliases: ut
containertrust:
description: Grants a player access to your containers.
usage: /ContainerTrust <player>
usage: /ContainerTrust <player>. Grants a player access to your inventory, bed, and buttons/levers.
aliases: ct
accesstrust:
description: Grants a player entry to your claim(s) and use of your bed.
usage: /AccessTrust <player>
usage: /AccessTrust <player>. Grants a player access to your bed, buttons, and levers.
aliases: at
permissiontrust:
description: Grants a player permission to grant his level of permission to others.
usage: /PermissionTrust <player>
usage: /PermissionTrust <player>. Permits a player to share his permission level with others.
aliases: pt
subdivideclaims:
description: Switches the shovel tool to subdivision mode, used to subdivide your claims.
@ -100,6 +103,7 @@ permissions:
griefprevention.adjustclaimblocks: true
griefprevention.deleteclaims: true
griefprevention.spam: true
griefprevention.lava: true
griefprevention.restorenature:
description: Grants permission to use /RestoreNature.
default: op

View File

@ -186,20 +186,18 @@ public class BlockEventHandler implements Listener
Player player = placeEvent.getPlayer();
Block block = placeEvent.getBlock();
//FEATURE: limit fire placement, to prevent PvP-by-fire and general fiery messes
//FEATURE: limit fire placement, to prevent PvP-by-fire
//if placed block is fire and pvp is off, block it apply limitations based on the block it's placed on
if(block.getType() == Material.FIRE && !block.getWorld().getPVP())
//if placed block is fire and pvp is off, apply rules for proximity to other players
if(block.getType() == Material.FIRE && !block.getWorld().getPVP() && !player.hasPermission("griefprevention.lava"))
{
List<Player> players = block.getWorld().getPlayers();
for(int i = 0; i < players.size(); i++)
{
Player otherPlayer = players.get(i);
Location location = otherPlayer.getLocation();
if(!otherPlayer.equals(player) && block.getY() <= location.getBlockY() - 1 && location.distanceSquared(block.getLocation()) < 9)
if(!otherPlayer.equals(player) && location.distanceSquared(block.getLocation()) < 9)
{
player.sendMessage(block.getY() + " " + otherPlayer.getLocation().getBlockY());
GriefPrevention.sendMessage(player, TextMode.Err, "You can't start a fire this close to " + otherPlayer.getName() + ".");
placeEvent.setCancelled(true);
return;
@ -349,25 +347,34 @@ public class BlockEventHandler implements Listener
}
}
//blocks are ignited ONLY by flint and steel (not by being near lava, open flames, etc)
//blocks are ignited ONLY by flint and steel (not by being near lava, open flames, etc), unless configured otherwise
@EventHandler(priority = EventPriority.HIGHEST)
public void onBlockIgnite (BlockIgniteEvent igniteEvent)
{
if(igniteEvent.getCause() != IgniteCause.FLINT_AND_STEEL) igniteEvent.setCancelled(true);
if(igniteEvent.getCause() != IgniteCause.FLINT_AND_STEEL && !GriefPrevention.instance.config_fireSpreads) igniteEvent.setCancelled(true);
}
//fire doesn't spread, but other blocks still do (mushrooms and vines, for example)
//fire doesn't spread unless configured to, but other blocks still do (mushrooms and vines, for example)
@EventHandler(priority = EventPriority.HIGHEST)
public void onBlockSpread (BlockSpreadEvent spreadEvent)
{
if(spreadEvent.getSource().getType() == Material.FIRE) spreadEvent.setCancelled(true);
if(spreadEvent.getSource().getType() == Material.FIRE && !GriefPrevention.instance.config_fireSpreads) spreadEvent.setCancelled(true);
}
//blocks are not destroyed by fire
//blocks are not destroyed by fire, unless configured to do so
@EventHandler(priority = EventPriority.HIGHEST)
public void onBlockBurn (BlockBurnEvent burnEvent)
{
burnEvent.setCancelled(true);
if(!GriefPrevention.instance.config_fireDestroys)
{
burnEvent.setCancelled(true);
}
//never burn claimed blocks, regardless of settings
if(this.dataStore.getClaimAt(burnEvent.getBlock().getLocation(), false, null) != null)
{
burnEvent.setCancelled(true);
}
}
//ensures fluids don't flow into claims, unless out of another claim where the owner is trusted to build in the receiving claim

View File

@ -515,6 +515,8 @@ public class Claim
//NOTE: if trying to understand this makes your head hurt, don't feel bad - it hurts mine too.
//try drawing pictures to visualize test cases.
if(!this.lesserBoundaryCorner.getWorld().equals(otherClaim.getLesserBoundaryCorner().getWorld())) return false;
//first, check the corners of this claim aren't inside any existing claims
if(otherClaim.contains(this.lesserBoundaryCorner, true, false)) return true;
if(otherClaim.contains(this.greaterBoundaryCorner, true, false)) return true;

View File

@ -843,7 +843,7 @@ public class DataStore
//why isn't this a "repeating" task?
//because depending on the status of the siege at the time the task runs, there may or may not be a reason to run the task again
SiegeCheckupTask task = new SiegeCheckupTask(siegeData);
siegeData.checkupTaskID = GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, task, 20L * 60);
siegeData.checkupTaskID = GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, task, 20L * 30);
}
//ends a siege

View File

@ -31,6 +31,10 @@ class DeliverClaimBlocksTask implements Runnable
{
Player [] players = GriefPrevention.instance.getServer().getOnlinePlayers();
//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;
//for each online player
for(int i = 0; i < players.length; i++)
{
@ -43,8 +47,8 @@ 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))
{
playerData.accruedClaimBlocks += GriefPrevention.instance.config_claims_blocksAccruedPerHour / 12;
{
playerData.accruedClaimBlocks += accruedBlocks;
//respect limits
if(playerData.accruedClaimBlocks > GriefPrevention.instance.config_claims_maxAccruedBlocks)

View File

@ -236,36 +236,39 @@ class EntityEventHandler implements Listener
return;
}
//FEATURE: prevent players who very recently participated in pvp combat from hiding inventory to protect it from looting
//FEATURE: prevent players who are in pvp combat from logging out to avoid being defeated
//FEATURE: prevent pvp in the first minute after spawn, and prevent pvp when one or both players have no inventory
Player defender = (Player)(event.getEntity());
PlayerData defenderData = this.dataStore.getPlayerData(((Player)event.getEntity()).getName());
PlayerData attackerData = this.dataStore.getPlayerData(attacker.getName());
long now = Calendar.getInstance().getTimeInMillis();
defenderData.lastPvpTimestamp = now;
defenderData.lastPvpPlayer = attacker.getName();
attackerData.lastPvpTimestamp = now;
attackerData.lastPvpPlayer = defender.getName();
//FEATURE: prevent pvp in the first minute after spawn, and prevent pvp when one or both players have no inventory
//otherwise if protecting spawning players
if(GriefPrevention.instance.config_pvp_protectFreshSpawns)
{
if(defenderData.pvpImmune)
{
event.setCancelled(true);
GriefPrevention.sendMessage(attacker, TextMode.Err, "You can't injure defenseless players.");
return;
}
if(attackerData.pvpImmune)
{
event.setCancelled(true);
GriefPrevention.sendMessage(attacker, TextMode.Err, "You can't fight someone while you're protected from PvP.");
return;
}
}
//FEATURE: prevent players who very recently participated in pvp combat from hiding inventory to protect it from looting
//FEATURE: prevent players who are in pvp combat from logging out to avoid being defeated
long now = Calendar.getInstance().getTimeInMillis();
defenderData.lastPvpTimestamp = now;
defenderData.lastPvpPlayer = attacker.getName();
attackerData.lastPvpTimestamp = now;
attackerData.lastPvpPlayer = defender.getName();
}
//FEATURE: protect claimed animals, boats, minecarts

View File

@ -97,6 +97,9 @@ public class GriefPrevention extends JavaPlugin
public boolean config_creepersDontDestroySurface; //whether creeper explosions near or above the surface destroy blocks
public boolean config_fireSpreads; //whether fire spreads outside of claims
public boolean config_fireDestroys; //whether fire destroys blocks outside of claims
//reference to the economy plugin, if economy integration is enabled
public static Economy economy = null;
@ -183,6 +186,9 @@ public class GriefPrevention extends JavaPlugin
this.config_creepersDontDestroySurface = config.getBoolean("GriefPrevention.CreepersDontDestroySurface", true);
this.config_fireSpreads = config.getBoolean("GriefPrevention.FireSpreads", false);
this.config_fireDestroys = config.getBoolean("GriefPrevention.FireDestroys", false);
//default for claims worlds list
ArrayList<String> defaultSiegeWorldNames = new ArrayList<String>();
@ -218,6 +224,7 @@ public class GriefPrevention extends JavaPlugin
this.config_siege_blocks.add(Material.GRAVEL);
this.config_siege_blocks.add(Material.SAND);
this.config_siege_blocks.add(Material.GLASS);
this.config_siege_blocks.add(Material.THIN_GLASS);
this.config_siege_blocks.add(Material.WOOD);
this.config_siege_blocks.add(Material.WOOL);
this.config_siege_blocks.add(Material.SNOW);
@ -286,7 +293,10 @@ public class GriefPrevention extends JavaPlugin
config.set("GriefPrevention.CreepersDontDestroySurface", this.config_creepersDontDestroySurface);
config.set("GriefPrevention.Siege.Enabled", siegeEnabledWorldNames);
config.set("GriefPrevention.FireSpreads", this.config_fireSpreads);
config.set("GriefPrevention.FireDestroys", this.config_fireDestroys);
config.set("GriefPrevention.Siege.Worlds", siegeEnabledWorldNames);
config.set("GriefPrevention.Siege.BreakableBlocks", breakableBlocksList);
try
@ -311,8 +321,11 @@ public class GriefPrevention extends JavaPlugin
//unless claim block accrual is disabled, start the recurring per 5 minute event to give claim blocks to online players
//20L ~ 1 second
DeliverClaimBlocksTask task = new DeliverClaimBlocksTask();
this.getServer().getScheduler().scheduleSyncRepeatingTask(this, task, 20L * 60 * 5, 20L * 60 * 5);
if(this.config_claims_blocksAccruedPerHour > 0)
{
DeliverClaimBlocksTask task = new DeliverClaimBlocksTask();
this.getServer().getScheduler().scheduleSyncRepeatingTask(this, task, 20L * 60 * 5, 20L * 60 * 5);
}
//register for events
PluginManager pluginManager = this.getServer().getPluginManager();
@ -378,35 +391,14 @@ public class GriefPrevention extends JavaPlugin
//abandonclaim
if(cmd.getName().equalsIgnoreCase("abandonclaim") && player != null)
{
//which claim is being abandoned?
Claim claim = this.dataStore.getClaimAt(player.getLocation(), true /*ignore height*/, null);
if(claim == null)
{
GriefPrevention.sendMessage(player, TextMode.Instr, "Stand in the claim you want to delete, or consider /AbandonAllClaims.");
}
//verify ownership
else if(claim.allowEdit(player) != null)
{
GriefPrevention.sendMessage(player, TextMode.Err, "This isn't your claim.");
}
else
{
//delete it
this.dataStore.deleteClaim(claim);
//tell the player how many claim blocks he has left
PlayerData playerData = this.dataStore.getPlayerData(player.getName());
int remainingBlocks = playerData.getRemainingClaimBlocks();
GriefPrevention.sendMessage(player, TextMode.Success, "Claim abandoned. You now have " + String.valueOf(remainingBlocks) + " available claim blocks.");
//revert any current visualization
Visualization.Revert(player);
}
return true;
}
this.abandonClaimHandler(player, false);
}
//abandontoplevelclaim
if(cmd.getName().equalsIgnoreCase("abandontoplevelclaim") && player != null)
{
return this.abandonClaimHandler(player, true);
}
//ignoreclaims
if(cmd.getName().equalsIgnoreCase("ignoreclaims") && player != null)
@ -1014,7 +1006,7 @@ public class GriefPrevention extends JavaPlugin
else if(cmd.getName().equalsIgnoreCase("siege") && player != null)
{
//error message for when siege mode is disabled
if(this.siegeEnabledForWorld(player.getWorld()))
if(!this.siegeEnabledForWorld(player.getWorld()))
{
GriefPrevention.sendMessage(player, TextMode.Err, "Siege is disabled here.");
return true;
@ -1070,8 +1062,22 @@ public class GriefPrevention extends JavaPlugin
return true;
}
//victim must not be pvp immune
if(defenderData.pvpImmune)
{
GriefPrevention.sendMessage(player, TextMode.Err, defender.getName() + " is defenseless. Go pick on somebody else.");
return true;
}
Claim defenderClaim = this.dataStore.getClaimAt(defender.getLocation(), false, null);
//defender must have some level of permission there to be protected
if(defenderClaim == null || defenderClaim.allowAccess(defender) != null)
{
GriefPrevention.sendMessage(player, TextMode.Err, defender.getName() + " isn't protected there.");
return true;
}
//attacker must be close to the claim he wants to siege
if(!defenderClaim.isNear(attacker.getLocation(), 25))
{
@ -1093,13 +1099,6 @@ public class GriefPrevention extends JavaPlugin
return true;
}
//defender must have some level of permission there to be protected
if(defenderClaim == null || defenderClaim.allowAccess(defender) != null)
{
GriefPrevention.sendMessage(player, TextMode.Err, defender.getName() + " isn't protected there.");
return true;
}
//can't be on cooldown
if(dataStore.onCooldown(attacker, defender, defenderClaim))
{
@ -1111,13 +1110,53 @@ public class GriefPrevention extends JavaPlugin
dataStore.startSiege(attacker, defender, defenderClaim);
//confirmation message for attacker, warning message for defender
GriefPrevention.sendMessage(player, TextMode.Warn, "You're under siege! If you log out now, you will die. You must defeat " + attacker.getName() + ", wait for him to give up, or escape.");
GriefPrevention.sendMessage(defender, TextMode.Warn, "You're under siege! If you log out now, you will die. You must defeat " + attacker.getName() + ", wait for him to give up, or escape.");
GriefPrevention.sendMessage(player, TextMode.Success, "The siege has begun! If you log out now, you will die. You must defeat " + defender.getName() + ", chase him away, or admit defeat and walk away.");
}
return false;
}
private boolean abandonClaimHandler(Player player, boolean deleteTopLevelClaim)
{
//which claim is being abandoned?
Claim claim = this.dataStore.getClaimAt(player.getLocation(), true /*ignore height*/, null);
if(claim == null)
{
GriefPrevention.sendMessage(player, TextMode.Instr, "Stand in the claim you want to delete, or consider /AbandonAllClaims.");
}
//verify ownership
else if(claim.allowEdit(player) != null)
{
GriefPrevention.sendMessage(player, TextMode.Err, "This isn't your claim.");
}
//warn if has children and we're not explicitly deleting a top level claim
else if(claim.children.size() > 0 && !deleteTopLevelClaim)
{
GriefPrevention.sendMessage(player, TextMode.Instr, "To delete a subdivision, stand inside it. Otherwise, use /AbandonTopLevelClaim to delete this claim and all subdivisions.");
return true;
}
else
{
//delete it
this.dataStore.deleteClaim(claim);
//tell the player how many claim blocks he has left
PlayerData playerData = this.dataStore.getPlayerData(player.getName());
int remainingBlocks = playerData.getRemainingClaimBlocks();
GriefPrevention.sendMessage(player, TextMode.Success, "Claim abandoned. You now have " + String.valueOf(remainingBlocks) + " available claim blocks.");
//revert any current visualization
Visualization.Revert(player);
}
return true;
}
//helper method keeps the trust commands consistent and eliminates duplicate code
private void handleTrustCommand(Player player, ClaimPermission permissionLevel, String recipientName)
{
@ -1314,8 +1353,7 @@ public class GriefPrevention extends JavaPlugin
playerData.pvpImmune = true;
//inform the player
GriefPrevention.sendMessage(player, TextMode.Info, "You have one minute of PvP protection, starting now.");
GriefPrevention.sendMessage(player, TextMode.Info, "After the minute, you can pick up items, but doing so will drop your PvP protection.");
GriefPrevention.sendMessage(player, TextMode.Success, "You're protected from attack by other players as long as your inventory is empty.");
}
//checks whether players can create claims in a world

View File

@ -40,6 +40,7 @@ import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.*;
import org.bukkit.event.player.PlayerLoginEvent.Result;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
@ -314,6 +315,36 @@ class PlayerEventHandler implements Listener
}
}
//when a player teleports
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerTeleport(PlayerTeleportEvent event)
{
//FEATURE: prevent teleport abuse to win sieges
//these rules only apply to non-ender-pearl teleportation
if(event.getCause() == TeleportCause.ENDER_PEARL) return;
Player player = event.getPlayer();
Location source = event.getFrom();
Claim sourceClaim = this.dataStore.getClaimAt(source, false, null);
if(sourceClaim != null && sourceClaim.siegeData != null)
{
GriefPrevention.sendMessage(player, TextMode.Err, "You can't teleport out of a besieged area.");
event.setCancelled(true);
return;
}
Location destination = event.getTo();
Claim destinationClaim = this.dataStore.getClaimAt(destination, false, null);
if(destinationClaim != null && destinationClaim.siegeData != null)
{
GriefPrevention.sendMessage(player, TextMode.Err, "You can't teleport into a besieged area.");
event.setCancelled(true);
return;
}
}
//when a player interacts with an entity...
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onPlayerInteractEntity(PlayerInteractEntityEvent event)
@ -397,10 +428,10 @@ class PlayerEventHandler implements Listener
PlayerData playerData = this.dataStore.getPlayerData(event.getPlayer().getName());
if(playerData.pvpImmune)
{
//if it's been at least a minute since the last time he spawned, don't pick up the item
//if it's been less than 10 seconds since the last time he spawned, don't pick up the item
long now = Calendar.getInstance().getTimeInMillis();
long elapsedSinceLastSpawn = now - playerData.lastSpawn;
if(elapsedSinceLastSpawn < 60000)
if(elapsedSinceLastSpawn < 10000)
{
event.setCancelled(true);
return;
@ -408,7 +439,7 @@ class PlayerEventHandler implements Listener
//otherwise take away his immunity. he may be armed now. at least, he's worth killing for some loot
playerData.pvpImmune = false;
GriefPrevention.sendMessage(player, TextMode.Warn, "You're now vulnerable to damage from other players.");
GriefPrevention.sendMessage(player, TextMode.Warn, "Now you can fight with other players.");
}
}
}
@ -481,7 +512,7 @@ class PlayerEventHandler implements Listener
public void onPlayerBucketEmpty (PlayerBucketEmptyEvent bucketEvent)
{
Player player = bucketEvent.getPlayer();
Block block = bucketEvent.getBlockClicked();
Block block = bucketEvent.getBlockClicked().getRelative(bucketEvent.getBlockFace());
int minLavaDistance = 10;
//if the bucket is being used in a claim
@ -520,7 +551,7 @@ class PlayerEventHandler implements Listener
}
//lava buckets can't be dumped near other players unless pvp is on
if(!block.getWorld().getPVP())
if(!block.getWorld().getPVP() && !player.hasPermission("griefprevention.lava"))
{
if(bucketEvent.getBucket() == Material.LAVA_BUCKET)
{
@ -531,8 +562,6 @@ class PlayerEventHandler implements Listener
Location location = otherPlayer.getLocation();
if(!otherPlayer.equals(player) && block.getY() >= location.getBlockY() - 1 && location.distanceSquared(block.getLocation()) < minLavaDistance * minLavaDistance)
{
player.sendMessage(block.getY() + " " + otherPlayer.getLocation().getBlockY());
GriefPrevention.sendMessage(player, TextMode.Err, "You can't place lava this close to " + otherPlayer.getName() + ".");
bucketEvent.setCancelled(true);
return;
@ -611,6 +640,7 @@ class PlayerEventHandler implements Listener
event.getAction() == Action.RIGHT_CLICK_BLOCK && (
clickedBlock.getState() instanceof InventoryHolder ||
clickedBlockType == Material.BREWING_STAND ||
clickedBlockType == Material.WORKBENCH ||
clickedBlockType == Material.JUKEBOX ||
clickedBlockType == Material.ENCHANTMENT_TABLE)))
{
@ -639,12 +669,20 @@ class PlayerEventHandler implements Listener
if(noContainersReason != null)
{
event.setCancelled(true);
player.sendMessage(noContainersReason);
GriefPrevention.sendMessage(player, TextMode.Err, noContainersReason);
}
}
//if the event hasn't been cancelled, then the player is allowed to use the container
//so drop any pvp protection
if(playerData.pvpImmune)
{
playerData.pvpImmune = false;
GriefPrevention.sendMessage(player, TextMode.Warn, "Now you can fight with other players.");
}
}
//apply rule for players trampling dirt (never allow it)
//apply rule for players trampling tilled soil back to dirt (never allow it)
//NOTE: that this event applies only to players. monsters and animals can still trample.
else if(event.getAction() == Action.PHYSICAL && clickedBlockType == Material.SOIL)
{

View File

@ -105,6 +105,6 @@ class SiegeCheckupTask implements Runnable
//schedules another checkup later
private void scheduleAnotherCheck()
{
this.siegeData.checkupTaskID = GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, this, 20L * 60);
this.siegeData.checkupTaskID = GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, this, 20L * 30);
}
}