-
-
Notifications
You must be signed in to change notification settings - Fork 25
Add vanish flight option #1371
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add vanish flight option #1371
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| package com.eternalcode.core.feature.vanish.controller; | ||
|
|
||
| import com.eternalcode.core.feature.vanish.VanishService; | ||
| 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.HashMap; | ||
| import java.util.Iterator; | ||
| import java.util.Map; | ||
| import java.util.UUID; | ||
| import org.bukkit.Server; | ||
| import org.bukkit.entity.Player; | ||
| import org.bukkit.event.EventHandler; | ||
| import org.bukkit.event.Listener; | ||
|
|
||
| @Controller | ||
| class VanishFlightController implements Listener { | ||
|
|
||
| private final Map<UUID, FlightState> previousFlightStates = new HashMap<>(); | ||
|
|
||
| private final VanishSettings settings; | ||
| private final Server server; | ||
|
|
||
| @Inject | ||
| VanishFlightController(VanishSettings settings, Server server) { | ||
| this.settings = settings; | ||
| this.server = server; | ||
| } | ||
|
|
||
| @EventHandler(ignoreCancelled = true) | ||
| void onEnable(EnableVanishEvent event) { | ||
| if (!this.settings.allowFlight()) { | ||
| return; | ||
| } | ||
|
|
||
| Player player = event.getPlayer(); | ||
| UUID playerId = player.getUniqueId(); | ||
|
|
||
| this.previousFlightStates.putIfAbsent( | ||
| playerId, | ||
| new FlightState(player.getAllowFlight(), player.isFlying()) | ||
| ); | ||
|
|
||
| player.setAllowFlight(true); | ||
| player.setFlying(true); | ||
| } | ||
|
|
||
| @EventHandler(ignoreCancelled = true) | ||
| void onDisable(DisableVanishEvent event) { | ||
| this.restoreFlightState(event.getPlayer()); | ||
| } | ||
|
|
||
| void synchronize(VanishService vanishService) { | ||
| Iterator<Map.Entry<UUID, FlightState>> iterator = this.previousFlightStates.entrySet().iterator(); | ||
|
|
||
| while (iterator.hasNext()) { | ||
| Map.Entry<UUID, FlightState> entry = iterator.next(); | ||
| UUID playerId = entry.getKey(); | ||
| Player player = this.server.getPlayer(playerId); | ||
|
|
||
| if (player == null || !player.isOnline()) { | ||
| iterator.remove(); | ||
| continue; | ||
| } | ||
|
|
||
| if (!this.settings.allowFlight() || !vanishService.isVanished(playerId)) { | ||
| entry.getValue().apply(player); | ||
| iterator.remove(); | ||
| continue; | ||
| } | ||
|
|
||
| if (!player.getAllowFlight()) { | ||
| player.setAllowFlight(true); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private void restoreFlightState(Player player) { | ||
| FlightState previousFlightState = this.previousFlightStates.remove(player.getUniqueId()); | ||
| if (previousFlightState == null) { | ||
| return; | ||
| } | ||
|
|
||
| previousFlightState.apply(player); | ||
| } | ||
|
|
||
| private record FlightState(boolean allowFlight, boolean flying) { | ||
|
|
||
| private void apply(Player player) { | ||
| if (this.allowFlight) { | ||
| player.setAllowFlight(true); | ||
| player.setFlying(this.flying); | ||
| return; | ||
| } | ||
|
|
||
| player.setFlying(false); | ||
| player.setAllowFlight(false); | ||
| } | ||
| } | ||
| } | ||
|
Comment on lines
+1
to
+102
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a critical bug/exploit in the current implementation: when a vanished player disconnects, they are removed from Additionally, if a player joins the server already vanished (e.g., silent join or persistent vanish), they won't be added to To fix these issues, we should:
package com.eternalcode.core.feature.vanish.controller;
import com.eternalcode.core.feature.vanish.VanishService;
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.HashMap;
import java.util.Iterator;
import java.util.Map;
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.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
@Controller
class VanishFlightController implements Listener {
private final Map<UUID, FlightState> previousFlightStates = new HashMap<>();
private final VanishSettings settings;
private final Server server;
private final VanishService vanishService;
@Inject
VanishFlightController(VanishSettings settings, Server server, VanishService vanishService) {
this.settings = settings;
this.server = server;
this.vanishService = vanishService;
}
@EventHandler(ignoreCancelled = true)
void onEnable(EnableVanishEvent event) {
if (!this.settings.allowFlight()) {
return;
}
Player player = event.getPlayer();
this.enableFlight(player);
}
@EventHandler(ignoreCancelled = true)
void onDisable(DisableVanishEvent event) {
this.restoreFlightState(event.getPlayer());
}
@EventHandler
void onQuit(PlayerQuitEvent event) {
this.restoreFlightState(event.getPlayer());
}
@EventHandler
void onJoin(PlayerJoinEvent event) {
if (!this.settings.allowFlight()) {
return;
}
Player player = event.getPlayer();
if (this.vanishService.isVanished(player)) {
this.enableFlight(player);
}
}
void synchronize() {
for (UUID playerId : this.vanishService.getVanishedPlayers()) {
Player player = this.server.getPlayer(playerId);
if (player == null || !player.isOnline()) {
continue;
}
if (this.settings.allowFlight()) {
this.previousFlightStates.putIfAbsent(
playerId,
new FlightState(player.getAllowFlight(), player.isFlying())
);
if (!player.getAllowFlight()) {
player.setAllowFlight(true);
}
}
}
Iterator<Map.Entry<UUID, FlightState>> iterator = this.previousFlightStates.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<UUID, FlightState> entry = iterator.next();
UUID playerId = entry.getKey();
Player player = this.server.getPlayer(playerId);
if (player == null || !player.isOnline()) {
iterator.remove();
continue;
}
if (!this.settings.allowFlight() || !this.vanishService.isVanished(playerId)) {
entry.getValue().apply(player);
iterator.remove();
}
}
}
private void enableFlight(Player player) {
this.previousFlightStates.putIfAbsent(
player.getUniqueId(),
new FlightState(player.getAllowFlight(), player.isFlying())
);
player.setAllowFlight(true);
player.setFlying(true);
}
private void restoreFlightState(Player player) {
FlightState previousFlightState = this.previousFlightStates.remove(player.getUniqueId());
if (previousFlightState == null) {
return;
}
previousFlightState.apply(player);
}
private record FlightState(boolean allowFlight, boolean flying) {
private void apply(Player player) {
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
return;
}
if (this.allowFlight) {
player.setAllowFlight(true);
player.setFlying(this.flying);
return;
}
player.setFlying(false);
player.setAllowFlight(false);
}
}
} |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package com.eternalcode.core.feature.vanish.controller; | ||
|
|
||
| import com.eternalcode.core.feature.vanish.VanishService; | ||
| import com.eternalcode.core.injector.annotations.Inject; | ||
| import com.eternalcode.core.injector.annotations.component.Task; | ||
| import java.util.concurrent.TimeUnit; | ||
|
|
||
| @Task(period = 5, delay = 5, unit = TimeUnit.SECONDS) | ||
| class VanishFlightWatchdogTask implements Runnable { | ||
|
|
||
| private final VanishService vanishService; | ||
| private final VanishFlightController vanishFlightController; | ||
|
|
||
| @Inject | ||
| VanishFlightWatchdogTask(VanishService vanishService, VanishFlightController vanishFlightController) { | ||
| this.vanishService = vanishService; | ||
| this.vanishFlightController = vanishFlightController; | ||
| } | ||
|
|
||
| @Override | ||
| public void run() { | ||
| this.vanishFlightController.synchronize(this.vanishService); | ||
| } | ||
| } | ||
|
Comment on lines
+1
to
+24
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since package com.eternalcode.core.feature.vanish.controller;
import com.eternalcode.core.injector.annotations.Inject;
import com.eternalcode.core.injector.annotations.component.Task;
import java.util.concurrent.TimeUnit;
@Task(period = 5, delay = 5, unit = TimeUnit.SECONDS)
class VanishFlightWatchdogTask implements Runnable {
private final VanishFlightController vanishFlightController;
@Inject
VanishFlightWatchdogTask(VanishFlightController vanishFlightController) {
this.vanishFlightController = vanishFlightController;
}
@Override
public void run() {
this.vanishFlightController.synchronize();
}
} |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If a player enters vanish from survival and is later switched to creative/spectator while still vanished, this restores the pre-vanish
allowFlight=falsesnapshot when vanish ends, leaving them in the new game mode with flight disabled. Since gamemode changes are not rolled back by this controller, the stale flight snapshot should not blindly clear flight when the current game mode or another feature has legitimately granted it during vanish.Useful? React with 👍 / 👎.