/*
ChunkClaim Plugin for Minecraft Bukkit Servers
Copyright (C) 2012 Felix Schmidt
This file is part of ChunkClaim.
ChunkClaim 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.
ChunkClaim 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 ChunkClaim. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.schmidtbochum.chunkclaim;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
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.BlockDispenseEvent;
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.event.world.StructureGrowEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
public class BlockEventHandler implements Listener {
private DataStore dataStore;
public BlockEventHandler(DataStore dataStore) {
this.dataStore = dataStore;
}
//when a player breaks a block...
@EventHandler (ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onBlockBreak(BlockBreakEvent event) {
if(!ChunkClaim.plugin.config_worlds.contains(event.getBlock().getWorld().getName())) return;
Player player = event.getPlayer();
Block block = event.getBlock();
Location location = block.getLocation();
PlayerData playerData = this.dataStore.getPlayerData(player.getName());
Chunk chunk = dataStore.getChunkAt(location, playerData.lastChunk);
if(playerData.ignorechunks) return;
if(chunk == null) {
String playerName = player.getName();
if(!player.hasPermission("chunkclaim.claim")) {
ChunkClaim.plugin.sendMsg(player,"You don't have permissions for claiming chunks.");
event.setCancelled(true);
return;
}
if(!dataStore.ownsNear(location, playerName)) {
if(!ChunkClaim.plugin.config_nextToForce && !player.hasPermission("chunkclaim.admin")) {
ChunkClaim.plugin.sendMsg(player,"You don't own a chunk next to this one.");
ChunkClaim.plugin.sendMsg(player,"Confirm with /chunk claim. Please don't spam claimed chunks.");
}
else
{
ArrayList<Chunk> playerChunks = ChunkClaim.plugin.dataStore.getAllChunksForPlayer(playerName);
if(playerChunks.size()>0) {
ChunkClaim.plugin.sendMsg(player,"You can only build next to your first claimed chunk.");
}
else
{
ChunkClaim.plugin.sendMsg(player,"You don't own a chunk next to this one.");
ChunkClaim.plugin.sendMsg(player,"Confirm with /chunk claim. Please don't spam claimed chunks.");
}
}
event.setCancelled(true);
Visualization visualization = Visualization.FromBukkitChunk(location.getChunk(), location.getBlockY(), VisualizationType.Public, location);
Visualization.Apply(player, visualization);
return;
} else
if(playerData.getCredits() > 0) {
Chunk newChunk = new Chunk(location,playerName,playerData.builderNames);
this.dataStore.addChunk(newChunk);
playerData.credits--;
playerData.lastChunk=newChunk;
//newChunk.modify();
this.dataStore.savePlayerData(playerName, playerData);
ChunkClaim.plugin.sendMsg(player,"You claimed this chunk. Credits left: " + playerData.getCredits());
Visualization visualization = Visualization.FromChunk(newChunk, location.getBlockY(), VisualizationType.Chunk, location);
Visualization.Apply(player, visualization);
} else {
ChunkClaim.plugin.sendMsg(player,"Not enough credits to claim this chunk.");
if(playerData.lastChunk!=chunk) {
playerData.lastChunk=chunk;
Visualization visualization = Visualization.FromBukkitChunk(location.getChunk(), location.getBlockY(), VisualizationType.Public, location);
Visualization.Apply(player, visualization);
}
event.setCancelled(true);
}
return;
}
else if(chunk.isTrusted(player.getName())) {
/*
if(playerData.lastChunk!=chunk) {
playerData.lastChunk=chunk;
Visualization visualization = Visualization.FromChunk(chunk, location.getBlockY(), VisualizationType.Chunk, location);
Visualization.Apply(player, visualization);
}
*/
//chunk.modify();
return;
} else {
ChunkClaim.plugin.sendMsg(player,"You don't have " + chunk.ownerName + "'s permission to build here.");
if(playerData.lastChunk!=chunk) {
playerData.lastChunk=chunk;
Visualization visualization = Visualization.FromChunk(chunk, location.getBlockY(), VisualizationType.ErrorChunk, location);
Visualization.Apply(player, visualization);
}
event.setCancelled(true);
return;
}
}
//when a player places a block...
@EventHandler (ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onBlockPlace(BlockPlaceEvent event) {
if(!ChunkClaim.plugin.config_worlds.contains(event.getBlock().getWorld().getName())) return;
Player player = event.getPlayer();
Block block = event.getBlock();
Location location = block.getLocation();
PlayerData playerData = this.dataStore.getPlayerData(player.getName());
Chunk chunk = dataStore.getChunkAt(location, playerData.lastChunk);
if(playerData.ignorechunks) return;
if(chunk == null) {
if(!player.hasPermission("chunkclaim.claim")) {
ChunkClaim.plugin.sendMsg(player,"You don't have permissions for claiming chunks.");
event.setCancelled(true);
}
String playerName = player.getName();
//prevent fire spam
if(block.getType() == Material.FIRE) {
event.setCancelled(true);
return;
}
//prevent tree spam
if(block.getType() == Material.SAPLING) {
ChunkClaim.plugin.sendMsg(player,"Please dont spam chunks with trees.");
event.setCancelled(true);
return;
}
if(!dataStore.ownsNear(location, playerName)) {
if(!ChunkClaim.plugin.config_nextToForce && !player.hasPermission("chunkclaim.admin")) {
ChunkClaim.plugin.sendMsg(player,"You don't own a chunk next to this one.");
ChunkClaim.plugin.sendMsg(player,"Confirm with /chunk claim. Please don't spam claimed chunks.");
}
else
{
ArrayList<Chunk> playerChunks = ChunkClaim.plugin.dataStore.getAllChunksForPlayer(playerName);
if(playerChunks.size()>0) {
ChunkClaim.plugin.sendMsg(player,"You can only build next to your first claimed chunk.");
}
else
{
ChunkClaim.plugin.sendMsg(player,"You don't own a chunk next to this one.");
ChunkClaim.plugin.sendMsg(player,"Confirm with /chunk claim. Please don't spam claimed chunks.");
}
}
event.setCancelled(true);
Visualization visualization = Visualization.FromBukkitChunk(location.getChunk(), location.getBlockY(), VisualizationType.Public, location);
Visualization.Apply(player, visualization);
return;
} else
//check credits
if(playerData.getCredits() <= 0) {
ChunkClaim.plugin.sendMsg(player,"Not enough credits to claim this chunk.");
Visualization visualization = Visualization.FromBukkitChunk(location.getChunk(), location.getBlockY(), VisualizationType.Public, location);
Visualization.Apply(player, visualization);
event.setCancelled(true);
return;
}
//claim the chunk
Chunk newChunk = new Chunk(location,playerName,playerData.builderNames);
this.dataStore.addChunk(newChunk);
playerData.credits--;
playerData.lastChunk=newChunk;
newChunk.modify();
this.dataStore.savePlayerData(playerName, playerData);
ChunkClaim.plugin.sendMsg(player,"You claimed this chunk. Credits left: " + playerData.getCredits());
Visualization visualization = Visualization.FromChunk(newChunk, location.getBlockY(), VisualizationType.Chunk, location);
Visualization.Apply(player, visualization);
return;
}
else if(chunk.isTrusted(player.getName())) {
/*
if(playerData.lastChunk!=chunk) {
playerData.lastChunk=chunk;
Visualization visualization = Visualization.FromChunk(chunk, location.getBlockY(), VisualizationType.Chunk, location);
Visualization.Apply(player, visualization);
}
*/
chunk.modify();
return;
} else {
ChunkClaim.plugin.sendMsg(player,"You don't have " + chunk.ownerName + "'s permission to build here.");
if(playerData.lastChunk!=chunk) {
playerData.lastChunk=chunk;
Visualization visualization = Visualization.FromChunk(chunk, location.getBlockY(), VisualizationType.ErrorChunk, location);
Visualization.Apply(player, visualization);
}
event.setCancelled(true);
return;
}
}
//blocks "pushing" other players' blocks around (pistons)
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onBlockPistonExtend (BlockPistonExtendEvent event) {
if(!ChunkClaim.plugin.config_worlds.contains(event.getBlock().getWorld().getName())) return;
List<Block> blocks = event.getBlocks();
Block piston = event.getBlock();
Chunk pistonChunk = this.dataStore.getChunkAt(piston.getLocation(), null);
String pistonOwnerName = (pistonChunk==null)? null : pistonChunk.ownerName;
//if no blocks moving, then only check to make sure we're not pushing into a claim from outside
//this avoids pistons breaking non-solids just inside a claim, like torches, doors, and touchplates
if(blocks.size() == 0) {
Block invadedBlock = piston.getRelative(event.getDirection());
Chunk invadedBlockChunk = this.dataStore.getChunkAt(invadedBlock.getLocation(), null);
String invadedBlockOwnerName = (invadedBlockChunk==null)? null : invadedBlockChunk.ownerName;
if(pistonOwnerName==null || invadedBlockOwnerName==null || (!invadedBlockChunk.isTrusted(pistonOwnerName))) {
event.setCancelled(true);
return;
}
return;
}
Chunk chunk;
//which blocks are being pushed?
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);
chunk = this.dataStore.getChunkAt(block.getLocation(), null);
if(chunk == null || !chunk.isTrusted(pistonOwnerName)) {
event.setCancelled(true);
event.getBlock().getWorld().createExplosion(event.getBlock().getLocation(), 0);
event.getBlock().getWorld().dropItem(event.getBlock().getLocation(), new ItemStack(event.getBlock().getType()));
event.getBlock().setType(Material.AIR);
return;
}
}
Block block = blocks.get(blocks.size()-1).getRelative(event.getDirection());
chunk = this.dataStore.getChunkAt(block.getLocation(), null);
if(chunk == null || !chunk.isTrusted(pistonOwnerName)) {
event.setCancelled(true);
event.getBlock().getWorld().createExplosion(event.getBlock().getLocation(), 0);
event.getBlock().getWorld().dropItem(event.getBlock().getLocation(), new ItemStack(event.getBlock().getType()));
event.getBlock().setType(Material.AIR);
return;
}
}
//blocks theft by pulling blocks out of a claim (again pistons)
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onBlockPistonRetract (BlockPistonRetractEvent event) {
if(!ChunkClaim.plugin.config_worlds.contains(event.getBlock().getWorld().getName())) return;
//we only care about sticky pistons
if(!event.isSticky()) return;
//who owns the moving block, if anyone?
Chunk movingBlockChunk = this.dataStore.getChunkAt(event.getRetractLocation(), null);
if(movingBlockChunk == null) {
event.setCancelled(true);
return;
}
String movingBlockOwnerName = movingBlockChunk.ownerName;
//who owns the piston, if anyone?
Chunk pistonChunk = this.dataStore.getChunkAt(event.getBlock().getLocation(), null);
if(pistonChunk == null) {
event.setCancelled(true);
return;
}
String pistonOwnerName = pistonChunk.ownerName;
if((!pistonChunk.isTrusted(movingBlockOwnerName) && !movingBlockChunk.isTrusted(pistonOwnerName))) {
event.setCancelled(true);
return;
}
}
//blocks are ignited ONLY by flint and steel
@EventHandler(priority = EventPriority.LOWEST)
public void onBlockIgnite (BlockIgniteEvent igniteEvent) {
if(!ChunkClaim.plugin.config_worlds.contains(igniteEvent.getBlock().getWorld().getName())) return;
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.LOWEST)
public void onBlockSpread (BlockSpreadEvent spreadEvent) {
if(!ChunkClaim.plugin.config_worlds.contains(spreadEvent.getBlock().getWorld().getName())) return;
if(spreadEvent.getSource().getType() == Material.FIRE) {
spreadEvent.setCancelled(true);
}
}
//blocks are not destroyed by fire
@EventHandler(priority = EventPriority.LOWEST)
public void onBlockBurn (BlockBurnEvent burnEvent) {
if(!ChunkClaim.plugin.config_worlds.contains(burnEvent.getBlock().getWorld().getName())) return;
burnEvent.setCancelled(true);
}
//ensures fluids don't flow out of chunks, unless into another chunk where the owner is trusted to build
private Chunk lastSpreadChunk = null;
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onBlockFromTo (BlockFromToEvent spreadEvent) {
if(!ChunkClaim.plugin.config_worlds.contains(spreadEvent.getBlock().getWorld().getName())) return;
//always allow fluids to flow straight down
if(spreadEvent.getFace() == BlockFace.DOWN) return;
//from where?
Block fromBlock = spreadEvent.getBlock();
Chunk fromChunk = this.dataStore.getChunkAt(fromBlock.getLocation(), this.lastSpreadChunk);
if(fromChunk != null) {
this.lastSpreadChunk = fromChunk;
}
//where to?
Block toBlock = spreadEvent.getToBlock();
Chunk toChunk = this.dataStore.getChunkAt(toBlock.getLocation(), fromChunk);
//if it's within the same claim or wilderness to wilderness, allow it
if(fromChunk == toChunk) return;
//block any spread into the wilderness from a claim
if(fromChunk != null && toChunk == null) {
spreadEvent.setCancelled(true);
return;
}
//if spreading into a claim
else if(toChunk != null) {
//who owns the spreading block, if anyone?
String fromOwner = null;
if(fromChunk != null) {
fromOwner = fromChunk.ownerName;
}
//cancel unless the owner of the spreading block is allowed to build in the receiving claim
if(fromOwner == null || !toChunk.isTrusted(fromOwner)) {
spreadEvent.setCancelled(true);
}
}
}
//ensures dispensers can't be used to dispense a block(like water or lava) or item across a claim boundary
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onDispense(BlockDispenseEvent dispenseEvent) {
if(!ChunkClaim.plugin.config_worlds.contains(dispenseEvent.getBlock().getWorld().getName())) return;
//from where?
Block fromBlock = dispenseEvent.getBlock();
//to where?
Vector velocity = dispenseEvent.getVelocity();
int xChange = 0;
int zChange = 0;
if(Math.abs(velocity.getX()) > Math.abs(velocity.getZ())) {
if(velocity.getX() > 0) xChange = 1; else xChange = -1;
} else {
if(velocity.getZ() > 0) zChange = 1; else zChange = -1;
}
Block toBlock = fromBlock.getRelative(xChange, 0, zChange);
Chunk fromChunk = this.dataStore.getChunkAt(fromBlock.getLocation(), null);
Chunk toChunk = this.dataStore.getChunkAt(toBlock.getLocation(), fromChunk);
Material materialDispensed = dispenseEvent.getItem().getType();
if(materialDispensed == Material.WATER_BUCKET || materialDispensed == Material.LAVA_BUCKET) {
//wilderness is NOT OK
if(fromChunk == null || toChunk == null) {
dispenseEvent.setCancelled(true);
return;
}
//within chunks is OK
if(fromChunk == toChunk) return;
//chunks are ok
if(toChunk.isTrusted(fromChunk.ownerName)) {
return;
}
//everything else is NOT OK
dispenseEvent.setCancelled(true);
}
}
@EventHandler(ignoreCancelled = true)
public void onTreeGrow (StructureGrowEvent growEvent) {
if(!ChunkClaim.plugin.config_worlds.contains(growEvent.getLocation().getBlock().getWorld().getName())) return;
Location rootLocation = growEvent.getLocation();
Chunk rootChunk = this.dataStore.getChunkAt(rootLocation, null);
String rootOwnerName = (rootChunk==null) ? null : rootChunk.ownerName;
//for each block growing
for(int i = 0; i < growEvent.getBlocks().size(); i++) {
BlockState block = growEvent.getBlocks().get(i);
Chunk blockChunk = this.dataStore.getChunkAt(block.getLocation(), rootChunk);
if(blockChunk != null) {
if(rootOwnerName == null || !blockChunk.isTrusted(rootOwnerName)) {
growEvent.getBlocks().remove(i--);
}
} else if(blockChunk == null && rootOwnerName != null) {
growEvent.getBlocks().remove(i--);
}
}
}
}