Skip to content

Commit

Permalink
improve elytrafly speed detection
Browse files Browse the repository at this point in the history
  • Loading branch information
xGinko committed Jan 20, 2024
1 parent 0df880d commit cc60943
Show file tree
Hide file tree
Showing 19 changed files with 494 additions and 489 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ public class AnarchyExploitFixes extends JavaPlugin {
private static Logger logger;
private static boolean isServerFolia, foundProtocolLib;

public final HashSet<UUID> NEW_CHUNK_PLAYERS = new HashSet<>();

@Override
public void onEnable() {
instance = this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,11 @@ static void reloadModules() {
/*
Elytra
*/
modules.add(new ElytraHelper());
modules.add(new ElytraAtSpawn());
modules.add(new ElytraGlobal());
modules.add(new ElytraOnCeiling());
modules.add(new NewChunksListener());
modules.add(new ElytraPacketFly());
modules.add(new ElytraTimer());
/*
Illegals
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.ItemSpawnEvent;
import org.bukkit.event.world.ChunkLoadEvent;

import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -84,6 +85,28 @@ public void disable() {
if (scheduledTask != null) scheduledTask.cancel();
}

@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onChunkLoad(ChunkLoadEvent event) {
if (event.isNewChunk()) return;

int droppedItemCount = 0;

for (Entity entity : event.getChunk().getEntities()) {
if (entity.getType() != EntityType.DROPPED_ITEM) continue;

droppedItemCount++;
if (droppedItemCount <= maxDroppedItemsPerChunk) continue;
if (usingWhitelist && whitelistedItems.contains(((Item) entity).getItemStack().getType())) continue;

entity.getScheduler().run(plugin, kill -> {
entity.remove();
if (logIsEnabled) LogUtil.moduleLog(Level.INFO, name(), "Removed dropped item at"
+ " x:" + entity.getLocation().getX() + " y:" + entity.getLocation().getY() + " z:" + entity.getLocation().getZ()
+ " in world " + entity.getWorld().getName() + ", because reached limit of " + maxDroppedItemsPerChunk);
}, null);
}
}

@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onItemDrop(ItemSpawnEvent event) {
int droppedItemCount = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
public class ElytraAtSpawn implements AnarchyExploitFixesModule, Listener {

private final AnarchyExploitFixes plugin;
private static final double tolerance = 0.02;
private final int spawn_Radius;
private final double spawn_SpeedOldChunks, spawn_SpeedNewChunks, spawn_DenyElytraTPS;
private final boolean teleportBack, spawn_shouldCheckPermission, spawn_DenyElytra, spawn_DenyOnLowTPS,
Expand Down Expand Up @@ -77,12 +76,13 @@ public void disable() {
private void onPlayerMove(PlayerMoveEvent event) {
Player player = event.getPlayer();
if (!player.isGliding()) return;
if (!event.hasExplicitlyChangedPosition()) return;
if (spawn_shouldCheckPermission && player.hasPermission("anarchyexploitfixes.bypass")) return;
Location playerLoc = player.getLocation();
if (LocationUtil.getFlatDistanceTo00(playerLoc) > spawn_Radius) return;

if (spawn_DenyElytra) {
if (teleportBack) player.teleportAsync(event.getFrom());
if (teleportBack) player.teleportAsync(ElytraHelper.getInstance().getFrom(event));
else event.setCancelled(true);

if (playNotifSound) player.playSound(player.getEyeLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0F, 1.0F);
Expand All @@ -93,7 +93,7 @@ private void onPlayerMove(PlayerMoveEvent event) {
}

if (spawn_DenyOnLowTPS && AnarchyExploitFixes.getTpsCache().getTPS(event) <= spawn_DenyElytraTPS) {
if (teleportBack) player.teleportAsync(event.getFrom());
if (teleportBack) player.teleportAsync(ElytraHelper.getInstance().getFrom(event));
else event.setCancelled(true);

if (playNotifSound) player.playSound(player.getEyeLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0F, 1.0F);
Expand All @@ -116,12 +116,12 @@ private void onPlayerMove(PlayerMoveEvent event) {
return;
}

double flySpeed = LocationUtil.getFlatDistance(event.getFrom(), event.getTo());
double flySpeed = ElytraHelper.getInstance().getBlocksPerTick(event);

if (plugin.NEW_CHUNK_PLAYERS.contains(player.getUniqueId())) {
if (ElytraHelper.getInstance().isInNewChunks(player.getUniqueId())) {
// Speed New Chunks
if (flySpeed > spawn_SpeedNewChunks+tolerance) {
if (teleportBack) player.teleportAsync(event.getFrom());
if (flySpeed > spawn_SpeedNewChunks + ElytraHelper.SPEED_TOLERANCE) {
if (teleportBack) player.teleportAsync(ElytraHelper.getInstance().getFrom(event));
else event.setCancelled(true);

if (playNotifSound) player.playSound(player.getEyeLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0F, 1.0F);
Expand Down Expand Up @@ -162,8 +162,8 @@ private void onPlayerMove(PlayerMoveEvent event) {
}
} else {
// Speed Old Chunks
if (flySpeed > spawn_SpeedOldChunks+tolerance) {
if (teleportBack) player.teleportAsync(event.getFrom());
if (flySpeed > spawn_SpeedOldChunks + ElytraHelper.SPEED_TOLERANCE) {
if (teleportBack) player.teleportAsync(ElytraHelper.getInstance().getFrom(event));
else event.setCancelled(true);

if (playNotifSound) player.playSound(player.getEyeLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0F, 1.0F);
Expand Down Expand Up @@ -204,4 +204,6 @@ private void onPlayerMove(PlayerMoveEvent event) {
}
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
public class ElytraGlobal implements AnarchyExploitFixesModule, Listener {

private final AnarchyExploitFixes plugin;
private static final double tolerance = 0.02;
private final int spawn_Radius;
private final double global_SpeedOldChunks, global_SpeedNewChunks, global_BurstSpeedOldChunks,
global_BurstSpeedNewChunks, global_BurstOldChunk_TPS, global_BurstNewChunk_TPS, global_DenyElytraTPS;
Expand Down Expand Up @@ -87,22 +86,23 @@ public void disable() {
private void onPlayerMove(PlayerMoveEvent event) {
Player player = event.getPlayer();
if (!player.isGliding()) return;
if (!event.hasExplicitlyChangedPosition()) return;
if (global_shouldCheckPermission && player.hasPermission("anarchyexploitfixes.bypass")) return;
Location playerLoc = player.getLocation();
if (spawn_SettingsEnabled && LocationUtil.getFlatDistanceTo00(playerLoc) <= spawn_Radius) return;
if (ceiling_SettingsEnabled && LocationUtil.isNetherCeiling(playerLoc)) return;

if (global_DenyElytra) {
if (teleportBack) player.teleportAsync(event.getFrom());
if (teleportBack) player.teleportAsync(ElytraHelper.getInstance().getFrom(event));
else event.setCancelled(true);

if (playNotifSound) player.playSound(player.getEyeLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0F, 1.0F);
if (showActionbarMsgs) player.sendActionBar(AnarchyExploitFixes.getLang(player.locale()).elytra_global_DisabledHere);
return;
}

if (global_DenyOnLowTPS && AnarchyExploitFixes.getTpsCache().getTPS(event.getTo()) <= global_DenyElytraTPS) {
if (teleportBack) player.teleportAsync(event.getFrom());
if (global_DenyOnLowTPS && AnarchyExploitFixes.getTpsCache().getTPS(event) <= global_DenyElytraTPS) {
if (teleportBack) player.teleportAsync(ElytraHelper.getInstance().getFrom(event));
else event.setCancelled(true);

if (playNotifSound) player.playSound(player.getEyeLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0F, 1.0F);
Expand All @@ -125,14 +125,14 @@ private void onPlayerMove(PlayerMoveEvent event) {
return;
}

double flySpeed = LocationUtil.getFlatDistance(event.getFrom(), event.getTo());
double flySpeed = ElytraHelper.getInstance().getBlocksPerTick(event);

if (plugin.NEW_CHUNK_PLAYERS.contains(player.getUniqueId())) {
if (ElytraHelper.getInstance().isInNewChunks(player.getUniqueId())) {
// Speed New Chunks
if (global_EnableBursting && AnarchyExploitFixes.getTpsCache().getTPS(event.getTo()) >= global_BurstNewChunk_TPS) {
if (global_EnableBursting && AnarchyExploitFixes.getTpsCache().getTPS(event) >= global_BurstNewChunk_TPS) {
// Burst Speed New Chunks
if (flySpeed > global_BurstSpeedNewChunks+tolerance) {
if (teleportBack) player.teleportAsync(event.getFrom());
if (flySpeed > global_BurstSpeedNewChunks + ElytraHelper.SPEED_TOLERANCE) {
if (teleportBack) player.teleportAsync(ElytraHelper.getInstance().getFrom(event));
else event.setCancelled(true);

if (playNotifSound) player.playSound(player.getEyeLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0F, 1.0F);
Expand Down Expand Up @@ -170,9 +170,9 @@ private void onPlayerMove(PlayerMoveEvent event) {
}
} else {
// Normal Speed New Chunks
if (flySpeed > global_SpeedNewChunks+tolerance) {
if (flySpeed > global_SpeedNewChunks + ElytraHelper.SPEED_TOLERANCE) {
// too fast
if (teleportBack) player.teleportAsync(event.getFrom());
if (teleportBack) player.teleportAsync(ElytraHelper.getInstance().getFrom(event));
else event.setCancelled(true);

if (playNotifSound) player.playSound(player.getEyeLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0F, 1.0F);
Expand Down Expand Up @@ -215,10 +215,10 @@ private void onPlayerMove(PlayerMoveEvent event) {
}
} else {
// Speed Old Chunks
if (global_EnableBursting && AnarchyExploitFixes.getTpsCache().getTPS(event.getTo()) >= global_BurstOldChunk_TPS) {
if (global_EnableBursting && AnarchyExploitFixes.getTpsCache().getTPS(event) >= global_BurstOldChunk_TPS) {
// Burst Speed Old Chunks
if (flySpeed > global_BurstSpeedOldChunks+tolerance) {
if (teleportBack) player.teleportAsync(event.getFrom());
if (flySpeed > global_BurstSpeedOldChunks + ElytraHelper.SPEED_TOLERANCE) {
if (teleportBack) player.teleportAsync(ElytraHelper.getInstance().getFrom(event));
else event.setCancelled(true);

if (playNotifSound) player.playSound(player.getEyeLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0F, 1.0F);
Expand Down Expand Up @@ -256,8 +256,8 @@ private void onPlayerMove(PlayerMoveEvent event) {
}
} else {
// Normal Speed Old Chunks
if (flySpeed > global_SpeedOldChunks+tolerance) {
if (teleportBack) player.teleportAsync(event.getFrom());
if (flySpeed > global_SpeedOldChunks + ElytraHelper.SPEED_TOLERANCE) {
if (teleportBack) player.teleportAsync(ElytraHelper.getInstance().getFrom(event));
else event.setCancelled(true);

if (playNotifSound) player.playSound(player.getEyeLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0F, 1.0F);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package me.moomoo.anarchyexploitfixes.modules.elytra;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import me.moomoo.anarchyexploitfixes.AnarchyExploitFixes;
import me.moomoo.anarchyexploitfixes.config.Config;
import me.moomoo.anarchyexploitfixes.modules.AnarchyExploitFixesModule;
import me.moomoo.anarchyexploitfixes.utils.LocationUtil;
import org.apache.commons.math3.util.FastMath;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.world.ChunkLoadEvent;

import java.time.Duration;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

public class ElytraHelper implements AnarchyExploitFixesModule, Listener {
private static ElytraHelper instance;
private final AnarchyExploitFixes plugin;
private final Set<UUID> PLAYERS_NEAR_NEW_CHUNKS;
private final Cache<UUID, Double> PLAYER_SPEEDS_MOVE_EVENT;
private final Cache<UUID, Double> PLAYER_SPEEDS_INTERVAL;
private final Cache<UUID, Location> LAST_GLIDE_POS;
public static final double SPEED_TOLERANCE = 0.02;
private final int checkIntervalTicks;
private final boolean doIntervalCheck;

public ElytraHelper() {
instance = this;
this.plugin = AnarchyExploitFixes.getInstance();
this.PLAYERS_NEAR_NEW_CHUNKS = new HashSet<>(plugin.getServer().getOnlinePlayers().size());
Config config = AnarchyExploitFixes.getConfiguration();
this.doIntervalCheck = config.getBoolean("elytra.patch-generic-speedhacks.enable", true,
"Patches speed-limit bypass using generic speedhacks (Timer) by additionally checking player position every x ticks");
final int tickInterval = config.getInt("elytra.patch-generic-speedhacks.check-interval-in-ticks", 10,
"Lower value means more accuracy but also more overhead.");
this.checkIntervalTicks = tickInterval > 0 ? tickInterval : 1;
final Duration cacheTime = Duration.ofMillis(FastMath.max(1000, tickInterval * 50L));
this.PLAYER_SPEEDS_MOVE_EVENT = Caffeine.newBuilder().expireAfterWrite(cacheTime).build();
this.PLAYER_SPEEDS_INTERVAL = Caffeine.newBuilder().expireAfterWrite(cacheTime).build();
this.LAST_GLIDE_POS = Caffeine.newBuilder().expireAfterWrite(cacheTime).build();
}

public static ElytraHelper getInstance() {
return instance;
}

@Override
public String name() {
return "elytra-helper";
}

@Override
public String category() {
return "elytra";
}

@Override
public void enable() {
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}

@Override
public boolean shouldEnable() {
Config config = AnarchyExploitFixes.getConfiguration();
return config.getBoolean("elytra.elytra-speed.Global-Settings.enable", true)
|| config.getBoolean("elytra.elytra-speed.At-Spawn.enable", false)
|| config.getBoolean("elytra.elytra-speed.Nether-Ceiling.enable", true);
}

@Override
public void disable() {
HandlerList.unregisterAll(this);
}

@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onPlayerJoin(PlayerJoinEvent event) {
if (!doIntervalCheck) return;

Player player = event.getPlayer();
player.getScheduler().runAtFixedRate(plugin, watchSpeed -> {
if (player.isGliding()) {
Location currentLocation = player.getLocation();
Location lastLocation = LAST_GLIDE_POS.getIfPresent(player.getUniqueId());
if (lastLocation == null)
lastLocation = currentLocation;
PLAYER_SPEEDS_INTERVAL.put(player.getUniqueId(), LocationUtil.getFlatDistance(lastLocation, currentLocation) / checkIntervalTicks);
LAST_GLIDE_POS.put(player.getUniqueId(), currentLocation);
}
}, null, checkIntervalTicks, checkIntervalTicks);
}

@EventHandler(priority = EventPriority.LOW)
private void onPlayerMove(PlayerMoveEvent event) {
if (event.getPlayer().isGliding()) {
PLAYER_SPEEDS_MOVE_EVENT.put(event.getPlayer().getUniqueId(), LocationUtil.getFlatDistance(event.getFrom(), event.getTo()));
}
}

private double getFlatDistanceInChunks(Chunk chunk, Location location) {
return FastMath.hypot(chunk.getX() - location.getBlockX() >> 4, chunk.getZ() - location.getBlockZ() >> 4);
}

@EventHandler(priority = EventPriority.LOW)
private void onChunkLoad(ChunkLoadEvent event) {
for (Player player : event.getWorld().getPlayers()) {
if (this.getFlatDistanceInChunks(event.getChunk(), player.getLocation()) < player.getViewDistance()) {
if (event.isNewChunk()) {
PLAYERS_NEAR_NEW_CHUNKS.add(player.getUniqueId());
} else {
PLAYERS_NEAR_NEW_CHUNKS.remove(player.getUniqueId());
}
}
}
}

public Location getFrom(PlayerMoveEvent event) {
final Location lastGlidePos = LAST_GLIDE_POS.getIfPresent(event.getPlayer().getUniqueId());
return lastGlidePos != null ? lastGlidePos : event.getFrom();
}

public double getBlocksPerTick(PlayerMoveEvent event) {
final Double speedInterval = PLAYER_SPEEDS_INTERVAL.getIfPresent(event.getPlayer().getUniqueId());
final Double speedMoveEvent = PLAYER_SPEEDS_MOVE_EVENT.getIfPresent(event.getPlayer().getUniqueId());

if (speedInterval != null) {
if (speedMoveEvent != null) {
return FastMath.max(speedInterval, speedMoveEvent);
} else {
return speedInterval;
}
} else {
if (speedMoveEvent != null) {
return speedMoveEvent;
} else {
return LocationUtil.getFlatDistance(event.getFrom(), event.getTo());
}
}
}

public boolean isInNewChunks(UUID playerUniqueId) {
return PLAYERS_NEAR_NEW_CHUNKS.contains(playerUniqueId);
}
}
Loading

0 comments on commit cc60943

Please sign in to comment.