Skip to content

Commit

Permalink
elytra speed detection new concept init commit
Browse files Browse the repository at this point in the history
  • Loading branch information
xGinko committed Mar 15, 2024
1 parent 9252db7 commit 9faf5e6
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ private void onPlayerMove(PlayerMoveEvent event) {

double flySpeed = ElytraHelper.getInstance().getBlocksPerTick(event);

if (ElytraHelper.getInstance().isInNewChunks(player.getUniqueId())) {
if (ElytraHelper.getInstance().isInNewChunks(player)) {
// Speed New Chunks
if (flySpeed > spawn_SpeedNewChunks + ElytraHelper.SPEED_TOLERANCE) {
if (teleportBack) player.teleportAsync(ElytraHelper.getInstance().getFrom(event));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ private void onPlayerMove(PlayerMoveEvent event) {

double flySpeed = ElytraHelper.getInstance().getBlocksPerTick(event);

if (ElytraHelper.getInstance().isInNewChunks(player.getUniqueId())) {
if (ElytraHelper.getInstance().isInNewChunks(player)) {
// Speed New Chunks
if (global_EnableBursting && AnarchyExploitFixes.getTickData().getTPS() >= global_BurstNewChunk_TPS) {
// Burst Speed New Chunks
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,65 +2,42 @@

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerPosition;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerPositionAndRotation;
import me.moomoo.anarchyexploitfixes.AnarchyExploitFixes;
import me.moomoo.anarchyexploitfixes.config.Config;
import me.moomoo.anarchyexploitfixes.modules.AEFModule;
import me.moomoo.anarchyexploitfixes.utils.LocationUtil;
import org.bukkit.Chunk;
import org.apache.commons.math3.util.FastMath;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
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.entity.EntityToggleGlideEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.util.NumberConversions;
import oshi.util.tuples.Pair;

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

public class ElytraHelper implements AEFModule, Listener {
public class ElytraHelper extends PacketListenerAbstract implements AEFModule, Listener {

private static ElytraHelper instance;
public static final double SPEED_TOLERANCE = 0.02;

private final AnarchyExploitFixes plugin;
private final boolean calculate3D;

private final Set<UUID> PLAYERS_GLIDING, PLAYERS_NEAR_NEW_CHUNKS;
private ScheduledTask scheduledSpeedCheck;
private final Cache<UUID, Double> PLAYER_SPEEDS;
private final Cache<UUID, Location> LAST_GLIDE_POS;
private final int checkIntervalTicks;
private final boolean doIntervalCheck;
private final Cache<UUID, Double> speeds;
private final Cache<UUID, Pair<Vector3d, Long>> last_player_pos;

public ElytraHelper() {
instance = this;
this.plugin = AnarchyExploitFixes.getInstance();
this.calculate3D = AnarchyExploitFixes.getConfiguration().elytra_calculate_3D;
this.PLAYERS_GLIDING = plugin.getServer().getOnlinePlayers().stream()
.filter(LivingEntity::isGliding)
.map(Player::getUniqueId)
.collect(Collectors.toCollection(HashSet::new));
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 = Math.max(tickInterval, 1);
final Duration cacheTime = Duration.ofMillis(Math.max(1000, (tickInterval * 50L) + 100L));
this.PLAYER_SPEEDS = Caffeine.newBuilder().expireAfterWrite(cacheTime).build();
this.LAST_GLIDE_POS = Caffeine.newBuilder().expireAfterWrite(cacheTime).build();
this.speeds = Caffeine.newBuilder().expireAfterWrite(Duration.ofSeconds(8)).build();
this.last_player_pos = Caffeine.newBuilder().expireAfterWrite(Duration.ofSeconds(8)).build();
}

public static ElytraHelper getInstance() {
Expand All @@ -80,8 +57,6 @@ public String category() {
@Override
public void enable() {
plugin.getServer().getPluginManager().registerEvents(this, plugin);
this.scheduledSpeedCheck = plugin.getServer().getGlobalRegionScheduler().runAtFixedRate(plugin,
scheduleChecks -> this.run(), checkIntervalTicks, checkIntervalTicks);
}

@Override
Expand All @@ -93,91 +68,55 @@ public boolean shouldEnable() {
@Override
public void disable() {
HandlerList.unregisterAll(this);
if (scheduledSpeedCheck != null) scheduledSpeedCheck.cancel();
}

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) {
double eventSpeed = calculate3D ? LocationUtil.getRelDistance3D(event.getTo(), event.getFrom()) : LocationUtil.getRelDistance2D(event.getTo(), event.getFrom());
if (doIntervalCheck) {
Double speedInterval = PLAYER_SPEEDS.getIfPresent(event.getPlayer().getUniqueId());
if (speedInterval != null) return Math.max(speedInterval, eventSpeed);
@Override
public void onPacketReceive(PacketReceiveEvent event) {
final Vector3d position;
if (event.getPacketType() == PacketType.Play.Client.PLAYER_POSITION) {
position = new WrapperPlayClientPlayerPosition(event).getPosition();
} else if (event.getPacketType() == PacketType.Play.Client.PLAYER_POSITION_AND_ROTATION) {
position = new WrapperPlayClientPlayerPositionAndRotation(event).getPosition();
} else {
return;
}
return eventSpeed;
}

public boolean isInNewChunks(UUID playerUniqueId) {
return PLAYERS_NEAR_NEW_CHUNKS.contains(playerUniqueId);
}
final UUID uuid = event.getUser().getUUID();
final Pair<Vector3d, Long> toPos = new Pair<>(position, System.currentTimeMillis());
final Pair<Vector3d, Long> fromPos = last_player_pos.get(uuid, k -> toPos);

public boolean isGliding(Player player) {
return PLAYERS_GLIDING.contains(player.getUniqueId()) || player.isGliding();
}

private void run() {
for (Player player : plugin.getServer().getOnlinePlayers()) {
final UUID uuid = player.getUniqueId();
player.getScheduler().run(plugin, checkFlight -> {
if (isGliding(player)) {
Location currentLocation = player.getLocation().clone();
Location lastLocation = this.LAST_GLIDE_POS.getIfPresent(uuid);
if (lastLocation != null) this.PLAYER_SPEEDS.put(uuid,
(calculate3D ? LocationUtil.getRelDistance3D(lastLocation, currentLocation)
: LocationUtil.getRelDistance2D(lastLocation, currentLocation)) / checkIntervalTicks);
this.LAST_GLIDE_POS.put(uuid, currentLocation);
}
}, () -> this.LAST_GLIDE_POS.invalidate(uuid));
double blocksPerMilli;
if (calculate3D) {
blocksPerMilli = LocationUtil.getDistance3D(
position.x - fromPos.getA().x,
position.y - fromPos.getA().y,
position.z - fromPos.getA().z);
} else {
blocksPerMilli = FastMath.hypot(
position.x - fromPos.getA().x,
position.z - fromPos.getA().z);
}
}

@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
private void onGlideToggle(EntityToggleGlideEvent event) {
if (event.getEntityType() == EntityType.PLAYER) {
if (event.isGliding()) {
this.PLAYERS_GLIDING.add(event.getEntity().getUniqueId());
} else {
this.PLAYERS_GLIDING.remove(event.getEntity().getUniqueId());
}
}
blocksPerMilli = blocksPerMilli / FastMath.abs(toPos.getB() - fromPos.getB());

speeds.put(uuid, blocksPerMilli * 50);
}

@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
private void onQuit(PlayerQuitEvent event) {
this.PLAYERS_GLIDING.remove(event.getPlayer().getUniqueId());
public Location getFrom(PlayerMoveEvent event) {
return event.getFrom();
}

@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
private void onJoin(PlayerJoinEvent event) {
if (event.getPlayer().isGliding()) {
this.PLAYERS_GLIDING.add(event.getPlayer().getUniqueId());
} else {
this.PLAYERS_GLIDING.remove(event.getPlayer().getUniqueId());
}
public double getBlocksPerTick(PlayerMoveEvent event) {
return speeds.get(event.getPlayer().getUniqueId(),
k -> calculate3D ? LocationUtil.getRelDistance3D(event.getTo(), event.getFrom()) :
LocationUtil.getRelDistance2D(event.getTo(), event.getFrom()));
}

@EventHandler(priority = EventPriority.LOWEST)
private void onChunkLoad(ChunkLoadEvent event) {
for (Player player : event.getWorld().getPlayers()) {
if (this.getChunkDistanceSquared(event.getChunk(), player.getLocation().clone()) < NumberConversions.square(player.getViewDistance())) {
if (event.isNewChunk()) {
this.PLAYERS_NEAR_NEW_CHUNKS.add(player.getUniqueId());
} else {
this.PLAYERS_NEAR_NEW_CHUNKS.remove(player.getUniqueId());
}
}
}
public boolean isInNewChunks(Player player) {
return player.getChunk().getInhabitedTime() > 200L;
}

/**
* Since the distance here is only used to see whether a chunk is loaded roughly within the player's view distance,
* we can resort to comparing squared distances.
* This saves cpu usage as we don't have to use {@link Math#sqrt(double)} to get the accurate distance in chunks.
*/
private double getChunkDistanceSquared(Chunk chunk, Location location) {
return NumberConversions.square(chunk.getX() - location.getBlockX() >> 4) + NumberConversions.square(chunk.getZ() - location.getBlockZ() >> 4);
public boolean isGliding(Player player) {
return player.isGliding();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ private void onPlayerMove(PlayerMoveEvent event) {

double flySpeed = ElytraHelper.getInstance().getBlocksPerTick(event);

if (ElytraHelper.getInstance().isInNewChunks(player.getUniqueId())) {
if (ElytraHelper.getInstance().isInNewChunks(player)) {
// Speed New Chunks
if (ceiling_EnableBursting && AnarchyExploitFixes.getTickData().getTPS() >= ceiling_BurstNewChunk_TPS) {
// Burst Speed New Chunks
Expand Down

0 comments on commit 9faf5e6

Please sign in to comment.