fix(combat): patch elytra escape exploit#374
fix(combat): patch elytra escape exploit#374MassiveLag wants to merge 4 commits intoEternalCodeTeam:masterfrom
Conversation
…k 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
…n 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
There was a problem hiding this comment.
Code Review
This pull request introduces a feature to forcefully unequip and disable Elytras during combat, alongside a significant overhaul of the knockback system. The new knockback logic includes smarter direction calculation, velocity dampening, and an optional asynchronous teleport fallback to ensure players are moved out of restricted regions. My feedback focuses on critical stability and performance issues: the new timer-based fallback task risks memory and resource leaks by capturing player objects and lacking a timeout, the ground detection loop should be optimized to reduce object allocation, and the inventory click logic needs refinement to prevent bypasses while allowing non-equipping interactions.
| 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)); |
There was a problem hiding this comment.
The timer task created here lacks a maximum duration or iteration limit. If a player remains inside the region and continues to move (maintaining velocity > 0.02), the task will run indefinitely every 100ms. This can lead to a significant resource leak and performance degradation over time. Consider adding a timeout or a maximum number of attempts after which the task cancels itself and removes the player from fallbackActive.
| taskRef[0] = scheduler.timer(() -> { | ||
|
|
||
| Location check = player.getLocation(); |
There was a problem hiding this comment.
Capturing a Player object directly in a long-running timer task can lead to memory leaks if the player disconnects, as the task will hold a reference to the player object, preventing it from being garbage collected. It is safer to store the UUID and retrieve the player using Bukkit.getPlayer(uuid) inside the task, checking if they are still online (player != null) before proceeding.
| if (event.getCurrentItem().getType() == Material.ELYTRA) { | ||
| event.setCancelled(true); | ||
|
|
||
| this.noticeService.create() | ||
| .player(uniqueId) | ||
| .notice(this.config.messagesSettings.elytraDisabledDuringCombat) | ||
| .send(); | ||
| } |
There was a problem hiding this comment.
This logic blocks any interaction with an Elytra item in the inventory while in combat, which prevents players from organizing their inventory, moving the item to a chest, or dropping it. Furthermore, it is easily bypassed (e.g., by dragging the item into the armor slot or using hotbar swap keys on an empty slot). To properly prevent equipping, you should check if the destination slot is the chestplate slot (event.getSlotType() == InventoryType.SlotType.ARMOR) or handle the specific actions that lead to equipping (like InventoryDragEvent and PlayerInteractEvent).
There was a problem hiding this comment.
That is not true, because i check if the item is an elytra. so i only cancel then.
| 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(); |
There was a problem hiding this comment.
Using check.clone().add(...) inside a loop that can potentially iterate hundreds of times (from world height down to min height) creates a large number of short-lived Location objects. This puts unnecessary pressure on the Garbage Collector. You can use world.getBlockAt() with coordinate offsets to avoid object allocation.
| 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(); | |
| Material type = check.getBlock().getType(); | |
| Material above = check.getWorld().getBlockAt(check.getBlockX(), check.getBlockY() + 1, check.getBlockZ()).getType(); | |
| Material above2 = check.getWorld().getBlockAt(check.getBlockX(), check.getBlockY() + 2, check.getBlockZ()).getType(); |
name: Pull Request
about: Submit a contribution to EternalCombat
title: 'GH-{NUMBER} fix(combat): prevent elytra escape exploit'
labels: []
assignees: []
📝 Description
Fixed an issue where players could abuse elytra (e.g. cliff jumping, trident launching, or mid-air tagging) to escape combat and bypass restrictions.
This update introduces a complete elytra restriction system during combat, including glide prevention, forced glide stop, and automatic unequip on combat tag. It also blocks all known re-equip methods and improves overall movement handling.
🎯 Type of Change
Test Environment:
📸 Screenshots (if applicable)
https://share.veloraplugins.online/javaw_T0EYKFFnTq.gif