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