001/*
002 * PlotSquared, a land and world management plugin for Minecraft.
003 * Copyright (C) IntellectualSites <https://intellectualsites.com>
004 * Copyright (C) IntellectualSites team and contributors
005 *
006 * This program is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU General Public License for more details.
015 *
016 * You should have received a copy of the GNU General Public License
017 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
018 */
019package com.plotsquared.bukkit.listener;
020
021import com.destroystokyo.paper.MaterialTags;
022import com.google.common.base.Charsets;
023import com.google.inject.Inject;
024import com.plotsquared.bukkit.player.BukkitPlayer;
025import com.plotsquared.bukkit.util.BukkitEntityUtil;
026import com.plotsquared.bukkit.util.BukkitUtil;
027import com.plotsquared.bukkit.util.UpdateUtility;
028import com.plotsquared.core.PlotSquared;
029import com.plotsquared.core.configuration.Settings;
030import com.plotsquared.core.configuration.caption.Caption;
031import com.plotsquared.core.configuration.caption.TranslatableCaption;
032import com.plotsquared.core.listener.PlayerBlockEventType;
033import com.plotsquared.core.listener.PlotListener;
034import com.plotsquared.core.location.Location;
035import com.plotsquared.core.permissions.Permission;
036import com.plotsquared.core.player.ConsolePlayer;
037import com.plotsquared.core.player.MetaDataAccess;
038import com.plotsquared.core.player.PlayerMetaDataKeys;
039import com.plotsquared.core.player.PlotPlayer;
040import com.plotsquared.core.plot.Plot;
041import com.plotsquared.core.plot.PlotArea;
042import com.plotsquared.core.plot.PlotId;
043import com.plotsquared.core.plot.PlotInventory;
044import com.plotsquared.core.plot.flag.FlagContainer;
045import com.plotsquared.core.plot.flag.implementations.AnimalInteractFlag;
046import com.plotsquared.core.plot.flag.implementations.BlockedCmdsFlag;
047import com.plotsquared.core.plot.flag.implementations.ChatFlag;
048import com.plotsquared.core.plot.flag.implementations.DenyPortalTravelFlag;
049import com.plotsquared.core.plot.flag.implementations.DenyPortalsFlag;
050import com.plotsquared.core.plot.flag.implementations.DenyTeleportFlag;
051import com.plotsquared.core.plot.flag.implementations.DoneFlag;
052import com.plotsquared.core.plot.flag.implementations.DropProtectionFlag;
053import com.plotsquared.core.plot.flag.implementations.EditSignFlag;
054import com.plotsquared.core.plot.flag.implementations.HangingBreakFlag;
055import com.plotsquared.core.plot.flag.implementations.HangingPlaceFlag;
056import com.plotsquared.core.plot.flag.implementations.HostileInteractFlag;
057import com.plotsquared.core.plot.flag.implementations.InteractionInteractFlag;
058import com.plotsquared.core.plot.flag.implementations.ItemDropFlag;
059import com.plotsquared.core.plot.flag.implementations.KeepInventoryFlag;
060import com.plotsquared.core.plot.flag.implementations.LecternReadBookFlag;
061import com.plotsquared.core.plot.flag.implementations.MiscInteractFlag;
062import com.plotsquared.core.plot.flag.implementations.PlayerInteractFlag;
063import com.plotsquared.core.plot.flag.implementations.PreventCreativeCopyFlag;
064import com.plotsquared.core.plot.flag.implementations.TamedInteractFlag;
065import com.plotsquared.core.plot.flag.implementations.TileDropFlag;
066import com.plotsquared.core.plot.flag.implementations.UntrustedVisitFlag;
067import com.plotsquared.core.plot.flag.implementations.UseFlag;
068import com.plotsquared.core.plot.flag.implementations.VehicleBreakFlag;
069import com.plotsquared.core.plot.flag.implementations.VehicleUseFlag;
070import com.plotsquared.core.plot.flag.implementations.VillagerInteractFlag;
071import com.plotsquared.core.plot.flag.types.BlockTypeWrapper;
072import com.plotsquared.core.plot.world.PlotAreaManager;
073import com.plotsquared.core.util.EventDispatcher;
074import com.plotsquared.core.util.MathMan;
075import com.plotsquared.core.util.PlotFlagUtil;
076import com.plotsquared.core.util.PremiumVerification;
077import com.plotsquared.core.util.entity.EntityCategories;
078import com.plotsquared.core.util.task.TaskManager;
079import com.plotsquared.core.util.task.TaskTime;
080import com.sk89q.worldedit.WorldEdit;
081import com.sk89q.worldedit.bukkit.BukkitAdapter;
082import com.sk89q.worldedit.util.Enums;
083import com.sk89q.worldedit.world.block.BlockType;
084import com.sk89q.worldedit.world.block.BlockTypes;
085import io.papermc.lib.PaperLib;
086import net.kyori.adventure.text.Component;
087import net.kyori.adventure.text.minimessage.MiniMessage;
088import net.kyori.adventure.text.minimessage.tag.Tag;
089import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
090import net.kyori.adventure.text.minimessage.tag.standard.StandardTags;
091import org.bukkit.Bukkit;
092import org.bukkit.FluidCollisionMode;
093import org.bukkit.Material;
094import org.bukkit.block.Block;
095import org.bukkit.block.BlockState;
096import org.bukkit.block.Sign;
097import org.bukkit.command.PluginCommand;
098import org.bukkit.entity.ArmorStand;
099import org.bukkit.entity.Boat;
100import org.bukkit.entity.Entity;
101import org.bukkit.entity.EntityType;
102import org.bukkit.entity.HumanEntity;
103import org.bukkit.entity.ItemFrame;
104import org.bukkit.entity.LivingEntity;
105import org.bukkit.entity.Player;
106import org.bukkit.entity.Projectile;
107import org.bukkit.entity.Tameable;
108import org.bukkit.entity.Vehicle;
109import org.bukkit.event.Event;
110import org.bukkit.event.EventHandler;
111import org.bukkit.event.EventPriority;
112import org.bukkit.event.Listener;
113import org.bukkit.event.block.Action;
114import org.bukkit.event.block.BlockBreakEvent;
115import org.bukkit.event.entity.EntityPickupItemEvent;
116import org.bukkit.event.entity.EntityPlaceEvent;
117import org.bukkit.event.entity.EntityPotionEffectEvent;
118import org.bukkit.event.entity.PlayerDeathEvent;
119import org.bukkit.event.hanging.HangingBreakByEntityEvent;
120import org.bukkit.event.hanging.HangingPlaceEvent;
121import org.bukkit.event.inventory.ClickType;
122import org.bukkit.event.inventory.InventoryClickEvent;
123import org.bukkit.event.inventory.InventoryCloseEvent;
124import org.bukkit.event.player.AsyncPlayerChatEvent;
125import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
126import org.bukkit.event.player.PlayerBucketEmptyEvent;
127import org.bukkit.event.player.PlayerBucketFillEvent;
128import org.bukkit.event.player.PlayerChangedWorldEvent;
129import org.bukkit.event.player.PlayerCommandPreprocessEvent;
130import org.bukkit.event.player.PlayerDropItemEvent;
131import org.bukkit.event.player.PlayerEvent;
132import org.bukkit.event.player.PlayerInteractAtEntityEvent;
133import org.bukkit.event.player.PlayerInteractEntityEvent;
134import org.bukkit.event.player.PlayerInteractEvent;
135import org.bukkit.event.player.PlayerJoinEvent;
136import org.bukkit.event.player.PlayerLocaleChangeEvent;
137import org.bukkit.event.player.PlayerMoveEvent;
138import org.bukkit.event.player.PlayerPortalEvent;
139import org.bukkit.event.player.PlayerQuitEvent;
140import org.bukkit.event.player.PlayerRespawnEvent;
141import org.bukkit.event.player.PlayerTakeLecternBookEvent;
142import org.bukkit.event.player.PlayerTeleportEvent;
143import org.bukkit.event.vehicle.VehicleDestroyEvent;
144import org.bukkit.event.vehicle.VehicleEntityCollisionEvent;
145import org.bukkit.event.vehicle.VehicleMoveEvent;
146import org.bukkit.event.world.PortalCreateEvent;
147import org.bukkit.help.HelpTopic;
148import org.bukkit.inventory.ItemStack;
149import org.bukkit.inventory.PlayerInventory;
150import org.bukkit.inventory.meta.ItemMeta;
151import org.bukkit.metadata.FixedMetadataValue;
152import org.bukkit.metadata.MetadataValue;
153import org.bukkit.plugin.Plugin;
154import org.bukkit.potion.PotionEffect;
155import org.bukkit.util.Vector;
156import org.checkerframework.checker.nullness.qual.NonNull;
157
158import java.lang.reflect.Field;
159import java.util.Collections;
160import java.util.HashSet;
161import java.util.List;
162import java.util.Locale;
163import java.util.Objects;
164import java.util.Optional;
165import java.util.Set;
166import java.util.UUID;
167
168/**
169 * Player Events involving plots.
170 */
171@SuppressWarnings("unused")
172public class PlayerEventListener implements Listener {
173
174    private static final Set<Material> MINECARTS = Set.of(
175            Material.MINECART,
176            Material.TNT_MINECART,
177            Material.CHEST_MINECART,
178            Material.COMMAND_BLOCK_MINECART,
179            Material.FURNACE_MINECART,
180            Material.HOPPER_MINECART
181    );
182    private static final Set<Material> BOOKS = Set.of(
183            Material.BOOK,
184            Material.KNOWLEDGE_BOOK,
185            Material.WRITABLE_BOOK,
186            Material.WRITTEN_BOOK
187    );
188
189    /**
190     * The correct EntityType for End Crystal, determined once at class loading time.
191     * Tries END_CRYSTAL first (1.21+), falls back to ENDER_CRYSTAL (1.20.4 and older).
192     */
193    private static final EntityType END_CRYSTAL_ENTITY_TYPE = Objects.requireNonNull(
194            Enums.findByValue(EntityType.class, "END_CRYSTAL", "ENDER_CRYSTAL")
195    );
196
197    private static final Set<String> DYES;
198
199    static {
200        Set<String> mutableDyes = new HashSet<>(Set.of(
201                "WHITE_DYE",
202                "LIGHT_GRAY_DYE",
203                "GRAY_DYE",
204                "BLACK_DYE",
205                "BROWN_DYE",
206                "RED_DYE",
207                "ORANGE_DYE",
208                "YELLOW_DYE",
209                "LIME_DYE",
210                "GREEN_DYE",
211                "CYAN_DYE",
212                "LIGHT_BLUE_DYE",
213                "BLUE_DYE",
214                "PURPLE_DYE",
215                "MAGENTA_DYE",
216                "PINK_DYE",
217                "GLOW_INK_SAC"
218        ));
219        int[] version = PlotSquared.platform().serverVersion();
220        if (version[1] >= 20) {
221            mutableDyes.add("HONEYCOMB");
222        }
223        DYES = Set.copyOf(mutableDyes);
224    }
225
226    private static final Set<String> INTERACTABLE_MATERIALS;
227
228    static {
229        // @formatter:off
230        // "temporary" fix for https://hub.spigotmc.org/jira/browse/SPIGOT-7813
231        // can (and should) be removed when 1.21 support is dropped
232        // List of all interactable 1.21 materials
233        INTERACTABLE_MATERIALS = Material.CHEST.isInteractable() ? null : Set.of(
234                "REDSTONE_ORE", "DEEPSLATE_REDSTONE_ORE", "CHISELED_BOOKSHELF", "DECORATED_POT", "CHEST", "CRAFTING_TABLE",
235                "FURNACE", "JUKEBOX", "OAK_FENCE", "SPRUCE_FENCE", "BIRCH_FENCE", "JUNGLE_FENCE", "ACACIA_FENCE", "CHERRY_FENCE",
236                "DARK_OAK_FENCE", "MANGROVE_FENCE", "BAMBOO_FENCE", "CRIMSON_FENCE", "WARPED_FENCE", "PUMPKIN",
237                "NETHER_BRICK_FENCE", "ENCHANTING_TABLE", "DRAGON_EGG", "ENDER_CHEST", "COMMAND_BLOCK", "BEACON", "ANVIL",
238                "CHIPPED_ANVIL", "DAMAGED_ANVIL", "LIGHT", "REPEATING_COMMAND_BLOCK", "CHAIN_COMMAND_BLOCK", "SHULKER_BOX",
239                "WHITE_SHULKER_BOX", "ORANGE_SHULKER_BOX", "MAGENTA_SHULKER_BOX", "LIGHT_BLUE_SHULKER_BOX", "YELLOW_SHULKER_BOX",
240                "LIME_SHULKER_BOX", "PINK_SHULKER_BOX", "GRAY_SHULKER_BOX", "LIGHT_GRAY_SHULKER_BOX", "CYAN_SHULKER_BOX",
241                "PURPLE_SHULKER_BOX", "BLUE_SHULKER_BOX", "BROWN_SHULKER_BOX", "GREEN_SHULKER_BOX", "RED_SHULKER_BOX",
242                "BLACK_SHULKER_BOX", "REPEATER", "COMPARATOR", "HOPPER", "DISPENSER", "DROPPER", "LECTERN", "LEVER",
243                "DAYLIGHT_DETECTOR", "TRAPPED_CHEST", "TNT", "NOTE_BLOCK", "STONE_BUTTON", "POLISHED_BLACKSTONE_BUTTON",
244                "OAK_BUTTON", "SPRUCE_BUTTON", "BIRCH_BUTTON", "JUNGLE_BUTTON", "ACACIA_BUTTON", "CHERRY_BUTTON",
245                "DARK_OAK_BUTTON", "MANGROVE_BUTTON", "BAMBOO_BUTTON", "CRIMSON_BUTTON", "WARPED_BUTTON", "IRON_DOOR", "OAK_DOOR",
246                "SPRUCE_DOOR", "BIRCH_DOOR", "JUNGLE_DOOR", "ACACIA_DOOR", "CHERRY_DOOR", "DARK_OAK_DOOR", "MANGROVE_DOOR",
247                "BAMBOO_DOOR", "CRIMSON_DOOR", "WARPED_DOOR", "COPPER_DOOR", "EXPOSED_COPPER_DOOR", "WEATHERED_COPPER_DOOR",
248                "OXIDIZED_COPPER_DOOR", "WAXED_COPPER_DOOR", "WAXED_EXPOSED_COPPER_DOOR", "WAXED_WEATHERED_COPPER_DOOR",
249                "WAXED_OXIDIZED_COPPER_DOOR", "IRON_TRAPDOOR", "OAK_TRAPDOOR", "SPRUCE_TRAPDOOR", "BIRCH_TRAPDOOR",
250                "JUNGLE_TRAPDOOR", "ACACIA_TRAPDOOR", "CHERRY_TRAPDOOR", "DARK_OAK_TRAPDOOR", "MANGROVE_TRAPDOOR",
251                "BAMBOO_TRAPDOOR", "CRIMSON_TRAPDOOR", "WARPED_TRAPDOOR", "COPPER_TRAPDOOR", "EXPOSED_COPPER_TRAPDOOR",
252                "WEATHERED_COPPER_TRAPDOOR", "OXIDIZED_COPPER_TRAPDOOR", "WAXED_COPPER_TRAPDOOR", "WAXED_EXPOSED_COPPER_TRAPDOOR",
253                "WAXED_WEATHERED_COPPER_TRAPDOOR", "WAXED_OXIDIZED_COPPER_TRAPDOOR", "OAK_FENCE_GATE", "SPRUCE_FENCE_GATE",
254                "BIRCH_FENCE_GATE", "JUNGLE_FENCE_GATE", "ACACIA_FENCE_GATE", "CHERRY_FENCE_GATE", "DARK_OAK_FENCE_GATE",
255                "MANGROVE_FENCE_GATE", "BAMBOO_FENCE_GATE", "CRIMSON_FENCE_GATE", "WARPED_FENCE_GATE", "STRUCTURE_BLOCK",
256                "JIGSAW", "OAK_SIGN", "SPRUCE_SIGN", "BIRCH_SIGN", "JUNGLE_SIGN", "ACACIA_SIGN", "CHERRY_SIGN", "DARK_OAK_SIGN",
257                "MANGROVE_SIGN", "BAMBOO_SIGN", "CRIMSON_SIGN", "WARPED_SIGN", "OAK_HANGING_SIGN", "SPRUCE_HANGING_SIGN",
258                "BIRCH_HANGING_SIGN", "JUNGLE_HANGING_SIGN", "ACACIA_HANGING_SIGN", "CHERRY_HANGING_SIGN",
259                "DARK_OAK_HANGING_SIGN", "MANGROVE_HANGING_SIGN", "BAMBOO_HANGING_SIGN", "CRIMSON_HANGING_SIGN",
260                "WARPED_HANGING_SIGN", "CAKE", "WHITE_BED", "ORANGE_BED", "MAGENTA_BED", "LIGHT_BLUE_BED", "YELLOW_BED",
261                "LIME_BED", "PINK_BED", "GRAY_BED", "LIGHT_GRAY_BED", "CYAN_BED", "PURPLE_BED", "BLUE_BED", "BROWN_BED",
262                "GREEN_BED", "RED_BED", "BLACK_BED", "CRAFTER", "BREWING_STAND", "CAULDRON", "FLOWER_POT", "LOOM", "COMPOSTER",
263                "BARREL", "SMOKER", "BLAST_FURNACE", "CARTOGRAPHY_TABLE", "FLETCHING_TABLE", "GRINDSTONE", "SMITHING_TABLE",
264                "STONECUTTER", "BELL", "CAMPFIRE", "SOUL_CAMPFIRE", "BEE_NEST", "BEEHIVE", "RESPAWN_ANCHOR", "CANDLE",
265                "WHITE_CANDLE", "ORANGE_CANDLE", "MAGENTA_CANDLE", "LIGHT_BLUE_CANDLE", "YELLOW_CANDLE", "LIME_CANDLE",
266                "PINK_CANDLE", "GRAY_CANDLE", "LIGHT_GRAY_CANDLE", "CYAN_CANDLE", "PURPLE_CANDLE", "BLUE_CANDLE", "BROWN_CANDLE",
267                "GREEN_CANDLE", "RED_CANDLE", "BLACK_CANDLE", "VAULT", "MOVING_PISTON", "REDSTONE_WIRE", "OAK_WALL_SIGN",
268                "SPRUCE_WALL_SIGN", "BIRCH_WALL_SIGN", "ACACIA_WALL_SIGN", "CHERRY_WALL_SIGN", "JUNGLE_WALL_SIGN",
269                "DARK_OAK_WALL_SIGN", "MANGROVE_WALL_SIGN", "BAMBOO_WALL_SIGN", "OAK_WALL_HANGING_SIGN",
270                "SPRUCE_WALL_HANGING_SIGN", "BIRCH_WALL_HANGING_SIGN", "ACACIA_WALL_HANGING_SIGN",
271                "CHERRY_WALL_HANGING_SIGN", "JUNGLE_WALL_HANGING_SIGN", "DARK_OAK_WALL_HANGING_SIGN",
272                "MANGROVE_WALL_HANGING_SIGN", "CRIMSON_WALL_HANGING_SIGN", "WARPED_WALL_HANGING_SIGN", "BAMBOO_WALL_HANGING_SIGN",
273                "WATER_CAULDRON", "LAVA_CAULDRON", "POWDER_SNOW_CAULDRON", "POTTED_TORCHFLOWER", "POTTED_OAK_SAPLING",
274                "POTTED_SPRUCE_SAPLING", "POTTED_BIRCH_SAPLING", "POTTED_JUNGLE_SAPLING", "POTTED_ACACIA_SAPLING",
275                "POTTED_CHERRY_SAPLING", "POTTED_DARK_OAK_SAPLING", "POTTED_MANGROVE_PROPAGULE", "POTTED_FERN",
276                "POTTED_DANDELION", "POTTED_POPPY", "POTTED_BLUE_ORCHID", "POTTED_ALLIUM", "POTTED_AZURE_BLUET",
277                "POTTED_RED_TULIP", "POTTED_ORANGE_TULIP", "POTTED_WHITE_TULIP", "POTTED_PINK_TULIP", "POTTED_OXEYE_DAISY",
278                "POTTED_CORNFLOWER", "POTTED_LILY_OF_THE_VALLEY", "POTTED_WITHER_ROSE", "POTTED_RED_MUSHROOM",
279                "POTTED_BROWN_MUSHROOM", "POTTED_DEAD_BUSH", "POTTED_CACTUS", "POTTED_BAMBOO", "SWEET_BERRY_BUSH",
280                "CRIMSON_WALL_SIGN", "WARPED_WALL_SIGN", "POTTED_CRIMSON_FUNGUS", "POTTED_WARPED_FUNGUS", "POTTED_CRIMSON_ROOTS",
281                "POTTED_WARPED_ROOTS", "CANDLE_CAKE", "WHITE_CANDLE_CAKE", "ORANGE_CANDLE_CAKE", "MAGENTA_CANDLE_CAKE",
282                "LIGHT_BLUE_CANDLE_CAKE", "YELLOW_CANDLE_CAKE", "LIME_CANDLE_CAKE", "PINK_CANDLE_CAKE", "GRAY_CANDLE_CAKE",
283                "LIGHT_GRAY_CANDLE_CAKE", "CYAN_CANDLE_CAKE", "PURPLE_CANDLE_CAKE", "BLUE_CANDLE_CAKE", "BROWN_CANDLE_CAKE",
284                "GREEN_CANDLE_CAKE", "RED_CANDLE_CAKE", "BLACK_CANDLE_CAKE", "CAVE_VINES", "CAVE_VINES_PLANT",
285                "POTTED_AZALEA_BUSH", "POTTED_FLOWERING_AZALEA_BUSH"
286        );
287        // @formatter:on
288    }
289
290    private final EventDispatcher eventDispatcher;
291    private final WorldEdit worldEdit;
292    private final PlotAreaManager plotAreaManager;
293    private final PlotListener plotListener;
294    // To prevent recursion
295    private boolean tmpTeleport = true;
296    private Field fieldPlayer;
297    private PlayerMoveEvent moveTmp;
298    private String internalVersion;
299
300    {
301        try {
302            fieldPlayer = PlayerEvent.class.getDeclaredField("player");
303            fieldPlayer.setAccessible(true);
304        } catch (NoSuchFieldException e) {
305            e.printStackTrace();
306        }
307    }
308
309    @Inject
310    public PlayerEventListener(
311            final @NonNull PlotAreaManager plotAreaManager,
312            final @NonNull EventDispatcher eventDispatcher,
313            final @NonNull WorldEdit worldEdit,
314            final @NonNull PlotListener plotListener
315    ) {
316        this.eventDispatcher = eventDispatcher;
317        this.worldEdit = worldEdit;
318        this.plotAreaManager = plotAreaManager;
319        this.plotListener = plotListener;
320    }
321
322    @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
323    public void onBlockBreak(final BlockBreakEvent event) {
324        Location location = BukkitUtil.adapt(event.getBlock().getLocation());
325        PlotArea area = location.getPlotArea();
326        if (area == null) {
327            return;
328        }
329        Plot plot = area.getPlot(location);
330        if (plot != null) {
331            event.setDropItems(plot.getFlag(TileDropFlag.class));
332        }
333    }
334
335    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
336    public void onPlayerDyeSign(PlayerInteractEvent event) {
337        ItemStack itemStack = event.getItem();
338        if (itemStack == null) {
339            return;
340        }
341        Block block = event.getClickedBlock();
342        if (block != null && block.getState() instanceof Sign) {
343            if (DYES.contains(itemStack.getType().toString())) {
344                Location location = BukkitUtil.adapt(block.getLocation());
345                PlotArea area = location.getPlotArea();
346                if (area == null) {
347                    return;
348                }
349                Plot plot = location.getOwnedPlot();
350                if (plot == null) {
351                    if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, EditSignFlag.class, false)
352                            && !event.getPlayer().hasPermission(Permission.PERMISSION_ADMIN_INTERACT_ROAD.toString())) {
353                        event.setCancelled(true);
354                    }
355                    return;
356                }
357                if (plot.isAdded(event.getPlayer().getUniqueId())) {
358                    return; // allow for added players
359                }
360                if (!plot.getFlag(EditSignFlag.class)
361                        && !event.getPlayer().hasPermission(Permission.PERMISSION_ADMIN_INTERACT_OTHER.toString())) {
362                    plot.debug(event.getPlayer().getName() + " could not color the sign because of edit-sign = false");
363                    event.setCancelled(true);
364                }
365            }
366        }
367    }
368
369    @EventHandler(ignoreCancelled = true)
370    public void onEffect(@NonNull EntityPotionEffectEvent event) {
371        if (Settings.Enabled_Components.DISABLE_BEACON_EFFECT_OVERFLOW ||
372                event.getCause() != EntityPotionEffectEvent.Cause.BEACON ||
373                !(event.getEntity() instanceof Player player)) {
374            return;
375        }
376
377        UUID uuid = player.getUniqueId();
378        PotionEffect effect = event.getNewEffect();
379        if (effect == null) {
380            PotionEffect oldEffect = event.getOldEffect();
381            if (oldEffect != null) {
382                String name = oldEffect.getType().getName();
383                plotListener.addEffect(uuid, name, -1);
384            }
385        } else {
386            long expiresAt = System.currentTimeMillis() + effect.getDuration() * 50L; //Convert ticks to milliseconds
387            String name = effect.getType().getName();
388            plotListener.addEffect(uuid, name, expiresAt);
389        }
390    }
391
392    @EventHandler
393    public void onVehicleEntityCollision(VehicleEntityCollisionEvent e) {
394        if (e.getVehicle() instanceof Boat) {
395            Location location = BukkitUtil.adapt(e.getEntity().getLocation());
396            if (location.isPlotArea()) {
397                if (e.getEntity() instanceof Player) {
398                    PlotPlayer<Player> player = BukkitUtil.adapt((Player) e.getEntity());
399                    Plot plot = player.getCurrentPlot();
400                    if (plot != null) {
401                        if (!plot.isAdded(player.getUUID())) {
402                            //Here the event is only canceled if the player is not the owner
403                            //of the property on which he is located.
404                            e.setCancelled(true);
405                        }
406                    } else {
407                        e.setCancelled(true);
408                    }
409                } else {
410                    //Here the event is cancelled too, otherwise you can move the
411                    //boat with EchoPets or other mobs running around on the plot.
412                    e.setCancelled(true);
413                }
414            }
415        }
416    }
417
418    @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
419    public void playerCommand(PlayerCommandPreprocessEvent event) {
420        String msg = event.getMessage().replace("/", "").toLowerCase(Locale.ROOT).trim();
421        if (msg.isEmpty()) {
422            return;
423        }
424        Player player = event.getPlayer();
425        PlotPlayer<Player> plotPlayer = BukkitUtil.adapt(player);
426        Location location = plotPlayer.getLocation();
427        PlotArea area = location.getPlotArea();
428        if (area == null) {
429            return;
430        }
431        String[] parts = msg.split(" ");
432        Plot plot = plotPlayer.getCurrentPlot();
433        // Check WorldEdit
434        switch (parts[0]) {
435            case "up", "worldedit:up" -> {
436                if (plot == null || (!plot.isAdded(plotPlayer.getUUID()) && !plotPlayer.hasPermission(
437                        Permission.PERMISSION_ADMIN_BUILD_OTHER,
438                        true
439                ))) {
440                    event.setCancelled(true);
441                    return;
442                }
443            }
444        }
445        if (plot == null && !area.isRoadFlags()) {
446            return;
447        }
448
449        List<String> blockedCommands = plot != null ?
450                plot.getFlag(BlockedCmdsFlag.class) :
451                area.getFlag(BlockedCmdsFlag.class);
452        if (blockedCommands.isEmpty()) {
453            return;
454        }
455        if (plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_BLOCKED_CMDS)) {
456            return;
457        }
458        // When using namespaced commands, we're not interested in the namespace
459        String part = parts[0];
460        if (part.contains(":")) {
461            String[] namespaced = part.split(":");
462            part = namespaced[1];
463            msg = msg.substring(namespaced[0].length() + 1);
464        }
465        msg = replaceAliases(msg, part);
466        for (String blocked : blockedCommands) {
467            if (blocked.equalsIgnoreCase(msg)) {
468                String perm;
469                if (plot != null && plot.isAdded(plotPlayer.getUUID())) {
470                    perm = "plots.admin.command.blocked-cmds.shared";
471                } else {
472                    perm = "plots.admin.command.blocked-cmds.road";
473                }
474                if (!plotPlayer.hasPermission(perm)) {
475                    plotPlayer.sendMessage(TranslatableCaption.of("blockedcmds.command_blocked"));
476                    event.setCancelled(true);
477                }
478                return;
479            }
480        }
481    }
482
483    private String replaceAliases(String msg, String part) {
484        String s1 = part;
485        Set<String> aliases = new HashSet<>();
486        for (HelpTopic cmdLabel : Bukkit.getServer().getHelpMap().getHelpTopics()) {
487            if (part.equals(cmdLabel.getName())) {
488                break;
489            }
490            String label = cmdLabel.getName().replaceFirst("/", "");
491            if (aliases.contains(label)) {
492                continue;
493            }
494            PluginCommand p = Bukkit.getPluginCommand(label);
495            if (p != null) {
496                for (String a : p.getAliases()) {
497                    if (aliases.contains(a)) {
498                        continue;
499                    }
500                    aliases.add(a);
501                    a = a.replaceFirst("/", "");
502                    if (!a.equals(label) && a.equals(part)) {
503                        part = label;
504                        break;
505                    }
506                }
507            }
508        }
509        if (!s1.equals(part)) {
510            msg = msg.replace(s1, part);
511        }
512        return msg;
513    }
514
515    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
516    public void onPreLogin(final AsyncPlayerPreLoginEvent event) {
517        final UUID uuid;
518        if (Settings.UUID.OFFLINE) {
519            if (Settings.UUID.FORCE_LOWERCASE) {
520                uuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + event.getName().toLowerCase()).getBytes(Charsets.UTF_8));
521            } else {
522                uuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + event.getName()).getBytes(Charsets.UTF_8));
523            }
524        } else {
525            uuid = event.getUniqueId();
526        }
527        PlotSquared.get().getImpromptuUUIDPipeline().storeImmediately(event.getName(), uuid);
528    }
529
530    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
531    @SuppressWarnings("deprecation") // Paper deprecation
532    public void onConnect(PlayerJoinEvent event) {
533        final Player player = event.getPlayer();
534        PlotSquared.platform().playerManager().removePlayer(player.getUniqueId());
535        final PlotPlayer<Player> pp = BukkitUtil.adapt(player);
536
537        // we're stripping the country code as we don't want to differ between countries
538        pp.setLocale(Locale.forLanguageTag(player.getLocale().substring(0, 2)));
539
540        Location location = pp.getLocation();
541        PlotArea area = location.getPlotArea();
542        if (area != null) {
543            Plot plot = area.getPlot(location);
544            if (plot != null) {
545                plotListener.plotEntry(pp, plot);
546            }
547        }
548        // Delayed
549
550        // Async
551        TaskManager.runTaskLaterAsync(
552                () -> {
553                    if (!player.hasPlayedBefore() && player.isOnline()) {
554                        player.saveData();
555                    }
556                    this.eventDispatcher.doJoinTask(pp);
557                }, TaskTime.seconds(1L)
558        );
559
560        if (pp.hasPermission(Permission.PERMISSION_ADMIN_UPDATE_NOTIFICATION.toString()) && Settings.Enabled_Components.UPDATE_NOTIFICATIONS
561                && PremiumVerification.isPremium() && UpdateUtility.hasUpdate) {
562            Caption boundary = TranslatableCaption.of("update.update_boundary");
563            Caption updateNotification = TranslatableCaption.of("update.update_notification");
564            pp.sendMessage(boundary);
565            pp.sendMessage(
566                    updateNotification,
567                    TagResolver.builder()
568                            .tag("p2version", Tag.inserting(Component.text(UpdateUtility.internalVersion.versionString())))
569                            .tag("spigotversion", Tag.inserting(Component.text(UpdateUtility.spigotVersion)))
570                            .tag("downloadurl", Tag.preProcessParsed("https://www.spigotmc.org/resources/77506/updates"))
571                            .build()
572            );
573            pp.sendMessage(boundary);
574        }
575    }
576
577    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
578    public void playerRespawn(PlayerRespawnEvent event) {
579        Player player = event.getPlayer();
580        PlotPlayer<Player> pp = BukkitUtil.adapt(player);
581        this.eventDispatcher.doRespawnTask(pp);
582    }
583
584    @SuppressWarnings("deprecation") // We explicitly want #getHomeSynchronous here
585    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
586    public void onTeleport(PlayerTeleportEvent event) {
587        Player player = event.getPlayer();
588        //We need to account for bad plugins like NoCheatPlus that teleports player on/before login -_-
589        if (!player.isOnline()) {
590            return;
591        }
592        BukkitPlayer pp = BukkitUtil.adapt(player);
593        try (final MetaDataAccess<Plot> lastPlotAccess =
594                     pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
595            Plot lastPlot = lastPlotAccess.get().orElse(null);
596            org.bukkit.Location to = event.getTo();
597            //noinspection ConstantConditions
598            if (to != null) {
599                Location location = BukkitUtil.adapt(to);
600                PlotArea area = location.getPlotArea();
601                if (area == null) {
602                    if (lastPlot != null) {
603                        plotListener.plotExit(pp, lastPlot);
604                        lastPlotAccess.remove();
605                    }
606                    try (final MetaDataAccess<Location> lastLocationAccess =
607                                 pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LOCATION)) {
608                        lastLocationAccess.remove();
609                    }
610                    return;
611                }
612                Plot plot = area.getPlot(location);
613                if (plot != null && !plot.equals(lastPlot)) {
614                    final boolean result = DenyTeleportFlag.allowsTeleport(pp, plot);
615                    // there is one possibility to still allow teleportation:
616                    // to is identical to the plot's home location, and untrusted-visit is true
617                    // i.e. untrusted-visit can override deny-teleport
618                    // this is acceptable, because otherwise it wouldn't make sense to have both flags set
619                    if (result || (plot.getFlag(UntrustedVisitFlag.class) && plot
620                            .getHomeSynchronous()
621                            .equals(BukkitUtil.adaptComplete(to)))) {
622                        // returns false if the player is not allowed to enter the plot (if they are denied, for example)
623                        // don't let the move event cancel the entry after teleport, but rather catch and cancel early (#4647)
624                        if (!plotListener.plotEntry(pp, plot)) {
625                            event.setCancelled(true);
626                        }
627                    } else {
628                        pp.sendMessage(
629                                TranslatableCaption.of("deny.no_enter"),
630                                TagResolver.resolver("plot", Tag.inserting(Component.text(plot.toString())))
631                        );
632                        event.setCancelled(true);
633                    }
634                }
635            }
636        }
637        playerMove(event);
638    }
639
640    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
641    public void onWorldChanged(PlayerChangedWorldEvent event) {
642        Player player = event.getPlayer();
643        BukkitPlayer pp = BukkitUtil.adapt(player);
644        if (this.worldEdit != null) {
645            if (!pp.hasPermission(Permission.PERMISSION_WORLDEDIT_BYPASS)) {
646                if (pp.getAttribute("worldedit")) {
647                    pp.removeAttribute("worldedit");
648                }
649            }
650        }
651    }
652
653    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
654    public void vehicleMove(VehicleMoveEvent event)
655            throws IllegalAccessException {
656        final org.bukkit.Location from = event.getFrom();
657        final org.bukkit.Location to = event.getTo();
658
659        int toX, toZ;
660        if ((toX = MathMan.roundInt(to.getX())) != MathMan.roundInt(from.getX()) | (toZ = MathMan.roundInt(to.getZ())) != MathMan
661                .roundInt(from.getZ())) {
662            Vehicle vehicle = event.getVehicle();
663
664            // Check allowed
665            if (!vehicle.getPassengers().isEmpty()) {
666                Entity passenger = vehicle.getPassengers().get(0);
667
668                if (passenger instanceof final Player player) {
669                    // reset
670                    if (moveTmp == null) {
671                        moveTmp = new PlayerMoveEvent(null, from, to);
672                    }
673                    moveTmp.setFrom(from);
674                    moveTmp.setTo(to);
675                    moveTmp.setCancelled(false);
676                    fieldPlayer.set(moveTmp, player);
677
678                    List<Entity> passengers = vehicle.getPassengers();
679
680                    this.playerMove(moveTmp);
681                    org.bukkit.Location dest;
682                    if (moveTmp.isCancelled()) {
683                        dest = from;
684                    } else if (MathMan.roundInt(moveTmp.getTo().getX()) != toX || MathMan.roundInt(moveTmp
685                            .getTo()
686                            .getZ()) != toZ) {
687                        dest = to;
688                    } else {
689                        dest = null;
690                    }
691                    if (dest != null) {
692                        vehicle.eject();
693                        vehicle.setVelocity(new Vector(0d, 0d, 0d));
694                        PaperLib.teleportAsync(vehicle, dest);
695                        passengers.forEach(vehicle::addPassenger);
696                        return;
697                    }
698                }
699                if (Settings.Enabled_Components.KILL_ROAD_VEHICLES) {
700                    final com.sk89q.worldedit.world.entity.EntityType entityType = BukkitAdapter.adapt(vehicle.getType());
701                    // Horses etc are vehicles, but they're also animals
702                    // so this filters out all living entities
703                    if (EntityCategories.VEHICLE.contains(entityType) && !EntityCategories.ANIMAL.contains(entityType)) {
704                        List<MetadataValue> meta = vehicle.getMetadata("plot");
705                        Plot toPlot = BukkitUtil.adapt(to).getPlot();
706                        if (!meta.isEmpty()) {
707                            Plot origin = (Plot) meta.get(0).value();
708                            if (origin != null && !origin.getBasePlot(false).equals(toPlot)) {
709                                vehicle.remove();
710                            }
711                        } else if (toPlot != null) {
712                            vehicle.setMetadata("plot", new FixedMetadataValue((Plugin) PlotSquared.platform(), toPlot));
713                        }
714                    }
715                }
716            }
717
718        }
719    }
720
721    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
722    public void playerMove(PlayerMoveEvent event) {
723        org.bukkit.Location from = event.getFrom();
724        org.bukkit.Location to = event.getTo();
725        int x2;
726        if (MathMan.roundInt(from.getX()) != (x2 = MathMan.roundInt(to.getX()))) {
727            Player player = event.getPlayer();
728            BukkitPlayer pp = BukkitUtil.adapt(player);
729            // Cancel teleport
730            if (TaskManager.removeFromTeleportQueue(pp.getName())) {
731                pp.sendMessage(TranslatableCaption.of("teleport.teleport_failed"));
732            }
733            // Set last location
734            Location location = BukkitUtil.adapt(to);
735            try (final MetaDataAccess<Location> lastLocationAccess =
736                         pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LOCATION)) {
737                lastLocationAccess.remove();
738            }
739            PlotArea area = location.getPlotArea();
740            if (area == null) {
741                try (final MetaDataAccess<Plot> lastPlotAccess =
742                             pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
743                    lastPlotAccess.remove();
744                }
745                return;
746            }
747            Plot now = area.getPlot(location);
748            Plot lastPlot;
749            try (final MetaDataAccess<Plot> lastPlotAccess =
750                         pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
751                lastPlot = lastPlotAccess.get().orElse(null);
752            }
753            if (now == null) {
754                try (final MetaDataAccess<Boolean> kickAccess =
755                             pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_KICK)) {
756                    if (lastPlot != null && !plotListener.plotExit(pp, lastPlot) && this.tmpTeleport && !kickAccess.get().orElse(
757                            false)) {
758                        pp.sendMessage(
759                                TranslatableCaption.of("permission.no_permission_event"),
760                                TagResolver.resolver(
761                                        "node",
762                                        Tag.inserting(Permission.PERMISSION_ADMIN_EXIT_DENIED)
763                                )
764                        );
765                        this.tmpTeleport = false;
766                        if (lastPlot.equals(BukkitUtil.adapt(from).getPlot())) {
767                            player.teleport(from);
768                        } else {
769                            player.teleport(player.getWorld().getSpawnLocation());
770                        }
771                        this.tmpTeleport = true;
772                        event.setCancelled(true);
773                        return;
774                    }
775                }
776            } else if (now.equals(lastPlot)) {
777                ForceFieldListener.handleForcefield(player, pp, now);
778            } else if (!plotListener.plotEntry(pp, now) && this.tmpTeleport) {
779                pp.sendMessage(
780                        TranslatableCaption.of("deny.no_enter"),
781                        TagResolver.resolver("plot", Tag.inserting(Component.text(now.toString())))
782                );
783                this.tmpTeleport = false;
784                to.setX(from.getBlockX());
785                to.setY(from.getBlockY());
786                to.setZ(from.getBlockZ());
787                player.teleport(event.getTo());
788                this.tmpTeleport = true;
789                return;
790            }
791            int border = area.getBorder(true);
792            int x1;
793            if (x2 > border && this.tmpTeleport) {
794                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BYPASS_BORDER)) {
795                    to.setX(border - 1);
796                    this.tmpTeleport = false;
797                    player.teleport(event.getTo());
798                    this.tmpTeleport = true;
799                    pp.sendMessage(TranslatableCaption.of("border.denied"));
800                } else if (MathMan.roundInt(from.getX()) <= border) { // Only send if they just moved out of the border
801                    pp.sendMessage(TranslatableCaption.of("border.bypass.exited"));
802                }
803            } else if (x2 < -border && this.tmpTeleport) {
804                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BYPASS_BORDER)) {
805                    to.setX(-border + 1);
806                    this.tmpTeleport = false;
807                    player.teleport(event.getTo());
808                    this.tmpTeleport = true;
809                    pp.sendMessage(TranslatableCaption.of("border.denied"));
810                } else if (MathMan.roundInt(from.getX()) >= -border) { // Only send if they just moved out of the border
811                    pp.sendMessage(TranslatableCaption.of("border.bypass.exited"));
812                }
813            } else if (((x1 = MathMan.roundInt(from.getX())) >= border && x2 <= border) || (x1 <= -border && x2 >= -border)) {
814                if (pp.hasPermission(Permission.PERMISSION_ADMIN_BYPASS_BORDER)) {
815                    pp.sendMessage(TranslatableCaption.of("border.bypass.entered"));
816                }
817            }
818        }
819        int z2;
820        if (MathMan.roundInt(from.getZ()) != (z2 = MathMan.roundInt(to.getZ()))) {
821            Player player = event.getPlayer();
822            BukkitPlayer pp = BukkitUtil.adapt(player);
823            // Cancel teleport
824            if (TaskManager.removeFromTeleportQueue(pp.getName())) {
825                pp.sendMessage(TranslatableCaption.of("teleport.teleport_failed"));
826            }
827            // Set last location
828            Location location = BukkitUtil.adapt(to);
829            try (final MetaDataAccess<Location> lastLocationAccess =
830                         pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LOCATION)) {
831                lastLocationAccess.set(location);
832            }
833            PlotArea area = location.getPlotArea();
834            if (area == null) {
835                try (final MetaDataAccess<Plot> lastPlotAccess =
836                             pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
837                    lastPlotAccess.remove();
838                }
839                return;
840            }
841            Plot plot = area.getPlot(location);
842            Plot lastPlot;
843            try (final MetaDataAccess<Plot> lastPlotAccess =
844                         pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
845                lastPlot = lastPlotAccess.get().orElse(null);
846            }
847            if (plot == null) {
848                try (final MetaDataAccess<Boolean> kickAccess =
849                             pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_KICK)) {
850                    if (lastPlot != null && !plotListener.plotExit(pp, lastPlot) && this.tmpTeleport && !kickAccess.get().orElse(
851                            false)) {
852                        pp.sendMessage(
853                                TranslatableCaption.of("permission.no_permission_event"),
854                                TagResolver.resolver(
855                                        "node",
856                                        Tag.inserting(Permission.PERMISSION_ADMIN_EXIT_DENIED)
857                                )
858                        );
859                        this.tmpTeleport = false;
860                        if (lastPlot.equals(BukkitUtil.adapt(from).getPlot())) {
861                            player.teleport(from);
862                        } else {
863                            player.teleport(player.getWorld().getSpawnLocation());
864                        }
865                        this.tmpTeleport = true;
866                        event.setCancelled(true);
867                        return;
868                    }
869                }
870            } else if (plot.equals(lastPlot)) {
871                ForceFieldListener.handleForcefield(player, pp, plot);
872            } else if (!plotListener.plotEntry(pp, plot) && this.tmpTeleport) {
873                pp.sendMessage(
874                        TranslatableCaption.of("deny.no_enter"),
875                        TagResolver.resolver("plot", Tag.inserting(Component.text(plot.toString())))
876                );
877                this.tmpTeleport = false;
878                player.teleport(from);
879                to.setX(from.getBlockX());
880                to.setY(from.getBlockY());
881                to.setZ(from.getBlockZ());
882                player.teleport(event.getTo());
883                this.tmpTeleport = true;
884                return;
885            }
886            int border = area.getBorder(true);
887            int z1;
888            if (z2 > border && this.tmpTeleport) {
889                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BYPASS_BORDER)) {
890                    to.setZ(border - 1);
891                    this.tmpTeleport = false;
892                    player.teleport(event.getTo());
893                    this.tmpTeleport = true;
894                    pp.sendMessage(TranslatableCaption.of("border.denied"));
895                } else if (MathMan.roundInt(from.getZ()) <= border) { // Only send if they just moved out of the border
896                    pp.sendMessage(TranslatableCaption.of("border.bypass.exited"));
897                }
898            } else if (z2 < -border && this.tmpTeleport) {
899                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BYPASS_BORDER)) {
900                    to.setZ(-border + 1);
901                    this.tmpTeleport = false;
902                    player.teleport(event.getTo());
903                    this.tmpTeleport = true;
904                    pp.sendMessage(TranslatableCaption.of("border.denied"));
905                } else if (MathMan.roundInt(from.getZ()) >= -border) { // Only send if they just moved out of the border
906                    pp.sendMessage(TranslatableCaption.of("border.bypass.exited"));
907                }
908            } else if (((z1 = MathMan.roundInt(from.getZ())) >= border && z2 <= border) || (z1 <= -border && z2 >= -border)) {
909                if (pp.hasPermission(Permission.PERMISSION_ADMIN_BYPASS_BORDER)) {
910                    pp.sendMessage(TranslatableCaption.of("border.bypass.entered"));
911                }
912            }
913        }
914    }
915
916    @EventHandler(priority = EventPriority.LOW)
917    @SuppressWarnings("deprecation") // Paper deprecation
918    public void onChat(AsyncPlayerChatEvent event) {
919        if (event.isCancelled()) {
920            return;
921        }
922
923        BukkitPlayer plotPlayer = BukkitUtil.adapt(event.getPlayer());
924        Location location = plotPlayer.getLocation();
925        PlotArea area = location.getPlotArea();
926        if (area == null) {
927            return;
928        }
929        Plot plot = area.getPlot(location);
930        if (plot == null) {
931            return;
932        }
933        if (!((plot.getFlag(ChatFlag.class) && area.isPlotChat() && plotPlayer.getAttribute("chat"))
934                || area.isForcingPlotChat())) {
935            return;
936        }
937        if (plot.isDenied(plotPlayer.getUUID()) && !plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_CHAT_BYPASS)) {
938            return;
939        }
940        event.setCancelled(true);
941        Set<Player> recipients = event.getRecipients();
942        recipients.clear();
943        Set<PlotPlayer<?>> spies = new HashSet<>();
944        Set<PlotPlayer<?>> plotRecipients = new HashSet<>();
945        for (final PlotPlayer<?> pp : PlotSquared.platform().playerManager().getPlayers()) {
946            if (pp.getAttribute("chatspy")) {
947                spies.add(pp);
948            } else {
949                Plot current = pp.getCurrentPlot();
950                if (current != null && current.getBasePlot(false).equals(plot)) {
951                    plotRecipients.add(pp);
952                }
953            }
954        }
955        String message = event.getMessage();
956        String sender = event.getPlayer().getDisplayName();
957        PlotId id = plot.getId();
958        String worldName = plot.getWorldName();
959        Caption msg = TranslatableCaption.of("chat.plot_chat_format");
960        TagResolver.Builder builder = TagResolver.builder();
961        builder.tag("world", Tag.inserting(Component.text(worldName)));
962        builder.tag("plot_id", Tag.inserting(Component.text(id.toString())));
963        builder.tag("sender", Tag.inserting(Component.text(sender)));
964        if (plotPlayer.hasPermission("plots.chat.color")) {
965            builder.tag(
966                    "msg", Tag.inserting(MiniMessage.miniMessage().deserialize(
967                            message,
968                            TagResolver.resolver(
969                                    StandardTags.color(), StandardTags.gradient(),
970                                    StandardTags.rainbow(), StandardTags.decorations()
971                            )
972                    ))
973            );
974        } else {
975            builder.tag("msg", Tag.inserting(Component.text(message)));
976        }
977        for (PlotPlayer<?> receiver : plotRecipients) {
978            receiver.sendMessage(msg, builder.build());
979        }
980        if (!spies.isEmpty()) {
981            Caption spymsg = TranslatableCaption.of("chat.plot_chat_spy_format");
982            for (PlotPlayer<?> player : spies) {
983                player.sendMessage(spymsg, builder.tag("message", Tag.inserting(Component.text(message))).build());
984            }
985        }
986        if (Settings.Chat.LOG_PLOTCHAT_TO_CONSOLE) {
987            Caption spymsg = TranslatableCaption.of("chat.plot_chat_spy_format");
988            ConsolePlayer.getConsole().sendMessage(
989                    spymsg,
990                    builder.tag("message", Tag.inserting(Component.text(message))).build()
991            );
992        }
993    }
994
995    @SuppressWarnings("deprecation")
996    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
997    public void onInventoryClick(InventoryClickEvent event) {
998        /*if (!event.isLeftClick() || (event.getAction() != InventoryAction.PLACE_ALL) || event
999            .isShiftClick()) {
1000            return;
1001        }*/
1002        HumanEntity entity = event.getWhoClicked();
1003        if (!(entity instanceof Player) || !this.plotAreaManager
1004                .hasPlotArea(entity.getWorld().getName())) {
1005            return;
1006        }
1007
1008        HumanEntity clicker = event.getWhoClicked();
1009        if (!(clicker instanceof Player player)) {
1010            return;
1011        }
1012        BukkitPlayer pp = BukkitUtil.adapt(player);
1013        final PlotInventory inventory = PlotInventory.getOpenPlotInventory(pp);
1014        if (inventory != null && event.getRawSlot() == event.getSlot()) {
1015            if (!inventory.onClick(event.getSlot())) {
1016                event.setResult(Event.Result.DENY);
1017                event.setCancelled(true);
1018                inventory.close();
1019            }
1020        }
1021        PlayerInventory inv = player.getInventory();
1022        int slot = inv.getHeldItemSlot();
1023        if ((slot > 8) || !event.getEventName().equals("InventoryCreativeEvent")) {
1024            return;
1025        }
1026        ItemStack oldItem = inv.getItemInHand();
1027        ItemMeta oldMeta = oldItem.getItemMeta();
1028        ItemStack newItem = event.getCursor();
1029        ItemMeta newMeta = newItem.getItemMeta();
1030
1031        if (event.getClick() == ClickType.CREATIVE) {
1032            final Plot plot = pp.getCurrentPlot();
1033            if (plot != null) {
1034                if (plot.getFlag(PreventCreativeCopyFlag.class) && !plot
1035                        .isAdded(player.getUniqueId()) && !pp.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_OTHER)) {
1036                    final ItemStack newStack =
1037                            new ItemStack(newItem.getType(), newItem.getAmount());
1038                    event.setCursor(newStack);
1039                    plot.debug(player.getName()
1040                            + " could not creative-copy an item because prevent-creative-copy = true");
1041                }
1042            } else {
1043                PlotArea area = pp.getPlotAreaAbs();
1044                if (area != null && PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, PreventCreativeCopyFlag.class, true)) {
1045                    final ItemStack newStack =
1046                            new ItemStack(newItem.getType(), newItem.getAmount());
1047                    event.setCursor(newStack);
1048                }
1049            }
1050            return;
1051        }
1052
1053        String newLore = "";
1054        if (newMeta != null) {
1055            List<String> lore = newMeta.getLore();
1056            if (lore != null) {
1057                newLore = lore.toString();
1058            }
1059        }
1060        String oldLore = "";
1061        if (oldMeta != null) {
1062            List<String> lore = oldMeta.getLore();
1063            if (lore != null) {
1064                oldLore = lore.toString();
1065            }
1066        }
1067        Material itemType = newItem.getType();
1068        if (!"[(+NBT)]".equals(newLore) || (oldItem.equals(newItem) && newLore.equals(oldLore))) {
1069            if (newMeta == null || (itemType != Material.LEGACY_BANNER && itemType != Material.PLAYER_HEAD)) {
1070                return;
1071            }
1072        }
1073        Block block = player.getTargetBlock(null, 7);
1074        org.bukkit.block.BlockState state = block.getState();
1075        Material stateType = state.getType();
1076        if (stateType != itemType) {
1077            if (stateType == Material.LEGACY_WALL_BANNER || stateType == Material.LEGACY_STANDING_BANNER) {
1078                if (itemType != Material.LEGACY_BANNER) {
1079                    return;
1080                }
1081            } else if (stateType == Material.LEGACY_SKULL) {
1082                if (itemType != Material.LEGACY_SKULL_ITEM) {
1083                    return;
1084                }
1085            } else {
1086                return;
1087            }
1088        }
1089        Location location = BukkitUtil.adapt(state.getLocation());
1090        PlotArea area = location.getPlotArea();
1091        if (area == null) {
1092            return;
1093        }
1094        Plot plot = area.getPlotAbs(location);
1095        boolean cancelled = false;
1096        if (plot == null) {
1097            if (!pp.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_ROAD)) {
1098                pp.sendMessage(
1099                        TranslatableCaption.of("permission.no_permission_event"),
1100                        TagResolver.resolver(
1101                                "node",
1102                                Tag.inserting(Permission.PERMISSION_ADMIN_INTERACT_ROAD)
1103                        )
1104                );
1105                cancelled = true;
1106            }
1107        } else if (!plot.hasOwner()) {
1108            if (!pp.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_UNOWNED)) {
1109                pp.sendMessage(
1110                        TranslatableCaption.of("permission.no_permission_event"),
1111                        TagResolver.resolver(
1112                                "node",
1113                                Tag.inserting(Permission.PERMISSION_ADMIN_INTERACT_UNOWNED)
1114                        )
1115                );
1116                cancelled = true;
1117            }
1118        } else {
1119            UUID uuid = pp.getUUID();
1120            if (!plot.isAdded(uuid)) {
1121                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_OTHER)) {
1122                    pp.sendMessage(
1123                            TranslatableCaption.of("permission.no_permission_event"),
1124                            TagResolver.resolver(
1125                                    "node",
1126                                    Tag.inserting(Permission.PERMISSION_ADMIN_INTERACT_OTHER)
1127                            )
1128                    );
1129                    cancelled = true;
1130                }
1131            }
1132        }
1133        if (cancelled) {
1134            if ((oldItem.getType() == newItem.getType()) && (oldItem.getDurability() == newItem
1135                    .getDurability())) {
1136                event.setCursor(
1137                        new ItemStack(newItem.getType(), newItem.getAmount(), newItem.getDurability()));
1138                event.setCancelled(true);
1139                return;
1140            }
1141            event.setCursor(
1142                    new ItemStack(newItem.getType(), newItem.getAmount(), newItem.getDurability()));
1143        }
1144    }
1145
1146    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
1147    public void onInteract(PlayerInteractAtEntityEvent e) {
1148        Entity entity = e.getRightClicked();
1149        if (!(entity instanceof ArmorStand) && !(entity instanceof ItemFrame)) {
1150            return;
1151        }
1152        Location location = BukkitUtil.adapt(e.getRightClicked().getLocation());
1153        PlotArea area = location.getPlotArea();
1154        if (area == null) {
1155            return;
1156        }
1157        EntitySpawnListener.testNether(entity);
1158        Plot plot = location.getPlotAbs();
1159        BukkitPlayer pp = BukkitUtil.adapt(e.getPlayer());
1160        if (plot == null) {
1161            if (!PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, MiscInteractFlag.class, true) && !pp.hasPermission(
1162                    Permission.PERMISSION_ADMIN_INTERACT_ROAD
1163            )) {
1164                pp.sendMessage(
1165                        TranslatableCaption.of("permission.no_permission_event"),
1166                        TagResolver.resolver(
1167                                "node",
1168                                Tag.inserting(Permission.PERMISSION_ADMIN_INTERACT_ROAD)
1169                        )
1170                );
1171                e.setCancelled(true);
1172            }
1173        } else {
1174            if (Settings.Done.RESTRICT_BUILDING && DoneFlag.isDone(plot)) {
1175                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_OTHER)) {
1176                    pp.sendMessage(TranslatableCaption.of("done.building_restricted"));
1177                    e.setCancelled(true);
1178                    return;
1179                }
1180            }
1181            if (!plot.hasOwner()) {
1182                if (!pp.hasPermission("plots.admin.interact.unowned")) {
1183                    pp.sendMessage(
1184                            TranslatableCaption.of("permission.no_permission_event"),
1185                            TagResolver.resolver(
1186                                    "node",
1187                                    Tag.inserting(Permission.PERMISSION_ADMIN_INTERACT_UNOWNED)
1188                            )
1189                    );
1190                    e.setCancelled(true);
1191                }
1192            } else {
1193                UUID uuid = pp.getUUID();
1194                if (plot.isAdded(uuid)) {
1195                    return;
1196                }
1197                if (plot.getFlag(MiscInteractFlag.class)) {
1198                    return;
1199                }
1200                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_OTHER)) {
1201                    pp.sendMessage(
1202                            TranslatableCaption.of("permission.no_permission_event"),
1203                            TagResolver.resolver(
1204                                    "node",
1205                                    Tag.inserting(Permission.PERMISSION_ADMIN_INTERACT_OTHER)
1206                            )
1207                    );
1208                    e.setCancelled(true);
1209                    plot.debug(pp.getName() + " could not interact with " + entity.getType()
1210                            + " because misc-interact = false");
1211                }
1212            }
1213        }
1214    }
1215
1216    @EventHandler(priority = EventPriority.LOW)
1217    @SuppressWarnings("deprecation") // Paper deprecation
1218    public void onCancelledInteract(PlayerInteractEvent event) {
1219        if (event.isCancelled() && event.getAction() == Action.RIGHT_CLICK_AIR) {
1220            Player player = event.getPlayer();
1221            BukkitPlayer pp = BukkitUtil.adapt(player);
1222            PlotArea area = pp.getPlotAreaAbs();
1223            if (area == null) {
1224                return;
1225            }
1226            if (event.getAction() == Action.RIGHT_CLICK_AIR) {
1227                Material item = event.getMaterial();
1228                if (item.toString().toLowerCase().endsWith("_egg")) {
1229                    event.setCancelled(true);
1230                    event.setUseItemInHand(Event.Result.DENY);
1231                }
1232            }
1233            ItemStack hand = player.getInventory().getItemInMainHand();
1234            ItemStack offHand = player.getInventory().getItemInOffHand();
1235            Material type = hand.getType();
1236            Material offType = offHand.getType();
1237            if (type == Material.AIR) {
1238                type = offType;
1239            }
1240            if (type.toString().toLowerCase().endsWith("_egg")) {
1241                Block block = player.getTargetBlockExact(5, FluidCollisionMode.SOURCE_ONLY);
1242                if (block != null && block.getType() != Material.AIR) {
1243                    Location location = BukkitUtil.adapt(block.getLocation());
1244                    if (!this.eventDispatcher.checkPlayerBlockEvent(pp, PlayerBlockEventType.SPAWN_MOB, location, null, true)) {
1245                        event.setCancelled(true);
1246                        event.setUseItemInHand(Event.Result.DENY);
1247                    }
1248                }
1249            }
1250        }
1251    }
1252
1253    @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
1254    public void onInteract(PlayerInteractEvent event) {
1255        Player player = event.getPlayer();
1256        BukkitPlayer pp = BukkitUtil.adapt(player);
1257        PlotArea area = pp.getPlotAreaAbs();
1258        if (area == null) {
1259            return;
1260        }
1261        PlayerBlockEventType eventType;
1262        BlockType blocktype1;
1263        Block block = event.getClickedBlock();
1264        if (block == null) {
1265            // We do not care in this case, the player is likely interacting with air ("nothing").
1266            return;
1267        }
1268        Location location = BukkitUtil.adapt(block.getLocation());
1269        Action action = event.getAction();
1270        switch (action) {
1271            case PHYSICAL -> {
1272                eventType = PlayerBlockEventType.TRIGGER_PHYSICAL;
1273                blocktype1 = BukkitAdapter.asBlockType(block.getType());
1274            }
1275
1276            //todo rearrange the right click code. it is all over the place.
1277            case RIGHT_CLICK_BLOCK -> {
1278                Material blockType = block.getType();
1279                eventType = PlayerBlockEventType.INTERACT_BLOCK;
1280                blocktype1 = BukkitAdapter.asBlockType(block.getType());
1281
1282                if (INTERACTABLE_MATERIALS != null
1283                        ? INTERACTABLE_MATERIALS.contains(blockType.name())
1284                        : blockType.isInteractable()) {
1285                    if (!player.isSneaking()) {
1286                        break;
1287                    }
1288                    ItemStack hand = player.getInventory().getItemInMainHand();
1289                    ItemStack offHand = player.getInventory().getItemInOffHand();
1290
1291                    // sneaking players interact with blocks if both hands are empty
1292                    if (hand.getType() == Material.AIR && offHand.getType() == Material.AIR) {
1293                        break;
1294                    }
1295                }
1296
1297                Material type = event.getMaterial();
1298
1299                // in the following, lb needs to have the material of the item in hand i.e. type
1300                switch (type.toString()) {
1301                    case "REDSTONE", "STRING", "PUMPKIN_SEEDS", "MELON_SEEDS", "COCOA_BEANS", "WHEAT_SEEDS", "BEETROOT_SEEDS",
1302                         "SWEET_BERRIES", "GLOW_BERRIES" -> {
1303                        return;
1304                    }
1305                    default -> {
1306                        //eventType = PlayerBlockEventType.PLACE_BLOCK;
1307                        if (type.isBlock()) {
1308                            return;
1309                        }
1310                    }
1311                }
1312                if (PaperLib.isPaper()) {
1313                    if (MaterialTags.SPAWN_EGGS.isTagged(type) || Material.EGG.equals(type)) {
1314                        eventType = PlayerBlockEventType.SPAWN_MOB;
1315                        break;
1316                    }
1317                } else {
1318                    if (type.toString().toLowerCase().endsWith("egg")) {
1319                        eventType = PlayerBlockEventType.SPAWN_MOB;
1320                        break;
1321                    }
1322                }
1323                if (type.isEdible()) {
1324                    //Allow all players to eat while also allowing the block place event to be fired
1325                    return;
1326                }
1327                // Process creature spawning of armor stands & end crystals here if spawned by the player in order to be able to
1328                // reset the player's hand item if spawning needs to be cancelled.
1329                if (type == Material.ARMOR_STAND || type == Material.END_CRYSTAL) {
1330                    Plot plot = location.getOwnedPlotAbs();
1331                    EntityType entityType = type == Material.ARMOR_STAND ? EntityType.ARMOR_STAND : END_CRYSTAL_ENTITY_TYPE;
1332                    if (BukkitEntityUtil.checkEntity(entityType, plot)) {
1333                        event.setCancelled(true);
1334                        break;
1335                    }
1336                }
1337                // Continue with normal place event checks
1338                if (type == Material.ARMOR_STAND) {
1339                    location = BukkitUtil.adapt(block.getRelative(event.getBlockFace()).getLocation());
1340                    eventType = PlayerBlockEventType.PLACE_MISC;
1341                }
1342                if (org.bukkit.Tag.ITEMS_BOATS.isTagged(type) || MINECARTS.contains(type)) {
1343                    eventType = PlayerBlockEventType.PLACE_VEHICLE;
1344                    break;
1345                }
1346                if (type == Material.FIREWORK_ROCKET || type == Material.FIREWORK_STAR) {
1347                    eventType = PlayerBlockEventType.SPAWN_MOB;
1348                    break;
1349                }
1350                if (BOOKS.contains(type)) {
1351                    eventType = PlayerBlockEventType.READ;
1352                    break;
1353                }
1354            }
1355            case LEFT_CLICK_BLOCK -> {
1356                Material blockType = block.getType();
1357
1358                // todo: when the code above is rearranged, it would be great to beautify this as well.
1359                // will code this as a temporary, specific bug fix (for dragon eggs)
1360                if (blockType != Material.DRAGON_EGG) {
1361                    return;
1362                }
1363
1364                eventType = PlayerBlockEventType.INTERACT_BLOCK;
1365                blocktype1 = BukkitAdapter.asBlockType(block.getType());
1366            }
1367            default -> {
1368                return;
1369            }
1370        }
1371        if (this.worldEdit != null && pp.getAttribute("worldedit")) {
1372            if (event.getMaterial() == Material.getMaterial(this.worldEdit.getConfiguration().wandItem)) {
1373                return;
1374            }
1375        }
1376        if (!this.eventDispatcher.checkPlayerBlockEvent(pp, eventType, location, blocktype1, true)) {
1377            event.setCancelled(true);
1378            event.setUseInteractedBlock(Event.Result.DENY);
1379        }
1380    }
1381
1382    // Boats can sometimes be placed on interactable blocks such as levers,
1383    // see PS-175. Armor stands, minecarts and end crystals (the other entities
1384    // supported by this event) don't have this issue.
1385    @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
1386    public void onBoatPlace(EntityPlaceEvent event) {
1387        Player player = event.getPlayer();
1388        if (player == null) {
1389            return;
1390        }
1391        Entity placed = event.getEntity();
1392        if (!(placed instanceof Boat)) {
1393            return;
1394        }
1395        BukkitPlayer pp = BukkitUtil.adapt(event.getPlayer());
1396        PlotArea area = pp.getPlotAreaAbs();
1397        if (area == null) {
1398            return;
1399        }
1400        PlayerBlockEventType eventType = PlayerBlockEventType.PLACE_VEHICLE;
1401        Block block = event.getBlock();
1402        BlockType blockType = BukkitAdapter.asBlockType(block.getType());
1403        Location location = BukkitUtil.adapt(block.getLocation());
1404        if (!PlotSquared.get().getEventDispatcher()
1405                .checkPlayerBlockEvent(pp, eventType, location, blockType, true)) {
1406            event.setCancelled(true);
1407        }
1408    }
1409
1410    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
1411    public void onBucketEmpty(PlayerBucketEmptyEvent event) {
1412        final Block block = event.getBlock();
1413        Location location = BukkitUtil.adapt(block.getLocation());
1414        PlotArea area = location.getPlotArea();
1415        if (area == null) {
1416            return;
1417        }
1418        BukkitPlayer pp = BukkitUtil.adapt(event.getPlayer());
1419        Plot plot = area.getPlot(location);
1420        final List<BlockTypeWrapper> use =
1421                Optional.ofNullable(plot).map(p -> p.getFlag(UseFlag.class)).orElse(area.isRoadFlags() ?
1422                        area.getFlag(UseFlag.class) : Collections.emptyList());
1423        BlockType type = BukkitAdapter.asBlockType(block.getType());
1424        for (final BlockTypeWrapper blockTypeWrapper : use) {
1425            if (blockTypeWrapper.accepts(BlockTypes.AIR) || blockTypeWrapper
1426                    .accepts(type)) {
1427                return;
1428            }
1429        }
1430        if (plot == null) {
1431            if (pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_ROAD)) {
1432                return;
1433            }
1434            pp.sendMessage(
1435                    TranslatableCaption.of("permission.no_permission_event"),
1436                    TagResolver.resolver("node", Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_ROAD))
1437            );
1438            event.setCancelled(true);
1439        } else if (!plot.hasOwner()) {
1440            if (pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_UNOWNED)) {
1441                return;
1442            }
1443            pp.sendMessage(
1444                    TranslatableCaption.of("permission.no_permission_event"),
1445                    TagResolver.resolver(
1446                            "node",
1447                            Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_UNOWNED)
1448                    )
1449            );
1450            event.setCancelled(true);
1451        } else if (!plot.isAdded(pp.getUUID())) {
1452            if (pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_OTHER)) {
1453                return;
1454            }
1455            pp.sendMessage(
1456                    TranslatableCaption.of("permission.no_permission_event"),
1457                    TagResolver.resolver(
1458                            "node",
1459                            Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_OTHER)
1460                    )
1461            );
1462            event.setCancelled(true);
1463        } else if (Settings.Done.RESTRICT_BUILDING && DoneFlag.isDone(plot)) {
1464            if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_OTHER)) {
1465                pp.sendMessage(
1466                        TranslatableCaption.of("done.building_restricted")
1467                );
1468                event.setCancelled(true);
1469            }
1470        }
1471    }
1472
1473    @EventHandler(priority = EventPriority.HIGHEST)
1474    public void onInventoryClose(InventoryCloseEvent event) {
1475        HumanEntity closer = event.getPlayer();
1476        if (!(closer instanceof Player player)) {
1477            return;
1478        }
1479        PlotInventory.removePlotInventoryOpen(BukkitUtil.adapt(player));
1480    }
1481
1482    @EventHandler(priority = EventPriority.MONITOR)
1483    public void onLeave(PlayerQuitEvent event) {
1484        TaskManager.removeFromTeleportQueue(event.getPlayer().getName());
1485        BukkitPlayer pp = BukkitUtil.adapt(event.getPlayer());
1486        pp.unregister();
1487        plotListener.logout(pp.getUUID());
1488    }
1489
1490    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
1491    public void onBucketFill(PlayerBucketFillEvent event) {
1492        Block blockClicked = event.getBlockClicked();
1493        Location location = BukkitUtil.adapt(blockClicked.getLocation());
1494        PlotArea area = location.getPlotArea();
1495        if (area == null) {
1496            return;
1497        }
1498        Player player = event.getPlayer();
1499        BukkitPlayer plotPlayer = BukkitUtil.adapt(player);
1500        Plot plot = area.getPlot(location);
1501        final List<BlockTypeWrapper> use =
1502                Optional.ofNullable(plot).map(p -> p.getFlag(UseFlag.class)).orElse(area.isRoadFlags() ?
1503                        area.getFlag(UseFlag.class) : Collections.emptyList());
1504        BlockType type = BukkitAdapter.asBlockType(blockClicked.getType());
1505        for (final BlockTypeWrapper blockTypeWrapper : use) {
1506            if (blockTypeWrapper.accepts(BlockTypes.AIR) || blockTypeWrapper
1507                    .accepts(type)) {
1508                return;
1509            }
1510        }
1511        if (plot == null) {
1512            if (plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_BUILD_ROAD)) {
1513                return;
1514            }
1515            plotPlayer.sendMessage(
1516                    TranslatableCaption.of("permission.no_permission_event"),
1517                    TagResolver.resolver("node", Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_ROAD))
1518            );
1519            event.setCancelled(true);
1520        } else if (!plot.hasOwner()) {
1521            if (plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_BUILD_UNOWNED)) {
1522                return;
1523            }
1524            plotPlayer.sendMessage(
1525                    TranslatableCaption.of("permission.no_permission_event"),
1526                    TagResolver.resolver(
1527                            "node",
1528                            Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_UNOWNED)
1529                    )
1530            );
1531            event.setCancelled(true);
1532        } else if (!plot.isAdded(plotPlayer.getUUID())) {
1533            if (plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_BUILD_OTHER)) {
1534                return;
1535            }
1536            plotPlayer.sendMessage(
1537                    TranslatableCaption.of("permission.no_permission_event"),
1538                    TagResolver.resolver(
1539                            "node",
1540                            Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_OTHER)
1541                    )
1542            );
1543            event.setCancelled(true);
1544        } else if (Settings.Done.RESTRICT_BUILDING && DoneFlag.isDone(plot)) {
1545            if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_BUILD_OTHER)) {
1546                plotPlayer.sendMessage(
1547                        TranslatableCaption.of("done.building_restricted")
1548                );
1549                event.setCancelled(true);
1550            }
1551        }
1552    }
1553
1554    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
1555    public void onHangingPlace(HangingPlaceEvent event) {
1556        Block block = event.getBlock().getRelative(event.getBlockFace());
1557        Location location = BukkitUtil.adapt(block.getLocation());
1558        PlotArea area = location.getPlotArea();
1559        if (area == null) {
1560            return;
1561        }
1562        Player p = event.getPlayer();
1563        if (p == null) {
1564            event.setCancelled(true);
1565            return;
1566        }
1567        BukkitPlayer pp = BukkitUtil.adapt(p);
1568        Plot plot = area.getPlot(location);
1569        if (plot == null) {
1570            if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_ROAD)) {
1571                pp.sendMessage(
1572                        TranslatableCaption.of("permission.no_permission_event"),
1573                        TagResolver.resolver(
1574                                "node",
1575                                Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_ROAD)
1576                        )
1577                );
1578                event.setCancelled(true);
1579            }
1580        } else {
1581            if (!plot.hasOwner()) {
1582                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_UNOWNED)) {
1583                    pp.sendMessage(
1584                            TranslatableCaption.of("permission.no_permission_event"),
1585                            TagResolver.resolver(
1586                                    "node",
1587                                    Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_UNOWNED)
1588                            )
1589                    );
1590                    event.setCancelled(true);
1591                }
1592                return;
1593            }
1594            if (!plot.isAdded(pp.getUUID())) {
1595                if (!plot.getFlag(HangingPlaceFlag.class)) {
1596                    if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_OTHER)) {
1597                        pp.sendMessage(
1598                                TranslatableCaption.of("permission.no_permission_event"),
1599                                TagResolver.resolver(
1600                                        "node",
1601                                        Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_OTHER)
1602                                )
1603                        );
1604                        event.setCancelled(true);
1605                    }
1606                    return;
1607                }
1608            }
1609            if (BukkitEntityUtil.checkEntity(event.getEntity(), plot)) {
1610                event.setCancelled(true);
1611            }
1612
1613        }
1614    }
1615
1616    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
1617    public void onHangingBreakByEntity(HangingBreakByEntityEvent event) {
1618        Entity remover = event.getRemover();
1619        if (remover instanceof Player p) {
1620            Location location = BukkitUtil.adapt(event.getEntity().getLocation());
1621            PlotArea area = location.getPlotArea();
1622            if (area == null) {
1623                return;
1624            }
1625            BukkitPlayer pp = BukkitUtil.adapt(p);
1626            Plot plot = area.getPlot(location);
1627            if (plot == null) {
1628                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_ROAD)) {
1629                    pp.sendMessage(
1630                            TranslatableCaption.of("permission.no_permission_event"),
1631                            TagResolver.resolver(
1632                                    "node",
1633                                    Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_ROAD)
1634                            )
1635                    );
1636                    event.setCancelled(true);
1637                }
1638            } else if (!plot.hasOwner()) {
1639                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_UNOWNED)) {
1640                    pp.sendMessage(
1641                            TranslatableCaption.of("permission.no_permission_event"),
1642                            TagResolver.resolver(
1643                                    "node",
1644                                    Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_UNOWNED)
1645                            )
1646                    );
1647                    event.setCancelled(true);
1648                }
1649            } else if (!plot.isAdded(pp.getUUID())) {
1650                if (plot.getFlag(HangingBreakFlag.class)) {
1651                    return;
1652                }
1653                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_OTHER)) {
1654                    pp.sendMessage(
1655                            TranslatableCaption.of("permission.no_permission_event"),
1656                            TagResolver.resolver(
1657                                    "node",
1658                                    Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_OTHER)
1659                            )
1660                    );
1661                    event.setCancelled(true);
1662                    plot.debug(p.getName()
1663                            + " could not break hanging entity because hanging-break = false");
1664                }
1665            }
1666        } else if (remover instanceof Projectile p) {
1667            if (p.getShooter() instanceof Player shooter) {
1668                Location location = BukkitUtil.adapt(event.getEntity().getLocation());
1669                PlotArea area = location.getPlotArea();
1670                if (area == null) {
1671                    return;
1672                }
1673                BukkitPlayer player = BukkitUtil.adapt(shooter);
1674                Plot plot = area.getPlot(BukkitUtil.adapt(event.getEntity().getLocation()));
1675                if (plot != null) {
1676                    if (!plot.hasOwner()) {
1677                        if (!player.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_UNOWNED)) {
1678                            player.sendMessage(
1679                                    TranslatableCaption.of("permission.no_permission_event"),
1680                                    TagResolver.resolver(
1681                                            "node",
1682                                            Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_UNOWNED)
1683                                    )
1684                            );
1685                            event.setCancelled(true);
1686                        }
1687                    } else if (!plot.isAdded(player.getUUID())) {
1688                        if (!plot.getFlag(HangingBreakFlag.class)) {
1689                            if (!player.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_OTHER)) {
1690                                player.sendMessage(
1691                                        TranslatableCaption.of("permission.no_permission_event"),
1692                                        TagResolver.resolver(
1693                                                "node",
1694                                                Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_OTHER)
1695                                        )
1696                                );
1697                                event.setCancelled(true);
1698                                plot.debug(player.getName()
1699                                        + " could not break hanging entity because hanging-break = false");
1700                            }
1701                        }
1702                    }
1703                }
1704            }
1705        } else {
1706            event.setCancelled(true);
1707        }
1708    }
1709
1710    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
1711    public void onPlayerInteractEntity(PlayerInteractEntityEvent event) {
1712        if (event.getRightClicked().getType() == EntityType.UNKNOWN) {
1713            return;
1714        }
1715        Location location = BukkitUtil.adapt(event.getRightClicked().getLocation());
1716        PlotArea area = location.getPlotArea();
1717        if (area == null) {
1718            return;
1719        }
1720        Player p = event.getPlayer();
1721        BukkitPlayer pp = BukkitUtil.adapt(p);
1722        Plot plot = area.getPlot(location);
1723        if (plot == null && !area.isRoadFlags()) {
1724            if (!pp.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_ROAD)) {
1725                pp.sendMessage(
1726                        TranslatableCaption.of("permission.no_permission_event"),
1727                        TagResolver.resolver(
1728                                "node",
1729                                Tag.inserting(Permission.PERMISSION_ADMIN_INTERACT_ROAD)
1730                        )
1731                );
1732                event.setCancelled(true);
1733            }
1734        } else if (plot != null && !plot.hasOwner()) {
1735            if (!pp.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_UNOWNED)) {
1736                pp.sendMessage(
1737                        TranslatableCaption.of("permission.no_permission_event"),
1738                        TagResolver.resolver(
1739                                "node",
1740                                Tag.inserting(Permission.PERMISSION_ADMIN_INTERACT_UNOWNED)
1741                        )
1742                );
1743                event.setCancelled(true);
1744            }
1745        } else if ((plot != null && !plot.isAdded(pp.getUUID())) || (plot == null && area
1746                .isRoadFlags())) {
1747            final Entity entity = event.getRightClicked();
1748            final com.sk89q.worldedit.world.entity.EntityType entityType =
1749                    BukkitAdapter.adapt(entity.getType());
1750
1751            FlagContainer flagContainer;
1752            if (plot == null) {
1753                flagContainer = area.getRoadFlagContainer();
1754            } else {
1755                flagContainer = plot.getFlagContainer();
1756            }
1757
1758            if (EntityCategories.HOSTILE.contains(entityType) && flagContainer
1759                    .getFlag(HostileInteractFlag.class).getValue()) {
1760                return;
1761            }
1762
1763            if (EntityCategories.ANIMAL.contains(entityType) && flagContainer
1764                    .getFlag(AnimalInteractFlag.class).getValue()) {
1765                return;
1766            }
1767
1768            // This actually makes use of the interface, so we don't use the
1769            // category
1770            if (entity instanceof Tameable && ((Tameable) entity).isTamed() && flagContainer
1771                    .getFlag(TamedInteractFlag.class).getValue()) {
1772                return;
1773            }
1774
1775            if (EntityCategories.VEHICLE.contains(entityType) && flagContainer
1776                    .getFlag(VehicleUseFlag.class).getValue()) {
1777                return;
1778            }
1779
1780            if (EntityCategories.PLAYER.contains(entityType) && flagContainer
1781                    .getFlag(PlayerInteractFlag.class).getValue()) {
1782                return;
1783            }
1784
1785            if (EntityCategories.INTERACTION.contains(entityType) && flagContainer
1786                    .getFlag(InteractionInteractFlag.class).getValue()) {
1787                return;
1788            }
1789
1790            if (EntityCategories.VILLAGER.contains(entityType) && flagContainer
1791                    .getFlag(VillagerInteractFlag.class).getValue()) {
1792                return;
1793            }
1794
1795            if ((EntityCategories.HANGING.contains(entityType) || EntityCategories.OTHER
1796                    .contains(entityType)) && flagContainer.getFlag(MiscInteractFlag.class)
1797                    .getValue()) {
1798                return;
1799            }
1800
1801            if (!pp.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_OTHER)) {
1802                pp.sendMessage(
1803                        TranslatableCaption.of("permission.no_permission_event"),
1804                        TagResolver.resolver(
1805                                "node",
1806                                Tag.inserting(Permission.PERMISSION_ADMIN_INTERACT_OTHER)
1807                        )
1808                );
1809                event.setCancelled(true);
1810            }
1811        }
1812    }
1813
1814    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
1815    public void onVehicleDestroy(VehicleDestroyEvent event) {
1816        Location location = BukkitUtil.adapt(event.getVehicle().getLocation());
1817        PlotArea area = location.getPlotArea();
1818        if (area == null) {
1819            return;
1820        }
1821        Entity attacker = event.getAttacker();
1822        if (attacker instanceof Player p) {
1823            BukkitPlayer pp = BukkitUtil.adapt(p);
1824            Plot plot = area.getPlot(location);
1825            if (plot == null) {
1826                if (!PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, VehicleBreakFlag.class, true) && !pp.hasPermission(
1827                        Permission.PERMISSION_ADMIN_DESTROY_VEHICLE_ROAD
1828                )) {
1829                    pp.sendMessage(
1830                            TranslatableCaption.of("permission.no_permission_event"),
1831                            TagResolver.resolver(
1832                                    "node",
1833                                    Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_VEHICLE_ROAD)
1834                            )
1835                    );
1836                    event.setCancelled(true);
1837                }
1838            } else {
1839                if (!plot.hasOwner()) {
1840                    if (!pp.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_VEHICLE_UNOWNED)) {
1841                        pp.sendMessage(
1842                                TranslatableCaption.of("permission.no_permission_event"),
1843                                TagResolver.resolver(
1844                                        "node",
1845                                        Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_VEHICLE_UNOWNED)
1846                                )
1847                        );
1848                        event.setCancelled(true);
1849                        return;
1850                    }
1851                    return;
1852                }
1853                if (!plot.isAdded(pp.getUUID())) {
1854                    if (plot.getFlag(VehicleBreakFlag.class)) {
1855                        return;
1856                    }
1857                    if (!pp.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_VEHICLE_OTHER)) {
1858                        pp.sendMessage(
1859                                TranslatableCaption.of("permission.no_permission_event"),
1860                                TagResolver.resolver(
1861                                        "node",
1862                                        Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_VEHICLE_OTHER)
1863                                )
1864                        );
1865                        event.setCancelled(true);
1866                        plot.debug(pp.getName()
1867                                + " could not break vehicle because vehicle-break = false");
1868                    }
1869                }
1870            }
1871        }
1872    }
1873
1874    @EventHandler
1875    public void onItemDrop(PlayerDropItemEvent event) {
1876        Player player = event.getPlayer();
1877        BukkitPlayer pp = BukkitUtil.adapt(player);
1878        Location location = pp.getLocation();
1879        PlotArea area = location.getPlotArea();
1880        if (area == null) {
1881            return;
1882        }
1883        Plot plot = location.getOwnedPlot();
1884        if (plot == null) {
1885            if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, ItemDropFlag.class, false)) {
1886                event.setCancelled(true);
1887            }
1888            return;
1889        }
1890        UUID uuid = pp.getUUID();
1891        if (!plot.isAdded(uuid)) {
1892            if (!plot.getFlag(ItemDropFlag.class)) {
1893                plot.debug(player.getName() + " could not drop item because of item-drop = false");
1894                event.setCancelled(true);
1895            }
1896        }
1897    }
1898
1899    @EventHandler
1900    public void onItemPickup(EntityPickupItemEvent event) {
1901        LivingEntity ent = event.getEntity();
1902        if (ent instanceof Player player) {
1903            BukkitPlayer pp = BukkitUtil.adapt(player);
1904            Location location = pp.getLocation();
1905            PlotArea area = location.getPlotArea();
1906            if (area == null) {
1907                return;
1908            }
1909            Plot plot = location.getOwnedPlot();
1910            if (plot == null) {
1911                if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, DropProtectionFlag.class, true)) {
1912                    event.setCancelled(true);
1913                }
1914                return;
1915            }
1916            UUID uuid = pp.getUUID();
1917            if (!plot.isAdded(uuid) && plot.getFlag(DropProtectionFlag.class)) {
1918                plot.debug(player.getName() + " could not pick up item because of drop-protection = true");
1919                event.setCancelled(true);
1920            }
1921        }
1922    }
1923
1924    @EventHandler
1925    public void onDeath(final PlayerDeathEvent event) {
1926        Location location = BukkitUtil.adapt(event.getEntity().getLocation());
1927        PlotArea area = location.getPlotArea();
1928        if (area == null) {
1929            return;
1930        }
1931        Plot plot = location.getOwnedPlot();
1932        if (plot == null) {
1933            if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, KeepInventoryFlag.class, true)) {
1934                event.setCancelled(true);
1935            }
1936            return;
1937        }
1938        if (plot.getFlag(KeepInventoryFlag.class)) {
1939            plot.debug(event.getEntity().getName() + " kept their inventory because of keep-inventory = true");
1940            event.getDrops().clear();
1941            event.setKeepInventory(true);
1942        }
1943    }
1944
1945    @SuppressWarnings("deprecation") // #getLocate is needed for Spigot compatibility
1946    @EventHandler
1947    public void onLocaleChange(final PlayerLocaleChangeEvent event) {
1948        // The event is fired before the player is deemed online upon login
1949        if (!event.getPlayer().isOnline()) {
1950            return;
1951        }
1952        BukkitPlayer player = BukkitUtil.adapt(event.getPlayer());
1953        // we're stripping the country code as we don't want to differ between countries
1954        player.setLocale(Locale.forLanguageTag(event.getLocale().substring(0, 2)));
1955    }
1956
1957    @EventHandler
1958    public void onPortalEnter(PlayerPortalEvent event) {
1959        Location location = BukkitUtil.adapt(event.getPlayer().getLocation());
1960        PlotArea area = location.getPlotArea();
1961        if (area == null) {
1962            return;
1963        }
1964        Plot plot = location.getOwnedPlot();
1965        if (plot == null) {
1966            if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, DenyPortalTravelFlag.class, true)) {
1967                event.setCancelled(true);
1968            }
1969            return;
1970        }
1971        if (plot.getFlag(DenyPortalTravelFlag.class)) {
1972            plot.debug(event.getPlayer().getName() + " did not travel thru a portal because of deny-portal-travel = true");
1973            event.setCancelled(true);
1974        }
1975    }
1976
1977    @EventHandler
1978    public void onPortalCreation(PortalCreateEvent event) {
1979        String world = event.getWorld().getName();
1980        if (PlotSquared.get().getPlotAreaManager().getPlotAreasSet(world).size() == 0) {
1981            return;
1982        }
1983        BukkitPlayer pp = (event.getEntity() instanceof Player player) ? BukkitUtil.adapt(player) : null;
1984        int minX = Integer.MAX_VALUE;
1985        int maxX = Integer.MIN_VALUE;
1986        int minZ = Integer.MAX_VALUE;
1987        int maxZ = Integer.MIN_VALUE;
1988        for (BlockState state : event.getBlocks()) {
1989            minX = Math.min(state.getX(), minX);
1990            maxX = Math.max(state.getX(), maxX);
1991            minZ = Math.min(state.getZ(), minZ);
1992            maxZ = Math.max(state.getZ(), maxZ);
1993        }
1994        int y = event.getBlocks().get(0).getY(); // Don't need to worry about this too much
1995        for (Location location : List.of( // We don't care about duplicate locations
1996                Location.at(world, minX, y, minZ),
1997                Location.at(world, minX, y, maxZ),
1998                Location.at(world, maxX, y, minZ),
1999                Location.at(world, maxX, y, maxZ)
2000        )) {
2001            PlotArea area = location.getPlotArea();
2002            if (area == null) {
2003                continue;
2004            }
2005            if (area.notifyIfOutsideBuildArea(pp, location.getY())) {
2006                event.setCancelled(true);
2007                return;
2008            }
2009            Plot plot = location.getOwnedPlot();
2010            if (plot == null) {
2011                if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, DenyPortalsFlag.class, true)) {
2012                    event.setCancelled(true);
2013                    return;
2014                }
2015                continue;
2016            }
2017            if (plot.getFlag(DenyPortalsFlag.class)) {
2018                StringBuilder builder = new StringBuilder();
2019                if (event.getEntity() != null) {
2020                    builder.append(event.getEntity().getName()).append(" did not create a portal");
2021                } else {
2022                    builder.append("Portal creation cancelled");
2023                }
2024                plot.debug(builder.append(" because of deny-portals = true").toString());
2025                event.setCancelled(true);
2026                return;
2027            }
2028        }
2029    }
2030
2031    @EventHandler
2032    public void onPlayerTakeLecternBook(PlayerTakeLecternBookEvent event) {
2033        Player player = event.getPlayer();
2034        BukkitPlayer pp = BukkitUtil.adapt(player);
2035        Location location = pp.getLocation();
2036        PlotArea area = location.getPlotArea();
2037        if (area == null) {
2038            return;
2039        }
2040        Plot plot = location.getOwnedPlot();
2041        if (plot == null) {
2042            if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, LecternReadBookFlag.class, true)) {
2043                event.setCancelled(true);
2044            }
2045            return;
2046        }
2047        if (!plot.isAdded(pp.getUUID())) {
2048            if (plot.getFlag(LecternReadBookFlag.class)) {
2049                plot.debug(event.getPlayer().getName() + " could not take the book because of lectern-read-book = true");
2050                event.setCancelled(true);
2051            }
2052        }
2053    }
2054
2055}