Skip to content

fix(combat): patch elytra escape exploit#374

Open
MassiveLag wants to merge 4 commits intoEternalCodeTeam:masterfrom
MassiveLag:elytra-exploit
Open

fix(combat): patch elytra escape exploit#374
MassiveLag wants to merge 4 commits intoEternalCodeTeam:masterfrom
MassiveLag:elytra-exploit

Conversation

@MassiveLag
Copy link
Copy Markdown
Contributor


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

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 🔧 Refactoring (no functional changes, just code improvements)
  • 🎨 Style/Cosmetic (formatting, whitespace, visual changes)
  • 📚 Documentation (updates to documentation or README)
  • 🤖 CI/CD/Build (changes to workflows, gradle, etc.)

Test Environment:

  • Minecraft Version: 1.21x
  • Java Version: 21
  • Platform: Linux

📸 Screenshots (if applicable)

https://share.veloraplugins.online/javaw_T0EYKFFnTq.gif

…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
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +129 to +165
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));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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.

Comment on lines +129 to +131
taskRef[0] = scheduler.timer(() -> {

Location check = player.getLocation();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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.

Comment on lines +275 to +282
if (event.getCurrentItem().getType() == Material.ELYTRA) {
event.setCancelled(true);

this.noticeService.create()
.player(uniqueId)
.notice(this.config.messagesSettings.elytraDisabledDuringCombat)
.send();
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is not true, because i check if the item is an elytra. so i only cancel then.

Comment on lines +186 to +188
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();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Suggested change
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();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant