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