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.google.inject.Inject; 022import com.plotsquared.bukkit.player.BukkitPlayer; 023import com.plotsquared.bukkit.util.BukkitUtil; 024import com.plotsquared.core.PlotSquared; 025import com.plotsquared.core.configuration.Settings; 026import com.plotsquared.core.database.DBFunc; 027import com.plotsquared.core.location.Location; 028import com.plotsquared.core.player.PlotPlayer; 029import com.plotsquared.core.plot.Plot; 030import com.plotsquared.core.plot.PlotArea; 031import com.plotsquared.core.plot.flag.implementations.DisablePhysicsFlag; 032import com.plotsquared.core.plot.flag.implementations.RedstoneFlag; 033import com.plotsquared.core.plot.world.PlotAreaManager; 034import com.plotsquared.core.util.PlotFlagUtil; 035import com.plotsquared.core.util.task.TaskManager; 036import com.plotsquared.core.util.task.TaskTime; 037import com.sk89q.worldedit.WorldEdit; 038import org.bukkit.Bukkit; 039import org.bukkit.Material; 040import org.bukkit.block.Block; 041import org.bukkit.block.BlockFace; 042import org.bukkit.block.data.BlockData; 043import org.bukkit.event.EventHandler; 044import org.bukkit.event.EventPriority; 045import org.bukkit.event.Listener; 046import org.bukkit.event.block.BlockPhysicsEvent; 047import org.bukkit.event.block.BlockRedstoneEvent; 048import org.checkerframework.checker.nullness.qual.NonNull; 049 050import java.util.Set; 051import java.util.UUID; 052 053@SuppressWarnings("unused") 054public class HighFreqBlockEventListener implements Listener { 055 056 private static final Set<Material> PISTONS = Set.of( 057 Material.PISTON, 058 Material.STICKY_PISTON 059 ); 060 private static final Set<Material> PHYSICS_BLOCKS = Set.of( 061 Material.TURTLE_EGG, 062 Material.TURTLE_SPAWN_EGG 063 ); 064 065 private final PlotAreaManager plotAreaManager; 066 private final WorldEdit worldEdit; 067 068 @Inject 069 public HighFreqBlockEventListener(final @NonNull PlotAreaManager plotAreaManager, final @NonNull WorldEdit worldEdit) { 070 this.plotAreaManager = plotAreaManager; 071 this.worldEdit = worldEdit; 072 } 073 074 public static void sendBlockChange(final org.bukkit.Location bloc, final BlockData data) { 075 TaskManager.runTaskLater(() -> { 076 String world = bloc.getWorld().getName(); 077 int x = bloc.getBlockX(); 078 int z = bloc.getBlockZ(); 079 int distance = Bukkit.getViewDistance() * 16; 080 081 for (final PlotPlayer<?> player : PlotSquared.platform().playerManager().getPlayers()) { 082 Location location = player.getLocation(); 083 if (location.getWorldName().equals(world)) { 084 if (16 * Math.abs(location.getX() - x) / 16 > distance || 16 * Math.abs(location.getZ() - z) / 16 > distance) { 085 continue; 086 } 087 ((BukkitPlayer) player).player.sendBlockChange(bloc, data); 088 } 089 } 090 }, TaskTime.ticks(3L)); 091 } 092 093 @EventHandler 094 public void onRedstoneEvent(BlockRedstoneEvent event) { 095 Block block = event.getBlock(); 096 Location location = BukkitUtil.adapt(block.getLocation()); 097 PlotArea area = location.getPlotArea(); 098 if (area == null) { 099 return; 100 } 101 Plot plot = location.getOwnedPlot(); 102 if (plot == null) { 103 if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, RedstoneFlag.class, false)) { 104 event.setNewCurrent(0); 105 } 106 return; 107 } 108 if (!plot.getFlag(RedstoneFlag.class)) { 109 event.setNewCurrent(0); 110 plot.debug("Redstone event was cancelled because redstone = false"); 111 return; 112 } 113 if (Settings.Redstone.DISABLE_OFFLINE) { 114 boolean disable = false; 115 if (!DBFunc.SERVER.equals(plot.getOwner())) { 116 if (plot.isMerged()) { 117 disable = true; 118 for (UUID owner : plot.getOwners()) { 119 if (PlotSquared.platform().playerManager().getPlayerIfExists(owner) != null) { 120 disable = false; 121 break; 122 } 123 } 124 } else { 125 disable = PlotSquared.platform().playerManager().getPlayerIfExists(plot.getOwnerAbs()) == null; 126 } 127 } 128 if (disable) { 129 for (UUID trusted : plot.getTrusted()) { 130 if (PlotSquared.platform().playerManager().getPlayerIfExists(trusted) != null) { 131 disable = false; 132 break; 133 } 134 } 135 if (disable) { 136 event.setNewCurrent(0); 137 plot.debug("Redstone event was cancelled because no trusted player was in the plot"); 138 return; 139 } 140 } 141 } 142 if (Settings.Redstone.DISABLE_UNOCCUPIED) { 143 for (final PlotPlayer<?> player : PlotSquared.platform().playerManager().getPlayers()) { 144 if (plot.equals(player.getCurrentPlot())) { 145 return; 146 } 147 } 148 event.setNewCurrent(0); 149 } 150 } 151 152 @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) 153 public void onPhysicsEvent(BlockPhysicsEvent event) { 154 Block block = event.getBlock(); 155 Location location = BukkitUtil.adapt(block.getLocation()); 156 PlotArea area = location.getPlotArea(); 157 if (area == null) { 158 return; 159 } 160 Plot plot = area.getOwnedPlotAbs(location); 161 if (plot == null) { 162 return; 163 } 164 if (event.getChangedType().hasGravity() && plot.getFlag(DisablePhysicsFlag.class)) { 165 event.setCancelled(true); 166 sendBlockChange(event.getBlock().getLocation(), event.getBlock().getBlockData()); 167 plot.debug("Prevented block physics and resent block change because disable-physics = true"); 168 return; 169 } 170 if (event.getChangedType() == Material.COMPARATOR) { 171 if (!plot.getFlag(RedstoneFlag.class)) { 172 event.setCancelled(true); 173 plot.debug("Prevented comparator update because redstone = false"); 174 } 175 return; 176 } 177 if (PHYSICS_BLOCKS.contains(event.getChangedType())) { 178 if (plot.getFlag(DisablePhysicsFlag.class)) { 179 event.setCancelled(true); 180 plot.debug("Prevented block physics because disable-physics = true"); 181 } 182 return; 183 } 184 if (Settings.Redstone.DETECT_INVALID_EDGE_PISTONS) { 185 if (PISTONS.contains(block.getType())) { 186 org.bukkit.block.data.Directional piston = (org.bukkit.block.data.Directional) block.getBlockData(); 187 final BlockFace facing = piston.getFacing(); 188 location = location.add(facing.getModX(), facing.getModY(), facing.getModZ()); 189 Plot newPlot = area.getOwnedPlotAbs(location); 190 if (plot.equals(newPlot)) { 191 return; 192 } 193 if (!plot.isMerged() || !plot.getConnectedPlots().contains(newPlot)) { 194 event.setCancelled(true); 195 plot.debug("Prevented piston update because of invalid edge piston detection"); 196 } 197 } 198 } 199 } 200 201}