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