From acdd2dad55a08250c123202f9b515c9f143615cd Mon Sep 17 00:00:00 2001 From: joelb Date: Thu, 9 Apr 2026 19:58:42 +0200 Subject: [PATCH 1/4] - Changed old methods to new worldguard methods --- .../combat/region/worldguard/WorldGuardRegion.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/region/worldguard/WorldGuardRegion.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/region/worldguard/WorldGuardRegion.java index 4d0a5a8b..0ffc17de 100644 --- a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/region/worldguard/WorldGuardRegion.java +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/region/worldguard/WorldGuardRegion.java @@ -8,13 +8,14 @@ import org.bukkit.World; record WorldGuardRegion(World world, ProtectedRegion region) implements Region { + @Override public Point getCenter() { BlockVector3 min = this.region.getMinimumPoint(); BlockVector3 max = this.region.getMaximumPoint(); - double x = (double) (min.getX() + max.getX()) / 2; - double z = (double) (min.getZ() + max.getZ()) / 2; + double x = (min.x() + max.x()) / 2.0; + double z = (min.z() + max.z()) / 2.0; return new Point(this.world, x, z); } @@ -22,12 +23,13 @@ public Point getCenter() { @Override public Location getMin() { BlockVector3 min = this.region.getMinimumPoint(); - return new Location(this.world, min.getX(), min.getY(), min.getZ()); + return new Location(this.world, min.x(), min.y(), min.z()); } @Override public Location getMax() { BlockVector3 max = this.region.getMaximumPoint(); - return new Location(this.world, max.getX(), max.getY(), max.getZ()); + return new Location(this.world, max.x(), max.y(), max.z()); } + } From 9bbd1261ceabcbe0bae77592126465c41544a5e1 Mon Sep 17 00:00:00 2001 From: joelb Date: Thu, 9 Apr 2026 19:59:04 +0200 Subject: [PATCH 2/4] fix(knockback): prevent roof teleport exploit & improve safe knockback handling Fixed an issue where players using fight charge could remain inside knockback regions and get teleported onto the map roof (above playable area). ### Key changes: - Reworked knockback direction to push players toward nearest region edge instead of center-based vector - Added velocity dampening system for more consistent knockback behavior - Improved vertical handling: - Separate ground vs air vertical values - Prevent excessive vertical stacking while airborne ### Teleport fallback system: - Introduced optional smart fallback teleport when knockback fails - Added continuous velocity check before teleporting (prevents false triggers) - Prevent duplicate fallback tasks using active tracking set - Improved force knockback logic with edge distance + velocity validation ### Safe location handling: - Implemented safe ground detection system: - Avoid unsafe blocks (lava, cactus, magma, etc.) - Ensure 2-block air clearance above ground - Added configurable fallback scanning from highest block - Prevent teleport if no safe location is found (optional) ### Stability improvements: - Added recursion limit for region expansion to prevent infinite loops - Improved region overlap handling during location generation - Reduced unnecessary teleports and edge-case glitches ### Config additions: - vertical / maxAirVertical - dampenVelocity / dampenFactor - useTeleport (smart fallback) - safeGroundCheck + safeHighestFallback - unsafeGroundBlocks - groundOffset - maxAttempts - cancelIfNoSafeGround Result: - Eliminates roof teleport exploit - More natural and predictable knockback - Safer teleport fallback system - Better handling of edge cases and overlapping regions --- .../fight/knockback/KnockbackService.java | 258 ++++++++++++++++-- .../fight/knockback/KnockbackSettings.java | 139 +++++++++- 2 files changed, 363 insertions(+), 34 deletions(-) diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/knockback/KnockbackService.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/knockback/KnockbackService.java index bae8afab..b4135a9c 100644 --- a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/knockback/KnockbackService.java +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/knockback/KnockbackService.java @@ -1,21 +1,20 @@ package com.eternalcode.combat.fight.knockback; import com.eternalcode.combat.config.implementation.PluginConfig; -import com.eternalcode.combat.region.Point; import com.eternalcode.combat.region.Region; import com.eternalcode.combat.region.RegionProvider; import com.eternalcode.commons.bukkit.scheduler.MinecraftScheduler; +import com.eternalcode.commons.scheduler.Task; import io.papermc.lib.PaperLib; -import java.time.Duration; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; import org.bukkit.util.Vector; +import java.time.Duration; +import java.util.*; + public final class KnockbackService { private final PluginConfig config; @@ -23,6 +22,7 @@ public final class KnockbackService { private final RegionProvider regionProvider; private final Map insideRegion = new HashMap<>(); + private final Set fallbackActive = new HashSet<>(); public KnockbackService(PluginConfig config, MinecraftScheduler scheduler, RegionProvider regionProvider) { this.config = config; @@ -31,57 +31,257 @@ public KnockbackService(PluginConfig config, MinecraftScheduler scheduler, Regio } public void knockbackLater(Region region, Player player, Duration duration) { - this.scheduler.runLater(() -> this.knockback(region, player), duration); + scheduler.runLater(() -> this.knockback(region, player), duration); + } + + public void knockback(Region region, Player player) { + + if (player.isInsideVehicle()) { + player.leaveVehicle(); + } + + Location loc = player.getLocation(); + Vector direction = getDirectionToEdge(region, loc); + + if (config.knockback.dampenVelocity) { + player.setVelocity(player.getVelocity().multiply(config.knockback.dampenFactor)); + } + + boolean onGround = Math.abs(player.getVelocity().getY()) < 0.08; + + double y = onGround + ? config.knockback.vertical + : Math.min(player.getVelocity().getY(), config.knockback.maxAirVertical); + + Vector velocity = direction.multiply(config.knockback.multiplier).setY(y); + + player.setFallDistance(0); + player.setVelocity(velocity); + + + if (config.knockback.useTeleport) { + scheduleSmartFallback(player, region); + } } public void forceKnockbackLater(Player player, Region region) { - if (insideRegion.containsKey(player.getUniqueId())) { + UUID uuid = player.getUniqueId(); + + if (insideRegion.containsKey(uuid)) { return; } - insideRegion.put(player.getUniqueId(), region); + insideRegion.put(uuid, region); scheduler.runLater(player.getLocation(), () -> { - insideRegion.remove(player.getUniqueId()); - Location playerLocation = player.getLocation(); - if (!region.contains(playerLocation) && !regionProvider.isInRegion(playerLocation)) { + insideRegion.remove(uuid); + + Location loc = player.getLocation(); + double velocity = player.getVelocity().lengthSquared(); + + if (velocity > 0.02) { return; } - if (player.isInsideVehicle()) { - player.leaveVehicle(); + if (!region.contains(loc)) { + return; + } + + double distanceToEdge = getDistanceToEdge(region, loc); + + if (distanceToEdge > 1.5) { + return; + } + + if (fallbackActive.contains(uuid)) { + return; + } + + Location generated = generate( + player.getLocation(), + Point2D.from(region.getMin()), + Point2D.from(region.getMax()), + 0 + ); + + Location safe = makeSafe(generated); + if (safe == null || safe.getWorld() == null) { + return; } - Location location = generate(playerLocation, Point2D.from(region.getMin()), Point2D.from(region.getMax())); + PaperLib.teleportAsync(player, safe.clone(), TeleportCause.PLUGIN); - PaperLib.teleportAsync(player, location, TeleportCause.PLUGIN); - }, this.config.knockback.forceDelay); + }, config.knockback.forceDelay); } - private Location generate(Location playerLocation, Point2D minX, Point2D maxX) { + private void scheduleSmartFallback(Player player, Region region) { + UUID uuid = player.getUniqueId(); + + if (fallbackActive.contains(uuid)) { + return; + } + + fallbackActive.add(uuid); + + final Task[] taskRef = new Task[1]; + + taskRef[0] = scheduler.timer(() -> { + + Location check = player.getLocation(); + double velocity = player.getVelocity().lengthSquared(); + + if (!region.contains(check)) { + fallbackActive.remove(uuid); + taskRef[0].cancel(); + return; + } + + if (velocity > 0.02) { + return; + } + + Location generated = generate( + player.getLocation(), + Point2D.from(region.getMin()), + Point2D.from(region.getMax()), + 0 + ); + + Location safe = makeSafe(generated); + if (safe == null || safe.getWorld() == null) { + fallbackActive.remove(uuid); + taskRef[0].cancel(); + return; + } + + PaperLib.teleportAsync(player, safe.clone(), TeleportCause.PLUGIN); + + fallbackActive.remove(uuid); + taskRef[0].cancel(); + + }, + Duration.ofMillis(100), + Duration.ofMillis(100)); + } + + private Location makeSafe(Location loc) { + if (loc == null || loc.getWorld() == null) return loc; + + return config.knockback.safeGroundCheck + ? findSafeGround(loc) + : loc.getWorld().getHighestBlockAt(loc).getLocation().add(0, config.knockback.groundOffset, 0); + } + + private Location findSafeGround(Location loc) { + + if (loc.getWorld() == null) return loc; + + Location check = loc.clone(); + int minY = loc.getWorld().getMinHeight(); + + for (int y = check.getBlockY(); y > minY; y--) { + check.setY(y); + + Material type = check.getBlock().getType(); + Material above = check.clone().add(0, 1, 0).getBlock().getType(); + Material above2 = check.clone().add(0, 2, 0).getBlock().getType(); + + if (type.isSolid() + && !config.knockback.unsafeGroundBlocks.contains(type) + && above.isAir() + && above2.isAir()) { + + return check.clone().add(0, config.knockback.groundOffset, 0); + } + } + + return getSafeHighest(loc); + } + + private Location getSafeHighest(Location loc) { + if (loc == null || loc.getWorld() == null) return loc; + + if (!config.knockback.safeHighestFallback) { + return loc.getWorld() + .getHighestBlockAt(loc) + .getLocation() + .add(0, config.knockback.groundOffset, 0); + } + + Location highest = loc.getWorld().getHighestBlockAt(loc).getLocation(); + int minY = loc.getWorld().getMinHeight(); + + int startY = highest.getBlockY(); + int maxScan = config.knockback.safeHighestMaxScan; + + int endY = (maxScan < 0) + ? minY + : Math.max(minY, startY - maxScan); + + for (int y = startY; y > endY; y--) { + highest.setY(y); + + Material type = highest.getBlock().getType(); + Material above = highest.clone().add(0, 1, 0).getBlock().getType(); + Material above2 = highest.clone().add(0, 2, 0).getBlock().getType(); + + if (type.isSolid() + && !config.knockback.unsafeGroundBlocks.contains(type) + && above.isAir() + && above2.isAir()) { + + return highest.clone().add(0, config.knockback.groundOffset, 0); + } + } + + return config.knockback.cancelIfNoSafeGround ? null : loc; + } + + private Location generate(Location playerLocation, Point2D minX, Point2D maxX, int attempts) { + if (attempts >= config.knockback.maxAttempts) { + return playerLocation; + } + Location location = KnockbackOutsideRegionGenerator.generate(minX, maxX, playerLocation); + Optional otherRegion = regionProvider.getRegion(location); if (otherRegion.isPresent()) { + Region region = otherRegion.get(); - return generate(playerLocation, minX.min(region.getMin()), maxX.max(region.getMax())); + + return generate( + playerLocation, + minX.min(region.getMin()), + maxX.max(region.getMax()), + attempts + 1 + ); } return location; } - public void knockback(Region region, Player player) { - if (player.isInsideVehicle()) { - player.leaveVehicle(); - } + private Vector getDirectionToEdge(Region region, Location loc) { + double dxMin = loc.getX() - region.getMin().getX(); + double dxMax = region.getMax().getX() - loc.getX(); + double dzMin = loc.getZ() - region.getMin().getZ(); + double dzMax = region.getMax().getZ() - loc.getZ(); + + double min = Math.min(Math.min(dxMin, dxMax), Math.min(dzMin, dzMax)); - Point point = region.getCenter(); - Location subtract = player.getLocation().subtract(point.x(), 0, point.z()); + if (Math.abs(min - dxMin) < 1e-6) return new Vector(-1, 0, 0); + if (Math.abs(min - dxMax) < 1e-6) return new Vector(1, 0, 0); + if (Math.abs(min - dzMin) < 1e-6) return new Vector(0, 0, -1); + + return new Vector(0, 0, 1); + } - Vector knockbackVector = new Vector(subtract.getX(), 0, subtract.getZ()).normalize(); - double multiplier = this.config.knockback.multiplier; - Vector configuredVector = new Vector(multiplier, 0.5, multiplier); + private double getDistanceToEdge(Region region, Location loc) { + double dxMin = loc.getX() - region.getMin().getX(); + double dxMax = region.getMax().getX() - loc.getX(); + double dzMin = loc.getZ() - region.getMin().getZ(); + double dzMax = region.getMax().getZ() - loc.getZ(); - player.setVelocity(knockbackVector.multiply(configuredVector)); + return Math.min(Math.min(dxMin, dxMax), Math.min(dzMin, dzMax)); } } diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/knockback/KnockbackSettings.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/knockback/KnockbackSettings.java index 8b722435..48d08248 100644 --- a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/knockback/KnockbackSettings.java +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/knockback/KnockbackSettings.java @@ -2,18 +2,147 @@ import eu.okaeri.configs.OkaeriConfig; import eu.okaeri.configs.annotation.Comment; +import org.bukkit.Material; + import java.time.Duration; +import java.util.Set; public class KnockbackSettings extends OkaeriConfig { @Comment({ - "# Adjust the knockback multiplier for restricted regions.", - "# Higher values increase the knockback distance. Avoid using negative values.", - "# A value of 1.0 typically knocks players 2-4 blocks away." + "# Horizontal knockback strength multiplier.", + "# This controls how far the player is pushed horizontally.", + "#", + "# Applied in: direction.multiply(multiplier)", + "# Example: 1.0 ≈ 2–4 blocks push" + }) + public double multiplier = 1.0; + + @Comment({ + "# Vertical velocity applied when the player is considered on ground.", + "#", + "# Used when Y velocity is near zero (< 0.08).", + "# Higher values = more upward knockback.", + "# Recommended: 0.15 - 0.3" + }) + public double vertical = 0.2; + + @Comment({ + "# Maximum vertical velocity when player is already airborne.", + "#", + "# Prevents stacking vertical velocity or launching too high.", + "# Used as: min(currentY, maxAirVertical)" }) - public double multiplier = 1; + public double maxAirVertical = 0.2; - @Comment({ "# Time after which the player will be force knocked back outside the safe zone" }) + @Comment({ + "# Delay before force teleport is applied after entering a region.", + "#", + "# Used in forceKnockbackLater().", + "# Prevents instant teleport when crossing region borders." + }) public Duration forceDelay = Duration.ofSeconds(1); + @Comment({ + "# Enables teleport fallback after knockback.", + "#", + "# If knockback does not push the player outside the region,", + "# a safe location will be generated and player will be teleported." + }) + public boolean useTeleport = false; + + @Comment({ + "# Blocks that are considered unsafe to stand on when searching for ground.", + "# These blocks will be ignored during safe ground detection.", + "#", + "# Used in findSafeGround():", + "# - Skips these blocks even if they are solid", + "# - Prevents teleporting onto dangerous or invalid blocks", + "#", + "# Examples:", + "# - BARRIER (invisible collision)", + "# - LAVA / WATER (damage / movement issues)", + "# - CACTUS / MAGMA (damage blocks)", + }) + public Set unsafeGroundBlocks = Set.of( + Material.BARRIER, + Material.LAVA, + Material.WATER, + Material.CACTUS, + Material.MAGMA_BLOCK, + Material.FIRE, + Material.SOUL_FIRE + ); + + @Comment({ + "# Use custom safe ground detection instead of highest block. (recommended)", + "#", + "# true -> scans downward for safe landing", + "# false -> uses Bukkit getHighestBlockAt()", + "#", + "# Safe mode prevents:", + "# - Landing on roofs", + "# - Landing on barriers", + "# - Unsafe teleport positions" + }) + public boolean safeGroundCheck = true; + + @Comment({ + "# Enables safe fallback scanning instead of using Bukkit highest block directly.", + "#", + "# true -> scans downward from highest block to find safe ground", + "# false -> uses Bukkit getHighestBlockAt (faster but unsafe)" + }) + public boolean safeHighestFallback = true; + + @Comment({ + "# Maximum vertical scan distance for safe highest fallback.", + "#", + "# Prevents excessive scanning in very tall worlds.", + "# Set to -1 to scan all the way down to min world height." + }) + public int safeHighestMaxScan = -1; + + @Comment({ + "# If true, prevents teleport if no safe ground is found at all.", + "#", + "# true -> cancel teleport", + "# false -> fallback to original location" + }) + public boolean cancelIfNoSafeGround = false; + + @Comment({ + "# Y offset added after finding ground.", + "#", + "# Usually 1.0 = player stands exactly on block.", + "# Can be increased slightly to prevent clipping issues." + }) + public double groundOffset = 1.0; + + @Comment({ + "# Reduces player's current velocity BEFORE applying knockback.", + "#", + "# Helps create smoother and more consistent knockback.", + "# Prevents stacking velocity from previous movement." + }) + public boolean dampenVelocity = true; + + @Comment({ + "# Multiplier applied when dampening velocity.", + "#", + "# 1.0 = no change", + "# 0.0 = completely stop player", + "# Recommended: 0.6 - 0.9" + }) + public double dampenFactor = 0.8; + + @Comment({ + "# Maximum recursive attempts when generating a safe location.", + "#", + "# Used in generate() method.", + "# Prevents infinite loops when regions overlap or chain.", + "#", + "# If exceeded -> fallback to player current location." + }) + public int maxAttempts = 5; } From 5e85e81c7995c62c4f8b831085832700397d4004 Mon Sep 17 00:00:00 2001 From: joelb Date: Wed, 15 Apr 2026 10:30:45 +0200 Subject: [PATCH 3/4] fix(combat): prevent elytra escape exploit & improve glide restriction handling Fixed an issue where players could abuse elytra (e.g. cliff jumping, trident launching, or mid-air tagging) to escape combat and bypass restrictions. ### Key changes: - Fully disabled elytra usage during combat - Blocked glide activation via EntityToggleGlideEvent - Added continuous glide checks to stop mid-air abuse - Forced downward velocity to prevent spacebar glide spam ### Elytra handling system: - Introduced automatic elytra unequip on combat tag - Prevents players from staying airborne when entering combat - Moves elytra safely to inventory or drops if full - Eliminates pre-glide and mid-air tagging exploits ### Inventory protection: - Blocked equipping elytra during combat: - Click equip - Shift-click - Hotbar swap (number keys) - Prevents all known re-equip bypass methods ### Flight & movement improvements: - Disabled flight properly on combat start - Prevents glide reactivation while moving ### Messaging improvements: - Added dedicated elytra combat message - Clear feedback when elytra is blocked or removed ### Config additions: - unequipElytraOnCombat - blockElytraEquipDuringCombat - elytraDisabledDuringCombat Result: - Eliminates cliff jump & trident launch exploits - Prevents all elytra-based combat escapes - Provides consistent and predictable combat behavior - Matches behavior of high-end PvP combat systems --- .../config/implementation/CombatSettings.java | 8 ++ .../implementation/MessagesSettings.java | 9 ++ .../FightActionBlockerController.java | 87 ++++++++++++++++++- 3 files changed, 102 insertions(+), 2 deletions(-) diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/config/implementation/CombatSettings.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/config/implementation/CombatSettings.java index f84740e4..8378dae9 100644 --- a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/config/implementation/CombatSettings.java +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/config/implementation/CombatSettings.java @@ -33,6 +33,14 @@ public class CombatSettings extends OkaeriConfig { }) public boolean disableFlying = true; + @Comment({ + "# Forcefully unequip elytra when a player enters combat.", + "# This prevents players from abusing glide by jumping before being tagged.", + "# The elytra will be moved to the player's inventory.", + "# Recommended: true" + }) + public boolean unequipElytraOnCombat = true; + @Comment({ "# Prevent players from boosting themselves while flying with fireworks", "# This setting blocks usage of fireworks to boost elytra flight during combat" diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/config/implementation/MessagesSettings.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/config/implementation/MessagesSettings.java index d5f69e21..cc9e53ec 100644 --- a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/config/implementation/MessagesSettings.java +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/config/implementation/MessagesSettings.java @@ -70,6 +70,15 @@ public class MessagesSettings extends OkaeriConfig { public Notice commandDisabledDuringCombat = Notice.chat( "Command blocked! Cannot use this during combat!"); + @Comment({ + "# Message displayed when a player attempts to use an elytra during combat.", + "# This includes gliding, equipping, or having it forcefully removed.", + "# Informs the player that elytra usage is disabled in combat." + }) + public Notice elytraDisabledDuringCombat = Notice.chat( + "Elytra disabled! Cannot use elytra during combat!" + ); + @Comment({ "# Message displayed when a player uses a command with incorrect arguments.", "# The {USAGE} placeholder is replaced with the correct command syntax." diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/controller/FightActionBlockerController.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/controller/FightActionBlockerController.java index cd193717..371a64d4 100644 --- a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/controller/FightActionBlockerController.java +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/controller/FightActionBlockerController.java @@ -16,14 +16,18 @@ import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityToggleGlideEvent; +import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryOpenEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerToggleFlightEvent; +import java.util.HashMap; import java.util.List; import java.util.UUID; + +import org.bukkit.inventory.ItemStack; import org.bukkit.util.StringUtil; public class FightActionBlockerController implements Listener { @@ -109,6 +113,7 @@ void onToggleGlide(EntityToggleGlideEvent event) { if (event.isGliding()) { event.setCancelled(true); + player.setGliding(false); } } @@ -127,11 +132,12 @@ void onMoveWhileGliding(PlayerMoveEvent event) { if (player.isGliding()) { player.setGliding(false); + + player.setFallDistance(0f); + player.setVelocity(player.getVelocity().setY(-1)); } } - - @EventHandler void onFly(PlayerToggleFlightEvent event) { if (!this.config.combat.disableFlying) { @@ -152,6 +158,38 @@ void onFly(PlayerToggleFlightEvent event) { } } + @EventHandler + void onTag(com.eternalcode.combat.fight.event.FightTagEvent event) { + UUID uniqueId = event.getPlayer(); + Player player = this.server.getPlayer(uniqueId); + + if (player == null) { + return; + } + + if (this.config.combat.disableFlying) { + GameMode gameMode = player.getGameMode(); + + if (gameMode != GameMode.CREATIVE && gameMode != GameMode.SPECTATOR) { + player.setAllowFlight(false); + player.setFlying(false); + } + } + + if (this.config.combat.unequipElytraOnCombat) { + ItemStack chest = player.getInventory().getChestplate(); + + if (chest != null && chest.getType() == Material.ELYTRA) { + removeChestplateIfElytra(player); + + this.noticeService.create() + .player(uniqueId) + .notice(this.config.messagesSettings.elytraDisabledDuringCombat) + .send(); + } + } + } + @EventHandler void onUnTag(FightUntagEvent event) { if (!this.config.combat.disableFlying) { @@ -213,4 +251,49 @@ void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) { } } + + @EventHandler + void onInventoryClick(InventoryClickEvent event) { + if (!(event.getWhoClicked() instanceof Player player)) { + return; + } + + if (!this.config.combat.unequipElytraOnCombat) { + return; + } + + UUID uniqueId = player.getUniqueId(); + + if (!this.fightManager.isInCombat(uniqueId)) { + return; + } + + if (event.getCurrentItem() == null) { + return; + } + + if (event.getCurrentItem().getType() == Material.ELYTRA) { + event.setCancelled(true); + + this.noticeService.create() + .player(uniqueId) + .notice(this.config.messagesSettings.elytraDisabledDuringCombat) + .send(); + } + } + + private void removeChestplateIfElytra(Player player) { + ItemStack chestplate = player.getInventory().getChestplate(); + + if (chestplate != null && chestplate.getType() == Material.ELYTRA) { + player.getInventory().setChestplate(null); + + HashMap leftover = player.getInventory().addItem(chestplate); + if (!leftover.isEmpty()) { + leftover.values().forEach(item -> + player.getWorld().dropItemNaturally(player.getLocation(), item) + ); + } + } + } } From 6fc48e27b1ad0984b492ee9a54222e8d67e79e88 Mon Sep 17 00:00:00 2001 From: Rollczi Date: Tue, 28 Apr 2026 23:27:46 +0200 Subject: [PATCH 4/4] CR --- eternalcombat-plugin/build.gradle.kts | 2 +- .../com/eternalcode/combat/CombatPlugin.java | 15 +- .../combat/fight/blocker/CommandsBlocker.java | 54 ++++ .../combat/fight/blocker/ElytraBlocker.java | 162 ++++++++++ .../combat/fight/blocker/FlyingBlocker.java | 86 +++++ .../InventoryContainersBlocker.java} | 6 +- .../fight/blocker/PlaceBlockBlocker.java | 81 +++++ .../FightActionBlockerController.java | 299 ------------------ 8 files changed, 398 insertions(+), 307 deletions(-) create mode 100644 eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/blocker/CommandsBlocker.java create mode 100644 eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/blocker/ElytraBlocker.java create mode 100644 eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/blocker/FlyingBlocker.java rename eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/{controller/FightInventoryController.java => blocker/InventoryContainersBlocker.java} (86%) create mode 100644 eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/blocker/PlaceBlockBlocker.java delete mode 100644 eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/controller/FightActionBlockerController.java diff --git a/eternalcombat-plugin/build.gradle.kts b/eternalcombat-plugin/build.gradle.kts index 6edfab7f..3cbf3602 100644 --- a/eternalcombat-plugin/build.gradle.kts +++ b/eternalcombat-plugin/build.gradle.kts @@ -96,7 +96,7 @@ bukkit { tasks { runServer { - minecraftVersion("1.21.10") + minecraftVersion("1.21.11") downloadPlugins.modrinth("WorldEdit", Versions.WORLDEDIT) downloadPlugins.modrinth("PacketEvents", "${Versions.PACKETEVENTS}+spigot") downloadPlugins.modrinth("WorldGuard", Versions.WORLDGUARD) diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java index 48874171..b88762e4 100644 --- a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java @@ -8,10 +8,13 @@ import com.eternalcode.combat.bridge.BridgeService; import com.eternalcode.combat.crystalpvp.RespawnAnchorListener; import com.eternalcode.combat.crystalpvp.EndCrystalListener; +import com.eternalcode.combat.fight.blocker.CommandsBlocker; +import com.eternalcode.combat.fight.blocker.ElytraBlocker; +import com.eternalcode.combat.fight.blocker.FlyingBlocker; import com.eternalcode.combat.fight.controller.FightBypassAdminController; import com.eternalcode.combat.fight.controller.FightBypassCreativeController; import com.eternalcode.combat.fight.controller.FightBypassPermissionController; -import com.eternalcode.combat.fight.controller.FightInventoryController; +import com.eternalcode.combat.fight.blocker.InventoryContainersBlocker; import com.eternalcode.combat.fight.death.DeathFlareController; import com.eternalcode.combat.fight.death.DeathLightningController; import com.eternalcode.combat.fight.drop.DropKeepInventoryService; @@ -32,7 +35,7 @@ import com.eternalcode.combat.fight.drop.impl.PercentDropModifier; import com.eternalcode.combat.fight.drop.impl.PlayersHealthDropModifier; import com.eternalcode.combat.fight.FightTagCommand; -import com.eternalcode.combat.fight.controller.FightActionBlockerController; +import com.eternalcode.combat.fight.blocker.PlaceBlockBlocker; import com.eternalcode.combat.fight.controller.FightMessageController; import com.eternalcode.combat.fight.controller.FightTagController; import com.eternalcode.combat.fight.controller.FightUnTagController; @@ -181,7 +184,7 @@ public void onEnable() { new FightBypassAdminController(server, pluginConfig), new FightBypassPermissionController(server, pluginConfig), new FightBypassCreativeController(server, pluginConfig), - new FightActionBlockerController(this.fightManager, noticeService, pluginConfig, server), + new PlaceBlockBlocker(this.fightManager, noticeService, pluginConfig), new FightPearlController(pluginConfig.pearl, noticeService, this.fightManager, this.fightPearlService), new DeathFlareController(pluginConfig, server, scheduler, this), new DeathLightningController(pluginConfig, server), @@ -196,7 +199,11 @@ public void onEnable() { new EndCrystalListener(this, this.fightManager, pluginConfig), new RespawnAnchorListener(this, this.fightManager, pluginConfig), new FireworkController(this.fightManager, pluginConfig, noticeService), - new FightInventoryController(this.fightManager, pluginConfig, noticeService) + new InventoryContainersBlocker(this.fightManager, pluginConfig, noticeService), + new CommandsBlocker(this.fightManager, noticeService, pluginConfig), + new ElytraBlocker(this.fightManager, noticeService, pluginConfig, server), + new FlyingBlocker(this.fightManager, pluginConfig, server), + new PlaceBlockBlocker(this.fightManager, noticeService, pluginConfig) ); eventManager.subscribe( diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/blocker/CommandsBlocker.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/blocker/CommandsBlocker.java new file mode 100644 index 00000000..8f6867ab --- /dev/null +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/blocker/CommandsBlocker.java @@ -0,0 +1,54 @@ +package com.eternalcode.combat.fight.blocker; + +import com.eternalcode.combat.WhitelistBlacklistMode; +import com.eternalcode.combat.config.implementation.PluginConfig; +import com.eternalcode.combat.fight.FightManager; +import com.eternalcode.combat.notification.NoticeService; +import java.util.UUID; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.util.StringUtil; + +public class CommandsBlocker implements Listener { + + private final FightManager fightManager; + private final NoticeService noticeService; + private final PluginConfig config; + + public CommandsBlocker(FightManager fightManager, NoticeService noticeService, PluginConfig config) { + this.fightManager = fightManager; + this.noticeService = noticeService; + this.config = config; + } + + @EventHandler + void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) { + Player player = event.getPlayer(); + UUID playerUniqueId = player.getUniqueId(); + + if (!this.fightManager.isInCombat(playerUniqueId)) { + return; + } + + String command = event.getMessage().substring(1); + + boolean isAnyMatch = this.config.commands.restrictedCommands.stream() + .anyMatch(restrictedCommand -> StringUtil.startsWithIgnoreCase(command, restrictedCommand)); + + WhitelistBlacklistMode mode = this.config.commands.commandRestrictionMode; + + boolean shouldCancel = mode.shouldBlock(isAnyMatch); + + if (shouldCancel) { + event.setCancelled(true); + this.noticeService.create() + .player(playerUniqueId) + .notice(this.config.messagesSettings.commandDisabledDuringCombat) + .send(); + + } + } + +} diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/blocker/ElytraBlocker.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/blocker/ElytraBlocker.java new file mode 100644 index 00000000..05e833bb --- /dev/null +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/blocker/ElytraBlocker.java @@ -0,0 +1,162 @@ +package com.eternalcode.combat.fight.blocker; + +import com.eternalcode.combat.config.implementation.PluginConfig; +import com.eternalcode.combat.fight.FightManager; +import com.eternalcode.combat.fight.event.FightTagEvent; +import com.eternalcode.combat.notification.NoticeService; +import java.util.HashMap; +import java.util.UUID; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityToggleGlideEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.inventory.ItemStack; + +public class ElytraBlocker implements Listener { + + private final FightManager fightManager; + private final NoticeService noticeService; + private final PluginConfig config; + private final Server server; + + public ElytraBlocker(FightManager fightManager, NoticeService noticeService, PluginConfig config, Server server) { + this.fightManager = fightManager; + this.noticeService = noticeService; + this.config = config; + this.server = server; + } + + @EventHandler + void onToggleGlide(EntityToggleGlideEvent event) { + if (!this.config.combat.disableElytraUsage) { + return; + } + + if (!(event.getEntity() instanceof Player player)) { + return; + } + UUID uniqueId = player.getUniqueId(); + + if (!this.fightManager.isInCombat(uniqueId)) { + return; + } + + if (event.isGliding()) { + event.setCancelled(true); + player.setGliding(false); + } + } + + + @EventHandler + void onMoveWhileGliding(PlayerMoveEvent event) { + if (!this.config.combat.disableElytraUsage) { + return; + } + + Player player = event.getPlayer(); + UUID uniqueId = player.getUniqueId(); + + if (!this.fightManager.isInCombat(uniqueId)) { + return; + } + + if (player.isGliding()) { + player.setGliding(false); + + player.setFallDistance(0f); + player.setVelocity(player.getVelocity().setY(-1)); + } + } + + @EventHandler + void onInventoryClick(InventoryClickEvent event) { + if (!(event.getWhoClicked() instanceof Player player)) { + return; + } + + if (!this.config.combat.unequipElytraOnCombat) { + return; + } + + UUID uniqueId = player.getUniqueId(); + + if (!this.fightManager.isInCombat(uniqueId)) { + return; + } + + if (event.getCurrentItem() == null) { + return; + } + + if (event.getCurrentItem().getType() == Material.ELYTRA) { + event.setCancelled(true); + + this.noticeService.create() + .player(uniqueId) + .notice(this.config.messagesSettings.elytraDisabledDuringCombat) + .send(); + } + } + + + @EventHandler + void onTag(FightTagEvent event) { + UUID uniqueId = event.getPlayer(); + Player player = this.server.getPlayer(uniqueId); + + if (player == null) { + return; + } + + if (this.config.combat.unequipElytraOnCombat) { + ItemStack chest = player.getInventory().getChestplate(); + + if (chest != null && chest.getType() == Material.ELYTRA) { + removeChestplateIfElytra(player); + + this.noticeService.create() + .player(uniqueId) + .notice(this.config.messagesSettings.elytraDisabledDuringCombat) + .send(); + } + } + } + + private void removeChestplateIfElytra(Player player) { + ItemStack chestplate = player.getInventory().getChestplate(); + + if (chestplate != null && chestplate.getType() == Material.ELYTRA) { + player.getInventory().setChestplate(null); + + HashMap leftover = player.getInventory().addItem(chestplate); + if (!leftover.isEmpty()) { + leftover.values().forEach(item -> + player.getWorld().dropItemNaturally(player.getLocation(), item) + ); + } + } + } + + @EventHandler + void onDamage(EntityDamageEvent event) { + if (!this.config.combat.disableElytraOnDamage) { + return; + } + + if (!(event.getEntity() instanceof Player player)) { + return; + } + UUID uniqueId = player.getUniqueId(); + + if (this.fightManager.isInCombat(uniqueId) && player.isGliding()) { + player.setGliding(false); + } + } + +} diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/blocker/FlyingBlocker.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/blocker/FlyingBlocker.java new file mode 100644 index 00000000..5b3ecb01 --- /dev/null +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/blocker/FlyingBlocker.java @@ -0,0 +1,86 @@ +package com.eternalcode.combat.fight.blocker; + +import com.eternalcode.combat.config.implementation.PluginConfig; +import com.eternalcode.combat.fight.FightManager; +import com.eternalcode.combat.fight.event.FightTagEvent; +import com.eternalcode.combat.fight.event.FightUntagEvent; +import java.util.UUID; +import org.bukkit.GameMode; +import org.bukkit.Server; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerToggleFlightEvent; + +public class FlyingBlocker implements Listener { + + private final FightManager fightManager; + private final PluginConfig config; + private final Server server; + + public FlyingBlocker(FightManager fightManager, PluginConfig config, Server server) { + this.fightManager = fightManager; + this.config = config; + this.server = server; + } + + + @EventHandler + void onFly(PlayerToggleFlightEvent event) { + if (!this.config.combat.disableFlying) { + return; + } + + Player player = event.getPlayer(); + UUID uniqueId = player.getUniqueId(); + + if (!this.fightManager.isInCombat(uniqueId)) { + return; + } + + if (event.isFlying()) { + player.setAllowFlight(false); + + event.setCancelled(true); + } + } + + + @EventHandler + void onTag(FightTagEvent event) { + UUID uniqueId = event.getPlayer(); + Player player = this.server.getPlayer(uniqueId); + + if (player == null) { + return; + } + + if (this.config.combat.disableFlying) { + GameMode gameMode = player.getGameMode(); + + if (gameMode != GameMode.CREATIVE && gameMode != GameMode.SPECTATOR) { + player.setAllowFlight(false); + player.setFlying(false); + } + } + } + + @EventHandler + void onUnTag(FightUntagEvent event) { + if (!this.config.combat.disableFlying) { + return; + } + + UUID uniqueId = event.getPlayer(); + Player player = this.server.getPlayer(uniqueId); + + if (player == null) { + return; + } + GameMode playerGameMode = player.getGameMode(); + if (playerGameMode == GameMode.CREATIVE || playerGameMode == GameMode.SPECTATOR) { + player.setAllowFlight(true); + } + } + +} diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/controller/FightInventoryController.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/blocker/InventoryContainersBlocker.java similarity index 86% rename from eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/controller/FightInventoryController.java rename to eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/blocker/InventoryContainersBlocker.java index 3858cfb9..4479a693 100644 --- a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/controller/FightInventoryController.java +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/blocker/InventoryContainersBlocker.java @@ -1,4 +1,4 @@ -package com.eternalcode.combat.fight.controller; +package com.eternalcode.combat.fight.blocker; import com.eternalcode.combat.config.implementation.PluginConfig; import com.eternalcode.combat.fight.FightManager; @@ -10,13 +10,13 @@ import org.bukkit.event.inventory.InventoryOpenEvent; import org.bukkit.event.inventory.InventoryType; -public class FightInventoryController implements Listener { +public class InventoryContainersBlocker implements Listener { private final FightManager fightManager; private final PluginConfig config; private final NoticeService noticeService; - public FightInventoryController(FightManager fightManager, PluginConfig config, NoticeService noticeService) { + public InventoryContainersBlocker(FightManager fightManager, PluginConfig config, NoticeService noticeService) { this.fightManager = fightManager; this.config = config; this.noticeService = noticeService; diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/blocker/PlaceBlockBlocker.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/blocker/PlaceBlockBlocker.java new file mode 100644 index 00000000..4e669f2f --- /dev/null +++ b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/blocker/PlaceBlockBlocker.java @@ -0,0 +1,81 @@ +package com.eternalcode.combat.fight.blocker; + +import com.eternalcode.combat.config.implementation.PluginConfig; +import com.eternalcode.combat.config.implementation.BlockPlacementSettings; +import com.eternalcode.combat.fight.FightManager; +import com.eternalcode.combat.notification.NoticeService; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockPlaceEvent; + +import java.util.List; +import java.util.UUID; + +public class PlaceBlockBlocker implements Listener { + + private final FightManager fightManager; + private final NoticeService noticeService; + private final PluginConfig config; + + public PlaceBlockBlocker(FightManager fightManager, NoticeService noticeService, PluginConfig config) { + this.fightManager = fightManager; + this.noticeService = noticeService; + this.config = config; + } + + @EventHandler + void onPlace(BlockPlaceEvent event) { + if (!this.config.blockPlacement.disableBlockPlacing) { + return; + } + + Player player = event.getPlayer(); + UUID uniqueId = player.getUniqueId(); + + if (!this.fightManager.isInCombat(uniqueId)) { + return; + } + + Block block = event.getBlock(); + int level = block.getY(); + + List specificBlocksToPreventPlacing = this.config.blockPlacement.restrictedBlockTypes; + + boolean isPlacementBlocked = this.isPlacementBlocked(level); + + if (isPlacementBlocked && specificBlocksToPreventPlacing.isEmpty()) { + event.setCancelled(true); + this.noticeService.create() + .player(uniqueId) + .notice(this.config.messagesSettings.blockPlacingBlockedDuringCombat) + .placeholder("{Y}", String.valueOf(this.config.blockPlacement.blockPlacementYCoordinate)) + .placeholder("{MODE}", this.config.blockPlacement.blockPlacementModeDisplayName) + .send(); + + } + + Material blockMaterial = block.getType(); + boolean isBlockInDisabledList = specificBlocksToPreventPlacing.contains(blockMaterial); + if (isPlacementBlocked && isBlockInDisabledList) { + event.setCancelled(true); + + this.noticeService.create() + .player(uniqueId) + .notice(this.config.messagesSettings.blockPlacingBlockedDuringCombat) + .placeholder("{Y}", String.valueOf(this.config.blockPlacement.blockPlacementYCoordinate)) + .placeholder("{MODE}", this.config.blockPlacement.blockPlacementModeDisplayName) + .send(); + + } + } + + private boolean isPlacementBlocked(int level) { + return this.config.blockPlacement.blockPlacementMode == BlockPlacementSettings.BlockPlacingMode.ABOVE + ? level > this.config.blockPlacement.blockPlacementYCoordinate + : level < this.config.blockPlacement.blockPlacementYCoordinate; + } + +} diff --git a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/controller/FightActionBlockerController.java b/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/controller/FightActionBlockerController.java deleted file mode 100644 index 371a64d4..00000000 --- a/eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/controller/FightActionBlockerController.java +++ /dev/null @@ -1,299 +0,0 @@ -package com.eternalcode.combat.fight.controller; - -import com.eternalcode.combat.config.implementation.PluginConfig; -import com.eternalcode.combat.WhitelistBlacklistMode; -import com.eternalcode.combat.config.implementation.BlockPlacementSettings; -import com.eternalcode.combat.fight.FightManager; -import com.eternalcode.combat.fight.event.FightUntagEvent; -import com.eternalcode.combat.notification.NoticeService; -import org.bukkit.GameMode; -import org.bukkit.Material; -import org.bukkit.Server; -import org.bukkit.block.Block; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockPlaceEvent; -import org.bukkit.event.entity.EntityDamageEvent; -import org.bukkit.event.entity.EntityToggleGlideEvent; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryOpenEvent; -import org.bukkit.event.inventory.InventoryType; -import org.bukkit.event.player.PlayerCommandPreprocessEvent; -import org.bukkit.event.player.PlayerMoveEvent; -import org.bukkit.event.player.PlayerToggleFlightEvent; - -import java.util.HashMap; -import java.util.List; -import java.util.UUID; - -import org.bukkit.inventory.ItemStack; -import org.bukkit.util.StringUtil; - -public class FightActionBlockerController implements Listener { - - private final FightManager fightManager; - private final NoticeService noticeService; - private final PluginConfig config; - private final Server server; - - public FightActionBlockerController(FightManager fightManager, NoticeService noticeService, PluginConfig config, Server server) { - this.fightManager = fightManager; - this.noticeService = noticeService; - this.config = config; - this.server = server; - } - - @EventHandler - void onPlace(BlockPlaceEvent event) { - if (!this.config.blockPlacement.disableBlockPlacing) { - return; - } - - Player player = event.getPlayer(); - UUID uniqueId = player.getUniqueId(); - - if (!this.fightManager.isInCombat(uniqueId)) { - return; - } - - Block block = event.getBlock(); - int level = block.getY(); - - List specificBlocksToPreventPlacing = this.config.blockPlacement.restrictedBlockTypes; - - boolean isPlacementBlocked = this.isPlacementBlocked(level); - - if (isPlacementBlocked && specificBlocksToPreventPlacing.isEmpty()) { - event.setCancelled(true); - this.noticeService.create() - .player(uniqueId) - .notice(this.config.messagesSettings.blockPlacingBlockedDuringCombat) - .placeholder("{Y}", String.valueOf(this.config.blockPlacement.blockPlacementYCoordinate)) - .placeholder("{MODE}", this.config.blockPlacement.blockPlacementModeDisplayName) - .send(); - - } - - Material blockMaterial = block.getType(); - boolean isBlockInDisabledList = specificBlocksToPreventPlacing.contains(blockMaterial); - if (isPlacementBlocked && isBlockInDisabledList) { - event.setCancelled(true); - - this.noticeService.create() - .player(uniqueId) - .notice(this.config.messagesSettings.blockPlacingBlockedDuringCombat) - .placeholder("{Y}", String.valueOf(this.config.blockPlacement.blockPlacementYCoordinate)) - .placeholder("{MODE}", this.config.blockPlacement.blockPlacementModeDisplayName) - .send(); - - } - } - - private boolean isPlacementBlocked(int level) { - return this.config.blockPlacement.blockPlacementMode == BlockPlacementSettings.BlockPlacingMode.ABOVE - ? level > this.config.blockPlacement.blockPlacementYCoordinate - : level < this.config.blockPlacement.blockPlacementYCoordinate; - } - - @EventHandler - void onToggleGlide(EntityToggleGlideEvent event) { - if (!this.config.combat.disableElytraUsage) { - return; - } - - if (!(event.getEntity() instanceof Player player)) { - return; - } - UUID uniqueId = player.getUniqueId(); - - if (!this.fightManager.isInCombat(uniqueId)) { - return; - } - - if (event.isGliding()) { - event.setCancelled(true); - player.setGliding(false); - } - } - - @EventHandler - void onMoveWhileGliding(PlayerMoveEvent event) { - if (!this.config.combat.disableElytraUsage) { - return; - } - - Player player = event.getPlayer(); - UUID uniqueId = player.getUniqueId(); - - if (!this.fightManager.isInCombat(uniqueId)) { - return; - } - - if (player.isGliding()) { - player.setGliding(false); - - player.setFallDistance(0f); - player.setVelocity(player.getVelocity().setY(-1)); - } - } - - @EventHandler - void onFly(PlayerToggleFlightEvent event) { - if (!this.config.combat.disableFlying) { - return; - } - - Player player = event.getPlayer(); - UUID uniqueId = player.getUniqueId(); - - if (!this.fightManager.isInCombat(uniqueId)) { - return; - } - - if (event.isFlying()) { - player.setAllowFlight(false); - - event.setCancelled(true); - } - } - - @EventHandler - void onTag(com.eternalcode.combat.fight.event.FightTagEvent event) { - UUID uniqueId = event.getPlayer(); - Player player = this.server.getPlayer(uniqueId); - - if (player == null) { - return; - } - - if (this.config.combat.disableFlying) { - GameMode gameMode = player.getGameMode(); - - if (gameMode != GameMode.CREATIVE && gameMode != GameMode.SPECTATOR) { - player.setAllowFlight(false); - player.setFlying(false); - } - } - - if (this.config.combat.unequipElytraOnCombat) { - ItemStack chest = player.getInventory().getChestplate(); - - if (chest != null && chest.getType() == Material.ELYTRA) { - removeChestplateIfElytra(player); - - this.noticeService.create() - .player(uniqueId) - .notice(this.config.messagesSettings.elytraDisabledDuringCombat) - .send(); - } - } - } - - @EventHandler - void onUnTag(FightUntagEvent event) { - if (!this.config.combat.disableFlying) { - return; - } - - UUID uniqueId = event.getPlayer(); - Player player = this.server.getPlayer(uniqueId); - - if (player == null) { - return; - } - GameMode playerGameMode = player.getGameMode(); - if (playerGameMode == GameMode.CREATIVE || playerGameMode == GameMode.SPECTATOR) { - player.setAllowFlight(true); - } - } - - @EventHandler - void onDamage(EntityDamageEvent event) { - if (!this.config.combat.disableElytraOnDamage) { - return; - } - - if (!(event.getEntity() instanceof Player player)) { - return; - } - UUID uniqueId = player.getUniqueId(); - - if (this.fightManager.isInCombat(uniqueId) && player.isGliding()) { - player.setGliding(false); - } - } - - @EventHandler - void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) { - Player player = event.getPlayer(); - UUID playerUniqueId = player.getUniqueId(); - - if (!this.fightManager.isInCombat(playerUniqueId)) { - return; - } - - String command = event.getMessage().substring(1); - - boolean isAnyMatch = this.config.commands.restrictedCommands.stream() - .anyMatch(restrictedCommand -> StringUtil.startsWithIgnoreCase(command, restrictedCommand)); - - WhitelistBlacklistMode mode = this.config.commands.commandRestrictionMode; - - boolean shouldCancel = mode.shouldBlock(isAnyMatch); - - if (shouldCancel) { - event.setCancelled(true); - this.noticeService.create() - .player(playerUniqueId) - .notice(this.config.messagesSettings.commandDisabledDuringCombat) - .send(); - - } - } - - @EventHandler - void onInventoryClick(InventoryClickEvent event) { - if (!(event.getWhoClicked() instanceof Player player)) { - return; - } - - if (!this.config.combat.unequipElytraOnCombat) { - return; - } - - UUID uniqueId = player.getUniqueId(); - - if (!this.fightManager.isInCombat(uniqueId)) { - return; - } - - if (event.getCurrentItem() == null) { - return; - } - - if (event.getCurrentItem().getType() == Material.ELYTRA) { - event.setCancelled(true); - - this.noticeService.create() - .player(uniqueId) - .notice(this.config.messagesSettings.elytraDisabledDuringCombat) - .send(); - } - } - - private void removeChestplateIfElytra(Player player) { - ItemStack chestplate = player.getInventory().getChestplate(); - - if (chestplate != null && chestplate.getType() == Material.ELYTRA) { - player.getInventory().setChestplate(null); - - HashMap leftover = player.getInventory().addItem(chestplate); - if (!leftover.isEmpty()) { - leftover.values().forEach(item -> - player.getWorld().dropItemNaturally(player.getLocation(), item) - ); - } - } - } -}