From f88ea94bfece9eb56c9438b46238c88b2c1aaddc Mon Sep 17 00:00:00 2001 From: RedstoneFuture Date: Mon, 13 May 2024 21:20:21 +0200 Subject: [PATCH] Performance Improvement: high-requency listener (#4402) * Adding new 'high-frequency-listener' setting * Moving high-frequency event listener in new class * Small typo --- .../plotsquared/bukkit/BukkitPlatform.java | 4 + .../bukkit/listener/BlockEventListener.java | 121 ----------- .../listener/HighFreqBlockEventListener.java | 201 ++++++++++++++++++ .../core/configuration/Settings.java | 5 + 4 files changed, 210 insertions(+), 121 deletions(-) create mode 100644 Bukkit/src/main/java/com/plotsquared/bukkit/listener/HighFreqBlockEventListener.java diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitPlatform.java b/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitPlatform.java index b169ddb4f..faaa67e7d 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitPlatform.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitPlatform.java @@ -34,6 +34,7 @@ import com.plotsquared.bukkit.listener.BlockEventListener117; import com.plotsquared.bukkit.listener.ChunkListener; import com.plotsquared.bukkit.listener.EntityEventListener; import com.plotsquared.bukkit.listener.EntitySpawnListener; +import com.plotsquared.bukkit.listener.HighFreqBlockEventListener; import com.plotsquared.bukkit.listener.PaperListener; import com.plotsquared.bukkit.listener.PlayerEventListener; import com.plotsquared.bukkit.listener.PlayerEventListener1201; @@ -362,6 +363,9 @@ public final class BukkitPlatform extends JavaPlugin implements Listener, PlotPl getServer().getPluginManager().registerEvents(injector().getInstance(PlayerEventListener1201.class), this); } getServer().getPluginManager().registerEvents(injector().getInstance(BlockEventListener.class), this); + if (Settings.HIGH_FREQUENCY_LISTENER) { + getServer().getPluginManager().registerEvents(injector().getInstance(HighFreqBlockEventListener.class), this); + } if (serverVersion()[1] >= 17) { getServer().getPluginManager().registerEvents(injector().getInstance(BlockEventListener117.class), this); } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/BlockEventListener.java b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/BlockEventListener.java index 6c65d3721..af3e7cfa8 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/BlockEventListener.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/BlockEventListener.java @@ -24,7 +24,6 @@ import com.plotsquared.bukkit.util.BukkitUtil; import com.plotsquared.core.PlotSquared; import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.configuration.caption.TranslatableCaption; -import com.plotsquared.core.database.DBFunc; import com.plotsquared.core.location.Location; import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.player.PlotPlayer; @@ -48,7 +47,6 @@ import com.plotsquared.core.plot.flag.implementations.LeafDecayFlag; import com.plotsquared.core.plot.flag.implementations.LiquidFlowFlag; import com.plotsquared.core.plot.flag.implementations.MycelGrowFlag; import com.plotsquared.core.plot.flag.implementations.PlaceFlag; -import com.plotsquared.core.plot.flag.implementations.RedstoneFlag; import com.plotsquared.core.plot.flag.implementations.SnowFormFlag; import com.plotsquared.core.plot.flag.implementations.SnowMeltFlag; import com.plotsquared.core.plot.flag.implementations.SoilDryFlag; @@ -92,11 +90,9 @@ import org.bukkit.event.block.BlockFromToEvent; import org.bukkit.event.block.BlockGrowEvent; import org.bukkit.event.block.BlockIgniteEvent; import org.bukkit.event.block.BlockMultiPlaceEvent; -import org.bukkit.event.block.BlockPhysicsEvent; import org.bukkit.event.block.BlockPistonExtendEvent; import org.bukkit.event.block.BlockPistonRetractEvent; import org.bukkit.event.block.BlockPlaceEvent; -import org.bukkit.event.block.BlockRedstoneEvent; import org.bukkit.event.block.BlockSpreadEvent; import org.bukkit.event.block.CauldronLevelChangeEvent; import org.bukkit.event.block.EntityBlockFormEvent; @@ -112,7 +108,6 @@ import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Set; -import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -123,14 +118,6 @@ import static org.bukkit.Tag.WALL_CORALS; @SuppressWarnings("unused") public class BlockEventListener implements Listener { - private static final Set PISTONS = Set.of( - Material.PISTON, - Material.STICKY_PISTON - ); - private static final Set PHYSICS_BLOCKS = Set.of( - Material.TURTLE_EGG, - Material.TURTLE_SPAWN_EGG - ); private static final Set SNOW = Stream.of(Material.values()) // needed as Tag.SNOW isn't present in 1.16.5 .filter(material -> material.name().contains("SNOW")) .filter(Material::isBlock) @@ -164,114 +151,6 @@ public class BlockEventListener implements Listener { }, TaskTime.ticks(3L)); } - @EventHandler - public void onRedstoneEvent(BlockRedstoneEvent event) { - Block block = event.getBlock(); - Location location = BukkitUtil.adapt(block.getLocation()); - PlotArea area = location.getPlotArea(); - if (area == null) { - return; - } - Plot plot = location.getOwnedPlot(); - if (plot == null) { - if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, RedstoneFlag.class, false)) { - event.setNewCurrent(0); - } - return; - } - if (!plot.getFlag(RedstoneFlag.class)) { - event.setNewCurrent(0); - plot.debug("Redstone event was cancelled because redstone = false"); - return; - } - if (Settings.Redstone.DISABLE_OFFLINE) { - boolean disable = false; - if (!DBFunc.SERVER.equals(plot.getOwner())) { - if (plot.isMerged()) { - disable = true; - for (UUID owner : plot.getOwners()) { - if (PlotSquared.platform().playerManager().getPlayerIfExists(owner) != null) { - disable = false; - break; - } - } - } else { - disable = PlotSquared.platform().playerManager().getPlayerIfExists(plot.getOwnerAbs()) == null; - } - } - if (disable) { - for (UUID trusted : plot.getTrusted()) { - if (PlotSquared.platform().playerManager().getPlayerIfExists(trusted) != null) { - disable = false; - break; - } - } - if (disable) { - event.setNewCurrent(0); - plot.debug("Redstone event was cancelled because no trusted player was in the plot"); - return; - } - } - } - if (Settings.Redstone.DISABLE_UNOCCUPIED) { - for (final PlotPlayer player : PlotSquared.platform().playerManager().getPlayers()) { - if (plot.equals(player.getCurrentPlot())) { - return; - } - } - event.setNewCurrent(0); - } - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onPhysicsEvent(BlockPhysicsEvent event) { - Block block = event.getBlock(); - Location location = BukkitUtil.adapt(block.getLocation()); - PlotArea area = location.getPlotArea(); - if (area == null) { - return; - } - Plot plot = area.getOwnedPlotAbs(location); - if (plot == null) { - return; - } - if (event.getChangedType().hasGravity() && plot.getFlag(DisablePhysicsFlag.class)) { - event.setCancelled(true); - sendBlockChange(event.getBlock().getLocation(), event.getBlock().getBlockData()); - plot.debug("Prevented block physics and resent block change because disable-physics = true"); - return; - } - if (event.getChangedType() == Material.COMPARATOR) { - if (!plot.getFlag(RedstoneFlag.class)) { - event.setCancelled(true); - plot.debug("Prevented comparator update because redstone = false"); - } - return; - } - if (PHYSICS_BLOCKS.contains(event.getChangedType())) { - if (plot.getFlag(DisablePhysicsFlag.class)) { - event.setCancelled(true); - plot.debug("Prevented block physics because disable-physics = true"); - } - return; - } - if (Settings.Redstone.DETECT_INVALID_EDGE_PISTONS) { - if (PISTONS.contains(block.getType())) { - org.bukkit.block.data.Directional piston = (org.bukkit.block.data.Directional) block.getBlockData(); - final BlockFace facing = piston.getFacing(); - location = location.add(facing.getModX(), facing.getModY(), facing.getModZ()); - Plot newPlot = area.getOwnedPlotAbs(location); - if (plot.equals(newPlot)) { - return; - } - if (!plot.isMerged() || !plot.getConnectedPlots().contains(newPlot)) { - event.setCancelled(true); - plot.debug("Prevented piston update because of invalid edge piston detection"); - } - } - } - } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void blockCreate(BlockPlaceEvent event) { Location location = BukkitUtil.adapt(event.getBlock().getLocation()); diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/HighFreqBlockEventListener.java b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/HighFreqBlockEventListener.java new file mode 100644 index 000000000..24a01eb99 --- /dev/null +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/HighFreqBlockEventListener.java @@ -0,0 +1,201 @@ +/* + * 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.google.inject.Inject; +import com.plotsquared.bukkit.player.BukkitPlayer; +import com.plotsquared.bukkit.util.BukkitUtil; +import com.plotsquared.core.PlotSquared; +import com.plotsquared.core.configuration.Settings; +import com.plotsquared.core.database.DBFunc; +import com.plotsquared.core.location.Location; +import com.plotsquared.core.player.PlotPlayer; +import com.plotsquared.core.plot.Plot; +import com.plotsquared.core.plot.PlotArea; +import com.plotsquared.core.plot.flag.implementations.DisablePhysicsFlag; +import com.plotsquared.core.plot.flag.implementations.RedstoneFlag; +import com.plotsquared.core.plot.world.PlotAreaManager; +import com.plotsquared.core.util.PlotFlagUtil; +import com.plotsquared.core.util.task.TaskManager; +import com.plotsquared.core.util.task.TaskTime; +import com.sk89q.worldedit.WorldEdit; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.BlockData; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockPhysicsEvent; +import org.bukkit.event.block.BlockRedstoneEvent; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.Set; +import java.util.UUID; + +@SuppressWarnings("unused") +public class HighFreqBlockEventListener implements Listener { + + private static final Set PISTONS = Set.of( + Material.PISTON, + Material.STICKY_PISTON + ); + private static final Set PHYSICS_BLOCKS = Set.of( + Material.TURTLE_EGG, + Material.TURTLE_SPAWN_EGG + ); + + private final PlotAreaManager plotAreaManager; + private final WorldEdit worldEdit; + + @Inject + public HighFreqBlockEventListener(final @NonNull PlotAreaManager plotAreaManager, final @NonNull WorldEdit worldEdit) { + this.plotAreaManager = plotAreaManager; + this.worldEdit = worldEdit; + } + + public static void sendBlockChange(final org.bukkit.Location bloc, final BlockData data) { + TaskManager.runTaskLater(() -> { + String world = bloc.getWorld().getName(); + int x = bloc.getBlockX(); + int z = bloc.getBlockZ(); + int distance = Bukkit.getViewDistance() * 16; + + for (final PlotPlayer player : PlotSquared.platform().playerManager().getPlayers()) { + Location location = player.getLocation(); + if (location.getWorldName().equals(world)) { + if (16 * Math.abs(location.getX() - x) / 16 > distance || 16 * Math.abs(location.getZ() - z) / 16 > distance) { + continue; + } + ((BukkitPlayer) player).player.sendBlockChange(bloc, data); + } + } + }, TaskTime.ticks(3L)); + } + + @EventHandler + public void onRedstoneEvent(BlockRedstoneEvent event) { + Block block = event.getBlock(); + Location location = BukkitUtil.adapt(block.getLocation()); + PlotArea area = location.getPlotArea(); + if (area == null) { + return; + } + Plot plot = location.getOwnedPlot(); + if (plot == null) { + if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, RedstoneFlag.class, false)) { + event.setNewCurrent(0); + } + return; + } + if (!plot.getFlag(RedstoneFlag.class)) { + event.setNewCurrent(0); + plot.debug("Redstone event was cancelled because redstone = false"); + return; + } + if (Settings.Redstone.DISABLE_OFFLINE) { + boolean disable = false; + if (!DBFunc.SERVER.equals(plot.getOwner())) { + if (plot.isMerged()) { + disable = true; + for (UUID owner : plot.getOwners()) { + if (PlotSquared.platform().playerManager().getPlayerIfExists(owner) != null) { + disable = false; + break; + } + } + } else { + disable = PlotSquared.platform().playerManager().getPlayerIfExists(plot.getOwnerAbs()) == null; + } + } + if (disable) { + for (UUID trusted : plot.getTrusted()) { + if (PlotSquared.platform().playerManager().getPlayerIfExists(trusted) != null) { + disable = false; + break; + } + } + if (disable) { + event.setNewCurrent(0); + plot.debug("Redstone event was cancelled because no trusted player was in the plot"); + return; + } + } + } + if (Settings.Redstone.DISABLE_UNOCCUPIED) { + for (final PlotPlayer player : PlotSquared.platform().playerManager().getPlayers()) { + if (plot.equals(player.getCurrentPlot())) { + return; + } + } + event.setNewCurrent(0); + } + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onPhysicsEvent(BlockPhysicsEvent event) { + Block block = event.getBlock(); + Location location = BukkitUtil.adapt(block.getLocation()); + PlotArea area = location.getPlotArea(); + if (area == null) { + return; + } + Plot plot = area.getOwnedPlotAbs(location); + if (plot == null) { + return; + } + if (event.getChangedType().hasGravity() && plot.getFlag(DisablePhysicsFlag.class)) { + event.setCancelled(true); + sendBlockChange(event.getBlock().getLocation(), event.getBlock().getBlockData()); + plot.debug("Prevented block physics and resent block change because disable-physics = true"); + return; + } + if (event.getChangedType() == Material.COMPARATOR) { + if (!plot.getFlag(RedstoneFlag.class)) { + event.setCancelled(true); + plot.debug("Prevented comparator update because redstone = false"); + } + return; + } + if (PHYSICS_BLOCKS.contains(event.getChangedType())) { + if (plot.getFlag(DisablePhysicsFlag.class)) { + event.setCancelled(true); + plot.debug("Prevented block physics because disable-physics = true"); + } + return; + } + if (Settings.Redstone.DETECT_INVALID_EDGE_PISTONS) { + if (PISTONS.contains(block.getType())) { + org.bukkit.block.data.Directional piston = (org.bukkit.block.data.Directional) block.getBlockData(); + final BlockFace facing = piston.getFacing(); + location = location.add(facing.getModX(), facing.getModY(), facing.getModZ()); + Plot newPlot = area.getOwnedPlotAbs(location); + if (plot.equals(newPlot)) { + return; + } + if (!plot.isMerged() || !plot.getConnectedPlots().contains(newPlot)) { + event.setCancelled(true); + plot.debug("Prevented piston update because of invalid edge piston detection"); + } + } + } + } + +} 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 859c96673..896a294b5 100644 --- a/Core/src/main/java/com/plotsquared/core/configuration/Settings.java +++ b/Core/src/main/java/com/plotsquared/core/configuration/Settings.java @@ -43,6 +43,11 @@ public class Settings extends Config { "Leave it off if you don't need it, it can spam your console."}) public static boolean DEBUG = true; + @Comment({"The activity of high-frequency event listener can be deactivated here to improve the server performance. ", + "Affected settings: 'redstone' settings here below. Affected flags: 'disable-physics', 'redstone'. ", + "Only deactivate this setting if you do not need any of the mentioned settings or flags."}) + public static boolean HIGH_FREQUENCY_LISTENER = true; + @Create // This value will be generated automatically public static ConfigBlock AUTO_CLEAR = null; // A ConfigBlock is a section that can have multiple instances e.g. multiple expiry tasks