352 lines
14 KiB
Java
352 lines
14 KiB
Java
package tc.oc.pgm.modules;
|
|
|
|
import java.util.Iterator;
|
|
import javax.annotation.Nullable;
|
|
|
|
import net.md_5.bungee.api.chat.BaseComponent;
|
|
import net.md_5.bungee.api.chat.TranslatableComponent;
|
|
import org.bukkit.GameMode;
|
|
import org.bukkit.Material;
|
|
import org.bukkit.World;
|
|
import org.bukkit.entity.Entity;
|
|
import org.bukkit.entity.LivingEntity;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.event.Cancellable;
|
|
import org.bukkit.event.Event;
|
|
import org.bukkit.event.EventHandler;
|
|
import org.bukkit.event.EventPriority;
|
|
import org.bukkit.event.Listener;
|
|
import org.bukkit.event.block.Action;
|
|
import org.bukkit.event.block.BlockDamageEvent;
|
|
import org.bukkit.event.entity.AreaEffectCloudApplyEvent;
|
|
import org.bukkit.event.entity.EntityCombustByEntityEvent;
|
|
import org.bukkit.event.entity.EntityCombustEvent;
|
|
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
|
import org.bukkit.event.entity.EntityDamageEvent;
|
|
import org.bukkit.event.entity.EntityShootBowEvent;
|
|
import org.bukkit.event.entity.EntityTargetEvent;
|
|
import org.bukkit.event.entity.PotionSplashEvent;
|
|
import org.bukkit.event.hanging.HangingBreakByEntityEvent;
|
|
import org.bukkit.event.hanging.HangingBreakEvent;
|
|
import org.bukkit.event.inventory.ClickType;
|
|
import org.bukkit.event.player.PlayerArmorStandManipulateEvent;
|
|
import org.bukkit.event.player.PlayerAttackEntityEvent;
|
|
import org.bukkit.event.player.PlayerBedEnterEvent;
|
|
import org.bukkit.event.player.PlayerDropItemEvent;
|
|
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
|
|
import org.bukkit.event.player.PlayerInteractEntityEvent;
|
|
import org.bukkit.event.player.PlayerInteractEvent;
|
|
import org.bukkit.event.player.PlayerPickupItemEvent;
|
|
import org.bukkit.event.vehicle.VehicleDamageEvent;
|
|
import org.bukkit.event.vehicle.VehicleEnterEvent;
|
|
import org.bukkit.event.vehicle.VehicleEntityCollisionEvent;
|
|
import org.bukkit.event.weather.WeatherChangeEvent;
|
|
import org.bukkit.event.world.PortalCreateEvent;
|
|
import tc.oc.api.util.Permissions;
|
|
import tc.oc.commons.bukkit.event.AdventureModeInteractEvent;
|
|
import tc.oc.pgm.events.ListenerScope;
|
|
import tc.oc.pgm.events.ObserverInteractEvent;
|
|
import tc.oc.pgm.events.PlayerBlockTransformEvent;
|
|
import tc.oc.pgm.match.MatchModule;
|
|
import tc.oc.pgm.match.MatchPlayer;
|
|
import tc.oc.pgm.match.MatchPlayerState;
|
|
import tc.oc.pgm.match.MatchScope;
|
|
|
|
/**
|
|
* Listens to many events at low priority and cancels them if the actor is
|
|
* not allowed to interact with the world. Also cancels a few events that
|
|
* we just don't want ever.
|
|
*
|
|
* Any functionality beyond that should be implemented in other modules.
|
|
* This module should be kept simple.
|
|
*/
|
|
@ListenerScope(MatchScope.LOADED)
|
|
public class EventFilterMatchModule extends MatchModule implements Listener {
|
|
|
|
boolean cancel(Cancellable event, @Nullable MatchPlayer actor, @Nullable BaseComponent message) {
|
|
logger.fine("Cancel " + event + " actor=" + actor);
|
|
event.setCancelled(true);
|
|
if(actor != null && message != null) {
|
|
actor.sendWarning(message, true);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
boolean cancel(Cancellable event, boolean cancel, World world, @Nullable MatchPlayer actor, @Nullable BaseComponent message) {
|
|
if(cancel && getMatch().getWorld().equals(world)) {
|
|
return cancel(event, actor, message);
|
|
} else {
|
|
logger.fine("Allow " + event + " actor=" + actor);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
boolean cancel(Cancellable event, boolean cancel, World world) {
|
|
return cancel(event, cancel, world, null, null);
|
|
}
|
|
|
|
boolean cancelAlways(Cancellable event, World world) {
|
|
return cancel(event, true, world);
|
|
}
|
|
|
|
boolean cancelUnlessInteracting(Cancellable event, MatchPlayer player) {
|
|
return cancel(event, !player.canInteract(), player.getBukkit().getWorld(), player, null);
|
|
}
|
|
|
|
boolean cancelUnlessInteracting(Cancellable event, Entity entity) {
|
|
return entity != null && cancel(event, !getMatch().canInteract(entity), entity.getWorld(), getMatch().getPlayer(entity), null);
|
|
}
|
|
|
|
boolean cancelUnlessInteracting(Cancellable event, MatchPlayerState player) {
|
|
return cancel(event, !getMatch().canInteract(player), player.getMatch().getWorld(), null, null);
|
|
}
|
|
|
|
ClickType convertClick(ClickType clickType, Player player) {
|
|
if(clickType == ClickType.RIGHT && player.isSneaking()) {
|
|
return ClickType.SHIFT_RIGHT;
|
|
} else {
|
|
return clickType;
|
|
}
|
|
}
|
|
|
|
@Nullable ClickType convertClick(Action action, Player player) {
|
|
switch(action) {
|
|
case LEFT_CLICK_BLOCK:
|
|
case LEFT_CLICK_AIR:
|
|
return ClickType.LEFT;
|
|
|
|
case RIGHT_CLICK_BLOCK:
|
|
case RIGHT_CLICK_AIR:
|
|
return convertClick(ClickType.RIGHT, player);
|
|
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------
|
|
// -- Unconditionally cancelled events i.e. rejected features --
|
|
// -------------------------------------------------------------
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
|
public void onPortalCreate(final PortalCreateEvent event) {
|
|
cancelAlways(event, event.getWorld());
|
|
}
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
|
public void onWeatherChange(final WeatherChangeEvent event) {
|
|
cancelAlways(event, event.getWorld());
|
|
}
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
|
public void onBedEnter(final PlayerBedEnterEvent event) {
|
|
cancel(event, true, event.getPlayer().getWorld(), getMatch().getPlayer(event.getPlayer()), new TranslatableComponent("match.bed.disabled"));
|
|
}
|
|
|
|
|
|
// ---------------------------
|
|
// -- Player item/block use --
|
|
// ---------------------------
|
|
|
|
// This handler listens on HIGHEST so that other plugins get a chance
|
|
// to handle observer clicks before we cancel them i.e. WorldEdit.
|
|
@EventHandler(priority = EventPriority.HIGHEST)
|
|
public void onInteract(final PlayerInteractEvent event) {
|
|
if(cancelUnlessInteracting(event, event.getPlayer())) {
|
|
// Allow the how-to book to be read
|
|
if(event.getMaterial() == Material.WRITTEN_BOOK) {
|
|
event.setUseItemInHand(Event.Result.ALLOW);
|
|
} else {
|
|
event.setUseItemInHand(Event.Result.DENY);
|
|
event.setUseInteractedBlock(Event.Result.DENY);
|
|
}
|
|
|
|
MatchPlayer player = getMatch().getPlayer(event.getPlayer());
|
|
if(player == null) return;
|
|
|
|
if(!player.isSpawned()) {
|
|
ClickType clickType = convertClick(event.getAction(), event.getPlayer());
|
|
if(clickType == null) return;
|
|
|
|
getMatch().callEvent(new ObserverInteractEvent(player, clickType, event.getClickedBlock(), null, event.getItem()));
|
|
}
|
|
|
|
// Right-clicking armor will put it on unless we do this
|
|
event.getPlayer().updateInventory();
|
|
}
|
|
}
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST)
|
|
public void onShoot(final EntityShootBowEvent event) {
|
|
// PlayerInteractEvent is fired on draw, this is fired on release. Need to cancel both.
|
|
cancelUnlessInteracting(event, event.getEntity());
|
|
}
|
|
|
|
|
|
// --------------------------------------
|
|
// -- Player interaction with entities --
|
|
// --------------------------------------
|
|
|
|
void callObserverInteractEvent(PlayerInteractEntityEvent event) {
|
|
MatchPlayer player = getMatch().getPlayer(event.getPlayer());
|
|
if(player == null || player.isSpawned()) return;
|
|
|
|
getMatch().callEvent(new ObserverInteractEvent(player,
|
|
convertClick(ClickType.RIGHT, event.getPlayer()),
|
|
null,
|
|
event.getRightClicked(),
|
|
event.getPlayer().getInventory().getItem(event.getHand())));
|
|
}
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
|
public void onEntityInteract(final PlayerInteractEntityEvent event) {
|
|
if(cancelUnlessInteracting(event, event.getPlayer())) {
|
|
callObserverInteractEvent(event);
|
|
}
|
|
}
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
|
public void onArmorStandInteract(final PlayerInteractAtEntityEvent event) {
|
|
cancelUnlessInteracting(event, event.getPlayer());
|
|
}
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
|
public void onArmorStandInteract(final PlayerArmorStandManipulateEvent event) {
|
|
cancelUnlessInteracting(event, event.getPlayer());
|
|
}
|
|
|
|
|
|
// --------------------------------------
|
|
// -- Player interaction with vehicles --
|
|
// --------------------------------------
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
|
public void onVehicleDamage(final VehicleDamageEvent event) {
|
|
cancelUnlessInteracting(event, event.getAttacker());
|
|
}
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
|
public void onVehiclePush(final VehicleEntityCollisionEvent event) {
|
|
cancelUnlessInteracting(event, event.getEntity());
|
|
}
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
|
public void onVehicleEnter(final VehicleEnterEvent event) {
|
|
cancelUnlessInteracting(event, event.getEntered());
|
|
}
|
|
|
|
|
|
// ------------------------------------
|
|
// -- Player interaction with blocks --
|
|
// ------------------------------------
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
|
public void onPlayerBlockChange(final PlayerBlockTransformEvent event) {
|
|
cancelUnlessInteracting(event, event.getPlayerState());
|
|
|
|
if(!event.isCancelled() && event.getNewState().getType() == Material.ENDER_CHEST) {
|
|
cancel(event, true, event.getWorld(), event.getPlayer(), new TranslatableComponent("match.enderChestsDisabled"));
|
|
}
|
|
}
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
|
public void onPlayerBlockDamage(final BlockDamageEvent event) {
|
|
cancelUnlessInteracting(event, event.getPlayer());
|
|
}
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
|
public void onHangingBreak(final HangingBreakEvent event) {
|
|
cancelUnlessInteracting(event, event instanceof HangingBreakByEntityEvent ? ((HangingBreakByEntityEvent) event).getRemover()
|
|
: null);
|
|
}
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
|
public void onAdventureModeInteract(final AdventureModeInteractEvent event) {
|
|
cancelUnlessInteracting(event, event.getActor());
|
|
}
|
|
|
|
|
|
// --------------------------
|
|
// -- Player damage/combat --
|
|
// --------------------------
|
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
|
public void onAttack(final PlayerAttackEntityEvent event) {
|
|
Player player = event.getPlayer();
|
|
if(!(player.getGameMode() == GameMode.SPECTATOR && player.hasPermission(Permissions.STAFF)) && cancelUnlessInteracting(event, player)) {
|
|
final MatchPlayer attacker = getMatch().getPlayer(event.getPlayer());
|
|
if(attacker == null || attacker.isSpawned()) return;
|
|
getMatch().callEvent(new ObserverInteractEvent(attacker, ClickType.LEFT, null, event.getLeftClicked(), event.getPlayer().getInventory().getItemInMainHand()));
|
|
}
|
|
}
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
|
public void onDamage(final EntityDamageEvent event) {
|
|
cancelUnlessInteracting(event, event.getEntity());
|
|
if(event instanceof EntityDamageByEntityEvent) {
|
|
cancelUnlessInteracting(event, ((EntityDamageByEntityEvent) event).getDamager());
|
|
}
|
|
}
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
|
public void onCombust(final EntityCombustEvent event) {
|
|
cancelUnlessInteracting(event, event.getEntity());
|
|
if(event instanceof EntityCombustByEntityEvent) {
|
|
cancelUnlessInteracting(event, ((EntityCombustByEntityEvent) event).getCombuster());
|
|
}
|
|
}
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
|
public void onPotionSplash(final PotionSplashEvent event) {
|
|
for(LivingEntity entity : event.getAffectedEntities()) {
|
|
if(!getMatch().canInteract(entity)) {
|
|
event.setIntensity(entity, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST)
|
|
public void onPotionLinger(final AreaEffectCloudApplyEvent event) {
|
|
for(Iterator<LivingEntity> iterator = event.getAffectedEntities().iterator(); iterator.hasNext(); ) {
|
|
if(!getMatch().canInteract(iterator.next())) {
|
|
iterator.remove();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// -----------------------------------
|
|
// -- Player item/inventory actions --
|
|
// -----------------------------------
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
|
public void onPlayerDropItem(final PlayerDropItemEvent event) {
|
|
match.player(event.getPlayer()).ifPresent(player -> {
|
|
if(!player.canInteract()) {
|
|
if(player.isSpawned()) {
|
|
// If player is spawned (but frozen), force them to keep the item
|
|
event.setCancelled(true);
|
|
} else {
|
|
// If player is observing, just destroy the item
|
|
event.getItemDrop().remove();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
|
public void onPlayerPickupItem(final PlayerPickupItemEvent event) {
|
|
cancelUnlessInteracting(event, event.getPlayer());
|
|
}
|
|
|
|
|
|
// ----------------------
|
|
// -- Player targeting --
|
|
// ----------------------
|
|
|
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
|
public void onEntityTrack(final EntityTargetEvent event) {
|
|
// Handles mobs and XP orbs
|
|
if(event.getTarget() != null) cancelUnlessInteracting(event, event.getTarget());
|
|
}
|
|
}
|