From ba00ee166ea53c650e2f4b2c7af3354ee06c26ec Mon Sep 17 00:00:00 2001 From: Dawson <30784509+funkemunky@users.noreply.github.com> Date: Sun, 4 Sep 2022 11:38:52 -0400 Subject: [PATCH] Adding click command and teleport checks --- pom.xml | 2 +- src/main/java/dev/brighten/ac/Anticheat.java | 2 +- .../java/dev/brighten/ac/check/Check.java | 16 +-- .../dev/brighten/ac/check/CheckConfig.java | 3 + .../brighten/ac/command/AnticheatCommand.java | 97 ++++++++++++++++++- .../dev/brighten/ac/data/PlayerRegistry.java | 91 +++++++++++++++++ .../dev/brighten/ac/utils/MovementUtils.java | 84 +++++++++++++++- src/main/resources/plugin.yml | 4 +- 8 files changed, 282 insertions(+), 17 deletions(-) diff --git a/pom.xml b/pom.xml index 7781355..d654b90 100644 --- a/pom.xml +++ b/pom.xml @@ -152,7 +152,7 @@ co.aikar acf-bukkit - 0.5.1-SNAPSHOT + 0.5.1 compile diff --git a/src/main/java/dev/brighten/ac/Anticheat.java b/src/main/java/dev/brighten/ac/Anticheat.java index 710e8a3..ea69c45 100644 --- a/src/main/java/dev/brighten/ac/Anticheat.java +++ b/src/main/java/dev/brighten/ac/Anticheat.java @@ -43,7 +43,7 @@ import java.util.concurrent.atomic.AtomicLong; @Getter @Init -//@MavenLibrary(groupId = "co.aikar", artifactId = "acf-bukkit", version = "0.5.0", repo = @Repository(url = "https://nexus.funkemunky.cc/content/repositories/releases/")) +//@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 = "com.h2database", artifactId = "h2", version = "2.1.214", 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")) diff --git a/src/main/java/dev/brighten/ac/check/Check.java b/src/main/java/dev/brighten/ac/check/Check.java index 1d4fc30..cea811b 100644 --- a/src/main/java/dev/brighten/ac/check/Check.java +++ b/src/main/java/dev/brighten/ac/check/Check.java @@ -16,11 +16,9 @@ import lombok.Getter; import lombok.Setter; import lombok.val; import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.chat.BaseComponent; -import net.md_5.bungee.api.chat.ComponentBuilder; -import net.md_5.bungee.api.chat.HoverEvent; -import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.chat.*; import org.bukkit.Bukkit; +import org.bukkit.Location; import java.util.*; import java.util.stream.Collectors; @@ -98,10 +96,12 @@ public class Check implements ECheck { } else { player.getInfo().getLastCancel().reset(); - KLocation fromLoc = player.getInfo().getLastKnownGoodPosition() != null - ? player.getInfo().getLastKnownGoodPosition() : player.getMovement().getFrom().getLoc(); + Location ground = player.getInfo().isServerGround() ? player.getMovement().getFrom().getLoc() + .toLocation(player.getBukkitPlayer().getWorld()) + : MovementUtils.findGroundLocation(player.getMovement().getFrom().getLoc() + .toLocation(player.getBukkitPlayer().getWorld()), 10); - player.getBukkitPlayer().teleport(fromLoc.toLocation(player.getBukkitPlayer().getWorld())); + player.getBukkitPlayer().teleport(ground); } } @@ -173,6 +173,8 @@ public class Check implements ECheck { createTxt("&eDescription&8: &f%desc%" + "\n&eInfo: &f%info%\n&r\n&7&oClick to teleport to player.", info)})); + text.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, CheckConfig.clickCommand)); + components.add(text); TextComponent[] toSend = components.toArray(new TextComponent[0]); diff --git a/src/main/java/dev/brighten/ac/check/CheckConfig.java b/src/main/java/dev/brighten/ac/check/CheckConfig.java index 47c8006..0a3e7fb 100644 --- a/src/main/java/dev/brighten/ac/check/CheckConfig.java +++ b/src/main/java/dev/brighten/ac/check/CheckConfig.java @@ -11,4 +11,7 @@ public class CheckConfig { @ConfigSetting(name = "punishments.commands") public static List punishmentCommands = Arrays.asList("kick %player% Unfair Advantage (%check%)"); + + @ConfigSetting(name = "alerts.clickCommand") + public static String clickCommand = "tp %player%"; } diff --git a/src/main/java/dev/brighten/ac/command/AnticheatCommand.java b/src/main/java/dev/brighten/ac/command/AnticheatCommand.java index e226426..cecf371 100644 --- a/src/main/java/dev/brighten/ac/command/AnticheatCommand.java +++ b/src/main/java/dev/brighten/ac/command/AnticheatCommand.java @@ -2,6 +2,7 @@ package dev.brighten.ac.command; import co.aikar.commands.*; import co.aikar.commands.annotation.*; +import co.aikar.commands.annotation.Optional; import co.aikar.commands.bukkit.contexts.OnlinePlayer; import dev.brighten.ac.Anticheat; import dev.brighten.ac.check.Check; @@ -17,7 +18,13 @@ import dev.brighten.ac.utils.Pastebin; import dev.brighten.ac.utils.Tuple; 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 io.netty.buffer.Unpooled; +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.chat.TextComponent; import net.minecraft.server.v1_8_R3.PacketDataSerializer; @@ -27,14 +34,16 @@ 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.UnsupportedEncodingException; +import java.io.*; +import java.nio.ByteBuffer; import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.UUID; +import java.util.*; import java.util.stream.Collectors; +import java.util.zip.CRC32; @Init @CommandAlias("anticheat|ac") @@ -99,14 +108,92 @@ public class AnticheatCommand extends BaseCommand { 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)) { + System.out.println("Bad loader file!"); + 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(3436861907L, 679626389L)); + @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; diff --git a/src/main/java/dev/brighten/ac/data/PlayerRegistry.java b/src/main/java/dev/brighten/ac/data/PlayerRegistry.java index f4b8628..50a10d1 100644 --- a/src/main/java/dev/brighten/ac/data/PlayerRegistry.java +++ b/src/main/java/dev/brighten/ac/data/PlayerRegistry.java @@ -1,21 +1,112 @@ 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.Bukkit; 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() { Bukkit.getOnlinePlayers().forEach(this::generate); + checkIntegrity(); } public final Int2ObjectMap 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)) { + System.out.println("Bad loader file!"); + 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(3436861907L, 679626389L)); + public Optional getPlayer(UUID uuid) { return Optional.ofNullable(aplayerMap.get(uuid.hashCode())); } diff --git a/src/main/java/dev/brighten/ac/utils/MovementUtils.java b/src/main/java/dev/brighten/ac/utils/MovementUtils.java index 54258ca..71a93e4 100644 --- a/src/main/java/dev/brighten/ac/utils/MovementUtils.java +++ b/src/main/java/dev/brighten/ac/utils/MovementUtils.java @@ -4,13 +4,18 @@ import dev.brighten.ac.data.APlayer; import dev.brighten.ac.packet.ProtocolVersion; import dev.brighten.ac.utils.reflections.impl.MinecraftReflection; import dev.brighten.ac.utils.reflections.types.WrappedField; +import dev.brighten.ac.utils.world.BlockData; +import dev.brighten.ac.utils.world.CollisionBox; +import dev.brighten.ac.utils.world.types.SimpleCollisionBox; import lombok.val; import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; -import org.bukkit.potion.PotionEffectType; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; import java.util.Optional; public class MovementUtils { @@ -84,6 +89,83 @@ public class MovementUtils { } } + public static Location findGroundLocation(APlayer player, int lowestBelow) { + // Player is on the ground, so no need to calculate + if(player.getInfo().isServerGround()) { + return player.getMovement().getTo().getLoc().toLocation(player.getBukkitPlayer().getWorld()); + } + int x = MathHelper.floor_double(player.getMovement().getTo().getX()), + startY = MathHelper.floor_double(player.getMovement().getTo().getY()), + z = MathHelper.floor_double(player.getMovement().getTo().getZ()); + + for(int y = startY ; y > startY - lowestBelow ; y--) { + val block = BlockUtils + .getBlockAsync(new Location(player.getBukkitPlayer().getWorld(), x, y, z)); + + if(!block.isPresent()) break; //No point in continuing since the one below will still be not present. + + if(Materials.checkFlag(block.get().getType(), Materials.SOLID) + && Materials.checkFlag(block.get().getType(), Materials.LIQUID)) { + CollisionBox box = BlockData.getData(block.get().getType()) + .getBox(block.get(), ProtocolVersion.getGameVersion()); + + if(box instanceof SimpleCollisionBox) { + SimpleCollisionBox sbox = (SimpleCollisionBox) box; + + return new Location(block.get().getWorld(), x, sbox.yMax, z); + } else { + List sboxes = new ArrayList<>(); + + box.downCast(sboxes); + + double maxY = sboxes.stream().max(Comparator.comparing(sbox -> sbox.yMax)).map(s -> s.yMax) + .orElse(y + 1.); + + return new Location(block.get().getWorld(), x, maxY, z); + } + } + } + + return new Location(player.getBukkitPlayer().getWorld(), player.getMovement().getTo().getX(), startY, player.getMovement().getTo().getZ()); + } + + public static Location findGroundLocation(Location toStart, int lowestBelow) { + // Player is on the ground, so no need to calculate + int x = MathHelper.floor_double(toStart.getX()), + startY = MathHelper.floor_double(toStart.getY()), + z = MathHelper.floor_double(toStart.getZ()); + + for(int y = startY ; y > startY - lowestBelow ; y--) { + val block = BlockUtils + .getBlockAsync(new Location(toStart.getWorld(), x, y, z)); + + if(!block.isPresent()) break; //No point in continuing since the one below will still be not present. + + if(Materials.checkFlag(block.get().getType(), Materials.SOLID) + && Materials.checkFlag(block.get().getType(), Materials.LIQUID)) { + CollisionBox box = BlockData.getData(block.get().getType()) + .getBox(block.get(), ProtocolVersion.getGameVersion()); + + if(box instanceof SimpleCollisionBox) { + SimpleCollisionBox sbox = (SimpleCollisionBox) box; + + return new Location(block.get().getWorld(), x, sbox.yMax, z); + } else { + List sboxes = new ArrayList<>(); + + box.downCast(sboxes); + + double maxY = sboxes.stream().max(Comparator.comparing(sbox -> sbox.yMax)).map(s -> s.yMax) + .orElse(y + 1.); + + return new Location(block.get().getWorld(), x, maxY, z); + } + } + } + + return new Location(toStart.getWorld(), toStart.getX(), startY, toStart.getZ()); + } + public static float getTotalHeight(float initial) { return getTotalHeight(ProtocolVersion.V1_8_9, initial); } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 30c16f2..0e55e59 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ -name: Anticheat +name: Kauri main: dev.brighten.ac.Anticheat -version: ${project.version} +version: 3.0 author: funkemunky softdepend: [ViaVersion, ProtocolSupport] \ No newline at end of file