1160 lines
58 KiB
Java
1160 lines
58 KiB
Java
/*
|
|
* This file is part of GriefDefender, licensed under the MIT License (MIT).
|
|
*
|
|
* Copyright (c) bloodmc
|
|
* Copyright (c) contributors
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
package com.griefdefender.listener;
|
|
|
|
import com.flowpowered.math.vector.Vector3i;
|
|
import com.google.common.collect.ImmutableMap;
|
|
import com.google.common.reflect.TypeToken;
|
|
import com.griefdefender.GDPlayerData;
|
|
import com.griefdefender.GDTimings;
|
|
import com.griefdefender.GriefDefenderPlugin;
|
|
import com.griefdefender.api.GriefDefender;
|
|
import com.griefdefender.api.Tristate;
|
|
import com.griefdefender.api.claim.Claim;
|
|
import com.griefdefender.api.claim.ClaimResult;
|
|
import com.griefdefender.api.claim.ClaimTypes;
|
|
import com.griefdefender.api.claim.ClaimVisualTypes;
|
|
import com.griefdefender.api.claim.TrustTypes;
|
|
import com.griefdefender.api.economy.PaymentType;
|
|
import com.griefdefender.api.permission.flag.Flags;
|
|
import com.griefdefender.api.permission.option.Options;
|
|
import com.griefdefender.cache.EventResultCache;
|
|
import com.griefdefender.cache.MessageCache;
|
|
import com.griefdefender.cache.PermissionHolderCache;
|
|
import com.griefdefender.claim.GDClaim;
|
|
import com.griefdefender.claim.GDClaimManager;
|
|
import com.griefdefender.configuration.GriefDefenderConfig;
|
|
import com.griefdefender.configuration.MessageStorage;
|
|
import com.griefdefender.event.GDCauseStackManager;
|
|
import com.griefdefender.internal.registry.BlockTypeRegistryModule;
|
|
import com.griefdefender.internal.registry.GDBlockType;
|
|
import com.griefdefender.internal.util.BlockUtil;
|
|
import com.griefdefender.internal.util.NMSUtil;
|
|
import com.griefdefender.internal.visual.GDClaimVisual;
|
|
import com.griefdefender.permission.GDPermissionManager;
|
|
import com.griefdefender.permission.GDPermissionUser;
|
|
import com.griefdefender.permission.GDPermissions;
|
|
import com.griefdefender.permission.flag.GDFlags;
|
|
import com.griefdefender.storage.BaseStorage;
|
|
import com.griefdefender.util.BlockPosCache;
|
|
import com.griefdefender.util.CauseContextHelper;
|
|
import com.griefdefender.util.SignUtil;
|
|
import com.griefdefender.util.SpongeUtil;
|
|
import net.kyori.text.Component;
|
|
import net.kyori.text.TextComponent;
|
|
import net.kyori.text.format.TextColor;
|
|
import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
|
|
import org.spongepowered.api.Sponge;
|
|
import org.spongepowered.api.block.BlockSnapshot;
|
|
import org.spongepowered.api.block.BlockState;
|
|
import org.spongepowered.api.block.BlockType;
|
|
import org.spongepowered.api.block.BlockTypes;
|
|
import org.spongepowered.api.block.tileentity.Piston;
|
|
import org.spongepowered.api.block.tileentity.Sign;
|
|
import org.spongepowered.api.block.tileentity.TileEntity;
|
|
import org.spongepowered.api.block.tileentity.carrier.Chest;
|
|
import org.spongepowered.api.data.Transaction;
|
|
import org.spongepowered.api.data.key.Keys;
|
|
import org.spongepowered.api.entity.Entity;
|
|
import org.spongepowered.api.entity.FallingBlock;
|
|
import org.spongepowered.api.entity.Item;
|
|
import org.spongepowered.api.entity.hanging.ItemFrame;
|
|
import org.spongepowered.api.entity.living.player.Player;
|
|
import org.spongepowered.api.entity.living.player.User;
|
|
import org.spongepowered.api.event.Listener;
|
|
import org.spongepowered.api.event.Order;
|
|
import org.spongepowered.api.event.block.ChangeBlockEvent;
|
|
import org.spongepowered.api.event.block.CollideBlockEvent;
|
|
import org.spongepowered.api.event.block.NotifyNeighborBlockEvent;
|
|
import org.spongepowered.api.event.block.tileentity.ChangeSignEvent;
|
|
import org.spongepowered.api.event.cause.Cause;
|
|
import org.spongepowered.api.event.cause.EventContext;
|
|
import org.spongepowered.api.event.cause.EventContextKeys;
|
|
import org.spongepowered.api.event.filter.cause.Root;
|
|
import org.spongepowered.api.event.world.ExplosionEvent;
|
|
import org.spongepowered.api.item.ItemTypes;
|
|
import org.spongepowered.api.item.inventory.ItemStackSnapshot;
|
|
import org.spongepowered.api.text.Text;
|
|
import org.spongepowered.api.util.Direction;
|
|
import org.spongepowered.api.world.DimensionTypes;
|
|
import org.spongepowered.api.world.LocatableBlock;
|
|
import org.spongepowered.api.world.Location;
|
|
import org.spongepowered.api.world.World;
|
|
import org.spongepowered.api.world.explosion.Explosion;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
|
|
//event handlers related to blocks
|
|
public class BlockEventHandler {
|
|
|
|
private int lastBlockPreTick = -1;
|
|
private boolean lastBlockPreCancelled = false;
|
|
|
|
// convenience reference to singleton datastore
|
|
private final BaseStorage dataStore;
|
|
|
|
// constructor
|
|
public BlockEventHandler(BaseStorage dataStore) {
|
|
this.dataStore = dataStore;
|
|
}
|
|
|
|
@Listener(order = Order.FIRST, beforeModifications = true)
|
|
public void onBlockPre(ChangeBlockEvent.Pre event) {
|
|
lastBlockPreTick = Sponge.getServer().getRunningTimeTicks();
|
|
if (GriefDefenderPlugin.isSourceIdBlacklisted("block-pre", event.getSource(), event.getLocations().get(0).getExtent().getProperties())) {
|
|
return;
|
|
}
|
|
|
|
final World world = event.getLocations().get(0).getExtent();
|
|
if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
|
|
GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
|
|
final Cause cause = event.getCause();
|
|
final EventContext context = event.getContext();
|
|
final User user = CauseContextHelper.getEventUser(event);
|
|
final boolean hasFakePlayer = context.containsKey(EventContextKeys.FAKE_PLAYER);
|
|
|
|
if (user != null) {
|
|
if (context.containsKey(EventContextKeys.PISTON_RETRACT)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
final LocatableBlock locatableBlock = cause.first(LocatableBlock.class).orElse(null);
|
|
final TileEntity tileEntity = cause.first(TileEntity.class).orElse(null);
|
|
Entity sourceEntity = null;
|
|
// Always use TE as source if available
|
|
final Object source = tileEntity != null ? tileEntity : cause.root();
|
|
Location<World> sourceLocation = locatableBlock != null ? locatableBlock.getLocation() : tileEntity != null ? tileEntity.getLocation() : null;
|
|
if (sourceLocation == null && source instanceof Entity) {
|
|
// check entity
|
|
sourceEntity = ((Entity) source);
|
|
sourceLocation = sourceEntity.getLocation();
|
|
}
|
|
final boolean pistonExtend = context.containsKey(EventContextKeys.PISTON_EXTEND);
|
|
boolean isLiquidSource = context.containsKey(EventContextKeys.LIQUID_FLOW);
|
|
if (!isLiquidSource && locatableBlock != null && locatableBlock.getBlockState().getType() == BlockTypes.FLOWING_WATER) {
|
|
isLiquidSource = true;
|
|
}
|
|
final boolean isFireSource = isLiquidSource ? false : context.containsKey(EventContextKeys.FIRE_SPREAD);
|
|
final boolean isLeafDecay = context.containsKey(EventContextKeys.LEAVES_DECAY);
|
|
if (!GDFlags.LEAF_DECAY && isLeafDecay) {
|
|
return;
|
|
}
|
|
if (!GDFlags.LIQUID_FLOW && isLiquidSource) {
|
|
return;
|
|
}
|
|
if (!GDFlags.BLOCK_SPREAD && isFireSource) {
|
|
return;
|
|
}
|
|
|
|
lastBlockPreCancelled = false;
|
|
final boolean isForgePlayerBreak = context.containsKey(EventContextKeys.PLAYER_BREAK);
|
|
GDTimings.BLOCK_PRE_EVENT.startTimingIfSync();
|
|
// Handle player block breaks separately
|
|
if (isForgePlayerBreak && !hasFakePlayer && source instanceof Player) {
|
|
final Player player = (Player) source;
|
|
GDClaim targetClaim = null;
|
|
final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getPlayerData(world, player.getUniqueId());
|
|
for (Location<World> location : event.getLocations()) {
|
|
if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.BLOCK_BREAK.getName(), location.getBlock(), world.getProperties())) {
|
|
GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
|
|
targetClaim = this.dataStore.getClaimAt(location, targetClaim);
|
|
if (location.getBlockType() == BlockTypes.AIR) {
|
|
continue;
|
|
}
|
|
if (!checkSurroundings(event, location, player, playerData, targetClaim)) {
|
|
event.setCancelled(true);
|
|
GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
|
|
// check overrides
|
|
final Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, Flags.BLOCK_BREAK, source, location.getBlock(), player, TrustTypes.BUILDER, true);
|
|
if (result != Tristate.TRUE) {
|
|
final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_BUILD,
|
|
ImmutableMap.of(
|
|
"player", targetClaim.getOwnerName()));
|
|
GriefDefenderPlugin.sendClaimDenyMessage(targetClaim, player, message);
|
|
event.setCancelled(true);
|
|
lastBlockPreCancelled = true;
|
|
GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
}
|
|
|
|
GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
|
|
if (sourceLocation != null) {
|
|
GDPlayerData playerData = null;
|
|
if (user != null) {
|
|
playerData = GriefDefenderPlugin.getInstance().dataStore.getPlayerData(world, user.getUniqueId());
|
|
}
|
|
GDClaim sourceClaim = this.dataStore.getClaimAt(sourceLocation);
|
|
GDClaim targetClaim = null;
|
|
List<Location<World>> sourceLocations = event.getLocations();
|
|
if (pistonExtend) {
|
|
// check next block in extend direction
|
|
sourceLocations = new ArrayList<>(event.getLocations());
|
|
Location<World> location = sourceLocations.get(sourceLocations.size() - 1);
|
|
final Direction direction = locatableBlock.getLocation().getBlock().get(Keys.DIRECTION).get();
|
|
final Location<World> dirLoc = location.getBlockRelative(direction);
|
|
sourceLocations.add(dirLoc);
|
|
}
|
|
for (Location<World> location : sourceLocations) {
|
|
// Mods such as enderstorage will send chest updates to itself
|
|
// We must ignore cases like these to avoid issues with mod
|
|
if (tileEntity != null) {
|
|
if (location.getPosition().equals(tileEntity.getLocation().getPosition())) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
final BlockState blockState = location.getBlock();
|
|
targetClaim = this.dataStore.getClaimAt(location, targetClaim);
|
|
// If a player successfully interacted with a block recently such as a pressure plate, ignore check
|
|
// This fixes issues such as pistons not being able to extend
|
|
if (user != null && !isForgePlayerBreak && playerData != null && playerData.eventResultCache != null && playerData.eventResultCache.checkEventResultCache(targetClaim, "block-pre") == Tristate.TRUE) {
|
|
if (!isLiquidSource && !isFireSource && !isLeafDecay) {
|
|
GDPermissionManager.getInstance().processEventLog(event, location, targetClaim, Flags.BLOCK_BREAK.getPermission(), source, blockState, user, playerData.eventResultCache.lastTrust, Tristate.TRUE);
|
|
}
|
|
continue;
|
|
}
|
|
if (user != null && targetClaim.isUserTrusted(user, TrustTypes.BUILDER)) {
|
|
if (!isLiquidSource && !isFireSource && !isLeafDecay) {
|
|
GDPermissionManager.getInstance().processEventLog(event, location, targetClaim, Flags.BLOCK_BREAK.getPermission(), source, blockState, user, TrustTypes.BUILDER.getName().toLowerCase(), Tristate.TRUE);
|
|
}
|
|
continue;
|
|
}
|
|
if (sourceClaim.getOwnerUniqueId().equals(targetClaim.getOwnerUniqueId()) && user == null && sourceEntity == null && !isFireSource && !isLeafDecay) {
|
|
if (!isLiquidSource) {
|
|
GDPermissionManager.getInstance().processEventLog(event, location, targetClaim, Flags.BLOCK_BREAK.getPermission(), source, blockState, user, "owner", Tristate.TRUE);
|
|
}
|
|
continue;
|
|
}
|
|
if (user != null && pistonExtend) {
|
|
if (targetClaim.isUserTrusted(user, TrustTypes.ACCESSOR)) {
|
|
GDPermissionManager.getInstance().processEventLog(event, location, targetClaim, Flags.BLOCK_BREAK.getPermission(), source, blockState, user, TrustTypes.ACCESSOR.getName().toLowerCase(), Tristate.TRUE);
|
|
continue;
|
|
}
|
|
}
|
|
if (isLeafDecay) {
|
|
if (GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, Flags.LEAF_DECAY, source, blockState, user) == Tristate.FALSE) {
|
|
event.setCancelled(true);
|
|
GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
} else if (isFireSource) {
|
|
if (GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, Flags.BLOCK_SPREAD, source, blockState, user) == Tristate.FALSE) {
|
|
event.setCancelled(true);
|
|
GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
} else if (isLiquidSource) {
|
|
if (GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, Flags.LIQUID_FLOW, source, blockState, user) == Tristate.FALSE) {
|
|
event.setCancelled(true);
|
|
lastBlockPreCancelled = true;
|
|
GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
continue;
|
|
} else if (GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, Flags.BLOCK_BREAK, source, blockState, user) == Tristate.FALSE) {
|
|
// PRE events can be spammy so we need to avoid sending player messages here.
|
|
event.setCancelled(true);
|
|
lastBlockPreCancelled = true;
|
|
GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
}
|
|
} else if (user != null) {
|
|
final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getPlayerData(world, user.getUniqueId());
|
|
GDClaim targetClaim = null;
|
|
for (Location<World> location : event.getLocations()) {
|
|
// Mods such as enderstorage will send chest updates to itself
|
|
// We must ignore cases like these to avoid issues with mod
|
|
if (tileEntity != null) {
|
|
if (location.getPosition().equals(tileEntity.getLocation().getPosition())) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
final BlockState blockState = location.getBlock();
|
|
targetClaim = this.dataStore.getClaimAt(location, targetClaim);
|
|
// If a player successfully interacted with a block recently such as a pressure plate, ignore check
|
|
// This fixes issues such as pistons not being able to extend
|
|
if (!isForgePlayerBreak && playerData != null && playerData.eventResultCache != null && playerData.eventResultCache.checkEventResultCache(targetClaim, "block-pre") == Tristate.TRUE) {
|
|
GDPermissionManager.getInstance().processEventLog(event, location, targetClaim, Flags.BLOCK_BREAK.getPermission(), source, blockState, user, playerData.eventResultCache.lastTrust, Tristate.TRUE);
|
|
continue;
|
|
}
|
|
if (targetClaim.isUserTrusted(user, TrustTypes.BUILDER)) {
|
|
GDPermissionManager.getInstance().processEventLog(event, location, targetClaim, Flags.BLOCK_BREAK.getPermission(), source, blockState, user, TrustTypes.BUILDER.getName().toLowerCase(), Tristate.TRUE);
|
|
continue;
|
|
}
|
|
|
|
if (isFireSource) {
|
|
if (GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, Flags.BLOCK_SPREAD, source, blockState, user) == Tristate.FALSE) {
|
|
event.setCancelled(true);
|
|
GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
} else if (isLiquidSource) {
|
|
if (GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, Flags.LIQUID_FLOW, source, blockState, user) == Tristate.FALSE) {
|
|
event.setCancelled(true);
|
|
lastBlockPreCancelled = true;
|
|
GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
continue;
|
|
} else if (GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, Flags.BLOCK_BREAK, source, blockState, user) == Tristate.FALSE) {
|
|
event.setCancelled(true);
|
|
lastBlockPreCancelled = true;
|
|
GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
|
|
}
|
|
|
|
// Handle fluids flowing into claims
|
|
@Listener(order = Order.FIRST, beforeModifications = true)
|
|
public void onBlockNotify(NotifyNeighborBlockEvent event) {
|
|
LocatableBlock locatableBlock = event.getCause().first(LocatableBlock.class).orElse(null);
|
|
TileEntity tileEntity = event.getCause().first(TileEntity.class).orElse(null);
|
|
Location<World> sourceLocation = locatableBlock != null ? locatableBlock.getLocation() : tileEntity != null ? tileEntity.getLocation() : null;
|
|
GDClaim sourceClaim = null;
|
|
GDPlayerData playerData = null;
|
|
if (sourceLocation != null) {
|
|
if (GriefDefenderPlugin.isSourceIdBlacklisted("block-notify", event.getSource(), sourceLocation.getExtent().getProperties())) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
final User user = CauseContextHelper.getEventUser(event);
|
|
if (user == null) {
|
|
return;
|
|
}
|
|
if (sourceLocation == null) {
|
|
Player player = event.getCause().first(Player.class).orElse(null);
|
|
if (player == null) {
|
|
return;
|
|
}
|
|
|
|
sourceLocation = player.getLocation();
|
|
playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
|
|
sourceClaim = this.dataStore.getClaimAtPlayer(playerData, player.getLocation());
|
|
} else {
|
|
playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(sourceLocation.getExtent(), user.getUniqueId());
|
|
sourceClaim = this.dataStore.getClaimAt(sourceLocation);
|
|
}
|
|
|
|
if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(sourceLocation.getExtent().getUniqueId())) {
|
|
return;
|
|
}
|
|
|
|
GDTimings.BLOCK_NOTIFY_EVENT.startTimingIfSync();
|
|
Iterator<Direction> iterator = event.getNeighbors().keySet().iterator();
|
|
GDClaim targetClaim = null;
|
|
while (iterator.hasNext()) {
|
|
Direction direction = iterator.next();
|
|
Location<World> location = sourceLocation.getBlockRelative(direction);
|
|
Vector3i pos = location.getBlockPosition();
|
|
targetClaim = this.dataStore.getClaimAt(location, targetClaim);
|
|
if (sourceClaim.isWilderness() && targetClaim.isWilderness()) {
|
|
if (playerData != null) {
|
|
playerData.eventResultCache = new EventResultCache(targetClaim, "block-notify", Tristate.TRUE);
|
|
}
|
|
continue;
|
|
} else if (!sourceClaim.isWilderness() && targetClaim.isWilderness()) {
|
|
if (playerData != null) {
|
|
playerData.eventResultCache = new EventResultCache(targetClaim, "block-notify", Tristate.TRUE);
|
|
}
|
|
continue;
|
|
} else if (sourceClaim.getUniqueId().equals(targetClaim.getUniqueId())) {
|
|
if (playerData != null) {
|
|
playerData.eventResultCache = new EventResultCache(targetClaim, "block-notify", Tristate.TRUE);
|
|
}
|
|
continue;
|
|
} else {
|
|
if (playerData.eventResultCache != null && playerData.eventResultCache.checkEventResultCache(targetClaim, "block-notify") == Tristate.TRUE) {
|
|
continue;
|
|
}
|
|
// Needed to handle levers notifying doors to open etc.
|
|
if (targetClaim.isUserTrusted(user, TrustTypes.ACCESSOR)) {
|
|
if (playerData != null) {
|
|
playerData.eventResultCache = new EventResultCache(targetClaim, "block-notify", Tristate.TRUE, TrustTypes.ACCESSOR.getName().toLowerCase());
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// no claim crossing unless trusted
|
|
iterator.remove();
|
|
}
|
|
GDTimings.BLOCK_NOTIFY_EVENT.stopTimingIfSync();
|
|
}
|
|
|
|
@Listener(order = Order.FIRST, beforeModifications = true)
|
|
public void onBlockCollide(CollideBlockEvent event, @Root Entity source) {
|
|
if (event instanceof CollideBlockEvent.Impact) {
|
|
return;
|
|
}
|
|
// ignore falling blocks
|
|
if (!GDFlags.COLLIDE_BLOCK || source instanceof FallingBlock) {
|
|
return;
|
|
}
|
|
|
|
final GDBlockType gdBlock = BlockTypeRegistryModule.getInstance().getById(event.getTargetBlock().getType().getId()).orElse(null);
|
|
if (gdBlock != null && !gdBlock.isCollidable() && !(source instanceof ItemFrame)) {
|
|
return;
|
|
}
|
|
if (event.getTargetBlock().getType() == BlockTypes.PORTAL) {
|
|
// ignore as we handle this with entity-teleport-from
|
|
return;
|
|
}
|
|
if (GriefDefenderPlugin.isSourceIdBlacklisted(Flags.COLLIDE_BLOCK.getName(), source.getType().getId(), source.getWorld().getProperties())) {
|
|
return;
|
|
}
|
|
if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.COLLIDE_BLOCK.getName(), event.getTargetBlock(), source.getWorld().getProperties())) {
|
|
return;
|
|
}
|
|
|
|
final User user = CauseContextHelper.getEventUser(event);
|
|
if (user == null) {
|
|
return;
|
|
}
|
|
|
|
GDTimings.BLOCK_COLLIDE_EVENT.startTimingIfSync();
|
|
final BlockType blockType = event.getTargetBlock().getType();
|
|
if (blockType.equals(BlockTypes.AIR)
|
|
|| !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(event.getTargetLocation().getExtent().getUniqueId())) {
|
|
GDTimings.BLOCK_COLLIDE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
|
|
if (source instanceof Item && (blockType != BlockTypes.PORTAL && !NMSUtil.getInstance().isBlockPressurePlate(blockType))) {
|
|
GDTimings.BLOCK_COLLIDE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
|
|
Vector3i collidePos = event.getTargetLocation().getBlockPosition();
|
|
short shortPos = BlockUtil.getInstance().blockPosToShort(collidePos);
|
|
int entityId = NMSUtil.getInstance().getEntityMinecraftId(source);
|
|
BlockPosCache entityBlockCache = BlockUtil.ENTITY_BLOCK_CACHE.get(entityId);
|
|
if (entityBlockCache == null) {
|
|
entityBlockCache = new BlockPosCache(shortPos);
|
|
BlockUtil.ENTITY_BLOCK_CACHE.put(entityId, entityBlockCache);
|
|
} else {
|
|
Tristate result = entityBlockCache.getCacheResult(shortPos);
|
|
if (result != Tristate.UNDEFINED) {
|
|
if (result == Tristate.FALSE) {
|
|
event.setCancelled(true);
|
|
}
|
|
|
|
GDTimings.BLOCK_COLLIDE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
}
|
|
|
|
GDPlayerData playerData = null;
|
|
GDClaim targetClaim = null;
|
|
if (user instanceof Player) {
|
|
playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(event.getTargetLocation().getExtent(), user.getUniqueId());
|
|
targetClaim = this.dataStore.getClaimAtPlayer(playerData, event.getTargetLocation());
|
|
} else {
|
|
targetClaim = this.dataStore.getClaimAt(event.getTargetLocation());
|
|
}
|
|
|
|
Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, event.getTargetLocation(), targetClaim, Flags.COLLIDE_BLOCK, source, event.getTargetBlock(), user, TrustTypes.ACCESSOR, true);
|
|
if (result != Tristate.UNDEFINED) {
|
|
if (result == Tristate.TRUE) {
|
|
entityBlockCache.setLastResult(Tristate.TRUE);
|
|
GDTimings.BLOCK_COLLIDE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
|
|
entityBlockCache.setLastResult(Tristate.FALSE);
|
|
event.setCancelled(true);
|
|
GDTimings.BLOCK_COLLIDE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
if (GDFlags.PORTAL_USE && event.getTargetBlock().getType() == BlockTypes.PORTAL) {
|
|
if (GDPermissionManager.getInstance().getFinalPermission(event, event.getTargetLocation(), targetClaim, Flags.PORTAL_USE, source, event.getTargetBlock(), user, TrustTypes.ACCESSOR, true) == Tristate.TRUE) {
|
|
entityBlockCache.setLastResult(Tristate.TRUE);
|
|
GDTimings.BLOCK_COLLIDE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
if (event.getCause().root() instanceof Player){
|
|
if (event.getTargetLocation().getExtent().getProperties().getTotalTime() % 20 == 0L) { // log once a second to avoid spam
|
|
// Disable message temporarily
|
|
//GriefDefender.sendMessage((Player) user, TextMode.Err, Messages.NoPortalFromProtectedClaim, claim.getOwnerName());
|
|
/*final Text message = GriefDefenderPlugin.getInstance().messageData.permissionProtectedPortal
|
|
.apply(ImmutableMap.of(
|
|
"owner", targetClaim.getOwnerName())).build();*/
|
|
event.setCancelled(true);
|
|
entityBlockCache.setLastResult(Tristate.FALSE);
|
|
GDTimings.BLOCK_COLLIDE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
entityBlockCache.setLastResult(Tristate.TRUE);
|
|
GDTimings.BLOCK_COLLIDE_EVENT.stopTimingIfSync();
|
|
}
|
|
|
|
@Listener(order = Order.FIRST, beforeModifications = true)
|
|
public void onProjectileImpactBlock(CollideBlockEvent.Impact event) {
|
|
if (!GDFlags.PROJECTILE_IMPACT_BLOCK || !(event.getSource() instanceof Entity)) {
|
|
return;
|
|
}
|
|
|
|
final Entity source = (Entity) event.getSource();
|
|
if (GriefDefenderPlugin.isSourceIdBlacklisted(Flags.PROJECTILE_IMPACT_BLOCK.getName(), source.getType().getId(), source.getWorld().getProperties())) {
|
|
return;
|
|
}
|
|
|
|
final User user = CauseContextHelper.getEventUser(event);
|
|
if (user == null) {
|
|
return;
|
|
}
|
|
|
|
if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(event.getImpactPoint().getExtent().getUniqueId())) {
|
|
return;
|
|
}
|
|
|
|
GDTimings.PROJECTILE_IMPACT_BLOCK_EVENT.startTimingIfSync();
|
|
Location<World> impactPoint = event.getImpactPoint();
|
|
GDClaim targetClaim = null;
|
|
GDPlayerData playerData = null;
|
|
if (user instanceof Player) {
|
|
playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(event.getTargetLocation().getExtent(), user.getUniqueId());
|
|
targetClaim = this.dataStore.getClaimAtPlayer(playerData, impactPoint);
|
|
} else {
|
|
targetClaim = this.dataStore.getClaimAt(impactPoint);
|
|
}
|
|
|
|
Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, impactPoint, targetClaim, Flags.PROJECTILE_IMPACT_BLOCK, source, event.getTargetBlock(), user, TrustTypes.ACCESSOR, true);
|
|
if (result == Tristate.FALSE) {
|
|
event.setCancelled(true);
|
|
GDTimings.PROJECTILE_IMPACT_BLOCK_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
|
|
GDTimings.PROJECTILE_IMPACT_BLOCK_EVENT.stopTimingIfSync();
|
|
}
|
|
|
|
@Listener(order = Order.FIRST, beforeModifications = true)
|
|
public void onExplosionPre(ExplosionEvent.Pre event) {
|
|
final World world = event.getExplosion().getWorld();
|
|
if (!GDFlags.EXPLOSION_BLOCK || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
|
|
return;
|
|
}
|
|
|
|
Object source = event.getSource();
|
|
final Explosion explosion = event.getExplosion();
|
|
if (explosion.getSourceExplosive().isPresent()) {
|
|
source = explosion.getSourceExplosive().get();
|
|
} else {
|
|
Entity exploder = event.getCause().first(Entity.class).orElse(null);
|
|
if (exploder != null) {
|
|
source = exploder;
|
|
}
|
|
}
|
|
|
|
if (GriefDefenderPlugin.isSourceIdBlacklisted(Flags.EXPLOSION_BLOCK.getName(), source, event.getExplosion().getWorld().getProperties())) {
|
|
return;
|
|
}
|
|
|
|
GDTimings.EXPLOSION_PRE_EVENT.startTimingIfSync();
|
|
final User user = CauseContextHelper.getEventUser(event);
|
|
final Location<World> location = event.getExplosion().getLocation();
|
|
final GDClaim targetClaim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(location);
|
|
// If affected claim does not inherit parent, skip logic
|
|
if (!targetClaim.isWilderness() && targetClaim.getParent().isPresent() && !targetClaim.getInternalClaimData().doesInheritParent()) {
|
|
GDTimings.EXPLOSION_PRE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
final GDClaim radiusClaim = NMSUtil.getInstance().createClaimFromCenter(location, event.getExplosion().getRadius());
|
|
final GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(location.getExtent().getUniqueId());
|
|
final Set<Claim> surroundingClaims = claimManager.findOverlappingClaims(radiusClaim);
|
|
if (surroundingClaims.size() == 0) {
|
|
return;
|
|
}
|
|
for (Claim claim : surroundingClaims) {
|
|
// Use any location for permission check
|
|
Location<World> targetLocation = new Location<>(location.getExtent(), claim.getLesserBoundaryCorner());
|
|
Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, location, claim, Flags.EXPLOSION_BLOCK, source, targetLocation, user, true);
|
|
if (result == Tristate.FALSE) {
|
|
event.setCancelled(true);
|
|
break;
|
|
}
|
|
}
|
|
|
|
GDTimings.EXPLOSION_PRE_EVENT.stopTimingIfSync();
|
|
}
|
|
|
|
@Listener(order = Order.FIRST, beforeModifications = true)
|
|
public void onExplosionDetonate(ExplosionEvent.Detonate event) {
|
|
final World world = event.getExplosion().getWorld();
|
|
if (!GDFlags.EXPLOSION_BLOCK || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
|
|
return;
|
|
}
|
|
|
|
Object source = event.getSource();
|
|
if (source instanceof Explosion) {
|
|
final Explosion explosion = (Explosion) source;
|
|
if (explosion.getSourceExplosive().isPresent()) {
|
|
source = explosion.getSourceExplosive().get();
|
|
} else {
|
|
Entity exploder = event.getCause().first(Entity.class).orElse(null);
|
|
if (exploder != null) {
|
|
source = exploder;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (GriefDefenderPlugin.isSourceIdBlacklisted(Flags.EXPLOSION_BLOCK.getName(), source, event.getExplosion().getWorld().getProperties())) {
|
|
return;
|
|
}
|
|
|
|
GDTimings.EXPLOSION_EVENT.startTimingIfSync();
|
|
final User user = CauseContextHelper.getEventUser(event);
|
|
GDClaim targetClaim = null;
|
|
final List<Location<World>> filteredLocations = new ArrayList<>();
|
|
final String sourceId = GDPermissionManager.getInstance().getPermissionIdentifier(source);
|
|
final int surfaceBlockLevel = GriefDefenderPlugin.getActiveConfig(world.getUniqueId()).getConfig().claim.explosionSurfaceBlockLevel;
|
|
boolean denySurfaceExplosion = GriefDefenderPlugin.getActiveConfig(world.getUniqueId()).getConfig().claim.explosionBlockSurfaceBlacklist.contains(sourceId);
|
|
if (!denySurfaceExplosion) {
|
|
denySurfaceExplosion = GriefDefenderPlugin.getActiveConfig(world.getUniqueId()).getConfig().claim.explosionBlockSurfaceBlacklist.contains("any");
|
|
}
|
|
for (Location<World> location : event.getAffectedLocations()) {
|
|
if (location.getBlockType().equals(BlockTypes.AIR)) {
|
|
continue;
|
|
}
|
|
targetClaim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(location, targetClaim);
|
|
if (denySurfaceExplosion && world.getDimension().getType() != DimensionTypes.NETHER && location.getBlockY() >= surfaceBlockLevel) {
|
|
filteredLocations.add(location);
|
|
GDPermissionManager.getInstance().processEventLog(event, location, targetClaim, Flags.EXPLOSION_BLOCK.getPermission(), source, location.getBlock(), user, "explosion-surface", Tristate.FALSE);
|
|
continue;
|
|
}
|
|
|
|
Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, Flags.EXPLOSION_BLOCK, source, location.getBlock(), user, true);
|
|
|
|
if (result == Tristate.FALSE) {
|
|
filteredLocations.add(location);
|
|
}
|
|
}
|
|
// Workaround for SpongeForge bug
|
|
if (event.isCancelled()) {
|
|
event.getAffectedLocations().clear();
|
|
} else if (!filteredLocations.isEmpty()) {
|
|
event.getAffectedLocations().removeAll(filteredLocations);
|
|
}
|
|
GDTimings.EXPLOSION_EVENT.stopTimingIfSync();
|
|
}
|
|
|
|
@Listener(order = Order.FIRST, beforeModifications = true)
|
|
public void onBlockBreak(ChangeBlockEvent.Break event) {
|
|
if (!GDFlags.BLOCK_BREAK || event instanceof ExplosionEvent) {
|
|
return;
|
|
}
|
|
|
|
if (lastBlockPreTick == Sponge.getServer().getRunningTimeTicks()) {
|
|
event.setCancelled(lastBlockPreCancelled);
|
|
return;
|
|
}
|
|
|
|
Object source = event.getSource();
|
|
// Handled in Explosion listeners
|
|
if (source instanceof Explosion) {
|
|
return;
|
|
}
|
|
|
|
// Pistons are handled in onBlockPre
|
|
if (source == BlockTypes.PISTON || source instanceof Piston) {
|
|
return;
|
|
}
|
|
|
|
final World world = event.getTransactions().get(0).getFinal().getLocation().get().getExtent();
|
|
if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
|
|
return;
|
|
}
|
|
|
|
if (GriefDefenderPlugin.isSourceIdBlacklisted(Flags.BLOCK_BREAK.getName(), source, world.getProperties())) {
|
|
return;
|
|
}
|
|
|
|
final Player player = source instanceof Player ? (Player) source : null;
|
|
final User user = player != null ? player : CauseContextHelper.getEventUser(event);
|
|
|
|
// ignore falling blocks when there is no user
|
|
// avoids dupes with falling blocks such as Dragon Egg
|
|
if (user == null && source instanceof FallingBlock) {
|
|
return;
|
|
}
|
|
GDClaim sourceClaim = null;
|
|
LocatableBlock locatable = null;
|
|
if (source instanceof LocatableBlock) {
|
|
locatable = (LocatableBlock) source;
|
|
sourceClaim = this.dataStore.getClaimAt(locatable.getLocation());
|
|
} else {
|
|
sourceClaim = this.getSourceClaim(event.getCause());
|
|
}
|
|
if (sourceClaim == null) {
|
|
return;
|
|
}
|
|
|
|
GDTimings.BLOCK_BREAK_EVENT.startTimingIfSync();
|
|
List<Transaction<BlockSnapshot>> transactions = event.getTransactions();
|
|
GDClaim targetClaim = null;
|
|
for (Transaction<BlockSnapshot> transaction : transactions) {
|
|
if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.BLOCK_BREAK.getName(), transaction.getOriginal(), world.getProperties())) {
|
|
continue;
|
|
}
|
|
|
|
Location<World> location = transaction.getOriginal().getLocation().orElse(null);
|
|
targetClaim = this.dataStore.getClaimAt(location, targetClaim);
|
|
if (location == null || transaction.getOriginal().getState().getType() == BlockTypes.AIR) {
|
|
continue;
|
|
}
|
|
|
|
// check overrides
|
|
final Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, Flags.BLOCK_BREAK, source, transaction.getOriginal(), user, TrustTypes.BUILDER, true);
|
|
if (result != Tristate.TRUE) {
|
|
if (player != null) {
|
|
final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_BUILD,
|
|
ImmutableMap.of("player", targetClaim.getOwnerName()));
|
|
GriefDefenderPlugin.sendClaimDenyMessage(targetClaim, player, message);
|
|
}
|
|
|
|
event.setCancelled(true);
|
|
GDTimings.BLOCK_BREAK_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
}
|
|
GDTimings.BLOCK_BREAK_EVENT.stopTimingIfSync();
|
|
}
|
|
|
|
@Listener(order = Order.FIRST, beforeModifications = true)
|
|
public void onBlockPlace(ChangeBlockEvent.Place event) {
|
|
final Object source = event.getSource();
|
|
// Pistons are handled in onBlockPre
|
|
if (source instanceof Piston) {
|
|
return;
|
|
}
|
|
|
|
final World world = event.getTransactions().get(0).getFinal().getLocation().get().getExtent();
|
|
if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
|
|
return;
|
|
}
|
|
if (GriefDefenderPlugin.isSourceIdBlacklisted(Flags.BLOCK_PLACE.getName(), event.getSource(), world.getProperties())) {
|
|
return;
|
|
}
|
|
|
|
ItemStackSnapshot itemSnapshot = event.getContext().get(EventContextKeys.USED_ITEM).orElse(null);
|
|
if (itemSnapshot != null) {
|
|
if (itemSnapshot.getType().equals(ItemTypes.BUCKET)) {
|
|
// Sponge bug - empty buckets should never fire a block place
|
|
return;
|
|
}
|
|
}
|
|
GDTimings.BLOCK_PLACE_EVENT.startTimingIfSync();
|
|
GDClaim sourceClaim = null;
|
|
LocatableBlock locatable = null;
|
|
final User user = CauseContextHelper.getEventUser(event);
|
|
// handle ice form/melt
|
|
if (!(event.getCause().root() instanceof Entity) && event.getTransactions().size() == 1) {
|
|
final BlockSnapshot sourceBlock = event.getTransactions().get(0).getOriginal();
|
|
final BlockSnapshot targetBlock = event.getTransactions().get(0).getFinal();
|
|
if (NMSUtil.getInstance().isBlockWater(sourceBlock.getState().getType()) && NMSUtil.getInstance().isBlockIce(targetBlock.getState().getType())) {
|
|
final Location<World> loc = targetBlock.getLocation().get();
|
|
final GDClaim claim = this.dataStore.getClaimAt(loc);
|
|
final Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, loc, claim, Flags.BLOCK_MODIFY, sourceBlock, targetBlock, user, true);
|
|
if (result == Tristate.FALSE) {
|
|
event.setCancelled(true);
|
|
}
|
|
return;
|
|
} else if (NMSUtil.getInstance().isBlockIce(sourceBlock.getState().getType()) && NMSUtil.getInstance().isBlockWater(targetBlock.getState().getType())) {
|
|
final Location<World> loc = targetBlock.getLocation().get();
|
|
final GDClaim claim = this.dataStore.getClaimAt(loc);
|
|
final Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, loc, claim, Flags.BLOCK_MODIFY, sourceBlock, targetBlock, user, true);
|
|
if (result == Tristate.FALSE) {
|
|
event.setCancelled(true);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (source instanceof LocatableBlock) {
|
|
locatable = (LocatableBlock) source;
|
|
if (user != null && user instanceof Player) {
|
|
final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(locatable.getWorld(), user.getUniqueId());
|
|
sourceClaim = this.dataStore.getClaimAtPlayer(playerData, locatable.getLocation());
|
|
} else {
|
|
sourceClaim = this.dataStore.getClaimAt(locatable.getLocation());
|
|
}
|
|
} else {
|
|
sourceClaim = this.getSourceClaim(event.getCause());
|
|
}
|
|
if (sourceClaim == null && user == null) {
|
|
GDTimings.BLOCK_PLACE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
|
|
Player player = user != null && user instanceof Player ? (Player) user : null;
|
|
GDPlayerData playerData = null;
|
|
if (user != null) {
|
|
playerData = this.dataStore.getOrCreatePlayerData(world, user.getUniqueId());
|
|
}
|
|
|
|
GriefDefenderConfig<?> activeConfig = GriefDefenderPlugin.getActiveConfig(world.getProperties());
|
|
if (sourceClaim != null && !(source instanceof User) && playerData != null && playerData.eventResultCache != null && playerData.eventResultCache.checkEventResultCache(sourceClaim, Flags.BLOCK_PLACE.getName()) == Tristate.TRUE) {
|
|
GDTimings.BLOCK_PLACE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
|
|
GDClaim targetClaim = null;
|
|
for (Transaction<BlockSnapshot> transaction : event.getTransactions()) {
|
|
final BlockSnapshot block = transaction.getFinal();
|
|
if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.BLOCK_PLACE.getName(), block, world.getProperties())) {
|
|
continue;
|
|
}
|
|
|
|
Location<World> location = block.getLocation().orElse(null);
|
|
if (location == null) {
|
|
continue;
|
|
}
|
|
|
|
targetClaim = this.dataStore.getClaimAt(location, targetClaim);
|
|
if (!checkSurroundings(event, location, player, playerData, targetClaim)) {
|
|
event.setCancelled(true);
|
|
GDTimings.BLOCK_PLACE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
|
|
if (GDFlags.BLOCK_PLACE) {
|
|
// Allow blocks to grow within claims
|
|
if (user == null && sourceClaim != null && sourceClaim.getUniqueId().equals(targetClaim.getUniqueId())) {
|
|
GDTimings.BLOCK_PLACE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
|
|
// check overrides
|
|
final Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, Flags.BLOCK_PLACE, source, block, user, TrustTypes.BUILDER, true);
|
|
if (result != Tristate.TRUE) {
|
|
// TODO - make sure this doesn't spam
|
|
/*if (source instanceof Player) {
|
|
final Text message = GriefDefenderPlugin.getInstance().messageData.permissionBuild
|
|
.apply(ImmutableMap.of(
|
|
"player", Text.of(targetClaim.getOwnerName())
|
|
)).build();
|
|
GriefDefenderPlugin.sendClaimDenyMessage(targetClaim, (Player) source, message);
|
|
}*/
|
|
event.setCancelled(true);
|
|
GDTimings.BLOCK_PLACE_EVENT.stopTimingIfSync();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!(source instanceof Player)) {
|
|
continue;
|
|
}
|
|
|
|
if (targetClaim.isWilderness() && activeConfig.getConfig().claim.autoChestClaimBlockRadius > -1) {
|
|
TileEntity tileEntity = block.getLocation().get().getTileEntity().orElse(null);
|
|
if (tileEntity == null || !(tileEntity instanceof Chest)) {
|
|
GDTimings.BLOCK_PLACE_EVENT.stopTimingIfSync();
|
|
continue;
|
|
}
|
|
|
|
final int minClaimLevel = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), user, Options.MIN_LEVEL).intValue();
|
|
final int maxClaimLevel = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), user, Options.MAX_LEVEL).intValue();
|
|
if (block.getPosition().getY() < minClaimLevel || block.getPosition().getY() > maxClaimLevel) {
|
|
final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_CHEST_OUTSIDE_LEVEL,
|
|
ImmutableMap.of(
|
|
"min-claim-level", minClaimLevel,
|
|
"max-claim-level", maxClaimLevel));
|
|
GriefDefenderPlugin.sendMessage(player, message);
|
|
GDTimings.BLOCK_PLACE_EVENT.stopTimingIfSync();
|
|
continue;
|
|
}
|
|
|
|
int radius = activeConfig.getConfig().claim.autoChestClaimBlockRadius;
|
|
|
|
if (playerData.getInternalClaims().size() == 0) {
|
|
if (activeConfig.getConfig().claim.autoChestClaimBlockRadius == 0) {
|
|
GDCauseStackManager.getInstance().pushCause(player);
|
|
final ClaimResult result = GriefDefender.getRegistry().createBuilder(Claim.Builder.class)
|
|
.bounds(block.getPosition(), block.getPosition())
|
|
.cuboid(false)
|
|
.owner(player.getUniqueId())
|
|
.sizeRestrictions(false)
|
|
.type(ClaimTypes.BASIC)
|
|
.world(block.getLocation().get().getExtent().getUniqueId())
|
|
.build();
|
|
GDCauseStackManager.getInstance().popCause();
|
|
if (result.successful()) {
|
|
final Claim claim = result.getClaim().get();
|
|
final GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(world.getUniqueId());
|
|
claimManager.addClaim(claim, true);
|
|
GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_CHEST_CONFIRMATION);
|
|
GDTimings.BLOCK_PLACE_EVENT.stopTimingIfSync();
|
|
continue;
|
|
}
|
|
} else {
|
|
Vector3i lesserBoundary = new Vector3i(
|
|
block.getPosition().getX() - radius,
|
|
minClaimLevel,
|
|
block.getPosition().getZ() - radius);
|
|
Vector3i greaterBoundary = new Vector3i(
|
|
block.getPosition().getX() + radius,
|
|
maxClaimLevel,
|
|
block.getPosition().getZ() + radius);
|
|
|
|
while (radius >= 0) {
|
|
GDCauseStackManager.getInstance().pushCause(player);
|
|
ClaimResult result = GriefDefender.getRegistry().createBuilder(Claim.Builder.class)
|
|
.bounds(lesserBoundary, greaterBoundary)
|
|
.cuboid(false)
|
|
.owner(player.getUniqueId())
|
|
.sizeRestrictions(false)
|
|
.type(ClaimTypes.BASIC)
|
|
.world(block.getLocation().get().getExtent().getUniqueId())
|
|
.build();
|
|
GDCauseStackManager.getInstance().popCause();
|
|
if (!result.successful()) {
|
|
radius--;
|
|
} else {
|
|
GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_AUTOMATIC_NOTIFICATION);
|
|
GDClaim newClaim = this.dataStore.getClaimAt(block.getLocation().get());
|
|
GDClaimVisual visualization = new GDClaimVisual(newClaim, ClaimVisualTypes.BASIC);
|
|
visualization.createClaimBlockVisuals(block.getPosition().getY(), player.getLocation(), playerData);
|
|
visualization.apply(player);
|
|
|
|
GDTimings.BLOCK_PLACE_EVENT.stopTimingIfSync();
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (targetClaim.isWilderness() && player.hasPermission(GDPermissions.CLAIM_SHOW_TUTORIAL)) {
|
|
GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.TUTORIAL_CLAIM_BASIC));
|
|
}
|
|
}
|
|
}
|
|
|
|
GDTimings.BLOCK_PLACE_EVENT.stopTimingIfSync();
|
|
}
|
|
|
|
@Listener(order = Order.FIRST, beforeModifications = true)
|
|
public void onSignChanged(ChangeSignEvent event) {
|
|
final User user = CauseContextHelper.getEventUser(event);
|
|
if (user == null) {
|
|
return;
|
|
}
|
|
|
|
if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(event.getTargetTile().getLocation().getExtent().getUniqueId())) {
|
|
return;
|
|
}
|
|
|
|
GDTimings.SIGN_CHANGE_EVENT.startTimingIfSync();
|
|
Location<World> location = event.getTargetTile().getLocation();
|
|
// Prevent users exploiting signs
|
|
GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(location);
|
|
if (GDPermissionManager.getInstance().getFinalPermission(event, location, claim, Flags.INTERACT_BLOCK_SECONDARY, user, location.getBlock(), user, TrustTypes.ACCESSOR, true) == Tristate.FALSE) {
|
|
if (user instanceof Player) {
|
|
event.setCancelled(true);
|
|
final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_ACCESS,
|
|
ImmutableMap.of("player", claim.getOwnerName()));
|
|
GriefDefenderPlugin.sendClaimDenyMessage(claim, (Player) user, message);
|
|
return;
|
|
}
|
|
}
|
|
|
|
GDTimings.SIGN_CHANGE_EVENT.stopTimingIfSync();
|
|
}
|
|
|
|
@Listener(order = Order.FIRST)
|
|
public void onSignChangeEvent(ChangeSignEvent event) {
|
|
if (GriefDefenderPlugin.getInstance().getEconomyService() == null) {
|
|
return;
|
|
}
|
|
|
|
final GriefDefenderConfig<?> activeConfig = GriefDefenderPlugin.getActiveConfig(event.getTargetTile().getWorld().getUniqueId());
|
|
if (!activeConfig.getConfig().economy.isRentSignEnabled() && !activeConfig.getConfig().economy.isSellSignEnabled()) {
|
|
return;
|
|
}
|
|
|
|
final User eventUser = CauseContextHelper.getEventUser(event);
|
|
if (eventUser == null) {
|
|
return;
|
|
}
|
|
|
|
final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(eventUser);
|
|
if (user == null) {
|
|
return;
|
|
}
|
|
|
|
final Player player = user.getOnlinePlayer();
|
|
if (player == null) {
|
|
return;
|
|
}
|
|
final Sign sign = event.getTargetTile();
|
|
final GDClaim claim = this.dataStore.getClaimAt(sign.getLocation());
|
|
if (claim.isWilderness()) {
|
|
return;
|
|
}
|
|
|
|
final List<Text> lines = event.getText().asList();
|
|
final String header = lines.get(0).toPlain();
|
|
if (header == null || (!header.equalsIgnoreCase("gd") && !header.equalsIgnoreCase("griefdefender"))) {
|
|
return;
|
|
}
|
|
|
|
final String line1 = lines.get(1).toPlain();
|
|
final String line2 = lines.get(2).toPlain();
|
|
final String line3 = lines.get(3).toPlain();
|
|
if (line1.equalsIgnoreCase("sell") && activeConfig.getConfig().economy.isSellSignEnabled()) {
|
|
if (!player.hasPermission(GDPermissions.USER_SELL_SIGN)) {
|
|
return;
|
|
}
|
|
|
|
// check price
|
|
Double price = null;
|
|
try {
|
|
price = Double.valueOf(line2);
|
|
} catch (NumberFormatException e) {
|
|
return;
|
|
}
|
|
|
|
SignUtil.setClaimForSale(claim, user.getOnlinePlayer(), sign, price);
|
|
} else if (line1.equalsIgnoreCase("rent") && activeConfig.getConfig().economy.isRentSignEnabled() && activeConfig.getConfig().economy.rentSystem) {
|
|
if (!player.hasPermission(GDPermissions.USER_RENT_SIGN)) {
|
|
return;
|
|
}
|
|
|
|
Double rate = null;
|
|
try {
|
|
rate = Double.valueOf(line2.substring(0, line2.length() - 1));
|
|
} catch (NumberFormatException e) {
|
|
return;
|
|
}
|
|
|
|
int rentMin = 0;
|
|
int rentMax = 0;
|
|
if (line3 != null) {
|
|
rentMin = SignUtil.getRentMinTime(line3);
|
|
rentMax = SignUtil.getRentMaxTime(line3);
|
|
}
|
|
|
|
String rentType = line2;
|
|
final PaymentType paymentType = SignUtil.getPaymentType(rentType);
|
|
if (paymentType == PaymentType.UNDEFINED) {
|
|
// invalid
|
|
return;
|
|
}
|
|
|
|
SignUtil.setClaimForRent(claim, player, sign, rate, rentMin, rentMax, paymentType);
|
|
}
|
|
}
|
|
|
|
public GDClaim getSourceClaim(Cause cause) {
|
|
BlockSnapshot blockSource = cause.first(BlockSnapshot.class).orElse(null);
|
|
LocatableBlock locatableBlock = null;
|
|
TileEntity tileEntitySource = null;
|
|
Entity entitySource = null;
|
|
if (blockSource == null) {
|
|
locatableBlock = cause.first(LocatableBlock.class).orElse(null);
|
|
if (locatableBlock == null) {
|
|
entitySource = cause.first(Entity.class).orElse(null);
|
|
}
|
|
if (locatableBlock == null && entitySource == null) {
|
|
tileEntitySource = cause.first(TileEntity.class).orElse(null);
|
|
}
|
|
}
|
|
|
|
GDClaim sourceClaim = null;
|
|
if (blockSource != null) {
|
|
sourceClaim = this.dataStore.getClaimAt(blockSource.getLocation().get());
|
|
} else if (locatableBlock != null) {
|
|
sourceClaim = this.dataStore.getClaimAt(locatableBlock.getLocation());
|
|
} else if (tileEntitySource != null) {
|
|
sourceClaim = this.dataStore.getClaimAt(tileEntitySource.getLocation());
|
|
} else if (entitySource != null) {
|
|
Entity entity = entitySource;
|
|
if (entity instanceof Player) {
|
|
Player player = (Player) entity;
|
|
GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
|
|
sourceClaim = this.dataStore.getClaimAtPlayer(playerData, player.getLocation());
|
|
} else {
|
|
sourceClaim = this.dataStore.getClaimAt(entity.getLocation());
|
|
}
|
|
}
|
|
|
|
return sourceClaim;
|
|
}
|
|
|
|
// TODO: Add configuration for distance between claims
|
|
private boolean checkSurroundings(org.spongepowered.api.event.Event event, Location<World> location, Player player, GDPlayerData playerData, GDClaim targetClaim) {
|
|
if (playerData == null) {
|
|
return true;
|
|
}
|
|
// Don't allow players to break blocks next to land they do not own
|
|
if (!playerData.canIgnoreClaim(targetClaim)) {
|
|
// check surrounding blocks for access
|
|
for (Direction direction : BlockUtil.CARDINAL_SET) {
|
|
Location<World> loc = location.getBlockRelative(direction);
|
|
if (!(loc.getTileEntity().isPresent())) {
|
|
continue;
|
|
}
|
|
final GDClaim claim = this.dataStore.getClaimAt(loc, targetClaim);
|
|
if (!claim.isWilderness() && !targetClaim.equals(claim)) {
|
|
Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, loc, claim, Flags.BLOCK_BREAK, player, loc.getBlock(), player, TrustTypes.BUILDER, true);
|
|
if (result != Tristate.TRUE) {
|
|
final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_BUILD_NEAR_CLAIM,
|
|
ImmutableMap.of(
|
|
"player", claim.getOwnerName()));
|
|
GriefDefenderPlugin.sendClaimDenyMessage(claim, player, message);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|