ProjectAres/PGM/src/main/java/tc/oc/pgm/modules/EventFilterMatchModule.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());
}
}