New Speed check and lots of new stuff implemented

This commit is contained in:
Dawson
2022-08-09 17:03:15 -04:00
parent 281f716f20
commit d9b35af788
55 changed files with 2950 additions and 90 deletions
+13 -1
View File
@@ -58,7 +58,13 @@
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>false</minimizeJar>
<minimizeJar>true</minimizeJar>
<relocations>
<relocation>
<pattern>com.google.common</pattern>
<shadedPattern>dev.brighten.ac.com.google.common</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
@@ -103,6 +109,12 @@
<version>8.5.6</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
+78 -11
View File
@@ -2,28 +2,35 @@ package dev.brighten.ac;
import co.aikar.commands.BaseCommand;
import co.aikar.commands.BukkitCommandManager;
import dev.brighten.ac.check.CheckManager;
import dev.brighten.ac.data.PlayerRegistry;
import dev.brighten.ac.handler.PacketHandler;
import dev.brighten.ac.handler.keepalive.KeepaliveProcessor;
import dev.brighten.ac.packet.handler.HandlerAbstract;
import dev.brighten.ac.packet.listener.PacketProcessor;
import dev.brighten.ac.utils.*;
import dev.brighten.ac.utils.math.RollingAverageDouble;
import dev.brighten.ac.utils.objects.RemoteClassLoader;
import dev.brighten.ac.utils.reflections.Reflections;
import dev.brighten.ac.utils.reflections.types.WrappedClass;
import dev.brighten.ac.utils.reflections.types.WrappedField;
import dev.brighten.ac.utils.reflections.types.WrappedMethod;
import dev.brighten.ac.utils.timer.Timer;
import dev.brighten.ac.utils.timer.impl.TickTimer;
import lombok.Getter;
import lombok.experimental.PackagePrivate;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Set;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
@Getter
@Init
@@ -34,7 +41,18 @@ public class Anticheat extends JavaPlugin {
private ScheduledExecutorService scheduler;
private PacketProcessor packetProcessor;
private BukkitCommandManager commandManager;
private int currentTicks;
private CheckManager checkManager;
private PlayerRegistry playerRegistry;
private KeepaliveProcessor keepaliveProcessor;
private PacketHandler packetHandler;
private int currentTick;
private Deque<Runnable> onTickEnd = new LinkedList<>();
private ServerInjector injector;
//Lag Information
private Timer lastTickLag;
private long lastTick;
@PackagePrivate
private RollingAverageDouble tps = new RollingAverageDouble(4, 20);
@ConfigSetting(path = "logging", name = "verbose")
private static boolean verboseLogging = true;
@@ -48,23 +66,43 @@ public class Anticheat extends JavaPlugin {
commandManager = new BukkitCommandManager(this);
this.checkManager = new CheckManager();
this.playerRegistry = new PlayerRegistry();
this.keepaliveProcessor = new KeepaliveProcessor();
this.packetHandler = new PacketHandler();
injector = new ServerInjector();
try {
injector.inject();
} catch (Exception e) {
e.printStackTrace();
}
initializeScanner(getClass(), this,
null,
true,
true);
new BukkitRunnable() {
public void run() {
currentTicks++;
}
}.runTaskTimer(this, 1L, 1L);
}
public void onDisable() {
scheduler.shutdown();
commandManager.unregisterCommands();
// Unregistering packet listeners for players
Bukkit.getOnlinePlayers().forEach(HandlerAbstract.getHandler()::remove);
HandlerList.unregisterAll(this);
packetProcessor.shutdown();
checkManager = null;
// Unregistering APlayer objects
playerRegistry.unregisterAll();
playerRegistry = null;
try {
injector.eject();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void initializeScanner(Class<? extends Plugin> mainClass, Plugin plugin, ClassLoader loader,
@@ -193,4 +231,33 @@ public class Anticheat extends JavaPlugin {
else MiscUtils.printToConsole(log);
}
}
public double getTps() {
return this.tps.getAverage();
}
public void runTpsTask() {
lastTickLag = new TickTimer();
AtomicInteger ticks = new AtomicInteger();
AtomicLong lastTimeStamp = new AtomicLong(0);
RunUtils.taskTimer(() -> {
ticks.getAndIncrement();
currentTick++;
long currentTime = System.currentTimeMillis();
if(currentTime - lastTick > 120) {
lastTickLag.reset();
}
if(ticks.get() >= 10) {
ticks.set(0);
tps.add(500D / (currentTime - lastTimeStamp.get()) * 20);
lastTimeStamp.set(currentTime);
}
lastTick = currentTime;
}, this, 1L, 1L);
}
public void onTickEnd(Runnable runnable) {
onTickEnd.add(runnable);
}
}
@@ -0,0 +1,8 @@
package dev.brighten.ac.check;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Action {
}
@@ -0,0 +1,95 @@
package dev.brighten.ac.check;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.utils.Color;
import dev.brighten.ac.utils.MathUtils;
import dev.brighten.ac.utils.MiscUtils;
import dev.brighten.ac.utils.timer.Timer;
import dev.brighten.ac.utils.timer.impl.MillisTimer;
import lombok.Getter;
import lombok.val;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.TextComponent;
import java.util.ArrayList;
import java.util.List;
@Getter
public abstract class Check {
private final APlayer player;
private final CheckData checkData;
private int vl;
private long lastFlagRun;
private Timer lastAlert = new MillisTimer();
public Check(APlayer player) {
this.player = player;
this.checkData = getClass().getAnnotation(CheckData.class);
}
private String formatAlert(String toFormat, String info) {
return addPlaceHolders(Color.translate(toFormat.replace("%desc%", String.join("\n",
MiscUtils
.splitIntoLine("", 20))))
.replace("%info%", info));
}
private String addPlaceHolders(String string) {
return string.replace("%player%", player.getBukkitPlayer().getName())
.replace("%check%", checkData.name())
.replace("%name%", player.getBukkitPlayer().getName())
.replace("%vl%", String.valueOf(MathUtils.round(vl, 1)));
}
public void flag(boolean devAlerts, int resetVLTime, String information, Object... variables) {
vl++;
Anticheat.INSTANCE.getScheduler().execute(() -> {
if(Anticheat.INSTANCE.getTps() < 18)
vl = 0;
if(System.currentTimeMillis() - lastFlagRun < 50L) return;
lastFlagRun = System.currentTimeMillis();
final String finalInformation = String.format(information, variables);
boolean dev = devAlerts || Anticheat.INSTANCE.getTps() < 18;
final String info = finalInformation
.replace("%p", String.valueOf(player.getLagInfo().getTransPing()))
.replace("%t", String.valueOf(MathUtils.round(Anticheat.INSTANCE.getTps(), 2)));
//if(vl > 0) Anticheat.INSTANCE.loggerManager.addLog(player, this, info);
if (true) {
//Sending Discord webhook alert
List<TextComponent> components = new ArrayList<>();
if(dev) {
components.add(new TextComponent(createTxt("&8[&cDev&8] ")));
}
val text = createTxt("&8[&4!&8] &f%player% &7flagged &f%check%" +
" &8(&ex%vl%&8)", info);
text.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new TextComponent[] {
createTxt("&eDescription&8: &f%desc%" +
"\n&eInfo: &f%info%\n&r\n&7&oClick to teleport to player.", info)}));
components.add(text);
TextComponent[] toSend = components.toArray(new TextComponent[0]);
player.getBukkitPlayer().spigot().sendMessage(toSend);
lastAlert.reset();
}
});
}
private TextComponent createTxt(String txt) {
return createTxt(txt, "");
}
private TextComponent createTxt(String txt, String info) {
return new TextComponent(TextComponent.fromLegacyText(Color.translate(formatAlert(txt, info))));
}
}
@@ -0,0 +1,10 @@
package dev.brighten.ac.check;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckData {
String name();
CheckType type();
}
@@ -0,0 +1,58 @@
package dev.brighten.ac.check;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.utils.ClassScanner;
import dev.brighten.ac.utils.Tuple;
import dev.brighten.ac.utils.reflections.types.WrappedClass;
import dev.brighten.ac.utils.reflections.types.WrappedMethod;
import lombok.Getter;
import java.util.*;
@Getter
public class CheckManager {
private final List<CheckStatic> checkClasses = new ArrayList<>();
private final Map<Tuple<String, Class<?>>, WrappedMethod[]> events = new HashMap<>();
public CheckManager() {
for (WrappedClass aClass : ClassScanner.getClasses(CheckData.class,
"dev.brighten.ac.check.impl")) {
addCheck(aClass);
}
}
public void addCheck(WrappedClass checkClass) {
CheckStatic check = new CheckStatic(checkClass);
if(!check.getCheckClass().isAnnotationPresent(CheckData.class)) {
return;
}
CheckData checkData = check.getCheckClass().getAnnotation(CheckData.class);
Anticheat.INSTANCE.alog(true, "&7Adding check to CheckManager: " + checkData.name());
synchronized (events) {
// Loop through all the methods in Check that contain @Action annotation by Class
check.getEvents().forEach((actionClass, methods) -> {
// Check if array is already cached for Class and return Array if so, if not create new Array
events.compute(new Tuple<String, Class<?>>(checkData.name(), actionClass), (packetClass, array) -> {
if(array == null) array = new WrappedMethod[0];
// Adding preexisting cached WrappedMethod into List for further additions
List<WrappedMethod> methodList = new ArrayList<>(Arrays.asList(array));
// Adding all precached Check-specific WrappedMethod into global cache of WrappedMethods.
methodList.addAll(methods);
System.out.println("Registering " + packetClass.toString());
// Returning newly created array for use in detections.
return methodList.toArray(new WrappedMethod[0]);
});
});
}
checkClasses.add(check);
}
}
@@ -0,0 +1,51 @@
package dev.brighten.ac.check;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.WPacket;
import dev.brighten.ac.utils.reflections.types.WrappedClass;
import dev.brighten.ac.utils.reflections.types.WrappedConstructor;
import dev.brighten.ac.utils.reflections.types.WrappedMethod;
import lombok.Getter;
import net.minecraft.server.v1_8_R3.Packet;
import org.bukkit.event.Event;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class CheckStatic {
@Getter
private final WrappedClass checkClass;
private WrappedConstructor initConst;
@Getter
private final Map<Class<?>, List<WrappedMethod>> events = new HashMap<>();
public CheckStatic(WrappedClass checkClass) {
this.checkClass = checkClass;
processClass();
}
private void processClass() {
initConst = checkClass.getConstructor(APlayer.class);
for (WrappedMethod method : checkClass.getDeclaredMethods()) {
if(!method.isAnnotationPresent(Action.class)
|| method.getParameters().length == 0) continue;
Class<?> type = method.getParameterTypes()[0];
if(Packet.class.isAssignableFrom(type)
|| WPacket.class.isAssignableFrom(type) || Event.class.isAssignableFrom(type)) {
events.compute(type, (key, list) -> {
if(list == null) list = new ArrayList<>();
list.add(method);
return list;
});
}
}
}
public Check playerInit(APlayer player) {
return initConst.newInstance(player);
}
}
@@ -0,0 +1,9 @@
package dev.brighten.ac.check;
public enum CheckType {
COMBAT,
MOVEMENT,
BLOCK,
INTERACT,
ORDER
}
@@ -0,0 +1,21 @@
package dev.brighten.ac.check.impl.join;
import dev.brighten.ac.check.Action;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.CheckType;
import dev.brighten.ac.data.APlayer;
import org.bukkit.Bukkit;
import org.bukkit.event.player.PlayerJoinEvent;
@CheckData(name = "Join", type = CheckType.ORDER)
public class JoinCheck extends Check {
public JoinCheck(APlayer player) {
super(player);
}
@Action
public void onJoin(PlayerJoinEvent event) {
Bukkit.broadcastMessage("Player joined server: " + getPlayer().getBukkitPlayer().getName());
}
}
@@ -0,0 +1,272 @@
package dev.brighten.ac.check.impl.speed;
import dev.brighten.ac.check.Action;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.CheckType;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import dev.brighten.ac.utils.BlockUtils;
import dev.brighten.ac.utils.MathHelper;
import org.bukkit.block.Block;
import org.bukkit.craftbukkit.v1_8_R3.util.CraftMagicNumbers;
import org.bukkit.potion.PotionEffectType;
@CheckData(name = "Speed", type = CheckType.MOVEMENT)
public class Speed extends Check {
private boolean lastLastClientGround;
private float buffer;
private static boolean[] TRUE_FALSE = new boolean[]{true, false};
private static float[] VALUES = new float[]{-0.98f, 0f, 0.98f};
public Speed(APlayer player) {
super(player);
}
@Action
public void onFlying(WPacketPlayInFlying packet) {
Block underBlock = BlockUtils.getBlock(getPlayer().getMovement().getTo().getLoc()
.toLocation(getPlayer().getBukkitPlayer().getWorld())
.subtract(0, 1, 0)),
lastUnderBlock = BlockUtils.getBlock(getPlayer().getMovement().getFrom().getLoc()
.toLocation(getPlayer().getBukkitPlayer().getWorld())
.subtract(0, 1, 0));
if (underBlock == null || lastUnderBlock == null) {
getPlayer().getBukkitPlayer().sendMessage("Null");
return;
}
float friction = CraftMagicNumbers.getBlock(underBlock).frictionFactor,
lfriction = CraftMagicNumbers.getBlock(lastUnderBlock).frictionFactor;
check:
{
if (!packet.isMoved()
/*|| getPlayer().playerInfo.generalCancel
|| getPlayer().playerInfo.onLadder
|| getPlayer().playerInfo.lastEntityCollision.isNotPassed(2)
|| getPlayer().playerInfo.lastVelocity.isNotPassed(1)*/
|| getPlayer().getBlockInformation().inLiquid
|| getPlayer().getBlockInformation().collidesHorizontally) {
break check;
}
double smallestDelta = Double.MAX_VALUE;
double pmotionx = 0, pmotionz = 0;
boolean onGround = getPlayer().getMovement().getFrom().isOnGround();
loop:
{
for (int f = -1; f < 2; f++) {
for (int s = -1; s < 2; s++) {
for (boolean sprinting : TRUE_FALSE) {
for (int fastMath = 0; fastMath <= 2; fastMath++) {
for (boolean attack : TRUE_FALSE) {
for (boolean using : TRUE_FALSE) {
for (boolean sneaking : TRUE_FALSE) {
for (boolean jumped : TRUE_FALSE) {
float forward = f, strafe = s;
if (sneaking) {
forward *= 0.3;
strafe *= 0.3;
}
if (using) {
forward *= 0.2;
strafe *= 0.2;
}
//Multiplying by 0.98 like in client
forward *= 0.9800000190734863F;
strafe *= 0.9800000190734863F;
double aiMoveSpeed = getPlayer().getBukkitPlayer().getWalkSpeed() / 2;
float drag = 0.91f;
double lmotionX = getPlayer().getMovement().getLDeltaX(),
lmotionZ = getPlayer().getMovement().getLDeltaZ();
//The "1" will effectively remove lastFriction from the equation
lmotionX *= (lastLastClientGround ? lfriction : 1) * 0.9100000262260437D;
lmotionZ *= (lastLastClientGround ? lfriction : 1) * 0.9100000262260437D;
//Running multiplication done after previous prediction
if (getPlayer().getPlayerVersion().isOrAbove(ProtocolVersion.V1_9)) {
if (Math.abs(lmotionX) < 0.003)
lmotionX = 0;
if (Math.abs(lmotionZ) < 0.003)
lmotionZ = 0;
} else {
if (Math.abs(lmotionX) < 0.005)
lmotionX = 0;
if (Math.abs(lmotionZ) < 0.005)
lmotionZ = 0;
}
// Attack slowdown
if (attack) {
lmotionX *= 0.6;
lmotionZ *= 0.6;
}
if (sprinting) aiMoveSpeed += aiMoveSpeed * 0.30000001192092896D;
if (getPlayer().getPotionHandler().hasPotionEffect(PotionEffectType.SPEED))
aiMoveSpeed += (getPlayer().getPotionHandler().getEffectByType(PotionEffectType.SPEED)
.get()
.getAmplifier() + 1) * (double) 0.20000000298023224D * aiMoveSpeed;
if (getPlayer().getPotionHandler().hasPotionEffect(PotionEffectType.SLOW))
aiMoveSpeed += (getPlayer().getPotionHandler().getEffectByType(PotionEffectType.SLOW)
.get()
.getAmplifier() + 1) * (double) -0.15000000596046448D * aiMoveSpeed;
float f5;
if (onGround) {
drag *= friction;
f5 = (float) (aiMoveSpeed * (0.16277136F / (drag * drag * drag)));
if (sprinting && jumped) {
float rot = getPlayer().getMovement().getTo().getLoc().yaw * 0.017453292F;
lmotionX -= sin(fastMath, rot) * 0.2F;
lmotionZ += cos(fastMath, rot) * 0.2F;
}
} else f5 = sprinting ? 0.025999999F : 0.02f;
if (getPlayer().getPlayerVersion().isOrAbove(ProtocolVersion.V1_9)) {
double keyedMotion = forward * forward + strafe * strafe;
if (keyedMotion >= 1.0E-4F) {
keyedMotion = f5 / Math.max(1.0, Math.sqrt(keyedMotion));
forward *= keyedMotion;
strafe *= keyedMotion;
final float yawSin = sin(fastMath,
getPlayer().getMovement().getTo().getLoc().yaw * (float) Math.PI / 180.F),
yawCos = cos(fastMath,
getPlayer().getMovement().getTo().getLoc().yaw * (float) Math.PI / 180.F);
lmotionX += (strafe * yawCos - forward * yawSin);
lmotionZ += (forward * yawCos + strafe * yawSin);
}
} else {
float keyedMotion = forward * forward + strafe * strafe;
if (keyedMotion >= 1.0E-4F) {
keyedMotion = f5 / Math.max(1.0f, MathHelper.sqrt_float(keyedMotion));
forward *= keyedMotion;
strafe *= keyedMotion;
final float yawSin = sin(fastMath,
getPlayer().getMovement().getTo().getLoc().yaw * (float) Math.PI / 180.F),
yawCos = cos(fastMath,
getPlayer().getMovement().getTo().getLoc().yaw * (float) Math.PI / 180.F);
lmotionX += (strafe * yawCos - forward * yawSin);
lmotionZ += (forward * yawCos + strafe * yawSin);
}
}
double diffX = getPlayer().getMovement().getDeltaX() - lmotionX,
diffZ = getPlayer().getMovement().getDeltaZ() - lmotionZ;
double delta = (diffX * diffX) + (diffZ * diffZ);
if (delta < smallestDelta) {
smallestDelta = delta;
pmotionx = lmotionX;
pmotionz = lmotionZ;
if (delta < 1E-15) {
break loop;
}
}
}
}
}
}
}
}
}
}
}
double pmotion = Math.hypot(pmotionx, pmotionz);
if (getPlayer().getMovement().getDeltaXZ() > pmotion && smallestDelta > 1E-4 && getPlayer().getMovement().getDeltaXZ() > 0.1) {
if (++buffer > 3) {
buffer = Math.min(3.5f, buffer); //Ensuring we don't have a run-away buffer
flag(false,0, "smallest=%s b=%.1f to=%s dxz=%.2f", smallestDelta, buffer, getPlayer().getMovement().getTo().getLoc(), getPlayer().getMovement().getDeltaXZ());
}
} else if (buffer > 0) buffer -= 0.1f;
//getPlayer().getBukkitPlayer().sendMessage(String.format("smallest=%s b=%.1f to=%s dxz=%.2f", smallestDelta, buffer, getPlayer().getMovement().getTo().getLoc(), getPlayer().getMovement().getDeltaXZ()));
//debug("smallest=%s b=%.1f", smallestDelta, buffer);
}
lastLastClientGround = getPlayer().getMovement().getFrom().isOnGround();
}
private static final float[] SIN_TABLE_FAST = new float[4096], SIN_TABLE_FAST_NEW = new float[4096];
private static final float[] SIN_TABLE = new float[65536];
private static final float radToIndex = roundToFloat(651.8986469044033D);
public static float sin(int type, float value) {
switch(type) {
case 0:
default: {
return SIN_TABLE[(int) (value * 10430.378F) & 65535];
}
case 1: {
return SIN_TABLE_FAST[(int) (value * 651.8986F) & 4095];
}
case 2: {
return SIN_TABLE_FAST_NEW[(int)(value * radToIndex) & 4095];
}
}
}
public static float cos(int type, float value) {
switch (type) {
case 0:
default:
return SIN_TABLE[(int) (value * 10430.378F + 16384.0F) & 65535];
case 1:
return SIN_TABLE_FAST[(int) ((value + ((float) Math.PI / 2F)) * 651.8986F) & 4095];
case 2:
return SIN_TABLE_FAST_NEW[(int)(value * radToIndex + 1024.0F) & 4095];
}
}
static {
for (int i = 0; i < 65536; ++i)
{
SIN_TABLE[i] = (float)Math.sin((double)i * Math.PI * 2.0D / 65536.0D);
}
for (int j = 0; j < 4096; ++j)
{
SIN_TABLE_FAST[j] = (float)Math.sin((double)(((float)j + 0.5F) / 4096.0F * ((float)Math.PI * 2F)));
}
for (int l = 0; l < 360; l += 90)
{
SIN_TABLE_FAST[(int)((float)l * 11.377778F) & 4095] = (float)Math.sin((double)((float)l * 0.017453292F));
}
for (int j = 0; j < SIN_TABLE_FAST_NEW.length; ++j)
{
SIN_TABLE_FAST_NEW[j] = roundToFloat(Math.sin((double)j * Math.PI * 2.0D / 4096.0D));
}
}
private static float roundToFloat(double d)
{
return (float)((double)Math.round(d * 1.0E8D) / 1.0E8D);
}
}
@@ -0,0 +1,155 @@
package dev.brighten.ac.data;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckStatic;
import dev.brighten.ac.data.handlers.BlockInformation;
import dev.brighten.ac.data.handlers.GeneralInformation;
import dev.brighten.ac.data.handlers.LagInformation;
import dev.brighten.ac.data.handlers.MovementHandler;
import dev.brighten.ac.data.obj.InstantAction;
import dev.brighten.ac.data.obj.NormalAction;
import dev.brighten.ac.handler.PotionHandler;
import dev.brighten.ac.handler.keepalive.KeepAlive;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.packet.handler.HandlerAbstract;
import dev.brighten.ac.utils.Tuple;
import dev.brighten.ac.utils.reflections.types.WrappedMethod;
import lombok.Getter;
import lombok.val;
import net.minecraft.server.v1_8_R3.PacketPlayOutTransaction;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Consumer;
public class APlayer {
@Getter
private final Player bukkitPlayer;
private final List<Check> checks = new ArrayList<>();
@Getter
private MovementHandler movement;
@Getter
private PotionHandler potionHandler;
@Getter
private GeneralInformation info;
@Getter
private LagInformation lagInfo;
@Getter
private BlockInformation blockInformation;
@Getter
private int playerTick;
@Getter
//TODO Actually grab real player version once finished implementing version grabber from Atlas
private ProtocolVersion playerVersion = ProtocolVersion.V1_8_9;
public final Map<Short, Tuple<InstantAction, Consumer<InstantAction>>> instantTransaction = new HashMap<>();
public final List<NormalAction> keepAliveStamps = new ArrayList<>();
public APlayer(Player player) {
this.bukkitPlayer = player;
load();
}
private void load() {
for (CheckStatic check : Anticheat.INSTANCE.getCheckManager().getCheckClasses()) {
checks.add(check.playerInit(this));
}
this.movement = new MovementHandler(this);
this.potionHandler = new PotionHandler(this);
this.info = new GeneralInformation();
this.lagInfo = new LagInformation();
this.blockInformation = new BlockInformation(this);
}
protected void unload() {
checks.clear();
this.info = null;
this.lagInfo = null;
this.movement = null;
}
public void callEvent(Event event) {
for (Check check : checks) {
WrappedMethod[] methods = Anticheat.INSTANCE.getCheckManager().getEvents()
.get(new Tuple<String, Class<?>>(check.getCheckData().name(), event.getClass()));
if(methods != null) {
for (WrappedMethod method : methods) {
method.invoke(check, event);
}
}
}
}
//TODO When using WPacket wrappers only, make this strictly WPacket param based only
public void callPacket(Object packet) {
for (Check check : checks) {
WrappedMethod[] methods = Anticheat.INSTANCE.getCheckManager().getEvents()
.get(new Tuple<String, Class<?>>(check.getCheckData().name(), packet.getClass()));
if(methods != null) {
for (WrappedMethod method :
methods) {
method.invoke(check, packet);
}
}
}
}
public int runKeepaliveAction(Consumer<KeepAlive> action) {
return runKeepaliveAction(action, 0);
}
public int runKeepaliveAction(Consumer<KeepAlive> action, int later) {
int id = Anticheat.INSTANCE.getKeepaliveProcessor().currentKeepalive.start + later;
keepAliveStamps.add(new NormalAction(id, action));
return id;
}
public void runInstantAction(Consumer<InstantAction> runnable) {
runInstantAction(runnable, false);
}
public void runInstantAction(Consumer<InstantAction> runnable, boolean flush) {
short startId = (short) ThreadLocalRandom.current().nextInt(Short.MIN_VALUE, Short.MAX_VALUE),
endId = (short)(startId + 1);
//Ensuring we don't have any duplicate IDS
val map = Anticheat.INSTANCE.getKeepaliveProcessor().keepAlives.asMap();
while (map.containsKey(startId)
|| map.containsKey(endId)) {
startId = (short) ThreadLocalRandom.current().nextInt(Short.MIN_VALUE, Short.MAX_VALUE);
endId = (short)(startId + (short)1);
}
InstantAction startAction = new InstantAction(startId, endId, false);
instantTransaction.put(startId, new Tuple<>(startAction, runnable));
HandlerAbstract.getHandler().sendPacket(this, new PacketPlayOutTransaction(0, startId, false));
short finalEndId = endId, finalStartId = startId;
Anticheat.INSTANCE.onTickEnd(() -> {
InstantAction endAction = new InstantAction(finalStartId, finalEndId, true);
instantTransaction.put(finalEndId, new Tuple<>(endAction, runnable));
HandlerAbstract.getHandler()
.sendPacket(this, new PacketPlayOutTransaction(0, finalEndId, false));
});
}
public void addPlayerTick() {
playerTick++;
}
}
@@ -0,0 +1,47 @@
package dev.brighten.ac.data;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.Optional;
import java.util.UUID;
public class PlayerRegistry {
public PlayerRegistry() {
Bukkit.getOnlinePlayers().forEach(this::generate);
}
public final Int2ObjectMap<APlayer> aplayerMap = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
public Optional<APlayer> getPlayer(UUID uuid) {
return Optional.of(aplayerMap.get(uuid.hashCode()));
}
public APlayer generate(Player player) {
if(aplayerMap.containsKey(player.getUniqueId().hashCode())) {
unregister(player.getUniqueId());
}
synchronized (aplayerMap) {
APlayer aplayer = new APlayer(player);
aplayerMap.put(player.getUniqueId().hashCode(), aplayer);
return aplayer;
}
}
public void unregister(UUID uuid) {
synchronized (aplayerMap) {
Optional.of(aplayerMap.remove(uuid.hashCode())).ifPresent(APlayer::unload);
}
}
public void unregisterAll() {
synchronized (aplayerMap) {
aplayerMap.forEach((key, val) -> val.unload());
aplayerMap.clear();
}
}
}
@@ -0,0 +1,368 @@
package dev.brighten.ac.data.handlers;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.utils.*;
import dev.brighten.ac.utils.world.BlockData;
import dev.brighten.ac.utils.world.CollisionBox;
import dev.brighten.ac.utils.world.EntityData;
import dev.brighten.ac.utils.world.types.SimpleCollisionBox;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
public class BlockInformation {
private APlayer player;
public boolean onClimbable, onSlab, onStairs, onHalfBlock, inLiquid, inLava, inWater, inWeb, onSlime, onIce,
onSoulSand, blocksAbove, collidesVertically, bedNear, collidesHorizontally, blocksNear, inBlock, miscNear,
collidedWithEntity, roseBush, inPortal, blocksBelow, pistonNear, fenceBelow, inScaffolding, inHoney;
public float currentFriction, fromFriction;
public CollisionHandler
handler = new CollisionHandler(new ArrayList<>(), new ArrayList<>(), new KLocation(0,0,0), null);
public final List<SimpleCollisionBox> aboveCollisions = Collections.synchronizedList(new ArrayList<>()),
belowCollisions = Collections.synchronizedList(new ArrayList<>());
public final List<Block> blocks = Collections.synchronizedList(new ArrayList<>());
private static EnumMap<Material, XMaterial> matchMaterial = new EnumMap<>(Material.class);
//Caching material
private final Material cobweb = XMaterial.COBWEB.parseMaterial(),
rosebush = XMaterial.ROSE_BUSH.parseMaterial(),
scaffolding = XMaterial.SCAFFOLDING.parseMaterial(),
honey = XMaterial.HONEY_BLOCK.parseMaterial();
static {
for (Material mat : Material.values()) {
matchMaterial.put(mat, XMaterial.matchXMaterial(mat));
}
}
public static XMaterial getXMaterial(Material material) {
return matchMaterial.getOrDefault(material, null);
}
public BlockInformation(APlayer objectData) {
this.player = objectData;
}
public void runCollisionCheck() {
if(!Anticheat.INSTANCE.isEnabled())
return;
double dy = player.getMovement().getDeltaY();
double dh = player.getMovement().getDeltaXZ();
blocks.clear();
player.getInfo().setServerGround(false);
player.getInfo().setNearGround(false);
onClimbable = fenceBelow
= inScaffolding = inHoney
= onSlab = onStairs = onHalfBlock = inLiquid = inLava = inWater = inWeb = onSlime = pistonNear
= onIce = onSoulSand = blocksAbove = collidesVertically = bedNear = collidesHorizontally =
blocksNear = inBlock = miscNear = collidedWithEntity = blocksBelow = inPortal = false;
if(dy > 10) dy = 10;
else if(dy < -10) dy = -10;
if(dh > 10) dh = 10;
int startX = Location.locToBlock(player.getMovement().getTo().getLoc().x - 1 - dh);
int endX = Location.locToBlock(player.getMovement().getTo().getLoc().x + 1 + dh);
int startY = Location.locToBlock(player.getMovement().getTo().getLoc().y - Math.max(0.6, 0.6 + Math.abs(dy)));
int endY = Location.locToBlock(player.getMovement().getTo().getLoc().y + Math.max(2.1, 2.1 + Math.abs(dy)));
int startZ = Location.locToBlock(player.getMovement().getTo().getLoc().z - 1 - dh);
int endZ = Location.locToBlock(player.getMovement().getTo().getLoc().z + 1 + dh);
SimpleCollisionBox waterBox = player.getMovement().getTo().getBox().copy().expand(0, -.38, 0);
waterBox.xMin = MathHelper.floor_double(waterBox.xMin);
waterBox.yMin = MathHelper.floor_double(waterBox.yMin);
waterBox.zMin = MathHelper.floor_double(waterBox.zMin);
waterBox.xMax = MathHelper.floor_double(waterBox.xMax + 1.);
waterBox.yMax = MathHelper.floor_double(waterBox.yMax + 1.);
waterBox.zMax = MathHelper.floor_double(waterBox.zMax + 1.);
SimpleCollisionBox lavaBox = player.getMovement().getTo().getBox().copy().expand(-.1f, -.4f, -.1f);
lavaBox.xMin = MathHelper.floor_double(waterBox.xMin);
lavaBox.yMin = MathHelper.floor_double(waterBox.yMin);
lavaBox.zMin = MathHelper.floor_double(waterBox.zMin);
lavaBox.xMax = MathHelper.floor_double(waterBox.xMax + 1.);
lavaBox.yMax = MathHelper.floor_double(waterBox.yMax + 1.);
lavaBox.zMax = MathHelper.floor_double(waterBox.zMax + 1.);
SimpleCollisionBox normalBox = player.getMovement().getTo().getBox().copy();
inWater = MiscUtils.isInMaterialBB(player.getBukkitPlayer().getWorld(), waterBox, Materials.WATER);
inLava = MiscUtils.isInMaterialBB(player.getBukkitPlayer().getWorld(), lavaBox, Materials.LAVA);
inLiquid = inWater || inLava;
player.getInfo().setWorldLoaded(true);
player.getInfo().setLastServerGround(player.getInfo().isServerGround());
synchronized (belowCollisions) {
belowCollisions.clear();
}
synchronized (aboveCollisions) {
aboveCollisions.clear();
}
final World world = player.getBukkitPlayer().getWorld();
int it = 9 * 9;
start:
for (int chunkx = startX >> 4; chunkx <= endX >> 4; ++chunkx) {
int cx = chunkx << 4;
for (int chunkz = startZ >> 4; chunkz <= endZ >> 4; ++chunkz) {
if (!world.isChunkLoaded(chunkx, chunkz)) {
player.getInfo().setWorldLoaded(false);
continue;
}
Chunk chunk = world.getChunkAt(chunkx, chunkz);
if (chunk != null) {
int cz = chunkz << 4;
int xstart = Math.max(startX, cx);
int xend = Math.min(endX, cx + 16);
int zstart = Math.max(startZ, cz);
int zend = Math.min(endZ, cz + 16);
for (int x = xstart; x <= xend; ++x) {
for (int z = zstart; z <= zend; ++z) {
for (int y = Math.max(-50, startY); y <= endY; ++y) {
if (it-- <= 0) {
break start;
}
if(y > 400 || y < -50) continue;
Block block = chunk.getBlock(x & 15, y, z & 15);
final Material type = block.getType();
if (type != Material.AIR) {
blocks.add(block);
CollisionBox blockBox = BlockData.getData(type)
.getBox(block, player.getPlayerVersion());
if(blockBox.isCollided(normalBox)) {
if(type.equals(cobweb))
inWeb = true;
else if(type.equals(scaffolding)) inScaffolding = true;
else if(type.equals(honey)) inHoney = true;
}
if(type.equals(rosebush))
roseBush = true;
if(normalBox.copy().offset(0, 0.6f, 0).isCollided(blockBox))
blocksAbove = true;
if(normalBox.copy().expand(1, -0.0001, 1).isIntersected(blockBox))
blocksNear = true;
if(normalBox.copy().expand(0.1, 0, 0.1)
.offset(0, 1,0).isCollided(blockBox)) {
synchronized (aboveCollisions) {
blockBox.downCast(aboveCollisions);
}
}
if(normalBox.copy().expand(0.1, 0, 0.1).offset(0, -1, 0)
.isCollided(blockBox)) {
synchronized (belowCollisions) {
blockBox.downCast(belowCollisions);
}
if(Materials.checkFlag(type, Materials.FENCE)
|| Materials.checkFlag(type, Materials.WALL)) {
fenceBelow = true;
}
}
if(Materials.checkFlag(type, Materials.SOLID)) {
SimpleCollisionBox groundBox = normalBox.copy()
.offset(0, -.49, 0).expandMax(0, -1.2, 0);
XMaterial blockMaterial = getXMaterial(type);
if(normalBox.copy().expand(0.4, 0, 0.4).expandMin(0, -1, 0)
.isIntersected(blockBox))
blocksBelow = true;
if(normalBox.isIntersected(blockBox)) inBlock = true;
SimpleCollisionBox box = player.getMovement().getTo().getBox().copy();
box.expand(Math.abs(player.getMovement().getDeltaX()) + 0.1, -0.001,
Math.abs(player.getMovement().getDeltaZ()) + 0.1);
if (blockBox.isCollided(box))
collidesHorizontally = true;
box = player.getMovement().getTo().getBox().copy();
box.expand(0, 0.1, 0);
if (blockBox.isCollided(box))
collidesVertically = true;
if(groundBox.copy().expandMin(0, -0.8, 0).expand(0.2, 0, 0.2)
.isIntersected(blockBox))
player.getInfo().setNearGround(true);
if(groundBox.isCollided(blockBox)) {
player.getInfo().setServerGround(true);
if(blockMaterial != null)
switch (blockMaterial) {
case ICE:
case BLUE_ICE:
case FROSTED_ICE:
case PACKED_ICE: {
onIce = true;
break;
}
case SOUL_SAND: {
onSoulSand = true;
break;
}
case SLIME_BLOCK: {
onSlime = true;
break;
}
}
}
if(player.getMovement().getDeltaY() > 0
&& player.getPlayerVersion().isBelow(ProtocolVersion.V1_14)
&& Materials.checkFlag(type, Materials.LADDER)
&& normalBox.copy().expand(0.2f, 0, 0.2f)
.isCollided(blockBox)) {
onClimbable = true;
}
if(blockMaterial != null) {
switch (blockMaterial) {
case PISTON:
case PISTON_HEAD:
case MOVING_PISTON:
case STICKY_PISTON: {
if(normalBox.copy().expand(0.5, 0.5, 0.5)
.isCollided(blockBox))
pistonNear = true;
break;
}
}
}
if(groundBox.copy().expand(0.5, 0.3, 0.5).isCollided(blockBox)) {
if(Materials.checkFlag(type, Materials.SLABS))
onSlab = true;
else
if(Materials.checkFlag(type, Materials.STAIRS))
onStairs = true;
else
if(blockMaterial != null)
switch(blockMaterial) {
case CAKE:
case BREWING_STAND:
case FLOWER_POT:
case PLAYER_HEAD:
case PLAYER_WALL_HEAD:
case SKELETON_SKULL:
case CREEPER_HEAD:
case DRAGON_HEAD:
case ZOMBIE_HEAD:
case ZOMBIE_WALL_HEAD:
case CREEPER_WALL_HEAD:
case DRAGON_WALL_HEAD:
case WITHER_SKELETON_SKULL:
case LANTERN:
case SKELETON_WALL_SKULL:
case WITHER_SKELETON_WALL_SKULL:
case SNOW: {
miscNear = true;
break;
}
case BLACK_BED:
case BLUE_BED:
case BROWN_BED:
case CYAN_BED:
case GRAY_BED:
case GREEN_BED:
case LIME_BED:
case MAGENTA_BED:
case ORANGE_BED:
case PINK_BED:
case PURPLE_BED:
case RED_BED:
case WHITE_BED:
case YELLOW_BED:
case LIGHT_BLUE_BED:
case LIGHT_GRAY_BED: {
bedNear = true;
break;
}
}
}
} else if(blockBox.isCollided(normalBox)) {
XMaterial blockMaterial = getXMaterial(type);
if(blockMaterial != null)
switch(blockMaterial) {
case END_PORTAL:
case NETHER_PORTAL: {
inPortal = true;
break;
}
}
}
}
}
}
}
}
}
}
if(!player.getInfo().isWorldLoaded())
return;
CollisionHandler handler = new CollisionHandler(blocks,
player.getInfo().getNearbyEntities(),
player.getMovement().getTo().getLoc(), player);
//Bukkit.broadcastMessage("chigga4");
for (Entity entity : handler.getEntities()) {
CollisionBox entityBox = EntityData.getEntityBox(entity.getLocation(), entity);
if(entityBox == null) continue;
if(entityBox.isCollided(normalBox.copy().offset(0, -.1, 0)))
player.getInfo().setServerGround(true);
if(entityBox.isCollided(normalBox))
collidedWithEntity = true;
}
//Bukkit.broadcastMessage("chigga5");
onHalfBlock = onSlab || onStairs || miscNear || bedNear;
if((player.getMovement().getDeltaY() <= 0 || player.getPlayerVersion().isOrAbove(ProtocolVersion.V1_14))
&& !onClimbable) {
onClimbable = player.getInfo().getBlockOnTo().isPresent()
&& BlockUtils.isClimbableBlock(player.getInfo().getBlockOnTo().get());
}
handler.setSize(0.6f, 1.8f);
handler.setOffset(0f);
this.handler.getEntities().clear();
this.handler = handler;
}
public SimpleCollisionBox getBox() {
return new SimpleCollisionBox(player.getMovement().getTo().getLoc().toVector(), player.getMovement().getTo().getLoc().toVector())
.expand(0.3, 0,0.3).expandMax(0, 1.8, 0);
}
}
@@ -0,0 +1,25 @@
package dev.brighten.ac.data.handlers;
import dev.brighten.ac.utils.PastLocation;
import dev.brighten.ac.utils.timer.Timer;
import dev.brighten.ac.utils.timer.impl.TickTimer;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@Getter
@Setter
public class GeneralInformation {
private Optional<Block> blockOnTo, blockBelow;
private Timer lastMove = new TickTimer();
private LivingEntity target;
private boolean serverGround, lastServerGround, nearGround, worldLoaded;
private List<Entity> nearbyEntities = Collections.emptyList();
private PastLocation targetPastLocation;
}
@@ -0,0 +1,14 @@
package dev.brighten.ac.data.handlers;
import dev.brighten.ac.utils.timer.Timer;
import dev.brighten.ac.utils.timer.impl.MillisTimer;
import dev.brighten.ac.utils.timer.impl.TickTimer;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class LagInformation {
private Timer lastPingDrop = new TickTimer(), lastClientTransaction = new MillisTimer();
private long transPing, lastTransPing;
}
@@ -0,0 +1,124 @@
package dev.brighten.ac.data.handlers;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.data.obj.CMove;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import dev.brighten.ac.utils.BlockUtils;
import dev.brighten.ac.utils.MathUtils;
import dev.brighten.ac.utils.objects.evicting.EvictingList;
import dev.brighten.ac.utils.world.types.SimpleCollisionBox;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.bukkit.Location;
import java.util.LinkedList;
@RequiredArgsConstructor
public class MovementHandler {
private final APlayer player;
@Getter
private final CMove to = new CMove(), from = new CMove();
@Getter
private double deltaX, deltaY, deltaZ, deltaXZ,
lDeltaX, lDeltaY, lDeltaZ, lDeltaXZ;
@Getter
private float deltaYaw, deltaPitch, lDeltaYaw, lDeltaPitch;
private int moveTicks;
private int teleportsToConfirm;
private LinkedList<Float> yawGcdList = new EvictingList<>(45),
pitchGcdList = new EvictingList<>(45);
public void process(WPacketPlayInFlying packet, long currentTime) {
player.getPotionHandler().onFlying(packet);
if(moveTicks > 0) {
updateLocations(packet);
// Updating block locations
player.getInfo().setBlockOnTo(BlockUtils
.getBlockAsync(to.getLoc().toLocation(player.getBukkitPlayer().getWorld())));
player.getInfo().setBlockBelow(BlockUtils
.getBlockAsync(to.getLoc().toLocation(player.getBukkitPlayer().getWorld())
.subtract(0,1,0)));
if(packet.isMoved()) {
// Updating player bounding box
player.getInfo().getLastMove().reset();
}
player.getBlockInformation().runCollisionCheck();
}
moveTicks++; //Must be end of code
}
/**
* Updating the "to" and "from" location to current location.
* Resetting position tracking; meant primarily for instant teleports.
* @param location Location
*/
public void moveTo(Location location) {
to.getLoc().x = from.getLoc().x = location.getX();
to.getLoc().y = from.getLoc().y = location.getY();
to.getLoc().z = from.getLoc().z = location.getZ();
to.getLoc().yaw = from.getLoc().yaw = location.getYaw();
to.getLoc().pitch = from.getLoc().pitch = location.getPitch();
deltaX = deltaY = deltaZ = deltaXZ
= lDeltaX = lDeltaY = lDeltaZ
= lDeltaXZ = 0;
deltaYaw = lDeltaYaw =
deltaPitch = lDeltaPitch = 0;
moveTicks = 0;
//doingTeleport = inventoryOpen = false;
}
/**
* Setting the to and from to current location only if the player either moved or looked.
* @param packet WPacketPlayInFlyingh
*/
private void setTo(WPacketPlayInFlying packet) {
to.setWorld(player.getBukkitPlayer().getWorld());
if(packet.isMoved()) {
to.getLoc().x = packet.getX();
to.getLoc().y = packet.getY();
to.getLoc().z = packet.getZ();
}
if(packet.isLooked()) {
to.getLoc().yaw = packet.getYaw();
to.getLoc().pitch = packet.getPitch();
}
to.setBox(new SimpleCollisionBox(to.getLoc(), 0.6, 1.8));
to.setOnGround(packet.isOnGround());
}
/**
* If from location is null, update to loc after to is set, otherwise, update to before from.
* Updates the location of player and its general delta movement.
* @param packet WPacketPlayInFlying
*/
private void updateLocations(WPacketPlayInFlying packet) {
from.setLoc(to);
setTo(packet);
lDeltaX = deltaX;
lDeltaY = deltaY;
lDeltaZ = deltaZ;
lDeltaXZ = deltaXZ;
lDeltaYaw = deltaYaw;
lDeltaPitch = deltaPitch;
deltaX = to.getLoc().x - from.getLoc().x;
deltaY = to.getLoc().y - from.getLoc().y;
deltaZ = to.getLoc().z - from.getLoc().z;
deltaXZ = Math.hypot(deltaX, deltaZ); // Calculating here to cache since hypot() can be heavy.
deltaYaw = MathUtils.getAngleDelta(to.getLoc().yaw, from.getLoc().yaw);
deltaPitch = to.getLoc().pitch - from.getLoc().pitch;
}
}
@@ -0,0 +1,22 @@
package dev.brighten.ac.data.obj;
import dev.brighten.ac.utils.KLocation;
import dev.brighten.ac.utils.world.types.SimpleCollisionBox;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.World;
@Getter
@Setter
public class CMove {
private KLocation loc = new KLocation(0,0,0,0,0);
private World world;
private SimpleCollisionBox box;
private boolean onGround;
public void setLoc(CMove move) {
this.loc = move.getLoc().clone();
this.world = move.getWorld();
this.box = move.getBox();
this.onGround = move.isOnGround();
}
}
@@ -0,0 +1,13 @@
package dev.brighten.ac.data.obj;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Getter
public class InstantAction {
private final short startId, endId;
private final boolean end;
private final long stamp = System.currentTimeMillis();
}
@@ -0,0 +1,12 @@
package dev.brighten.ac.data.obj;
import dev.brighten.ac.handler.keepalive.KeepAlive;
import lombok.AllArgsConstructor;
import java.util.function.Consumer;
@AllArgsConstructor
public class NormalAction {
public int stamp;
public Consumer<KeepAlive> action;
}
@@ -0,0 +1,94 @@
package dev.brighten.ac.handler;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.data.obj.NormalAction;
import dev.brighten.ac.packet.wrapper.WPacket;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutEntityEffect;
import lombok.val;
import net.minecraft.server.v1_8_R3.Packet;
import net.minecraft.server.v1_8_R3.PacketPlayInTransaction;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Optional;
public class PacketHandler {
public void process(APlayer player, Packet packetObject) {
if(packetObject instanceof PacketPlayInTransaction) {
long currentTimeMillis = System.currentTimeMillis();
PacketPlayInTransaction packet = (PacketPlayInTransaction) packetObject;
if(packet.a() == 0) {
if(Anticheat.INSTANCE.getKeepaliveProcessor().keepAlives.getIfPresent(packet.b()) != null) {
Anticheat.INSTANCE.getKeepaliveProcessor().addResponse(player, packet.b());
val optional = Anticheat.INSTANCE.getKeepaliveProcessor().getResponse(player);
int current = Anticheat.INSTANCE.getKeepaliveProcessor().tick;
optional.ifPresent(ka -> {
player.addPlayerTick();
player.getLagInfo().setLastTransPing(player.getLagInfo().getTransPing());
player.getLagInfo().setTransPing(current - ka.start);
if(player.instantTransaction.size() > 0) {
synchronized (player.instantTransaction) {
Deque<Short> toRemove = new LinkedList<>();
player.instantTransaction.forEach((key, tuple) -> {
if((currentTimeMillis - tuple.one.getStamp())
> player.getLagInfo().getTransPing() * 52L + 750L) {
tuple.two.accept(tuple.one);
toRemove.add(key);
}
});
Short key = null;
while((key = toRemove.poll()) != null) {
player.instantTransaction.remove(key);
}
}
}
if(Math.abs(player.getLagInfo().getLastTransPing() - player.getLagInfo().getTransPing()) > 1) {
player.getLagInfo().getLastPingDrop().reset();
}
ka.getReceived(player.getBukkitPlayer().getUniqueId()).ifPresent(r -> {
r.receivedStamp = currentTimeMillis;
});
for (NormalAction action : player.keepAliveStamps) {
if(action.stamp > ka.start) continue;
action.action.accept(ka);
player.keepAliveStamps.remove(action);
}
});
player.getLagInfo().getLastClientTransaction().reset();
} else {
Optional.ofNullable(player.instantTransaction.remove(packet.b()))
.ifPresent(t -> t.two.accept(t.one));
}
}
}
player.callPacket(packetObject);
}
public void process(APlayer player, WPacket packetObject) {
if(packetObject instanceof WPacketPlayInFlying) {
WPacketPlayInFlying packet = (WPacketPlayInFlying) packetObject;
player.getMovement().process(packet, System.currentTimeMillis());
} else if(packetObject instanceof WPacketPlayOutEntityEffect) {
WPacketPlayOutEntityEffect packet = (WPacketPlayOutEntityEffect) packetObject;
player.getPotionHandler().onPotionEffect(packet);
}
player.callPacket(packetObject);
}
}
@@ -0,0 +1,55 @@
package dev.brighten.ac.handler;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutEntityEffect;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
@RequiredArgsConstructor
public class PotionHandler {
private final APlayer data;
public List<PotionEffect> potionEffects = new CopyOnWriteArrayList<>();
public void onFlying(WPacketPlayInFlying packet) {
for (PotionEffect effect : potionEffects) {
if(data.getBukkitPlayer().hasPotionEffect(effect.getType())) continue;
data.runKeepaliveAction(d -> data.getPotionHandler().potionEffects.remove(effect));
}
}
public void onPotionEffect(WPacketPlayOutEntityEffect packet) {
data.runKeepaliveAction(d -> {
val type = PotionEffectType.getById(packet.getEffectId());
data.getPotionHandler().potionEffects.stream().filter(pe -> pe.getType().equals(type))
.forEach(data.getPotionHandler().potionEffects::remove);
data.getPotionHandler().potionEffects
.add(new PotionEffect(type, packet.getDuration(), packet.getAmplifier(),
(packet.getFlags() & 1) == 1));
});
}
public boolean hasPotionEffect(PotionEffectType type) {
for (PotionEffect potionEffect : potionEffects) {
if(potionEffect.getType().equals(type))
return true;
}
return false;
}
public Optional<PotionEffect> getEffectByType(PotionEffectType type) {
for (PotionEffect potionEffect : potionEffects) {
if(potionEffect.getType().equals(type))
return Optional.of(potionEffect);
}
return Optional.empty();
}
}
@@ -0,0 +1,40 @@
package dev.brighten.ac.handler.keepalive;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.data.APlayer;
import lombok.RequiredArgsConstructor;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
public class KeepAlive {
public final int start;
public final short id;
public long startStamp;
public KeepAlive(int start, short id) {
this.start = start;
this.id = id;
}
public final Map<UUID, KAReceived> receivedKeepalive = new HashMap<>();
public void received(APlayer player) {
receivedKeepalive.put(player.getBukkitPlayer().getUniqueId(),
new KAReceived(player, Anticheat.INSTANCE.getKeepaliveProcessor().tick));
}
public Optional<KAReceived> getReceived(UUID uuid) {
return Optional.ofNullable(receivedKeepalive.getOrDefault(uuid, null));
}
@RequiredArgsConstructor
public static class KAReceived {
public final APlayer data;
public final int stamp;
public long receivedStamp;
}
}
@@ -0,0 +1,100 @@
package dev.brighten.ac.handler.keepalive;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.handler.HandlerAbstract;
import dev.brighten.ac.utils.RunUtils;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minecraft.server.v1_8_R3.PacketPlayOutTransaction;
import org.bukkit.scheduler.BukkitTask;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
public class KeepaliveProcessor implements Runnable {
private BukkitTask task;
public KeepAlive currentKeepalive = new KeepAlive(0, (short)0);
public int tick;
public int totalPlayers, laggyPlayers;
public final Cache<Short, KeepAlive> keepAlives = CacheBuilder.newBuilder().concurrencyLevel(4)
.expireAfterWrite(15, TimeUnit.SECONDS).build();
final Int2ObjectMap<Short> lastResponses = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
public KeepaliveProcessor() {
start();
}
@Override
public void run() {
tick++;
synchronized (keepAlives) {
short id = (short) (tick > Short.MAX_VALUE ? tick % Short.MAX_VALUE : tick);
//Ensuring we don't have any duplicate IDS
currentKeepalive = new KeepAlive(tick, id);
keepAlives.put(currentKeepalive.id, currentKeepalive);
}
currentKeepalive.startStamp = System.currentTimeMillis();
totalPlayers = laggyPlayers = 0;
for (APlayer value : Anticheat.INSTANCE.getPlayerRegistry().aplayerMap.values()) {
totalPlayers++;
if(value.getLagInfo().getLastPingDrop().isNotPassed(2)
|| value.getLagInfo().getLastClientTransaction().isPassed(135L)) laggyPlayers++;
if(value.getInfo().getTarget() != null) {
value.getInfo().getTargetPastLocation().addLocation(value.getInfo()
.getTarget().getLocation());
}
PacketPlayOutTransaction transaction = new PacketPlayOutTransaction(0, currentKeepalive.id, false);
HandlerAbstract.getHandler().sendPacket(value.getBukkitPlayer(), transaction);
}
}
public Optional<KeepAlive> getKeepByTick(int tick) {
return keepAlives.asMap().values().stream().filter(ka -> ka.start == tick).findFirst();
}
public Optional<KeepAlive> getKeepById(short id) {
return Optional.ofNullable(keepAlives.getIfPresent(id));
}
public Optional<KeepAlive> getResponse(APlayer data) {
if(!lastResponses.containsKey(data.getBukkitPlayer().getUniqueId().hashCode()))
return Optional.empty();
return getKeepById(lastResponses.get(data.getBukkitPlayer().getUniqueId().hashCode()));
}
public void start() {
if(task == null) {
task = RunUtils.taskTimer(this, Anticheat.INSTANCE, 20L, 0L);
}
}
public void addResponse(APlayer data, short id) {
getKeepById(id).ifPresent(ka -> {
lastResponses.put(data.getBukkitPlayer().getUniqueId().hashCode(), (Short)id);
ka.received(data);
});
}
public void stop() {
if(task != null) {
task.cancel();
task = null;
}
}
}
@@ -1,35 +1,48 @@
package dev.brighten.ac.listener;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.handler.HandlerAbstract;
import dev.brighten.ac.packet.wrapper.PacketType;
import dev.brighten.ac.packet.wrapper.WPacket;
import dev.brighten.ac.utils.Init;
import dev.brighten.ac.utils.RunUtils;
import net.minecraft.server.v1_8_R3.Packet;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.Optional;
@Init
public class JoinListener implements Listener {
public JoinListener() {
Anticheat.INSTANCE.getPacketProcessor().processAsync(Anticheat.INSTANCE, EventPriority.NORMAL, event -> {
if(event.getType().equals(PacketType.BLOCK_DIG)) {
Optional<APlayer> aplayer = Anticheat.INSTANCE.getPlayerRegistry()
.getPlayer(event.getPlayer().getUniqueId());
event.getPlayer().sendMessage("BlockDig:");
}
aplayer.ifPresent(player -> {
if(event.getPacket() instanceof WPacket) {
Anticheat.INSTANCE.getPacketHandler().process(player, (WPacket) event.getPacket());
} else {
Anticheat.INSTANCE.getPacketHandler().process(player, (Packet) event.getPacket());
}
});
});
}
@EventHandler
public void onJoin(PlayerJoinEvent event) {
APlayer player = Anticheat.INSTANCE.getPlayerRegistry().generate(event.getPlayer());
RunUtils.taskLater(() -> HandlerAbstract.getHandler().add(event.getPlayer()), 4L);
player.callEvent(event);
}
@EventHandler
public void onLeave(PlayerQuitEvent event) {
HandlerAbstract.getHandler().remove(event.getPlayer());
public void onQuit(PlayerQuitEvent event) {
Anticheat.INSTANCE.getPlayerRegistry().unregister(event.getPlayer().getUniqueId());
}
}
@@ -1,5 +1,6 @@
package dev.brighten.ac.packet.handler;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.utils.reflections.Reflections;
import dev.brighten.ac.utils.reflections.types.WrappedClass;
@@ -14,7 +15,7 @@ public abstract class HandlerAbstract {
fieldNetworkManager = Reflections.getNMSClass("PlayerConnection").getFieldByName("networkManager"),
fieldPlayerConnection = Reflections.getNMSClass("EntityPlayer").getFieldByName("playerConnection");
static String handlerName = "brigten-ac-packets";
static String handlerName = "brighten-ac-packets";
@Getter
private static HandlerAbstract handler;
@@ -30,4 +31,8 @@ public abstract class HandlerAbstract {
public abstract void add(Player player);
public abstract void remove(Player player);
public abstract void sendPacket(Player player, Object packet);
public abstract void sendPacket(APlayer player, Object packet);
}
@@ -1,6 +1,7 @@
package dev.brighten.ac.packet.handler;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.PacketType;
import dev.brighten.ac.utils.reflections.types.WrappedClass;
import dev.brighten.ac.utils.reflections.types.WrappedField;
@@ -58,6 +59,16 @@ public class LegacyHandler extends HandlerAbstract {
channel.eventLoop().execute(() -> channel.pipeline().remove(handlerName));
}
@Override
public void sendPacket(Player player, Object packet) {
getChannel(player).pipeline().writeAndFlush(packet);
}
@Override
public void sendPacket(APlayer player, Object packet) {
sendPacket(player.getBukkitPlayer(), packet);
}
private Channel getChannel(Player player) {
synchronized (channelCache) {
return channelCache.computeIfAbsent(player.getName(), name -> {
@@ -2,6 +2,7 @@ package dev.brighten.ac.packet.handler;
import com.google.common.collect.MapMaker;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.PacketType;
import dev.brighten.ac.utils.reflections.Reflections;
import dev.brighten.ac.utils.reflections.types.WrappedField;
@@ -58,6 +59,16 @@ public class ModernHandler extends HandlerAbstract {
channel.eventLoop().execute(() -> channel.pipeline().remove(handlerName));
}
@Override
public void sendPacket(Player player, Object packet) {
getChannel(player).pipeline().writeAndFlush(packet);
}
@Override
public void sendPacket(APlayer player, Object packet) {
sendPacket(player.getBukkitPlayer(), packet);
}
private Channel getChannel(Player player) {
synchronized (channelCache) {
return channelCache.computeIfAbsent(player.getName(), name -> {
@@ -78,8 +89,11 @@ public class ModernHandler extends HandlerAbstract {
int index = name.lastIndexOf(".");
String packetName = name.substring(index + 1);
boolean allowed = Anticheat.INSTANCE.getPacketProcessor().call(player, msg, PacketType
.getByPacketId(packetName).orElse(PacketType.NONE));
PacketType type = PacketType
.getByPacketId(packetName).orElse(PacketType.NONE);
boolean allowed = Anticheat.INSTANCE.getPacketProcessor().call(player, PacketType.processType(type, msg),
type);
if(allowed) {
super.channelRead(ctx, msg);
@@ -7,12 +7,20 @@ import lombok.Setter;
import org.bukkit.entity.Player;
@RequiredArgsConstructor
@Getter
public class PacketInfo {
@Getter
private final Player player;
private final Object packet;
@Getter
private final PacketType type;
@Getter
private final long timestamp;
@Getter
@Setter
private boolean cancelled;
public <T> T getPacket() {
return (T) packet;
}
}
@@ -2,7 +2,6 @@ package dev.brighten.ac.packet.listener;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.packet.handler.HandlerAbstract;
import dev.brighten.ac.packet.listener.functions.PacketListener;
import dev.brighten.ac.packet.wrapper.PacketConverter;
import dev.brighten.ac.packet.wrapper.PacketType;
@@ -11,7 +10,6 @@ import dev.brighten.ac.utils.MiscUtils;
import dev.brighten.ac.utils.RunUtils;
import lombok.Getter;
import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventPriority;
import org.bukkit.plugin.Plugin;
@@ -184,6 +182,5 @@ public class PacketProcessor {
public void shutdown() {
processors.clear();
asyncProcessors.clear();
Bukkit.getOnlinePlayers().forEach(HandlerAbstract.getHandler()::remove);
}
}
@@ -1,4 +0,0 @@
package dev.brighten.ac.packet.packet;
public interface WPacket {
}
@@ -1,6 +1,7 @@
package dev.brighten.ac.packet.wrapper;
import dev.brighten.ac.packet.wrapper.in.*;
import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutEntityEffect;
public interface PacketConverter {
WPacketPlayInFlying processFlying(Object object);
@@ -15,4 +16,8 @@ public interface PacketConverter {
WPacketPlayInBlockPlace processBlockPlace(Object object);
WPacketPlayInCloseWindow processCloseWindow(Object object);
WPacketPlayInEntityAction processEntityAction(Object object);
WPacketPlayOutEntityEffect processEntityEffect(Object object);
}
@@ -1,5 +1,6 @@
package dev.brighten.ac.packet.wrapper;
import dev.brighten.ac.Anticheat;
import lombok.Getter;
import java.util.Optional;
@@ -26,6 +27,10 @@ public enum PacketType {
CLOSE_WINDOW("PacketPlayInCloseWindow"),
ENTITY_ACTION("PacketPlayInEntityAction"),
ENTITY_EFFECT("PacketPlayOutEntityEffect"),
NONE();
PacketType(String... packetIds) {
@@ -92,5 +97,36 @@ public enum PacketType {
public static final String STATUS_START = "PacketStatusInStart";
public static final String LOGIN_START = "PacketLoginInStart";
}
public static Object processType(PacketType type, Object object) {
PacketConverter convert = Anticheat.INSTANCE.getPacketProcessor().getPacketConverter();
switch (type) {
case FLYING: {
return convert.processFlying(object);
}
case CHAT:
break;
case BLOCK_DIG:
return convert.processBlockDig(object);
case USE_ENTITY:
return convert.processUseEntity(object);
case BLOCK_PLACE:
return convert.processBlockPlace(object);
case CLOSE_WINDOW:
return convert.processCloseWindow(object);
case ARM_ANIMATION:
return convert.processAnimation(object);
case ENTITY_ACTION:
return convert.processEntityAction(object);
case CLIENT_ABILITIES:
return convert.processAbilities(object);
case ENTITY_EFFECT:
return convert.processEntityEffect(object);
default:
return object;
}
return object;
}
}
@@ -3,6 +3,7 @@ package dev.brighten.ac.packet.wrapper.impl;
import dev.brighten.ac.packet.wrapper.PacketConverter;
import dev.brighten.ac.packet.wrapper.in.*;
import dev.brighten.ac.packet.wrapper.objects.WrappedEnumDirection;
import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutEntityEffect;
import dev.brighten.ac.utils.math.IntVector;
import dev.brighten.ac.utils.reflections.types.WrappedClass;
import dev.brighten.ac.utils.reflections.types.WrappedField;
@@ -67,7 +68,7 @@ public class Processor_18 implements PacketConverter {
BlockPosition pos = packet.a();
return WPacketPlayInBlockPlace.builder().blockPos(new IntVector(pos.getX(), pos.getY(), pos.getZ()))
.direction(WrappedEnumDirection.values()[packet.getFace()])
.direction(WrappedEnumDirection.values()[Math.min(packet.getFace(), 5)])
.itemStack(CraftItemStack.asCraftMirror(packet.getItemStack()))
.vecX(packet.d()).vecY(packet.e()).vecZ(packet.f())
.build();
@@ -75,11 +76,39 @@ public class Processor_18 implements PacketConverter {
private static final WrappedClass classCloseWindow = new WrappedClass(PacketPlayInCloseWindow.class);
private static final WrappedField fieldWindowId = classAbilities.getFieldByType(int.class, 0);
private static final WrappedField fieldWindowId = classCloseWindow.getFieldByType(int.class, 0);
@Override
public WPacketPlayInCloseWindow processCloseWindow(Object object) {
PacketPlayInCloseWindow packet = (PacketPlayInCloseWindow) object;
return WPacketPlayInCloseWindow.builder().id(fieldWindowId.get(packet)).build();
}
@Override
public WPacketPlayInEntityAction processEntityAction(Object object) {
PacketPlayInEntityAction packet = (PacketPlayInEntityAction) object;
return WPacketPlayInEntityAction.builder().action(WPacketPlayInEntityAction.EnumPlayerAction
.valueOf(packet.b().name())).build();
}
private static final WrappedClass classEntityEffect = new WrappedClass(PacketPlayOutEntityEffect.class);
private static final WrappedField fieldEntityId2, fieldEffectId, fieldAmplifier, fieldDuration, fieldFlags;
static {
fieldEntityId2 = classEntityEffect.getFieldByType(Integer.TYPE, 0);
fieldEffectId = classEntityEffect.getFieldByType(Byte.TYPE, 0);
fieldAmplifier = classEntityEffect.getFieldByType(Byte.TYPE, 1);
fieldDuration = classEntityEffect.getFieldByType(Integer.TYPE, 1);
fieldFlags = classEntityEffect.getFieldByType(Byte.TYPE, 2);
}
@Override
public WPacketPlayOutEntityEffect processEntityEffect(Object object) {
return WPacketPlayOutEntityEffect.builder().effectId(fieldEffectId.get(object))
.amplifier(fieldAmplifier.get(object))
.flags(fieldFlags.get(object))
.entityId(fieldEntityId2.get(object))
.duration(fieldDuration.get(object)).build();
}
}
@@ -0,0 +1,31 @@
package dev.brighten.ac.packet.wrapper.in;
import dev.brighten.ac.packet.wrapper.PacketType;
import dev.brighten.ac.packet.wrapper.WPacket;
import lombok.Builder;
import lombok.Getter;
@Builder
@Getter
public class WPacketPlayInEntityAction implements WPacket {
private EnumPlayerAction action;
@Override
public PacketType getPacketType() {
return PacketType.ENTITY_ACTION;
}
public static enum EnumPlayerAction {
START_SNEAKING,
STOP_SNEAKING,
STOP_SLEEPING,
START_SPRINTING,
STOP_SPRINTING,
RIDING_JUMP,
OPEN_INVENTORY;
private EnumPlayerAction() {
}
}
}
@@ -21,7 +21,6 @@ public class WPacketPlayInUseEntity implements WPacket {
return PacketType.USE_ENTITY;
}
public enum EnumHand {
MAIN_HAND, OFF_HAND;
}
@@ -1,5 +1,8 @@
package dev.brighten.ac.utils;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.utils.reflections.Reflections;
import dev.brighten.ac.utils.reflections.types.WrappedClass;
import org.bukkit.Bukkit;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.AnnotationNode;
@@ -8,6 +11,7 @@ import org.objectweb.asm.tree.ClassNode;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
@@ -18,6 +22,7 @@ import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -39,6 +44,125 @@ public class ClassScanner {
return scanFile(file, urls);
}
public static Set<WrappedClass> getClasses(Class<? extends Annotation> annotationClass,
String packageName) {
return scanFile(annotationClass).stream().filter(pkg -> pkg.startsWith(packageName))
.map(Reflections::getClass).collect(Collectors.toSet());
}
public static Set<WrappedClass> getClasses(Class<? extends Annotation> annotationClass) {
return scanFile(annotationClass).stream().map(Reflections::getClass).collect(Collectors.toSet());
}
public static Set<String> scanFile(Class<? extends Annotation> annotationClass) {
return scanFile(annotationClass, new URL[]{Anticheat.class.getProtectionDomain().getCodeSource().getLocation()});
}
public static Set<String> scanFile(Class<? extends Annotation> annotationClass, URL[] urls) {
Set<URI> sources = new HashSet<>();
Set<String> plugins = new HashSet<>();
for (URL url : urls) {
if (!url.getProtocol().equals("file")) {
continue;
}
URI source;
try {
source = url.toURI();
} catch (URISyntaxException e) {
continue;
}
if (sources.add(source)) {
scanPath(Paths.get(source), annotationClass, plugins);
}
}
return plugins;
}
private static void scanPath(Path path, Class<? extends Annotation> annotationClass, Set<String> plugins) {
if (Files.exists(path)) {
if (Files.isDirectory(path)) {
scanDirectory(path, annotationClass, plugins);
} else {
scanZip(path, annotationClass, plugins);
}
}
}
private static void scanDirectory(Path dir, Class<? extends Annotation> annotationClass, final Set<String> plugins) {
try {
Files.walkFileTree(dir, newHashSet(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
if (CLASS_FILE.matches(path.getFileName())) {
try (InputStream in = Files.newInputStream(path)) {
String plugin = findClass(in, annotationClass);
if (plugin != null) {
plugins.add(plugin);
}
}
}
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
public static String findClass(InputStream in, Class<? extends Annotation> annotationClass) {
try {
ClassReader reader = new ClassReader(in);
ClassNode classNode = new ClassNode();
reader.accept(classNode, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
String className = classNode.name.replace('/', '.');
final String anName = annotationClass.getName().replace(".", "/");
if (classNode.visibleAnnotations != null) {
for (Object node : classNode.visibleAnnotations) {
AnnotationNode annotation = (AnnotationNode) node;
if (annotation.desc
.equals("L" + anName + ";"))
return className;
}
}
} catch (Exception e) {
//Bukkit.getLogger().info("Failed to scan: " + in.toString());
}
return null;
}
private static void scanZip(Path path, Class<? extends Annotation> annotationClass, Set<String> plugins) {
if (!ARCHIVE.matches(path.getFileName())) {
return;
}
try (ZipFile zip = new ZipFile(path.toFile())) {
Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if (entry.isDirectory() || !entry.getName().endsWith(".class")) {
continue;
}
try (InputStream in = zip.getInputStream(entry)) {
String plugin = findClass(in, annotationClass);
if (plugin != null) {
plugins.add(plugin);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static Set<String> scanFile(String file, Class<?> clazz) {
return scanFile(file, new URL[]{clazz.getProtectionDomain().getCodeSource().getLocation()});
}
@@ -0,0 +1,215 @@
package dev.brighten.ac.utils;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.utils.world.BlockData;
import dev.brighten.ac.utils.world.CollisionBox;
import dev.brighten.ac.utils.world.EntityData;
import dev.brighten.ac.utils.world.types.SimpleCollisionBox;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import java.util.*;
import java.util.function.Consumer;
@Getter
public class
CollisionHandler {
private List<Block> blocks;
private List<Entity> entities;
private APlayer data;
private KLocation location;
private List<Triad<Double[], Integer, Consumer<Boolean>>> intersects = new ArrayList<>(),
collides = new ArrayList<>();
private double width, height;
private double shift;
@Setter
private boolean single = false;
@Setter
private boolean debugging;
public CollisionHandler(List<Block> blocks, Collection<Entity> entities, KLocation to, APlayer data) {
this.blocks = blocks;
this.entities = new ArrayList<>(entities);
this.location = to;
this.data = data;
}
public void setSize(double width, double height) {
this.width = width;
this.height = height;
}
public void setOffset(double shift) {
this.shift = shift;
}
public boolean containsFlag(int bitmask) {
for (Block b : blocks) {
if (Materials.checkFlag(b.getType(), bitmask)) return true;
}
return false;
}
public boolean contains(EntityType type) {
return entities.stream().anyMatch(e -> e.getType() == type);
}
public void intersectsWithFuture(int bitMask, Consumer<Boolean> intersects) {
String bitMaskString = String.valueOf(bitMask) + "%%__NONCE__%%";
this.intersects.add(new Triad<>(new Double[] {width, height, shift}, bitMask, intersects));
}
public void collidesWithFuture(int bitMask, Consumer<Boolean> collides) {
this.collides.add(new Triad<>(new Double[] {width, height, shift}, bitMask, collides));
}
public boolean isCollidedWithEntity(SimpleCollisionBox box) {
for(Entity entity : entities) {
if(EntityData.getEntityBox(entity.getLocation(), entity).isCollided(box))
return true;
}
return false;
}
public boolean isCollidedWithEntity() {
SimpleCollisionBox playerBox = new SimpleCollisionBox()
.offset(location.x, location.y, location.z)
.expandMin(0, shift, 0)
.expandMax(0, height, 0)
.expand(width / 2, 0, width / 2);
return isCollidedWithEntity(playerBox);
}
public List<CollisionBox> getCollisionBoxes(SimpleCollisionBox playerBox) {
List<CollisionBox> collided = new ArrayList<>();
for (Block b : blocks) {
Material material = b.getType();
CollisionBox box;
if((box = BlockData.getData(material).getBox(b, ProtocolVersion.getGameVersion())).isCollided(playerBox)) {
collided.add(box);
}
}
for(Entity entity : entities) {
CollisionBox box = EntityData.getEntityBox(entity.getLocation(), entity);
if(box.isCollided(playerBox))
collided.add(box);
}
return collided;
}
public List<CollisionBox> getCollisionBoxes() {
SimpleCollisionBox playerBox = new SimpleCollisionBox()
.offset(location.x, location.y, location.z)
.expandMin(0, shift, 0)
.expandMax(0, height, 0)
.expand(width / 2, 0, width / 2);
return getCollisionBoxes(playerBox);
}
public boolean isCollidedWith(CollisionBox box) {
SimpleCollisionBox playerBox = new SimpleCollisionBox()
.offset(location.x, location.y, location.z)
.expandMin(0, shift, 0)
.expandMax(0, height, 0)
.expand(width / 2, 0, width / 2);
return box.isCollided(playerBox);
}
public boolean isCollidedWith(SimpleCollisionBox playerBox, Material... materials) {
for (Block b : blocks) {
Location block = b.getLocation();
Material material = b.getType();
if (materials.length == 0 || MiscUtils.contains(materials, material)) {
if (BlockData.getData(material).getBox(b, ProtocolVersion.getGameVersion()).isCollided(playerBox))
return true;
}
}
if(materials.length == 0) {
for(Entity entity : entities) {
if(EntityData.getEntityBox(entity.getLocation(), entity).isCollided(playerBox))
return true;
}
}
return false;
}
public boolean isCollidedWith(Material... materials) {
SimpleCollisionBox playerBox = new SimpleCollisionBox()
.offset(location.x, location.y, location.z)
.expandMin(0, shift, 0)
.expandMax(0, height, 0)
.expand(width / 2, 0, width / 2);
return isCollidedWith(playerBox, materials);
}
public void runFutures() {
Triad<Double[], Integer, Consumer<Boolean>> value = null;
Queue<Consumer<Boolean>> successful = new LinkedList<>();
//To remove objects
Queue<Triad<Double[], Integer, Consumer<Boolean>>> collisionRemove = new LinkedList<>(),
intersectsRemove = new LinkedList<>();
for (Block b : blocks) {
Location block = b.getLocation();
Material material = b.getType();
for (Triad<Double[], Integer, Consumer<Boolean>> intersect : intersects) {
if(!Materials.checkFlag(material, intersect.second)) continue;
SimpleCollisionBox playerBox = new SimpleCollisionBox()
.offset(location.x, location.y, location.z)
.expandMin(0, intersect.first[2], 0)
.expandMax(0, intersect.first[1], 0)
.expand(intersect.first[0] / 2, 0, intersect.first[0] / 2);
if (BlockData.getData(material).getBox(b, ProtocolVersion.getGameVersion()).isIntersected(playerBox)) {
successful.add(intersect.third);
intersectsRemove.add(intersect);
}
}
for (Triad<Double[], Integer, Consumer<Boolean>> collides : collides) {
if(!Materials.checkFlag(material, collides.second)) continue;
SimpleCollisionBox playerBox = new SimpleCollisionBox()
.offset(location.x, location.y, location.z)
.expandMin(0, collides.first[2], 0)
.expandMax(0, collides.first[1], 0)
.expand(collides.first[0] / 2, 0, collides.first[0] / 2);
if (BlockData.getData(material).getBox(b, ProtocolVersion.getGameVersion()).isCollided(playerBox)) {
successful.add(collides.third);
intersectsRemove.add(collides);
}
}
while((value = intersectsRemove.poll()) != null) {
intersects.remove(value);
}
while((value = collisionRemove.poll()) != null) {
collides.remove(value);
}
}
collides.clear();
intersects.clear();
Consumer<Boolean> consumer = null;
while((consumer = successful.poll()) != null) {
consumer.accept(true);
}
}
}
@@ -0,0 +1,132 @@
package dev.brighten.ac.utils;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
@SuppressWarnings({"unchecked"})
public abstract class HookedListWrapper<T> extends ListWrapper<T> {
public HookedListWrapper(List<T> base) {
super(base);
}
// We can use the List#size call to execute some code
public abstract void onSize();
@Override
public int size() {
this.onSize();
return this.base.size();
}
@Override
public boolean isEmpty() {
return this.base.isEmpty();
}
@Override
public boolean contains(Object o) {
return this.base.contains(o);
}
@Override
public Iterator<T> iterator() {
return this.listIterator();
}
@Override
public Object[] toArray() {
return this.base.toArray();
}
@Override
public boolean add(T o) {
return this.base.add(o);
}
@Override
public boolean remove(Object o) {
return this.base.remove(o);
}
@Override
public boolean addAll(Collection c) {
return this.base.addAll(c);
}
@Override
public boolean addAll(int index, Collection c) {
return this.base.addAll(index, c);
}
@Override
public void clear() {
this.base.clear();
}
@Override
public T get(int index) {
return this.base.get(index);
}
@Override
public T set(int index, T element) {
return this.base.set(index, element);
}
@Override
public void add(int index, T element) {
this.base.add(index, element);
}
@Override
public T remove(int index) {
return this.base.remove(index);
}
@Override
public int indexOf(Object o) {
return this.base.indexOf(o);
}
@Override
public int lastIndexOf(Object o) {
return this.base.lastIndexOf(o);
}
@Override
public ListIterator<T> listIterator() {
return this.base.listIterator();
}
@Override
public ListIterator<T> listIterator(int index) {
return this.base.listIterator(index);
}
@Override
public List<T> subList(int fromIndex, int toIndex) {
return this.base.subList(fromIndex, toIndex);
}
@Override
public boolean retainAll(Collection c) {
return this.base.retainAll(c);
}
@Override
public boolean removeAll(Collection c) {
return this.base.removeAll(c);
}
@Override
public boolean containsAll(Collection c) {
return this.base.containsAll(c);
}
@Override
public Object[] toArray(Object[] a) {
return this.base.toArray(a);
}
}
@@ -4,6 +4,8 @@ import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.util.Vector;
import java.util.Objects;
public class KLocation {
public double x, y, z;
public float yaw, pitch;
@@ -55,4 +57,29 @@ public class KLocation {
public KLocation clone() {
return new KLocation(x, y, z, yaw, pitch, timeStamp);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
KLocation kLocation = (KLocation) o;
return Double.compare(kLocation.x, x) == 0 && Double.compare(kLocation.y, y) == 0 && Double.compare(kLocation.z, z) == 0 && Float.compare(kLocation.yaw, yaw) == 0 && Float.compare(kLocation.pitch, pitch) == 0 && timeStamp == kLocation.timeStamp;
}
@Override
public int hashCode() {
return Objects.hash(x, y, z, yaw, pitch, timeStamp);
}
@Override
public String toString() {
return "KLocation{" +
"x=" + x +
", y=" + y +
", z=" + z +
", yaw=" + yaw +
", pitch=" + pitch +
", timeStamp=" + timeStamp +
'}';
}
}
@@ -0,0 +1,15 @@
package dev.brighten.ac.utils;
import java.util.List;
public abstract class ListWrapper<T> implements List<T> {
protected final List<T> base;
public ListWrapper(List<T> base) {
this.base = base;
}
public List<T> getBase() {
return this.base;
}
}
@@ -6,10 +6,14 @@ import dev.brighten.ac.utils.reflections.impl.CraftReflection;
import dev.brighten.ac.utils.reflections.impl.MinecraftReflection;
import dev.brighten.ac.utils.reflections.types.WrappedClass;
import dev.brighten.ac.utils.reflections.types.WrappedField;
import dev.brighten.ac.utils.world.types.SimpleCollisionBox;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.SimpleCommandMap;
@@ -42,6 +46,54 @@ public class MiscUtils {
return toCheck.toLowerCase().contains(contains.toLowerCase());
}
public static boolean isInMaterialBB(World world, SimpleCollisionBox entityBox, XMaterial xmaterial) {
int startX = MathHelper.floor_double(entityBox.xMin);
int startY = MathHelper.floor_double(entityBox.yMin);
int startZ = MathHelper.floor_double(entityBox.zMin);
int endX = MathHelper.floor_double(entityBox.xMax + 1D);
int endY = MathHelper.floor_double(entityBox.yMax + 1D);
int endZ = MathHelper.floor_double(entityBox.zMax + 1D);
for(int x = startX ; x < endX ; x++) {
for(int y = startY ; y < endY ; y++) {
for(int z = startZ ; z < endZ ; z++) {
Location loc = new Location(world, x, y, z);
Optional<Block> op = BlockUtils.getBlockAsync(loc);
if(op.isPresent()) {
if(XMaterial.matchXMaterial(op.get().getType()).equals(xmaterial))
return true;
}
}
}
}
return false;
}
public static boolean isInMaterialBB(World world, SimpleCollisionBox entityBox, int bitmask) {
int startX = MathHelper.floor_double(entityBox.xMin);
int startY = MathHelper.floor_double(entityBox.yMin);
int startZ = MathHelper.floor_double(entityBox.zMin);
int endX = MathHelper.floor_double(entityBox.xMax + 1D);
int endY = MathHelper.floor_double(entityBox.yMax + 1D);
int endZ = MathHelper.floor_double(entityBox.zMax + 1D);
for(int x = startX ; x < endX ; x++) {
for(int y = startY ; y < endY ; y++) {
for(int z = startZ ; z < endZ ; z++) {
Location loc = new Location(world, x, y, z);
Optional<Block> op = BlockUtils.getBlockAsync(loc);
if(op.isPresent()) {
if(Materials.checkFlag(op.get().getType(), bitmask))
return true;
}
}
}
}
return false;
}
public static <T> List<T> combineLists(List<T> one, List<T> two) {
List<T> newList = new ArrayList<>();
@@ -0,0 +1,29 @@
package dev.brighten.ac.utils;
public class MouseFilter {
public float x;
public float y;
public float z;
/**
* Smooths mouse input
*/
public float smooth(float p_76333_1_, float p_76333_2_) {
this.x += p_76333_1_;
p_76333_1_ = (this.x - this.y) * p_76333_2_;
this.z += (p_76333_1_ - this.z) * 0.5F;
if (p_76333_1_ > 0.0F && p_76333_1_ > this.z || p_76333_1_ < 0.0F && p_76333_1_ < this.z) {
p_76333_1_ = this.z;
}
this.y += p_76333_1_;
return p_76333_1_;
}
public void reset() {
this.x = 0.0F;
this.y = 0.0F;
this.z = 0.0F;
}
}
@@ -0,0 +1,95 @@
package dev.brighten.ac.utils;
import dev.brighten.ac.Anticheat;
import org.bukkit.Location;
import java.util.*;
import java.util.stream.Collectors;
public class PastLocation {
public final LinkedList<KLocation> previousLocations = new LinkedList<>();
public KLocation getPreviousLocation(int time) {
synchronized (previousLocations) {
return (this.previousLocations.stream()
.min(Comparator.comparing(loc -> Math.abs(time - loc.timeStamp)))
.orElse(this.previousLocations.getFirst()));
}
}
public List<KLocation> getEstimatedLocation(int currentTime, int ping, int delta) {
synchronized (previousLocations) {
int tick = currentTime - ping;
List<KLocation> locs = new ArrayList<>();
for (KLocation previousLocation : previousLocations) {
if (Math.abs(tick - previousLocation.timeStamp) <= delta) {
locs.add(previousLocation.clone());
}
}
return locs;
}
}
public List<KLocation> getEstimatedLocationByIndex(int ping, int minAdd, int maxAdd) {
synchronized (previousLocations) {
List<KLocation> locs = new ArrayList<>();
int size = previousLocations.size() - 1;
int max = Math.min(size, size - ping + maxAdd),
min = Math.max(0, size - Math.max(1, ping) - minAdd);
for (int i = max; i > min; i--) {
locs.add(previousLocations.get(i));
}
return locs;
}
}
public List<KLocation> getEstimatedLocation(long time, long ping) {
synchronized (previousLocations) {
return this.previousLocations.stream()
.filter(loc -> time - loc.timeStamp > 0
&& time - loc.timeStamp <= ping + (ping < 50 ? 100 : 50))
.collect(Collectors.toList());
}
}
public List<KLocation> getPreviousRange(long delta) {
synchronized (previousLocations) {
long stamp = System.currentTimeMillis();
return this.previousLocations.stream()
.filter(loc -> stamp - loc.timeStamp < delta)
.collect(Collectors.toList());
}
}
public void addLocation(Location location) {
synchronized (previousLocations) {
if (previousLocations.size() >= 20)
previousLocations.removeFirst();
KLocation loc = new KLocation(location);
loc.timeStamp = Anticheat.INSTANCE.getKeepaliveProcessor().tick;
previousLocations.add(loc);
}
}
public Deque<KLocation> getPreviousLocations() {
return previousLocations;
}
public void addLocation(KLocation location) {
KLocation loc = location.clone();
loc.timeStamp = Anticheat.INSTANCE.getKeepaliveProcessor().tick;
synchronized (previousLocations) {
if (previousLocations.size() >= 20)
previousLocations.removeFirst();
previousLocations.add(loc);
}
}
}
@@ -0,0 +1,100 @@
package dev.brighten.ac.utils;
import dev.brighten.ac.Anticheat;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
@SuppressWarnings({"ConstantConditions", "unchecked"})
public final class ReflectionUtil {
private static final Unsafe UNSAFE = ReflectionUtil.getUnsafeInstance();
private static Unsafe getUnsafeInstance() {
try {
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
return (Unsafe) unsafeField.get(null);
} catch (NoSuchFieldException | IllegalAccessException e) {
Anticheat.INSTANCE.getLogger().severe("Could not locate Unsafe object!");
e.printStackTrace();
return null;
}
}
public static Field get(Class<?> oClass, Class<?> type, int index) throws NoSuchFieldException {
int i = 0;
for (Field field : oClass.getDeclaredFields()) {
if (field.getType() == type) {
if (i == index) {
field.setAccessible(true);
return field;
}
i++;
}
}
throw new NoSuchFieldException("Could not find field of class " + type.getName() + " with index " + index);
}
public static Field getFieldByClassNames(Class<?> clazz, String... simpleNames) throws NoSuchFieldException {
for (Field field : clazz.getDeclaredFields()) {
String typeSimpleName = field.getType().getSimpleName();
for (String name : simpleNames) {
if (name.equals(typeSimpleName)) {
field.setAccessible(true);
return field;
}
}
}
throw new NoSuchFieldException("Could not find field in class " + clazz.getName() + " with names " + Arrays.toString(simpleNames));
}
public static Field getFieldByType(Class<?> clazz, Class<?> type) throws NoSuchFieldException {
for (Field field : clazz.getDeclaredFields()) {
Class<?> foundType = field.getType();
if (type.isAssignableFrom(foundType)) {
field.setAccessible(true);
return field;
}
}
throw new NoSuchFieldException("Could not find field in class " + clazz.getName() + " with type " + type.getName());
}
public static Method getMethodByName(Class<?> clazz, String name) throws NoSuchMethodException {
for (Method method : clazz.getDeclaredMethods()) {
if (name.equals(method.getName())) {
method.setAccessible(true);
return method;
}
}
throw new NoSuchMethodException("Could not find method in class " + clazz.getName() + " with name " + name);
}
public static Class<?> getSuperClassByName(Class<?> clazz, String simpleName) {
if (!clazz.getSimpleName().equals(simpleName)) {
Class<?> superClazz;
while((superClazz = clazz.getSuperclass()) != null) {
if (superClazz.getSimpleName().equals(simpleName)) {
break;
}
}
return superClazz;
} else {
return clazz;
}
}
public static <T> void setUnsafe(Object object, Field field, T value) {
ReflectionUtil.UNSAFE.putObject(object, ReflectionUtil.UNSAFE.objectFieldOffset(field), value);
}
public static <T> T instantiateUnsafe(Class<T> clazz) throws Exception{
return (T) ReflectionUtil.UNSAFE.allocateInstance(clazz);
}
}
@@ -0,0 +1,67 @@
package dev.brighten.ac.utils;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.utils.reflections.impl.CraftReflection;
import dev.brighten.ac.utils.reflections.impl.MinecraftReflection;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
public class ServerInjector {
private static final Collection<String> TICKABLE_CLASS_NAMES = Arrays.asList("IUpdatePlayerListBox", "ITickable", "Runnable");
private Field hookedField;
@SuppressWarnings({"unchecked", "rawtypes"})
public void inject() throws Exception {
// Start end of tick injection
Object server = CraftReflection.getMinecraftServer();
Class<?> serverClass = MinecraftReflection.minecraftServer.getParent();
// Inject our hooked list for end of tick
for (Field field : serverClass.getDeclaredFields()) {
try {
if (field.getType().equals(List.class)) {
// Check if type parameters match one of the tickable class names used throughout different versions
Class<?> genericType = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
if (!ServerInjector.TICKABLE_CLASS_NAMES.contains(genericType.getSimpleName())) {
continue;
}
field.setAccessible(true);
// Use a list wrapper to check when the size method is called
HookedListWrapper<?> wrapper = new HookedListWrapper<Object>((List) field.get(server)) {
@Override
public void onSize() {
Runnable toRun = null;
while((toRun = Anticheat.INSTANCE.getOnTickEnd().poll()) != null) {
toRun.run();
}
}
};
ReflectionUtil.setUnsafe(server, field, wrapper);
this.hookedField = field;
break;
}
} catch (Exception ignored) {
}
}
}
public void eject() throws Exception {
// Replace hooked wrapper with original
if (this.hookedField != null) {
Object server = CraftReflection.getMinecraftServer();
HookedListWrapper<?> hookedListWrapper = (HookedListWrapper<?>) this.hookedField.get(server);
ReflectionUtil.setUnsafe(server, this.hookedField, hookedListWrapper.getBase());
this.hookedField = null;
}
}
}
@@ -1,36 +0,0 @@
package dev.brighten.ac.utils;
import dev.brighten.ac.Anticheat;
public class TickTimer {
private int ticks = Anticheat.INSTANCE.getCurrentTicks(), defaultPassed;
public TickTimer(int defaultPassed) {
this.defaultPassed = defaultPassed;
}
public void reset() {
ticks = Anticheat.INSTANCE.getCurrentTicks();
}
public boolean hasPassed() {
return Anticheat.INSTANCE.getCurrentTicks() - ticks > defaultPassed;
}
public boolean hasPassed(int amount) {
return Anticheat.INSTANCE.getCurrentTicks() - ticks > amount;
}
public boolean hasNotPassed() {
return Anticheat.INSTANCE.getCurrentTicks() - ticks <= defaultPassed;
}
public boolean hasNotPassed(int amount) {
return Anticheat.INSTANCE.getCurrentTicks() - ticks <= amount;
}
public int getPassed() {
return Anticheat.INSTANCE.getCurrentTicks() - ticks;
}
}
@@ -0,0 +1,27 @@
package dev.brighten.ac.utils;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import java.util.Objects;
@AllArgsConstructor
@NoArgsConstructor
public class Triad<A, B, C> {
public A first;
public B second;
public C third;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Triad<?, ?, ?> triad = (Triad<?, ?, ?>) o;
return Objects.equals(first, triad.first) && Objects.equals(second, triad.second) && Objects.equals(third, triad.third);
}
@Override
public int hashCode() {
return Objects.hash(first, second, third);
}
}
@@ -2,6 +2,8 @@ package dev.brighten.ac.utils;
import lombok.NoArgsConstructor;
import java.util.Objects;
@NoArgsConstructor
public class Tuple<A, B> {
public A one;
@@ -11,4 +13,25 @@ public class Tuple<A, B> {
this.one = one;
this.two = two;
}
public boolean equals(Object object) {
if (object instanceof Tuple) {
Tuple toCompare = (Tuple) object;
return one.equals(toCompare.one) && two.equals(toCompare.two);
} else return false;
}
@Override
public String toString() {
return "Tuple{" +
"one=" + one +
", two=" + two +
'}';
}
@Override
public int hashCode() {
return Objects.hash(one, two);
}
}
@@ -9,7 +9,6 @@
package dev.brighten.ac.utils.reflections;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.utils.ClassScanner;
import dev.brighten.ac.utils.objects.QuadFunction;
import dev.brighten.ac.utils.objects.TriFunction;
import dev.brighten.ac.utils.reflections.types.WrappedClass;
@@ -22,18 +21,14 @@ import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.regex.Pattern;
@Getter
public class Reflections {
private static final String craftBukkitString;
private static final String netMinecraftServerString;
private static MethodHandles.Lookup lookup = MethodHandles.lookup();
private static Set<String> classNames;
public static String OBC_PREFIX = Bukkit.getServer().getClass().getPackage().getName();
public static String VERSION = OBC_PREFIX.replace("org.bukkit.craftbukkit", "")
.replace(".", "");
@@ -42,13 +37,6 @@ public class Reflections {
String version = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
craftBukkitString = "org.bukkit.craftbukkit." + version + ".";
netMinecraftServerString = "net.minecraft.server." + version + ".";
try {
classNames = ClassScanner.scanFile2(null,
Class.forName("org.bukkit.craftbukkit.Main"));
} catch(ClassNotFoundException e) {
classNames = Collections.emptySet();
}
}
public static boolean classExists(String name) {
@@ -69,18 +57,14 @@ public class Reflections {
try {
return getClass(netMinecraftServerString + name);
} catch(Throwable e) {
Pattern toTest = Pattern.compile("\\." + name.replace("$", ".") + "$");
for (String className : classNames) {
if(!className.startsWith("net.minecraft")) continue;
if(toTest.matcher(className).find()) {
return getClass(className);
}
}
throw new ClassNotFoundException(name);
}
}
public void hi() {
System.out.println("hi");
}
public static WrappedClass getClass(String name) {
try {
return new WrappedClass(Class.forName(name));
@@ -100,7 +100,8 @@ public class WrappedClass {
}
public WrappedConstructor getConstructor() {
val optional = Arrays.stream(this.parent.getConstructors()).filter(cons -> cons.getParameterCount() == 0).findFirst();
val optional = Arrays.stream(this.parent.getConstructors())
.filter(cons -> cons.getParameterCount() == 0).findFirst();
return optional.map(constructor -> new WrappedConstructor(this, constructor)).orElse(null);
}
@@ -160,6 +161,20 @@ public class WrappedClass {
return this.getFieldByType(type, 0);
}
public WrappedMethod[] getDeclaredMethods() {
return Arrays.stream(getParent().getDeclaredMethods())
.map(m -> new WrappedMethod(this, m))
.toArray(WrappedMethod[]::new);
}
public boolean isAssignableFrom(WrappedClass wrapped) {
return wrapped.getParent().isAssignableFrom(getParent());
}
public boolean isAssignableFrom(Class<?> paramClass) {
return getParent().isAssignableFrom(paramClass);
}
public WrappedMethod getMethod(String name, Class... parameters) {
String key = name + ";;" + Arrays.stream(parameters).map(Class::getName)
.collect(Collectors.joining(","));
@@ -14,7 +14,9 @@ import dev.brighten.ac.utils.objects.TriFunction;
import dev.brighten.ac.utils.reflections.Reflections;
import lombok.Getter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiFunction;
@@ -69,6 +71,18 @@ public class WrappedMethod {
return mfunc.invokeMethod(object, args);
}
public boolean isAnnotationPresent(Class<? extends Annotation> annotation) {
return method.isAnnotationPresent(annotation);
}
public Parameter[] getParameters() {
return method.getParameters();
}
public Class<?>[] getParameterTypes() {
return method.getParameterTypes();
}
public int getModifiers() {
return this.method.getModifiers();
}
@@ -0,0 +1,23 @@
package dev.brighten.ac.utils.timer;
public interface Timer {
boolean isPassed(long stamp);
boolean isPassed();
boolean isNotPassed(long stamp);
boolean isNotPassed();
boolean isReset();
int getResetStreak();
long getPassed();
long getCurrent();
void reset();
}
@@ -0,0 +1,66 @@
package dev.brighten.ac.utils.timer.impl;
import dev.brighten.ac.utils.timer.Timer;
public class MillisTimer implements Timer {
private long currentStamp;
private int resetStreak;
private final long defaultPassed;
public MillisTimer(long defaultPassed) {
this.defaultPassed = defaultPassed;
}
public MillisTimer() {
defaultPassed = 1000L;
}
@Override
public boolean isPassed(long stamp) {
return getPassed() > stamp;
}
@Override
public boolean isPassed() {
return getPassed() > defaultPassed;
}
@Override
public boolean isNotPassed(long stamp) {
return getPassed() <= stamp;
}
@Override
public boolean isNotPassed() {
return getPassed() <= defaultPassed;
}
@Override
public boolean isReset() {
return getPassed() <= 1L;
}
@Override
public int getResetStreak() {
return resetStreak;
}
@Override
public long getPassed() {
return System.currentTimeMillis() - currentStamp;
}
@Override
public long getCurrent() {
return currentStamp;
}
@Override
public void reset() {
if(getPassed() <= 60L) resetStreak++;
else resetStreak = 0;
currentStamp = System.currentTimeMillis();
}
}
@@ -0,0 +1,67 @@
package dev.brighten.ac.utils.timer.impl;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.utils.timer.Timer;
public class TickTimer implements Timer {
private long currentStamp;
private int resetStreak;
private final long defaultPassed;
public TickTimer(long defaultPassed) {
this.defaultPassed = defaultPassed;
currentStamp = Anticheat.INSTANCE.getCurrentTick();
}
public TickTimer() {
defaultPassed = 20;
}
@Override
public boolean isPassed(long stamp) {
return getPassed() > stamp;
}
@Override
public boolean isPassed() {
return getPassed() > defaultPassed;
}
@Override
public boolean isNotPassed(long stamp) {
return getPassed() <= stamp;
}
@Override
public boolean isNotPassed() {
return getPassed() <= defaultPassed;
}
@Override
public boolean isReset() {
return getPassed() == 0;
}
@Override
public int getResetStreak() {
return resetStreak;
}
@Override
public long getPassed() {
return Anticheat.INSTANCE.getCurrentTick()- currentStamp;
}
@Override
public long getCurrent() {
return currentStamp;
}
@Override
public void reset() {
if(getPassed() <= 1) resetStreak++;
else resetStreak = 0;
currentStamp = Anticheat.INSTANCE.getCurrentTick();
}
}
@@ -1,6 +1,7 @@
package dev.brighten.ac.utils.world.types;
import dev.brighten.ac.utils.BoundingBox;
import dev.brighten.ac.utils.KLocation;
import dev.brighten.ac.utils.reflections.impl.MinecraftReflection;
import dev.brighten.ac.utils.world.CollisionBox;
import org.bukkit.Location;
@@ -51,7 +52,11 @@ public class SimpleCollisionBox implements CollisionBox {
public SimpleCollisionBox(Vector min, Vector max) {
this(min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ());
}
public SimpleCollisionBox(KLocation loc, double width, double height) {
this(loc.toVector(), width, height);
}
public SimpleCollisionBox(Location loc, double width, double height) {
this(loc.toVector(), width, height);
}