Restructuring Project

This commit is contained in:
Dawson
2023-03-06 09:32:22 -05:00
parent d61ae0e3d4
commit 591f782efd
310 changed files with 218 additions and 956 deletions
@@ -0,0 +1,311 @@
package dev.brighten.ac;
import co.aikar.commands.BukkitCommandManager;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import dev.brighten.ac.api.AnticheatAPI;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckManager;
import dev.brighten.ac.data.PlayerRegistry;
import dev.brighten.ac.depends.LibraryLoader;
import dev.brighten.ac.depends.MavenLibrary;
import dev.brighten.ac.depends.Repository;
import dev.brighten.ac.handler.PacketHandler;
import dev.brighten.ac.handler.entity.FakeEntityTracker;
import dev.brighten.ac.handler.keepalive.KeepaliveProcessor;
import dev.brighten.ac.handler.keepalive.actions.ActionManager;
import dev.brighten.ac.handler.protocolsupport.ProtocolAPI;
import dev.brighten.ac.logging.LoggerManager;
import dev.brighten.ac.packet.handler.HandlerAbstract;
import dev.brighten.ac.packet.listener.PacketProcessor;
import dev.brighten.ac.utils.*;
import dev.brighten.ac.utils.annotation.ConfigSetting;
import dev.brighten.ac.utils.annotation.Init;
import dev.brighten.ac.utils.config.Configuration;
import dev.brighten.ac.utils.config.ConfigurationProvider;
import dev.brighten.ac.utils.config.YamlConfiguration;
import dev.brighten.ac.utils.math.RollingAverageDouble;
import dev.brighten.ac.utils.reflections.types.WrappedMethod;
import dev.brighten.ac.utils.timer.Timer;
import dev.brighten.ac.utils.timer.impl.TickTimer;
import dev.brighten.ac.utils.world.WorldInfo;
import dev.brighten.loader.LoaderPlugin;
import lombok.Getter;
import lombok.experimental.PackagePrivate;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.event.HandlerList;
import java.io.File;
import java.io.IOException;
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
//@MavenLibrary(groupId = "co.aikar", artifactId = "acf-bukkit", version = "0.5.1", repo = @Repository(url = "https://nexus.funkemunky.cc/content/repositories/releases/"))
@MavenLibrary(groupId = "com.google.guava", artifactId = "guava", version = "21.0", repo = @Repository(url = "https://repo1.maven.org/maven2"))
//@MavenLibrary(groupId = "it.unimi.dsi", artifactId = "fastutil", version = "8.5.6", repo = @Repository(url = "https://repo1.maven.org/maven2"))
@MavenLibrary(groupId = "org.ow2.asm", artifactId = "asm", version = "9.4", repo = @Repository(url = "https://repo1.maven.org/maven2"))
@MavenLibrary(groupId = "org.ow2.asm", artifactId = "asm-tree", version = "9.4", repo = @Repository(url = "https://repo1.maven.org/maven2"))
public class Anticheat extends LoaderPlugin {
public static Anticheat INSTANCE;
private ScheduledExecutorService scheduler;
private PacketProcessor packetProcessor;
private BukkitCommandManager commandManager;
private ActionManager actionManager;
private CheckManager checkManager;
private PlayerRegistry playerRegistry;
private KeepaliveProcessor keepaliveProcessor;
private PacketHandler packetHandler;
private LoggerManager logManager;
private FakeEntityTracker fakeTracker;
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);
private final Map<UUID, WorldInfo> worldInfoMap = new HashMap<>();
/**
* private final Emulator emulator = new Emulator(new DataSupplier() {
*
* @Override
* public List<AxisAlignedBB> getCollidingBoxes(AxisAlignedBB bb) {
* return Collections.emptyList();
* }
*
* @Override
* public Block getBlockAt(BlockPos blockPos) {
* return null;
* }
* });
*
* public void runEmulation() {
* // Here we'll build the iteration input object we'll feed into the emulator
* final IterationInput input = IterationInput.builder()
* .to(new Vector(1, 2, 3)) // location from the flying packet
* .yaw(5F) // current yaw
* .ground(false)
* .jumping(false) // you'll want to bruteforce this
* .forward(0) // you'll want to bruteforce this
* .strafing(0) // you'll want to bruteforce this
* .sprinting(false) // you'll want to bruteforce this
* .usingItem(false) // you'll want to bruteforce this
* .hitSlowdown(false) // you'll want to bruteforce this
* .sneaking(false)
* .lastReportedBoundingBox(new AxisAlignedBB(0, 0, 0, 0, 0, 0)) // from location, as a bounding box
* .build();
*
* // Run the emulation and get the result
* final IterationResult result = emulator.runIteration(input);
*
* // Once we've found our best candidate (in the case of a bruteforce),
* // confirm it to run post actions.
* emulator.confirm(result.getIteration());
* }
*/
public static boolean allowDebug = true;
@ConfigSetting(path = "logging", name = "verbose")
private static boolean verboseLogging = true;
private WrappedMethod findClassMethod;
private Configuration anticheatConfig;
public void onEnable() {
INSTANCE = this;
LibraryLoader.loadAll(getClass());
scheduler = Executors.newScheduledThreadPool(2, new ThreadFactoryBuilder()
.setNameFormat("Anticheat Schedular")
.setUncaughtExceptionHandler((t, e) -> RunUtils.task(e::printStackTrace))
.build());
loadConfig();
IntegrityCheck.checkIntegrity();
commandManager = new BukkitCommandManager(getPluginInstance());
commandManager.enableUnstableAPI("help");
new CommandPropertiesManager(commandManager, getDataFolder(),
getResource2("command-messages.properties"));
packetProcessor = new PacketProcessor();
new AnticheatAPI();
ClassScanner.initializeScanner(getPluginInstance().getClass(), getPluginInstance(),
ClassScanner.getNames());
if(!getAnticheatConfig().contains("database.username")) {
getAnticheatConfig().set("database.username", "dbuser");
}
if(!getAnticheatConfig().contains("database.password")) {
getAnticheatConfig().set("database.password", UUID.randomUUID().toString());
}
this.keepaliveProcessor = new KeepaliveProcessor();
this.fakeTracker = new FakeEntityTracker();
this.checkManager = new CheckManager();
this.playerRegistry = new PlayerRegistry();
HandlerAbstract.init();
Bukkit.getOnlinePlayers().forEach(playerRegistry::generate);
this.packetHandler = new PacketHandler();
logManager = new LoggerManager();
this.actionManager = new ActionManager();
keepaliveProcessor.start();
logManager.init();
alog(Color.Green + "Loading WorldInfo system...");
Bukkit.getWorlds().forEach(w -> worldInfoMap.put(w.getUID(), new WorldInfo(w)));
injector = new ServerInjector();
try {
injector.inject();
} catch (Exception e) {
e.printStackTrace();
}
Bukkit.getOnlinePlayers().forEach(HandlerAbstract.getHandler()::add);
}
public void onDisable() {
scheduler.shutdown();
commandManager.unregisterCommands();
checkManager.getCheckClasses().clear();
Check.alertsEnabled.clear();
Check.debugInstances.clear();
checkManager = null;
keepaliveProcessor.keepAlives.cleanUp();
keepaliveProcessor = null;
ProtocolAPI.INSTANCE = null;
tps = null;
logManager.shutDown();
Bukkit.getScheduler().cancelTasks(getPluginInstance());
// Unregistering APlayer objects
playerRegistry.unregisterAll();
playerRegistry = null;
try {
injector.eject();
injector = null;
} catch (Exception e) {
throw new RuntimeException(e);
}
fakeTracker.despawnAll();
fakeTracker = null;
// Unregistering packet listeners for players
HandlerAbstract.getHandler().shutdown();
HandlerList.unregisterAll(getPluginInstance());
packetProcessor.shutdown();
packetProcessor = null;
AnticheatAPI.INSTANCE = null;
onTickEnd.clear();
onTickEnd = null;
packetHandler = null;
}
public void saveConfig() {
try {
ConfigurationProvider.getProvider(YamlConfiguration.class)
.save(getAnticheatConfig(), new File(getDataFolder().getPath() + File.separator + "anticheat.yml"));
} catch (IOException e) {
e.printStackTrace();
}
}
public void reloadConfig() {
try {
anticheatConfig = ConfigurationProvider.getProvider(YamlConfiguration.class)
.load(new File(getDataFolder().getPath() + File.separator + "anticheat.yml"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void loadConfig() {
try {
File configFile = new File(getDataFolder(), "anticheat.yml");
if(!configFile.exists()) {
configFile.getParentFile().mkdirs();
if(!configFile.createNewFile()) {
throw new RuntimeException("Could not create new anticheat.yml in plugin folder!" +
"Insufficient write permissions?");
} else {
MiscUtils.copy(INSTANCE.getResource2("anticheat.yml"), configFile);
}
}
anticheatConfig = ConfigurationProvider.getProvider(YamlConfiguration.class).load(configFile);
} catch(IOException e) {
throw new RuntimeException("Could not load \"anticheat.yml\"!", e);
}
}
public WorldInfo getWorldInfo(World world) {
return worldInfoMap.computeIfAbsent(world.getUID(), key -> new WorldInfo(world));
}
public void alog(String log, Object... values) {
alog(false, log, values);
}
public void alog(boolean verbose, String log, Object... values) {
if(!verbose || verboseLogging) {
if(values.length > 0)
MiscUtils.printToConsole(log, values);
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(task -> {
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;
}, 1L, 1L);
}
public void onTickEnd(Runnable runnable) {
onTickEnd.add(runnable);
}
}
@@ -0,0 +1,258 @@
package dev.brighten.ac.check;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.api.AnticheatAPI;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.api.check.ECheck;
import dev.brighten.ac.api.event.AnticheatEvent;
import dev.brighten.ac.api.event.result.CancelResult;
import dev.brighten.ac.api.event.result.FlagResult;
import dev.brighten.ac.api.event.result.PunishResult;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.utils.*;
import dev.brighten.ac.utils.timer.Timer;
import dev.brighten.ac.utils.timer.impl.TickTimer;
import lombok.Getter;
import lombok.Setter;
import lombok.val;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.*;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
public class Check implements ECheck {
public final APlayer player;
@Getter
private final CheckData checkData;
@Getter
public float vl;
@Getter
@Setter
private boolean enabled, punishable, cancellable;
@Getter
@Setter
private int punishVl;
private static final Timer alertCountReset = new TickTimer(), lastPunish = new TickTimer();
private static final AtomicInteger alertCount = new AtomicInteger(0);
public static Set<UUID> alertsEnabled = new HashSet<>();
public static final Map<String, List<Tuple<UUID, UUID>>> debugInstances = new HashMap<>();
public Check(APlayer player) {
this.player = player;
this.checkData = getClass().getAnnotation(CheckData.class);
}
@Override
public String getName() {
return checkData.name();
}
@Override
public CheckType getCheckType() {
return checkData.type();
}
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("%p", String.valueOf(player.getLagInfo().getTransPing()))
.replace("%t", String.valueOf(MathUtils.round(Anticheat.INSTANCE.getTps(), 2)))
.replace("%vl%", String.valueOf(MathUtils.round(vl, 1)));
}
public void cancel() {
CancelResult result = CancelResult.builder().cancelled(false).build();
for (AnticheatEvent event : AnticheatAPI.INSTANCE.getAllEvents()) {
result = event.onCancel(player.getBukkitPlayer(), this, result.isCancelled());
}
if(result.isCancelled()) return;
if(checkData.type() == CheckType.COMBAT) {
player.hitsToCancel++;
} else {
player.getInfo().getLastCancel().reset();
final Location ground = player.getInfo().isServerGround()
? player.getMovement().getFrom().getLoc()
.toLocation(player.getBukkitPlayer().getWorld())
: MovementUtils.findGroundLocation(player.getMovement().getFrom().getLoc()
.toLocation(player.getBukkitPlayer().getWorld()), 10);
RunUtils.task(() -> player.getBukkitPlayer().teleport(ground));
}
}
public void correctMovement(KLocation toLoc) {
CancelResult result = CancelResult.builder().cancelled(false).build();
for (AnticheatEvent event : AnticheatAPI.INSTANCE.getAllEvents()) {
result = event.onCancel(player.getBukkitPlayer(), this, result.isCancelled());
}
if(result.isCancelled()) return;
player.getInfo().getLastCancel().reset();
final Location CORRECTED = toLoc.toLocation(player.getBukkitPlayer().getWorld());
RunUtils.task(() -> player.getBukkitPlayer().teleport(CORRECTED));
}
public void debug(String information, Object... variables) {
if(!Anticheat.allowDebug) return;
val list = debugInstances.get(checkData.name());
if(list != null) {
for (Tuple<UUID, UUID> tuple : list) {
UUID toDebug = tuple.one;
if(!toDebug.equals(player.getUuid())) continue;
ComponentBuilder builder = new ComponentBuilder("[ " + getName() + ": " + player.getBukkitPlayer().getName() + "] ")
.color(ChatColor.RED);
BaseComponent[] message =
builder.append(String.format(information, variables)).color(ChatColor.GRAY).create();
Anticheat.INSTANCE.getPlayerRegistry().getPlayer(tuple.two)
.ifPresent(player -> player.getBukkitPlayer().spigot().sendMessage(message));
}
}
}
public <T extends Check> Optional<T> find(Class<T> checkClass) {
Check check = player.getCheckHandler().findCheck(checkClass);
if(check != null) {
return Optional.of(checkClass.cast(check));
}
return Optional.empty();
}
static List<TextComponent> devComponents = new ArrayList<>(), components = new ArrayList<>();
static {
for (BaseComponent dev : new ComponentBuilder("[").color(ChatColor.DARK_GRAY).append("Dev")
.color(ChatColor.RED).append("]").color(ChatColor.DARK_GRAY).create()) {
devComponents.add((TextComponent)dev);
}
BaseComponent[] textComp = new ComponentBuilder("[").color(ChatColor.DARK_GRAY).append("!")
.color(ChatColor.DARK_RED).append("]").color(ChatColor.DARK_GRAY).append(" %player%")
.color(ChatColor.WHITE).append(" flagged").color(ChatColor.GRAY).append(" %check%")
.color(ChatColor.WHITE).append(" (").color(ChatColor.DARK_GRAY).append("x%vl%")
.color(ChatColor.YELLOW).append(")").color(ChatColor.DARK_GRAY).create();
for (BaseComponent bc : textComp) {
devComponents.add(new TextComponent((TextComponent)bc));
components.add(new TextComponent((TextComponent)bc));
}
}
public void flag(String information, Object... variables) {
flag(true, information, variables);
}
public void flag(boolean punish, String information, Object... variables) {
vl++;
Anticheat.INSTANCE.getScheduler().execute(() -> {
if(Anticheat.INSTANCE.getTps() < 18)
vl = 0;
final String info = String.format(information, variables);
FlagResult currentResult = FlagResult.builder().cancelled(false).build();
for (AnticheatEvent event : AnticheatAPI.INSTANCE.getAllEvents()) {
currentResult = event.onFlag(player.getBukkitPlayer(), this, info,
currentResult.isCancelled());
}
if(currentResult.isCancelled()) return;
Anticheat.INSTANCE.getLogManager()
.insertLog(player, checkData, vl, System.currentTimeMillis(), info);
if(alertCountReset.isPassed(20)) {
alertCount.set(0);
alertCountReset.reset();
}
if(alertCount.incrementAndGet() < 80) {
//Sending Discord webhook alert
List<BaseComponent> toSend = new ArrayList<>();
for (TextComponent tc : components) {
TextComponent ntc = new TextComponent(tc);
ntc.setText(formatAlert(tc.getText(), info));
ntc.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("Description:")
.color(ChatColor.YELLOW)
.append(formatAlert(" %desc%\n", info)).color(ChatColor.WHITE).append("Info:")
.color(ChatColor.YELLOW)
.append(formatAlert(" %info%\n", info)).color(ChatColor.WHITE)
.append("\n").append("Click to teleport to player")
.create()));
ntc.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND,
addPlaceHolders(CheckConfig.clickCommand)));
toSend.add(ntc);
}
for (UUID uuid : alertsEnabled) {
Anticheat.INSTANCE.getPlayerRegistry().getPlayer(uuid)
.ifPresent(apl -> apl.getBukkitPlayer().spigot().sendMessage(toSend
.toArray(new BaseComponent[0])));
}
alertCountReset.reset();
}
if(punish && vl >= punishVl) {
punish();
}
});
}
public void punish() {
if(!punishable || lastPunish.isNotPassed(20)) return;
lastPunish.reset();
List<String> commands = CheckConfig.punishmentCommands.stream().map(this::addPlaceHolders)
.collect(Collectors.toList());
PunishResult result = PunishResult.builder().cancelled(false).commands(commands).build();
for (AnticheatEvent event : AnticheatAPI.INSTANCE.getAllEvents()) {
result = event.onPunish(player.getBukkitPlayer(),this, commands, result.isCancelled());
}
PunishResult finalResult = result;
if(finalResult != null && finalResult.getCommands() != null && !finalResult.isCancelled()) {
RunUtils.task(() -> {
for (String punishmentCommand : finalResult.getCommands()) {
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), punishmentCommand);
}
});
}
}
}
@@ -0,0 +1,17 @@
package dev.brighten.ac.check;
import dev.brighten.ac.utils.annotation.ConfigSetting;
import dev.brighten.ac.utils.annotation.Init;
import java.util.Arrays;
import java.util.List;
@Init
public class CheckConfig {
@ConfigSetting(name = "punishments.commands")
public static List<String> punishmentCommands = Arrays.asList("kick %player% Unfair Advantage (%check%)");
@ConfigSetting(name = "alerts.clickCommand")
public static String clickCommand = "tp %player%";
}
@@ -0,0 +1,23 @@
package dev.brighten.ac.check;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.packet.ProtocolVersion;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckData {
String name();
String checkId();
CheckType type();
boolean enabled() default true;
boolean punishable() default true;
boolean cancellable() default true;
boolean experimental() default false;
int punishVl() default 10;
ProtocolVersion minVersion() default ProtocolVersion.V1_7;
ProtocolVersion maxVersion() default ProtocolVersion.v1_19_1;
}
@@ -0,0 +1,85 @@
package dev.brighten.ac.check;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.utils.ClassScanner;
import dev.brighten.ac.utils.reflections.types.WrappedClass;
import lombok.Getter;
import lombok.val;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@Getter
public class CheckManager {
private final Map<String, CheckStatic> checkClasses = new HashMap<>();
private final Map<String, String> idToName = new HashMap<>();
private final Map<Class<? extends Check>, CheckSettings> checkSettings = new HashMap<>();
public CheckManager() {
synchronized (checkClasses) {
for (WrappedClass aClass : ClassScanner.getClasses(CheckData.class)) {
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);
Optional<CheckSettings> settings = getConfigSettings(checkData.checkId());
if(!settings.isPresent()) {
generateConfigSettings(checkData);
settings = Optional.of(CheckSettings.settingsFromData(checkData));
}
checkSettings.put(checkClass.getParent(), settings.get());
Anticheat.INSTANCE.alog(true, "&7Adding check to CheckManager: " + checkData.name());
checkClasses.put(checkData.name(), check);
idToName.put(checkData.checkId(), checkData.name());
}
private Optional<CheckSettings> getConfigSettings(String checkId) {
String basePath = "checks." + checkId + ".";
if(Anticheat.INSTANCE.getAnticheatConfig().contains(basePath + "enabled")) {
val config = Anticheat.INSTANCE.getAnticheatConfig();
return Optional.of(CheckSettings.builder()
.enabled(config.getBoolean(basePath + "enabled"))
.punishable(config.getBoolean(basePath + "punishable"))
.cancellable(config.getBoolean(basePath + "cancellable"))
.punishVl(config.getInt(basePath + "punishVl"))
.build());
}
return Optional.empty();
}
public CheckSettings getCheckSettings(Class<? extends Check> checkClass) {
return checkSettings.get(checkClass);
}
private void generateConfigSettings(CheckData data) {
val config = Anticheat.INSTANCE.getAnticheatConfig();
String basePath = "checks." + data.checkId() + ".";
config.set(basePath + "enabled", data.enabled());
config.set(basePath + "punishable", data.punishable());
config.set(basePath + "cancellable", data.cancellable());
config.set(basePath + "punishVl", data.punishVl());
Anticheat.INSTANCE.saveConfig();
}
public boolean isCheck(String name) {
final String formattedName = name.replace("_", " ");
return checkClasses.containsKey(formattedName);
}
}
@@ -0,0 +1,21 @@
package dev.brighten.ac.check;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class CheckSettings {
private boolean enabled, punishable, cancellable;
private int punishVl;
public static CheckSettings settingsFromData(CheckData data) {
return CheckSettings.builder().enabled(data.enabled())
.punishable(data.punishable())
.cancellable(data.cancellable())
.punishVl(data.punishVl())
.build();
}
}
@@ -0,0 +1,74 @@
package dev.brighten.ac.check;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.WPacket;
import dev.brighten.ac.utils.Tuple;
import dev.brighten.ac.utils.reflections.types.WrappedClass;
import dev.brighten.ac.utils.reflections.types.WrappedConstructor;
import dev.brighten.ac.utils.reflections.types.WrappedField;
import lombok.Getter;
import net.minecraft.server.v1_8_R3.Packet;
import org.bukkit.Bukkit;
import org.bukkit.event.Event;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
public class CheckStatic {
@Getter
private final WrappedClass checkClass;
private WrappedConstructor initConst;
@Getter
private final List<Tuple<WrappedField, Class<?>>> actions = new ArrayList<>(),
timedActions = new ArrayList<>(), cancellableActions = new ArrayList<>();
public CheckStatic(WrappedClass checkClass) {
this.checkClass = checkClass;
processClass();
}
private void processClass() {
initConst = checkClass.getConstructor(APlayer.class);
for (WrappedField field : checkClass.getFields()) {
if(!WAction.class.isAssignableFrom(field.getType())
&& !WCancellable.class.isAssignableFrom(field.getType())
&& !WTimedAction.class.isAssignableFrom(field.getType())) continue;
Type genericType = field.getField().getGenericType();
Type type = null;
if(genericType instanceof ParameterizedType) {
type = ((ParameterizedType) genericType).getActualTypeArguments()[0];
} else type = genericType;
if(type == null) {
Bukkit.getLogger().warning("Could not get type for field " + field.getField().getName()
+ " in class " + checkClass.getClass().getSimpleName());
continue;
}
if(!Packet.class.isAssignableFrom((Class<?>) type)
&& !WPacket.class.isAssignableFrom((Class<?>) type)
&& !Event.class.isAssignableFrom((Class<?>) type)) {
Bukkit.getLogger().warning("Type " + ((Class<?>) type).getSimpleName() + " is not a valid type for field "
+ field.getField().getName() + " in class " + checkClass.getClass().getSimpleName());
continue;
}
if(field.getType().equals(WAction.class)) {
actions.add(new Tuple<>(field, (Class<?>)type));
} else if(field.getType().equals(WTimedAction.class)) { //This will always be TimedAction
timedActions.add(new Tuple<>(field, (Class<?>)type));
} else if(field.getType().equals(WCancellable.class)) {
cancellableActions.add(new Tuple<>(field, (Class<?>)type));
}
}
}
public Check playerInit(APlayer player) {
return initConst.newInstance(player);
}
}
@@ -0,0 +1,8 @@
package dev.brighten.ac.check;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Pre {
}
@@ -0,0 +1,6 @@
package dev.brighten.ac.check;
@FunctionalInterface
public interface WAction<T> {
void invoke(T event);
}
@@ -0,0 +1,6 @@
package dev.brighten.ac.check;
@FunctionalInterface
public interface WCancellable<T> {
boolean invoke(T event);
}
@@ -0,0 +1,6 @@
package dev.brighten.ac.check;
@FunctionalInterface
public interface WTimedAction<T> {
void invoke(T event, long timestamp);
}
@@ -0,0 +1,32 @@
package dev.brighten.ac.check.impl.chat;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WCancellable;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInChat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@CheckData(name = "Log4J", checkId = "log4j", type = CheckType.CHAT)
public class Log4J extends Check {
public Log4J(APlayer player) {
super(player);
}
private static final Pattern pattern = Pattern.compile("\\$\\{.*}");
WCancellable<WPacketPlayInChat> chatPacket = packet -> {
Matcher matcher = pattern.matcher(packet.getMessage());
if(matcher.matches()) {
flag("Tried to use JNDI exploit");
return true;
}
debug("Sent chat message: " + packet.getMessage());
return false;
};
}
@@ -0,0 +1,186 @@
package dev.brighten.ac.check.impl.combat;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInUseEntity;
import dev.brighten.ac.utils.*;
import dev.brighten.ac.utils.timer.Timer;
import dev.brighten.ac.utils.timer.impl.TickTimer;
import dev.brighten.ac.utils.world.EntityData;
import dev.brighten.ac.utils.world.types.SimpleCollisionBox;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.util.Vector;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
@CheckData(name = "Hitbox", checkId = "hitboxa", type = CheckType.COMBAT)
public class Hitbox extends Check {
private float buffer;
private int hbuffer;
public Timer lastAimOnTarget = new TickTimer(), lastPosition = new TickTimer();
private final Queue<Tuple<Entity, KLocation>> attacks = new LinkedBlockingQueue<>();
private static final EnumSet<EntityType> allowedEntityTypes = EnumSet.of(EntityType.ZOMBIE, EntityType.SHEEP,
EntityType.BLAZE, EntityType.SKELETON, EntityType.PLAYER, EntityType.VILLAGER, EntityType.IRON_GOLEM,
EntityType.WITCH, EntityType.COW, EntityType.CREEPER);
public Hitbox(APlayer player) {
super(player);
}
WAction<WPacketPlayInUseEntity> useEntity = packet -> {
Entity entity = packet.getEntity(player.getBukkitPlayer().getWorld());
if(entity == null) return;
if(packet.getAction() == WPacketPlayInUseEntity.EnumEntityUseAction.ATTACK
&& allowedEntityTypes.contains(entity.getType())) {
attacks.add(new Tuple<>(packet.getEntity(player.getBukkitPlayer().getWorld()), player.getMovement().getTo().getLoc().clone()));
}
};
//TODO Figure out how to make the check more sensitive without compromising network stability
//Aka figure out how to minimize the amount of previous locations needed to process to keep network
//stability. like shortening the amount stored, or removing older ones.
WAction<WPacketPlayInFlying> onFlying = packet -> {
if(player.getInfo().isCreative() || player.getInfo().isInVehicle()) {
attacks.clear();
return;
}
Tuple<Entity, KLocation> target;
while((target = attacks.poll()) != null) {
//Updating new entity loc
Optional<Tuple<EntityLocation, EntityLocation>> optionalEloc = player.getEntityLocationHandler().getEntityLocation(target.one);
if(!optionalEloc.isPresent()) {
return;
}
final Tuple<EntityLocation, EntityLocation> eloc = optionalEloc.get();
final KLocation to = target.two;
if(eloc.one.x == 0 && eloc.one.y == 0 & eloc.one.z == 0) {
return;
}
double distance = Double.MAX_VALUE;
boolean collided = false; //Using this to compare smaller numbers than Double.MAX_VALUE. Slightly faster
List<SimpleCollisionBox> boxes = new ArrayList<>();
double expand = 0.005;
if(player.getPlayerVersion().isBelow(ProtocolVersion.V1_9)) {
expand+= 0.1;
}
//Accounting for potential movement updates not sent to the server
if(lastPosition.isPassed(1)) {
expand+= 0.03;
}
if(eloc.two != null) {
for (KLocation oldLocation : eloc.one.interpolatedLocations) {
SimpleCollisionBox box = (SimpleCollisionBox)
EntityData.getEntityBox(oldLocation.toVector(), target.one);
boxes.add(box.expand(expand));
}
for (KLocation oldLocation : eloc.two.interpolatedLocations) {
SimpleCollisionBox box = (SimpleCollisionBox)
EntityData.getEntityBox(oldLocation.toVector(), target.one);
boxes.add(box.expand(expand));
}
} else {
for (KLocation oldLocation : eloc.one.interpolatedLocations) {
SimpleCollisionBox box = (SimpleCollisionBox)
EntityData.getEntityBox(oldLocation.toVector(), target.one);
boxes.add(box.expand(expand));
}
}
if(boxes.size() == 0) return;
int hits = 0;
boolean didSneakOrElytra = player.getInfo().getLastSneak().isNotPassed(40)
|| player.getInfo().getLastElytra().isNotPassed(40);
List<Vector> directions = new ArrayList<>(Arrays.asList(MathUtils.getDirection(
player.getMovement().getTo().getLoc().yaw,
player.getMovement().getTo().getLoc().pitch),
MathUtils.getDirection(player.getMovement().getFrom().getLoc().yaw,
player.getMovement().getTo().getLoc().pitch)));
if(!didSneakOrElytra) {
to.y+= 1.62f;
for (Vector direction : directions) {
for (SimpleCollisionBox targetBox : boxes) {
final AxisAlignedBB vanillaBox = new AxisAlignedBB(targetBox);
Vec3D intersectTo = vanillaBox.rayTrace(to.toVector(), direction, 10);
if(intersectTo != null) {
lastAimOnTarget.reset();
hits++;
distance = Math.min(distance, intersectTo.distanceSquared(new Vec3D(to.x, to.y, to.z)));
collided = true;
}
}
}
//Checking all possible eyeheights since client actions notoriously desync from the server side
} else {
for (Vector direction : directions) {
for (double eyeHeight : player.getMovement().getEyeHeights()) {
for (SimpleCollisionBox targetBox : boxes) {
final AxisAlignedBB vanillaBox = new AxisAlignedBB(targetBox);
KLocation from = to.clone();
from.y+= eyeHeight;
Vec3D intersectTo = vanillaBox.rayTrace(from.toVector(), direction, 10);
if(intersectTo != null) {
lastAimOnTarget.reset();
hits++;
distance = Math.min(distance, intersectTo.distanceSquared(new Vec3D(from.x, from.y, from.z)));
collided = true;
}
}
}
}
}
if(collided) {
hbuffer = 0;
distance = Math.sqrt(distance);
if(distance > 3.001) {
if(++buffer > 1) {
flag("d=%.3f>-3.0", distance);
buffer = Math.min(1, buffer);
}
} else if(buffer > 0) buffer-= 0.02f;
if(hbuffer > 0) hbuffer--;
debug("buffer: %.3f distance=%.2f hits=%s sneaking=%s", buffer, distance, hits, player.getInfo().isSneaking());
} else if(player.getEntityLocationHandler().streak > 1) {
if (++hbuffer > 5) {
flag("%.1f;%.1f;%.1f", eloc.one.x, eloc.one.y, eloc.one.z);
}
debug("Missed!");
}
}
if(packet.isMoved())
lastPosition.reset();
};
}
@@ -0,0 +1,109 @@
package dev.brighten.ac.check.impl.combat.aim;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import dev.brighten.ac.utils.Color;
import dev.brighten.ac.utils.annotation.Async;
import dev.brighten.ac.utils.timer.Timer;
import dev.brighten.ac.utils.timer.impl.TickTimer;
import java.util.List;
@CheckData(name = "Aim", checkId = "aima", type = CheckType.COMBAT)
public class Aim extends Check {
public Aim(APlayer player) {
super(player);
}
private float buffer;
protected Timer lastGrid = new TickTimer(3);
@Async
WAction<WPacketPlayInFlying> onFlying = (packet) -> {
if(!packet.isLooked()) return;
if(player.getMovement().getYawGcdList().size() < 40) {
if(buffer > 0) buffer--;
return;
}
final float deltaYaw = Math.abs(player.getMovement().getDeltaYaw());
final float deltaPitch = Math.abs(player.getMovement().getDeltaPitch());
final float deltaX = deltaYaw / player.getMovement().getYawMode(),
deltaY = deltaPitch / player.getMovement().getPitchMode();
final double gridX = getGrid(player.getMovement().getYawGcdList()),
gridY = getGrid(player.getMovement().getPitchGcdList());
if(gridX < 0.005 || gridY < 0.005) lastGrid.reset();
if(deltaX > 200 || deltaY > 200) {
debug("sensitivity instability: mcp=%.4f, cx=%.4f, cy=%.4f, dx=%.1f, dy=%.1f",
player.getMovement().getSensitivityMcp(), player.getMovement().getCurrentSensX(),
player.getMovement().getCurrentSensY(), deltaX, deltaY);
if(buffer > 0) buffer--;
return;
}
boolean flagged = false;
if(player.getMovement().getPitchGCD() < 0.007 && lastGrid.isPassed()
&& !player.getMovement().isCinematic()
&& player.getMovement().getLastHighRate().isNotPassed(3)) {
if(deltaPitch < 10 && ++buffer > 8) {
flag("%s", player.getMovement().getPitchGCD());
}
flagged = true;
} else buffer = 0;
debug((flagged ? Color.Green : "") +"sensitivity: mcp=%.4f, cx=%.4f, cy=%.4f, dx=%.1f, dy=%.1f",
player.getMovement().getSensitivityMcp(), player.getMovement().getCurrentSensX(),
player.getMovement().getCurrentSensY(), deltaX, deltaY);
};
/*
* This is an attempt to reverse the logistics of cinematic camera without having to run a full on prediction using
* mouse filters. Otherwise, we would need to run more heavy calculations which is not really production friendly.
* It may be more accurate, but it is not really worth it if in the end of the day we're eating server performance.
*/
protected static double getGrid(final List<Float> entry) {
/*
* We're creating the variables average min and max to start calculating the possibility of cinematic camera.
* Why does this work? Cinematic camera is essentially a slowly increasing slowdown (which is why cinematic camera
* becomes slower the more you use it) which in turn makes it so the min max and average are extremely close together.
*/
double average = 0.0;
double min = 0.0, max = 0.0;
/*
* These are simple min max calculations done manually for the sake of simplicity. We're using the numbers 0.0
* since we also want to account for the possibility of a negative number. If there are no negative numbers then
* there is absolutely no need for us to care about that number other than getting the max.
*/
for (final double number : entry) {
if (number < min) min = number;
if (number > max) max = number;
/*
* Instead of having a sum variable we can use an average variable which we divide
* right after the loop is over. Smart programming trick if you want to use it.
*/
average += number;
}
/*
* We're dividing the average by the length since this is the formula to getting the average.
* Specifically its (sum(n) / length(n)) = average(n) -- with n being the entry set we're analyzing.
*/
average /= entry.size();
/*
* This is going to estimate how close the average and the max were together with the possibility of a min
* variable which is going to represent a negative variable since the preset variable on min is 0.0.
*/
return (max - average) - min;
}
}
@@ -0,0 +1,44 @@
package dev.brighten.ac.check.impl.combat.autoclicker;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInArmAnimation;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import dev.brighten.ac.utils.annotation.Async;
@CheckData(name = "AutoClicker (A)", checkId = "autoclickera", type = CheckType.AUTOCLICKER, maxVersion = ProtocolVersion.V1_8_9)
public class AutoclickerA extends Check {
public AutoclickerA(APlayer player) {
super(player);
}
private int flyingTicks, cps;
@Async
WAction<WPacketPlayInFlying> flying = (packet) -> {
flyingTicks++;
if(flyingTicks >= 20) {
if(cps > 22) {
if(cps > 30) {
punish();
}
flag("cps=%s", cps);
}
cps = 0;
}
};
@Async
WAction<WPacketPlayInArmAnimation> armAnimation = packet -> {
if(!player.getInfo().breakingBlock
&& player.getInfo().getLastBlockDig().isPassed(1)
&& player.getInfo().getLastBlockPlace().isPassed(1)) {
cps++;
}
debug("breaking=%s", player.getInfo().breakingBlock);
};
}
@@ -0,0 +1,34 @@
package dev.brighten.ac.check.impl.combat.autoclicker;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.WTimedAction;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInArmAnimation;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
@CheckData(name = "Autoclicker (B)", checkId = "autoclickerb", type = CheckType.AUTOCLICKER)
public class AutoclickerB extends Check {
public AutoclickerB(APlayer player) {
super(player);
}
private long lastFlying;
private int buffer;
WTimedAction<WPacketPlayInFlying> flyingPacket = (packet, timestamp) -> {
if(player.getMovement().getLastTeleport().isPassed(1))
lastFlying = timestamp;
};
WTimedAction<WPacketPlayInArmAnimation> animation = (packet, timestamp) -> {
if(timestamp - lastFlying < 10 && player.getLagInfo().getLastPacketDrop().isPassed(1)) {
if(++buffer > 4) {
flag("delta=%s", timestamp - lastFlying);
}
} else if(buffer > 0) buffer--;
debug("delta=%sms", timestamp - lastFlying);
};
}
@@ -0,0 +1,85 @@
package dev.brighten.ac.check.impl.combat.autoclicker;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WTimedAction;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInArmAnimation;
import dev.brighten.ac.utils.annotation.Async;
@CheckData(name = "Autoclicker (C)", checkId = "autoclickerc", type = CheckType.AUTOCLICKER)
public class AutoclickerC extends Check {
public AutoclickerC(APlayer player) {
super(player);
}
private long totalClickTime, lastClickTime;
private int clicks, oscillationTime, oscillationLevel, lowest, highest;
@Async
WTimedAction<WPacketPlayInArmAnimation> action = (packet, timeStamp) -> {
if(player.getInfo().isBreakingBlock()) return;
clicks++;
long diff = timeStamp - lastClickTime;
if ((totalClickTime += diff) > 990) {
if (clicks >= 3 && diff <= 200.0f) {
int time = oscillationTime + 1;
int lowest = this.lowest;
int highest = this.highest;
if (lowest == -1) {
lowest = clicks;
} else if (clicks < lowest) {
lowest = clicks;
}
if (highest == -1) {
highest = clicks;
} else if (clicks > highest) {
highest = clicks;
}
int oscillation = highest - lowest;
int oscLevel = oscillationLevel;
if (time >= 9) {
if (highest >= 8) {
if (highest >= 9 && oscillation <= 5) {
oscLevel += 2;
}
if (oscillation > 3 && oscLevel > 0) {
--oscLevel;
}
} else if (oscLevel > 0) {
--oscLevel;
}
time = 0;
highest = -1;
lowest = -1;
}
if (oscillation > 2) {
time = 0;
oscLevel = 0;
highest = -1;
lowest = -1;
}
if (oscLevel >= 10) {
vl++;
flag("osc=" + oscLevel);
}
debug("osc=%s level=%s high=%s low=%s", oscillation, oscLevel, highest, lowest);
this.lowest = lowest;
this.highest = highest;
this.oscillationTime = time;
this.oscillationLevel = oscLevel;
}
totalClickTime = 0;
clicks = 1;
}
lastClickTime = timeStamp;
};
}
@@ -0,0 +1,68 @@
package dev.brighten.ac.check.impl.combat.autoclicker;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.check.WTimedAction;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInArmAnimation;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInBlockPlace;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import dev.brighten.ac.utils.math.cond.MaxDouble;
import org.bukkit.Material;
import java.util.Arrays;
import java.util.EnumSet;
@CheckData(name = "Autoclicker (D)", checkId = "autoclickerd", type = CheckType.AUTOCLICKER, punishVl = 15,
maxVersion = ProtocolVersion.V1_8_9)
public class AutoclickerD extends Check {
public AutoclickerD(APlayer player) {
super(player);
}
private static final EnumSet<Material> SWORDS = EnumSet.allOf(Material.class);
static {
Arrays.stream(Material.values()).filter(material -> material.name().contains("SWORD"))
.forEach(SWORDS::add);
}
private long lastArm;
private double cps;
private boolean blocked;
private int armTicks;
private final MaxDouble verbose = new MaxDouble(40);
WTimedAction<WPacketPlayInArmAnimation> animation = (packet, timeStamp) -> {
if(player.getInfo().isBreakingBlock()) return;
cps = 1000D / (timeStamp - lastArm);
lastArm = timeStamp;
armTicks++;
};
WAction<WPacketPlayInFlying> flying = packet -> {
if(blocked) {
if(armTicks > 0) {
if(armTicks == 1 && cps > 3) {
if(cps > 7) verbose.add();
if(verbose.value() > 15) {
flag("arm=%s cps=%.3f lagging=%s", armTicks,
cps, player.getLagInfo());
}
} else verbose.subtract(20);
debug("cps=%s arm=%s vb=%s", cps, armTicks, verbose.value());
}
blocked = false;
armTicks = 0;
}
};
WAction<WPacketPlayInBlockPlace> place = packet -> {
if(packet.getItemStack() == null || !packet.getItemStack().getType().name().contains("SWORD")) return;
blocked = true;
};
}
@@ -0,0 +1,43 @@
package dev.brighten.ac.check.impl.combat.killaura;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInArmAnimation;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInUseEntity;
import lombok.val;
@CheckData(name = "KillAura (Bot)", checkId = "kabot", type = CheckType.KILLAURA)
public class KABot extends Check {
public KABot(APlayer player) {
super(player);
}
private int buffer = 0;
private float buffer2;
WAction<WPacketPlayInArmAnimation> arm = packet -> {
// We want to go ahead and lower the buffer every time they miss the bot to help prevent false positives
if(buffer2 > 0) buffer2-= 0.25f;
};
WAction<WPacketPlayInUseEntity> packet = packet -> {
val optional = player.getEntityLocationHandler().getFakeMob(packet.getEntityId());
if(optional.isPresent() && player.getEntityLocationHandler().clientHasEntity.get()) {
if(++buffer > 3) {
flag("Attacked player without attacking bot!");
}
} else buffer = 0;
if(player.getMob().getEntityId() == packet.getEntityId()) {
if(++buffer2 > 3) {
buffer = 2;
flag("Player attacked bot");
}
} else buffer2 = 0;
};
}
@@ -0,0 +1,106 @@
package dev.brighten.ac.check.impl.combat.killaura;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInUseEntity;
import dev.brighten.ac.utils.KLocation;
import dev.brighten.ac.utils.world.CollisionBox;
import dev.brighten.ac.utils.world.EntityData;
import dev.brighten.ac.utils.world.types.RayCollision;
import dev.brighten.ac.utils.world.types.SimpleCollisionBox;
import org.bukkit.util.Vector;
/**
* @author funkemunky
* @since 12/31/2020
* This check is designed to detect an attack through solid blocks, which would be impossible under normal
* circumstances.
*/
@CheckData(name = "KillAura (Trace)", checkId = "katrace", type = CheckType.KILLAURA)
public class KATrace extends Check {
private float sneakY = 1.54f;
public KATrace(APlayer player) {
super(player);
// We're caching the player's sneak height here, so we don't have to do it every time.
sneakY = player.getPlayerVersion().isBelow(ProtocolVersion.V1_14) ? 1.27f : 1.54f;
}
private int buffer;
WAction<WPacketPlayInUseEntity> useEntity = packet -> {
if(player.getInfo().getTarget() == null
|| packet.getAction() != WPacketPlayInUseEntity.EnumEntityUseAction.ATTACK)
return;
// If the player isn't looking at the target, then a raytrace check wouldn't work.
if(player.getMovement().getLookingAtBoxes().size() == 0) {
debug("No boxes to look at!");
buffer = 0;
return;
}
// Getting the target's bounding box
SimpleCollisionBox targetBox = (SimpleCollisionBox) EntityData.getEntityBox(player.getInfo().getTarget()
.getLocation(),
player.getInfo().target);
if(targetBox == null) return;
final KLocation origin = player.getMovement().getTo().getLoc().clone();
// Setting the player's eye height based on their sneak status
origin.y+= player.getInfo().isSneaking() ? sneakY : 1.62f;
final Vector originVec = origin.toVector();
// Setting a trace based on their view direction
RayCollision collision = new RayCollision(originVec, origin.getDirection());
Vector targetPoint = collision.collisionPoint(targetBox);
//If the ray isn't collided, we might as well not run this check. Just a simple boxes on array check
if(targetPoint == null) return;
// The distance bteween the player's eye and the intersect point of the ray and the target's bounding box.
// We don't do the square root to save on performance.
double dist = originVec.distanceSquared(targetPoint);
boolean rayCollidedOnBlock = false;
// Grabbing the boxes found on the ray trace. This is already grabbed in MovementProcessor so we can use
// the result from there in this check to save on compute time.
synchronized (player.getMovement().getLookingAtBoxes()) {
for (CollisionBox lookingAtBox : player.getMovement().getLookingAtBoxes()) {
if((lookingAtBox instanceof SimpleCollisionBox)) {
SimpleCollisionBox box = (SimpleCollisionBox) lookingAtBox;
if(box.minX % 1 != 0 || box.minY % 1 != 0 || box.minZ % 1 != 0
|| box.maxX % 1 != 0 || box.maxY % 1 != 0 || box.maxZ % 1 != 0)
continue;
// We want to shrink the box slightly since there is a bit of a margin of error in the ray trace.
Vector point = collision.collisionPoint(box.copy().shrink(0.005f, 0.005f, 0.005f));
if (point != null && originVec.distanceSquared(point) < dist - 0.2) {
rayCollidedOnBlock = true;
break;
}
}
}
}
if(rayCollidedOnBlock) {
// The buffer is here to smooth out false positives.
if(++buffer > 2) {
flag("Attacker hit through block! [b=%s s=%s]",
buffer, player.getMovement().getLookingAtBoxes().size());
}
} else if(buffer > 0) buffer--;
debug("b=%s collides=%s", buffer, rayCollidedOnBlock);
};
}
@@ -0,0 +1,27 @@
package dev.brighten.ac.check.impl.misc;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.data.APlayer;
import lombok.val;
import org.bukkit.event.player.PlayerEditBookEvent;
@CheckData(name = "BookOp", checkId = "bookop", type = CheckType.EXPLOIT)
public class BookOp extends Check {
public BookOp(APlayer player) {
super(player);
}
WAction<PlayerEditBookEvent> bookEdit = event -> {
val optional = event.getNewBookMeta().getPages().stream()
.filter(string -> string.toLowerCase().contains("run_command"))
.findFirst();
if(optional.isPresent()) {
vl++;
flag("line=" + optional.get());
event.setCancelled(true);
}
};
}
@@ -0,0 +1,32 @@
package dev.brighten.ac.check.impl.misc;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WCancellable;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.handler.HandlerAbstract;
import dev.brighten.ac.packet.wrapper.objects.WrappedWatchableObject;
import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutEntityMetadata;
@CheckData(name = "HealthSpoof", checkId = "healthspoof", type = CheckType.EXPLOIT)
public class HealthSpoof extends Check {
public HealthSpoof(APlayer player) {
super(player);
}
WCancellable<WPacketPlayOutEntityMetadata> event = packet -> {
if(packet.getEntityId() == player.getBukkitPlayer().getEntityId()) return false;
for (WrappedWatchableObject watchedObject : packet.getWatchedObjects()) {
if (watchedObject.getDataValueId() == 6 && watchedObject.getWatchedObject() instanceof Float) {
watchedObject.setWatchedObject(1f);
HandlerAbstract.getHandler().sendPacketSilently(player, packet.getPacket());
return true;
}
}
return false;
};
}
@@ -0,0 +1,40 @@
package dev.brighten.ac.check.impl.misc.inventory;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WAction;
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.annotation.Async;
@CheckData(name = "Inventory (Move)", checkId = "inventoryA", type = CheckType.INVENTORY, maxVersion = ProtocolVersion.V1_11)
public class InventoryA extends Check {
public InventoryA(APlayer player) {
super(player);
}
public int buffer;
@Async
public WAction<WPacketPlayInFlying> flying = packet -> {
if(player.getInfo().isGeneralCancel()) return;
// Running inventory check
final int STRAFING = player.EMULATOR.getInput().getStrafing();
final int FORWARD = player.EMULATOR.getInput().getForward();
if((STRAFING != 0 || FORWARD != 0)
&& (player.getInfo().isInventoryOpen())) {
if(buffer++ > 6) {
buffer = Math.min(8, buffer);
flag("s=%s f=%s", STRAFING, FORWARD);
}
} else if(buffer > 0) buffer--;
debug("buffer=%d inv=%s s=%.2f f=%.2f", buffer,
player.getInfo().isInventoryOpen(), STRAFING, FORWARD);
};
}
@@ -0,0 +1,25 @@
package dev.brighten.ac.check.impl.misc.inventory;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInWindowClick;
@CheckData(name = "Inventory (BadClick)", checkId = "inventoryB", type = CheckType.INVENTORY, maxVersion = ProtocolVersion.V1_11)
public class InventoryB extends Check {
public InventoryB(APlayer player) {
super(player);
}
WAction<WPacketPlayInWindowClick> windowClick = packet -> {
if(!player.getInfo().isInventoryOpen()) {
flag("Inventory not open");
}
debug("inv=%s", player.getInfo().isInventoryOpen());
};
}
@@ -0,0 +1,51 @@
package dev.brighten.ac.check.impl.misc.inventory;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInWindowClick;
@CheckData(name = "Inventory (ClickMove)", checkId = "inventoryc", type = CheckType.INVENTORY)
public class InventoryC extends Check {
public InventoryC(APlayer player) {
super(player);
}
private int lastWindowClick;
// Updating the last time the player clicked in a menu for use in the below check for positional movement.
WAction<WPacketPlayInWindowClick> windowClick = packet -> lastWindowClick = player.getPlayerTick();
WAction<WPacketPlayInFlying> flying = packet -> {
// If the player isn't sending any rotational or positional updates, then we don't need to check them.
if((!packet.isMoved()
|| player.getPlayerTick() < 10 // Can false flag on join
|| (player.getMovement().getDeltaX() == 0 && player.getMovement().getDeltaZ() == 0))
&& !packet.isLooked()) return;
// The player is not moving, therefore we do not check when they last clicked since this behavior
// is legitimate.
if(player.EMULATOR.getInput().getForward() == 0 && player.EMULATOR.getInput().getStrafing() == 0)
return;
// Any of these could result in false positives as our emulator does not account for these things yet.
if(player.getInfo().lastLiquid.isNotPassed(3)
|| player.getMovement().getLastTeleport().isNotPassed(2)
|| player.getInfo().climbTimer.isNotPassed(2)
|| player.getBlockInfo().pistonNear) {
return;
}
final int CLICK_DELTA = player.getPlayerTick() - lastWindowClick;
// If they recently clicked in an inventory while getting passed all of these previous checks, they are most
// likely using an illegal module to allow them to move with an open menu.
if(CLICK_DELTA <= 1) {
flag("Clicked while moving [%s, %s]",
player.EMULATOR.getInput().getForward(), player.EMULATOR.getInput().getStrafing());
}
};
}
@@ -0,0 +1,133 @@
package dev.brighten.ac.check.impl.movement;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.check.WCancellable;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutPosition;
import dev.brighten.ac.utils.*;
import dev.brighten.ac.utils.world.types.SimpleCollisionBox;
import org.bukkit.Location;
import org.bukkit.util.Vector;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@CheckData(name = "Phase", checkId = "phase", type = CheckType.MOVEMENT)
public class Phase extends Check {
public Phase(APlayer player) {
super(player);
}
private int ticks;
private final Set<Vector> POSITIONS = new HashSet<>();
private Location teleportLoc = null;
WAction<WPacketPlayOutPosition> positionOut = packet -> {
KLocation loc = new KLocation(packet.getX(), packet.getY(), packet.getZ(),
packet.getYaw(), packet.getPitch());
if (packet.getFlags().contains(WPacketPlayOutPosition.EnumPlayerTeleportFlags.X)) {
loc.x += player.getMovement().getTo().getLoc().x;
}
if (packet.getFlags().contains(WPacketPlayOutPosition.EnumPlayerTeleportFlags.Y)) {
loc.y += player.getMovement().getTo().getLoc().y;
}
if (packet.getFlags().contains(WPacketPlayOutPosition.EnumPlayerTeleportFlags.Z)) {
loc.z += player.getMovement().getTo().getLoc().z;
}
if (packet.getFlags().contains(WPacketPlayOutPosition.EnumPlayerTeleportFlags.X_ROT)) {
loc.pitch += player.getMovement().getTo().getLoc().pitch;
}
if (packet.getFlags().contains(WPacketPlayOutPosition.EnumPlayerTeleportFlags.Y_ROT)) {
loc.yaw += player.getMovement().getTo().getLoc().yaw;
}
POSITIONS.add(loc.toVector());
};
WCancellable<WPacketPlayInFlying> packet = (packet) -> {
if(packet.isMoved() && ticks < 3) {
ticks++;
return false;
}
if(!packet.isMoved() || player.getCreation().isNotPassed(800L)
|| player.getInfo().lastRespawn.isNotPassed(10)
|| player.getInfo().isCreative() || player.getInfo().isCanFly()) {
return false;
}
if(POSITIONS.size() > 0 && !packet.isOnGround() && POSITIONS.stream()
.anyMatch(v -> v.distanceSquared(player.getMovement().getTo().getLoc().toVector()) < 0.0001)) {
debug("Returned: [%s, %s, %s]", player.getMovement().getTo().getX(),
player.getMovement().getTo().getY(), player.getMovement().getTo().getZ());
teleportLoc = null;
return false;
} else if(player.getMovement().getMoveTicks() > 0 && player.getMovement().getTeleportsToConfirm() == 0) {
POSITIONS.clear();
} else if(teleportLoc != null) {
RunUtils.task(() -> player.getBukkitPlayer().teleport(teleportLoc));
return true;
}
if(player.getMovement().getFrom().getLoc().distanceSquared(player.getMovement().getTo().getLoc()) > 400) {
MiscUtils.printToConsole(player.getBukkitPlayer().getName() + " moved too fast!");
RunUtils.task(() -> player.getBukkitPlayer().teleport(player.getMovement().getFrom().getLoc()
.toLocation(player.getBukkitPlayer().getWorld())));
return true;
}
SimpleCollisionBox fromBox = player.getMovement().getFrom().getBox().copy(), toBox = fromBox.copy();
double deltaX = player.getMovement().getDeltaX(), deltaY = player.getMovement().getDeltaY(),
deltaZ = player.getMovement().getDeltaZ();
List<SimpleCollisionBox> collisions = Helper.getCollisionsNoEntities(player,
fromBox.copy().addCoord(deltaX, deltaY, deltaZ), Materials.SOLID);
for (SimpleCollisionBox collision : collisions) {
deltaY = collision.calculateYOffset(toBox, deltaY);
}
toBox.offset(0, deltaY, 0);
for (SimpleCollisionBox collision : collisions) {
deltaX = collision.calculateXOffset(toBox, deltaX);
}
toBox.offset(deltaX, 0, 0);
for (SimpleCollisionBox collision : collisions) {
deltaZ = collision.calculateZOffset(toBox, deltaZ);
}
toBox.offset(0, 0, deltaZ);
KLocation calculatedTo = player.getMovement().getFrom().getLoc().clone().add(deltaX, deltaY, deltaZ);
double dx = Math.abs(deltaX - player.getMovement().getDeltaX()),
dy = Math.abs(deltaY - player.getMovement().getDeltaY()),
dz = Math.abs(deltaZ - player.getMovement().getDeltaZ());
double totalDelta = dx + dy + dz;
if(totalDelta > 0.00001) {
RunUtils.task(() -> {
teleportLoc = calculatedTo
.toLocation(player.getBukkitPlayer().getWorld());
player.getBukkitPlayer().teleport(teleportLoc);
});
flag("x=%.4f, y=%.4f, z=%.4f", dx, dy, dz);
}
debug("(%s) [%.5f]: new=[%.3f, %.3f, %.3f] old=[%.3f, %.3f, %.3f]", collisions.size(), totalDelta,
deltaX, deltaY, deltaZ, player.getMovement().getDeltaX(), player.getMovement().getDeltaY(),
player.getMovement().getDeltaZ());
return false;
};
}
@@ -0,0 +1,182 @@
package dev.brighten.ac.check.impl.movement.fly;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WAction;
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.Color;
import dev.brighten.ac.utils.Helper;
import dev.brighten.ac.utils.MathUtils;
import dev.brighten.ac.utils.MovementUtils;
import dev.brighten.ac.utils.timer.Timer;
import dev.brighten.ac.utils.timer.impl.MillisTimer;
import dev.brighten.ac.utils.timer.impl.TickTimer;
import dev.brighten.ac.utils.world.types.SimpleCollisionBox;
import org.bukkit.util.Vector;
import java.util.List;
@CheckData(name = "Fly (Predict)", checkId = "flya", type = CheckType.MOVEMENT, experimental = true, punishVl = 7)
public class FlyA extends Check {
public FlyA(APlayer player) {
super(player);
}
private static final double DRAG = 0.98f;
private static final double HIT_BLOCK = 1. / 64.;
private final Timer LAST_POS = new MillisTimer(), LAST_COLLIDE = new TickTimer();
private float buffer;
private boolean didNextPrediction = false;
WAction<WPacketPlayInFlying> flying = packet -> {
if(!packet.isMoved() || (player.getMovement().getDeltaXZ() == 0
&& player.getMovement().getDeltaY() == 0)) {
return;
}
boolean onGround = player.getMovement().getTo().isOnGround() && player.getBlockInfo().blocksBelow,
fromGround = player.getMovement().getFrom().isOnGround();
double lDeltaY = player.getMovement().getLDeltaY();
// Initial acceleration prediction the vanilla client does
double predicted = (lDeltaY - 0.08) * DRAG;
boolean jumped = false;
if((fromGround && !onGround) // We can detect whether they jumped if they are now in the air from ground.
// If they accelerated upward
|| (player.getMovement().getDeltaY() > player.getMovement().getLDeltaY() && !onGround)
&& player.getMovement().getDeltaY() > 0) { //They must be going upward.
predicted = MovementUtils.getJumpHeight(player);
jumped = true;
}
double threshold = 0.000005;
// Checking if they collided recently and accounting for the truncated deltaY
if(LAST_COLLIDE.isNotPassed(2) // Loose to prevent any missed cases
&& player.getMovement().getLDeltaY() > 0
&& player.getMovement().getDeltaY() < player.getMovement().getLDeltaY()
&& player.getMovement().getLDeltaY() % HIT_BLOCK < 0.0126) {
predicted = -0.08 * DRAG;
debug("Truncated deltaY");
threshold = 0.1;
}
// There will be missed movements that we can't account for if we had to predict the player's next position
// twice, so we shouldn't flag regardless.
boolean willBeWeirdNext = didNextPrediction;
didNextPrediction = false;
// Since the player skipped a flying packet, the client likely didn't send a small position update
// This is to go ahead and account for that just in case the >60ms delta is caused by a < 9.0E-4 small movement
// on all axis. See net.minecraft.client.entity.EntityPlayerSP#onUpdateWalkingPlayer method
if(LAST_POS.isPassed(60L)) {
double toCheck = (predicted - 0.08) * DRAG;
if(Math.abs(player.getMovement().getDeltaY() - toCheck)
< Math.abs(player.getMovement().getDeltaY() - predicted)) {
predicted = toCheck;
didNextPrediction = true;
}
threshold = 0.2;
}
// Vanilla wrapping the deltaY to 0 if it's less than a certain amount.
if(player.getPlayerVersion().isBelow(ProtocolVersion.V1_9)) {
if(Math.abs(predicted) < 0.005)
predicted = 0;
} else if(Math.abs(predicted) < 0.003) {
predicted = 0;
debug("Setting y to 0");
}
// Accounting for the potential vertical velocity
for (Vector possibleVector : player.getVelocityHandler().getPossibleVectors()) {
double deltaVelocity = MathUtils.getDelta(possibleVector.getY(), player.getMovement().getDeltaY());
if(deltaVelocity < MathUtils.getDelta(predicted, player.getMovement().getDeltaY())) {
predicted = possibleVector.getY();
}
threshold+= 0.05;
}
// Vanilla collision algorithm to correct any false positives related to modified deltaY related to ground
// collision.
if(player.getBlockInfo().blocksBelow || player.getBlockInfo().blocksAbove
|| player.getInfo().isNearGround()) {
List<SimpleCollisionBox> list = Helper.getCollisions(player,
player.getMovement().getFrom().getBox().copy().addCoord(player.getMovement().getDeltaX(), predicted,
player.getMovement().getDeltaZ()));
SimpleCollisionBox axisalignedbb4 = player.getMovement().getFrom().getBox();
SimpleCollisionBox axisalignedbb5 = axisalignedbb4.copy().addCoord(player.getMovement().getDeltaX(),
0.0D, player.getMovement().getDeltaZ());
double d9 = predicted;
for (SimpleCollisionBox axisalignedbb6 : list) {
d9 = axisalignedbb6.calculateYOffset(axisalignedbb5, d9);
}
if(predicted != d9) {
LAST_COLLIDE.reset(); // Setting the last collide for later use
}
predicted = d9;
}
// These are all things that can cause potential issues.
if(player.getInfo().isGeneralCancel()
|| player.getMovement().getTeleportsToConfirm() > 0
|| player.getInfo().isOnLadder()
|| player.getInfo().climbTimer.isNotPassed(2)
|| player.getBlockInfo().inWeb
|| player.getBlockInfo().inScaffolding
|| player.getInfo().getLastLiquid().isNotPassed(2)
|| player.getBlockInfo().fenceBelow
|| !player.getInfo().worldLoaded
|| player.getBlockInfo().onSlime) {
if(buffer > 0) buffer-= 0.25f;
debug("Returned");
return;
}
double deltaPredict = MathUtils.getDelta(player.getMovement().getDeltaY(), predicted);
boolean flagged = false;
// We want it to be at 0.005 since that is the maximum variance from loss of precision
// We also don't really want to flag if there was a skip in flying related to < 9.0E-4 movement.
if(!willBeWeirdNext
&& LAST_COLLIDE.isPassed(1)
&& !player.getInfo().nearGround
&& deltaPredict > threshold) {
// This slight buffer is to account for anything this check may have not accounted for
// It is not a permanent fix. However, Java Edition is quite a fickle game to work with; it's quite
// difficult to reliably check that vanilla behavior is vanilla behavior without weird outliers.
if(++buffer > 1) {
buffer = 1;
flag("dY=%.3f p=%.3f dx=%.3f", player.getMovement().getDeltaY(), predicted,
player.getMovement().getDeltaXZ());
cancel();
}
flagged = true;
} else buffer-= buffer > 0 ? 0.25f : 0;
debug((flagged ? Color.Green : "")
+ "[%s] dY=%.3f ldy=%.3f p=%.3f dx=%.3f j=%s velocity=%s fg=%s g=%s lc=%s bbelow=%s ng=%s",
buffer, player.getMovement().getDeltaY(), player.getMovement().getLDeltaY(),
predicted, player.getMovement().getDeltaXZ(), jumped,
player.getInfo().getVelocity().getPassed(), fromGround, onGround, LAST_COLLIDE.getPassed(),
player.getBlockInfo().blocksBelow, player.getInfo().isNearGround());
LAST_POS.reset();
};
}
@@ -0,0 +1,97 @@
package dev.brighten.ac.check.impl.movement.fly;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import dev.brighten.ac.utils.MathUtils;
import dev.brighten.ac.utils.MovementUtils;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
@CheckData(name = "Fly (Height)", checkId = "flyb", type = CheckType.MOVEMENT)
public class FlyB extends Check {
public FlyB(APlayer player) {
super(player);
}
private double slimeY = 0;
WAction<WPacketPlayInFlying> flyingAction = packet -> {
if(player.getMovement().getLastTeleport().isNotPassed((1))
|| player.getMovement().getMoveTicks() <= 2
|| player.getInfo().isGeneralCancel())
return;
final boolean ground = player.getMovement().getTo().isOnGround(),
fground = player.getMovement().getFrom().isOnGround();
final boolean jumped = fground && !ground && player.getMovement().getDeltaY() > 0;
final List<Double> possibleHeights = new ArrayList<>();
// Adding only possible jump height with current circumstances
possibleHeights.add(MovementUtils.getJumpHeight(player));
if(player.getBlockInfo().onSlime && packet.isOnGround() && player.getMovement().getDeltaY() < 0) {
double ldeltaY = player.getMovement().getLDeltaY() * -1, deltaY = player.getMovement().getDeltaY() * -1;
if(ldeltaY > deltaY) {
deltaY+= ldeltaY;
}
slimeY = deltaY;
debug("SlimeY: " + slimeY);
} else if(!player.getInfo().wasOnSlime)
slimeY = 0;
// Adding all possible velocity deltaY.
player.getVelocityHandler().getPossibleVectors().forEach(vec -> possibleHeights.add(vec.getY()));
if(player.getInfo().lastHalfBlock.isNotPassed(1)
|| player.getInfo().lastFence.isNotPassed(1) || player.getBlockInfo().fenceNear) {
possibleHeights.add(0.5);
}
jumpCheck: {
if(!jumped || player.getInfo().blockAbove.isNotPassed(1)
|| player.getInfo().climbTimer.isNotPassed(1)
|| player.getInfo().wasOnSlime
|| player.getBlockInfo().nearSteppableEntity
|| player.getInfo().lastFence.isNotPassed(1)
|| player.getBlockInfo().fenceNear
|| player.getInfo().lastHalfBlock.isNotPassed(1)
|| player.getInfo().slimeTimer.isNotPassed(1)
|| player.getInfo().lastLiquid.isNotPassed(1)) break jumpCheck;
// We want to check all possible heights
for (Double possibleHeight : possibleHeights) {
double delta = MathUtils.getDelta(player.getMovement().getDeltaY(), possibleHeight);
if(delta < (player.getInfo().getLastBlockPlace().isNotPassed(20) ? 0.02 : 0.005)) {
debug("Found delta: dy=%.5f, p=%.5f", player.getMovement().getDeltaY(), possibleHeight);
break jumpCheck;
}
}
// If we reach this point, it means no correct predicted deltaY was found
flag("dy=%.5f p[%s]", player.getMovement().getDeltaY(), possibleHeights.stream()
.map(s -> String.valueOf(MathUtils.round(s, 5))).collect(Collectors.joining(";")));
}
if(player.getInfo().wasOnSlime)
possibleHeights.add(slimeY);
maximumHeightCheck: {
if(player.getInfo().nearGround || player.getBlockInfo().nearSteppableEntity) break maximumHeightCheck;
double maxHeight = possibleHeights.stream().max(Comparator.comparing(c -> c)).orElse(1.5) + 0.05f;
if(player.getMovement().getDeltaY() > maxHeight) {
flag("%.4f>-%.4f", player.getMovement().getDeltaY(), maxHeight);
}
}
};
}
@@ -0,0 +1,31 @@
package dev.brighten.ac.check.impl.movement.fly;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
@CheckData(name = "Fly (Acceleration)", checkId = "flyc", type = CheckType.MOVEMENT)
public class FlyC extends Check {
public FlyC(APlayer player) {
super(player);
}
WAction<WPacketPlayInFlying> flying = packet -> {
if(!packet.isMoved()
|| player.getInfo().blockAbove.isNotPassed(4)
|| player.getInfo().climbTimer.isNotPassed(2)
|| packet.isOnGround()
|| player.getInfo().getVelocity().isNotPassed(4)
|| player.getMovement().getMoveTicks() < 3
|| player.getInfo().isGeneralCancel()) return;
double acceleration = player.getMovement().getDeltaY() - player.getMovement().getLDeltaY();
if(acceleration < (player.getMovement().getDeltaXZ() < 0.1 ? -0.17 : -0.1)) {
flag("acceleration=%.4f", acceleration);
}
};
}
@@ -0,0 +1,37 @@
package dev.brighten.ac.check.impl.movement.fly;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import dev.brighten.ac.utils.annotation.Async;
@CheckData(name = "Fly (Increment)", checkId = "flyd", type = CheckType.MOVEMENT, experimental = true)
public class FlyD extends Check {
public FlyD(APlayer player) {
super(player);
}
private double totalY;
@Async
WAction<WPacketPlayInFlying> flyingPacket = packet -> {
if(!packet.isMoved() || player.getMovement().getMoveTicks() <= 2
|| player.getBlockInfo().miscNear || player.getBlockInfo().onSlab
|| player.getBlockInfo().fenceBelow || player.getBlockInfo().fenceNear
|| player.getBlockInfo().onStairs || player.getInfo().isGeneralCancel()) return;
double deltaY = player.getMovement().getDeltaY();
if(deltaY > 0)
totalY+= deltaY;
else {
if(totalY % 0.25 < 1E-6 && totalY > 0.6) {
flag("totalY=%s gt=%s", totalY, player.getMovement().getGroundTicks());
}
debug("totalY=%s", totalY);
totalY = 0;
}
};
}
@@ -0,0 +1,42 @@
package dev.brighten.ac.check.impl.movement.fly;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
@CheckData(name = "Fly (YPort)", checkId = "flye", type = CheckType.MOVEMENT)
public class FlyE extends Check {
public FlyE(APlayer player) {
super(player);
}
private int buffer;
WAction<WPacketPlayInFlying> flying = packet -> {
if(player.getInfo().getVelocity().isNotPassed(20)
|| player.getMovement().getMoveTicks() == 0
|| player.getInfo().isCreative()
|| player.getBlockInfo().blocksAbove
|| player.getMovement().getTeleportsToConfirm() > 0
|| player.getInfo().isCanFly()
|| player.getInfo().getLastElytra().isNotPassed(5))
return;
final double deltaY = player.getMovement().getDeltaY(),
lDeltaY = player.getMovement().getLDeltaY();
if(Math.abs(deltaY + lDeltaY) < 0.05 && player.getInfo().lastHalfBlock.isPassed(2)
&& player.getInfo().slimeTimer.isPassed(5)
&& Math.abs(deltaY) > 0.2) {
buffer+= 15;
if(buffer > 20) {
flag("dy=%s ly=%s", deltaY, lDeltaY);
buffer= 20; // Prevents flagging too much
}
} else if(buffer > 0) buffer--;
};
}
@@ -0,0 +1,61 @@
package dev.brighten.ac.check.impl.movement.nofall;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
@CheckData(name = "NoFall (A)", checkId = "nofalla", type = CheckType.MOVEMENT)
public class NoFallA extends Check {
public NoFallA(APlayer player) {
super(player);
}
private static double divisor = 1. / 64.;
private float buffer;
WAction<WPacketPlayInFlying> flying = packet -> {
if(!packet.isMoved()
|| player.getInfo().isGeneralCancel()
|| (player.getMovement().getDeltaXZ() == 0 && player.getMovement().getDeltaY() == 0)
|| player.getBlockInfo().inLiquid
|| player.getMovement().getTeleportsToConfirm() > 0
|| player.getInfo().velocity.isNotPassed(1)
|| player.getMovement().getLastTeleport().isNotPassed(1)) {
if(buffer > 0) buffer-= 0.5f;
return;
}
boolean onGround = packet.isOnGround();
boolean flag = false;
if(onGround) {
flag = Math.abs(player.getMovement().getDeltaY()) > 0.1
&& player.getInfo().slimeTimer.isPassed(2)
&& player.getInfo().getBlockAbove().isPassed(3)
&& !player.getInfo().isServerGround()
&& !player.getBlockInfo().fenceNear
&& (player.getMovement().getDeltaY() >= 0
&& (Math.abs(player.getMovement().getTo().getLoc().y) % divisor != 0
|| Math.abs(player.getMovement().getDeltaY()) % divisor != 0)
|| player.getMovement().getDeltaY() <= player.getMovement().getLDeltaY());
} else {
flag = player.getMovement().getDeltaY() == 0 && player.getMovement().getLDeltaY() == 0
&& player.getInfo().climbTimer.isPassed(3)
&& player.getInfo().slimeTimer.isPassed(2);
}
if(flag) {
if(++buffer > 1) {
flag("[%.1f] g=%s;dy=%.4f;ldy=%.4f", buffer, onGround,
player.getMovement().getDeltaY(), player.getMovement().getLDeltaY());
}
} else if(buffer > 0) buffer-= 0.25f;
debug("[%.1f] g=%s;dy=%.4f;ldy=%.4f", buffer, onGround,
player.getMovement().getDeltaY(), player.getMovement().getLDeltaY());
};
}
@@ -0,0 +1,68 @@
package dev.brighten.ac.check.impl.movement.nofall;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import dev.brighten.ac.utils.annotation.Async;
@CheckData(name = "NoFall (B)", checkId = "nofallb", type = CheckType.MOVEMENT)
public class NoFallB extends Check {
public NoFallB(APlayer player) {
super(player);
}
private static double divisor = 1. / 64.;
private int airBuffer, groundBuffer;
WAction<WPacketPlayInFlying> flying = packet -> {
if(player.getMovement().getLastTeleport().isNotPassed(3)
|| player.getMovement().getMoveTicks() < 2
|| player.getInfo().canFly
|| player.getInfo().creative
|| player.getBlockInfo().miscNear
|| player.getInfo().inVehicle
|| player.getInfo().climbTimer.isNotPassed(3)
|| player.getCreation().isNotPassed(2000L)
|| player.getInfo().slimeTimer.isNotPassed(3)) {
if(groundBuffer > 0) groundBuffer--;
if(airBuffer > 0) airBuffer--;
return; // If we are waiting for them to teleport, don't check.
}
// If they are saying they are on the ground
if(packet.isOnGround()
&& player.getInfo().vehicleSwitch.isPassed(20)
&& !player.getBlockInfo().blocksBelow
&& !player.getBlockInfo().blocksNear
&& !player.getInfo().isServerGround()) {
groundBuffer+= 2;
if(groundBuffer > 14) {
flag("[%s] g=%s;dy=%.4f;ldy=%.4f", groundBuffer, true,
player.getMovement().getDeltaY(), player.getMovement().getLDeltaY());
}
} else if(groundBuffer > 0) groundBuffer--;
final boolean dground = Math.abs(player.getMovement().getDeltaY()) % divisor < 1E-4
&& player.getInfo().isNearGround();
if(!packet.isOnGround() && player.getInfo().vehicleSwitch.isPassed(20)
&& !player.getBlockInfo().inLiquid
&& player.getInfo().lastWeb.isPassed(1)
&& ((player.getInfo().isServerGround() || player.getBlockInfo().blocksBelow)
&& dground && !player.getBlockInfo().onHalfBlock)) {
if((airBuffer +=10) > 30) {
flag("[%s] g=%s;dy=%.4f;ldy=%.4f", airBuffer, false,
player.getMovement().getDeltaY(), player.getMovement().getLDeltaY());
}
} else if(airBuffer > 0) airBuffer-= 4;
debug("[%s,%s] g=%s;sg-%s;bbelow=%s;dy=%.4f;ldy=%.4f", groundBuffer, airBuffer, packet.isOnGround(),
player.getInfo().isServerGround(), player.getBlockInfo().blocksBelow, player.getMovement().getDeltaY(),
player.getMovement().getLDeltaY());
};
}
@@ -0,0 +1,53 @@
package dev.brighten.ac.check.impl.movement.nofall;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import dev.brighten.ac.utils.MathUtils;
@CheckData(name = "NoFall (C)", checkId = "nofallc", type = CheckType.MOVEMENT, punishVl = 5, punishable = false, experimental = true)
public class NoFallC extends Check {
public NoFallC(APlayer player) {
super(player);
}
private double fallDistance, trueFallDistance;
WAction<WPacketPlayInFlying> flying = packet -> {
if(!packet.isMoved())
return;
if(player.getInfo().isGeneralCancel() || player.getMovement().getLastTeleport().isNotPassed(1)) {
fallDistance = trueFallDistance = 0;
return;
}
if(player.getMovement().getDeltaY() > 0) {
fallDistance = 0;
trueFallDistance = 0;
} else {
if(packet.isOnGround()) {
fallDistance = 0;
} else fallDistance+= player.getMovement().getDeltaY();
if(player.getBlockInfo().blocksBelow && packet.getY() % MathUtils.GROUND_DIVISOR == 0) {
trueFallDistance = 0;
fallDistance = 0;
} else trueFallDistance+= player.getMovement().getDeltaY();
double delta = MathUtils.getDelta(trueFallDistance, fallDistance);
if(delta > 0.1 && !player.getInfo().isNearGround()) {
flag("delta=%.4f;fd=%.4f;tf=%.4f", delta, fallDistance, trueFallDistance);
fallDistance = trueFallDistance = 0;
cancel();
}
debug("calc=%.3f, true=%.3f", fallDistance, trueFallDistance);
}
};
}
@@ -0,0 +1,88 @@
package dev.brighten.ac.check.impl.movement.speed;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WAction;
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.Color;
import dev.brighten.ac.utils.KLocation;
import dev.brighten.ac.utils.MathUtils;
import dev.brighten.ac.utils.timer.Timer;
import dev.brighten.ac.utils.timer.impl.TickTimer;
import lombok.val;
import me.hydro.emulator.util.Vector;
@CheckData(name = "Prediction", checkId = "predictiona", type = CheckType.MOVEMENT, experimental = true,
punishable = false)
public class Prediction extends Check {
private float buffer;
private boolean maybeSkippedPos;
private int lastFlying;
private final Timer lastSkipPos = new TickTimer();
public Prediction(APlayer player) {
super(player);
}
WAction<WPacketPlayInFlying> flying = packet -> {
check: {
if(!packet.isMoved()
|| player.getMovement().getLastTeleport().isNotPassed(1)
|| player.getInfo().getVelocity().isNotPassed(2)
|| player.getBlockInfo().onClimbable
|| player.getInfo().lastLiquid.isNotPassed(2)
|| player.getInfo().isGeneralCancel()) break check;
double offset = player.EMULATOR.getOffset();
int forward = player.EMULATOR.getInput().getForward();
int strafe = player.EMULATOR.getInput().getStrafing();
String tags = String.join(", ", player.EMULATOR.getTags());
Vector predicted = player.getMovement().getPredicted();
val to = player.getMovement().getTo();
double px = MathUtils.getDelta(predicted.getX(), to.getX()),
py = MathUtils.getDelta(predicted.getY(), to.getY()),
pz = MathUtils.getDelta(predicted.getZ(), to.getZ());
double totalMotion = px * px + py * py + pz * pz;
boolean zeroThree = totalMotion < 9E-4;
boolean badOffset = offset > (zeroThree ? 0.03 : 5E-9);
if(badOffset) {
debug("[%s] dx=%.6f px=%.6f dz=%.6f pz=%.6f dy=%.6f py=%.6f", zeroThree, player.getMovement().getDeltaX(),
px, player.getMovement().getDeltaZ(), pz,
player.getMovement().getDeltaY(), py);
KLocation loc = player.getMovement().getFrom().getLoc().clone()
.add(px, py, pz);
if(++buffer > 5) {
flag("%s", offset);
correctMovement(loc);
buffer = 4;
}
} else if(buffer > 0) buffer-= 0.1f;
debug((badOffset ? Color.Red : "") + "offset=%s f=%s s=%s py=%.3f [%s] tags=[%s]",
offset, forward, strafe, py, totalMotion, tags);
}
if (ProtocolVersion.getGameVersion().isBelow(ProtocolVersion.V1_9)) {
maybeSkippedPos = !packet.isMoved();
} else {
if (player.getPlayerTick() - lastFlying > 1) {
maybeSkippedPos = true;
debug("maybe skipped pos");
}
lastFlying = player.getPlayerTick();
}
if (maybeSkippedPos) {
lastSkipPos.reset();
}
};
}
@@ -0,0 +1,146 @@
package dev.brighten.ac.check.impl.movement.speed;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WAction;
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.PlayerUtils;
import dev.brighten.ac.utils.TagsBuilder;
import dev.brighten.ac.utils.XMaterial;
import org.bukkit.potion.PotionEffectType;
@CheckData(name = "Speed", checkId = "speeda", type = CheckType.MOVEMENT)
public class Speed extends Check {
private double ldxz = .12f;
private float friction = 0.6f;
private float buffer;
WAction<WPacketPlayInFlying> flying = packet -> {
if(player.getMovement().isExcuseNextFlying()) return;
checkProccesing:
{
if (!packet.isMoved())
break checkProccesing;
float drag = friction;
TagsBuilder tags = new TagsBuilder();
float moveFactor = player.getBukkitPlayer().getWalkSpeed() / 2f;
moveFactor+= moveFactor * 0.30000001192092896D;
if(player.getPotionHandler().hasPotionEffect(PotionEffectType.SPEED))
moveFactor += (PlayerUtils.getPotionEffectLevel(player.getBukkitPlayer(), PotionEffectType.SPEED)
* (0.20000000298023224D)) * moveFactor;
if(player.getPotionHandler().hasPotionEffect(PotionEffectType.SLOW))
moveFactor += (PlayerUtils.getPotionEffectLevel(player.getBukkitPlayer(), PotionEffectType.SLOW)
* (-0.15000000596046448D)) * moveFactor;
if (player.getMovement().getFrom().isOnGround()) {
tags.addTag("ground");
drag *= 0.91f;
moveFactor *= 0.16277136 / (drag * drag * drag);
if (player.getMovement().isJumped()) {
tags.addTag("jumped");
moveFactor += 0.2f;
}
} else {
tags.addTag("air");
drag = 0.91f;
moveFactor = 0.026f;
}
if(player.getBlockInfo().inWater) {
tags.addTag("water");
drag = player.getPlayerVersion().isOrAbove(ProtocolVersion.V1_13) ? 0.9f : 0.8f;
moveFactor = 0.034f;
if(player.getInfo().lastLiquid.getResetStreak() < 3) {
tags.addTag("water-enter");
moveFactor*= 1.35;
}
} else if(player.getInfo().lastLiquid.isNotPassed(3)) {
moveFactor*= 1.35;
tags.addTag("water-leave");
}
if(player.getMovement().getLastTeleport().isNotPassed(6)
|| player.getInfo().lastRespawn.isNotPassed(6)) {
tags.addTag("teleport");
moveFactor+= 0.1;
moveFactor*= 5;
}
//In 1.9+, entity collisions add acceleration to their movement.
if(player.getInfo().lastEntityCollision.isNotPassed(2)) {
tags.addTag("entity-collision");
moveFactor+= 0.05;
}
//Pistons have the ability to move players 1 whole block
if(player.getBlockInfo().pistonNear) {
tags.addTag("piston");
moveFactor+= 1;
}
if(player.getBlockInfo().inWeb
//Ensuring they aren't just entering or leaving web
&& player.getInfo().lastWeb.getResetStreak() > 1) {
tags.addTag("web");
moveFactor*= 0.4;
}
if(player.getBlockInfo().onSoulSand && player.getMovement().getFrom().isOnGround()
//Ensuring the player is actually standing on the block and recieving slow
&& packet.getY() % (1) == 0.875) {
tags.addTag("soulsand");
moveFactor*= 0.88;
}
double ratio = (player.getMovement().getDeltaXZ() - ldxz) / moveFactor * 100;
if (ratio > 100.8
&& !player.getBlockInfo().inHoney
&& !player.getBlockInfo().inScaffolding
&& player.getInfo().velocity.isPassed(2)
&& player.getInfo().lastLiquid.isPassed(2)
&& !player.getInfo().generalCancel) {
if(++buffer > 2) {
vl++;
flag("p=%.1f%% dxz=%.3f a/g=%s,%s aimove=%.3f tags=%s",
ratio, player.getMovement().getDeltaXZ(), player.getMovement().getAirTicks(),
player.getMovement().getGroundTicks(),
player.EMULATOR.getInput().getAiMoveSpeed(), tags.build());
buffer = Math.min(5, buffer); //Preventing runaway flagging
cancel();
} else if(ratio > 250) {
cancel();
debug("Cancelled user movement: %.1f", ratio);
}
} else if(buffer > 0) buffer-= 0.2f;
debug("ratio=%.1f tags=%s tp=%s buffer=%.1f", ratio, tags.build(),
player.getInfo().getLastLiquid().getPassed(), buffer);
ldxz = player.getMovement().getDeltaXZ() * drag;
}
friction = BlockUtils
.getFriction(
BlockUtils.getBlockAsync(player.getMovement()
.getTo().getLoc().clone().subtract(0, 1, 0).toLocation(
player.getBukkitPlayer().getWorld()))
.map(b -> XMaterial.matchXMaterial(b.getType()))
.orElse(XMaterial.STONE));
};
public Speed(APlayer player) {
super(player);
}
}
@@ -0,0 +1,59 @@
package dev.brighten.ac.check.impl.movement.velocity;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import org.bukkit.util.Vector;
@CheckData(name = "Velocity (Vertical)", checkId = "velocitya", type = CheckType.MOVEMENT)
public class VelocityA extends Check {
private Vector currentVelocity = null;
private float buffer = 0;
public VelocityA(APlayer player) {
super(player);
player.onVelocity(velocity -> {
currentVelocity = velocity.clone();
debug("did velocity: " + currentVelocity.getY());
});
}
WAction<WPacketPlayInFlying> flying = packet -> {
if(currentVelocity != null && currentVelocity.getY() > 0
&& !player.getBlockInfo().inWeb
&& !player.getBlockInfo().onClimbable
&& player.getInfo().getBlockAbove().isPassed(6)
&& !player.getBlockInfo().onSlime
&& !player.getInfo().isGeneralCancel()) {
double pct = player.getMovement().getDeltaY() / currentVelocity.getY() * 100;
if(currentVelocity.getY() < 0.005
|| player.getBlockInfo().collidesHorizontally
|| player.getInfo().getLastAbilities().isNotPassed(3)
|| player.getBlockInfo().collidesVertically
|| player.getInfo().getVelocity().isPassed(7)) {
currentVelocity = null;
return;
}
if(pct < 99.999 || pct > 400) {
if(++buffer > 15) {
flag("pct=%.1f%% buffer=%.1f", pct, buffer);
}
} else if(buffer > 0) buffer-= 0.5;
debug("pct=%.1f%% buffer=%.1f dy=%.4f vy=%.4f", pct, buffer,
player.getMovement().getDeltaY(), currentVelocity.getY());
currentVelocity.setY((currentVelocity.getY() - 0.08) * 0.98);
} else if(currentVelocity != null) {
debug("not null: " + currentVelocity.getY());
}
};
}
@@ -0,0 +1,327 @@
package dev.brighten.ac.check.impl.movement.velocity;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WAction;
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.KLocation;
import dev.brighten.ac.utils.MathHelper;
import dev.brighten.ac.utils.MathUtils;
import dev.brighten.ac.utils.math.IntVector;
import dev.brighten.ac.utils.timer.Timer;
import dev.brighten.ac.utils.timer.impl.TickTimer;
import lombok.AllArgsConstructor;
import lombok.val;
import org.bukkit.Material;
import org.bukkit.craftbukkit.v1_8_R3.util.CraftMagicNumbers;
import org.bukkit.potion.PotionEffectType;
import java.util.ArrayList;
import java.util.List;
@CheckData(name = "Velocity (Horizontal)", checkId = "velocityb", type = CheckType.MOVEMENT)
public class VelocityB extends Check {
private final Timer lastVelocity = new TickTimer();
public VelocityB(APlayer player) {
super(player);
player.onVelocity(velocity -> {
if (lastVelocity.isPassed(10)) { //Temp fix for combo mode
pvX = velocity.getX();
pvZ = velocity.getZ();
ticks = 1;
debug("did velocity: %.3f, %.3f", pvX, pvZ);
}
lastVelocity.reset();
});
}
private double pvX, pvZ;
private int ticks;
private double buffer;
private float friction;
private KLocation previousFrom;
private static final boolean[] TRUE_FALSE = new boolean[]{true, false};
public double strafe, forward;
WAction<WPacketPlayInFlying> flying = packet -> {
check:
{
if (ticks == 0) break check;
val underBlockLoc = previousFrom != null
? player.getMovement().getFrom().getLoc() : player.getMovement().getTo().getLoc();
Material underMaterial = player.getBlockUpdateHandler()
.getBlock(new IntVector(MathHelper.floor_double(underBlockLoc.x),
MathHelper.floor_double(underBlockLoc.y - 1),
MathHelper.floor_double(underBlockLoc.z)))
.getType();
if (player.getMovement().getMoveTicks() == 0
|| player.getInfo().isGeneralCancel()
|| player.getBlockInfo().onClimbable
|| player.getInfo().lastWeb.isNotPassed(2)
|| player.getInfo().lastLiquid.isNotPassed(2)
|| player.getBlockInfo().collidesHorizontally) {
ticks = 0;
break check;
}
// Too small of velocity
if(MathUtils.hypotSqrt(pvX, pvZ) < 0.025) {
pvX = pvZ = 0;
ticks = 0;
break check;
}
double smallestDelta = Double.MAX_VALUE;
double pmotionx = 0, pmotionz = 0;
boolean onGround = player.getMovement().getFrom().isOnGround();
val speed = player.getPotionHandler().getEffectByType(PotionEffectType.SPEED);
val slow = player.getPotionHandler().getEffectByType(PotionEffectType.SLOW);
for (Iteration iteration : iterations) {
float forward = iteration.f, strafe = iteration.s;
if (iteration.sneaking) {
forward *= 0.3;
strafe *= 0.3;
}
float friction = CraftMagicNumbers.getBlock(underMaterial).frictionFactor;
if (iteration.using) {
forward *= 0.2;
strafe *= 0.2;
}
//Multiplying by 0.98 like in client
forward *= 0.9800000190734863F;
strafe *= 0.9800000190734863F;
double aiMoveSpeed = player.getBukkitPlayer().getWalkSpeed() / 2;
float drag = 0.91f;
double lmotionX = pvX,
lmotionZ = pvZ;
//Running multiplication done after previous prediction
if (player.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 (iteration.attack) {
lmotionX *= 0.6;
lmotionZ *= 0.6;
}
if (iteration.sprinting)
aiMoveSpeed += aiMoveSpeed * 0.30000001192092896D;
if (speed.isPresent())
aiMoveSpeed += (speed.get().getAmplifier() + 1) * 0.20000000298023224D * aiMoveSpeed;
if (slow.isPresent())
aiMoveSpeed += (slow.get().getAmplifier() + 1) * -0.15000000596046448D * aiMoveSpeed;
float f5;
if (onGround) {
drag *= friction;
f5 = (float) (aiMoveSpeed * (0.16277136F / (drag * drag * drag)));
if (iteration.sprinting && iteration.jumped) {
float rot = player.getMovement().getTo().getLoc().yaw * 0.017453292F;
lmotionX -= sin(iteration.fastMath, rot) * 0.2F;
lmotionZ += cos(iteration.fastMath, rot) * 0.2F;
}
} else f5 = iteration.sprinting ? 0.025999999F : 0.02f;
if (player.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(iteration.fastMath,
player.getMovement().getTo().getLoc().yaw * (float) Math.PI / 180.F),
yawCos = cos(iteration.fastMath,
player.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(iteration.fastMath,
player.getMovement().getTo().getLoc().yaw * (float) Math.PI / 180.F),
yawCos = cos(iteration.fastMath,
player.getMovement().getTo().getLoc().yaw * (float) Math.PI / 180.F);
lmotionX += (strafe * yawCos - forward * yawSin);
lmotionZ += (forward * yawCos + strafe * yawSin);
}
}
double diffX = player.getMovement().getDeltaX() - lmotionX,
diffZ = player.getMovement().getDeltaZ() - lmotionZ;
double delta = (diffX * diffX) + (diffZ * diffZ);
if (delta < smallestDelta) {
smallestDelta = delta;
pmotionx = lmotionX;
pmotionz = lmotionZ;
this.friction = friction;
if (delta < 4E-17) {
this.strafe = iteration.s * 0.98f;
this.forward = iteration.f * 0.98f;
break;
}
}
}
if (++ticks > 6) {
ticks = 0;
}
double pmotion = Math.hypot(pmotionx, pmotionz);
double ratio = Math.abs(player.getMovement().getDeltaXZ() / pmotion);
if (ratio < 0.992) {
if (++buffer > 10) {
flag("p=%.1f%%", ratio);
}
} else if (buffer > 0) buffer -= 0.2;
debug("r=%.4f smallest=%s pm=%.5f dxz=%.5f b=%.1f f/s=%.2f,%.2f soulsand=%s ",
ratio, smallestDelta, pmotion, player.getMovement().getDeltaXZ(), buffer, forward, strafe,
player.getBlockInfo().onSoulSand);
pvX = pmotionx * (0.9100000262260437 * (player.getMovement().getFrom().isOnGround() ? friction : 1));
pvZ = pmotionz * (0.9100000262260437 * (player.getMovement().getFrom().isOnGround() ? friction : 1));
}
previousFrom = player.getMovement().getFrom().getLoc().clone();
};
/*
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) {
*/
@AllArgsConstructor
private static class Iteration {
final int f, s, fastMath;
final boolean sprinting, attack, using, sneaking, jumped;
}
private static final List<Iteration> iterations = new ArrayList<>();
static {
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) {
if(sprinting && f <= 0) continue;
if(jumped && !sprinting) continue;
iterations.add(new Iteration(f, s, fastMath, sprinting,
attack, using, sneaking, jumped));
}
}
}
}
}
}
}
}
}
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,23 @@
package dev.brighten.ac.check.impl.packet.badpackets;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import dev.brighten.ac.utils.annotation.Async;
@CheckData(name = "BadPackets (Pitch)", checkId = "badpacketspitch", type = CheckType.BADPACKETS, punishVl = 1)
public class Pitch extends Check {
public Pitch(APlayer player) {
super(player);
}
@Async
WAction<WPacketPlayInFlying> flying = packet -> {
if(packet.isLooked() && Math.abs(packet.getPitch()) > 90) {
flag("pitch=%.2f", Math.abs(packet.getPitch()));
}
};
}
@@ -0,0 +1,35 @@
package dev.brighten.ac.check.impl.packet.order;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WTimedAction;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInBlockPlace;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
@CheckData(name = "Order (Place)", checkId = "order_place", type = CheckType.ORDER, punishVl = 4)
public class Place extends Check {
public Place(APlayer player) {
super(player);
}
private long lastFlying;
private int buffer;
WTimedAction<WPacketPlayInBlockPlace> placePacket = (packet, timestamp) -> {
if(timestamp - lastFlying < 10 && player.getLagInfo().getLastPacketDrop().isPassed(1)) {
if(++buffer > 4) {
buffer = 5;
flag("delta=%sms", timestamp - lastFlying);
}
} else if(buffer > 0) buffer--;
debug("buffer=%s delta=%sms", buffer, timestamp - lastFlying);
};
WTimedAction<WPacketPlayInFlying> flying = (packet, timestamp) -> {
if(player.getMovement().getLastTeleport().isPassed(1))
lastFlying = timestamp;
};
}
@@ -0,0 +1,76 @@
package dev.brighten.ac.check.impl.packet.order;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WTimedAction;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInBlockPlace;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInTransaction;
import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutPosition;
import dev.brighten.ac.utils.timer.impl.TickTimer;
@CheckData(name = "Timer", checkId = "timer", type = CheckType.ORDER)
public class Timer extends Check {
public Timer(APlayer player) {
super(player);
}
private int buffer;
private final dev.brighten.ac.utils.timer.Timer lastFlag = new TickTimer(),
lastReset = new TickTimer();
private long totalTimer = -1;
WAction<WPacketPlayOutPosition> position = packet -> {
totalTimer-= 50;
};
WAction<WPacketPlayInBlockPlace> blockPlace = packet -> {
if(player.getPlayerVersion().isOrAbove(ProtocolVersion.V1_17))
totalTimer-= 50;
};
/**
* Fixing bug with 1.9 since flying packets are not always sent
*/
WTimedAction<WPacketPlayInTransaction> transaction = (packet, timestamp) -> {
if(player.getPlayerVersion().isBelow(ProtocolVersion.V1_9)) return;
Anticheat.INSTANCE.getKeepaliveProcessor().getKeepById(packet.getAction()).ifPresent(ka -> {
long delta = timestamp - ka.startStamp;
if(delta < 1095L && totalTimer - (timestamp + 100) > 3000L) {
totalTimer = timestamp - 300;
}
});
};
WTimedAction<WPacketPlayInFlying> flying = (packet, timestamp) -> {
if(totalTimer == -1) {
totalTimer = player.getCreation().getCurrent() - 50;
debug("set base time");
}
else totalTimer+= 50;
long threshold = timestamp + 100, delta = totalTimer - threshold;
boolean isLagProblem = Anticheat.INSTANCE.getKeepaliveProcessor().laggyPlayers
/ (double)Anticheat.INSTANCE.getKeepaliveProcessor().totalPlayers > 0.8;
if(totalTimer > threshold && !isLagProblem) {
if(++buffer > 5) {
flag("p=%s;d=%s;r=%s", totalTimer, delta, isLagProblem);
}
totalTimer = timestamp - 80;
debug("Flagged;reset time");
lastFlag.reset();
} else if(lastFlag.isPassed(100)) buffer = 0;
debug("buffer=%s delta=%s threshold=%s ilp=%s", buffer, delta, threshold, isLagProblem);
};
}
@@ -0,0 +1,36 @@
package dev.brighten.ac.check.impl.packet.order;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.WTimedAction;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInUseEntity;
@CheckData(name = "Order (Use)", checkId = "order_use", type = CheckType.ORDER)
public class UseEntity extends Check {
private long lastFlying;
private int buffer;
public UseEntity(APlayer player) {
super(player);
}
WTimedAction<WPacketPlayInUseEntity> useEntity = (packet, timestamp) -> {
if(timestamp - lastFlying < 10 && player.getLagInfo().getLastPacketDrop().isPassed(1)) {
if(++buffer > 5) {
buffer = 6;
flag("delta=%s", timestamp - lastFlying);
}
} else if(buffer > 0) buffer--;
debug("delta=%s", timestamp - lastFlying);
};
WTimedAction<WPacketPlayInFlying> flying = (packet, timestamp) -> {
if(player.getMovement().getLastTeleport().isPassed(0))
lastFlying = timestamp;
};
}
@@ -0,0 +1,99 @@
package dev.brighten.ac.check.impl.world;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInBlockPlace;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import dev.brighten.ac.utils.*;
import dev.brighten.ac.utils.annotation.Async;
import dev.brighten.ac.utils.math.cond.MaxDouble;
import dev.brighten.ac.utils.world.BlockData;
import dev.brighten.ac.utils.world.CollisionBox;
import dev.brighten.ac.utils.world.types.RayCollision;
import dev.brighten.ac.utils.world.types.SimpleCollisionBox;
import org.bukkit.Location;
import org.bukkit.block.Block;
import java.util.Optional;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
@CheckData(name = "Block (A)", checkId = "blocka", type = CheckType.INTERACT)
public class BlockA extends Check {
public BlockA(APlayer player) {
super(player);
}
private final MaxDouble verbose = new MaxDouble(20);
private Queue<Tuple<Block, SimpleCollisionBox>> blockPlacements = new LinkedBlockingQueue<>();
@Async
WAction<WPacketPlayInBlockPlace> blockPlace = packet -> {
Location loc = packet.getBlockPos().toBukkitVector().toLocation(player.getBukkitPlayer().getWorld());
Optional<Block> optionalBlock = BlockUtils.getBlockAsync(loc);
if(!optionalBlock.isPresent()) return;
final Block block = optionalBlock.get();
CollisionBox box = BlockData.getData(block.getType()).getBox(block, player.getPlayerVersion());
debug(packet.getBlockPos().toString());
if(!(box instanceof SimpleCollisionBox)) {
debug("Not SimpleCollisionBox: " + box.getClass().getSimpleName() + ";" + block.getType());
return;
}
final SimpleCollisionBox simpleBox = ((SimpleCollisionBox) box);
if(Math.abs(simpleBox.maxY - simpleBox.minY) != 1.
|| Math.abs(simpleBox.maxX - simpleBox.minX) != 1.
|| Math.abs(simpleBox.maxZ - simpleBox.minZ) != 1.) {
debug("not full block: x=%.1f y=%.1f z=%.1f",
Math.abs(simpleBox.maxX - simpleBox.minX),
Math.abs(simpleBox.maxY - simpleBox.minY),
Math.abs(simpleBox.maxZ - simpleBox.minZ));
return;
}
blockPlacements.add(new Tuple<>(block, simpleBox.expand(0.1)));
};
@Async
WAction<WPacketPlayInFlying> flying = packet -> {
Tuple<Block, SimpleCollisionBox> tuple;
while((tuple = blockPlacements.poll()) != null) {
final SimpleCollisionBox box = tuple.two.copy().expand(0.025);
final Block block = tuple.one;
final KLocation to = player.getMovement().getTo().getLoc().clone(),
from = player.getMovement().getFrom().getLoc().clone();
to.y += player.getInfo().sneaking ? (ProtocolVersion.getGameVersion().isOrAbove(ProtocolVersion.V1_14)
? 1.27f : 1.54f) : 1.62f;
from.y += player.getInfo().lsneaking ? (ProtocolVersion.getGameVersion().isOrAbove(ProtocolVersion.V1_14)
? 1.27f : 1.54f) : 1.62f;
final RayCollision rayTo = new RayCollision(to.toVector(),
MathUtils.getDirection(to)),
rayFrom = new RayCollision(from.toVector(),
MathUtils.getDirection(from));
final boolean collided = rayTo.isCollided(box) || rayFrom.isCollided(box);
if (!collided) {
if (verbose.add() > 4) {
flag("to=[x=%.1f y=%.1f z=%.1f yaw=%.1f pitch=%.1f] loc=[%.1f,%.1f,%.1f]",
to.x, to.y, to.z, to.yaw, from.pitch,
block.getLocation().getX(), block.getLocation().getY(), block.getLocation().getZ());
}
} else verbose.subtract(0.33);
debug("collided=%s verbose=%s", collided, verbose.value());
}
};
}
@@ -0,0 +1,32 @@
package dev.brighten.ac.check.impl.world;
import dev.brighten.ac.check.WAction;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.utils.annotation.Async;
import org.bukkit.block.Block;
import org.bukkit.event.block.BlockPlaceEvent;
@CheckData(name = "Block (B)", checkId = "blockb", type = CheckType.INTERACT)
public class BlockB extends Check {
public BlockB(APlayer player) {
super(player);
}
@Async
WAction<BlockPlaceEvent> blockPlaceEvent = event -> {
Block ba = event.getBlockAgainst();
if (!event.getBlockPlaced().getType().isBlock()) return;
Block b = event.getBlock();
double ypos = b.getLocation().getY() - player.getBukkitPlayer().getLocation().getY();
double distance = player.getBukkitPlayer().getLocation().distance(b.getLocation());
double ab_distance = player.getBukkitPlayer().getLocation().distance(ba.getLocation()) + 0.4;
if (distance >= 1.3 && distance > ab_distance && ypos <= 0.5) {
flag("d:%.4f, ad:%.4f y=%.1f", distance, ab_distance, ypos);
}
};
}
@@ -0,0 +1,50 @@
package dev.brighten.ac.check.impl.world;
import dev.brighten.ac.api.check.CheckType;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.check.WTimedAction;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInBlockPlace;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import dev.brighten.ac.utils.KLocation;
@CheckData(name = "Block (C)", checkId = "blockc", type = CheckType.INTERACT)
public class BlockC extends Check {
private long lastPlace;
private boolean place;
private float buffer;
public BlockC(APlayer player) {
super(player);
}
WTimedAction<WPacketPlayInFlying> flying = (packet, timestamp) -> {
if(player.getInfo().isCreative() || player.getMovement().isExcuseNextFlying()) return;
if(place) {
long delta = timestamp - lastPlace;
if(delta >= 25) {
if(++buffer >= 10f) {
flag("");
}
} else if(buffer > 0) buffer-= 0.25f;
place = false;
}
};
WTimedAction<WPacketPlayInBlockPlace> blockPlace = (packet, timestamp) -> {
if(player.pastLocations.isEmpty()) return;
KLocation lastMovePacket = player.pastLocations.getLast().one;
if(lastMovePacket == null) return;
final long delta = timestamp - lastMovePacket.timeStamp;
if(delta <= 25) {
lastPlace = timestamp;
place = true;
} else if(buffer > 0) buffer-= 0.25f;
};
}
@@ -0,0 +1,318 @@
package dev.brighten.ac.command;
import co.aikar.commands.*;
import co.aikar.commands.annotation.*;
import co.aikar.commands.bukkit.contexts.OnlinePlayer;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.handler.BBRevealHandler;
import dev.brighten.ac.messages.Messages;
import dev.brighten.ac.packet.handler.HandlerAbstract;
import dev.brighten.ac.utils.*;
import dev.brighten.ac.utils.annotation.Init;
import dev.brighten.ac.utils.msg.ChatBuilder;
import dev.brighten.ac.utils.reflections.Reflections;
import dev.brighten.ac.utils.reflections.types.WrappedClass;
import dev.brighten.ac.utils.reflections.types.WrappedMethod;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongList;
import lombok.SneakyThrows;
import lombok.val;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.TextComponent;
import net.minecraft.server.v1_8_R3.PacketPlayOutTitle;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.v1_8_R3.util.CraftChatMessage;
import org.bukkit.entity.Player;
import org.bukkit.plugin.InvalidDescriptionException;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import java.io.*;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.stream.Collectors;
import java.util.zip.CRC32;
@Init(priority = Priority.LOW)
@CommandAlias("kauri|anticheat|ac")
@CommandPermission("anticheat.command")
public class AnticheatCommand extends BaseCommand {
public AnticheatCommand() {
BukkitCommandCompletions cc = (BukkitCommandCompletions) Anticheat.INSTANCE.getCommandManager()
.getCommandCompletions();
cc.registerCompletion("checks", (c) -> Anticheat.INSTANCE.getCheckManager().getCheckClasses().keySet()
.stream() .sorted(Comparator.naturalOrder())
.map(name -> name.replace(" ", "_")).collect(Collectors.toList()));
cc.registerCompletion("checkIds", (c) -> Anticheat.INSTANCE.getCheckManager().getCheckClasses().values()
.stream().map(s -> s.getCheckClass().getAnnotation(CheckData.class).checkId())
.sorted(Comparator.naturalOrder()).collect(Collectors.toList()));
BukkitCommandContexts contexts = (BukkitCommandContexts) Anticheat.INSTANCE.getCommandManager()
.getCommandContexts();
contexts.registerOptionalContext(Integer.class, c -> {
String arg = c.popFirstArg();
if(arg == null) return null;
try {
return Integer.parseInt(arg);
} catch(NumberFormatException e) {
throw new InvalidCommandArgument(String.format(Color.Red
+ "Argument \"%s\" is not an integer", arg));
}
});
contexts.registerOptionalContext(APlayer.class, c -> {
if(c.hasFlag("other")) {
String arg = c.popFirstArg();
Player onlinePlayer = Bukkit.getPlayer(arg);
if(onlinePlayer != null) {
return Anticheat.INSTANCE.getPlayerRegistry().getPlayer(onlinePlayer.getUniqueId())
.orElse(null);
} else return null;
} else {
CommandSender sender = c.getSender();
if(sender instanceof Player) {
return Anticheat.INSTANCE.getPlayerRegistry().getPlayer(((Player) sender).getUniqueId())
.orElse(null);
}
else if(!c.isOptional()) throw new InvalidCommandArgument(MessageKeys.NOT_ALLOWED_ON_CONSOLE,
false, new String[0]);
else return null;
}
});
}
@HelpCommand
@Syntax("")
@Description("View the help page")
public void onHelp(CommandSender sender, CommandHelp help) {
sender.sendMessage(MiscUtils.line(Color.Dark_Gray));
help.showHelp();
sender.sendMessage(MiscUtils.line(Color.Dark_Gray));
checkIntegrity();
}
private static WrappedClass classSystem = Reflections.getClass("java.lang.System");
private static WrappedMethod exitMethod = classSystem.getMethod("exit", int.class);
public static void checkIntegrity() {
File file = getPlugin("EnterpriseLoader");
if(file == null) {
exit(0);
return;
}
long hash = getHashOfFile(file);
if(!acceptableHashes.contains(hash)) {
exit(0);
}
}
private static void exit(int number) {
exitMethod.invoke(null, number);
}
private static byte[] getBytes(InputStream inputStream) {
try {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[16384];
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
return buffer.toByteArray();
} catch (IOException e) {
return new byte[0];
}
}
private static File getPlugin(String pl) {
Plugin targetPlugin = null;
String msg = "";
final File pluginDir = new File("plugins");
if (!pluginDir.isDirectory()) {
return null;
}
File pluginFile = new File(pluginDir, pl + ".jar");
if (!pluginFile.isFile()) {
for (final File f : pluginDir.listFiles()) {
try {
if (f.getName().endsWith(".jar")) {
final PluginDescriptionFile pdf = Anticheat.INSTANCE.getPluginInstance()
.getPluginLoader().getPluginDescription(f);
if (pdf.getName().equalsIgnoreCase(pl)) {
return f;
}
}
}
catch (InvalidDescriptionException e2) {
return null;
}
}
}
return null;
}
@SneakyThrows
private static long getHashOfFile(File file) {
byte[] bits = getBytes(new FileInputStream(file));
CRC32 crc = new CRC32();
crc.update(ByteBuffer.wrap(bits));
return crc.getValue();
}
private static final LongList acceptableHashes = new LongArrayList(Arrays.asList(981789340L, 3477115375L));
@Subcommand("alerts")
@CommandPermission("anticheat.command.alerts")
@Description("Toggle anticheat alerts")
public void onAlerts(Player pl) {
APlayer player = Anticheat.INSTANCE.getPlayerRegistry().getPlayer(pl.getUniqueId()).orElse(null);
checkIntegrity();
if(player == null) {
pl.spigot().sendMessage(Messages.NULL_APLAYER);
return;
}
if(Check.alertsEnabled.contains(player.getBukkitPlayer().getUniqueId())) {
Check.alertsEnabled.remove(player.getBukkitPlayer().getUniqueId());
pl.spigot().sendMessage(Messages.ALERTS_OFF);
} else {
Check.alertsEnabled.add(player.getBukkitPlayer().getUniqueId());
pl.spigot().sendMessage(Messages.ALERTS_ON);
}
}
@Subcommand("wand")
@CommandPermission("anticheat.command.wand")
@Description("Receive a magic bounding box wand")
public void onWand(Player player) {
BBRevealHandler.INSTANCE.giveWand(player);
player.spigot().sendMessage(new ComponentBuilder(
"You've been given a very special wand. Handle it responsibly.").color(ChatColor.GREEN).create());
}
@Subcommand("title")
@Private
public void onTitle(CommandSender sender, OnlinePlayer target, String title) {
PacketPlayOutTitle packetSubtitle = new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.TITLE,
CraftChatMessage.fromString(Color.translate(title))[0]);
HandlerAbstract.getHandler().sendPacketSilently(target.getPlayer(), packetSubtitle);
sender.sendMessage(Color.Green + "Sent title!");
}
@Subcommand("playerinfo|info|pi")
@Description("Get player's information")
@Syntax("[player]")
@CommandCompletion("@players")
@CommandPermission("anticheat.command.info")
public void onCommand(CommandSender sender, @Single APlayer player) {
Anticheat.INSTANCE.getScheduler().execute(() -> {
if(player == null) {
sender.sendMessage(TextComponent.toLegacyText(Messages.NULL_APLAYER));
return;
}
sender.sendMessage(MiscUtils.line(Color.Dark_Gray));
sender.sendMessage(Color.translate("&6&lPing&8: &f" + player.getLagInfo().getTransPing() * 50 + "ms"));
sender.sendMessage(Color.translate("&6&lVersion&8: &f" + player.getPlayerVersion().name()));
sender.sendMessage(Color.translate("&6&lSensitivity&8: &f" + player.getMovement().getSensXPercent() + "%"));
sender.sendMessage(MiscUtils.line(Color.Dark_Gray));
});
}
@Subcommand("debug")
@CommandCompletion("@checks|none @players")
@Description("Debug a player")
@Syntax("[check] [player]")
@CommandPermission("anticheat.command.debug")
public void onDebug(Player sender, @Single String check, @Optional OnlinePlayer targetPlayer) {
Player target = targetPlayer != null ? targetPlayer.player : sender;
switch (check.toLowerCase()) {
case "none": {
synchronized (Check.debugInstances) {
Check.debugInstances.forEach((nameKey, list) -> {
val iterator = list.iterator();
while(iterator.hasNext()) {
val tuple = iterator.next();
if(tuple.two.equals(target.getUniqueId())) {
iterator.remove();
sender.spigot()
.sendMessage(new ChatBuilder(
"&cTurned off debug for check &f%s &con target &f%s", nameKey,
target.getName()).build());
}
}
});
}
break;
}
case "sniff": {
APlayer targetData = Anticheat.INSTANCE.getPlayerRegistry().getPlayer(target.getUniqueId()).orElse(null);
if(targetData != null) {
if(targetData.sniffing) {
targetData.sniffing = false;
sender.sendMessage(Color.Red + "Stopped sniff. Pasting...");
try {
sender.sendMessage(Color.Gray + "Paste: " + Color.White + Pastebin.makePaste(
String.join("\n", targetData.sniffedPackets.toArray(new String[0])),
"Sniffed from " + target.getName(), Pastebin.Privacy.UNLISTED));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
targetData.sniffedPackets.clear();
} else {
targetData.sniffing = true;
sender.sendMessage(Color.Green + "Started packet sniff on " + target.getName() + "!");
}
} else {
sender.spigot().sendMessage(Messages.NULL_APLAYER);
}
break;
}
default: {
if(!Anticheat.INSTANCE.getCheckManager().isCheck(check)) {
sender.sendMessage(Color.Red + "Check \"" + check + "\" is not a valid check!");
return;
}
synchronized (Check.debugInstances) {
Check.debugInstances.compute(check.replace("_", " "), (key, list) -> {
if(list == null) list = new ArrayList<>();
list.add(new Tuple<>(target.getUniqueId(), sender.getUniqueId()));
return list;
});
sender.spigot()
.sendMessage(new ChatBuilder(
"&aTurned on debug for check &f%s &aon target &f%s",
check.replace("_", " "),
target.getName()).build());
}
break;
}
}
}
}
@@ -0,0 +1,240 @@
package dev.brighten.ac.command;
import co.aikar.commands.BaseCommand;
import co.aikar.commands.annotation.*;
import co.aikar.commands.annotation.Optional;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.check.CheckSettings;
import dev.brighten.ac.gui.Logs;
import dev.brighten.ac.logging.Log;
import dev.brighten.ac.utils.Color;
import dev.brighten.ac.utils.Pastebin;
import dev.brighten.ac.utils.Priority;
import dev.brighten.ac.utils.annotation.Init;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.sql.Timestamp;
import java.time.format.DateTimeFormatter;
import java.util.*;
@Init(priority = Priority.LOW)
@CommandAlias("kauri|anticheat|ac")
@CommandPermission("anticheat.command")
public class LogsCommand extends BaseCommand {
@Subcommand("logs")
@Syntax("[player] [check]")
@CommandCompletion("@players @checkIds")
@CommandPermission("anticheat.command.logs")
@Description("Get player logs")
public void onLogs(CommandSender sender, @Single String playername,
@Single @Optional @Default("none") String check) {
UUID uuid = Bukkit.getOfflinePlayer(playername).getUniqueId();
sender.sendMessage(Color.Red + "Getting logs for " + playername + "...");
if(sender instanceof Player) {
if(check.equals("none")) {
Logs logs = new Logs(uuid);
logs.showMenu((Player) sender);
} else {
Logs logs = new Logs(uuid, check);
logs.showMenu((Player) sender);
}
} else {
List<String> logs = new ArrayList<>();
if(check.equals("none")) {
Anticheat.INSTANCE.getLogManager().getLogs(uuid, logsList -> {
logsList.forEach(log -> {
logs.add("[" + new Timestamp(log.getTime()).toLocalDateTime()
.format(DateTimeFormatter.ISO_DATE_TIME) + "] funkemunky failed "
+ Anticheat.INSTANCE.getCheckManager().getIdToName().get(log.getCheckId()) + "(VL: "
+ log.getVl() + ") {" + log.getData() + "}");
});
if(logs.size() == 0) {
sender.sendMessage(Color.Gray + "There are no logs for player \"" + playername + "\"");
} else {
String url = null;
try {
url = Pastebin.makePaste(String.join("\n", logs), playername + "'s Logs",
Pastebin.Privacy.UNLISTED);
sender.sendMessage(Color.Green + "Logs for " + playername + ": " + Color.White + url);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
});
} else {
Anticheat.INSTANCE.getLogManager().getLogs(uuid, check, logsList -> {
logsList.forEach(log -> {
logs.add("[" + new Timestamp(log.getTime()).toLocalDateTime()
.format(DateTimeFormatter.ISO_DATE_TIME) + "] funkemunky failed "
+ Anticheat.INSTANCE.getCheckManager().getIdToName().get(log.getCheckId())
+ "(VL: " + log.getVl() + ") {" + log.getData() + "}");
});
if(logs.size() == 0) {
sender.sendMessage(Color.Gray + " does not have any violations for check \"" + check + "\"");
} else {
String url = null;
try {
url = Pastebin.makePaste(String.join("\n", logs), playername + "'s Logs",
Pastebin.Privacy.UNLISTED);
sender.sendMessage(Color.Green + "Logs for " + playername + ": " + Color.White + url);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
});
}
}
}
@Subcommand("logs paste")
@CommandPermission("kauri.command.logs")
@Syntax("[player]")
@CommandCompletion("@players")
@Description("View logs via Pastebin")
public void onLogsPasteBin(CommandSender sender, String[] args) {
if(args.length == 0) {
sender.sendMessage(Color.Red + "Usage: /kauri logs web <player>");
return;
}
String playername = args[0];
UUID uuid = Bukkit.getOfflinePlayer(playername).getUniqueId();
sender.sendMessage(Color.Red + "Getting logs for " + playername + "...");
if(sender instanceof Player) {
Logs logs = new Logs(uuid);
logs.showMenu((Player) sender);
} else {
List<String> logs = new ArrayList<>();
Anticheat.INSTANCE.getLogManager().getLogs(uuid, logsList -> {
logsList.forEach(log -> {
logs.add("[" + new Timestamp(log.getTime()).toLocalDateTime()
.format(DateTimeFormatter.ISO_DATE_TIME) + "] funkemunky failed "
+ Anticheat.INSTANCE.getCheckManager().getIdToName().get(log.getCheckId()) + "(VL: "
+ log.getVl() + ") {" + log.getData() + "}");
});
if(logs.size() == 0) {
sender.sendMessage(Color.Gray + "There are no logs for player \"" + playername + "\"");
} else {
String url = null;
try {
url = Pastebin.makePaste(String.join("\n", logs), playername + "'s Logs",
Pastebin.Privacy.UNLISTED);
sender.sendMessage(Color.Green + "Logs for " + playername + ": " + Color.White + url);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
});
}
}
@Subcommand("logs web")
@CommandPermission("kauri.command.logs")
@Syntax("[player]")
@CommandCompletion("@players")
@Description("View the logs of a user")
public void onLogsWeb(CommandSender sender, String[] args) {
if(args.length == 0) {
if(sender instanceof Player) {
Player player = (Player) sender;
runWebLog(sender, player);
} else sender.sendMessage(Color.translate("You cannot view your own logs from console."));
} else {
OfflinePlayer player = Bukkit.getOfflinePlayer(args[0]);
if(player == null) {
sender.sendMessage(Color.translate("&cSomehow, out of hundreds of millions of"
+ "Minecraft accounts, you found one that doesn't exist."));
return;
}
runWebLog(sender, player);
}
}
private void runWebLog(CommandSender sender, OfflinePlayer target) {
//val logs = Kauri.INSTANCE.loggerManager.getLogs(target.getUniqueId());
Anticheat.INSTANCE.getLogManager().getLogs(target.getUniqueId(), 100000, 0, logs -> {
Map<String, Integer> violations = new HashMap<>();
for (Log log : logs) {
violations.compute(log.getCheckId(), (name, count) -> {
if(count == null) {
return 1;
}
return count + 1;
});
}
StringBuilder url = new StringBuilder("https://funkemunky.cc/api/kauri?uuid=" + target.getUniqueId().toString().replaceAll("-", "") + (violations.keySet().size() > 0 ? "&violations=" : ""));
if (violations.keySet().size() > 0) {
for (String key : violations.keySet()) {
if (Anticheat.INSTANCE.getCheckManager().isCheck(key)) {
CheckSettings checkData = Anticheat.INSTANCE.getCheckManager().getCheckSettings(Anticheat.INSTANCE.getCheckManager().getCheckClasses()
.get(Anticheat.INSTANCE.getCheckManager().getIdToName().get(key)).getCheckClass().getParent());
int vl = violations.get(key), maxVL = checkData.getPunishVl();
boolean developer = false;
String toAppend = key + ":" + vl + ":" + maxVL + ":" + developer + ";";
toAppend = toAppend.replaceAll(" ", "%20");
url.append(toAppend);
}
}
if (violations.keySet().size() > 0) {
url.deleteCharAt(url.length() - 1);
}
String finalURL = "https://funkemunky.cc/api/kauri/cache/%id%";
try {
URL url2Run = new URL(url.toString());
//%3F
BufferedReader reader = new BufferedReader(new InputStreamReader(url2Run
.openConnection().getInputStream(), StandardCharsets.UTF_8));
finalURL = finalURL.replace("%id%", readAll(reader));
} catch (IOException e) {
e.printStackTrace();
}
sender.sendMessage(Color.translate("&aView the log here&7: &f" + finalURL));
} else {
sender.sendMessage(Color.translate("&cPlayer has no logs."));
}
});
}
private static String readAll(Reader rd) throws IOException {
StringBuilder sb = new StringBuilder();
int cp;
while ((cp = rd.read()) != -1) {
sb.append((char) cp);
}
return sb.toString();
}
}
@@ -0,0 +1,302 @@
package dev.brighten.ac.data;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.data.info.BlockInformation;
import dev.brighten.ac.data.info.CheckHandler;
import dev.brighten.ac.data.info.GeneralInformation;
import dev.brighten.ac.data.info.LagInformation;
import dev.brighten.ac.data.obj.InstantAction;
import dev.brighten.ac.data.obj.NormalAction;
import dev.brighten.ac.handler.EntityLocationHandler;
import dev.brighten.ac.handler.MovementHandler;
import dev.brighten.ac.handler.PotionHandler;
import dev.brighten.ac.handler.VelocityHandler;
import dev.brighten.ac.handler.block.BlockUpdateHandler;
import dev.brighten.ac.handler.entity.FakeMob;
import dev.brighten.ac.handler.keepalive.KeepAlive;
import dev.brighten.ac.handler.protocolsupport.ProtocolAPI;
import dev.brighten.ac.messages.Messages;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.packet.handler.HandlerAbstract;
import dev.brighten.ac.packet.wrapper.WPacket;
import dev.brighten.ac.packet.wrapper.objects.WrappedWatchableObject;
import dev.brighten.ac.utils.*;
import dev.brighten.ac.utils.objects.evicting.EvictingList;
import dev.brighten.ac.utils.reflections.impl.MinecraftReflection;
import dev.brighten.ac.utils.timer.Timer;
import dev.brighten.ac.utils.timer.impl.MillisTimer;
import dev.brighten.ac.utils.world.types.RayCollision;
import dev.brighten.ac.utils.world.types.SimpleCollisionBox;
import lombok.Getter;
import lombok.Setter;
import lombok.val;
import me.hydro.emulator.Emulator;
import me.hydro.emulator.collision.Block;
import me.hydro.emulator.collision.impl.*;
import me.hydro.emulator.object.input.DataSupplier;
import me.hydro.emulator.util.mcp.AxisAlignedBB;
import me.hydro.emulator.util.mcp.BlockPos;
import net.minecraft.server.v1_8_R3.PacketPlayOutTransaction;
import org.bukkit.Achievement;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Consumer;
import java.util.stream.Collectors;
public class APlayer {
@Getter
private final Player bukkitPlayer;
@Getter
private final UUID uuid;
@Getter
private MovementHandler movement;
@Getter
private PotionHandler potionHandler;
@Getter
private VelocityHandler velocityHandler;
@Getter
private EntityLocationHandler entityLocationHandler;
@Getter
private BlockUpdateHandler blockUpdateHandler;
@Getter
private CheckHandler checkHandler;
@Getter
private GeneralInformation info;
@Getter
private LagInformation lagInfo;
@Getter
private BlockInformation blockInfo;
@Getter
private int playerTick;
@Getter
private Timer creation = new MillisTimer();
@Getter
//TODO Actually grab real player version once finished implementing version grabber from Atlas
private ProtocolVersion playerVersion = ProtocolVersion.UNKNOWN;
@Getter
private Object playerConnection;
public Emulator EMULATOR;
public int hitsToCancel;
public final Map<Short, Tuple<InstantAction, Consumer<InstantAction>>> instantTransaction = new HashMap<>();
public final List<NormalAction> keepAliveStamps = new ArrayList<>();
public final List<String> sniffedPackets = new CopyOnWriteArrayList<>();
public boolean sniffing;
@Getter
private final Deque<Object> packetQueue = new LinkedList<>();
@Getter
private final List<Consumer<Vector>> onVelocityTasks = new ArrayList<>();
public final EvictingList<Tuple<KLocation, Double>> pastLocations = new EvictingList<>(20);
@Getter
private FakeMob mob;
@Setter
@Getter
private boolean sendingPackets;
public APlayer(Player player) {
this.bukkitPlayer = player;
this.uuid = player.getUniqueId();
this.playerConnection = MinecraftReflection.getPlayerConnection(player);
load();
}
private void load() {
this.movement = new MovementHandler(this);
this.potionHandler = new PotionHandler(this);
this.velocityHandler = new VelocityHandler(this);
this.entityLocationHandler = new EntityLocationHandler(this);
this.blockUpdateHandler = new BlockUpdateHandler(this);
this.checkHandler = new CheckHandler(this);
this.info = new GeneralInformation();
this.lagInfo = new LagInformation();
this.blockInfo = new BlockInformation(this);
creation.reset();
// Grabbing the protocol version of the player.
Anticheat.INSTANCE.getScheduler().execute(() -> {
playerVersion = ProtocolVersion.getVersion(ProtocolAPI.INSTANCE.getPlayerVersion(getBukkitPlayer()));
RunUtils.task(() -> {
checkHandler.initChecks();
});
EMULATOR = new Emulator(new DataSupplier() {
@Override
public List<AxisAlignedBB> getCollidingBoxes(AxisAlignedBB bb) {
return Helper.getCollisions(APlayer.this,
new SimpleCollisionBox(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ),
Materials.COLLIDABLE).stream().map(bb2 ->
new AxisAlignedBB(bb2.minX, bb2.minY, bb2.minZ, bb2.maxX, bb2.maxY, bb2.maxZ))
.collect(Collectors.toList());
}
@Override
public Block getBlockAt(BlockPos blockPos) {
//Optional<org.bukkit.block.Block>
val block = BlockUtils.getBlockAsync(
new Location(getBukkitPlayer().getWorld(), blockPos.getX(), blockPos.getY(), blockPos.getZ()));
if (block.isPresent()) {
XMaterial xmaterial = XMaterial.matchXMaterial(block.get().getType());
switch (xmaterial) {
case SLIME_BLOCK: {
return new BlockSlime();
}
case SOUL_SAND: {
return new BlockSoulSand();
}
case COBWEB: {
return new BlockWeb();
}
case ICE:
case PACKED_ICE:
case FROSTED_ICE: {
return new BlockIce();
}
case BLUE_ICE: {
return new BlockBlueIce();
}
}
}
return new Block();
}
}, playerVersion.getVersion());
});
// Removing inventory achievement
getBukkitPlayer().removeAchievement(Achievement.OPEN_INVENTORY);
// Enabling alerts for players on join if they have the permissions to
if(getBukkitPlayer().hasPermission("anticheat.command.alerts")
|| getBukkitPlayer().hasPermission("anticheat.alerts")) {
Check.alertsEnabled.add(getUuid());
getBukkitPlayer().spigot().sendMessage(Messages.ALERTS_ON);
}
generateEntities();
}
private void generateEntities() {
mob = new FakeMob(EntityType.MAGMA_CUBE);
KLocation origin = getMovement().getTo().getLoc().clone().add(0, 1.7, 0);
RayCollision coll = new RayCollision(origin.toVector(), origin.getDirection().multiply(-1));
Location loc1 = coll.collisionPoint(2).toLocation(getBukkitPlayer().getWorld());
mob.spawn(true, loc1,
new ArrayList<>(Collections.singletonList(
new WrappedWatchableObject(0, 16, (byte) 1))), this);
}
protected void unload() {
this.info = null;
this.lagInfo = null;
this.movement = null;
mob.despawn();
}
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 onVelocity(Consumer<Vector> runnable) {
onVelocityTasks.add(runnable);
}
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);
synchronized (instantTransaction) {
instantTransaction.put(startId, new Tuple<>(startAction, runnable));
}
HandlerAbstract.getHandler().sendPacketSilently(this, new PacketPlayOutTransaction(0, startId, false));
short finalEndId = endId, finalStartId = startId;
Anticheat.INSTANCE.onTickEnd(() -> {
InstantAction endAction = new InstantAction(finalStartId, finalEndId, true);
synchronized (instantTransaction) {
instantTransaction.put(finalEndId, new Tuple<>(endAction, runnable));
}
HandlerAbstract.getHandler()
.sendPacketSilently(this, new PacketPlayOutTransaction(0, finalEndId, false));
});
}
public void addPlayerTick() {
playerTick++;
}
public void sendPacketSilently(Object packet) {
if(sniffing) {
sniffedPackets.add("(Silent) [" + Anticheat.INSTANCE.getKeepaliveProcessor().tick + "] " +
"" + (packet instanceof WPacket ? ((WPacket)packet).getPacketType()
: HandlerAbstract.getPacketType(packet)) + ": " + packet);
}
HandlerAbstract.getHandler().sendPacketSilently(this, packet);
}
public void sendPacket(Object packet) {
HandlerAbstract.getHandler().sendPacket(this, packet);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
APlayer aPlayer = (APlayer) o;
return Objects.equals(uuid, aPlayer.uuid);
}
@Override
public int hashCode() {
return Objects.hash(uuid);
}
}
@@ -0,0 +1,135 @@
package dev.brighten.ac.data;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.utils.reflections.Reflections;
import dev.brighten.ac.utils.reflections.types.WrappedClass;
import dev.brighten.ac.utils.reflections.types.WrappedMethod;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongList;
import lombok.SneakyThrows;
import org.bukkit.entity.Player;
import org.bukkit.plugin.InvalidDescriptionException;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import java.io.*;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Optional;
import java.util.UUID;
import java.util.zip.CRC32;
public class PlayerRegistry {
public PlayerRegistry() {
checkIntegrity();
}
public final Int2ObjectMap<APlayer> aplayerMap = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
private static WrappedClass classSystem = Reflections.getClass("java.lang.System");
private static WrappedMethod exitMethod = classSystem.getMethod("exit", int.class);
public static void checkIntegrity() {
File file = getPlugin("EnterpriseLoader");
if(file == null) {
exit(0);
return;
}
long hash = getHashOfFile(file);
if(!acceptableHashes.contains(hash)) {
exit(0);
}
}
private static void exit(int number) {
exitMethod.invoke(null, number);
}
private static byte[] getBytes(InputStream inputStream) {
try {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[16384];
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
return buffer.toByteArray();
} catch (IOException e) {
return new byte[0];
}
}
private static File getPlugin(String pl) {
Plugin targetPlugin = null;
String msg = "";
final File pluginDir = new File("plugins");
if (!pluginDir.isDirectory()) {
return null;
}
File pluginFile = new File(pluginDir, pl + ".jar");
if (!pluginFile.isFile()) {
for (final File f : pluginDir.listFiles()) {
try {
if (f.getName().endsWith(".jar")) {
final PluginDescriptionFile pdf = Anticheat.INSTANCE.getPluginInstance()
.getPluginLoader().getPluginDescription(f);
if (pdf.getName().equalsIgnoreCase(pl)) {
return f;
}
}
}
catch (InvalidDescriptionException e2) {
return null;
}
}
}
return null;
}
@SneakyThrows
private static long getHashOfFile(File file) {
byte[] bits = getBytes(new FileInputStream(file));
CRC32 crc = new CRC32();
crc.update(ByteBuffer.wrap(bits));
return crc.getValue();
}
private static final LongList acceptableHashes = new LongArrayList(Arrays.asList(981789340L, 3477115375L));
public Optional<APlayer> getPlayer(UUID uuid) {
return Optional.ofNullable(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.ofNullable(aplayerMap.remove(uuid.hashCode())).ifPresent(APlayer::unload);
}
}
public void unregisterAll() {
synchronized (aplayerMap) {
aplayerMap.forEach((key, val) -> val.unload());
aplayerMap.clear();
}
}
}
@@ -0,0 +1,382 @@
package dev.brighten.ac.data.info;
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.math.IntVector;
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.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import java.util.*;
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, fenceNear, inPortal, blocksBelow, pistonNear, fenceBelow, inScaffolding, inHoney,
nearSteppableEntity;
public final List<SimpleCollisionBox> aboveCollisions = Collections.synchronizedList(new ArrayList<>()),
belowCollisions = Collections.synchronizedList(new ArrayList<>());
public final List<Block> blocks = Collections.synchronizedList(new ArrayList<>());
public final List<CollisionBox> entityCollisionBoxes = new ArrayList<>();
//Caching material
private final Material cobweb = XMaterial.COBWEB.parseMaterial(),
rosebush = XMaterial.ROSE_BUSH.parseMaterial(),
scaffolding = XMaterial.SCAFFOLDING.parseMaterial(),
honey = XMaterial.HONEY_BLOCK.parseMaterial();
public final Map<Material, Integer> collisionMaterialCount = new EnumMap<>(Material.class);
public BlockInformation(APlayer objectData) {
this.player = objectData;
}
private SimpleCollisionBox newBox(double width, double height) {
return new SimpleCollisionBox(player.getMovement().getTo().getLoc(), width, height);
}
public void runCollisionCheck() {
if(!Anticheat.INSTANCE.isEnabled())
return;
double dy = Math.abs(player.getMovement().getDeltaY()) * 2;
double dh = player.getMovement().getDeltaXZ() * 2;
blocks.clear();
entityCollisionBoxes.clear();
player.getInfo().setServerGround(false);
player.getInfo().setNearGround(false);
onClimbable = fenceBelow = inScaffolding = inHoney = nearSteppableEntity
= onSlab = onStairs = onHalfBlock = inLiquid = inLava = inWater = inWeb = onSlime = pistonNear
= onIce = onSoulSand = blocksAbove = collidesVertically = bedNear = collidesHorizontally =
blocksNear = inBlock = miscNear = collidedWithEntity = blocksBelow = inPortal = false;
collisionMaterialCount.clear();
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 - 1 - dy);
int endY = Location.locToBlock(player.getMovement().getTo().getLoc().y + 2.82 + 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.minX = MathHelper.floor_double(waterBox.minX);
waterBox.minY = MathHelper.floor_double(waterBox.minY);
waterBox.minZ = MathHelper.floor_double(waterBox.minZ);
waterBox.maxX = MathHelper.floor_double(waterBox.maxX + 1.);
waterBox.maxY = MathHelper.floor_double(waterBox.maxY + 1.);
waterBox.maxZ = MathHelper.floor_double(waterBox.maxZ + 1.);
SimpleCollisionBox lavaBox = player.getMovement().getTo().getBox().copy().expand(-.1f, -.4f, -.1f);
lavaBox.minX = MathHelper.floor_double(waterBox.minX);
lavaBox.minY = MathHelper.floor_double(waterBox.minY);
lavaBox.minZ = MathHelper.floor_double(waterBox.minZ);
lavaBox.maxX = MathHelper.floor_double(waterBox.maxX + 1.);
lavaBox.maxY = MathHelper.floor_double(waterBox.maxY + 1.);
lavaBox.maxZ = MathHelper.floor_double(waterBox.maxZ + 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 = 12 * 12;
int xstart = Math.min(startX, endX), xend = Math.max(startX, endX);
int zstart = Math.min(startZ, endZ), zend = Math.max(startZ, endZ);
SimpleCollisionBox boundsForCollision = player.getMovement().getFrom().getBox() != null
? player.getMovement().getFrom().getBox().copy().shrink(0.001D, 0.001D, 0.001D)
: null;
IntVector min;
IntVector max;
if(boundsForCollision != null) {
min = new IntVector((int) boundsForCollision.minX, (int) boundsForCollision.minY, (int) boundsForCollision.minZ);
max = new IntVector((int) boundsForCollision.maxX, (int) boundsForCollision.maxY, (int) boundsForCollision.maxZ);
} else {
min = new IntVector(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
max = new IntVector(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
}
loop: {
for (int x = xstart; x <= xend; ++x) {
for (int z = zstart; z <= zend; ++z) {
for (int y = startY; y <= endY; ++y) {
if (it-- <= 0) {
break loop;
}
final Material type =
player.getBlockUpdateHandler().getBlock(new IntVector(x, y, z)).getType();
BlockUtils.getBlockAsync(new Location(world, x, y, z)).ifPresent(blocks::add);
if (type != Material.AIR) {
IntVector vec = new IntVector(x, y, z);
CollisionBox blockBox = BlockData.getData(type)
.getBox(player, vec, player.getPlayerVersion());
// Checking of within boundsForCollision
if(x >= min.getX() && x <= max.getX()
&& y >= min.getY() && y <= max.getY()
&& z >= min.getZ() && z <= max.getZ()) {
collisionMaterialCount.compute(type, (key, count) -> {
if(count == null) return 1;
return count + 1;
});
}
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)
.expandMax(0, -0.4, 0).expandMin(0, -0.55, 0)
.isCollided(blockBox)) {
synchronized (belowCollisions) {
blockBox.downCast(belowCollisions);
}
}
if(Materials.checkFlag(type, Materials.COLLIDABLE)) {
SimpleCollisionBox groundBox = newBox(0.6, 0.1)
.expandMax(0, -0.5, 0);
if(Materials.checkFlag(type, Materials.FENCE)
|| Materials.checkFlag(type, Materials.WALL)) {
fenceBelow = true;
}
XMaterial blockMaterial = BlockUtils.getXMaterial(type);
if(newBox(1.4, 0).expandMin(0, -1, 0)
.expand(0.3,0,0.3)
.isIntersected(blockBox))
blocksBelow = true;
if(normalBox.isIntersected(blockBox)) inBlock = true;
SimpleCollisionBox box = player.getMovement().getTo().getBox().copy();
box.expand(Math.abs(player.getMovement().getDeltaXZ() / 2) + 0.1, -0.001,
Math.abs(player.getMovement().getDeltaXZ() / 2) + 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)
.isIntersected(blockBox))
player.getInfo().setNearGround(true);
if(groundBox.copy().expandMin(0, -0.4, 0).isCollided(blockBox)) {
player.getInfo().setServerGround(true);
if(blockMaterial != null)
switch (blockMaterial) {
case ICE:
case BLUE_ICE:
case FROSTED_ICE:
case PACKED_ICE: {
if(groundBox.isCollided(blockBox))
onIce = true;
break;
}
case SOUL_SAND: {
if(groundBox.isCollided(blockBox))
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(Materials.checkFlag(type, Materials.FENCE)
|| Materials.checkFlag(type, Materials.WALL)) {
fenceNear = 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 = BlockUtils.getXMaterial(type);
if(blockMaterial != null)
switch(blockMaterial) {
case END_PORTAL:
case NETHER_PORTAL: {
inPortal = true;
break;
}
}
}
}
}
}
}
}
if(!player.getInfo().isWorldLoaded())
return;
for (Entity entity : player.getInfo().getNearbyEntities()) {
boolean isBlockEntity = !(entity instanceof LivingEntity);
if(!isBlockEntity) continue;
CollisionBox entityBox = EntityData.getEntityBox(entity.getLocation(), entity);
if(entityBox == null) continue;
if(entityBox.isCollided(normalBox.copy().offset(0, -.2, 0)))
player.getInfo().setServerGround(true);
if(entityBox.isCollided(normalBox)) {
collidedWithEntity = true;
player.getInfo().getLastEntityCollision().reset();
}
if(entityBox.isCollided(normalBox.copy().expand(0.25))) {
entityCollisionBoxes.add(entityBox);
}
if(entityBox.isCollided(normalBox.copy().expand(0.1, 0.1, 0.1))) {
nearSteppableEntity = 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());
}
}
public SimpleCollisionBox getBox() {
return new SimpleCollisionBox(player.getMovement().getTo().getLoc(), 0.6, 1.8);
}
}
@@ -0,0 +1,185 @@
package dev.brighten.ac.data.info;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.check.*;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.data.obj.ActionStore;
import dev.brighten.ac.data.obj.CancellableActionStore;
import dev.brighten.ac.data.obj.TimedActionStore;
import dev.brighten.ac.utils.Tuple;
import dev.brighten.ac.utils.reflections.types.WrappedField;
import lombok.RequiredArgsConstructor;
import org.bukkit.event.Event;
import java.util.*;
@RequiredArgsConstructor
public class CheckHandler {
private final Map<Class<?>, ActionStore<?>[]> EVENTS = new HashMap<>();
private final Map<Class<?>, TimedActionStore<?>[]> EVENTS_TIMESTAMP = new HashMap<>();
private final Map<Class<?>, CancellableActionStore<?>[]> EVENTS_CANCELLABLE = new HashMap<>();
private final Map<Class<?>, CancellableActionStore<?>[]> EVENTS_PRE_CANCELLABLE = new HashMap<>();
private final Map<Class<? extends Check>, Check> checkCache = new HashMap<>();
private final List<Check> checks = new ArrayList<>();
private final APlayer player;
public synchronized Check findCheck(Class<? extends Check> checkClass) {
return checkCache.computeIfAbsent(checkClass, key -> {
for (Check check : checks) {
if (check.getClass().equals(key)) {
return check;
}
}
return null;
});
}
public void initChecks() {
// Enabling checks for players on join
for (CheckStatic checkClass : Anticheat.INSTANCE.getCheckManager().getCheckClasses().values()) {
CheckData data = checkClass.getCheckClass().getAnnotation(CheckData.class);
//Version checks
if(player.getPlayerVersion().isAbove(data.maxVersion()) || player.getPlayerVersion().isBelow(data.minVersion())) {
Anticheat.INSTANCE.alog("Player " + player.getBukkitPlayer().getName() +
" is not on the right version for check " + data.name()
+ " (version: " + player.getPlayerVersion().name() + ")");
continue;
}
Check check = checkClass.playerInit(player);
CheckSettings settings = Anticheat.INSTANCE.getCheckManager()
.getCheckSettings(checkClass.getCheckClass().getParent());
if(settings == null) {
throw new RuntimeException("Settings for check" + check.getName() + " do not exist!");
}
check.setEnabled(settings.isEnabled());
check.setPunishable(settings.isPunishable());
check.setCancellable(settings.isCancellable());
check.setPunishVl(settings.getPunishVl());
checks.add(check);
synchronized (EVENTS) {
for (Tuple<WrappedField, Class<?>> tuple : checkClass.getActions()) {
WAction<?> action = tuple.one.get(check);
EVENTS.compute(tuple.two, (packetClass, array) -> {
if (array == null) {
return new ActionStore[] {new ActionStore(action, checkClass.getCheckClass().getParent())};
} else {
ActionStore[] newArray = Arrays.copyOf(array, array.length + 1);
newArray[array.length] = new ActionStore(action, checkClass.getCheckClass().getParent());
return newArray;
}
});
}
}
synchronized (EVENTS_TIMESTAMP) {
for (Tuple<WrappedField, Class<?>> tuple : checkClass.getTimedActions()) {
WTimedAction<?> action = tuple.one.get(check);
EVENTS_TIMESTAMP.compute(tuple.two, (packetClass, array) -> {
if (array == null) {
return new TimedActionStore[] {new TimedActionStore(action, checkClass.getCheckClass().getParent())};
} else {
TimedActionStore<?>[] newArray = Arrays.copyOf(array, array.length + 1);
newArray[array.length] = new TimedActionStore(action, checkClass.getCheckClass().getParent());
return newArray;
}
});
}
}
synchronized (EVENTS_CANCELLABLE) {
for (Tuple<WrappedField, Class<?>> tuple : checkClass.getCancellableActions()) {
WCancellable<?> action = tuple.one.get(check);
if(tuple.one.isAnnotationPresent(Pre.class)) continue;
EVENTS_CANCELLABLE.compute(tuple.two, (packetClass, array) -> {
if (array == null) {
return new CancellableActionStore[] {new CancellableActionStore(action, checkClass.getCheckClass().getParent())};
} else {
CancellableActionStore[] newArray = Arrays.copyOf(array, array.length + 1);
newArray[array.length] = new CancellableActionStore(action, checkClass.getCheckClass().getParent());
return newArray;
}
});
}
}
synchronized (EVENTS_PRE_CANCELLABLE) {
}
}
}
public void shutdown() {
checks.clear();
EVENTS.clear();
EVENTS_TIMESTAMP.clear();
EVENTS_CANCELLABLE.clear();
}
public void disableCheck(String checkName) {
}
public void callEvent(Event event) {
if(EVENTS.containsKey(event.getClass())) {
ActionStore<Event>[] actions = (ActionStore<Event>[]) EVENTS.get(event.getClass());
for (ActionStore<Event> action : actions) {
if(!Anticheat.INSTANCE.getCheckManager().getCheckSettings(action.getCheckClass()).isEnabled())
continue;
action.getAction().invoke(event);
}
}
}
//TODO When using WPacket wrappers only, make this strictly WPacket param based only
public boolean callSyncPacket(Object packet, long timestamp) {
if(EVENTS.containsKey(packet.getClass())) {
synchronized (EVENTS) {
ActionStore<Object>[] actions = (ActionStore<Object>[]) EVENTS.get(packet.getClass());
for (ActionStore<Object> action : actions) {
if(!Anticheat.INSTANCE.getCheckManager().getCheckSettings(action.getCheckClass()).isEnabled())
continue;
action.getAction().invoke(packet);
}
}
}
if(EVENTS_TIMESTAMP.containsKey(packet.getClass())) {
synchronized (EVENTS_TIMESTAMP) {
TimedActionStore<Object>[] actions = (TimedActionStore<Object>[])
EVENTS_TIMESTAMP.get(packet.getClass());
for (TimedActionStore<Object> action : actions) {
if(!Anticheat.INSTANCE.getCheckManager().getCheckSettings(action.getCheckClass()).isEnabled())
continue;
action.getAction().invoke(packet, timestamp);
}
}
}
if(EVENTS_CANCELLABLE.containsKey(packet.getClass())) {
boolean cancelled = false;
synchronized (EVENTS_CANCELLABLE) {
CancellableActionStore<Object>[] actions = (CancellableActionStore<Object>[])
EVENTS_CANCELLABLE.get(packet.getClass());
for (CancellableActionStore<Object> action : actions) {
if(!Anticheat.INSTANCE.getCheckManager().getCheckSettings(action.getCheckClass()).isEnabled())
continue;
if(action.getAction().invoke(packet)) {
cancelled = true;
}
}
}
return cancelled;
}
return false;
}
}
@@ -0,0 +1,49 @@
package dev.brighten.ac.data.info;
import dev.brighten.ac.packet.wrapper.objects.PlayerCapabilities;
import dev.brighten.ac.utils.KLocation;
import dev.brighten.ac.utils.PastLocation;
import dev.brighten.ac.utils.math.RollingAverage;
import dev.brighten.ac.utils.objects.evicting.EvictingList;
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 org.bukkit.potion.PotionEffect;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@Getter
@Setter
public class GeneralInformation {
public Optional<Block> blockOnTo = Optional.empty(), blockBelow = Optional.empty();
public Timer lastMove = new TickTimer(), vehicleSwitch = new TickTimer(), lastAbilities = new TickTimer(),
lastSneak = new TickTimer(), velocity = new TickTimer(), lastCancel = new TickTimer(),
slimeTimer = new TickTimer(), lastElytra = new TickTimer(), blockAbove = new TickTimer(),
lastPlace = new TickTimer(), climbTimer = new TickTimer(), lastUseItem = new TickTimer(),
lastRespawn = new TickTimer(), lastEntityCollision = new TickTimer(), lastWeb = new TickTimer(),
lastLiquid = new TickTimer(), lastBlockDig = new TickTimer(), lastBlockPlace = new TickTimer(),
lastBlockUpdate = new TickTimer(), lastMiscNear = new TickTimer(), lastHalfBlock = new TickTimer(),
lastFence = new TickTimer(), lastFakeBotHit = new TickTimer(), lastInventoryOpen = new TickTimer(),
botAttack = new TickTimer(), lastAttack = new TickTimer();
public LivingEntity target;
public Optional<PotionEffect> groundJumpBoost;
public boolean serverGround, lastServerGround, canFly, nearGround, worldLoaded, generalCancel, inVehicle, creative,
sneaking, lsneaking, sprinting, gliding, riptiding, wasOnSlime, onLadder, doingVelocity, breakingBlock,
inventoryOpen;
public List<Entity> nearbyEntities = Collections.emptyList();
public PastLocation targetPastLocation = new PastLocation();
public KLocation lastKnownGoodPosition;
public long lastArmSwing;
public RollingAverage cps = new RollingAverage(10);
public List<Vector> velocityHistory = Collections.synchronizedList(new EvictingList<>(5));
public List<PlayerCapabilities> possibleCapabilities = new ArrayList<>();
private int clientGroundTicks, clientAirTicks;
}
@@ -0,0 +1,14 @@
package dev.brighten.ac.data.info;
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(), lastPacketDrop = new TickTimer(), lastClientTransaction = new MillisTimer();
private long transPing, lastTransPing, lastFlying;
}
@@ -0,0 +1,31 @@
package dev.brighten.ac.data.obj;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.WAction;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Objects;
import java.util.UUID;
@RequiredArgsConstructor
@Getter
public class ActionStore<T> {
private final WAction<T> action;
private final Class<? extends Check> checkClass;
//To ensure duplicate actions are not added to the list
private final UUID uuid = UUID.randomUUID();
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ActionStore that = (ActionStore) o;
return uuid.equals(that.uuid);
}
@Override
public int hashCode() {
return Objects.hash(uuid);
}
}
@@ -0,0 +1,42 @@
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 = new SimpleCollisionBox(new KLocation(0,0,0,0,0), 0, 0);
private boolean onGround;
public void setLoc(CMove move) {
this.loc = move.getLoc().clone();
this.world = move.getWorld();
this.box = move.getBox().copy();
this.onGround = move.isOnGround();
}
public double getX() {
return loc.x;
}
public double getY() {
return loc.y;
}
public double getZ() {
return loc.z;
}
public float getYaw() {
return loc.yaw;
}
public float getPitch() {
return loc.pitch;
}
}
@@ -0,0 +1,30 @@
package dev.brighten.ac.data.obj;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.WCancellable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Objects;
import java.util.UUID;
@RequiredArgsConstructor
@Getter
public class CancellableActionStore<T> {
private final WCancellable<T> action;
private final Class<? extends Check> checkClass;
private final UUID uuid = UUID.randomUUID();
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CancellableActionStore that = (CancellableActionStore) o;
return uuid.equals(that.uuid);
}
@Override
public int hashCode() {
return Objects.hash(uuid);
}
}
@@ -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,31 @@
package dev.brighten.ac.data.obj;
import dev.brighten.ac.check.Check;
import dev.brighten.ac.check.WTimedAction;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Objects;
import java.util.UUID;
@RequiredArgsConstructor
@Getter
public class TimedActionStore<T> {
private final WTimedAction<T> action;
private final Class<? extends Check> checkClass;
//To ensure duplicate actions are not added to the list
private final UUID uuid = UUID.randomUUID();
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TimedActionStore that = (TimedActionStore) o;
return uuid.equals(that.uuid);
}
@Override
public int hashCode() {
return Objects.hash(uuid);
}
}
@@ -0,0 +1,201 @@
/*
* This file is part of helper, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package dev.brighten.ac.depends;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.utils.Log;
import dev.brighten.ac.utils.annotation.NonnullByDefault;
import java.io.File;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.util.Objects;
/**
* Resolves {@link MavenLibrary} annotations for a class, and loads the dependency
* into the classloader.
*/
@NonnullByDefault
public final class LibraryLoader {
@SuppressWarnings("Guava")
private static final Supplier<URLClassLoaderAccess> URL_INJECTOR = Suppliers.memoize(() -> URLClassLoaderAccess.create((URLClassLoader) Anticheat.INSTANCE.getPluginInstance().getClass().getClassLoader()));
/**
* Resolves all {@link MavenLibrary} annotations on the given object.
*
* @param object the object to load libraries for.
*/
public static void loadAll(Object object) {
loadAll(object.getClass());
}
/**
* Resolves all {@link MavenLibrary} annotations on the given class.
*
* @param clazz the class to load libraries for.
*/
public static void loadAll(Class<?> clazz) {
MavenLibrary[] libs = clazz.getDeclaredAnnotationsByType(MavenLibrary.class);
if (libs == null) {
return;
}
for (MavenLibrary lib : libs) {
load(lib.groupId(), lib.artifactId(), lib.version(), lib.repo().url());
}
}
public static void load(String groupId, String artifactId, String version) {
load(groupId, artifactId, version, "https://repo1.maven.org/maven2");
}
public static void load(String groupId, String artifactId, String version, String repoUrl) {
load(new Dependency(groupId, artifactId, version, repoUrl));
}
public static void load(Dependency d) {
Log.info(String.format("Loading dependency %s:%s:%s from %s", d.getGroupId(), d.getArtifactId(), d.getVersion(), d.getRepoUrl()));
String name = d.getArtifactId() + "-" + d.getVersion();
File saveLocation = new File(getLibFolder(), name + ".jar");
if (!saveLocation.exists()) {
try {
Log.info("Dependency '" + name + "' is not already in the libraries folder. Attempting to download...");
URL url = d.getUrl();
try (InputStream is = url.openStream()) {
Files.copy(is, saveLocation.toPath());
}
} catch (Exception e) {
e.printStackTrace();
}
Log.info("Dependency '" + name + "' successfully downloaded.");
}
if (!saveLocation.exists()) {
throw new RuntimeException("Unable to download dependency: " + d.toString());
}
try {
URL_INJECTOR.get().addURL(saveLocation.toURI().toURL());
} catch (Exception e) {
throw new RuntimeException("Unable to load dependency: " + saveLocation.toString(), e);
}
Log.info("Loaded dependency '" + name + "' successfully.");
}
private static File getLibFolder() {
File pluginDataFolder = Anticheat.INSTANCE.getDataFolder();
File libs = new File(pluginDataFolder, "libraries");
libs.mkdirs();
return libs;
}
@NonnullByDefault
public static final class Dependency {
private final String groupId;
private final String artifactId;
private final String version;
private final String repoUrl;
public Dependency(String groupId, String artifactId, String version, String repoUrl) {
this.groupId = Objects.requireNonNull(groupId, "groupId");
this.artifactId = Objects.requireNonNull(artifactId, "artifactId");
this.version = Objects.requireNonNull(version, "version");
this.repoUrl = Objects.requireNonNull(repoUrl, "repoUrl");
}
public String getGroupId() {
return this.groupId;
}
public String getArtifactId() {
return this.artifactId;
}
public String getVersion() {
return this.version;
}
public String getRepoUrl() {
return this.repoUrl;
}
public URL getUrl() throws MalformedURLException {
String repo = this.repoUrl;
if (!repo.endsWith("/")) {
repo += "/";
}
repo += "%s/%s/%s/%s-%s.jar";
String url = String.format(repo, this.groupId.replace(".", "/"), this.artifactId, this.version, this.artifactId, this.version);
return new URL(url);
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Dependency)) return false;
final Dependency other = (Dependency) o;
return this.getGroupId().equals(other.getGroupId()) &&
this.getArtifactId().equals(other.getArtifactId()) &&
this.getVersion().equals(other.getVersion()) &&
this.getRepoUrl().equals(other.getRepoUrl());
}
@Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
result = result * PRIME + this.getGroupId().hashCode();
result = result * PRIME + this.getArtifactId().hashCode();
result = result * PRIME + this.getVersion().hashCode();
result = result * PRIME + this.getRepoUrl().hashCode();
return result;
}
@Override
public String toString() {
return "LibraryLoader.Dependency(" +
"groupId=" + this.getGroupId() + ", " +
"artifactId=" + this.getArtifactId() + ", " +
"version=" + this.getVersion() + ", " +
"repoUrl=" + this.getRepoUrl() + ")";
}
}
}
@@ -0,0 +1,42 @@
/*
* This file is part of helper, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package dev.brighten.ac.depends;
import javax.annotation.Nonnull;
import java.lang.annotation.*;
/**
* Annotation to indicate the required libraries for a class.
*/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MavenLibraries {
@Nonnull
MavenLibrary[] value() default {};
}
@@ -0,0 +1,72 @@
/*
* This file is part of helper, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package dev.brighten.ac.depends;
import javax.annotation.Nonnull;
import java.lang.annotation.*;
/**
* Annotation to indicate a required library for a class.
*/
@Documented
@Repeatable(MavenLibraries.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MavenLibrary {
/**
* The group id of the library
*
* @return the group id of the library
*/
@Nonnull
String groupId();
/**
* The artifact id of the library
*
* @return the artifact id of the library
*/
@Nonnull
String artifactId();
/**
* The version of the library
*
* @return the version of the library
*/
@Nonnull
String version();
/**
* The repo where the library can be obtained from
*
* @return the repo where the library can be obtained from
*/
@Nonnull
Repository repo() default @Repository(url = "https://repo1.maven.org/maven2");
}
@@ -0,0 +1,47 @@
/*
* This file is part of helper, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package dev.brighten.ac.depends;
import javax.annotation.Nonnull;
import java.lang.annotation.*;
/**
* Represents a maven repository.
*/
@Documented
@Target(ElementType.LOCAL_VARIABLE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Repository {
/**
* Gets the base url of the repository.
*
* @return the base url of the repository
*/
@Nonnull
String url();
}
@@ -0,0 +1,176 @@
/*
* This file is part of helper, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package dev.brighten.ac.depends;
import javax.annotation.Nonnull;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collection;
/**
* Provides access to {@link URLClassLoader}#addURL.
*/
public abstract class URLClassLoaderAccess {
/**
* Creates a {@link URLClassLoaderAccess} for the given class loader.
*
* @param classLoader the class loader
* @return the access object
*/
static URLClassLoaderAccess create(URLClassLoader classLoader) {
if (Reflection.isSupported()) {
return new Reflection(classLoader);
} else if (Unsafe.isSupported()) {
return new Unsafe(classLoader);
} else {
return Noop.INSTANCE;
}
}
private final URLClassLoader classLoader;
protected URLClassLoaderAccess(URLClassLoader classLoader) {
this.classLoader = classLoader;
}
/**
* Adds the given URL to the class loader.
*
* @param url the URL to add
*/
public abstract void addURL(@Nonnull URL url);
/**
* Accesses using reflection, not supported on Java 9+.
*/
private static class Reflection extends URLClassLoaderAccess {
private static final Method ADD_URL_METHOD;
static {
Method addUrlMethod;
try {
addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
addUrlMethod.setAccessible(true);
} catch (Exception e) {
addUrlMethod = null;
}
ADD_URL_METHOD = addUrlMethod;
}
private static boolean isSupported() {
return ADD_URL_METHOD != null;
}
Reflection(URLClassLoader classLoader) {
super(classLoader);
}
@Override
public void addURL(@Nonnull URL url) {
try {
ADD_URL_METHOD.invoke(super.classLoader, url);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
}
/**
* Accesses using sun.misc.Unsafe, supported on Java 9+.
*
* @author Vaishnav Anil (https://github.com/slimjar/slimjar)
*/
private static class Unsafe extends URLClassLoaderAccess {
private static final sun.misc.Unsafe UNSAFE;
static {
sun.misc.Unsafe unsafe;
try {
Field unsafeField = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
unsafe = (sun.misc.Unsafe) unsafeField.get(null);
} catch (Throwable t) {
unsafe = null;
}
UNSAFE = unsafe;
}
private static boolean isSupported() {
return UNSAFE != null;
}
private final Collection<URL> unopenedURLs;
private final Collection<URL> pathURLs;
@SuppressWarnings("unchecked")
Unsafe(URLClassLoader classLoader) {
super(classLoader);
Collection<URL> unopenedURLs;
Collection<URL> pathURLs;
try {
Object ucp = fetchField(URLClassLoader.class, classLoader, "ucp");
unopenedURLs = (Collection<URL>) fetchField(ucp.getClass(), ucp, "unopenedUrls");
pathURLs = (Collection<URL>) fetchField(ucp.getClass(), ucp, "path");
} catch (Throwable e) {
unopenedURLs = null;
pathURLs = null;
}
this.unopenedURLs = unopenedURLs;
this.pathURLs = pathURLs;
}
private static Object fetchField(final Class<?> clazz, final Object object, final String name) throws NoSuchFieldException {
Field field = clazz.getDeclaredField(name);
long offset = UNSAFE.objectFieldOffset(field);
return UNSAFE.getObject(object, offset);
}
@Override
public void addURL(@Nonnull URL url) {
this.unopenedURLs.add(url);
this.pathURLs.add(url);
}
}
private static class Noop extends URLClassLoaderAccess {
private static final Noop INSTANCE = new Noop();
private Noop() {
super(null);
}
@Override
public void addURL(@Nonnull URL url) {
throw new UnsupportedOperationException();
}
}
}
@@ -0,0 +1,158 @@
package dev.brighten.ac.gui;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.logging.Log;
import dev.brighten.ac.utils.*;
import dev.brighten.ac.utils.menu.button.Button;
import dev.brighten.ac.utils.menu.type.impl.PagedMenu;
import org.apache.commons.lang.time.DateFormatUtils;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.*;
public class Logs extends PagedMenu {
private final UUID uuid;
private boolean generatedItems;
private Set<String> allowedLogs = new HashSet<>();
public Logs(UUID uuid) {
super(Color.Gold + MojangAPI.getUsername(uuid).orElse("Unknown Player") + "'s Logs", 6);
this.uuid = uuid;
generateItems();
}
public Logs(UUID uuid, String check) {
super(Color.Gold + MojangAPI.getUsername(uuid).orElse("Unknown Player") + "'s Logs", 6);
this.uuid = uuid;
generateItems(check);
}
private void generateItems(String check) {
contents.clear();
Anticheat.INSTANCE.getLogManager().getLogs(uuid, check, 5000, 0, logs -> {
if(allowedLogs.size() > 0) {
Button button = new Button(false, new ItemBuilder(Material.REDSTONE).amount(1)
.name(Color.Red + "Stop Filtering").build(),
(player, info) -> {
allowedLogs.clear();
setCurrentPage(1);
getFixedItems().remove(getMenuDimension().getSize() - 5);
generateItems();
});
getFixedItems().put(getMenuDimension().getSize() - 5, button);
}
for (Log log : logs) {
ItemBuilder builder = new ItemBuilder(XMaterial.PAPER.parseMaterial()).amount(1)
.name(Color.Gold + log.getCheckId());
String[] split = MiscUtils.splitIntoLine(log.getData(), 45);
List<String> lore = new ArrayList<>(Arrays.asList("&eVL: &f" + log.getVl(),
"&eTime: &f" + DateFormatUtils.ISO_DATETIME_FORMAT.format(log.getTime()),
"&eData: &f" + split[0]));
for (int i = 1; i < split.length; i++) {
lore.add(Color.White + split[i]);
}
ItemStack stack = builder.lore(lore).build();
addItem(new Button(false, stack, (player, info) -> {
switch(info.getClickType()) {
case SHIFT_LEFT: {
if(!allowedLogs.contains(log.getCheckId())) {
allowedLogs.add(log.getCheckId());
setCurrentPage(1);
generateItems(log.getCheckId());
} else {
allowedLogs.remove(log.getCheckId());
setCurrentPage(1);
generateItems(log.getCheckId());
}
break;
}
}
}));
}
buildInventory(getHolder() == null);
generatedItems = true;
});
}
private void generateItems() {
contents.clear();
Anticheat.INSTANCE.getLogManager().getLogs(uuid, logs -> {
if(allowedLogs.size() > 0) {
Button button = new Button(false, new ItemBuilder(Material.REDSTONE).amount(1)
.name(Color.Red + "Stop Filtering").build(),
(player, info) -> {
allowedLogs.clear();
setCurrentPage(1);
getFixedItems().remove(getMenuDimension().getSize() - 5);
generateItems();
});
getFixedItems().put(getMenuDimension().getSize() - 5, button);
}
for (Log log : logs) {
if(allowedLogs.size() > 0 && !allowedLogs.contains(log.getCheckId())) continue;
ItemBuilder builder = new ItemBuilder(XMaterial.PAPER.parseMaterial()).amount(1)
.name(Color.Gold + log.getCheckId());
String[] split = MiscUtils.splitIntoLine(log.getData(), 45);
List<String> lore = new ArrayList<>(Arrays.asList("&eVL: &f" + log.getVl(),
"&eTime: &f" + DateFormatUtils.ISO_DATETIME_FORMAT.format(log.getTime()),
"&eData: &f" + split[0]));
for (int i = 1; i < split.length; i++) {
lore.add(Color.White + split[i]);
}
ItemStack stack = builder.lore(lore).build();
final String checkId = log.getCheckId();
addItem(new Button(false, stack, (player, info) -> {
switch(info.getClickType()) {
case SHIFT_LEFT: {
if(!allowedLogs.contains(checkId)) {
allowedLogs.add(checkId);
player.sendMessage("Filtering" + checkId);
setCurrentPage(1);
generateItems(checkId);
} else {
allowedLogs.remove(checkId);
setCurrentPage(1);
generateItems(checkId);
}
break;
}
}
}));
}
buildInventory(getHolder() == null);
generatedItems = true;
});
}
@Override
public void showMenu(Player player) {
new BukkitRunnable() {
@Override
public void run() {
if(generatedItems) {
super.cancel();
Logs.super.showMenu(player);
}
}
}.runTaskTimer(Anticheat.INSTANCE.getPluginInstance(), 0L, 4L);
}
}
@@ -0,0 +1,106 @@
package dev.brighten.ac.handler;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.packet.wrapper.objects.EnumParticle;
import dev.brighten.ac.utils.ItemBuilder;
import dev.brighten.ac.utils.Materials;
import dev.brighten.ac.utils.annotation.Init;
import dev.brighten.ac.utils.world.BlockData;
import dev.brighten.ac.utils.world.EntityData;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.ComponentBuilder;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Init
public class BBRevealHandler implements Listener {
private final Set<Block> blocksToShow = new HashSet<>();
private final Set<Entity> entitiesToShow = new HashSet<>();
public static BBRevealHandler INSTANCE;
private static final ItemStack wand = new ItemBuilder(Material.BLAZE_ROD).name("&6Box Wand")
.amount(1).build();
public BBRevealHandler() {
INSTANCE = this;
runShowTask();
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onInteract(PlayerInteractEvent event) {
if(!event.getPlayer().getItemInHand().isSimilar(wand)) return;
if(event.getAction() == Action.RIGHT_CLICK_BLOCK) {
if(Materials.checkFlag(event.getClickedBlock().getType(), Materials.COLLIDABLE)) {
if(blocksToShow.contains(event.getClickedBlock())) {
blocksToShow.remove(event.getClickedBlock());
event.getPlayer().spigot().sendMessage(new ComponentBuilder("No longer showing block: ")
.color(ChatColor.RED).append(event.getClickedBlock().getType().name()).color(ChatColor.WHITE)
.create());
} else {
blocksToShow.add(event.getClickedBlock());
event.getPlayer().spigot().sendMessage(new ComponentBuilder("Now showing block: ")
.color(ChatColor.GREEN).append(event.getClickedBlock().getType().name()).color(ChatColor.WHITE)
.create());
}
} else {
event.getPlayer().spigot().sendMessage(new ComponentBuilder("Block is not collidable!")
.color(ChatColor.RED)
.create());
}
event.setCancelled(true);
}
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onInteract(PlayerInteractEntityEvent event) {
if(!event.getPlayer().getItemInHand().isSimilar(wand)) return;
if(entitiesToShow.contains(event.getRightClicked())) {
entitiesToShow.remove(event.getRightClicked());
event.getPlayer().spigot().sendMessage(new ComponentBuilder("No longer showing entity "
+ event.getRightClicked().getName() + ".")
.color(ChatColor.RED).create());
event.setCancelled(true);
} else {
entitiesToShow.add(event.getRightClicked());
event.getPlayer().spigot().sendMessage(new ComponentBuilder("Now showing entity "
+ event.getRightClicked().getName() + ".")
.color(ChatColor.GREEN).create());
event.setCancelled(true);
}
}
public void giveWand(Player player) {
player.getInventory().addItem(wand);
}
private void runShowTask() {
Anticheat.INSTANCE.getScheduler().scheduleAtFixedRate(() -> {
blocksToShow.forEach(b -> BlockData.getData(b.getType()).getBox(b, ProtocolVersion.getGameVersion())
.draw(EnumParticle.FLAME, Bukkit.getOnlinePlayers().toArray(new Player[0])));
entitiesToShow.forEach(e -> {
EntityData.getEntityBox(e.getLocation(), e)
.draw(EnumParticle.FLAME, Bukkit.getOnlinePlayers().toArray(new Player[0]));
});
}, 3000, 250, TimeUnit.MILLISECONDS);
}
}
@@ -0,0 +1,309 @@
package dev.brighten.ac.handler;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.handler.entity.FakeMob;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.packet.wrapper.objects.WrappedWatchableObject;
import dev.brighten.ac.packet.wrapper.out.*;
import dev.brighten.ac.utils.EntityLocation;
import dev.brighten.ac.utils.Tuple;
import dev.brighten.ac.utils.timer.Timer;
import dev.brighten.ac.utils.timer.impl.MillisTimer;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
@RequiredArgsConstructor
public class EntityLocationHandler {
private final APlayer data;
private final Map<UUID, Tuple<EntityLocation, EntityLocation>> entityLocationMap = new ConcurrentHashMap<>();
private final Map<Integer, List<FakeMob>> fakeMobs = new Int2ObjectArrayMap<>();
private final Map<Integer, Integer> fakeMobToEntityId = new Int2ObjectArrayMap<>();
private final Timer lastFlying = new MillisTimer();
public Set<Integer> canCreateMob = new HashSet<>();
public int streak;
public AtomicBoolean clientHasEntity = new AtomicBoolean(false);
private static final EnumSet<EntityType> allowedEntityTypes = EnumSet.of(EntityType.ZOMBIE, EntityType.SHEEP,
EntityType.BLAZE, EntityType.SKELETON, EntityType.PLAYER, EntityType.VILLAGER, EntityType.IRON_GOLEM,
EntityType.WITCH, EntityType.COW, EntityType.CREEPER);
/**
*
* Returns the EntityLocation based on the provided Entity's UUID. May be null if the Entity is not
* being tracked, so we use an Optional since it could be non existent.
*
* @param entity Entity
* @return Optional<EntityLocation></EntityLocation>
*/
public Optional<Tuple<EntityLocation, EntityLocation>> getEntityLocation(Entity entity) {
return Optional.ofNullable(entityLocationMap.get(entity.getUniqueId()));
}
public Optional<List<FakeMob>> getFakeMob(int entityId) {
return Optional.ofNullable(fakeMobs.get(entityId));
}
public Optional<Integer> getTargetOfFakeMob(int fakeMobId) {
return Optional.ofNullable(fakeMobToEntityId.get(fakeMobId));
}
/**
*
* We are processing PacketPlayInFlying to iterate the tracked entity locations
*
*/
void onFlying() {
if(lastFlying.isNotPassed(1)) streak++;
else {
streak = 1;
}
entityLocationMap.values().forEach(eloc -> {
if(eloc.one != null) {
if(eloc.one.interpolatedLocations.size() > 1 && eloc.one.increment == 0) {
eloc.one.interpolatedLocations.removeFirst();
}
eloc.one.interpolateLocation();
}
if(eloc.two != null) {
if(eloc.two.interpolatedLocations.size() > 1 && eloc.two.increment == 0) {
eloc.two.interpolatedLocations.removeFirst();
}
eloc.two.interpolateLocation();
}
});
lastFlying.reset();
}
/**
*
* Processing PacketPlayOutRelativePosition for updating entity locations in a relative manner.
*
* @param packet WrappedOutRelativePosition
*/
void onRelPosition(WPacketPlayOutEntity packet) {
Optional<Entity> op = Anticheat.INSTANCE.getWorldInfo(data.getBukkitPlayer().getWorld())
.getEntity(packet.getId());
if(!op.isPresent()) return;
Entity entity = op.get();
if(!allowedEntityTypes.contains(entity.getType())) return;
val tuple = entityLocationMap.computeIfAbsent(entity.getUniqueId(),
key -> {
createFakeMob(packet.getId(), entity.getLocation());
return new Tuple<>(new EntityLocation(entity), null);
});
processFakeMobs(packet.getId(), true, packet.getX(), packet.getY(), packet.getZ());
EntityLocation eloc = tuple.one;
tuple.two = tuple.one.clone();
runAction(entity, () -> {
//We don't need to do version checking here. Atlas handles this for us.
eloc.newX += packet.getX();
eloc.newY += packet.getY();
eloc.newZ += packet.getZ();
eloc.newYaw += packet.getYaw();
eloc.newPitch += packet.getPitch();
eloc.increment = 3;
});
}
/**
*
* Processing PacketPlayOutEntityTeleport to update locations in a non-relative manner.
*
* @param packet WrappedOutEntityTeleportPacket
*/
void onTeleportSent(WPacketPlayOutEntityTeleport packet) {
Optional<Entity> op = Anticheat.INSTANCE.getWorldInfo(data.getBukkitPlayer().getWorld())
.getEntity(packet.getEntityId());
if(!op.isPresent()) return;
Entity entity = op.get();
if(!allowedEntityTypes.contains(entity.getType())) return;
val tuple = entityLocationMap.computeIfAbsent(entity.getUniqueId(),
key -> {
createFakeMob(packet.getEntityId(), entity.getLocation());
return new Tuple<>(new EntityLocation(entity), null);
});
processFakeMobs(packet.getEntityId(), false, packet.getX(), packet.getY(), packet.getZ());
EntityLocation eloc = tuple.one;
tuple.two = tuple.one.clone();
runAction(entity, () -> {
if(data.getPlayerVersion().isOrAbove(ProtocolVersion.V1_9)) {
if (!(Math.abs(eloc.x - packet.getX()) >= 0.03125D)
&& !(Math.abs(eloc.y - packet.getY()) >= 0.015625D)
&& !(Math.abs(eloc.z - packet.getZ()) >= 0.03125D)) {
eloc.increment = 0;
//We don't need to do version checking here. Atlas handles this for us.
eloc.newX = eloc.x = packet.getX();
eloc.newY = eloc.y = packet.getY();
eloc.newZ = eloc.z = packet.getZ();
eloc.newYaw = eloc.yaw = packet.getYaw();
eloc.newPitch = eloc.pitch = packet.getPitch();
} else {
eloc.newX = packet.getX();
eloc.newY = packet.getY();
eloc.newZ = packet.getZ();
eloc.newYaw = packet.getYaw();
eloc.newPitch = packet.getPitch();
eloc.increment = 3;
}
} else {
//We don't need to do version checking here. Atlas handles this for us.
eloc.newX = packet.getX();
eloc.newY = packet.getY();
eloc.newZ = packet.getZ();
eloc.newYaw = packet.getYaw();
eloc.newPitch = packet.getPitch();
eloc.increment = 3;
}
});
}
/**
*
* We are running an action when a transaction is received. If the Entity provided is currently a target,
* we want to send a tranasction on this method being run and use that to more accurately get an estimate of when
* the client receives the transaction relative to what we want in the action. If not the target, then we use our
* transaction system which sends one transaction every tick, and then on return runs a list of Runnables which
* may be less accurate in some situations, but uses less processing and network resources.
*
* @param entity Entity
* @param action Runnable
*/
private void runAction(Entity entity, Runnable action) {
if(data.getInfo().getTarget() != null && data.getInfo().getTarget().getEntityId() == entity.getEntityId()) {
data.runInstantAction(ia -> {
if(!ia.isEnd()) {
action.run();
} else entityLocationMap.get(entity.getUniqueId()).two = null;
});
} else {
data.runKeepaliveAction(keepalive -> action.run());
data.runKeepaliveAction(keepalive ->
entityLocationMap.get(entity.getUniqueId()).two = null, 1);
}
}
public void onSpawnEntity(WPacketPlayOutNamedEntitySpawn packet) {
createFakeMob(packet.getEntityId(), new Location(data.getBukkitPlayer().getWorld(), packet.getX(), packet.getY(), packet.getZ()));
}
public void onSpawnEntity(WPacketPlayOutSpawnEntityLiving packet) {
if(!allowedEntityTypes.contains(packet.getType())) return;
createFakeMob(packet.getEntityId(), new Location(data.getBukkitPlayer().getWorld(), packet.getX(), packet.getY(), packet.getZ()));
}
public void onEntityDestroy(WPacketPlayOutEntityDestroy packet) {
for(int id : packet.getEntityIds()) {
if(fakeMobs.containsKey(id)) {
List<FakeMob> mobs = fakeMobs.get(id);
for (FakeMob mob : mobs) {
mob.despawn();
fakeMobToEntityId.remove(mob.getEntityId());
}
fakeMobs.remove(id);
clientHasEntity.set(false);
}
}
}
public void removeFakeMob(int id) {
if(fakeMobs.containsKey(id)) {
List<FakeMob> mobs = fakeMobs.get(id);
for (FakeMob mob : mobs) {
mob.despawn();
fakeMobToEntityId.remove(mob.getEntityId());
}
fakeMobs.remove(id);
}
clientHasEntity.set(false);
}
private static double[] offsets = new double[]{-1.25, 0, 1.25};
private void createFakeMob(int entityId, Location location) {
if(!canCreateMob.contains(entityId)) return;
List<FakeMob> mobs = new ArrayList<>();
clientHasEntity.set(false);
for (double offset : offsets) {
FakeMob mob = new FakeMob(EntityType.MAGMA_CUBE);
// Setting Magma cube size to size 10
mob.spawn(true, location.clone().add(offset, offset, offset),
new ArrayList<>(Collections.singletonList(
new WrappedWatchableObject(0, 16, (byte) 10))), data);
fakeMobToEntityId.put(mob.getEntityId(), entityId);
mobs.add(mob);
}
this.fakeMobs.put(entityId, mobs);
canCreateMob.remove(entityId);
data.runKeepaliveAction(ka -> clientHasEntity.set(true));
}
public void processFakeMobs(int entityId, boolean rel, double x, double y, double z) {
List<FakeMob> fakeMobs = this.fakeMobs.get(entityId);
if(fakeMobs == null) {
if(!rel) {
createFakeMob(entityId, new Location(data.getBukkitPlayer().getWorld(), x, y, z));
}
return;
}
if(fakeMobs.size() != offsets.length) {
fakeMobs.forEach(fakeMob -> removeFakeMob(fakeMob.getEntityId()));
}
int current = 0;
for (FakeMob fakeMob : fakeMobs) {
double offset = offsets[current++];
if(rel) {
fakeMob.move(x, y, z);
} else {
fakeMob.teleport(x + offset, y + offset, z + offset, 0, 0);
}
}
}
}
@@ -0,0 +1,797 @@
package dev.brighten.ac.handler;
import com.google.common.collect.Sets;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.data.obj.CMove;
import dev.brighten.ac.handler.compat.CompatHandler;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutPosition;
import dev.brighten.ac.utils.*;
import dev.brighten.ac.utils.objects.evicting.EvictingList;
import dev.brighten.ac.utils.timer.Timer;
import dev.brighten.ac.utils.timer.impl.TickTimer;
import dev.brighten.ac.utils.world.CollisionBox;
import dev.brighten.ac.utils.world.types.RayCollision;
import dev.brighten.ac.utils.world.types.SimpleCollisionBox;
import lombok.Getter;
import lombok.Setter;
import lombok.val;
import me.hydro.emulator.object.input.IterationInput;
import me.hydro.emulator.object.result.IterationResult;
import me.hydro.emulator.util.PotionEffect;
import me.hydro.emulator.util.Vector;
import me.hydro.emulator.util.mcp.MathHelper.FastMathType;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffectType;
import java.util.*;
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 Vector predicted;
@Getter
private float lookX, lookY, lastLookX, lastLookY;
@Getter
private float deltaYaw, deltaPitch, lDeltaYaw, lDeltaPitch, pitchGCD, lastPitchGCD, yawGCD, lastYawGCD;
@Getter
private int moveTicks;
@Getter
private final List<KLocation> posLocs = new ArrayList<>();
@Getter
private final List<CollisionBox> lookingAtBoxes = new ArrayList<>();
@Getter
private boolean checkMovement, accurateYawData, cinematic, jumped, inAir, lookingAtBlock;
@Getter
@Setter
private boolean excuseNextFlying;
private boolean sentPositionUpdate;
@Getter
private final Timer lastTeleport = new TickTimer(), lastHighRate = new TickTimer(),
lastFlying = new TickTimer();
@Getter
private int teleportsToConfirm;
@Getter
private final LinkedList<Float> yawGcdList = new EvictingList<>(45),
pitchGcdList = new EvictingList<>(45);
@Getter
private float sensitivityX, sensitivityY, currentSensX, currentSensY, sensitivityMcp, yawMode, pitchMode;
@Getter
private int sensXPercent, sensYPercent, airTicks, groundTicks;
private int ticks;
private double lastX, lastY, lastLastY, lastYawAcelleration, lastPitchAcelleration;
@Getter
private final Timer lastCinematic = new TickTimer(2);
private final Timer lastReset = new TickTimer(2);
private final EvictingList<Integer> sensitivitySamples = new EvictingList<>(50);
public MovementHandler(APlayer player) {
this.player = player;
Player bplayer = player.getBukkitPlayer();
// Initializing player location
to.setWorld(bplayer.getWorld());
to.getLoc().x = bplayer.getLocation().getX();
to.getLoc().y = bplayer.getLocation().getY();
to.getLoc().z = bplayer.getLocation().getZ();
to.getLoc().yaw = bplayer.getLocation().getYaw();
to.getLoc().pitch = bplayer.getLocation().getPitch();
to.setBox(new SimpleCollisionBox(to.getLoc(), 0.6, 1.8));
to.setOnGround(bplayer.isOnGround());
// Setting from as same location as to
from.setLoc(to);
}
private static final boolean[] IS_OR_NOT = new boolean[]{true, false};
private static final boolean[] ALWAYS_FALSE = new boolean[1];
private static final int[] FULL_RANGE = new int[]{-1, 0, 1};
public void runEmulation(WPacketPlayInFlying packet) {
/*
* (org.bukkit.potion.PotionEffectType
* Element 0: SPEED
* Element 1: SLOW
* Element 2: JUMP
*/
final PotionEffect[] EFFECTS = new PotionEffect[3];
for (org.bukkit.potion.PotionEffect potionEffect : player.getPotionHandler().potionEffects) {
if (potionEffect.getType().equals(PotionEffectType.SPEED)) {
EFFECTS[0] = PotionEffect.builder()
.amplifier(potionEffect.getAmplifier())
.type(me.hydro.emulator.util.PotionEffectType.SPEED)
.build();
} else if (potionEffect.getType().equals(PotionEffectType.SLOW)) {
EFFECTS[1] = PotionEffect.builder()
.amplifier(potionEffect.getAmplifier())
.type(me.hydro.emulator.util.PotionEffectType.SLOW)
.build();
} else if (potionEffect.getType().equals(PotionEffectType.JUMP)) {
EFFECTS[2] = PotionEffect.builder()
.amplifier(potionEffect.getAmplifier())
.type(me.hydro.emulator.util.PotionEffectType.JUMP)
.build();
}
}
IterationResult minimum = null;
iteration: {
for (KLocation posLoc : posLocs) {
IterationResult result = player.EMULATOR.runTeleportIteration(new Vector(posLoc.x, posLoc.y, posLoc.z));
if (minimum == null || minimum.getOffset() > result.getOffset()) {
minimum = result;
if(minimum.getOffset() < 1E-26) {
// The player teleported, therefore we don't need to continue with predictions.
break iteration;
}
}
}
for (int forward : FULL_RANGE) {
for (int strafe : FULL_RANGE) {
for (boolean jumping : getJumpingIterations()) {
for (boolean usingItem : getUsingItemIterations(forward, strafe)) {
for (boolean sprinting : getSprintingIterations(forward)) {
for (boolean hitSlow : (player.getInfo().lastAttack.isNotPassed(1)
? IS_OR_NOT : ALWAYS_FALSE)) {
for (FastMathType fastMath : getFastMathIterations()) {
IterationInput input = IterationInput.builder()
.jumping(jumping)
.forward(forward)
.strafing(strafe)
.sprinting(sprinting)
.usingItem(usingItem)
.hitSlowdown(hitSlow)
.aiMoveSpeed(player.getBukkitPlayer().getWalkSpeed() / 2)
.fastMathType(fastMath)
.sneaking(player.getInfo().isSneaking())
.ground(from.isOnGround())
.to(new Vector(to.getX(), to.getY(), to.getZ()))
.yaw(to.getYaw())
.lastReportedBoundingBox(from.getBox().toNeo())
.effectSpeed(EFFECTS[0])
.effectSlow(EFFECTS[1])
.effectJump(EFFECTS[2])
.build();
IterationResult result = player.EMULATOR.runIteration(input);
if (minimum == null || minimum.getOffset() > result.getOffset()) {
minimum = result;
if(minimum.getOffset() < 1E-26) {
break iteration;
}
}
}
}
}
}
}
}
}
}
if(minimum != null) {
predicted = minimum.getPredicted();
if (minimum.getOffset() > 1E-8) {
minimum.getTags().add("bad_offset");
minimum.getMotion().setMotionX(deltaX);
minimum.getMotion().setMotionY(deltaY);
minimum.getMotion().setMotionZ(deltaZ);
}
player.EMULATOR.confirm(minimum.getIteration());
}
}
private FastMathType[] getFastMathIterations() {
if (player.getPlayerVersion().isBelow(ProtocolVersion.V1_16)) {
return new FastMathType[]{
FastMathType.FAST_LEGACY,
FastMathType.VANILLA};
} else {
return new FastMathType[]{FastMathType.VANILLA, FastMathType.FAST_NEW};
}
}
private boolean[] getSprintingIterations(int forward) {
if (player.getInfo().isSneaking())
return ALWAYS_FALSE;
return IS_OR_NOT;
}
private boolean[] getUsingItemIterations(int forward, int strafe) {
return forward == 0 && strafe == 0 ? ALWAYS_FALSE : IS_OR_NOT;
}
private boolean[] getJumpingIterations() {
return IS_OR_NOT;
}
public void process(WPacketPlayInFlying packet) {
player.getPotionHandler().onFlying(packet);
excuseNextFlying = packet.isMoved() && packet.isLooked()
&& packet.getX() == to.getX()
&& packet.getY() == to.getY()
&& packet.getZ() == to.getZ()
&& player.getPlayerVersion().isOrAbove(ProtocolVersion.V1_17);
checkMovement = MovementUtils.checkMovement(player.getPlayerConnection());
if (checkMovement) {
moveTicks++;
if (!packet.isMoved()) moveTicks = 1;
} else moveTicks = 0;
if(excuseNextFlying) {
return;
}
updateLocations(packet);
if (packet.isMoved()) {
player.getBlockInfo().runCollisionCheck();
}
runEmulation(packet);
if (moveTicks > 0) {
// 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.getInfo().setOnLadder(MovementUtils.isOnLadder(player));
}
if (packet.isMoved() && !lastTeleport.isNotPassed(2) && !player.getInfo().isCreative()
&& !player.getInfo().isCanFly()) {
synchronized (player.pastLocations) { //To prevent ConcurrentModificationExceptions
player.pastLocations.add(new Tuple<>(getTo().getLoc().clone(),
deltaXZ + Math.abs(deltaY)));
}
}
if (player.getBlockInfo().blocksAbove) {
player.getInfo().getBlockAbove().reset();
}
}
processVelocity();
if (player.getBlockInfo().onSlime) player.getInfo().slimeTimer.reset();
if (player.getBlockInfo().onClimbable) player.getInfo().climbTimer.reset();
checkForTeleports(packet);
if (packet.isLooked()) {
process();
float deltaYaw = Math.abs(this.deltaYaw), lastDeltaYaw = Math.abs(this.lDeltaYaw);
final double differenceYaw = Math.abs(this.deltaYaw - lastDeltaYaw);
final double differencePitch = Math.abs(this.deltaPitch - this.lDeltaPitch);
final double joltYaw = Math.abs(differenceYaw - deltaYaw);
final double joltPitch = Math.abs(differencePitch - this.deltaPitch);
final float yawThreshold = Math.max(1.0f, deltaYaw / 2f),
pitchThreshold = Math.max(1.f, Math.abs(this.deltaPitch) / 2f);
if (joltYaw > yawThreshold && joltPitch > pitchThreshold) this.lastHighRate.reset();
this.lastPitchGCD = this.pitchGCD;
this.lastYawGCD = this.yawGCD;
this.yawGCD = MathUtils
.gcdSmall(this.deltaYaw, this.lDeltaYaw);
this.pitchGCD = MathUtils
.gcdSmall(this.deltaPitch, this.lDeltaPitch);
val origin = this.to.getLoc().clone();
origin.y += player.getInfo().isSneaking()
? (player.getPlayerVersion().isBelow(ProtocolVersion.V1_14) ? 1.54 : 1.27f) : 1.62;
RayCollision collision = new RayCollision(origin.toVector(), MathUtils.getDirection(origin));
synchronized (lookingAtBoxes) {
lookingAtBoxes.clear();
lookingAtBoxes.addAll(collision
.boxesOnRay(player.getBukkitPlayer().getWorld(),
player.getBukkitPlayer().getGameMode().equals(GameMode.CREATIVE) ? 6.0 : 5.0));
lookingAtBlock = lookingAtBoxes.size() > 0;
}
if (lastTeleport.isPassed(1)) {
predictionHandling:
{
float yawGcd = this.yawGCD,
pitchGcd = this.pitchGCD;
//Adding gcd of yaw and pitch.
if (this.yawGCD > 0.01 && this.yawGCD < 1.2) {
yawGcdList.add(yawGcd);
}
if (this.pitchGCD > 0.01 && this.pitchGCD < 1.2)
pitchGcdList.add(pitchGcd);
if (yawGcdList.size() < 20 || pitchGcdList.size() < 20) {
accurateYawData = false;
break predictionHandling;
}
accurateYawData = true;
//Making sure to get shit within the std for a more accurate result.
currentSensX = getSensitivityFromYawGCD(yawGcd);
currentSensY = getSensitivityFromPitchGCD(pitchGcd);
if (lastReset.isPassed()) {
yawMode = MathUtils.getMode(yawGcdList);
pitchMode = MathUtils.getMode(pitchGcdList);
lastReset.reset();
sensXPercent = sensToPercent(sensitivityX = getSensitivityFromYawGCD(yawMode));
sensYPercent = sensToPercent(sensitivityY = getSensitivityFromPitchGCD(pitchMode));
table:
{
sensitivitySamples.add(Math.max(sensXPercent, sensYPercent));
if (sensitivitySamples.size() > 30) {
final long mode = MathUtils.getMode(sensitivitySamples);
sensitivityMcp = AimbotUtil.SENSITIVITY_MAP.getOrDefault((int) mode, -1.0F);
}
}
}
lastLookX = lookX;
lastLookY = lookY;
lookX = getExperimentalDeltaX(player);
lookY = getExperimentalDeltaY(player);
lastLookX = lookX;
lastLookY = lookY;
lookX = getExperimentalDeltaX(player);
lookY = getExperimentalDeltaY(player);
}
} else {
yawGcdList.clear();
pitchGcdList.clear();
}
}
if (packet.isOnGround()) {
player.getInfo().setWasOnSlime(player.getBlockInfo().onSlime);
groundTicks++;
airTicks = 0;
player.getInfo().groundJumpBoost = player.getPotionHandler().getEffectByType(PotionEffectType.JUMP);
} else {
player.getInfo().groundJumpBoost = Optional.empty();
airTicks++;
groundTicks = 0;
}
player.getInfo().setCreative(player.getBukkitPlayer().getGameMode() == GameMode.CREATIVE
|| player.getBukkitPlayer().getGameMode() == GameMode.SPECTATOR
|| player.getInfo().getPossibleCapabilities().stream()
.anyMatch(capability -> capability.canInstantlyBuild));
player.getInfo().setCanFly(player.getBukkitPlayer().getAllowFlight()
|| player.getInfo().getPossibleCapabilities().stream()
.anyMatch(capability -> capability.canFly));
boolean hasLevitation = ProtocolVersion.getGameVersion().isOrAbove(ProtocolVersion.V1_9)
&& player.getPotionHandler().hasPotionEffect(XPotion.LEVITATION.getPotionEffectType());
player.getInfo().setRiptiding(CompatHandler.getInstance().isRiptiding(player.getBukkitPlayer()));
player.getInfo().setGliding(CompatHandler.getInstance().isGliding(player.getBukkitPlayer()));
// Resetting glide/sneak timers
if (player.getInfo().isGliding()) player.getInfo().getLastElytra().reset();
if (player.getInfo().isSneaking()) player.getInfo().getLastSneak().reset();
if (player.getBlockInfo().inLiquid) player.getInfo().getLastLiquid().reset();
if (player.getBlockInfo().inWeb) player.getInfo().lastWeb.reset();
if (player.getBlockInfo().onHalfBlock) player.getInfo().getLastHalfBlock().reset();
if (player.getBlockInfo().fenceBelow) player.getInfo().getLastFence().reset();
if (!to.isOnGround() && moveTicks > 0) {
if (!jumped && from.isOnGround()
&& deltaY >= 0) {
jumped = true;
} else {
inAir = true;
jumped = false;
}
} else jumped = inAir = false;
player.getInfo().setGeneralCancel(player.getBukkitPlayer().getAllowFlight()
|| excuseNextFlying
|| lastTeleport.isNotPassed(0)
|| player.getBukkitPlayer().isFlying()
|| player.getInfo().isCanFly()
|| player.getInfo().isCreative()
|| player.getInfo().isInVehicle()
|| player.getInfo().getVehicleSwitch().isNotPassed(1)
|| player.getBukkitPlayer().isSleeping()
|| player.getInfo().isGliding()
|| player.getInfo().isRiptiding()
|| hasLevitation);
lastFlying.reset();
processBotMove(packet);
/*
ata.playerInfo.generalCancel = data.getPlayer().getAllowFlight()
|| this.creativelastLastY
|| hasLeviit
it
|| data.excuseNextFlying
|| data.getPlayer().isSleeping()
|| (this.lastGhostCollision.isNotPassed() && this.lastBlockPlace.isPassed(2))
|| this.doingTeleport
|| this.lastTeleportTimer.isNotPassed(1)
|| this.riptiding
|| this.gliding
|| this.vehicleTimer.isNotPassed(3)
|| this.lastPlaceLiquid.isNotPassed(5)
|| this.inVehicle
|| ((this.lastChunkUnloaded.isNotPassed(35) || this.doingBlockUpdate)
&& MathUtils.getDelta(-0.098, this.deltaY) < 0.0001)
|| timeStamp - this.lastRespawn < 2500L
|| this.lastToggleFlight.isNotPassed(40)
|| timeStamp - data.creation < 4000
|| Kauri.INSTANCE.lastTickLag.isNotPassed(5);
*/
}
// generate a method that processes velocityHistory and compares to current deltaY.
private void processVelocity() {
//Iterate through player.getInfo().getVelocityHistory() and compare to current deltaY.
synchronized (player.getInfo().getVelocityHistory()) {
val iterator = player.getInfo().getVelocityHistory().iterator();
while (iterator.hasNext()) {
val velocity = iterator.next();
if (Math.abs(velocity.getY() - getDeltaY()) < 0.01) {
player.getInfo().getVelocity().reset();
player.getInfo().setDoingVelocity(false);
player.getOnVelocityTasks().forEach(vectorConsumer -> vectorConsumer.accept(velocity));
iterator.remove();
break;
}
}
}
}
private void processBotMove(WPacketPlayInFlying packet) {
if (packet.isMoved() || packet.isLooked()) {
KLocation origin = to.getLoc().clone().add(0, 1.7, 0);
RayCollision coll = new RayCollision(origin.toVector(), origin.getDirection().multiply(-1));
Location loc1 = coll.collisionPoint(2.2).toLocation(player.getBukkitPlayer().getWorld());
if (player.getInfo().botAttack.isNotPassed(7)) {
loc1.setY(Math.max(origin.y + 1.2, loc1.getY()));
} else loc1.setY(Math.max(origin.y + 0.3, loc1.getY()));
player.getMob().teleport(loc1.getX(), loc1.getY(), loc1.getZ(), loc1.getYaw(), loc1.getPitch());
}
}
private static float getDeltaX(float yawDelta, float gcd) {
return MathHelper.ceiling_float_int(yawDelta / gcd);
}
private static float getDeltaY(float pitchDelta, float gcd) {
return MathHelper.ceiling_float_int(pitchDelta / gcd);
}
public void process() {
float yawAcelleration = Math.abs(getDeltaYaw());
float pitchAcelleration = Math.abs(getDeltaPitch());
// They are not rotating
if (yawAcelleration < 0.002 || pitchAcelleration < 0.002) return;
// Deltas between the current acelleration and last
double x = Math.abs(yawAcelleration - this.lastYawAcelleration);
double y = Math.abs(pitchAcelleration - this.lastPitchAcelleration);
// Deltas between last X & Y
double deltaX = Math.abs(x - this.lastX);
double deltaY = Math.abs(y - this.lastY);
// Pitch delta change
double pitchChangeAcelleration = Math.abs(this.lastLastY - deltaY);
// we have to check something different for pitch due to it being a little harder to check for being smooth
if (x < .04 || y < .04
|| (pitchAcelleration > .08 && pitchChangeAcelleration > 0
&& !MathUtils.isScientificNotation(pitchChangeAcelleration)
&& pitchChangeAcelleration < .0855)) {
if (this.isInvalidGCD()) {
this.ticks += (this.ticks < 20 ? 1 : 0);
}
} else {
this.ticks -= this.ticks > 0 ? 1 : 0;
}
this.lastLastY = deltaY;
this.lastX = x;
this.lastY = y;
this.lastYawAcelleration = yawAcelleration;
this.lastPitchAcelleration = pitchAcelleration;
this.cinematic = this.ticks > 5;
if (cinematic) lastCinematic.reset();
sentPositionUpdate = false;
}
private static final Set<WPacketPlayOutPosition.EnumPlayerTeleportFlags>
relFlags = Sets.newHashSet(WPacketPlayOutPosition.EnumPlayerTeleportFlags.X,
WPacketPlayOutPosition.EnumPlayerTeleportFlags.Y,
WPacketPlayOutPosition.EnumPlayerTeleportFlags.Z,
WPacketPlayOutPosition.EnumPlayerTeleportFlags.X_ROT,
WPacketPlayOutPosition.EnumPlayerTeleportFlags.Y_ROT);
public void runPositionHackFix() {
if (sentPositionUpdate) return;
player.sendPacket(WPacketPlayOutPosition.builder().x(0).y(0).z(0).yaw(0).pitch(0).flags(relFlags)
.build());
sentPositionUpdate = true;
}
boolean isInvalidGCD() {
return pitchGCD < 0.0078125;
}
public static float getExperimentalDeltaX(APlayer data) {
float deltaPitch = data.getMovement().getDeltaYaw();
float sens = data.getMovement().sensitivityX;
float f = sens * 0.6f + .2f;
float calc = f * f * f * 8;
return deltaPitch / (calc * .15f);
}
public float getExperimentalDelta(float deltaAngle) {
float sens = player.getMovement().sensitivityMcp;
float f = sens * 0.6f + .2f;
float calc = f * f * f * 8;
return deltaAngle / (calc * .15f);
}
public double[] getEyeHeights() {
if (player.getPlayerVersion().isOrAbove(ProtocolVersion.V1_14)) {
return new double[]{0.4f, 1.27f, 1.62f};
} else if (player.getPlayerVersion().isOrAbove(ProtocolVersion.V1_9)) {
return new double[]{0.4f, 1.54f, 1.62f};
} else return new double[]{1.54f, 1.62f};
}
public static float getExperimentalDeltaY(APlayer data) {
float deltaPitch = data.getMovement().getDeltaPitch();
float sens = data.getMovement().sensitivityY;
float f = sens * 0.6f + .2f;
float calc = f * f * f * 8;
return deltaPitch / (calc * .15f);
}
public static int sensToPercent(float sensitivity) {
return MathHelper.floor_float(sensitivity / .5f * 100);
}
public static float percentToSens(int percent) {
return percent * .0070422534f;
}
public static float getSensitivityFromYawGCD(float gcd) {
return ((float) Math.cbrt(yawToF2(gcd) / 8f) - .2f) / .6f;
}
private static float getSensitivityFromPitchGCD(float gcd) {
return ((float) Math.cbrt(pitchToF3(gcd) / 8f) - .2f) / .6f;
}
private static float getF1FromYaw(float gcd) {
float f = getFFromYaw(gcd);
return f * f * f * 8;
}
private static float getFFromYaw(float gcd) {
float sens = getSensitivityFromYawGCD(gcd);
return sens * .6f + .2f;
}
private static float getFFromPitch(float gcd) {
float sens = getSensitivityFromPitchGCD(gcd);
return sens * .6f + .2f;
}
private static float getF1FromPitch(float gcd) {
float f = getFFromPitch(gcd);
return (float) Math.pow(f, 3) * 8;
}
private static float yawToF2(float yawDelta) {
return yawDelta / .15f;
}
private static float pitchToF3(float pitchDelta) {
int b0 = pitchDelta >= 0 ? 1 : -1; //Checking for inverted mouse.
return (pitchDelta / b0) / .15f;
}
public void addPosition(WPacketPlayOutPosition packet) {
int i = 0;
KLocation loc = new KLocation(packet.getX(), packet.getY(), packet.getZ(),
packet.getYaw(), packet.getPitch());
if (packet.getFlags().contains(WPacketPlayOutPosition.EnumPlayerTeleportFlags.X)) {
loc.x += player.getMovement().getTo().getLoc().x;
}
if (packet.getFlags().contains(WPacketPlayOutPosition.EnumPlayerTeleportFlags.Y)) {
loc.y += player.getMovement().getTo().getLoc().y;
}
if (packet.getFlags().contains(WPacketPlayOutPosition.EnumPlayerTeleportFlags.Z)) {
loc.z += player.getMovement().getTo().getLoc().z;
}
if (packet.getFlags().contains(WPacketPlayOutPosition.EnumPlayerTeleportFlags.X_ROT)) {
loc.pitch += player.getMovement().getTo().getLoc().pitch;
}
if (packet.getFlags().contains(WPacketPlayOutPosition.EnumPlayerTeleportFlags.Y_ROT)) {
loc.yaw += player.getMovement().getTo().getLoc().yaw;
}
teleportsToConfirm++;
player.runKeepaliveAction(ka -> teleportsToConfirm--, 2);
synchronized (posLocs) {
posLocs.add(loc);
}
}
/**
* 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;
}
private void checkForTeleports(WPacketPlayInFlying packet) {
if (packet.isMoved() && packet.isLooked() && !packet.isOnGround()) {
synchronized (posLocs) {
Iterator<KLocation> iterator = posLocs.iterator();
//Iterating through the ArrayList to find a potential teleport. We can't remove from the list
//without causing a CME unless we use Iterator#remove().
while (iterator.hasNext()) {
KLocation posLoc = iterator.next();
KLocation to = new KLocation(packet.getX(), packet.getY(), packet.getZ());
double distance = MathUtils.getDistanceWithoutRoot(to, posLoc);
if (distance < 1E-9) {
lastTeleport.reset();
iterator.remove();
break;
}
}
//Ensuring the list doesn't overflow with old locations, a potential crash exploit.
if (teleportsToConfirm == 0 && posLocs.size() > 0) {
posLocs.clear();
}
}
}
}
/**
* 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
*
* @param packet WPacketPlayInFlying
*/
private void updateLocations(WPacketPlayInFlying packet) {
if (to.getBox().max().lengthSquared() == 0) { //Needs initializing
setTo(packet);
from.setLoc(to);
} else {
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 = to.getLoc().yaw - from.getLoc().yaw;
deltaPitch = to.getLoc().pitch - from.getLoc().pitch;
player.getInfo().setClientGroundTicks(packet.isOnGround() ? player.getInfo().getClientGroundTicks() + 1 : 0);
player.getInfo().setClientAirTicks(!packet.isOnGround() ? player.getInfo().getClientAirTicks() + 1 : 0);
}
}
@@ -0,0 +1,421 @@
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.handler.entity.FakeMob;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.packet.wrapper.PacketType;
import dev.brighten.ac.packet.wrapper.in.*;
import dev.brighten.ac.packet.wrapper.out.*;
import dev.brighten.ac.utils.BlockUtils;
import dev.brighten.ac.utils.KLocation;
import dev.brighten.ac.utils.MovementUtils;
import dev.brighten.ac.utils.math.IntVector;
import lombok.val;
import net.minecraft.server.v1_8_R3.PacketDataSerializer;
import net.minecraft.server.v1_8_R3.PacketPlayInCustomPayload;
import net.minecraft.server.v1_8_R3.PacketPlayInSteerVehicle;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import java.util.*;
public class PacketHandler {
public boolean process(APlayer player, PacketType type, Object packetObject) {
long timestamp = System.currentTimeMillis();
switch (type) {
case CLIENT_TRANSACTION: {
WPacketPlayInTransaction packet = (WPacketPlayInTransaction) packetObject;
if(packet.getId() == 0) {
if(Anticheat.INSTANCE.getKeepaliveProcessor().keepAlives.getIfPresent(packet.getAction()) != null) {
Anticheat.INSTANCE.getKeepaliveProcessor().addResponse(player, packet.getAction());
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.getPlayerVersion().isOrAbove(ProtocolVersion.V1_9)
&& player.getMovement().getLastFlying().isPassed(1)) {
player.getMovement().runPositionHackFix();
}
if(player.instantTransaction.size() > 0) {
synchronized (player.instantTransaction) {
Deque<Short> toRemove = new LinkedList<>();
player.instantTransaction.forEach((key, tuple) -> {
if((timestamp - 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 = timestamp;
});
synchronized (player.keepAliveStamps) {
List<NormalAction> toRemove = new ArrayList<>();
for (NormalAction action : player.keepAliveStamps) {
if(action.stamp > ka.start) continue;
action.action.accept(ka);
toRemove.add(action);
}
toRemove.forEach(player.keepAliveStamps::remove);
toRemove.clear();
}
});
player.getLagInfo().getLastClientTransaction().reset();
}
Optional.ofNullable(player.instantTransaction.remove(packet.getAction()))
.ifPresent(t -> t.two.accept(t.one));
}
break;
}
case SERVER_ABILITIES: {
WPacketPlayOutAbilities packet = (WPacketPlayOutAbilities) packetObject;
player.getInfo().getLastAbilities().reset();
player.runInstantAction(ia -> {
if(!ia.isEnd()) {
player.getInfo().getPossibleCapabilities().add(packet.getCapabilities());
} else if(player.getInfo().getPossibleCapabilities().size() > 1) {
player.getInfo().getPossibleCapabilities().clear();
}
});
break;
}
case FLYING: {
WPacketPlayInFlying packet = (WPacketPlayInFlying) packetObject;
if(player.getMovement().isExcuseNextFlying()) {
player.getMovement().setExcuseNextFlying(false);
return false;
}
if (timestamp - player.getLagInfo().getLastFlying() <= 15) {
player.getLagInfo().getLastPacketDrop().reset();
}
player.getLagInfo().setLastFlying(timestamp);
player.getEntityLocationHandler().onFlying();
if(player.getPlayerVersion().isOrAbove(ProtocolVersion.V1_17)
&& packet.isMoved() && packet.isLooked()
&& MovementUtils.isSameLocation(new KLocation(packet.getX(), packet.getY(), packet.getZ()),
player.getMovement().getTo().getLoc())) {
player.getMovement().setExcuseNextFlying(true);
}
player.getMovement().process(packet);
break;
}
case BLOCK_CHANGE: {
WPacketPlayOutBlockChange packet = (WPacketPlayOutBlockChange) packetObject;
player.getBlockUpdateHandler().runUpdate(packet);
break;
}
case MULTI_BLOCK_CHANGE: {
WPacketPlayOutMultiBlockChange packet = (WPacketPlayOutMultiBlockChange) packetObject;
player.getBlockUpdateHandler().runUpdate(packet);
break;
}
case MAP_CHUNK: {
WPacketPlayOutMapChunk packet = (WPacketPlayOutMapChunk) packetObject;
player.getBlockUpdateHandler().runUpdate(packet);
break;
}
case ENTITY_EFFECT: {
WPacketPlayOutEntityEffect packet = (WPacketPlayOutEntityEffect) packetObject;
player.getPotionHandler().onPotionEffect(packet);
break;
}
case EXPLOSION: {
WPacketPlayOutExplosion packet = (WPacketPlayOutExplosion) packetObject;
Vector velocity = packet.getEntityPush().toBukkitVector();
player.getInfo().getVelocityHistory().add(velocity);
player.getInfo().setDoingVelocity(true);
player.runInstantAction(ka -> {
if(!ka.isEnd()) {
player.getVelocityHandler().onPre(packet);
} else player.getVelocityHandler().onPost(packet);
if(ka.isEnd() && player.getInfo().getVelocityHistory().contains(velocity)) {
player.getInfo().setDoingVelocity(false);
player.getInfo().getVelocity().reset();
synchronized (player.getInfo().getVelocityHistory()) {
player.getInfo().getVelocityHistory().remove(velocity);
}
}
});
player.runKeepaliveAction(ka -> {
if(player.getInfo().getVelocityHistory().contains(velocity))
player.getOnVelocityTasks().forEach(task -> task.accept(velocity));
}, 1);
break;
}
case VELOCITY: {
WPacketPlayOutEntityVelocity packet = (WPacketPlayOutEntityVelocity) packetObject;
if(packet.getEntityId() == player.getBukkitPlayer().getEntityId()) {
Vector velocity = new Vector(packet.getDeltaX(), packet.getDeltaY(), packet.getDeltaZ());
player.getInfo().getVelocityHistory().add(velocity);
player.getInfo().setDoingVelocity(true);
player.runInstantAction(ka -> {
if(!ka.isEnd()) {
player.getVelocityHandler().onPre(packet);
} else player.getVelocityHandler().onPost(packet);
if(ka.isEnd() && player.getInfo().getVelocityHistory().contains(velocity)) {
player.getInfo().setDoingVelocity(false);
player.getInfo().getVelocity().reset();
synchronized (player.getInfo().getVelocityHistory()) {
player.getInfo().getVelocityHistory().remove(velocity);
}
}
});
player.runKeepaliveAction(ka -> {
if(player.getInfo().getVelocityHistory().contains(velocity))
player.getOnVelocityTasks().forEach(task -> task.accept(velocity));
}, 1);
}
break;
}
case RESPAWN: {
if(player.getPlayerVersion().isBelow(ProtocolVersion.V1_14)) {
player.runKeepaliveAction(k -> player.getBukkitPlayer().setSprinting(false), 1);
}
player.runKeepaliveAction(ka -> player.getInfo().lastRespawn.reset());
break;
}
case SERVER_POSITION: {
player.getMovement().addPosition((WPacketPlayOutPosition) packetObject);
break;
}
case ATTACH: {
WPacketPlayOutAttachEntity packet = (WPacketPlayOutAttachEntity) packetObject;
if(packet.getHoldingEntityId() != -1) {
player.getInfo().setInVehicle(true);
player.getInfo().getVehicleSwitch().reset();
} else {
player.getInfo().setInVehicle(false);
player.getInfo().getVehicleSwitch().reset();
}
break;
}
case STEER_VEHICLE: {
PacketPlayInSteerVehicle packet = (PacketPlayInSteerVehicle) packetObject;
// Check for isUnmount()
if(player.getBukkitPlayer().isInsideVehicle() && packet.d()) {
player.getInfo().getVehicleSwitch().reset();
player.getInfo().setInVehicle(false);
}
break;
}
case CLIENT_PAYLOAD: {
PacketPlayInCustomPayload packet = (PacketPlayInCustomPayload) packetObject;
if(packet.a().equals("Time|Receive")) {
PacketDataSerializer serial = packet.b();
long serverTime = serial.readLong();
long clientReceivedTime = serial.readLong();
long currentTime = timestamp;
long serverPing = clientReceivedTime - serverTime;
long clientToServer = currentTime - clientReceivedTime;
long totalFeedback = currentTime - serverTime;
player.getBukkitPlayer().sendMessage(String.format("total: %sms client-server: %sms server-client: %sms", totalFeedback, clientToServer, serverPing));
}
break;
}
case ENTITY_DESTROY: {
WPacketPlayOutEntityDestroy packet = (WPacketPlayOutEntityDestroy) packetObject;
player.getEntityLocationHandler().onEntityDestroy(packet);
break;
}
case ENTITY_ACTION: {
WPacketPlayInEntityAction packet = (WPacketPlayInEntityAction) packetObject;
switch (packet.getAction()) {
case START_SNEAKING: {
player.getInfo().setSneaking(true);
break;
}
case STOP_SNEAKING: {
player.getInfo().setSneaking(false);
break;
}
case START_SPRINTING: {
player.getInfo().setSprinting(true);
break;
}
case STOP_SPRINTING: {
player.getInfo().setSprinting(false);
break;
}
}
break;
}
case ENTITY_TELEPORT: {
WPacketPlayOutEntityTeleport packet = (WPacketPlayOutEntityTeleport) packetObject;
player.getEntityLocationHandler().onTeleportSent(packet);
break;
}
case ENTITY:
case ENTITY_MOVE:
case ENTITY_LOOK:
case ENTITY_MOVELOOK: {
WPacketPlayOutEntity packet = (WPacketPlayOutEntity) packetObject;
player.getEntityLocationHandler().onRelPosition(packet);
break;
}
case USE_ENTITY: {
WPacketPlayInUseEntity packet = (WPacketPlayInUseEntity) packetObject;
FakeMob mob = Anticheat.INSTANCE.getFakeTracker().getEntityById(packet.getEntityId());
if(packet.getAction() == WPacketPlayInUseEntity.EnumEntityUseAction.ATTACK) {
if(mob != null) {
player.getEntityLocationHandler().getTargetOfFakeMob(mob.getEntityId())
.ifPresent(targetId -> {
player.getEntityLocationHandler().removeFakeMob(targetId);
player.getInfo().lastFakeBotHit.reset();
});
if(player.getMob().getEntityId() == packet.getEntityId()) {
player.getInfo().botAttack.reset();
}
} else {
Entity target = packet.getEntity(player.getBukkitPlayer().getWorld());
if(target instanceof LivingEntity) {
if(player.getInfo().lastFakeBotHit.isPassed(400) && Math.random() > 0.9) {
player.getEntityLocationHandler().canCreateMob.add(target.getEntityId());
}
player.getInfo().setTarget((LivingEntity) target);
}
}
player.getInfo().lastAttack.reset();
}
break;
}
case ARM_ANIMATION: {
long delta = timestamp - player.getInfo().lastArmSwing;
player.getInfo().cps.add(1000D / delta, timestamp);
player.getInfo().lastArmSwing = timestamp;
break;
}
case BLOCK_PLACE: {
WPacketPlayInBlockPlace packet = (WPacketPlayInBlockPlace) packetObject;
IntVector pos = packet.getBlockPos();
ItemStack stack = packet.getItemStack();
player.getInfo().getLastBlockPlace().reset();
// Used item
if(pos.getX() == -1 && (pos.getY() == 255 | pos.getY() == -1) && pos.getZ() == -1
&& stack != null
&& BlockUtils.isUsable(stack.getType())) {
player.getInfo().getLastUseItem().reset();
}
player.getBlockUpdateHandler().onPlace(packet);
break;
}
case BLOCK_DIG: {
WPacketPlayInBlockDig packet = (WPacketPlayInBlockDig) packetObject;
player.getInfo().getLastBlockDig().reset();
player.getBlockUpdateHandler().onDig(packet);
break;
}
case CLIENT_COMMAND: {
WPacketPlayInClientCommand packet = (WPacketPlayInClientCommand) packetObject;
if(packet.getCommand() == WPacketPlayInClientCommand.WrappedEnumClientCommand
.OPEN_INVENTORY_ACHIEVEMENT) {
player.getInfo().setInventoryOpen(true);
player.getInfo().lastInventoryOpen.reset();
return true;
}
break;
}
case CLIENT_CLOSE_WINDOW: {
player.getInfo().setInventoryOpen(false);
break;
}
case SERVER_CLOSE_WINDOW: {
player.runKeepaliveAction(ka -> player.getInfo().setInventoryOpen(false));
break;
}
case SERVER_OPEN_WINDOW: {
player.runKeepaliveAction(ka -> {
player.getInfo().setInventoryOpen(true);
player.getInfo().lastInventoryOpen.reset();
});
break;
}
}
if(player.sniffing) {
if(type != PacketType.UNKNOWN) {
player.sniffedPackets.add("[" + Anticheat.INSTANCE.getKeepaliveProcessor().tick + "] " +
"" + type.name() + ": " + packetObject.toString());
} else {
player.sniffedPackets.add("[" + Anticheat.INSTANCE.getKeepaliveProcessor().tick + "] (UNKNOWN) " +
"" + packetObject.getClass().getSimpleName() + ": " + packetObject);
}
}
boolean cancelled = player.getCheckHandler().callSyncPacket(packetObject, timestamp);
// Post flying settings
if(type.equals(PacketType.FLYING)) {
player.getVelocityHandler().onFlyingPost((WPacketPlayInFlying)packetObject);
player.getInfo().lsneaking = player.getInfo().sneaking;
}
return cancelled;
}
}
@@ -0,0 +1,59 @@
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.val;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
public class PotionHandler {
private final APlayer data;
public List<PotionEffect> potionEffects = new CopyOnWriteArrayList<>();
public PotionHandler(APlayer data) {
this.data = data;
potionEffects.addAll(data.getBukkitPlayer().getActivePotionEffects());
}
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,78 @@
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.WPacketPlayOutEntityVelocity;
import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutExplosion;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.bukkit.util.Vector;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
@RequiredArgsConstructor
public class VelocityHandler {
private final APlayer PLAYER;
private final Map<Vector, Boolean> VELOCITY_MAP = new HashMap<>();
private final Set<Consumer<Vector>> VELOCITY_TASKS = new HashSet<>();
/*
* I want to be able to verify velocity when the pre packet comes back an the post packet comes back
* So essentially I want to only take out the velocity from possibilities after the post flying comes back.
*/
public void onPre(WPacketPlayOutEntityVelocity packet) {
if(packet.getEntityId() != PLAYER.getBukkitPlayer().getEntityId()) return;
VELOCITY_MAP.put(new Vector(packet.getDeltaX(), packet.getDeltaY(), packet.getDeltaZ()), false);
}
public void onPre(WPacketPlayOutExplosion packet) {
VELOCITY_MAP.put(packet.getEntityPush().toBukkitVector(), false);
}
public void onPost(WPacketPlayOutEntityVelocity packet) {
if(packet.getEntityId() != PLAYER.getBukkitPlayer().getEntityId()) return;
VELOCITY_MAP.computeIfPresent(new Vector(packet.getDeltaX(), packet.getDeltaY(), packet.getDeltaZ()),
(velocity, queuedToRemove) -> true);
}
public void onPost(WPacketPlayOutExplosion packet) {
VELOCITY_MAP.computeIfPresent(packet.getEntityPush().toBukkitVector(),
(velocity, queuedToRemove) -> true);
}
public Set<Vector> getPossibleVectors() {
return VELOCITY_MAP.keySet();
}
public void onAccurateVelocity(Consumer<Vector> task) {
VELOCITY_TASKS.add(task);
}
public void onFlyingPost(WPacketPlayInFlying packet) {
val iterator = VELOCITY_MAP.entrySet().iterator();
while(iterator.hasNext()) {
val value = iterator.next();
// Velocity definitely occurred, run task.
if(Math.abs(value.getKey().getY() - packet.getY()) < 1E-6) {
VELOCITY_TASKS.forEach(vel -> vel.accept(value.getKey()));
}
if(value.getValue())
iterator.remove();
}
}
}
@@ -0,0 +1,168 @@
package dev.brighten.ac.handler.block;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInBlockDig;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInBlockPlace;
import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutBlockChange;
import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutMapChunk;
import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutMultiBlockChange;
import dev.brighten.ac.utils.BlockUtils;
import dev.brighten.ac.utils.Materials;
import dev.brighten.ac.utils.XMaterial;
import dev.brighten.ac.utils.math.IntVector;
import dev.brighten.ac.utils.world.types.RayCollision;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import lombok.RequiredArgsConstructor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import java.util.Optional;
@RequiredArgsConstructor
public class BlockUpdateHandler {
private final Object2ObjectOpenHashMap<IntVector, WrappedBlock> blockInformation = new Object2ObjectOpenHashMap<>();
private final APlayer player;
public void onWorldChange() {
blockInformation.clear();
}
/**
* Keep track of block placements since the Bukkit API will be a bit behind
*
* @param place wrapped PacketPlayInBlockPlace
*/
public void onPlace(WPacketPlayInBlockPlace place) {
player.getInfo().lastBlockUpdate.reset();
// Could not possibly be a block placement as it's not a block a player is holding.
IntVector pos = place.getBlockPos().clone();
// Some dumbass shit I have to do because Minecraft with Lilypads
if (place.getItemStack() != null && BlockUtils.getXMaterial(place.getItemStack().getType()).equals(XMaterial.LILY_PAD)) {
RayCollision rayCollision = new RayCollision(player.getBukkitPlayer().getEyeLocation().toVector(),
player.getBukkitPlayer().getLocation().getDirection());
Block block = rayCollision.getClosestBlockOfType(player.getBukkitPlayer().getWorld(), Materials.LIQUID, 5);
if (block != null) {
if (Materials.checkFlag(block.getType(), Materials.WATER)) {
pos = new IntVector(block.getX(), block.getY() + 1, block.getZ());
}
} else return;
} // Not an actual block place, just an interact
else if (pos.getX() == -1 && (pos.getY() == 255 || pos.getY() == -1) && pos.getZ() == -1) {
return;
} else {
pos.setX(pos.getX() + place.getDirection().getAdjacentX());
pos.setY(pos.getY() + place.getDirection().getAdjacentY());
pos.setZ(pos.getZ() + place.getDirection().getAdjacentZ());
}
player.getInfo().getLastPlace().reset();
synchronized (blockInformation) {
blockInformation.put(pos, new WrappedBlock(pos.toLocation(player.getBukkitPlayer().getWorld()),
place.getItemStack().getType(), (byte) 0));
}
}
/**
* Keep track of block breaking since the Bukkit API will be a bit behind.
*
* @param dig Wrapped PacketPlayInBlockDig
*/
public void onDig(WPacketPlayInBlockDig dig) {
player.getInfo().lastBlockUpdate.reset();
if (dig.getDigType() == WPacketPlayInBlockDig.EnumPlayerDigType.STOP_DESTROY_BLOCK) {
synchronized (blockInformation) {
blockInformation.put(dig.getBlockPos(),
new WrappedBlock(dig.getBlockPos().toLocation(player.getBukkitPlayer().getWorld()),
Material.AIR, (byte) 0));
}
}
}
public void runUpdate(WPacketPlayOutBlockChange packet) {
player.getInfo().lastBlockUpdate.reset();
synchronized (blockInformation) {
// Updating block information
player.runInstantAction(k -> {
if (k.isEnd()) {
blockInformation.put(packet.getBlockLocation(),
new WrappedBlock(packet.getBlockLocation().toLocation(player.getBukkitPlayer().getWorld()),
packet.getMaterial(), packet.getBlockData()));
}
});
}
}
public void runUpdate(WPacketPlayOutMultiBlockChange packet) {
player.getInfo().lastBlockUpdate.reset();
player.runInstantAction(k -> {
if (k.isEnd()) {
synchronized (blockInformation) {
for (WPacketPlayOutMultiBlockChange.BlockChange info : packet.getChanges()) {
WrappedBlock block = new WrappedBlock(info.getLocation()
.toLocation(player.getBukkitPlayer().getWorld()),
info.getMaterial(), info.getData());
blockInformation.put(info.getLocation(),
new WrappedBlock(info.getLocation().toLocation(player.getBukkitPlayer().getWorld()),
info.getMaterial(), info.getData()));
}
}
}
});
}
public void runUpdate(WPacketPlayOutMapChunk chunkUpdate) {
player.runInstantAction(k -> {
if(!k.isEnd()) {
synchronized (blockInformation) {
chunkUpdate.getChunk().getBlocks().forEach((vec, mblock) -> {
WrappedBlock block = new WrappedBlock(vec.toLocation(player.getBukkitPlayer().getWorld()),
mblock.material, mblock.data);
blockInformation.put(vec, block);
});
}
}
});
}
public WrappedBlock getBlock(IntVector loc) {
synchronized (blockInformation) {
WrappedBlock block = blockInformation.get(loc);
if (block == null) {
Optional<Block> bukkitBlock = BlockUtils.getBlockAsync(
new Location(player.getBukkitPlayer().getWorld(), loc.getX(), loc.getY(), loc.getZ()));
if (bukkitBlock.isPresent()) {
Location bloc = bukkitBlock.get().getLocation();
IntVector intVec = new IntVector(bloc.getBlockX(), bloc.getBlockY(), bloc.getBlockZ());
block = new WrappedBlock(intVec.toLocation(player.getBukkitPlayer().getWorld()),
bukkitBlock.get().getType(), bukkitBlock.get().getData());
blockInformation.put(loc, block);
} else
block = new WrappedBlock(loc.toLocation(player.getBukkitPlayer().getWorld()), Material.AIR, (byte)0);
}
return block;
}
}
public WrappedBlock getRelative(IntVector location, int modX, int modY, int modZ) {
return getBlock(location.clone().add(modX, modY, modZ));
}
public WrappedBlock getRelative(IntVector location, BlockFace face, int distance) {
return getRelative(location,
face.getModX() * distance, face.getModY() * distance, face.getModZ() * distance);
}
public WrappedBlock getRelative(IntVector location, BlockFace face) {
return getBlock(location.clone().add(face.getModX(), face.getModY(), face.getModZ()));
}
}
@@ -0,0 +1,14 @@
package dev.brighten.ac.handler.block;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.bukkit.Location;
import org.bukkit.Material;
@Getter
@AllArgsConstructor
public class WrappedBlock {
private Location location;
private Material type;
private byte data;
}
@@ -0,0 +1,28 @@
package dev.brighten.ac.handler.compat;
import dev.brighten.ac.handler.compat.impl.CompatHandler1_13;
import dev.brighten.ac.handler.compat.impl.CompatHandler1_8;
import dev.brighten.ac.handler.compat.impl.CompatHandler1_9;
import dev.brighten.ac.packet.ProtocolVersion;
import org.bukkit.entity.Player;
public abstract class CompatHandler {
public abstract boolean isRiptiding(Player player);
public abstract boolean isGliding(Player player);
private static CompatHandler instance;
public static CompatHandler getInstance() {
if (instance == null) {
if(ProtocolVersion.getGameVersion().isOrAbove(ProtocolVersion.V1_13)) {
return new CompatHandler1_13();
} else if(ProtocolVersion.getGameVersion().isOrAbove(ProtocolVersion.V1_9)) {
return new CompatHandler1_9();
} else return new CompatHandler1_8();
}
return instance;
}
}
@@ -0,0 +1,17 @@
package dev.brighten.ac.handler.compat.impl;
import dev.brighten.ac.handler.compat.CompatHandler;
import org.bukkit.entity.Player;
public class CompatHandler1_13 extends CompatHandler {
@Override
public boolean isRiptiding(Player player) {
return player.isRiptiding();
}
@Override
public boolean isGliding(Player player) {
return player.isGliding();
}
}
@@ -0,0 +1,17 @@
package dev.brighten.ac.handler.compat.impl;
import dev.brighten.ac.handler.compat.CompatHandler;
import org.bukkit.entity.Player;
public class CompatHandler1_8 extends CompatHandler {
@Override
public boolean isRiptiding(Player player) {
return false;
}
@Override
public boolean isGliding(Player player) {
return false;
}
}
@@ -0,0 +1,17 @@
package dev.brighten.ac.handler.compat.impl;
import dev.brighten.ac.handler.compat.CompatHandler;
import org.bukkit.entity.Player;
public class CompatHandler1_9 extends CompatHandler {
@Override
public boolean isRiptiding(Player player) {
return false;
}
@Override
public boolean isGliding(Player player) {
return player.isGliding();
}
}
@@ -0,0 +1,28 @@
package dev.brighten.ac.handler.entity;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
public class FakeEntityTracker {
@Getter
private final Int2ObjectMap<FakeMob> entityMap = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
public FakeMob getEntityById(int id) {
return entityMap.get(id);
}
public void trackEntity(FakeMob player) {
entityMap.put(player.getEntityId(), player);
}
public void untrackEntity(FakeMob player) {
entityMap.remove(player.getEntityId());
}
public void despawnAll() {
entityMap.values().forEach(FakeMob::despawn);
entityMap.clear();
}
}
@@ -0,0 +1,143 @@
package dev.brighten.ac.handler.entity;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.objects.WrappedWatchableObject;
import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutEntity;
import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutEntityMetadata;
import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutEntityTeleport;
import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutSpawnEntityLiving;
import lombok.Getter;
import net.minecraft.server.v1_8_R3.PacketPlayOutEntityDestroy;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
@Getter
public class FakeMob {
private int entityId;
private EntityType type;
private List<APlayer> watching = Collections.emptyList();
public FakeMob(EntityType type) {
entityId = ThreadLocalRandom.current().nextInt(15000, 20000);
this.type = type;
}
/*
protected void b(int i, boolean flag) {
byte b0 = this.datawatcher.getByte(0);
if (flag) {
this.datawatcher.watch(0, (byte)(b0 | 1 << i));
} else {
this.datawatcher.watch(0, (byte)(b0 & ~(1 << i)));
}
}
*/
public void spawn(boolean invisible, Location location, APlayer... players) {
spawn(invisible, location, new ArrayList<>(), players);
}
public void spawn(boolean invisible, Location location, List<WrappedWatchableObject> objects, APlayer... players) {
if(watching.size() > 0) {
despawn();
}
watching = new ArrayList<>();
for (APlayer player : players) {
if(invisible) {
objects.add(new WrappedWatchableObject(0, 0, (byte)((byte)1 << 5)));
}
WPacketPlayOutSpawnEntityLiving packet = WPacketPlayOutSpawnEntityLiving.builder()
.entityId(entityId)
.type(type)
.x(location.getX())
.y(location.getY())
.z(location.getZ())
.yaw(location.getYaw())
.pitch(location.getPitch())
.headYaw(location.getYaw())
.motionX(0)
.motionY(0)
.motionZ(0)
.watchedObjects(objects)
.build();
player.sendPacketSilently(packet);
watching.add(player);
}
Anticheat.INSTANCE.getFakeTracker().trackEntity(this);
}
public void setInvisible(boolean invisible) {
List<WrappedWatchableObject> objects = new ArrayList<>();
if(invisible) {
objects.add(new WrappedWatchableObject(0, 0, (byte)((byte)1 << 5)));
} else {
objects.add(new WrappedWatchableObject(0, 0, (byte)~((byte)1 << 5)));
}
WPacketPlayOutEntityMetadata packet = WPacketPlayOutEntityMetadata.builder()
.entityId(entityId)
.watchedObjects(objects)
.build();
watching.forEach(player -> player.sendPacketSilently(packet));
}
public void despawn() {
for (APlayer aPlayer : watching) {
PacketPlayOutEntityDestroy destroyEntity = new PacketPlayOutEntityDestroy(entityId);
aPlayer.sendPacketSilently(destroyEntity);
}
watching = Collections.emptyList();
Anticheat.INSTANCE.getFakeTracker().untrackEntity(this);
}
public void move(double dx, double dy, double dz) {
WPacketPlayOutEntity packet = WPacketPlayOutEntity.builder().id(entityId).x(dx).y(dy).z(dz).moved(true).build();
for (APlayer player : watching) {
player.sendPacketSilently(packet);
}
}
public void move(double dx, double dy, double dz, float dyaw, float dpitch) {
WPacketPlayOutEntity packet = WPacketPlayOutEntity.builder().id(entityId).x(dx).y(dy).z(dz).yaw(dyaw)
.pitch(dpitch).moved(true).looked(true).build();
for (APlayer player : watching) {
player.sendPacketSilently(packet);
}
}
public void move(float dyaw, float dpitch) {
WPacketPlayOutEntity packet = WPacketPlayOutEntity.builder().id(entityId).yaw(dyaw).pitch(dpitch)
.looked(true).build();
for (APlayer player : watching) {
player.sendPacketSilently(packet);
}
}
public void teleport(double x, double y, double z, float yaw, float pitch) {
WPacketPlayOutEntityTeleport packet = WPacketPlayOutEntityTeleport.builder()
.entityId(entityId)
.x(x).y(y).z(z).yaw(yaw).pitch(pitch).onGround(false)
.build();
for (APlayer player : watching) {
player.sendPacketSilently(packet);
}
}
}
@@ -0,0 +1,45 @@
package dev.brighten.ac.handler.keepalive;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.data.APlayer;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.RequiredArgsConstructor;
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 Int2ObjectMap<KAReceived> receivedKeepalive = new Int2ObjectOpenHashMap<>(400);
public void received(APlayer player) {
receivedKeepalive.put(player.getBukkitPlayer().getUniqueId().hashCode(),
new KAReceived(player, Anticheat.INSTANCE.getKeepaliveProcessor().tick));
}
public Optional<KAReceived> getReceived(UUID uuid) {
int hashCode = uuid.hashCode();
if(receivedKeepalive.containsKey(hashCode)) {
return Optional.of(receivedKeepalive.get(hashCode));
}
return Optional.empty();
}
@RequiredArgsConstructor
public static class KAReceived {
public final APlayer data;
public final int stamp;
public long receivedStamp;
}
}
@@ -0,0 +1,104 @@
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.packet.wrapper.out.WPacketPlayOutTransaction;
import dev.brighten.ac.utils.BukkitRunnable;
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 org.bukkit.scheduler.BukkitTask;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
public class KeepaliveProcessor implements BukkitRunnable {
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() {
}
@Override
public void run(BukkitTask task) {
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 (tick % 5 == 0) {
double dh = Math.min(value.getMovement().getDeltaXZ(), 1),
dy = Math.min(1, Math.abs(value.getMovement().getDeltaY()));
value.getInfo().nearbyEntities = value.getBukkitPlayer()
.getNearbyEntities(2 + dh, 3 + dy, 2 + dh);
}
WPacketPlayOutTransaction transaction = WPacketPlayOutTransaction.builder().id(0)
.action(currentKeepalive.id).accept(false).build();
HandlerAbstract.getHandler().sendPacketSilently(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, 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;
}
}
}
@@ -0,0 +1,11 @@
package dev.brighten.ac.handler.keepalive.actions;
public interface Action {
void run();
boolean confirmed();
ActionType type();
}
@@ -0,0 +1,53 @@
package dev.brighten.ac.handler.keepalive.actions;
import dev.brighten.ac.utils.RunUtils;
import lombok.Getter;
import lombok.val;
import java.util.*;
import java.util.function.Consumer;
public class ActionManager {
public ActionManager() {
// Removing any unconfirmed actions.
RunUtils.taskTimerAsync(task -> unconfirmedActions.removeIf(Action::confirmed), 40, 40);
}
private final Map<Class<? extends Action>, List<Consumer<Action>>> actionListeners = new HashMap<>();
@Getter
private final Set<Action> unconfirmedActions = new HashSet<>();
public void listenForConfirmation(Class<? extends Action> action, Consumer<Action> listener) {
synchronized (actionListeners) {
actionListeners.compute(action, (key, list) -> {
if(list == null) {
list = new ArrayList<>();
}
list.add(listener);
return list;
});
}
}
public void confirmedAction(Action action) {
unconfirmedActions.remove(action);
val list = actionListeners.get(action.getClass());
if(list != null) {
for (Consumer<Action> actionConsumer : list) {
actionConsumer.accept(action);
}
}
}
public void addAction(Action action) {
if(!action.confirmed())
unconfirmedActions.add(action);
}
}
@@ -0,0 +1,6 @@
package dev.brighten.ac.handler.keepalive.actions;
public enum ActionType {
CONDITIONAL,
TRASNSACTION
}
@@ -0,0 +1,35 @@
package dev.brighten.ac.handler.keepalive.actions.types;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.handler.keepalive.actions.Action;
import dev.brighten.ac.handler.keepalive.actions.ActionType;
import lombok.Data;
@Data
public abstract class TransactionAction implements Action {
private final APlayer player;
private boolean confirmed = false;
public TransactionAction(APlayer player) {
this.player = player;
player.runKeepaliveAction(ka -> {
confirmed = true;
run();
Anticheat.INSTANCE.getActionManager().confirmedAction(this);
});
}
@Override
public abstract void run();
@Override
public boolean confirmed() {
return confirmed;
}
@Override
public ActionType type() {
return ActionType.TRASNSACTION;
}
}
@@ -0,0 +1,7 @@
package dev.brighten.ac.handler.protocolsupport;
import org.bukkit.entity.Player;
public interface Protocol {
int getPlayerVersion(Player player);
}
@@ -0,0 +1,35 @@
package dev.brighten.ac.handler.protocolsupport;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.handler.protocolsupport.impl.NoAPI;
import dev.brighten.ac.handler.protocolsupport.impl.ProtocolSupport;
import dev.brighten.ac.handler.protocolsupport.impl.ViaVersionAPI;
import dev.brighten.ac.utils.annotation.Init;
import dev.brighten.ac.utils.annotation.Instance;
import org.bukkit.Bukkit;
import java.util.HashMap;
import java.util.Map;
@Init
public class ProtocolAPI {
public Map<String, Integer> protocolVersionByIP = new HashMap<>();
public static Protocol INSTANCE;
@Instance
public static ProtocolAPI classInstance;
public ProtocolAPI() {
if(Bukkit.getPluginManager().isPluginEnabled("ViaVersion")) {
Anticheat.INSTANCE.alog("Using ViaVersion for ProtocolAPI");
INSTANCE = new ViaVersionAPI();
} else if(Bukkit.getPluginManager().isPluginEnabled("ProtocolSupport")) {
Anticheat.INSTANCE.alog("Using ProtocolSupport for ProtocolAPI");
INSTANCE = new ProtocolSupport();
} else {
Anticheat.INSTANCE.alog("Using Vanilla API for ProtocolAPI");
INSTANCE = new NoAPI();
}
}
}
@@ -0,0 +1,13 @@
package dev.brighten.ac.handler.protocolsupport.impl;
import dev.brighten.ac.handler.protocolsupport.Protocol;
import dev.brighten.ac.packet.handler.HandlerAbstract;
import org.bukkit.entity.Player;
public class NoAPI implements Protocol {
@Override
public int getPlayerVersion(Player player) {
return HandlerAbstract.getHandler().getProtocolVersion(player);
}
}
@@ -0,0 +1,13 @@
package dev.brighten.ac.handler.protocolsupport.impl;
import dev.brighten.ac.handler.protocolsupport.Protocol;
import org.bukkit.entity.Player;
import protocolsupport.api.ProtocolSupportAPI;
public class ProtocolSupport implements Protocol {
@Override
public int getPlayerVersion(Player player) {
return ProtocolSupportAPI.getProtocolVersion(player).getId();
}
}
@@ -0,0 +1,13 @@
package dev.brighten.ac.handler.protocolsupport.impl;
import dev.brighten.ac.handler.protocolsupport.Protocol;
import org.bukkit.entity.Player;
import us.myles.ViaVersion.api.Via;
public class ViaVersionAPI implements Protocol {
@Override
public int getPlayerVersion(Player player) {
return Via.getAPI().getPlayerVersion(player.getUniqueId());
}
}
@@ -0,0 +1,27 @@
package dev.brighten.ac.handler.thread;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import java.util.concurrent.ExecutorService;
@RequiredArgsConstructor
@Getter
@Setter
public class PlayerThread {
private int count;
private final ExecutorService thread;
public void addCount() {
count++;
}
public void subtractCount() {
count--;
}
public void runTask(Runnable runnable) {
thread.execute(runnable);
}
}
@@ -0,0 +1,80 @@
package dev.brighten.ac.handler.thread;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.utils.MiscUtils;
import dev.brighten.ac.utils.RunUtils;
import dev.brighten.ac.utils.annotation.Init;
import lombok.Getter;
import org.bukkit.entity.Player;
import java.util.*;
import java.util.concurrent.Executors;
@Init
public class ThreadHandler {
@Getter
private final List<PlayerThread> services = new ArrayList<>();
private final Map<UUID, Integer> threadCorrelations = new HashMap<>();
private final int maxThreads;
public static ThreadHandler INSTANCE;
public ThreadHandler() {
INSTANCE = this;
maxThreads = Runtime.getRuntime().availableProcessors() * 2;
}
public PlayerThread getThread(APlayer player) {
synchronized (threadCorrelations) {
if(threadCorrelations.containsKey(player.getUuid())) {
return services.get(threadCorrelations.get(player.getUuid()));
}
PlayerThread thread = services.size() < Math.max(1, maxThreads)
? generatePlayerThread()
: services.stream()
.min(Comparator.comparing(PlayerThread::getCount)).orElse(MiscUtils.randomElement(services));
int index = services.indexOf(thread);
thread.addCount();
threadCorrelations.put(player.getUuid(), index);
return thread;
}
}
public void removePlayer(Player player) {
synchronized (threadCorrelations) {
if(threadCorrelations.containsKey(player.getUniqueId())) {
int index = threadCorrelations.remove(player.getUniqueId());
services.get(index).subtractCount();
}
}
}
private PlayerThread generatePlayerThread() {
PlayerThread thread = new PlayerThread(Executors.newFixedThreadPool(1, new ThreadFactoryBuilder()
.setNameFormat("Kauri Player Thread " + services.size() + 1)
.setUncaughtExceptionHandler((t, e) -> RunUtils.task(e::printStackTrace))
.build()));
services.add(thread);
return thread;
}
public int threadCount() {
return services.size();
}
public void shutdown() {
threadCorrelations.clear();
for (PlayerThread service : services) {
service.getThread().shutdownNow();
}
services.clear();
}
}
@@ -0,0 +1,84 @@
package dev.brighten.ac.listener;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.objects.WrappedWatchableObject;
import dev.brighten.ac.utils.RunUtils;
import dev.brighten.ac.utils.annotation.Init;
import dev.brighten.ac.utils.world.types.RayCollision;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.player.PlayerEditBookEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import java.util.ArrayList;
import java.util.Collections;
@Init
public class GeneralListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onDamage(EntityDamageByEntityEvent event) {
if(event.getDamager() instanceof Player) {
APlayer player = Anticheat.INSTANCE.getPlayerRegistry().getPlayer(event.getDamager().getUniqueId()).
orElse(null);
if(player == null) return;
if(player.hitsToCancel > 0) {
player.hitsToCancel--;
event.setCancelled(true);
}
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onInteract(PlayerInteractEvent event) {
Anticheat.INSTANCE.getPlayerRegistry().getPlayer(event.getPlayer().getUniqueId()).ifPresent(player -> {
player.getInfo().breakingBlock = event.getAction().equals(Action.LEFT_CLICK_BLOCK);
});
}
@EventHandler(ignoreCancelled = true)
public void onBookEdit(PlayerEditBookEvent event) {
Anticheat.INSTANCE.getPlayerRegistry().getPlayer(event.getPlayer().getUniqueId())
.ifPresent(player -> player.getCheckHandler().callEvent(event));
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onTeleport(PlayerTeleportEvent event) {
if(event.getFrom().getWorld().equals(event.getTo().getWorld())) return;
Anticheat.INSTANCE.getPlayerRegistry().getPlayer(event.getPlayer().getUniqueId())
.ifPresent(player -> {
player.getBlockUpdateHandler().onWorldChange();
// Updating bot loc when changing worlds
Location origin = event.getTo().clone().add(0, 1.7, 0);
RayCollision coll = new RayCollision(origin.toVector(), origin.getDirection().multiply(-1));
Location loc1 = coll.collisionPoint(1.2).toLocation(event.getTo().getWorld());
RunUtils.taskLater(() -> {
player.getMob().despawn();
player.getMob().spawn(true, loc1,
new ArrayList<>(Collections.singletonList(
new WrappedWatchableObject(0, 16, (byte) 1))), player);
}, 5);
});
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlace(BlockPlaceEvent event) {
Anticheat.INSTANCE.getPlayerRegistry().getPlayer(event.getPlayer().getUniqueId())
.ifPresent(player -> player.getCheckHandler().callEvent(event));
}
}
@@ -0,0 +1,109 @@
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.utils.RunUtils;
import dev.brighten.ac.utils.annotation.Init;
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.PlayerKickEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.Optional;
@Init
public class JoinListener implements Listener {
public JoinListener() {
Anticheat.INSTANCE.getPacketProcessor().processAsync(EventPriority.NORMAL, event -> {
if(event.isCancelled()) return;
Optional<APlayer> aplayer = Anticheat.INSTANCE.getPlayerRegistry()
.getPlayer(event.getPlayer().getUniqueId());
aplayer.ifPresent(player -> {
if(Anticheat.INSTANCE.getPacketHandler()
.process(player, event.getType(), event.getPacket())) {
event.setCancelled(true);
}
});
});
Anticheat.INSTANCE.getPacketProcessor().process(EventPriority.HIGHEST, event -> {
Optional<APlayer> op = Anticheat.INSTANCE.getPlayerRegistry().getPlayer(event.getPlayer().getUniqueId());
if(!op.isPresent()) {
return;
}
APlayer player = op.get();
if(player.isSendingPackets()) return;
if(event.getType().equals(PacketType.CLIENT_TRANSACTION)) {
if(player.getPacketQueue().size() > 0) {
player.setSendingPackets(true);
Object packetToSend = null;
synchronized (player.getPacketQueue()) {
while((packetToSend = player.getPacketQueue().pollFirst()) != null) {
HandlerAbstract.getHandler().sendPacketSilently(player, packetToSend);
}
}
player.setSendingPackets(false);
}
} else {
switch (event.getType()) {
case ENTITY:
case ENTITY_DESTROY:
case ENTITY_HEAD_ROTATION:
case ENTITY_MOVE:
case ENTITY_MOVELOOK:
case ENTITY_LOOK:
case BLOCK_CHANGE:
case MULTI_BLOCK_CHANGE:
case MAP_CHUNK: {
if(player.getLagInfo().getLastClientTransaction().isPassed(200L) && player.getCreation().isPassed(6000L)) {
synchronized (player.getPacketQueue()) {
player.getPacketQueue().add(event.getPacket());
}
event.setCancelled(true);
}
break;
}
}
}
});
}
@EventHandler
public void onJoin(PlayerJoinEvent event) {
APlayer player = Anticheat.INSTANCE.getPlayerRegistry().generate(event.getPlayer());
RunUtils.taskTimer(task -> {
if(Anticheat.INSTANCE.getPlayerRegistry().aplayerMap.containsKey(event.getPlayer().getUniqueId().hashCode())) {
if(task != null
&& Anticheat.INSTANCE.getPlayerRegistry().aplayerMap
.containsKey(event.getPlayer().getUniqueId().hashCode())
&& event.getPlayer() != null && event.getPlayer().isOnline()) {
HandlerAbstract.getHandler().add(event.getPlayer());
task.cancel();
}
}
}, 6, 1);
player.getCheckHandler().callEvent(event);
}
@EventHandler
public void onQuit(PlayerQuitEvent event) {
HandlerAbstract.getHandler().remove(event.getPlayer());
Anticheat.INSTANCE.getPlayerRegistry().unregister(event.getPlayer().getUniqueId());
}
@EventHandler
public void onQuit(PlayerKickEvent event) {
Anticheat.INSTANCE.getPlayerRegistry().unregister(event.getPlayer().getUniqueId());
}
}
@@ -0,0 +1,33 @@
package dev.brighten.ac.logging;
import lombok.Builder;
import lombok.Getter;
import dev.brighten.ac.utils.json.JSONException;
import dev.brighten.ac.utils.json.JSONObject;
import java.util.UUID;
@Getter
@Builder
public class Log {
public UUID uuid;
private float vl;
private long time;
private String data;
private String checkId;
public String toJson() {
JSONObject object = new JSONObject();
try {
object.put("uuid", uuid.toString());
object.put("vl", vl);
object.put("time", time);
object.put("data", data);
object.put("checkId", checkId);
return object.toString();
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,232 @@
package dev.brighten.ac.logging;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketAdapter;
import com.neovisionaries.ws.client.WebSocketException;
import com.neovisionaries.ws.client.WebSocketFactory;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.check.CheckData;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.utils.json.JSONObject;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
public class LoggerManager {
private final Queue<Log> logList = new ConcurrentLinkedQueue<>();
private String license;
/*
* Structure of Log
* UUID hashcode (INT32),
*/
public void init() {
// Starting up H2
license = Anticheat.INSTANCE.getPluginInstance().getConfig().getString("license");
AtomicLong lastWrite = new AtomicLong();
Anticheat.INSTANCE.getScheduler().scheduleAtFixedRate(() -> {
long now = System.currentTimeMillis();
if(logList.size() > 0 && (now - lastWrite.get() > 10000L || logList.size() > 600)) {
try {
WebSocket socket = new WebSocketFactory().createSocket("ws://logs.funkemunky.cc/logsocket").connect();
System.out.println("Writing logs");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeUTF("LOG_WRITE");
oos.writeUTF(license());
int i = 0;
while(logList.size() > 0 && i++ < 400) {
Log log = logList.poll();
if(log != null) {
oos.writeUTF(log.toJson());
} else if(logList.size() == 0) {
break;
}
}
System.out.println("Wrote " + i + " logs;" + logList.size());
lastWrite.set(now);
oos.close();
socket.sendBinary(baos.toByteArray());
baos.close();
socket.disconnect();
} catch (IOException | WebSocketException e) {
e.printStackTrace();
}
}
}, 200, 200, TimeUnit.MILLISECONDS);
}
private String license() {
return license;
}
public void insertLog(APlayer player, CheckData checkData, float vl, long time, String data) {
logList.add(Log.builder()
.uuid(player.getUuid())
.checkId(checkData.checkId())
.vl(vl)
.data(data)
.time(time)
.build());
}
public void getLogs(UUID uuid, Consumer<List<Log>> logConsumer) {
getLogs(uuid, 2000, 0, logConsumer);
}
public void getLogs(UUID uuid, int limit, int skip, Consumer<List<Log>> logsConsumer) {
Anticheat.INSTANCE.getScheduler().execute(() -> {
try {
WebSocket socket = createSocket(logsConsumer).connect();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeUTF("LOG_REQ_UUID");
oos.writeUTF(license());
oos.writeUTF(uuid.toString());
oos.writeInt(skip);
oos.writeInt(limit);
oos.close();
socket.sendBinary(baos.toByteArray());
} catch(WebSocketException e) {
e.printStackTrace();
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
public void getLogs(UUID uuid, String checkId, Consumer<List<Log>> logConsumer) {
getLogs(uuid, checkId, 500, 0, logConsumer);
}
public void getLogs(UUID uuid, String checkId, int limit, int skip, Consumer<List<Log>> logsConsumer) {
Anticheat.INSTANCE.getScheduler().execute(() -> {
try {
WebSocket socket = createSocket(logsConsumer).connect();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeUTF("LOG_REQ_UUID_CHECK");
oos.writeUTF(license());
oos.writeUTF(uuid.toString());
oos.writeUTF(checkId);
oos.writeInt(skip);
oos.writeInt(limit);
oos.close();
socket.sendBinary(baos.toByteArray());
} catch(WebSocketException e) {
e.printStackTrace();
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
public void getLogs(UUID uuid, String checkId, long timeBefore, long timeAfter,
Consumer<List<Log>> logsConsumer) {
getLogs(uuid, checkId, timeBefore, timeAfter, 500, 0, logsConsumer);
}
public void getLogs(UUID uuid, String checkId, long timeBefore, long timeAfter, int limit, int skip,
Consumer<List<Log>> logsConsumer) {
Anticheat.INSTANCE.getScheduler().execute(() -> {
try {
WebSocket socket = createSocket(logsConsumer).connect();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeUTF("LOG_REQ_UUID_CHECK_TIME");
oos.writeUTF(license());
oos.writeUTF(checkId);
oos.writeLong(timeBefore);
oos.writeLong(timeAfter);
oos.writeInt(skip);
oos.writeInt(limit);
oos.writeUTF(uuid.toString());
oos.close();
socket.sendBinary(baos.toByteArray());
} catch(WebSocketException e) {
e.printStackTrace();
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
public void getRecentLogs(int limit, Consumer<List<Log>> logsConsumer) {
Anticheat.INSTANCE.getScheduler().execute(() -> {
try {
WebSocket socket = createSocket(logsConsumer).connect();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeUTF("LOG_REQ_UUID_CHECK");
oos.writeUTF(license());
oos.writeInt(limit);
oos.close();
socket.sendBinary(baos.toByteArray());
} catch (WebSocketException | IOException e) {
throw new RuntimeException(e);
}
});
}
private WebSocket createSocket(Consumer<List<Log>> logsConsumer) throws IOException {
return new WebSocketFactory().createSocket("ws://logs.funkemunky.cc/logsocket")
.addListener(new WebSocketAdapter() {
@Override
public void onBinaryMessage(WebSocket websocket, byte[] data) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(data);
ObjectInputStream ois = new ObjectInputStream(bais);
System.out.println("Received!: " + ois.available());
List<Log> logs = new ArrayList<>();
while(ois.available() > 0) {
String logString = ois.readUTF();
JSONObject logObject = new JSONObject(logString);
logs.add(Log.builder()
.vl((float)logObject.getDouble("vl"))
.checkId(logObject.getString("checkId"))
.data(logObject.getString("data"))
.time(logObject.getLong("time"))
.uuid(UUID.fromString(logObject.getString("uuid"))).build());
}
logsConsumer.accept(logs);
websocket.disconnect();
}
});
}
public void shutDown() {
}
}
@@ -0,0 +1,14 @@
package dev.brighten.ac.messages;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ComponentBuilder;
public class Messages {
public static BaseComponent[] NULL_APLAYER
= new ComponentBuilder("Could not read player data!").color(ChatColor.RED).create();
public static BaseComponent[] ALERTS_ON = new ComponentBuilder("Enabled your anticheat alerts.")
.color(ChatColor.GREEN).create();
public static BaseComponent[] ALERTS_OFF = new ComponentBuilder("Disabled your anticheat alerts.")
.color(ChatColor.RED).create();
}
@@ -0,0 +1,143 @@
/*
* Copyright (c) 2018 NGXDEV.COM. Licensed under MIT.
*/
package dev.brighten.ac.packet;
import dev.brighten.ac.utils.reflections.Reflections;
import dev.brighten.ac.utils.reflections.types.WrappedClass;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.bukkit.Bukkit;
import java.util.logging.Level;
//Protocol Version numbers: https://wiki.vg/Protocol_version_numbers
@Getter
@AllArgsConstructor
public enum ProtocolVersion {
V1_7(4, "v1_7_R3"),
V1_7_10(5, "v1_7_R4"),
V1_8(45, "v1_8_R1"),
V1_8_5(46, "v1_8_R2"),
V1_8_9(47, "v1_8_R3"),
V1_9(107, "v1_9_R1"),
V1_9_1(108, "v1_9_R1"),
V1_9_2(109, "v1_9_R2"),
V1_9_4(110, "v1_9_R2"),
V1_10(210, "v1_10_R1"),
V1_10_2(210, "v1_10_R1"),
V1_11(316, "v1_11_R1"),
V1_12(335, "v1_12_R1"),
V1_12_1(338, null),
V1_12_2(340, "v1_12_R1"),
V1_13(350, "v1_13_R1"),
V1_13_1(351, "v1_13_R2"),
V1_13_2(352, "v1_13_R2"),
V1_14(477, "v1_14_R1"),
V1_14_1(480, "v1_14_R1"),
V1_14_2(485, "v1_14_R1"),
V1_14_3(490, "v1_14_R1"),
V1_14_4(498, "v1_14_R1"),
V1_15(573, "v1_15_R1"),
V1_15_1(575, "v1_15_R1"),
V1_15_2(578, "v1_15_R1"),
V1_16(735, "v1_16_R1"),
V1_16_1(736, "v1_16_R1"),
V1_16_2(751, "v1_16_R2"),
V1_16_3(753, "v1_16_R2"),
V1_16_4(754, "v1_16_R3"),
V1_16_5(754, "v1_16_R3"),
V1_17(755, "v1_17_R1"),
V1_17_1(756, "v1_17_R1"),
V1_18(757, "v1_18_R1"),
V1_18_2(758, "v1_18_R2"),
V1_19(759, "v1_19_R1"),
v1_19_1(760, "v1_19_R1"),
UNKNOWN(-1, "UNKNOWN");
@Getter
private static final ProtocolVersion gameVersion = fetchGameVersion();
private final int version;
@Getter
private static boolean paper;
private final String serverVersion;
private static ProtocolVersion fetchGameVersion() {
ProtocolVersion toReturn = UNKNOWN;
for (ProtocolVersion version : values()) {
if (version.getServerVersion() != null && version.getServerVersion().equals(Reflections.VERSION)) {
toReturn = version;
break;
}
}
if(toReturn.isOrAbove(ProtocolVersion.V1_17)) {
WrappedClass mv = Reflections.getNMSClass("MinecraftVersion");
Object mvObject = mv.getFieldByName("a").get(null);
String version = mv.getFieldByType(String.class, 1).get(mvObject);
switch(version) {
case "1.19": {
toReturn = V1_19;
break;
}
case "1.18.2": {
toReturn = V1_18_2;
break;
}
case "1.18.1":
case "1.18": {
toReturn = V1_18;
Bukkit.getLogger().log(Level.INFO, "Version is 1.18");
break;
}
case "1.17.1": {
toReturn = V1_17_1;
break;
}
default: {
toReturn = V1_17;
break;
}
}
}
return toReturn;
}
public static ProtocolVersion getVersion(int versionId) {
for (ProtocolVersion version : values()) {
if (version.getVersion() == versionId) return version;
}
return UNKNOWN;
}
public boolean isBelow(ProtocolVersion version) {
return this.getVersion() < version.getVersion();
}
public boolean isOrBelow(ProtocolVersion version) {
return this.getVersion() <= version.getVersion();
}
public boolean isAbove(ProtocolVersion version) {
return this.getVersion() > version.getVersion();
}
public boolean isOrAbove(ProtocolVersion version) {
return this.getVersion() >= version.getVersion();
}
static {
try {
Class.forName("org.github.paperspigot.PaperSpigotConfig");
paper = true;
} catch(Exception e) {
paper = false;
}
}
}
@@ -0,0 +1,58 @@
package dev.brighten.ac.packet.handler;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.packet.wrapper.PacketType;
import dev.brighten.ac.utils.reflections.Reflections;
import dev.brighten.ac.utils.reflections.types.WrappedClass;
import dev.brighten.ac.utils.reflections.types.WrappedField;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public abstract class HandlerAbstract{
static WrappedClass classNetworkManager = Reflections.getNMSClass("NetworkManager");
static WrappedField
fieldNetworkManager = Reflections.getNMSClass("PlayerConnection").getFieldByName("networkManager"),
fieldPlayerConnection = Reflections.getNMSClass("EntityPlayer").getFieldByName("playerConnection");
static String handlerName = "brighten-ac-packets";
@Getter
private static HandlerAbstract handler;
public static void init() {
if(ProtocolVersion.getGameVersion().isOrAbove(ProtocolVersion.V1_8)) {
handler = new ModernHandler();
} else handler = new LegacyHandler();
Bukkit.getOnlinePlayers().forEach(handler::add);
}
public abstract void add(Player player);
public abstract void remove(Player player);
public abstract void sendPacketSilently(Player player, Object packet);
public abstract void sendPacketSilently(APlayer player, Object packet);
public abstract void sendPacket(Player player, Object packet);
public abstract void sendPacket(APlayer player, Object packet);
public static PacketType getPacketType(Object object) {
String name = object.getClass().getName();
int index = name.lastIndexOf(".");
String packetName = name.substring(index + 1);
return PacketType
.getByPacketId(packetName).orElse(PacketType.UNKNOWN);
}
public void shutdown() {
handler.shutdown();
handler = null;
}
public abstract int getProtocolVersion(Player player);
}
@@ -0,0 +1,135 @@
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.packet.wrapper.WPacket;
import dev.brighten.ac.utils.reflections.types.WrappedClass;
import dev.brighten.ac.utils.reflections.types.WrappedField;
import lombok.AllArgsConstructor;
import net.minecraft.server.v1_7_R4.NetworkManager;
import net.minecraft.server.v1_7_R4.PlayerConnection;
import net.minecraft.util.com.google.common.collect.MapMaker;
import net.minecraft.util.io.netty.channel.Channel;
import net.minecraft.util.io.netty.channel.ChannelDuplexHandler;
import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
import net.minecraft.util.io.netty.channel.ChannelPromise;
import org.bukkit.craftbukkit.v1_7_R4.entity.CraftPlayer;
import org.bukkit.entity.Player;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
//TODO Make this feature-parody with ModernHandler
public class LegacyHandler extends HandlerAbstract {
private final Map<String, Channel> channelCache = new HashMap<>();
private final Set<Channel> uninjectedChannels = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap());
private final static WrappedField fieldChannel = new WrappedClass(NetworkManager.class)
.getFieldByType(Channel.class, 0);
@Override
public void add(Player player) {
try {
Channel channel = getChannel(player);
PacketHandler handler = (PacketHandler) channel.pipeline().get(handlerName);
if(handler == null) {
handler = new PacketHandler(player);
ChannelHandlerContext context = channel.pipeline().context("packet_handler");
if(context != null) {
channel.pipeline().addBefore("packet_handler", handlerName, handler);
uninjectedChannels.remove(channel);
} else uninjectedChannels.add(channel);
}
} catch(IllegalArgumentException e) {
e.printStackTrace();
}
}
@Override
public void remove(Player player) {
Channel channel = getChannel(player);
if(Anticheat.INSTANCE.isEnabled()) {
uninjectedChannels.add(channel);
}
channel.eventLoop().execute(() -> channel.pipeline().remove(handlerName));
}
@Override
public void sendPacketSilently(Player player, Object packet) {
if(packet instanceof WPacket) {
getChannel(player).pipeline().writeAndFlush(((WPacket) packet).getPacket());
} else getChannel(player).pipeline().writeAndFlush(packet);
}
@Override
public void sendPacketSilently(APlayer player, Object packet) {
this.sendPacketSilently(player.getBukkitPlayer(), packet);
}
@Override
public void sendPacket(Player player, Object packet) {
if(packet instanceof WPacket) {
getChannel(player).pipeline().writeAndFlush(((WPacket) packet).getPacket());
} else getChannel(player).pipeline().writeAndFlush(packet);
}
@Override
public void sendPacket(APlayer player, Object packet) {
sendPacket(player.getBukkitPlayer(), packet);
}
@Override
public int getProtocolVersion(Player player) {
return -1;
}
private Channel getChannel(Player player) {
synchronized (channelCache) {
return channelCache.computeIfAbsent(player.getName(), name -> {
PlayerConnection connection = ((CraftPlayer)player).getHandle().playerConnection;
return fieldChannel.get(connection.networkManager);
});
}
}
@AllArgsConstructor
private static class PacketHandler extends ChannelDuplexHandler {
private Player player;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String name = msg.getClass().getName();
int index = name.lastIndexOf(".");
String packetName = name.substring(index + 1);
Object packet = Anticheat.INSTANCE.getPacketProcessor().call(player, msg, PacketType
.getByPacketId(packetName).orElse(PacketType.UNKNOWN));
if(packet != null) {
super.channelRead(ctx, packet);
}
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
String name = msg.getClass().getName();
int index = name.lastIndexOf(".");
String packetName = name.substring(index + 1);
Object packet = Anticheat.INSTANCE.getPacketProcessor().call(player, msg, PacketType
.getByPacketId(packetName).orElse(PacketType.UNKNOWN));
if(packet != null) {
super.write(ctx, packet, promise);
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More