3.2
This commit is contained in:
commit
e68fd63194
123
plugin.yml
Normal file
123
plugin.yml
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
name: GriefPrevention
|
||||
main: me.ryanhamshire.GriefPrevention.GriefPrevention
|
||||
softdepend: [Vault]
|
||||
version: 3.2
|
||||
commands:
|
||||
abandonclaim:
|
||||
description: Deletes a claim.
|
||||
usage: /abandonclaim
|
||||
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.
|
||||
aliases: t
|
||||
untrust:
|
||||
description: Revokes a player's access to your claim(s).
|
||||
usage: /UnTrust <player>
|
||||
aliases: ut
|
||||
containertrust:
|
||||
description: Grants a player access to your containers.
|
||||
usage: /ContainerTrust <player>
|
||||
aliases: ct
|
||||
accesstrust:
|
||||
description: Grants a player entry to your claim(s) and use of your bed.
|
||||
usage: /AccessTrust <player>
|
||||
aliases: at
|
||||
permissiontrust:
|
||||
description: Grants a player permission to grant his level of permission to others.
|
||||
usage: /PermissionTrust <player>
|
||||
aliases: pt
|
||||
subdivideclaims:
|
||||
description: Switches the shovel tool to subdivision mode, used to subdivide your claims.
|
||||
usage: /SubdivideClaims
|
||||
aliases: sc
|
||||
adjustbonusclaimblocks:
|
||||
description: Adds or subtracts bonus claim blocks for a player.
|
||||
usage: /AdjustBonusClaimBlocks <player> <amount>
|
||||
permission: griefprevention.adjustclaimblocks
|
||||
aliases: acb
|
||||
deleteclaim:
|
||||
description: Deletes the claim you're standing in, even if it's not your claim.
|
||||
usage: /DeleteClaim
|
||||
permission: griefprevention.deleteclaims
|
||||
aliases: dc
|
||||
deleteallclaims:
|
||||
description: Deletes all of another player's claims.
|
||||
usage: /DeleteAllClaims <player>
|
||||
permission: griefprevention.deleteclaims
|
||||
adminclaims:
|
||||
description: Switches the shovel tool to administrative claims mode.
|
||||
usage: /AdminClaims
|
||||
permission: griefprevention.adminclaims
|
||||
aliases: ac
|
||||
restorenature:
|
||||
description: Switches the shovel tool to restoration mode.
|
||||
usage: /RestoreNature
|
||||
permission: griefprevention.restorenature
|
||||
aliases: rn
|
||||
basicclaims:
|
||||
description: Switches the shovel tool back to basic claims mode.
|
||||
usage: /BasicClaims
|
||||
aliases: bc
|
||||
buyclaimblocks:
|
||||
description: Purchases additional claim blocks with server money. Doesn't work on servers without a Vault-compatible economy plugin.
|
||||
usage: /BuyClaimBlocks <numberOfBlocks>
|
||||
aliases: buyclaim
|
||||
sellclaimblocks:
|
||||
description: Sells your claim blocks for server money. Doesn't work on servers without a Vault-compatible economy plugin.
|
||||
usage: /SellClaimBlocks <numberOfBlocks>
|
||||
aliases: sellclaim
|
||||
trapped:
|
||||
description: Ejects you to nearby unclaimed land. Usable once per 8 hours.
|
||||
usage: /Trapped
|
||||
trustlist:
|
||||
description: Lists permissions for the claim you're standing in.
|
||||
usage: /TrustList
|
||||
siege:
|
||||
description: Initiates a siege versus another player.
|
||||
usage: /Siege <playerName>
|
||||
ignoreclaims:
|
||||
description: Toggles ignore claims mode.
|
||||
usage: /IgnoreClaims
|
||||
permission: griefprevention.ignoreclaims
|
||||
aliases: ic
|
||||
deletealladminclaims:
|
||||
description: Deletes all administrative claims.
|
||||
usage: /DeleteAllAdminClaims
|
||||
permission: adminclaims
|
||||
permissions:
|
||||
griefprevention.createclaims:
|
||||
description: Grants permission to create claims.
|
||||
default: op
|
||||
griefprevention.admin.*:
|
||||
description: Grants all administrative functionality.
|
||||
children:
|
||||
griefprevention.restorenature: true
|
||||
griefprevention.ignoreclaims: true
|
||||
griefprevention.adminclaims: true
|
||||
griefprevention.adjustclaimblocks: true
|
||||
griefprevention.deleteclaims: true
|
||||
griefprevention.spam: true
|
||||
griefprevention.restorenature:
|
||||
description: Grants permission to use /RestoreNature.
|
||||
default: op
|
||||
griefprevention.ignoreclaims:
|
||||
description: Grants permission to use /IgnoreClaims.
|
||||
default: op
|
||||
griefprevention.adminclaims:
|
||||
description: Grants permission to create administrative claims.
|
||||
default: op
|
||||
griefprevention.deleteclaims:
|
||||
description: Grants permission to delete other players' claims.
|
||||
default: op
|
||||
griefprevention.adjustclaimblocks:
|
||||
description: Grants permission to add or remove bonus blocks from a player's account.
|
||||
default: op
|
||||
griefprevention.spam:
|
||||
description: Grants permission to log in, send messages, and send commands rapidly.
|
||||
default: op
|
||||
griefprevention.lava:
|
||||
description: Grants permission to place lava near the surface and outside of claims.
|
||||
default: op
|
||||
405
src/me/ryanhamshire/GriefPrevention/BlockEventHandler.java
Normal file
405
src/me/ryanhamshire/GriefPrevention/BlockEventHandler.java
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
/*
|
||||
GriefPrevention Server Plugin for Minecraft
|
||||
Copyright (C) 2012 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.List;
|
||||
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.Chest;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.block.BlockBurnEvent;
|
||||
import org.bukkit.event.block.BlockDamageEvent;
|
||||
import org.bukkit.event.block.BlockFromToEvent;
|
||||
import org.bukkit.event.block.BlockIgniteEvent;
|
||||
import org.bukkit.event.block.BlockIgniteEvent.IgniteCause;
|
||||
import org.bukkit.event.block.BlockPistonExtendEvent;
|
||||
import org.bukkit.event.block.BlockPistonRetractEvent;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.block.BlockSpreadEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
|
||||
//event handlers related to blocks
|
||||
public class BlockEventHandler implements Listener
|
||||
{
|
||||
//convenience reference to singleton datastore
|
||||
private DataStore dataStore;
|
||||
|
||||
//boring typical constructor
|
||||
public BlockEventHandler(DataStore dataStore)
|
||||
{
|
||||
this.dataStore = dataStore;
|
||||
}
|
||||
|
||||
//when a block is damaged...
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onBlockDamaged(BlockDamageEvent event)
|
||||
{
|
||||
Block block = event.getBlock();
|
||||
Player player = event.getPlayer();
|
||||
|
||||
//only care about player-damaged blocks
|
||||
if(player == null) return;
|
||||
|
||||
//FEATURE: players may add items to a chest they don't have permission for by hitting it
|
||||
|
||||
//if it's a chest
|
||||
if(block.getType() == Material.CHEST)
|
||||
{
|
||||
//only care about non-creative mode players, since those would outright break the box in one hit
|
||||
if(player.getGameMode() == GameMode.CREATIVE) return;
|
||||
|
||||
//only care if the player has an itemstack in hand
|
||||
PlayerInventory playerInventory = player.getInventory();
|
||||
ItemStack stackInHand = playerInventory.getItemInHand();
|
||||
if(stackInHand == null || stackInHand.getType() == Material.AIR) return;
|
||||
|
||||
//only care if the chest is in a claim, and the player does not have access to the chest
|
||||
Claim claim = this.dataStore.getClaimAt(block.getLocation(), false, null);
|
||||
if(claim == null || claim.allowContainers(player) == null) return;
|
||||
|
||||
//if the player is under siege, he can't give away items
|
||||
PlayerData playerData = this.dataStore.getPlayerData(event.getPlayer().getName());
|
||||
if(playerData.siegeData != null)
|
||||
{
|
||||
GriefPrevention.sendMessage(player, TextMode.Err, "You can't give away items while involved in a siege.");
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
//NOTE: to eliminate accidental give-aways, first hit on a chest displays a confirmation message
|
||||
//subsequent hits donate item to the chest
|
||||
|
||||
//if first time damaging this chest, show confirmation message
|
||||
if(playerData.lastChestDamageLocation == null || !block.getLocation().equals(playerData.lastChestDamageLocation))
|
||||
{
|
||||
//remember this location
|
||||
playerData.lastChestDamageLocation = block.getLocation();
|
||||
|
||||
//give the player instructions
|
||||
GriefPrevention.sendMessage(player, TextMode.Instr, "To give away the item(s) in your hand, left-click the chest again.");
|
||||
}
|
||||
|
||||
//otherwise, try to donate the item stack in hand
|
||||
else
|
||||
{
|
||||
//look for empty slot in chest
|
||||
Chest chest = (Chest)block.getState();
|
||||
Inventory chestInventory = chest.getInventory();
|
||||
int availableSlot = chestInventory.firstEmpty();
|
||||
|
||||
//if there isn't one
|
||||
if(availableSlot < 0)
|
||||
{
|
||||
//tell the player and stop here
|
||||
GriefPrevention.sendMessage(player, TextMode.Err, "This chest is full.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//otherwise, transfer item stack from player to chest
|
||||
//NOTE: Inventory.addItem() is smart enough to add items to existing stacks, making filling a chest with garbage as a grief very difficult
|
||||
chestInventory.addItem(stackInHand);
|
||||
playerInventory.setItemInHand(new ItemStack(Material.AIR));
|
||||
|
||||
//and confirm for the player
|
||||
GriefPrevention.sendMessage(player, TextMode.Success, "Item(s) transferred to chest!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//when a player breaks a block...
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||
public void onBlockBreak(BlockBreakEvent breakEvent)
|
||||
{
|
||||
Player player = breakEvent.getPlayer();
|
||||
Block block = breakEvent.getBlock();
|
||||
PlayerData playerData = this.dataStore.getPlayerData(player.getName());
|
||||
Claim claim = this.dataStore.getClaimAt(block.getLocation(), true, playerData.lastClaim);
|
||||
|
||||
//if there's a claim here
|
||||
if(claim != null)
|
||||
{
|
||||
//cache the claim for later reference
|
||||
playerData.lastClaim = claim;
|
||||
|
||||
//check permissions
|
||||
String noBuildReason = claim.allowBreak(player, block.getType());
|
||||
|
||||
//if permission to break and breaking UNDER the claim
|
||||
if(block.getY() < claim.lesserBoundaryCorner.getBlockY())
|
||||
{
|
||||
if(noBuildReason == null)
|
||||
{
|
||||
//extend the claim downward beyond the breakage point
|
||||
this.dataStore.extendClaim(claim, claim.getLesserBoundaryCorner().getBlockY() - GriefPrevention.instance.config_claims_claimsExtendIntoGroundDistance);
|
||||
}
|
||||
}
|
||||
|
||||
//otherwise if not allowed to break blocks here, tell the player why
|
||||
else if(noBuildReason != null)
|
||||
{
|
||||
breakEvent.setCancelled(true);
|
||||
GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//FEATURE: automatically clean up hanging treetops
|
||||
//if it's a log
|
||||
if(block.getType() == Material.LOG && GriefPrevention.instance.config_trees_removeFloatingTreetops)
|
||||
{
|
||||
//run the specialized code for treetop removal (see below)
|
||||
GriefPrevention.instance.handleLogBroken(block);
|
||||
}
|
||||
}
|
||||
|
||||
//when a player places a block...
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||
public void onBlockPlace(BlockPlaceEvent placeEvent)
|
||||
{
|
||||
Player player = placeEvent.getPlayer();
|
||||
Block block = placeEvent.getBlock();
|
||||
|
||||
//FEATURE: limit fire placement, to prevent PvP-by-fire and general fiery messes
|
||||
|
||||
//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())
|
||||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if the block is being placed within an existing claim
|
||||
PlayerData playerData = this.dataStore.getPlayerData(player.getName());
|
||||
Claim claim = this.dataStore.getClaimAt(block.getLocation(), true, playerData.lastClaim);
|
||||
if(claim != null)
|
||||
{
|
||||
playerData.lastClaim = claim;
|
||||
String noBuildReason = claim.allowBuild(player);
|
||||
|
||||
//if the player has permission for the claim and he's placing UNDER the claim
|
||||
if(block.getY() < claim.lesserBoundaryCorner.getBlockY())
|
||||
{
|
||||
if(noBuildReason == null)
|
||||
{
|
||||
//extend the claim downward
|
||||
this.dataStore.extendClaim(claim, claim.getLesserBoundaryCorner().getBlockY() - GriefPrevention.instance.config_claims_claimsExtendIntoGroundDistance);
|
||||
}
|
||||
}
|
||||
|
||||
//otherwise if he doesn't have permission, tell him why
|
||||
else if(noBuildReason != null)
|
||||
{
|
||||
placeEvent.setCancelled(true);
|
||||
GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason);
|
||||
}
|
||||
}
|
||||
|
||||
//FEATURE: automatically create a claim when a player who has no claims places a chest
|
||||
|
||||
//otherwise if there's no claim, the player is placing a chest, and new player automatic claims are enabled
|
||||
else if(block.getType() == Material.CHEST && GriefPrevention.instance.config_claims_automaticClaimsForNewPlayersRadius > -1 && GriefPrevention.instance.claimsEnabledForWorld(block.getWorld()))
|
||||
{
|
||||
//if the chest is too deep underground, don't create the claim and explain why
|
||||
if(GriefPrevention.instance.config_claims_preventTheft && block.getY() < GriefPrevention.instance.config_claims_maxDepth)
|
||||
{
|
||||
GriefPrevention.sendMessage(player, TextMode.Warn, "This chest can't be protected because it's too deep underground. Consider moving it.");
|
||||
return;
|
||||
}
|
||||
|
||||
int radius = GriefPrevention.instance.config_claims_automaticClaimsForNewPlayersRadius;
|
||||
|
||||
//if the player doesn't have any claims yet, automatically create a claim centered at the chest
|
||||
if(playerData.claims.size() == 0)
|
||||
{
|
||||
//radius == 0 means protect ONLY the chest
|
||||
if(GriefPrevention.instance.config_claims_automaticClaimsForNewPlayersRadius == 0)
|
||||
{
|
||||
this.dataStore.createClaim(block.getWorld(), block.getX(), block.getX(), block.getY(), block.getY(), block.getZ(), block.getZ(), player.getName(), null);
|
||||
GriefPrevention.sendMessage(player, TextMode.Success, "This chest is protected.");
|
||||
}
|
||||
|
||||
//otherwise, create a claim in the area around the chest
|
||||
else
|
||||
{
|
||||
//as long as the automatic claim overlaps another existing claim, shrink it
|
||||
//note that since the player had permission to place the chest, at the very least, the automatic claim will include the chest
|
||||
while(radius >= 0 && !this.dataStore.createClaim(block.getWorld(),
|
||||
block.getX() - radius, block.getX() + radius,
|
||||
block.getY() - GriefPrevention.instance.config_claims_claimsExtendIntoGroundDistance, block.getY(),
|
||||
block.getZ() - radius, block.getZ() + radius,
|
||||
player.getName(),
|
||||
null).succeeded)
|
||||
{
|
||||
radius--;
|
||||
}
|
||||
|
||||
//notify and explain to player
|
||||
GriefPrevention.sendMessage(player, TextMode.Success, "This chest and nearby blocks are protected from breakage and theft. The gold and glowstone blocks mark the protected area.");
|
||||
|
||||
//show the player the protected area
|
||||
Claim newClaim = this.dataStore.getClaimAt(block.getLocation(), false, null);
|
||||
Visualization visualization = Visualization.FromClaim(newClaim, block.getY(), VisualizationType.Claim);
|
||||
Visualization.Apply(player, visualization);
|
||||
}
|
||||
|
||||
//instructions for using /trust
|
||||
GriefPrevention.sendMessage(player, TextMode.Instr, "Use the /trust command to grant other players access.");
|
||||
|
||||
//unless special permission is required to create a claim with the shovel, educate the player about the shovel
|
||||
if(!GriefPrevention.instance.config_claims_creationRequiresPermission)
|
||||
{
|
||||
GriefPrevention.sendMessage(player, TextMode.Instr, "To claim more land, use a golden shovel.");
|
||||
}
|
||||
}
|
||||
|
||||
//check to see if this chest is in a claim, and warn when it isn't
|
||||
if(GriefPrevention.instance.config_claims_preventTheft && this.dataStore.getClaimAt(block.getLocation(), false, playerData.lastClaim) == null)
|
||||
{
|
||||
GriefPrevention.sendMessage(player, TextMode.Warn, "This chest is NOT protected. Consider expanding an existing claim or creating a new one.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//blocks "pushing" other players' blocks around (pistons)
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||
public void onBlockPistonExtend (BlockPistonExtendEvent event)
|
||||
{
|
||||
//who owns the piston, if anyone?
|
||||
String pistonClaimOwnerName = "_";
|
||||
Claim claim = this.dataStore.getClaimAt(event.getBlock().getLocation(), false, null);
|
||||
if(claim != null) pistonClaimOwnerName = claim.getOwnerName();
|
||||
|
||||
//which blocks are being pushed?
|
||||
List<Block> blocks = event.getBlocks();
|
||||
for(int i = 0; i < blocks.size(); i++)
|
||||
{
|
||||
//if ANY of the pushed blocks are owned by someone other than the piston owner, cancel the event
|
||||
Block block = blocks.get(i);
|
||||
claim = this.dataStore.getClaimAt(block.getLocation(), false, null);
|
||||
if(claim != null && !claim.getOwnerName().equals(pistonClaimOwnerName))
|
||||
{
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//blocks theft by pulling blocks out of a claim (again pistons)
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||
public void onBlockPistonRetract (BlockPistonRetractEvent event)
|
||||
{
|
||||
//we only care about sticky pistons
|
||||
if(!event.isSticky()) return;
|
||||
|
||||
//who owns the moving block, if anyone?
|
||||
String movingBlockOwnerName = "_";
|
||||
Claim movingBlockClaim = this.dataStore.getClaimAt(event.getRetractLocation(), false, null);
|
||||
if(movingBlockClaim != null) movingBlockOwnerName = movingBlockClaim.getOwnerName();
|
||||
|
||||
//who owns the piston, if anyone?
|
||||
String pistonOwnerName = "_";
|
||||
Location pistonLocation = event.getBlock().getLocation();
|
||||
Claim pistonClaim = this.dataStore.getClaimAt(pistonLocation, false, null);
|
||||
if(pistonClaim != null) pistonOwnerName = pistonClaim.getOwnerName();
|
||||
|
||||
//if there are owners for the blocks, they must be the same player
|
||||
//otherwise cancel the event
|
||||
if(!pistonOwnerName.equals(movingBlockOwnerName))
|
||||
{
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
//blocks are ignited ONLY by flint and steel (not by being near lava, open flames, etc)
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onBlockIgnite (BlockIgniteEvent igniteEvent)
|
||||
{
|
||||
if(igniteEvent.getCause() != IgniteCause.FLINT_AND_STEEL) igniteEvent.setCancelled(true);
|
||||
}
|
||||
|
||||
//fire doesn't spread, 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);
|
||||
}
|
||||
|
||||
//blocks are not destroyed by fire
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onBlockBurn (BlockBurnEvent burnEvent)
|
||||
{
|
||||
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
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||
public void onBlockFromTo (BlockFromToEvent spreadEvent)
|
||||
{
|
||||
//where to?
|
||||
Block toBlock = spreadEvent.getToBlock();
|
||||
Claim toClaim = this.dataStore.getClaimAt(toBlock.getLocation(), false, null);
|
||||
|
||||
//if spreading into a claim
|
||||
if(toClaim != null)
|
||||
{
|
||||
//from where?
|
||||
Block fromBlock = spreadEvent.getBlock();
|
||||
Claim fromClaim = this.dataStore.getClaimAt(fromBlock.getLocation(), false, null);
|
||||
|
||||
//who owns the spreading block, if anyone?
|
||||
OfflinePlayer fromOwner = null;
|
||||
if(fromClaim != null)
|
||||
{
|
||||
//if it's within the same claim, allow it
|
||||
if(fromClaim == toClaim) return;
|
||||
|
||||
fromOwner = GriefPrevention.instance.getServer().getOfflinePlayer(fromClaim.ownerName);
|
||||
}
|
||||
|
||||
//cancel unless the owner of the spreading block is allowed to build in the receiving claim
|
||||
if(fromOwner == null || fromOwner.getPlayer() == null || toClaim.allowBuild(fromOwner.getPlayer()) != null)
|
||||
{
|
||||
spreadEvent.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/me/ryanhamshire/GriefPrevention/BlockSnapshot.java
Normal file
37
src/me/ryanhamshire/GriefPrevention/BlockSnapshot.java
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
GriefPrevention Server Plugin for Minecraft
|
||||
Copyright (C) 2012 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 org.bukkit.Location;
|
||||
|
||||
//basically, just a few data points from a block conveniently encapsulated in a class
|
||||
//this is used only by the RestoreNature code
|
||||
public class BlockSnapshot
|
||||
{
|
||||
public Location location;
|
||||
public int typeId;
|
||||
public byte data;
|
||||
|
||||
public BlockSnapshot(Location location, int typeId, byte data)
|
||||
{
|
||||
this.location = location;
|
||||
this.typeId = typeId;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
554
src/me/ryanhamshire/GriefPrevention/Claim.java
Normal file
554
src/me/ryanhamshire/GriefPrevention/Claim.java
Normal file
|
|
@ -0,0 +1,554 @@
|
|||
/*
|
||||
GriefPrevention Server Plugin for Minecraft
|
||||
Copyright (C) 2012 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.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
//represents a player claim
|
||||
//creating an instance doesn't make an effective claim
|
||||
//only claims which have been added to the datastore have any effect
|
||||
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;
|
||||
|
||||
//modification date. this comes from the file timestamp during load, and is updated with runtime changes
|
||||
public Date modifiedDate;
|
||||
|
||||
//ownername. for admin claims, this is the empty string
|
||||
//use getOwnerName() to get a friendly name (will be "an administrator" for admin claims)
|
||||
public String ownerName;
|
||||
|
||||
//list of players who (beyond the claim owner) have permission to grant permissions in this claim
|
||||
public ArrayList<String> managers = new ArrayList<String>();
|
||||
|
||||
//permissions for this claim, see ClaimPermission class
|
||||
private HashMap<String, ClaimPermission> playerNameToClaimPermissionMap = new HashMap<String, ClaimPermission>();
|
||||
|
||||
//whether or not this claim is in the data store
|
||||
//if a claim instance isn't in the data store, it isn't "active" - players can't interract with it
|
||||
//why keep this? so that claims which have been removed from the data store can be correctly
|
||||
//ignored even though they may have references floating around
|
||||
public boolean inDataStore = false;
|
||||
|
||||
//parent claim
|
||||
//only used for claim subdivisions. top level claims have null here
|
||||
public Claim parent = null;
|
||||
|
||||
//children (subdivisions)
|
||||
//note subdivisions themselves never have children
|
||||
public ArrayList<Claim> children = new ArrayList<Claim>();
|
||||
|
||||
//information about a siege involving this claim. null means no siege is impacting this claim
|
||||
public SiegeData siegeData = null;
|
||||
|
||||
//following a siege, buttons/levers are unlocked temporarily. this represents that state
|
||||
public boolean doorsOpen = false;
|
||||
|
||||
//whether or not this is an administrative claim
|
||||
//administrative claims are created and maintained by players with the griefprevention.adminclaims permission.
|
||||
public boolean isAdminClaim()
|
||||
{
|
||||
return this.ownerName.isEmpty();
|
||||
}
|
||||
|
||||
//basic constructor, just notes the creation time
|
||||
//see above declarations for other defaults
|
||||
Claim()
|
||||
{
|
||||
this.modifiedDate = Calendar.getInstance().getTime();
|
||||
}
|
||||
|
||||
//players may only siege someone when he's not in an admin claim
|
||||
//and when he has some level of permission in the claim
|
||||
public boolean canSiege(Player defender)
|
||||
{
|
||||
if(this.isAdminClaim()) return false;
|
||||
|
||||
if(this.allowAccess(defender) != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//main constructor. note that only creating a claim instance does nothing - a claim must be added to the data store to be effective
|
||||
Claim(Location lesserBoundaryCorner, Location greaterBoundaryCorner, String ownerName, String [] builderNames, String [] containerNames, String [] accessorNames, String [] managerNames)
|
||||
{
|
||||
//modification date
|
||||
this.modifiedDate = Calendar.getInstance().getTime();
|
||||
|
||||
//store corners
|
||||
this.lesserBoundaryCorner = lesserBoundaryCorner;
|
||||
this.greaterBoundaryCorner = greaterBoundaryCorner;
|
||||
|
||||
//if trying to create a claim under the max depth, auto-correct y values
|
||||
if(this.lesserBoundaryCorner.getBlockY() < GriefPrevention.instance.config_claims_maxDepth)
|
||||
{
|
||||
this.lesserBoundaryCorner.setY(GriefPrevention.instance.config_claims_maxDepth);
|
||||
}
|
||||
|
||||
if(this.greaterBoundaryCorner.getBlockY() < GriefPrevention.instance.config_claims_maxDepth)
|
||||
{
|
||||
this.greaterBoundaryCorner.setY(GriefPrevention.instance.config_claims_maxDepth);
|
||||
}
|
||||
|
||||
//owner
|
||||
this.ownerName = ownerName;
|
||||
|
||||
//other permissions
|
||||
for(int i = 0; i < builderNames.length; i++)
|
||||
{
|
||||
String name = builderNames[i];
|
||||
if(name != null && !name.isEmpty())
|
||||
{
|
||||
this.playerNameToClaimPermissionMap.put(name, ClaimPermission.Build);
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < containerNames.length; i++)
|
||||
{
|
||||
String name = containerNames[i];
|
||||
if(name != null && !name.isEmpty())
|
||||
{
|
||||
this.playerNameToClaimPermissionMap.put(name, ClaimPermission.Inventory);
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < accessorNames.length; i++)
|
||||
{
|
||||
String name = accessorNames[i];
|
||||
if(name != null && !name.isEmpty())
|
||||
{
|
||||
this.playerNameToClaimPermissionMap.put(name, ClaimPermission.Access);
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < managerNames.length; i++)
|
||||
{
|
||||
String name = managerNames[i];
|
||||
if(name != null && !name.isEmpty())
|
||||
{
|
||||
this.managers.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//measurements. all measurements are in blocks
|
||||
public int getArea()
|
||||
{
|
||||
int claimWidth = this.greaterBoundaryCorner.getBlockX() - this.lesserBoundaryCorner.getBlockX() + 1;
|
||||
int claimHeight = this.greaterBoundaryCorner.getBlockZ() - this.lesserBoundaryCorner.getBlockZ() + 1;
|
||||
|
||||
return claimWidth * claimHeight;
|
||||
}
|
||||
|
||||
public int getWidth()
|
||||
{
|
||||
return this.greaterBoundaryCorner.getBlockX() - this.lesserBoundaryCorner.getBlockX() + 1;
|
||||
}
|
||||
|
||||
public int getHeight()
|
||||
{
|
||||
return this.greaterBoundaryCorner.getBlockZ() - this.lesserBoundaryCorner.getBlockZ() + 1;
|
||||
}
|
||||
|
||||
//distance check for claims, distance in this case is a band around the outside of the claim rather then euclidean distance
|
||||
public boolean isNear(Location location, int howNear)
|
||||
{
|
||||
Claim claim = new Claim
|
||||
(new Location(this.lesserBoundaryCorner.getWorld(), this.lesserBoundaryCorner.getBlockX() - howNear, this.lesserBoundaryCorner.getBlockY(), this.lesserBoundaryCorner.getBlockZ() - howNear),
|
||||
new Location(this.greaterBoundaryCorner.getWorld(), this.greaterBoundaryCorner.getBlockX() + howNear, this.greaterBoundaryCorner.getBlockY(), this.greaterBoundaryCorner.getBlockZ() + howNear),
|
||||
"", new String[] {}, new String[] {}, new String[] {}, new String[] {});
|
||||
|
||||
return claim.contains(location, false, true);
|
||||
}
|
||||
|
||||
//permissions. note administrative "public" claims have different rules than other claims
|
||||
//all of these return NULL when a player has permission, or a String error message when the player doesn't have permission
|
||||
public String allowEdit(Player player)
|
||||
{
|
||||
//special cases...
|
||||
|
||||
//admin claims need adminclaims permission only.
|
||||
if(this.isAdminClaim())
|
||||
{
|
||||
if(player.hasPermission("griefprevention.adminclaims")) return null;
|
||||
}
|
||||
|
||||
//anyone with deleteclaims permission can modify non-admin claims at any time
|
||||
else
|
||||
{
|
||||
if(player.hasPermission("griefprevention.deleteclaims")) return null;
|
||||
}
|
||||
|
||||
//no resizing, deleting, and so forth while under siege
|
||||
if(this.ownerName.equals(player.getName()))
|
||||
{
|
||||
if(this.siegeData != null)
|
||||
{
|
||||
return "Claims can't be modified while under siege.";
|
||||
}
|
||||
|
||||
//otherwise, owners can do whatever
|
||||
return null;
|
||||
}
|
||||
|
||||
//permission inheritance for subdivisions
|
||||
if(this.parent != null)
|
||||
return this.parent.allowBuild(player);
|
||||
|
||||
//error message if all else fails
|
||||
return "Only " + this.getOwnerName() + " can modify this claim.";
|
||||
}
|
||||
|
||||
//build permission check
|
||||
public String allowBuild(Player player)
|
||||
{
|
||||
//when a player tries to build in a claim, if he's under siege, the siege may extend to include the new claim
|
||||
GriefPrevention.instance.dataStore.tryExtendSiege(player, this);
|
||||
|
||||
//admin claims can always be modified by admins, no exceptions
|
||||
if(this.isAdminClaim())
|
||||
{
|
||||
if(player.hasPermission("griefprevention.adminclaims")) return null;
|
||||
}
|
||||
|
||||
//no building while under siege
|
||||
if(this.siegeData != null)
|
||||
{
|
||||
return "This claim is under siege by " + this.siegeData.attacker.getName() + ". No one can build here.";
|
||||
}
|
||||
|
||||
//no building while in pvp combat
|
||||
PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getName());
|
||||
if(playerData.inPvpCombat())
|
||||
{
|
||||
return "You can't build in claims during PvP combat.";
|
||||
}
|
||||
|
||||
//owners can make changes, or admins with ignore claims mode enabled
|
||||
if(this.ownerName.equals(player.getName()) || GriefPrevention.instance.dataStore.getPlayerData(player.getName()).ignoreClaims) return null;
|
||||
|
||||
//anyone with explicit build permission can make changes
|
||||
ClaimPermission permissionLevel = this.playerNameToClaimPermissionMap.get(player.getName().toLowerCase());
|
||||
if(ClaimPermission.Build == permissionLevel) return null;
|
||||
|
||||
//also everyone is a member of the "public", so check for public permission
|
||||
permissionLevel = this.playerNameToClaimPermissionMap.get("public");
|
||||
if(ClaimPermission.Build == permissionLevel) return null;
|
||||
|
||||
//subdivision permission inheritance
|
||||
if(this.parent != null)
|
||||
return this.parent.allowBuild(player);
|
||||
|
||||
//failure message for all other cases
|
||||
return "You don't have " + this.getOwnerName() + "'s permission to build here.";
|
||||
}
|
||||
|
||||
//break permission check
|
||||
public String allowBreak(Player player, Material material)
|
||||
{
|
||||
//if under siege, some blocks will be breakable
|
||||
if(this.siegeData != null)
|
||||
{
|
||||
boolean breakable = false;
|
||||
|
||||
//search for block type in list of breakable blocks
|
||||
for(int i = 0; i < GriefPrevention.instance.config_siege_blocks.size(); i++)
|
||||
{
|
||||
Material breakableMaterial = GriefPrevention.instance.config_siege_blocks.get(i);
|
||||
if(breakableMaterial.getId() == material.getId())
|
||||
{
|
||||
breakable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//custom error messages for siege mode
|
||||
if(!breakable)
|
||||
{
|
||||
return "That material is too tough to break.";
|
||||
}
|
||||
else if(this.ownerName.equals(player.getName()))
|
||||
{
|
||||
return "You can't make changes while under siege.";
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//if not under siege, build rules apply
|
||||
return this.allowBuild(player);
|
||||
}
|
||||
|
||||
//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;
|
||||
|
||||
//claim owner and admins in ignoreclaims mode have access
|
||||
if(this.ownerName.equals(player.getName()) || GriefPrevention.instance.dataStore.getPlayerData(player.getName()).ignoreClaims) return null;
|
||||
|
||||
//look for explicit individual access, inventory, or build permission
|
||||
ClaimPermission permissionLevel = this.playerNameToClaimPermissionMap.get(player.getName().toLowerCase());
|
||||
if(ClaimPermission.Build == permissionLevel || ClaimPermission.Inventory == permissionLevel || ClaimPermission.Access == permissionLevel) return null;
|
||||
|
||||
//also check for public permission
|
||||
permissionLevel = this.playerNameToClaimPermissionMap.get("public");
|
||||
if(ClaimPermission.Build == permissionLevel || ClaimPermission.Inventory == permissionLevel || ClaimPermission.Access == permissionLevel) return null;
|
||||
|
||||
//permission inheritance for subdivisions
|
||||
if(this.parent != null)
|
||||
return this.parent.allowAccess(player);
|
||||
|
||||
//catch-all error message for all other cases
|
||||
return "You don't have " + this.getOwnerName() + "'s permission to use that.";
|
||||
}
|
||||
|
||||
//inventory permission check
|
||||
public String allowContainers(Player player)
|
||||
{
|
||||
//trying to access inventory in a claim may extend an existing siege to include this claim
|
||||
GriefPrevention.instance.dataStore.tryExtendSiege(player, this);
|
||||
|
||||
//if under siege, nobody accesses containers
|
||||
if(this.siegeData != null)
|
||||
{
|
||||
return "This claim is under siege by " + siegeData.attacker.getName() + ". No one can access containers here right now.";
|
||||
}
|
||||
|
||||
//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;
|
||||
|
||||
//check for explicit individual container or build permission
|
||||
ClaimPermission permissionLevel = this.playerNameToClaimPermissionMap.get(player.getName().toLowerCase());
|
||||
if(ClaimPermission.Build == permissionLevel || ClaimPermission.Inventory == permissionLevel) return null;
|
||||
|
||||
//check for public container or build permission
|
||||
permissionLevel = this.playerNameToClaimPermissionMap.get("public");
|
||||
if(ClaimPermission.Build == permissionLevel || ClaimPermission.Inventory == permissionLevel) return null;
|
||||
|
||||
//permission inheritance for subdivisions
|
||||
if(this.parent != null)
|
||||
return this.parent.allowContainers(player);
|
||||
|
||||
//error message for all other cases
|
||||
return "You don't have " + this.getOwnerName() + "'s permission to use that.";
|
||||
}
|
||||
|
||||
//grant permission check, relatively simple
|
||||
public String allowGrantPermission(Player player)
|
||||
{
|
||||
//anyone who can modify the claim, or who's explicitly in the managers (/PermissionTrust) list can do this
|
||||
if(this.allowEdit(player) == null || this.managers.contains(player.getName())) return null;
|
||||
|
||||
//permission inheritance for subdivisions
|
||||
if(this.parent != null)
|
||||
return this.parent.allowGrantPermission(player);
|
||||
|
||||
//generic error message
|
||||
return "You don't have " + this.getOwnerName() + "'s permission to grant permission here.";
|
||||
}
|
||||
|
||||
//grants a permission for a player or the public
|
||||
public void setPermission(String playerName, ClaimPermission permissionLevel)
|
||||
{
|
||||
this.playerNameToClaimPermissionMap.put(playerName.toLowerCase(), permissionLevel);
|
||||
}
|
||||
|
||||
//revokes a permission for a player or the public
|
||||
public void dropPermission(String playerName)
|
||||
{
|
||||
this.playerNameToClaimPermissionMap.remove(playerName.toLowerCase());
|
||||
}
|
||||
|
||||
//clears all permissions (except owner of course)
|
||||
public void clearPermissions()
|
||||
{
|
||||
this.playerNameToClaimPermissionMap.clear();
|
||||
}
|
||||
|
||||
//gets ALL permissions
|
||||
//useful for making copies of permissions during a claim resize and listing all permissions in a claim
|
||||
public void getPermissions(ArrayList<String> builders, ArrayList<String> containers, ArrayList<String> accessors, ArrayList<String> managers)
|
||||
{
|
||||
//loop through all the entries in the hash map
|
||||
Iterator<Map.Entry<String, ClaimPermission>> mappingsIterator = this.playerNameToClaimPermissionMap.entrySet().iterator();
|
||||
while(mappingsIterator.hasNext())
|
||||
{
|
||||
Map.Entry<String, ClaimPermission> entry = mappingsIterator.next();
|
||||
|
||||
//build up a list for each permission level
|
||||
if(entry.getValue() == ClaimPermission.Build)
|
||||
{
|
||||
builders.add(entry.getKey());
|
||||
}
|
||||
else if(entry.getValue() == ClaimPermission.Inventory)
|
||||
{
|
||||
containers.add(entry.getKey());
|
||||
}
|
||||
else
|
||||
{
|
||||
accessors.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
//managers are handled a little differently
|
||||
for(int i = 0; i < this.managers.size(); i++)
|
||||
{
|
||||
managers.add(this.managers.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
//returns a copy of the location representing lower x, y, z limits
|
||||
public Location getLesserBoundaryCorner()
|
||||
{
|
||||
return this.lesserBoundaryCorner.clone();
|
||||
}
|
||||
|
||||
//returns a copy of the location representing upper x, y, z limits
|
||||
//NOTE: remember upper Y will always be ignored, all claims always extend to the sky
|
||||
public Location getGreaterBoundaryCorner()
|
||||
{
|
||||
return this.greaterBoundaryCorner.clone();
|
||||
}
|
||||
|
||||
//returns a friendly owner name (for admin claims, returns "an administrator" as the owner)
|
||||
public String getOwnerName()
|
||||
{
|
||||
if(this.parent != null)
|
||||
return this.parent.getOwnerName();
|
||||
|
||||
if(this.ownerName.length() == 0)
|
||||
return "an administrator";
|
||||
|
||||
return this.ownerName;
|
||||
}
|
||||
|
||||
//whether or not a location is in a claim
|
||||
//ignoreHeight = true means location UNDER the claim will return TRUE
|
||||
//excludeSubdivisions = true means that locations inside subdivisions of the claim will return FALSE
|
||||
public boolean contains(Location location, boolean ignoreHeight, boolean excludeSubdivisions)
|
||||
{
|
||||
//not in the same world implies false
|
||||
if(!location.getWorld().equals(this.lesserBoundaryCorner.getWorld())) return false;
|
||||
|
||||
int x = location.getBlockX();
|
||||
int y = location.getBlockY();
|
||||
int z = location.getBlockZ();
|
||||
|
||||
//main check
|
||||
boolean inClaim = (ignoreHeight || y >= this.lesserBoundaryCorner.getBlockY()) &&
|
||||
x >= this.lesserBoundaryCorner.getBlockX() &&
|
||||
x <= this.greaterBoundaryCorner.getBlockX() &&
|
||||
z >= this.lesserBoundaryCorner.getBlockZ() &&
|
||||
z <= this.greaterBoundaryCorner.getBlockZ();
|
||||
|
||||
if(!inClaim) return false;
|
||||
|
||||
//additional check for subdivisions
|
||||
//you're only in a subdivision when you're also in its parent claim
|
||||
//NOTE: if a player creates subdivions then resizes the parent claim, it's possible that
|
||||
//a subdivision can reach outside of its parent's boundaries. so this check is important!
|
||||
if(this.parent != null)
|
||||
{
|
||||
return this.parent.contains(location, ignoreHeight, false);
|
||||
}
|
||||
|
||||
//code to exclude subdivisions in this check
|
||||
else if(excludeSubdivisions)
|
||||
{
|
||||
//search all subdivisions to see if the location is in any of them
|
||||
for(int i = 0; i < this.children.size(); i++)
|
||||
{
|
||||
//if we find such a subdivision, return false
|
||||
if(this.children.get(i).contains(location, ignoreHeight, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//otherwise yes
|
||||
return true;
|
||||
}
|
||||
|
||||
//whether or not two claims overlap
|
||||
//used internally to prevent overlaps when creating claims
|
||||
boolean overlaps(Claim otherClaim)
|
||||
{
|
||||
//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.
|
||||
|
||||
//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;
|
||||
if(otherClaim.contains(new Location(this.lesserBoundaryCorner.getWorld(), this.lesserBoundaryCorner.getBlockX(), 0, this.greaterBoundaryCorner.getBlockZ()), true, false)) return true;
|
||||
if(otherClaim.contains(new Location(this.lesserBoundaryCorner.getWorld(), this.greaterBoundaryCorner.getBlockX(), 0, this.lesserBoundaryCorner.getBlockZ()), true, false)) return true;
|
||||
|
||||
//verify that no claim's lesser boundary point is inside this new claim, to cover the "existing claim is entirely inside new claim" case
|
||||
if(this.contains(otherClaim.getLesserBoundaryCorner(), true, false)) return true;
|
||||
|
||||
//verify this claim doesn't band across an existing claim, either horizontally or vertically
|
||||
if( this.getLesserBoundaryCorner().getBlockZ() <= otherClaim.getGreaterBoundaryCorner().getBlockZ() &&
|
||||
this.getLesserBoundaryCorner().getBlockZ() >= otherClaim.getLesserBoundaryCorner().getBlockZ() &&
|
||||
this.getLesserBoundaryCorner().getBlockX() < otherClaim.getLesserBoundaryCorner().getBlockX() &&
|
||||
this.getGreaterBoundaryCorner().getBlockX() > otherClaim.getGreaterBoundaryCorner().getBlockX() )
|
||||
return true;
|
||||
|
||||
if( this.getGreaterBoundaryCorner().getBlockZ() <= otherClaim.getGreaterBoundaryCorner().getBlockZ() &&
|
||||
this.getGreaterBoundaryCorner().getBlockZ() >= otherClaim.getLesserBoundaryCorner().getBlockZ() &&
|
||||
this.getLesserBoundaryCorner().getBlockX() < otherClaim.getLesserBoundaryCorner().getBlockX() &&
|
||||
this.getGreaterBoundaryCorner().getBlockX() > otherClaim.getGreaterBoundaryCorner().getBlockX() )
|
||||
return true;
|
||||
|
||||
if( this.getLesserBoundaryCorner().getBlockX() <= otherClaim.getGreaterBoundaryCorner().getBlockX() &&
|
||||
this.getLesserBoundaryCorner().getBlockX() >= otherClaim.getLesserBoundaryCorner().getBlockX() &&
|
||||
this.getLesserBoundaryCorner().getBlockZ() < otherClaim.getLesserBoundaryCorner().getBlockZ() &&
|
||||
this.getGreaterBoundaryCorner().getBlockZ() > otherClaim.getGreaterBoundaryCorner().getBlockZ() )
|
||||
return true;
|
||||
|
||||
if( this.getGreaterBoundaryCorner().getBlockX() <= otherClaim.getGreaterBoundaryCorner().getBlockX() &&
|
||||
this.getGreaterBoundaryCorner().getBlockX() >= otherClaim.getLesserBoundaryCorner().getBlockX() &&
|
||||
this.getLesserBoundaryCorner().getBlockZ() < otherClaim.getLesserBoundaryCorner().getBlockZ() &&
|
||||
this.getGreaterBoundaryCorner().getBlockZ() > otherClaim.getGreaterBoundaryCorner().getBlockZ() )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
27
src/me/ryanhamshire/GriefPrevention/ClaimPermission.java
Normal file
27
src/me/ryanhamshire/GriefPrevention/ClaimPermission.java
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
GriefPrevention Server Plugin for Minecraft
|
||||
Copyright (C) 2012 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;
|
||||
|
||||
//basic enum stuff
|
||||
public enum ClaimPermission
|
||||
{
|
||||
Build,
|
||||
Inventory,
|
||||
Access
|
||||
}
|
||||
29
src/me/ryanhamshire/GriefPrevention/CreateClaimResult.java
Normal file
29
src/me/ryanhamshire/GriefPrevention/CreateClaimResult.java
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
GriefPrevention Server Plugin for Minecraft
|
||||
Copyright (C) 2012 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;
|
||||
|
||||
public class CreateClaimResult
|
||||
{
|
||||
//whether or not the creation succeeded (it would fail if the new claim overlapped another existing claim)
|
||||
public boolean succeeded;
|
||||
|
||||
//when succeeded, this is a reference to the new claim
|
||||
//when failed, this is a reference to the pre-existing, conflicting claim
|
||||
public Claim claim;
|
||||
}
|
||||
1069
src/me/ryanhamshire/GriefPrevention/DataStore.java
Normal file
1069
src/me/ryanhamshire/GriefPrevention/DataStore.java
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
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 org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
//FEATURE: give players claim blocks for playing, as long as they're not away from their computer
|
||||
|
||||
//runs every 5 minutes in the main thread, grants blocks per hour / 12 to each online player who appears to be actively playing
|
||||
class DeliverClaimBlocksTask implements Runnable
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
Player [] players = GriefPrevention.instance.getServer().getOnlinePlayers();
|
||||
|
||||
//for each online player
|
||||
for(int i = 0; i < players.length; i++)
|
||||
{
|
||||
Player player = players[i];
|
||||
DataStore dataStore = GriefPrevention.instance.dataStore;
|
||||
PlayerData playerData = dataStore.getPlayerData(player.getName());
|
||||
|
||||
Location lastLocation = playerData.lastAfkCheckLocation;
|
||||
try //distance squared will throw an exception if the player has changed worlds
|
||||
{
|
||||
//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;
|
||||
|
||||
//respect limits
|
||||
if(playerData.accruedClaimBlocks > GriefPrevention.instance.config_claims_maxAccruedBlocks)
|
||||
{
|
||||
playerData.accruedClaimBlocks = GriefPrevention.instance.config_claims_maxAccruedBlocks;
|
||||
}
|
||||
|
||||
dataStore.savePlayerData(player.getName(), playerData);
|
||||
}
|
||||
}
|
||||
catch(Exception e) { }
|
||||
|
||||
//remember current location for next time
|
||||
playerData.lastAfkCheckLocation = player.getLocation();
|
||||
}
|
||||
}
|
||||
}
|
||||
306
src/me/ryanhamshire/GriefPrevention/EntityEventHandler.java
Normal file
306
src/me/ryanhamshire/GriefPrevention/EntityEventHandler.java
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
/*
|
||||
GriefPrevention Server Plugin for Minecraft
|
||||
Copyright (C) 2012 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.List;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Animals;
|
||||
import org.bukkit.entity.Arrow;
|
||||
import org.bukkit.entity.Creeper;
|
||||
import org.bukkit.entity.Enderman;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.ThrownPotion;
|
||||
import org.bukkit.entity.Vehicle;
|
||||
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityChangeBlockEvent;
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
import org.bukkit.event.entity.EntityDeathEvent;
|
||||
import org.bukkit.event.entity.EntityExplodeEvent;
|
||||
import org.bukkit.event.painting.PaintingBreakByEntityEvent;
|
||||
import org.bukkit.event.painting.PaintingBreakEvent;
|
||||
import org.bukkit.event.painting.PaintingPlaceEvent;
|
||||
|
||||
//handles events related to entities
|
||||
class EntityEventHandler implements Listener
|
||||
{
|
||||
//convenience reference for the singleton datastore
|
||||
private DataStore dataStore;
|
||||
|
||||
public EntityEventHandler(DataStore dataStore)
|
||||
{
|
||||
this.dataStore = dataStore;
|
||||
}
|
||||
|
||||
//when an entity explodes...
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onEntityExplode(EntityExplodeEvent explodeEvent)
|
||||
{
|
||||
List<Block> blocks = explodeEvent.blockList();
|
||||
Entity entity = explodeEvent.getEntity();
|
||||
|
||||
//FEATURE: creepers don't destroy blocks when they explode near or above sea level
|
||||
|
||||
if(GriefPrevention.instance.config_creepersDontDestroySurface && entity instanceof Creeper)
|
||||
{
|
||||
if(entity.getLocation().getBlockY() > entity.getLocation().getWorld().getSeaLevel() - 7)
|
||||
{
|
||||
blocks.clear(); //explosion still happens, can damage creatures/players, but no blocks will be destroyed
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//FEATURE: creating an explosion near a claim doesn't damage any of the claimed blocks
|
||||
|
||||
Claim claim = null;
|
||||
for(int i = 0; i < blocks.size(); i++) //for each destroyed block
|
||||
{
|
||||
Block block = blocks.get(i);
|
||||
if(block.getType() == Material.AIR) continue; //if it's air, we don't care
|
||||
|
||||
claim = this.dataStore.getClaimAt(block.getLocation(), false, claim);
|
||||
//if the block is claimed, remove it from the list of destroyed blocks
|
||||
if(claim != null)
|
||||
{
|
||||
blocks.remove(i--);
|
||||
}
|
||||
|
||||
//if the block is not claimed and is a log, trigger the anti-tree-top code
|
||||
else if(block.getType() == Material.LOG)
|
||||
{
|
||||
GriefPrevention.instance.handleLogBroken(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//when an entity dies...
|
||||
@EventHandler
|
||||
public void onEntityDeath(EntityDeathEvent event)
|
||||
{
|
||||
//FEATURE: when a player is involved in a siege (attacker or defender role)
|
||||
//his death will end the siege
|
||||
|
||||
LivingEntity entity = event.getEntity();
|
||||
if(!(entity instanceof Player)) return; //only tracking players
|
||||
|
||||
Player player = (Player)entity;
|
||||
PlayerData playerData = this.dataStore.getPlayerData(player.getName());
|
||||
|
||||
//if involved in a siege
|
||||
if(playerData.siegeData != null)
|
||||
{
|
||||
//end it, with the dieing player being the loser
|
||||
this.dataStore.endSiege(playerData.siegeData, null, player.getName());
|
||||
}
|
||||
}
|
||||
|
||||
//when an entity picks up an item
|
||||
@EventHandler
|
||||
public void onEntityPickup(EntityChangeBlockEvent event)
|
||||
{
|
||||
//FEATURE: endermen don't steal claimed blocks
|
||||
|
||||
//if its an enderman
|
||||
if(event.getEntity() instanceof Enderman)
|
||||
{
|
||||
//and the block is claimed
|
||||
if(this.dataStore.getClaimAt(event.getBlock().getLocation(), false, null) != null)
|
||||
{
|
||||
//he doesn't get to steal it
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//when a painting is broken
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onPaintingBreak(PaintingBreakEvent event)
|
||||
{
|
||||
//FEATURE: claimed paintings are protected from breakage
|
||||
|
||||
//only allow players to break paintings, not anything else (like water and explosions)
|
||||
if(!(event instanceof PaintingBreakByEntityEvent))
|
||||
{
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
PaintingBreakByEntityEvent entityEvent = (PaintingBreakByEntityEvent)event;
|
||||
|
||||
//which claim is the painting in?
|
||||
Claim claim = this.dataStore.getClaimAt(event.getPainting().getLocation(), false, null);
|
||||
if(claim == null) return;
|
||||
|
||||
//who is removing it?
|
||||
Entity remover = entityEvent.getRemover();
|
||||
|
||||
//again, making sure the breaker is a player
|
||||
if(!(remover instanceof Player))
|
||||
{
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
//if the player doesn't have build permission, don't allow the breakage
|
||||
Player playerRemover = (Player)entityEvent.getRemover();
|
||||
String noBuildReason = claim.allowBuild(playerRemover);
|
||||
if(noBuildReason != null)
|
||||
{
|
||||
event.setCancelled(true);
|
||||
GriefPrevention.sendMessage(playerRemover, TextMode.Err, noBuildReason);
|
||||
}
|
||||
}
|
||||
|
||||
//when a painting is placed...
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onPaintingPlace(PaintingPlaceEvent event)
|
||||
{
|
||||
//FEATURE: similar to above, placing a painting requires build permission in the claim
|
||||
|
||||
//which claim is the painting in?
|
||||
Claim claim = this.dataStore.getClaimAt(event.getBlock().getLocation(), false, null);
|
||||
if(claim == null) return;
|
||||
|
||||
//if the player doesn't have permission, don't allow the placement
|
||||
String noBuildReason = claim.allowBuild(event.getPlayer());
|
||||
if(noBuildReason != null)
|
||||
{
|
||||
event.setCancelled(true);
|
||||
GriefPrevention.sendMessage(event.getPlayer(), TextMode.Err, noBuildReason);
|
||||
}
|
||||
}
|
||||
|
||||
//when an entity is damaged
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onEntityDamage (EntityDamageEvent event)
|
||||
{
|
||||
//only actually interested in entities damaging entities (ignoring environmental damage)
|
||||
if(!(event instanceof EntityDamageByEntityEvent)) return;
|
||||
|
||||
EntityDamageByEntityEvent subEvent = (EntityDamageByEntityEvent) event;
|
||||
|
||||
//determine which player is attacking, if any
|
||||
Player attacker = null;
|
||||
Entity damageSource = subEvent.getDamager();
|
||||
if(damageSource instanceof Player)
|
||||
{
|
||||
attacker = (Player)damageSource;
|
||||
}
|
||||
else if(damageSource instanceof Arrow)
|
||||
{
|
||||
Arrow arrow = (Arrow)damageSource;
|
||||
if(arrow.getShooter() instanceof Player)
|
||||
{
|
||||
attacker = (Player)arrow.getShooter();
|
||||
}
|
||||
}
|
||||
else if(damageSource instanceof ThrownPotion)
|
||||
{
|
||||
ThrownPotion potion = (ThrownPotion)damageSource;
|
||||
if(potion.getShooter() instanceof Player)
|
||||
{
|
||||
attacker = (Player)potion.getShooter();
|
||||
}
|
||||
}
|
||||
|
||||
//if the attacker is a player and defender is a player (pvp combat)
|
||||
if(attacker != null && event.getEntity() instanceof Player)
|
||||
{
|
||||
//if pvp is disabled, cancel the event
|
||||
if(!event.getEntity().getWorld().getPVP())
|
||||
{
|
||||
event.setCancelled(true);
|
||||
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
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
if(attackerData.pvpImmune)
|
||||
{
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//FEATURE: protect claimed animals, boats, minecarts
|
||||
//NOTE: animals can be lead with wheat, vehicles can be pushed around.
|
||||
//so unless precautions are taken by the owner, a resourceful thief might find ways to steal anyway
|
||||
|
||||
//if theft protection is enabled
|
||||
if(GriefPrevention.instance.config_claims_preventTheft && event instanceof EntityDamageByEntityEvent)
|
||||
{
|
||||
//if the entity is an animal or a vehicle
|
||||
if (subEvent.getEntity() instanceof Animals || subEvent.getEntity() instanceof Vehicle)
|
||||
{
|
||||
Claim claim = this.dataStore.getClaimAt(event.getEntity().getLocation(), false, null);
|
||||
|
||||
//if it's claimed
|
||||
if(claim != null)
|
||||
{
|
||||
//if damaged by anything other than a player, cancel the event
|
||||
if(attacker == null)
|
||||
{
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
//otherwise the player damaging the entity must have permission
|
||||
else
|
||||
{
|
||||
String noContainersReason = claim.allowContainers(attacker);
|
||||
if(noContainersReason != null)
|
||||
{
|
||||
event.setCancelled(true);
|
||||
GriefPrevention.sendMessage(attacker, TextMode.Err, "That belongs to " + claim.getOwnerName() + ".");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1568
src/me/ryanhamshire/GriefPrevention/GriefPrevention.java
Normal file
1568
src/me/ryanhamshire/GriefPrevention/GriefPrevention.java
Normal file
File diff suppressed because it is too large
Load Diff
130
src/me/ryanhamshire/GriefPrevention/PlayerData.java
Normal file
130
src/me/ryanhamshire/GriefPrevention/PlayerData.java
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
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.Date;
|
||||
import java.util.Vector;
|
||||
|
||||
import org.bukkit.Location;
|
||||
|
||||
//holds all of GriefPrevention's player-tied data
|
||||
public class PlayerData
|
||||
{
|
||||
//the player's claims
|
||||
public Vector<Claim> claims = new Vector<Claim>();
|
||||
|
||||
//how many claim blocks the player has earned via play time
|
||||
public int accruedClaimBlocks = GriefPrevention.instance.config_claims_initialBlocks;
|
||||
|
||||
//where this player was the last time we checked on him for earning claim blocks
|
||||
public Location lastAfkCheckLocation = null;
|
||||
|
||||
//how many claim blocks the player has been gifted by admins, or purchased via economy integration
|
||||
public int bonusClaimBlocks = 0;
|
||||
|
||||
//what "mode" the shovel is in determines what it will do when it's used
|
||||
public ShovelMode shovelMode = ShovelMode.Basic;
|
||||
|
||||
//last place the player used the shovel, useful in creating and resizing claims,
|
||||
//because the player must use the shovel twice in those instances
|
||||
public Location lastShovelLocation = null;
|
||||
|
||||
//the claim this player is currently resizing
|
||||
public Claim claimResizing = null;
|
||||
|
||||
//the claim this player is currently subdividing
|
||||
public Claim claimSubdividing = null;
|
||||
|
||||
//the timestamp for the last time the player used /trapped
|
||||
public Date lastTrappedUsage;
|
||||
|
||||
//whether or not the player has a pending /trapped rescue
|
||||
public boolean pendingTrapped = false;
|
||||
|
||||
//last place the player damaged a chest
|
||||
public Location lastChestDamageLocation = null;
|
||||
|
||||
//spam
|
||||
public Date lastLogin; //when the player last logged into the server
|
||||
public String lastMessage = ""; //the player's last chat message, or slash command complete with parameters
|
||||
public Date lastMessageTimestamp = new Date(); //last time the player sent a chat message or used a monitored slash command
|
||||
public int spamCount = 0; //number of consecutive "spams"
|
||||
|
||||
//visualization
|
||||
public Visualization currentVisualization = null;
|
||||
|
||||
//anti-camping pvp protection
|
||||
public boolean pvpImmune = false;
|
||||
public long lastSpawn = 0;
|
||||
|
||||
//ignore claims mode
|
||||
public boolean ignoreClaims = false;
|
||||
|
||||
//the last claim this player was in, that we know of
|
||||
public Claim lastClaim = null;
|
||||
|
||||
//siege
|
||||
public SiegeData siegeData = null;
|
||||
|
||||
//pvp
|
||||
public long lastPvpTimestamp = 0;
|
||||
public String lastPvpPlayer = "";
|
||||
|
||||
PlayerData()
|
||||
{
|
||||
//default last login date value to a year ago to ensure a brand new player can log in
|
||||
//see login cooldown feature, PlayerEventHandler.onPlayerLogin()
|
||||
//if the player successfully logs in, this value will be overwritten with the current date and time
|
||||
Calendar lastYear = Calendar.getInstance();
|
||||
lastYear.add(Calendar.YEAR, -1);
|
||||
this.lastLogin = lastYear.getTime();
|
||||
this.lastTrappedUsage = lastYear.getTime();
|
||||
}
|
||||
|
||||
//whether or not this player is "in" pvp combat
|
||||
public boolean inPvpCombat()
|
||||
{
|
||||
if(this.lastPvpTimestamp == 0) return false;
|
||||
|
||||
long now = Calendar.getInstance().getTimeInMillis();
|
||||
|
||||
long elapsed = now - this.lastPvpTimestamp;
|
||||
|
||||
if(elapsed > 15000) //15 seconds
|
||||
{
|
||||
this.lastPvpTimestamp = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//the number of claim blocks a player has available for claiming land
|
||||
public int getRemainingClaimBlocks()
|
||||
{
|
||||
int remainingBlocks = this.accruedClaimBlocks + this.bonusClaimBlocks;
|
||||
for(int i = 0; i < this.claims.size(); i++)
|
||||
{
|
||||
Claim claim = this.claims.get(i);
|
||||
remainingBlocks -= claim.getArea();
|
||||
}
|
||||
|
||||
return remainingBlocks;
|
||||
}
|
||||
}
|
||||
1097
src/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java
Normal file
1097
src/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java
Normal file
File diff suppressed because it is too large
Load Diff
69
src/me/ryanhamshire/GriefPrevention/PlayerRescueTask.java
Normal file
69
src/me/ryanhamshire/GriefPrevention/PlayerRescueTask.java
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
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 org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
//tries to rescue a trapped player from a claim where he doesn't have permission to save himself
|
||||
//related to the /trapped slash command
|
||||
//this does run in the main thread, so it's okay to make non-thread-safe calls
|
||||
class PlayerRescueTask implements Runnable
|
||||
{
|
||||
//original location where /trapped was used
|
||||
private Location location;
|
||||
|
||||
//player data
|
||||
private Player player;
|
||||
|
||||
public PlayerRescueTask(Player player, Location location)
|
||||
{
|
||||
this.player = player;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
//if he logged out, don't do anything
|
||||
if(!player.isOnline()) return;
|
||||
|
||||
//he no longer has a pending /trapped slash command, so he can try to use it again now
|
||||
PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getName());
|
||||
playerData.pendingTrapped = false;
|
||||
|
||||
//if the player moved three or more blocks from where he used /trapped, admonish him and don't save him
|
||||
if(player.getLocation().distance(this.location) > 3)
|
||||
{
|
||||
GriefPrevention.sendMessage(player, TextMode.Err, "You moved! Rescue cancelled.");
|
||||
return;
|
||||
}
|
||||
|
||||
//otherwise find a place to teleport him
|
||||
Location destination = GriefPrevention.instance.ejectPlayer(this.player);
|
||||
|
||||
//log entry, in case admins want to investigate the "trap"
|
||||
GriefPrevention.AddLogEntry("Rescued trapped player " + player.getName() + " from " + this.location.toString() + " to " + destination.toString() + ".");
|
||||
|
||||
//timestamp this successful save so that he can't use /trapped again for a while
|
||||
playerData.lastTrappedUsage = Calendar.getInstance().getTime();
|
||||
}
|
||||
}
|
||||
41
src/me/ryanhamshire/GriefPrevention/Public API.txt
Normal file
41
src/me/ryanhamshire/GriefPrevention/Public API.txt
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
This document describes the public API, which you can use to create extensions to GriefPrevention which add new features. Before I get into the specifics, let me give you a few examples of often-requested features which, to my knowledge, have not yet been implemented by anyone. If you want to make a big impact with a small project, these are the go-to areas! If you publish one of these extensions on BukkitDev, please contact me and I'll add a link from my project to yours.
|
||||
|
||||
Claim Buy/Sell
|
||||
|
||||
I keep saying no to this because I'm developing an anti grief plugin, not a real estate plugin. But it's a common ask. Lots of people would use an extension that allowed them to use server money to buy and sell claims, or to lease subdivisions.
|
||||
|
||||
More Locks
|
||||
|
||||
Many have asked for wooden doors, trap doors, and fence gates to require /AccessTrust. I've insisted that because players generally expect these to be openable (based on the Vanilla experience), players should just "earn" their privacy by finding some iron and building an iron door. Nonetheless, some folks definitely want this.
|
||||
|
||||
Claim Flags
|
||||
|
||||
Sometimes, folks want to add special flags to their claims like "no monsters spawn here". They can do this today by adding other plugins like WorldGuard, which are compatible with GriefPrevention, but it would be nice if they could just use one plugin (and an extension). I think their flag ideas come mostly from Residence and WorldGuard, so you can look there for ideas.
|
||||
|
||||
Claim Entry/Exit Messages
|
||||
|
||||
I keep telling people NO, I won't do this because it's not anti-grief-related and it's expensive to constantly track player movement. But folks want it, and they keep asking for it. You could build an extension which adds some slash commands for naming claims, and displays enter/exit messages as players walk around.
|
||||
|
||||
Now the specifics! Please note, these are the supported operations. I've done my best to "hide" fields and methods which you shouldn't play with, but if you happen to notice something not discussed here, it's best not to fiddle with it. If in doubt, at the very least look at my source code and comments before using something you're unfamiliar with in an extension.
|
||||
|
||||
Getting the Claim at a Location
|
||||
|
||||
Managing Permissions in a Claim
|
||||
|
||||
Creating a New Claim
|
||||
|
||||
Resizing or Moving a Claim
|
||||
|
||||
Extending a Claim Downward
|
||||
|
||||
Changing a Claim's Owner
|
||||
|
||||
Updating Other Claim Fields
|
||||
|
||||
Uniquely Identifying a Claim
|
||||
|
||||
Starting a Siege
|
||||
|
||||
Ending a Siege
|
||||
|
||||
Getting/Updating Player Data
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
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 org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
//this main thread task takes the output from the RestoreNatureProcessingTask\
|
||||
//and updates the world accordingly
|
||||
class RestoreNatureExecutionTask implements Runnable
|
||||
{
|
||||
//results from processing thread
|
||||
//will be applied to the world
|
||||
private BlockSnapshot[][][] snapshots;
|
||||
|
||||
//boundaries for changes
|
||||
private int miny;
|
||||
private Location lesserCorner;
|
||||
private Location greaterCorner;
|
||||
|
||||
//player who should be notified about the result (will see a visualization when the restoration is complete)
|
||||
private Player player;
|
||||
|
||||
public RestoreNatureExecutionTask(BlockSnapshot[][][] snapshots, int miny, Location lesserCorner, Location greaterCorner, Player player)
|
||||
{
|
||||
this.snapshots = snapshots;
|
||||
this.miny = miny;
|
||||
this.lesserCorner = lesserCorner;
|
||||
this.greaterCorner = greaterCorner;
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
//apply changes to the world, but ONLY to unclaimed blocks
|
||||
//note that the edge of the results is not applied (the 1-block-wide band around the outside of the chunk)
|
||||
//those data were sent to the processing thread for referernce purposes, but aren't part of the area selected for restoration
|
||||
Claim cachedClaim = null;
|
||||
for(int x = 1; x < this.snapshots.length - 1; x++)
|
||||
{
|
||||
for(int z = 1; z < this.snapshots[0][0].length; z++)
|
||||
{
|
||||
for(int y = this.miny; y < this.snapshots[0].length; y++)
|
||||
{
|
||||
BlockSnapshot blockUpdate = this.snapshots[x][y][z];
|
||||
Block currentBlock = blockUpdate.location.getBlock();
|
||||
if(blockUpdate.typeId != currentBlock.getTypeId() || blockUpdate.data != currentBlock.getData())
|
||||
{
|
||||
Claim claim = GriefPrevention.instance.dataStore.getClaimAt(blockUpdate.location, false, cachedClaim);
|
||||
if(claim != null)
|
||||
{
|
||||
cachedClaim = claim;
|
||||
break;
|
||||
}
|
||||
|
||||
currentBlock.setTypeId(blockUpdate.typeId);
|
||||
currentBlock.setData(blockUpdate.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//clean up any entities in the chunk
|
||||
Chunk chunk = this.lesserCorner.getChunk();
|
||||
Entity [] entities = chunk.getEntities();
|
||||
for(int i = 0; i < entities.length; i++)
|
||||
{
|
||||
Entity entity = entities[i];
|
||||
if(!(entity instanceof Player)) entity.remove();
|
||||
}
|
||||
|
||||
//show visualization to player
|
||||
Claim claim = new Claim(lesserCorner, greaterCorner, "", new String[] {}, new String[] {}, new String[] {}, new String[] {});
|
||||
Visualization visualization = Visualization.FromClaim(claim, player.getLocation().getBlockY(), VisualizationType.RestoreNature);
|
||||
Visualization.Apply(player, visualization);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,470 @@
|
|||
/*
|
||||
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.ArrayList;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World.Environment;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
//non-main-thread task which processes world data to repair the unnatural
|
||||
//after processing is complete, creates a main thread task to make the necessary changes to the world
|
||||
class RestoreNatureProcessingTask implements Runnable
|
||||
{
|
||||
//world information captured from the main thread
|
||||
//will be updated and sent back to main thread to be applied to the world
|
||||
private BlockSnapshot[][][] snapshots;
|
||||
|
||||
//other information collected from the main thread.
|
||||
//not to be updated, only to be passed back to main thread to provide some context about the operation
|
||||
private int miny;
|
||||
private Environment environment;
|
||||
private Location lesserBoundaryCorner;
|
||||
private Location greaterBoundaryCorner;
|
||||
private Player player; //absolutely must not be accessed. not thread safe.
|
||||
private Biome biome;
|
||||
private int seaLevel;
|
||||
|
||||
//two lists of materials
|
||||
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, Player player)
|
||||
{
|
||||
this.snapshots = snapshots;
|
||||
this.miny = miny;
|
||||
this.environment = environment;
|
||||
this.lesserBoundaryCorner = lesserBoundaryCorner;
|
||||
this.greaterBoundaryCorner = greaterBoundaryCorner;
|
||||
this.biome = biome;
|
||||
this.seaLevel = seaLevel;
|
||||
this.player = player;
|
||||
|
||||
this.notAllowedToHang = new ArrayList<Integer>();
|
||||
this.notAllowedToHang.add(Material.DIRT.getId());
|
||||
this.notAllowedToHang.add(Material.GRASS.getId());
|
||||
this.notAllowedToHang.add(Material.SNOW.getId());
|
||||
this.notAllowedToHang.add(Material.LOG.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 replacements
|
||||
this.playerBlocks = new ArrayList<Integer>();
|
||||
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.OBSIDIAN.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());
|
||||
|
||||
//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 sandy biomes, but not elsewhere
|
||||
if(this.biome == Biome.DESERT || this.biome == Biome.DESERT_HILLS || this.biome == Biome.BEACH)
|
||||
{
|
||||
this.playerBlocks.add(Material.LEAVES.getId());
|
||||
this.playerBlocks.add(Material.LOG.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
//order is important!
|
||||
|
||||
//remove any blocks which are definitely player placed
|
||||
this.removePlayerBlocks();
|
||||
|
||||
//remove natural blocks which are unnaturally hanging in the air
|
||||
this.removeHanging();
|
||||
|
||||
//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();
|
||||
|
||||
//fill water depressions and fix unnatural surface ripples
|
||||
this.fixWater();
|
||||
|
||||
//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 removePlayerBlocks()
|
||||
{
|
||||
int miny = this.miny;
|
||||
if(miny < 1) miny = 1;
|
||||
|
||||
for(int x = 1; x < snapshots.length - 1; x++)
|
||||
{
|
||||
for(int z = 1; z < snapshots[0][0].length - 1; z++)
|
||||
{
|
||||
for(int y = miny; y < snapshots[0].length - 1; y++)
|
||||
{
|
||||
BlockSnapshot block = snapshots[x][y][z];
|
||||
if(this.playerBlocks.contains(block.typeId))
|
||||
{
|
||||
block.typeId = Material.AIR.getId();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeHanging()
|
||||
{
|
||||
int miny = this.miny;
|
||||
if(miny < 1) miny = 1;
|
||||
|
||||
for(int x = 1; x < snapshots.length - 1; x++)
|
||||
{
|
||||
for(int z = 1; z < snapshots[0][0].length - 1; z++)
|
||||
{
|
||||
for(int y = miny; y < snapshots[0].length - 1; y++)
|
||||
{
|
||||
BlockSnapshot block = snapshots[x][y][z];
|
||||
BlockSnapshot underBlock = snapshots[x][y - 1][z];
|
||||
|
||||
if(underBlock.typeId == Material.AIR.getId() || underBlock.typeId == Material.WATER.getId())
|
||||
{
|
||||
if(this.notAllowedToHang.contains(block.typeId))
|
||||
{
|
||||
block.typeId = Material.AIR.getId();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeWallsAndTowers()
|
||||
{
|
||||
int [] excludedBlocksArray = new int []
|
||||
{
|
||||
Material.CACTUS.getId(),
|
||||
Material.LONG_GRASS.getId(),
|
||||
Material.RED_MUSHROOM.getId(),
|
||||
Material.BROWN_MUSHROOM.getId(),
|
||||
Material.DEAD_BUSH.getId(),
|
||||
Material.SAPLING.getId(),
|
||||
Material.YELLOW_FLOWER.getId(),
|
||||
Material.RED_ROSE.getId(),
|
||||
Material.SUGAR_CANE_BLOCK.getId(),
|
||||
Material.VINE.getId(),
|
||||
Material.PUMPKIN.getId(),
|
||||
Material.WATER_LILY.getId(),
|
||||
Material.LEAVES.getId()
|
||||
};
|
||||
|
||||
ArrayList<Integer> excludedBlocks = new ArrayList<Integer>();
|
||||
for(int i = 0; i < excludedBlocksArray.length; i++) excludedBlocks.add(excludedBlocksArray[i]);
|
||||
|
||||
boolean changed;
|
||||
do
|
||||
{
|
||||
changed = false;
|
||||
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);
|
||||
if(excludedBlocks.contains(this.snapshots[x][thisy][z].typeId)) continue;
|
||||
|
||||
int righty = this.highestY(x + 1, z);
|
||||
int lefty = this.highestY(x - 1, z);
|
||||
while(lefty < thisy && righty < thisy)
|
||||
{
|
||||
this.snapshots[x][thisy--][z].typeId = Material.AIR.getId();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
int upy = this.highestY(x, z + 1);
|
||||
int downy = this.highestY(x, z - 1);
|
||||
while(upy < thisy && downy < thisy)
|
||||
{
|
||||
this.snapshots[x][thisy--][z].typeId = Material.AIR.getId();
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}while(changed);
|
||||
}
|
||||
|
||||
private void coverSurfaceStone()
|
||||
{
|
||||
for(int x = 1; x < snapshots.length - 1; x++)
|
||||
{
|
||||
for(int z = 1; z < snapshots[0][0].length - 1; z++)
|
||||
{
|
||||
int y = this.highestY(x, z);
|
||||
BlockSnapshot block = snapshots[x][y][z];
|
||||
|
||||
if(block.typeId == Material.STONE.getId() || block.typeId == Material.GRAVEL.getId() || block.typeId == Material.DIRT.getId())
|
||||
{
|
||||
if(this.biome == Biome.DESERT || this.biome == Biome.DESERT_HILLS || this.biome == Biome.BEACH)
|
||||
{
|
||||
this.snapshots[x][y][z].typeId = Material.SAND.getId();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.snapshots[x][y][z].typeId = Material.GRASS.getId();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void fillHolesAndTrenches()
|
||||
{
|
||||
ArrayList<Integer> fillableBlocks = new ArrayList<Integer>();
|
||||
fillableBlocks.add(Material.AIR.getId());
|
||||
fillableBlocks.add(Material.STATIONARY_WATER.getId());
|
||||
fillableBlocks.add(Material.STATIONARY_LAVA.getId());
|
||||
|
||||
ArrayList<Integer> notSuitableForFillBlocks = new ArrayList<Integer>();
|
||||
notSuitableForFillBlocks.add(Material.LONG_GRASS.getId());
|
||||
notSuitableForFillBlocks.add(Material.CACTUS.getId());
|
||||
notSuitableForFillBlocks.add(Material.STATIONARY_WATER.getId());
|
||||
notSuitableForFillBlocks.add(Material.STATIONARY_LAVA.getId());
|
||||
|
||||
boolean changed;
|
||||
do
|
||||
{
|
||||
changed = false;
|
||||
for(int x = 1; x < snapshots.length - 1; x++)
|
||||
{
|
||||
for(int z = 1; z < snapshots[0][0].length - 1; z++)
|
||||
{
|
||||
for(int y = 0; y < snapshots[0].length - 1; y++)
|
||||
{
|
||||
BlockSnapshot block = this.snapshots[x][y][z];
|
||||
if(!fillableBlocks.contains(block.typeId)) continue;
|
||||
|
||||
BlockSnapshot leftBlock = this.snapshots[x + 1][y][z];
|
||||
BlockSnapshot rightBlock = this.snapshots[x - 1][y][z];
|
||||
|
||||
if(!fillableBlocks.contains(leftBlock.typeId) && !fillableBlocks.contains(rightBlock.typeId))
|
||||
{
|
||||
if(!notSuitableForFillBlocks.contains(rightBlock.typeId))
|
||||
{
|
||||
block.typeId = rightBlock.typeId;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
BlockSnapshot upBlock = this.snapshots[x][y][z + 1];
|
||||
BlockSnapshot downBlock = this.snapshots[x][y][z - 1];
|
||||
|
||||
if(!fillableBlocks.contains(upBlock.typeId) && !fillableBlocks.contains(downBlock.typeId))
|
||||
{
|
||||
if(!notSuitableForFillBlocks.contains(downBlock.typeId))
|
||||
{
|
||||
block.typeId = downBlock.typeId;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}while(changed);
|
||||
}
|
||||
|
||||
private void fixWater()
|
||||
{
|
||||
int miny = this.miny;
|
||||
if(miny < 1) miny = 1;
|
||||
|
||||
boolean changed;
|
||||
|
||||
//remove hanging water or lava
|
||||
for(int x = 1; x < snapshots.length - 1; x++)
|
||||
{
|
||||
for(int z = 1; z < snapshots[0][0].length - 1; z++)
|
||||
{
|
||||
for(int y = miny; y < snapshots[0].length - 1; y++)
|
||||
{
|
||||
BlockSnapshot block = this.snapshots[x][y][z];
|
||||
BlockSnapshot underBlock = this.snapshots[x][y][z];
|
||||
if(block.typeId == Material.STATIONARY_WATER.getId() || block.typeId == Material.STATIONARY_LAVA.getId())
|
||||
{
|
||||
if(underBlock.typeId == Material.AIR.getId() || (underBlock.data != 0))
|
||||
{
|
||||
block.typeId = Material.AIR.getId();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//fill water depressions
|
||||
do
|
||||
{
|
||||
changed = false;
|
||||
for(int y = this.seaLevel - 10; y <= this.seaLevel; y++)
|
||||
{
|
||||
for(int x = 1; x < snapshots.length - 1; x++)
|
||||
{
|
||||
for(int z = 1; z < snapshots[0][0].length - 1; z++)
|
||||
{
|
||||
BlockSnapshot block = snapshots[x][y][z];
|
||||
|
||||
//only consider air blocks and flowing water blocks for upgrade to water source blocks
|
||||
if(block.typeId == Material.AIR.getId() || (block.typeId == Material.STATIONARY_WATER.getId() && block.data != 0))
|
||||
{
|
||||
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];
|
||||
|
||||
//block underneath MUST be source water
|
||||
if(underBlock.typeId != Material.STATIONARY_WATER.getId() || underBlock.data != 0) continue;
|
||||
|
||||
//count adjacent source water blocks
|
||||
byte adjacentSourceWaterCount = 0;
|
||||
if(leftBlock.typeId == Material.STATIONARY_WATER.getId() && leftBlock.data == 0)
|
||||
{
|
||||
adjacentSourceWaterCount++;
|
||||
}
|
||||
if(rightBlock.typeId == Material.STATIONARY_WATER.getId() && rightBlock.data == 0)
|
||||
{
|
||||
adjacentSourceWaterCount++;
|
||||
}
|
||||
if(upBlock.typeId == Material.STATIONARY_WATER.getId() && upBlock.data == 0)
|
||||
{
|
||||
adjacentSourceWaterCount++;
|
||||
}
|
||||
if(downBlock.typeId == Material.STATIONARY_WATER.getId() && downBlock.data == 0)
|
||||
{
|
||||
adjacentSourceWaterCount++;
|
||||
}
|
||||
|
||||
//at least two adjacent blocks must be source water
|
||||
if(adjacentSourceWaterCount >= 2)
|
||||
{
|
||||
block.typeId = Material.STATIONARY_WATER.getId();
|
||||
block.data = 0;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}while(changed);
|
||||
}
|
||||
|
||||
private int highestY(int x, int z)
|
||||
{
|
||||
int y;
|
||||
for(y = snapshots[0].length - 1; y >= 0; y--)
|
||||
{
|
||||
BlockSnapshot block = this.snapshots[x][y][z];
|
||||
if(block.typeId != Material.AIR.getId() &&
|
||||
!(block.typeId == Material.STATIONARY_WATER.getId() && block.data != 0) &&
|
||||
!(block.typeId == Material.STATIONARY_LAVA.getId() && block.data != 0))
|
||||
{
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
return y;
|
||||
}
|
||||
}
|
||||
56
src/me/ryanhamshire/GriefPrevention/SecureClaimTask.java
Normal file
56
src/me/ryanhamshire/GriefPrevention/SecureClaimTask.java
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
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 org.bukkit.entity.Player;
|
||||
|
||||
//secures a claim after a siege looting window has closed
|
||||
class SecureClaimTask implements Runnable
|
||||
{
|
||||
private SiegeData siegeData;
|
||||
|
||||
public SecureClaimTask(SiegeData siegeData)
|
||||
{
|
||||
this.siegeData = siegeData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
//for each claim involved in this siege
|
||||
for(int i = 0; i < this.siegeData.claims.size(); i++)
|
||||
{
|
||||
//lock the doors
|
||||
Claim claim = this.siegeData.claims.get(i);
|
||||
claim.doorsOpen = false;
|
||||
|
||||
//eject bad guys
|
||||
Player [] onlinePlayers = GriefPrevention.instance.getServer().getOnlinePlayers();
|
||||
for(int j = 0; j < onlinePlayers.length; j++)
|
||||
{
|
||||
Player player = onlinePlayers[j];
|
||||
if(claim.contains(player.getLocation(), false, false) && claim.allowAccess(player) != null)
|
||||
{
|
||||
GriefPrevention.sendMessage(player, TextMode.Err, "Looting time is up! Ejected from the claim.");
|
||||
GriefPrevention.instance.ejectPlayer(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/me/ryanhamshire/GriefPrevention/ShovelMode.java
Normal file
28
src/me/ryanhamshire/GriefPrevention/ShovelMode.java
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
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;
|
||||
|
||||
//enumeration for golden shovel modes
|
||||
enum ShovelMode
|
||||
{
|
||||
Basic,
|
||||
Admin,
|
||||
Subdivide,
|
||||
RestoreNature
|
||||
}
|
||||
110
src/me/ryanhamshire/GriefPrevention/SiegeCheckupTask.java
Normal file
110
src/me/ryanhamshire/GriefPrevention/SiegeCheckupTask.java
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
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 org.bukkit.entity.Player;
|
||||
|
||||
//checks to see whether or not a siege should end based on the locations of the players
|
||||
//for example, defender escaped or attacker gave up and left
|
||||
class SiegeCheckupTask implements Runnable
|
||||
{
|
||||
private SiegeData siegeData;
|
||||
|
||||
public SiegeCheckupTask(SiegeData siegeData)
|
||||
{
|
||||
this.siegeData = siegeData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
DataStore dataStore = GriefPrevention.instance.dataStore;
|
||||
Player defender = this.siegeData.defender;
|
||||
Player attacker = this.siegeData.attacker;
|
||||
|
||||
//where is the defender?
|
||||
Claim defenderClaim = dataStore.getClaimAt(defender.getLocation(), false, null);
|
||||
|
||||
//if this is a new claim and he has some permission there, extend the siege to include it
|
||||
if(defenderClaim != null)
|
||||
{
|
||||
String noAccessReason = defenderClaim.allowAccess(defender);
|
||||
if(defenderClaim.canSiege(defender) && noAccessReason == null)
|
||||
{
|
||||
this.siegeData.claims.add(defenderClaim);
|
||||
defenderClaim.siegeData = this.siegeData;
|
||||
}
|
||||
}
|
||||
|
||||
//determine who's close enough to the siege area to be considered "still here"
|
||||
boolean attackerRemains = this.playerRemains(attacker);
|
||||
boolean defenderRemains = this.playerRemains(defender);
|
||||
|
||||
//if they're both here, just plan to come check again later
|
||||
if(attackerRemains && defenderRemains)
|
||||
{
|
||||
this.scheduleAnotherCheck();
|
||||
}
|
||||
|
||||
//otherwise attacker wins if the defender runs away
|
||||
else if(attackerRemains && !defenderRemains)
|
||||
{
|
||||
dataStore.endSiege(this.siegeData, attacker.getName(), defender.getName());
|
||||
}
|
||||
|
||||
//or defender wins if the attacker leaves
|
||||
else if(!attackerRemains && defenderRemains)
|
||||
{
|
||||
dataStore.endSiege(this.siegeData, defender.getName(), attacker.getName());
|
||||
}
|
||||
|
||||
//if they both left, but are still close together, the battle continues (check again later)
|
||||
else if(attacker.getLocation().distanceSquared(defender.getLocation()) < 2500) //50-block radius for chasing
|
||||
{
|
||||
this.scheduleAnotherCheck();
|
||||
}
|
||||
|
||||
//otherwise they both left and aren't close to each other, so call the attacker the winner (defender escaped, possibly after a chase)
|
||||
else
|
||||
{
|
||||
dataStore.endSiege(this.siegeData, attacker.getName(), defender.getName());
|
||||
}
|
||||
}
|
||||
|
||||
//a player has to be within 25 blocks of the edge of a besieged claim to be considered still in the fight
|
||||
private boolean playerRemains(Player player)
|
||||
{
|
||||
for(int i = 0; i < this.siegeData.claims.size(); i++)
|
||||
{
|
||||
Claim claim = this.siegeData.claims.get(i);
|
||||
if(claim.isNear(player.getLocation(), 25))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//schedules another checkup later
|
||||
private void scheduleAnotherCheck()
|
||||
{
|
||||
this.siegeData.checkupTaskID = GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, this, 20L * 60);
|
||||
}
|
||||
}
|
||||
40
src/me/ryanhamshire/GriefPrevention/SiegeData.java
Normal file
40
src/me/ryanhamshire/GriefPrevention/SiegeData.java
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
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.ArrayList;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
//information about an ongoing siege
|
||||
public class SiegeData
|
||||
{
|
||||
public Player defender;
|
||||
public Player attacker;
|
||||
public ArrayList<Claim> claims;
|
||||
public int checkupTaskID;
|
||||
|
||||
public SiegeData(Player attacker, Player defender, Claim claim)
|
||||
{
|
||||
this.defender = defender;
|
||||
this.attacker = attacker;
|
||||
this.claims = new ArrayList<Claim>();
|
||||
this.claims.add(claim);
|
||||
}
|
||||
}
|
||||
31
src/me/ryanhamshire/GriefPrevention/TextMode.java
Normal file
31
src/me/ryanhamshire/GriefPrevention/TextMode.java
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
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 org.bukkit.ChatColor;
|
||||
|
||||
//just a few constants for chat color codes
|
||||
class TextMode
|
||||
{
|
||||
final static ChatColor Info = ChatColor.BLUE;
|
||||
final static ChatColor Instr = ChatColor.YELLOW;
|
||||
final static ChatColor Warn = ChatColor.GOLD;
|
||||
final static ChatColor Err = ChatColor.RED;
|
||||
final static ChatColor Success = ChatColor.GREEN;
|
||||
}
|
||||
100
src/me/ryanhamshire/GriefPrevention/TreeCleanupTask.java
Normal file
100
src/me/ryanhamshire/GriefPrevention/TreeCleanupTask.java
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
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.ArrayList;
|
||||
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
|
||||
//FEATURE: treetops left unnaturally hanging will be automatically cleaned up
|
||||
|
||||
//this main thread task revisits the location of a partially chopped tree from several minutes ago
|
||||
//if any part of the tree is still there and nothing else has been built in its place, remove the remaining parts
|
||||
class TreeCleanupTask implements Runnable
|
||||
{
|
||||
private Block originalChoppedBlock; //first block chopped in the tree
|
||||
private Block originalRootBlock; //where the root of the tree used to be
|
||||
private ArrayList<Block> originalTreeBlocks; //a list of other log blocks determined to be part of this tree
|
||||
|
||||
public TreeCleanupTask(Block originalChoppedBlock, Block originalRootBlock, ArrayList<Block> originalTreeBlocks)
|
||||
{
|
||||
this.originalChoppedBlock = originalChoppedBlock;
|
||||
this.originalRootBlock = originalRootBlock;
|
||||
this.originalTreeBlocks = originalTreeBlocks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
//if this chunk is no longer loaded, load it and come back in a few seconds
|
||||
Chunk chunk = this.originalChoppedBlock.getWorld().getChunkAt(this.originalChoppedBlock);
|
||||
if(!chunk.isLoaded())
|
||||
{
|
||||
chunk.load();
|
||||
GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, this, 100L);
|
||||
return;
|
||||
}
|
||||
|
||||
//if the block originally chopped has been replaced with anything but air, something has been built (or has grown here)
|
||||
//in that case, don't do any cleanup
|
||||
if(this.originalChoppedBlock.getWorld().getBlockAt(this.originalChoppedBlock.getLocation()).getType() != Material.AIR) return;
|
||||
|
||||
//scan the original tree block locations to see if any of them have been replaced
|
||||
for(int i = 0; i < this.originalTreeBlocks.size(); i++)
|
||||
{
|
||||
Location location = this.originalTreeBlocks.get(i).getLocation();
|
||||
Block currentBlock = location.getBlock();
|
||||
|
||||
//if the block has been replaced, stop here, we won't do any cleanup
|
||||
if(currentBlock.getType() != Material.LOG && currentBlock.getType() != Material.AIR)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//otherwise scan again, this time removing any remaining log blocks
|
||||
boolean logsRemaining = false;
|
||||
for(int i = 0; i < this.originalTreeBlocks.size(); i++)
|
||||
{
|
||||
Location location = this.originalTreeBlocks.get(i).getLocation();
|
||||
Block currentBlock = location.getBlock();
|
||||
if(currentBlock.getType() == Material.LOG)
|
||||
{
|
||||
logsRemaining = true;
|
||||
currentBlock.setType(Material.AIR);
|
||||
}
|
||||
}
|
||||
|
||||
//if any were actually removed and we're set to automatically replant griefed trees, place a sapling where the root block was previously
|
||||
if(logsRemaining && GriefPrevention.instance.config_trees_regrowGriefedTrees)
|
||||
{
|
||||
Block currentBlock = this.originalRootBlock.getLocation().getBlock();
|
||||
//make sure there's grass or dirt underneath
|
||||
if(currentBlock.getType() == Material.AIR && (currentBlock.getRelative(BlockFace.DOWN).getType() == Material.DIRT || currentBlock.getRelative(BlockFace.DOWN).getType() == Material.GRASS))
|
||||
{
|
||||
currentBlock.setType(Material.SAPLING);
|
||||
currentBlock.setData(this.originalRootBlock.getData()); //makes the sapling type match the original tree type
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
214
src/me/ryanhamshire/GriefPrevention/Visualization.java
Normal file
214
src/me/ryanhamshire/GriefPrevention/Visualization.java
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
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.ArrayList;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
//represents a visualization sent to a player
|
||||
//FEATURE: to show players visually where claim boundaries are, we send them fake block change packets
|
||||
//the result is that those players see new blocks, but the world hasn't been changed. other players can't see the new blocks, either.
|
||||
public class Visualization
|
||||
{
|
||||
public ArrayList<VisualizationElement> elements = new ArrayList<VisualizationElement>();
|
||||
|
||||
//sends a visualization to a player
|
||||
public static void Apply(Player player, Visualization visualization)
|
||||
{
|
||||
PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getName());
|
||||
|
||||
//if he has any current visualization, clear it first
|
||||
if(playerData.currentVisualization != null)
|
||||
{
|
||||
Visualization.Revert(player);
|
||||
}
|
||||
|
||||
//if he's online, create a task to send him the visualization in about half a second
|
||||
if(player.isOnline())
|
||||
{
|
||||
GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, new VisualizationApplicationTask(player, playerData, visualization), 10L);
|
||||
}
|
||||
}
|
||||
|
||||
//reverts a visualization by sending another block change list, this time with the real world block values
|
||||
public static void Revert(Player player)
|
||||
{
|
||||
PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getName());
|
||||
|
||||
Visualization visualization = playerData.currentVisualization;
|
||||
|
||||
if(playerData.currentVisualization != null)
|
||||
{
|
||||
if(player.isOnline())
|
||||
{
|
||||
for(int i = 0; i < visualization.elements.size(); i++)
|
||||
{
|
||||
VisualizationElement element = visualization.elements.get(i);
|
||||
Block block = element.location.getBlock();
|
||||
player.sendBlockChange(element.location, block.getType(), block.getData());
|
||||
}
|
||||
}
|
||||
|
||||
playerData.currentVisualization = null;
|
||||
}
|
||||
}
|
||||
|
||||
//convenience method to build a visualization from a claim
|
||||
//visualizationType determines the style (gold blocks, silver, red, diamond, etc)
|
||||
public static Visualization FromClaim(Claim claim, int height, VisualizationType visualizationType)
|
||||
{
|
||||
//visualize only top level claims
|
||||
if(claim.parent != null)
|
||||
{
|
||||
return FromClaim(claim.parent, height, visualizationType);
|
||||
}
|
||||
|
||||
Visualization visualization = new Visualization();
|
||||
|
||||
//add subdivisions first
|
||||
for(int i = 0; i < claim.children.size(); i++)
|
||||
{
|
||||
visualization.addClaimElements(claim.children.get(i), height, VisualizationType.Subdivision);
|
||||
}
|
||||
|
||||
//add top level last so that it takes precedence (it shows on top when the child claim boundaries overlap with its boundaries)
|
||||
visualization.addClaimElements(claim, height, visualizationType);
|
||||
|
||||
return visualization;
|
||||
}
|
||||
|
||||
//adds a claim's visualization to the current visualization
|
||||
//handy for combining several visualizations together, as when visualization a top level claim with several subdivisions inside
|
||||
private void addClaimElements(Claim claim, int height, VisualizationType visualizationType)
|
||||
{
|
||||
Location smallXsmallZ = claim.getLesserBoundaryCorner();
|
||||
Location bigXbigZ = claim.getGreaterBoundaryCorner();
|
||||
World world = smallXsmallZ.getWorld();
|
||||
|
||||
int smallx = smallXsmallZ.getBlockX();
|
||||
int smallz = smallXsmallZ.getBlockZ();
|
||||
int bigx = bigXbigZ.getBlockX();
|
||||
int bigz = bigXbigZ.getBlockZ();
|
||||
|
||||
Material cornerMaterial;
|
||||
Material accentMaterial;
|
||||
|
||||
if(visualizationType == VisualizationType.Claim)
|
||||
{
|
||||
cornerMaterial = Material.GLOWSTONE;
|
||||
accentMaterial = Material.GOLD_BLOCK;
|
||||
}
|
||||
|
||||
else if(visualizationType == VisualizationType.Subdivision)
|
||||
{
|
||||
cornerMaterial = Material.IRON_BLOCK;
|
||||
accentMaterial = Material.WOOL;
|
||||
}
|
||||
|
||||
else if(visualizationType == VisualizationType.RestoreNature)
|
||||
{
|
||||
cornerMaterial = Material.DIAMOND_BLOCK;
|
||||
accentMaterial = Material.DIAMOND_BLOCK;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
cornerMaterial = Material.LAVA;
|
||||
accentMaterial = Material.NETHERRACK;
|
||||
}
|
||||
|
||||
//bottom left corner
|
||||
this.elements.add(new VisualizationElement(getVisibleLocation(world, smallx, height, smallz), cornerMaterial, (byte)0));
|
||||
this.elements.add(new VisualizationElement(getVisibleLocation(world, smallx + 1, height, smallz), accentMaterial, (byte)0));
|
||||
this.elements.add(new VisualizationElement(getVisibleLocation(world, smallx, height, smallz + 1), accentMaterial, (byte)0));
|
||||
|
||||
//bottom right corner
|
||||
this.elements.add(new VisualizationElement(getVisibleLocation(world, bigx, height, smallz), cornerMaterial, (byte)0));
|
||||
this.elements.add(new VisualizationElement(getVisibleLocation(world, bigx - 1, height, smallz), accentMaterial, (byte)0));
|
||||
this.elements.add(new VisualizationElement(getVisibleLocation(world, bigx, height, smallz + 1), accentMaterial, (byte)0));
|
||||
|
||||
//top right corner
|
||||
this.elements.add(new VisualizationElement(getVisibleLocation(world, bigx, height, bigz), cornerMaterial, (byte)0));
|
||||
this.elements.add(new VisualizationElement(getVisibleLocation(world, bigx - 1, height, bigz), accentMaterial, (byte)0));
|
||||
this.elements.add(new VisualizationElement(getVisibleLocation(world, bigx, height, bigz - 1), accentMaterial, (byte)0));
|
||||
|
||||
//top left corner
|
||||
this.elements.add(new VisualizationElement(getVisibleLocation(world, smallx, height, bigz), cornerMaterial, (byte)0));
|
||||
this.elements.add(new VisualizationElement(getVisibleLocation(world, smallx + 1, height, bigz), accentMaterial, (byte)0));
|
||||
this.elements.add(new VisualizationElement(getVisibleLocation(world, smallx, height, bigz - 1), accentMaterial, (byte)0));
|
||||
|
||||
//top line
|
||||
for(int x = smallx + 10; x < bigx - 10; x += 10)
|
||||
{
|
||||
this.elements.add(new VisualizationElement(getVisibleLocation(world, x, height, bigz), accentMaterial, (byte)0));
|
||||
}
|
||||
|
||||
//bottom line
|
||||
for(int x = smallx + 10; x < bigx - 10; x += 10)
|
||||
{
|
||||
this.elements.add(new VisualizationElement(getVisibleLocation(world, x, height, smallz), accentMaterial, (byte)0));
|
||||
}
|
||||
|
||||
//left line
|
||||
for(int z = smallz + 10; z < bigz - 10; z += 10)
|
||||
{
|
||||
this.elements.add(new VisualizationElement(getVisibleLocation(world, smallx, height, z), accentMaterial, (byte)0));
|
||||
}
|
||||
|
||||
//right line
|
||||
for(int z = smallz + 10; z < bigz - 10; z += 10)
|
||||
{
|
||||
this.elements.add(new VisualizationElement(getVisibleLocation(world, bigx, height, z), accentMaterial, (byte)0));
|
||||
}
|
||||
}
|
||||
|
||||
//finds a block the player can probably see. this is how visualizations "cling" to the ground or ceiling
|
||||
private static Location getVisibleLocation(World world, int x, int y, int z)
|
||||
{
|
||||
Block block = world.getBlockAt(x, y, z);
|
||||
BlockFace direction = (isTransparent(block)) ? BlockFace.DOWN : BlockFace.UP;
|
||||
|
||||
while( block.getY() >= 1 &&
|
||||
block.getY() < world.getMaxHeight() - 1 &&
|
||||
(!isTransparent(block.getRelative(BlockFace.UP)) || isTransparent(block)))
|
||||
{
|
||||
block = block.getRelative(direction);
|
||||
}
|
||||
|
||||
return block.getLocation();
|
||||
}
|
||||
|
||||
//helper method for above. allows visualization blocks to sit underneath partly transparent blocks like grass and fence
|
||||
private static boolean isTransparent(Block block)
|
||||
{
|
||||
return ( block.getType() == Material.AIR ||
|
||||
block.getType() == Material.LONG_GRASS ||
|
||||
block.getType() == Material.FENCE ||
|
||||
block.getType() == Material.LEAVES ||
|
||||
block.getType() == Material.RED_ROSE ||
|
||||
block.getType() == Material.CHEST ||
|
||||
block.getType() == Material.YELLOW_FLOWER );
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
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 org.bukkit.entity.Player;
|
||||
|
||||
//applies a visualization for a player by sending him block change packets
|
||||
class VisualizationApplicationTask implements Runnable
|
||||
{
|
||||
private Visualization visualization;
|
||||
private Player player;
|
||||
private PlayerData playerData;
|
||||
|
||||
public VisualizationApplicationTask(Player player, PlayerData playerData, Visualization visualization)
|
||||
{
|
||||
this.visualization = visualization;
|
||||
this.playerData = playerData;
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
//for each element (=block) of the visualization
|
||||
for(int i = 0; i < visualization.elements.size(); i++)
|
||||
{
|
||||
VisualizationElement element = visualization.elements.get(i);
|
||||
|
||||
//send the player a fake block change event
|
||||
player.sendBlockChange(element.location, element.visualizedMaterial, element.visualizedData);
|
||||
}
|
||||
|
||||
//remember the visualization applied to this player for later (so it can be inexpensively reverted)
|
||||
playerData.currentVisualization = visualization;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
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 org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
|
||||
//represents a "fake" block sent to a player as part of a visualization
|
||||
public class VisualizationElement
|
||||
{
|
||||
public Location location;
|
||||
public Material visualizedMaterial;
|
||||
public byte visualizedData;
|
||||
|
||||
public VisualizationElement(Location location, Material visualizedMaterial, byte visualizedData)
|
||||
{
|
||||
this.location = location;
|
||||
this.visualizedMaterial= visualizedMaterial;
|
||||
this.visualizedData = visualizedData;
|
||||
}
|
||||
}
|
||||
28
src/me/ryanhamshire/GriefPrevention/VisualizationType.java
Normal file
28
src/me/ryanhamshire/GriefPrevention/VisualizationType.java
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
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;
|
||||
|
||||
//just an enumeration of the visualization types, which determine what materials will be for the fake blocks
|
||||
public enum VisualizationType
|
||||
{
|
||||
Claim,
|
||||
Subdivision,
|
||||
ErrorClaim,
|
||||
RestoreNature
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user