Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ public class VanishConfig extends OkaeriConfig implements VanishSettings {
@Comment("Should vanished players be invulnerable to damage from other players")
public boolean godMode = true;

@Comment("Allow vanished players to fly")
public boolean flyMode = true;

@Comment("Give night vision effect to vanished players")
public boolean nightVision = true;

Expand Down Expand Up @@ -45,5 +48,4 @@ public class VanishConfig extends OkaeriConfig implements VanishSettings {

@Comment("Prevent vanished players from placing blocks")
public boolean blockBlockPlacing = false;

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public interface VanishSettings {

boolean godMode();

boolean flyMode();

boolean nightVision();

boolean silentInventoryAccess();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.eternalcode.core.feature.vanish.controller;

import com.eternalcode.core.feature.vanish.VanishSettings;
import com.eternalcode.core.feature.vanish.event.DisableVanishEvent;
import com.eternalcode.core.feature.vanish.event.EnableVanishEvent;
import com.eternalcode.core.injector.annotations.Inject;
import com.eternalcode.core.injector.annotations.component.Controller;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.GameMode;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;

@Controller
class FlyController implements Listener {

private final VanishSettings settings;
private final Map<UUID, FlightState> previousFlightStates = new ConcurrentHashMap<>();
Copy link
Contributor

Choose a reason for hiding this comment

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

move to separate class?


@Inject
FlyController(VanishSettings settings) {
this.settings = settings;
}

@EventHandler(ignoreCancelled = true)
void onEnable(EnableVanishEvent event) {
if (!this.settings.flyMode()) {
return;
}

Player player = event.getPlayer();

this.previousFlightStates.computeIfAbsent(
player.getUniqueId(),
id -> new FlightState(player.getAllowFlight(), player.isFlying())
);
Comment on lines +36 to +39
Copy link
Contributor

Choose a reason for hiding this comment

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

high

To fix a potential flight exploit, it's necessary to also store the player's GameMode when they enter vanish. This will be used when they exit vanish to determine if their flight status should be restored.

You will also need to update the FlightState record on line 68 to include a GameMode field:

private record FlightState(boolean hadAllowFlight, boolean wasFlying, GameMode gameMode) {}
Suggested change
this.previousFlightStates.computeIfAbsent(
player.getUniqueId(),
id -> new FlightState(player.getAllowFlight(), player.isFlying())
);
this.previousFlightStates.computeIfAbsent(
player.getUniqueId(),
id -> new FlightState(player.getAllowFlight(), player.isFlying(), player.getGameMode())
);


player.setAllowFlight(true);
player.setFlying(true);
}

@EventHandler(ignoreCancelled = true)
void onDisable(DisableVanishEvent event) {
Player player = event.getPlayer();

if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
this.previousFlightStates.remove(player.getUniqueId());
return;
}

FlightState state = this.previousFlightStates.remove(player.getUniqueId());
if (state == null) {
return;
}

player.setAllowFlight(state.hadAllowFlight());
player.setFlying(state.wasFlying());
}
Comment on lines +45 to +61
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

The current logic allows for a flight exploit where a player can enable vanish in creative mode, switch to survival, and retain flight after disabling vanish. By using the GameMode stored when vanish was enabled (as per my other comment), you can prevent this. The new logic should check if the player has switched from a gamemode that allows flight to one that doesn't, and if so, explicitly disable flight.

    @EventHandler(ignoreCancelled = true)
    void onDisable(DisableVanishEvent event) {
        Player player = event.getPlayer();

        FlightState state = this.previousFlightStates.remove(player.getUniqueId());
        if (state == null) {
            return;
        }

        GameMode currentMode = player.getGameMode();
        if (currentMode == GameMode.CREATIVE || currentMode == GameMode.SPECTATOR) {
            return;
        }

        GameMode originalMode = state.gameMode();
        if (originalMode == GameMode.CREATIVE || originalMode == GameMode.SPECTATOR) {
            player.setAllowFlight(false);
            player.setFlying(false);
            return;
        }

        player.setAllowFlight(state.hadAllowFlight());
        player.setFlying(state.wasFlying());
    }


@EventHandler
void onQuit(PlayerQuitEvent event) {
this.previousFlightStates.remove(event.getPlayer().getUniqueId());
}

private record FlightState(boolean hadAllowFlight, boolean wasFlying) {
}
}