diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitPlatform.java b/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitPlatform.java index f249c1b5f..b19130c1c 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitPlatform.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitPlatform.java @@ -41,6 +41,7 @@ import com.plotsquared.bukkit.listener.PlayerEventListener; import com.plotsquared.bukkit.listener.ProjectileEventListener; import com.plotsquared.bukkit.listener.ServerListener; import com.plotsquared.bukkit.listener.SingleWorldListener; +import com.plotsquared.bukkit.listener.SpigotListener; import com.plotsquared.bukkit.listener.WorldEvents; import com.plotsquared.bukkit.placeholder.PAPIPlaceholders; import com.plotsquared.bukkit.placeholder.PlaceholderFormatter; @@ -360,6 +361,8 @@ public final class BukkitPlatform extends JavaPlugin implements Listener, PlotPl } else { getServer().getPluginManager().registerEvents(injector().getInstance(PaperListener.class), this); } + } else { + getServer().getPluginManager().registerEvents(injector().getInstance(SpigotListener.class), this); } this.plotListener.startRunnable(); } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PaperListener.java b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PaperListener.java index 9e3d9ba8e..54cc21a41 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PaperListener.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PaperListener.java @@ -18,6 +18,7 @@ */ package com.plotsquared.bukkit.listener; +import com.destroystokyo.paper.event.block.BeaconEffectEvent; import com.destroystokyo.paper.event.entity.EntityPathfindEvent; import com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent; import com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent; @@ -36,8 +37,11 @@ import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.PlotArea; +import com.plotsquared.core.plot.flag.FlagContainer; +import com.plotsquared.core.plot.flag.implementations.BeaconEffectsFlag; import com.plotsquared.core.plot.flag.implementations.DoneFlag; import com.plotsquared.core.plot.flag.implementations.ProjectilesFlag; +import com.plotsquared.core.plot.flag.types.BooleanFlag; import com.plotsquared.core.plot.world.PlotAreaManager; import com.plotsquared.core.util.Permissions; import net.kyori.adventure.text.minimessage.Template; @@ -400,4 +404,50 @@ public class PaperListener implements Listener { } } + @EventHandler(ignoreCancelled = true) + public void onBeaconEffect(final BeaconEffectEvent event) { + Block block = event.getBlock(); + Location beaconLocation = BukkitUtil.adapt(block.getLocation()); + Plot beaconPlot = beaconLocation.getPlot(); + + PlotArea area = beaconLocation.getPlotArea(); + if (area == null) { + return; + } + + Player player = event.getPlayer(); + Location playerLocation = BukkitUtil.adapt(player.getLocation()); + + PlotPlayer plotPlayer = BukkitUtil.adapt(player); + Plot playerStandingPlot = playerLocation.getPlot(); + if (playerStandingPlot == null) { + FlagContainer container = area.getRoadFlagContainer(); + if (!getBooleanFlagValue(container, BeaconEffectsFlag.class, true) || + (beaconPlot != null && Settings.Enabled_Components.DISABLE_BEACON_EFFECT_OVERFLOW)) { + event.setCancelled(true); + } + return; + } + + FlagContainer container = playerStandingPlot.getFlagContainer(); + boolean plotBeaconEffects = getBooleanFlagValue(container, BeaconEffectsFlag.class, true); + if (playerStandingPlot.equals(beaconPlot)) { + if (!plotBeaconEffects) { + event.setCancelled(true); + } + return; + } + + if (!plotBeaconEffects || Settings.Enabled_Components.DISABLE_BEACON_EFFECT_OVERFLOW) { + event.setCancelled(true); + } + } + + private boolean getBooleanFlagValue(@NonNull FlagContainer container, + @NonNull Class> flagClass, + boolean defaultValue) { + BooleanFlag flag = container.getFlag(flagClass); + return flag == null ? defaultValue : flag.getValue(); + } + } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PlayerEventListener.java b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PlayerEventListener.java index 6eddc83e1..1af53b6ad 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PlayerEventListener.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PlayerEventListener.java @@ -106,6 +106,7 @@ import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.entity.EntityPickupItemEvent; import org.bukkit.event.entity.EntityPlaceEvent; +import org.bukkit.event.entity.EntityPotionEffectEvent; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.hanging.HangingBreakByEntityEvent; import org.bukkit.event.hanging.HangingPlaceEvent; @@ -142,6 +143,7 @@ import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.metadata.MetadataValue; import org.bukkit.plugin.Plugin; +import org.bukkit.potion.PotionEffect; import org.bukkit.util.Vector; import org.checkerframework.checker.nullness.qual.NonNull; @@ -204,6 +206,29 @@ public class PlayerEventListener implements Listener { this.plotListener = plotListener; } + @EventHandler(ignoreCancelled = true) + public void onEffect(@NonNull EntityPotionEffectEvent event) { + if (Settings.Enabled_Components.DISABLE_BEACON_EFFECT_OVERFLOW || + event.getCause() != EntityPotionEffectEvent.Cause.BEACON || + !(event.getEntity() instanceof Player player)) { + return; + } + + UUID uuid = player.getUniqueId(); + PotionEffect effect = event.getNewEffect(); + if (effect == null) { + PotionEffect oldEffect = event.getOldEffect(); + if (oldEffect != null) { + String name = oldEffect.getType().getName(); + plotListener.addEffect(uuid, name, -1); + } + } else { + long expiresAt = System.currentTimeMillis() + effect.getDuration() * 50L; //Convert ticks to milliseconds + String name = effect.getType().getName(); + plotListener.addEffect(uuid, name, expiresAt); + } + } + @EventHandler public void onVehicleEntityCollision(VehicleEntityCollisionEvent e) { if (e.getVehicle().getType() == EntityType.BOAT) { diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/SpigotListener.java b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/SpigotListener.java new file mode 100644 index 000000000..5d91bc513 --- /dev/null +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/SpigotListener.java @@ -0,0 +1,57 @@ +/* + * PlotSquared, a land and world management plugin for Minecraft. + * Copyright (C) IntellectualSites + * Copyright (C) IntellectualSites team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.plotsquared.bukkit.listener; + +import com.plotsquared.bukkit.util.BukkitUtil; +import com.plotsquared.core.location.Location; +import com.plotsquared.core.plot.Plot; +import com.plotsquared.core.plot.flag.FlagContainer; +import com.plotsquared.core.plot.flag.implementations.BeaconEffectsFlag; +import org.bukkit.entity.Entity; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityPotionEffectEvent; +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Fallback listener for paper events on spigot + */ +public class SpigotListener implements Listener { + + @EventHandler(ignoreCancelled = true) + public void onEffect(@NonNull EntityPotionEffectEvent event) { + if (event.getCause() != EntityPotionEffectEvent.Cause.BEACON) { + return; + } + + Entity entity = event.getEntity(); + Location location = BukkitUtil.adapt(entity.getLocation()); + Plot plot = location.getPlot(); + if (plot == null) { + return; + } + + FlagContainer container = plot.getFlagContainer(); + BeaconEffectsFlag effectsEnabled = container.getFlag(BeaconEffectsFlag.class); + if (effectsEnabled != null && !effectsEnabled.getValue()) { + event.setCancelled(true); + } + } + +} diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/player/BukkitPlayer.java b/Bukkit/src/main/java/com/plotsquared/bukkit/player/BukkitPlayer.java index 6dcaa9dd7..1bcd3750f 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/player/BukkitPlayer.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/player/BukkitPlayer.java @@ -47,13 +47,13 @@ import org.bukkit.event.EventException; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.permissions.PermissionAttachmentInfo; import org.bukkit.plugin.RegisteredListener; +import org.bukkit.potion.PotionEffectType; import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.nullness.qual.NonNull; import java.util.Arrays; import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; import static com.sk89q.worldedit.world.gamemode.GameModes.ADVENTURE; import static com.sk89q.worldedit.world.gamemode.GameModes.CREATIVE; @@ -348,6 +348,14 @@ public class BukkitPlayer extends PlotPlayer { return BukkitUtil.BUKKIT_AUDIENCES.player(this.player); } + @Override + public void removeEffect(@NonNull String name) { + PotionEffectType type = PotionEffectType.getByName(name); + if (type != null) { + player.removePotionEffect(type); + } + } + @Override public boolean canSee(final PlotPlayer other) { if (other instanceof ConsolePlayer) { diff --git a/Core/src/main/java/com/plotsquared/core/configuration/Settings.java b/Core/src/main/java/com/plotsquared/core/configuration/Settings.java index 9276edca1..4e5ac9bc5 100644 --- a/Core/src/main/java/com/plotsquared/core/configuration/Settings.java +++ b/Core/src/main/java/com/plotsquared/core/configuration/Settings.java @@ -808,6 +808,8 @@ public class Settings extends Config { ); @Comment("Whether PlotSquared should hook into MvDWPlaceholderAPI or not") public static boolean USE_MVDWAPI = true; + @Comment("Prevent cross plot beacon effects") + public static boolean DISABLE_BEACON_EFFECT_OVERFLOW = true; } diff --git a/Core/src/main/java/com/plotsquared/core/listener/PlotListener.java b/Core/src/main/java/com/plotsquared/core/listener/PlotListener.java index a3a9f49e6..548e5f02e 100644 --- a/Core/src/main/java/com/plotsquared/core/listener/PlotListener.java +++ b/Core/src/main/java/com/plotsquared/core/listener/PlotListener.java @@ -67,10 +67,13 @@ import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.item.ItemTypes; import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.Template; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; @@ -82,6 +85,7 @@ public class PlotListener { private final HashMap feedRunnable = new HashMap<>(); private final HashMap healRunnable = new HashMap<>(); + private final Map> playerEffects = new HashMap<>(); private final EventDispatcher eventDispatcher; @@ -131,6 +135,17 @@ public class PlotListener { } } } + + if (!playerEffects.isEmpty()) { + long currentTime = System.currentTimeMillis(); + for (Iterator>> iterator = + playerEffects.entrySet().iterator(); iterator.hasNext(); ) { + Map.Entry> entry = iterator.next(); + List effects = entry.getValue(); + effects.removeIf(effect -> currentTime > effect.expiresAt); + if (effects.isEmpty()) iterator.remove(); + } + } }, TaskTime.seconds(1L)); } @@ -360,6 +375,17 @@ public class PlotListener { try (final MetaDataAccess lastPlot = player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) { final Plot previous = lastPlot.remove(); this.eventDispatcher.callLeave(player, plot); + + List effects = playerEffects.remove(player.getUUID()); + if (effects != null) { + long currentTime = System.currentTimeMillis(); + effects.forEach(effect -> { + if (currentTime <= effect.expiresAt) { + player.removeEffect(effect.name); + } + }); + } + if (plot.hasOwner()) { PlotArea pw = plot.getArea(); if (pw == null) { @@ -468,6 +494,23 @@ public class PlotListener { public void logout(UUID uuid) { feedRunnable.remove(uuid); healRunnable.remove(uuid); + playerEffects.remove(uuid); + } + + /** + * Marks an effect as a status effect that will be removed on leaving a plot + * @param uuid The uuid of the player the effect belongs to + * @param name The name of the status effect + * @param expiresAt The time when the effect expires + * @since TODO + */ + public void addEffect(@NonNull UUID uuid, @NonNull String name, long expiresAt) { + List effects = playerEffects.getOrDefault(uuid, new ArrayList<>()); + effects.removeIf(effect -> effect.name.equals(name)); + if (expiresAt != -1) { + effects.add(new StatusEffect(name, expiresAt)); + } + playerEffects.put(uuid, effects); } private static class Interval { @@ -485,4 +528,13 @@ public class PlotListener { } + private record StatusEffect(@NonNull String name, long expiresAt) { + + private StatusEffect(@NonNull String name, long expiresAt) { + this.name = name; + this.expiresAt = expiresAt; + } + + } + } diff --git a/Core/src/main/java/com/plotsquared/core/player/ConsolePlayer.java b/Core/src/main/java/com/plotsquared/core/player/ConsolePlayer.java index fcf2bcfd2..dde0017cc 100644 --- a/Core/src/main/java/com/plotsquared/core/player/ConsolePlayer.java +++ b/Core/src/main/java/com/plotsquared/core/player/ConsolePlayer.java @@ -246,6 +246,10 @@ public class ConsolePlayer extends PlotPlayer { return PlotSquared.platform().consoleAudience(); } + @Override + public void removeEffect(@NonNull String name) { + } + @Override public boolean canSee(final PlotPlayer other) { return true; diff --git a/Core/src/main/java/com/plotsquared/core/player/PlotPlayer.java b/Core/src/main/java/com/plotsquared/core/player/PlotPlayer.java index 80d2fc769..4b2f90708 100644 --- a/Core/src/main/java/com/plotsquared/core/player/PlotPlayer.java +++ b/Core/src/main/java/com/plotsquared/core/player/PlotPlayer.java @@ -1016,6 +1016,14 @@ public abstract class PlotPlayer

implements CommandCaller, OfflinePlotPlayer, return this.lockRepository; } + /** + * Removes any effects present of the given type. + * + * @param name the name of the type to remove + * @since TODO + */ + public abstract void removeEffect(@NonNull String name); + @FunctionalInterface public interface PlotPlayerConverter { diff --git a/Core/src/main/java/com/plotsquared/core/plot/flag/GlobalFlagContainer.java b/Core/src/main/java/com/plotsquared/core/plot/flag/GlobalFlagContainer.java index 5a9d05d2a..734c31396 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/flag/GlobalFlagContainer.java +++ b/Core/src/main/java/com/plotsquared/core/plot/flag/GlobalFlagContainer.java @@ -23,6 +23,7 @@ import com.plotsquared.core.plot.flag.implementations.AnalysisFlag; import com.plotsquared.core.plot.flag.implementations.AnimalAttackFlag; import com.plotsquared.core.plot.flag.implementations.AnimalCapFlag; import com.plotsquared.core.plot.flag.implementations.AnimalInteractFlag; +import com.plotsquared.core.plot.flag.implementations.BeaconEffectsFlag; import com.plotsquared.core.plot.flag.implementations.BlockBurnFlag; import com.plotsquared.core.plot.flag.implementations.BlockIgnitionFlag; import com.plotsquared.core.plot.flag.implementations.BlockedCmdsFlag; @@ -137,6 +138,7 @@ public final class GlobalFlagContainer extends FlagContainer { this.addFlag(AnimalAttackFlag.ANIMAL_ATTACK_FALSE); this.addFlag(AnimalInteractFlag.ANIMAL_INTERACT_FALSE); this.addFlag(BlockBurnFlag.BLOCK_BURN_FALSE); + this.addFlag(BeaconEffectsFlag.BEACON_EFFECT_TRUE); this.addFlag(BlockIgnitionFlag.BLOCK_IGNITION_TRUE); this.addFlag(ChatFlag.CHAT_FLAG_TRUE); this.addFlag(CopperOxideFlag.COPPER_OXIDE_FALSE); diff --git a/Core/src/main/java/com/plotsquared/core/plot/flag/implementations/BeaconEffectsFlag.java b/Core/src/main/java/com/plotsquared/core/plot/flag/implementations/BeaconEffectsFlag.java new file mode 100644 index 000000000..6876f3965 --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/plot/flag/implementations/BeaconEffectsFlag.java @@ -0,0 +1,39 @@ +/* + * PlotSquared, a land and world management plugin for Minecraft. + * Copyright (C) IntellectualSites + * Copyright (C) IntellectualSites team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.plotsquared.core.plot.flag.implementations; + +import com.plotsquared.core.configuration.caption.TranslatableCaption; +import com.plotsquared.core.plot.flag.types.BooleanFlag; +import org.checkerframework.checker.nullness.qual.NonNull; + +public class BeaconEffectsFlag extends BooleanFlag { + + public static final BeaconEffectsFlag BEACON_EFFECT_TRUE = new BeaconEffectsFlag(true); + public static final BeaconEffectsFlag BEACON_EFFECT_FALSE = new BeaconEffectsFlag(false); + + private BeaconEffectsFlag(boolean value){ + super(value, TranslatableCaption.of("flags.flag_description_beacon_effect")); + } + + @Override + protected BeaconEffectsFlag flagOf(@NonNull final Boolean value) { + return value ? BEACON_EFFECT_TRUE : BEACON_EFFECT_FALSE; + } + +} diff --git a/Core/src/main/resources/lang/messages_en.json b/Core/src/main/resources/lang/messages_en.json index 397bb0933..a83554064 100644 --- a/Core/src/main/resources/lang/messages_en.json +++ b/Core/src/main/resources/lang/messages_en.json @@ -618,6 +618,7 @@ "flags.flag_description_prevent_creative_copy": "Prevents people from copying item NBT data in the plot unless they're added as members.", "flags.flag_description_leaf_decay": "Set to `false` to prevent leaves from decaying.", "flags.flag_description_projectiles": "Prevents guests from shooting projectiles on the plot when set to false.", + "flags.flag_description_beacon_effect": "Enables beacon effects on the plot.", "flags.flag_error_boolean": "Flag value must be a boolean (true | false).", "flags.flag_error_enum": "Must be one of: ", "flags.flag_error_integer": "Flag value must be a whole positive number.",