|
8 | 8 | import me.xginko.aef.modules.AEFModule; |
9 | 9 | import me.xginko.aef.utils.LocationUtil; |
10 | 10 | import me.xginko.aef.utils.WorldUtil; |
| 11 | +import me.xginko.aef.utils.models.Lazy; |
11 | 12 | import me.xginko.aef.utils.permissions.AEFPermission; |
| 13 | +import me.xginko.aef.utils.reflection.ReflectionUtil; |
12 | 14 | import org.bukkit.Chunk; |
13 | 15 | import org.bukkit.Location; |
14 | 16 | import org.bukkit.Material; |
|
18 | 20 | import org.bukkit.event.HandlerList; |
19 | 21 | import org.bukkit.event.Listener; |
20 | 22 | import org.bukkit.event.block.Action; |
| 23 | +import org.bukkit.event.block.BlockCanBuildEvent; |
21 | 24 | import org.bukkit.event.block.BlockPlaceEvent; |
22 | 25 | import org.bukkit.event.player.PlayerInteractEvent; |
23 | 26 | import org.jetbrains.annotations.NotNull; |
| 27 | +import org.jetbrains.annotations.Nullable; |
24 | 28 |
|
| 29 | +import java.lang.invoke.MethodHandle; |
25 | 30 | import java.time.Duration; |
26 | 31 | import java.util.EnumMap; |
27 | 32 | import java.util.Map; |
@@ -185,12 +190,45 @@ public void disable() { |
185 | 190 | } |
186 | 191 | } |
187 | 192 |
|
188 | | - private void sendPlayerNotification(Player player, Material block, int existing, int limit) { |
189 | | - if (notifyPlayer) player.sendMessage( |
190 | | - AnarchyExploitFixes.translation(player.getLocale()).chunklimits_block_limit_exceeded |
191 | | - .replace("%block%", block.name()) |
192 | | - .replace("%existing%", Integer.toString(existing)) |
193 | | - .replace("%limit%", Integer.toString(limit))); |
| 193 | + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) |
| 194 | + private void onBlockCanBuild(BlockCanBuildEvent event) { |
| 195 | + if (!event.isBuildable() || !blockLimits.containsKey(event.getMaterial())) { |
| 196 | + return; |
| 197 | + } |
| 198 | + |
| 199 | + Player player = getPlayer(event); |
| 200 | + if (player == null) return; |
| 201 | + |
| 202 | + if (AnarchyExploitFixes.permissions().permissionValue(player, AEFPermission.BYPASS_CHUNK_LIMITS_BLOCK_LIMIT.node()).toBoolean()) { |
| 203 | + logger().debug("Ignoring {} for player '{}' because they have bypass permission: {}", |
| 204 | + event.getEventName(), |
| 205 | + player.getName(), |
| 206 | + AEFPermission.BYPASS_CHUNK_LIMITS_BLOCK_LIMIT.node()); |
| 207 | + return; |
| 208 | + } |
| 209 | + |
| 210 | + int materialCount = getMaterialCountForChunk(event.getBlock().getType(), event.getBlock().getChunk()); |
| 211 | + int limit = blockLimits.get(event.getMaterial()); |
| 212 | + |
| 213 | + if (materialCount <= limit) { |
| 214 | + logger().debug("Allowing player '{}' to place {} at location {} => count={} limit={}", |
| 215 | + player.getName(), |
| 216 | + event.getMaterial(), |
| 217 | + LocationUtil.toString(event.getBlock().getLocation()), |
| 218 | + materialCount, |
| 219 | + limit); |
| 220 | + return; |
| 221 | + } |
| 222 | + |
| 223 | + event.setBuildable(false); |
| 224 | + |
| 225 | + sendPlayerNotification(player, event.getMaterial(), materialCount, limit); |
| 226 | + logger().info("Preventing player '{}' from placing {} at location {} => count={} limit={}", |
| 227 | + player.getName(), |
| 228 | + event.getBlock().getType(), |
| 229 | + LocationUtil.toString(event.getBlock().getLocation()), |
| 230 | + materialCount, |
| 231 | + limit); |
194 | 232 | } |
195 | 233 |
|
196 | 234 | @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) |
@@ -302,4 +340,32 @@ private int getMaterialCountForChunk(Material blockType, Chunk chunk) { |
302 | 340 |
|
303 | 341 | return cachedMaterialCount; |
304 | 342 | } |
| 343 | + |
| 344 | + private void sendPlayerNotification(Player player, Material block, int existing, int limit) { |
| 345 | + if (notifyPlayer) player.sendMessage( |
| 346 | + AnarchyExploitFixes.translation(player.getLocale()).chunklimits_block_limit_exceeded |
| 347 | + .replace("%block%", block.name()) |
| 348 | + .replace("%existing%", Integer.toString(existing)) |
| 349 | + .replace("%limit%", Integer.toString(limit))); |
| 350 | + } |
| 351 | + |
| 352 | + private @Nullable Player getPlayer(@NotNull BlockCanBuildEvent event) { |
| 353 | + if (BLOCK_CAN_BUILD_PLAYER_FIELD.get() == null) { |
| 354 | + return null; |
| 355 | + } |
| 356 | + |
| 357 | + try { |
| 358 | + return (Player) BLOCK_CAN_BUILD_PLAYER_FIELD.get().invoke(event); |
| 359 | + } catch (Throwable t) { |
| 360 | + return null; |
| 361 | + } |
| 362 | + } |
| 363 | + |
| 364 | + private static final Lazy<@Nullable MethodHandle> BLOCK_CAN_BUILD_PLAYER_FIELD = Lazy.of(() -> { |
| 365 | + try { |
| 366 | + return ReflectionUtil.findMethod(BlockCanBuildEvent.class, "getPlayer", Player.class); |
| 367 | + } catch (Throwable t) { |
| 368 | + return null; |
| 369 | + } |
| 370 | + }); |
305 | 371 | } |
0 commit comments