mirror of
https://github.com/funkemunky/KauriV3.git
synced 2026-06-25 07:50:38 +00:00
Restructuring Project
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
+44
@@ -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);
|
||||
};
|
||||
}
|
||||
+34
@@ -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);
|
||||
};
|
||||
}
|
||||
+85
@@ -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;
|
||||
};
|
||||
}
|
||||
+68
@@ -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
|
||||
}
|
||||
+35
@@ -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);
|
||||
}
|
||||
}
|
||||
+13
@@ -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();
|
||||
}
|
||||
}
|
||||
+13
@@ -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
Reference in New Issue
Block a user