content: package antip2w.tools.module;
  
  import antip2w.tools.util.MCUtil;
  import antip2w.tools.util.Util;
  import meteordevelopment.meteorclient.events.packets.PacketEvent;
  import meteordevelopment.meteorclient.events.world.TickEvent;
  import meteordevelopment.meteorclient.settings.BoolSetting;
  import meteordevelopment.meteorclient.settings.Setting;
  import meteordevelopment.meteorclient.settings.SettingGroup;
  import meteordevelopment.orbit.EventHandler;
  import meteordevelopment.orbit.EventPriority;
  import net.minecraft.entity.Entity;
  import net.minecraft.entity.EntityStatuses;
  import net.minecraft.entity.LivingEntity;
  import net.minecraft.entity.mob.GuardianEntity;
  import net.minecraft.entity.passive.SnifferEntity;
  import net.minecraft.entity.player.PlayerEntity;
  import net.minecraft.entity.player.PlayerPosition;
  import net.minecraft.network.packet.Packet;
  import net.minecraft.network.packet.s2c.play.*;
  import net.minecraft.util.math.Vec3d;
  
  import java.lang.reflect.Method;
  import java.lang.reflect.Parameter;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.concurrent.atomic.AtomicInteger;
  import java.util.function.Predicate;
  
  import static net.minecraft.network.packet.s2c.play.EntityAnimationS2CPacket.*;
  
  @SuppressWarnings("unused")
  public class AntiExploit extends BetterModule {
  
      private final SettingGroup defaultGroup = settings.getDefaultGroup();
  
      private final Setting<Boolean> chatMessage = defaultGroup.add(new BoolSetting.Builder()
          .name("chat-message")
          .description("Prints a message to chat upon receiving an exploit packet.")
          .defaultValue(false)
          .build()
      );
  
      @SuppressWarnings("unchecked")
      private static final Map<Class<? extends Packet<?>>, Predicate<Packet<?>>> PREDICATES = Util.make(new HashMap<>(), map -> {
          Method[] methods = AntiExploit.class.getDeclaredMethods();
  
          for (Method method : methods) {
              Parameter[] parameters = method.getParameters();
              if (method.getGenericReturnType() != boolean.class ||
                  parameters.length != 1 ||
                  !Packet.class.isAssignableFrom(parameters[0].getType())
              ) {
                  continue;
              }
  
              Predicate<Packet<?>> predicate = packet -> {
                  try {
                      return (boolean) method.invoke(null, packet);
                  } catch (Exception ignored) {
                      return false;
                  }
              };
  
              map.put((Class<? extends Packet<?>>) method.getParameters()[0].getType(), predicate);
          }
      });
  
      private static final AtomicInteger exploitPacketsLastTick = new AtomicInteger();
  
      public AntiExploit() {
          super("anti-exploit", "Attempts to block packets that mess up the game.");
      }
  
      @EventHandler(priority = EventPriority.HIGHEST + 1)
      private void onPacketReceive(PacketEvent.Receive e) {
          Predicate<Packet<?>> invalidPredicate = PREDICATES.get(e.packet.getClass());
  
          if (invalidPredicate != null && invalidPredicate.test(e.packet)) {
              exploitPacketsLastTick.getAndIncrement();
              e.cancel();
          }
      }
  
      @EventHandler
      private void postTick(TickEvent.Post event) {
          int i = exploitPacketsLastTick.getAndSet(0);
  
          if (i <= 0) {
              return;
          }
  
          if (chatMessage.get()) {
              warning(i == 1 ? "Canceled an exploit packet." : ("Canceled " + i + " exploit packets."));
          }
      }
  
      private static boolean isPositionInvalid(double x, double y, double z) {
          return x > 31_000_000 ||
              y > 31_000_000 ||
              z > 31_000_000 ||
              x < -31_000_000 ||
              y < -31_000_000 ||
              z < -31_000_000;
      }
  
      private static boolean isPositionInvalid(Vec3d position) {
          return isPositionInvalid(position.x, position.y, position.z);
      }
  
      private static boolean isVelocityInvalid(double x, double y, double z) {
          return x > 31_000_000 ||
              y > 31_000_000 ||
              z > 31_000_000 ||
              x < -31_000_000 ||
              y < -31_000_000 ||
              z < -31_000_000;
      }
  
      private static boolean isVelocityInvalid(Vec3d velocity) {
          return isPositionInvalid(velocity.x, velocity.y, velocity.z);
      }
  
      // These methods are stored in AntiExploit.PREDICATES using some reflection magic
  
      private static boolean isInvalid(ExplosionS2CPacket p) {
          return isPositionInvalid(p.center()) ||
              p.playerKnockback().map(AntiExploit::isVelocityInvalid).orElse(false);
      }
  
      private static boolean isInvalid(ParticleS2CPacket p) {
          return p.getCount() > 100_000 ||
              p.getSpeed() > 100_000;
      }
  
      private static boolean isInvalid(PlayerPositionLookS2CPacket p) {
          PlayerPosition change = p.change();
  
          return isPositionInvalid(change.position()) ||
              isPositionInvalid(change.deltaMovement()) ||
              Double.isNaN(change.yaw()) ||
              Double.isNaN(change.pitch());
      }
  
      private static boolean isInvalid(EntityVelocityUpdateS2CPacket p) {
          return isVelocityInvalid(p.getVelocityX(), p.getVelocityY(), p.getVelocityZ());
      }
  
      private static boolean isInvalid(InventoryS2CPacket packet) {
          if (packet.getSyncId() == 0) {
              return packet.getContents().size() > MC.player.playerScreenHandler.slots.size();
          }
  
          return MC.player.currentScreenHandler == null ||
              packet.getContents().size() > MC.player.currentScreenHandler.slots.size() + MC.player.playerScreenHandler.slots.size();
      }
  
      private static boolean isInvalid(ScreenHandlerSlotUpdateS2CPacket packet) {
          if (packet.getSyncId() == 0) {
              return packet.getSlot() > MC.player.playerScreenHandler.slots.size();
          }
  
          return MC.player.currentScreenHandler == null ||
              packet.getSlot() > MC.player.currentScreenHandler.slots.size() + MC.player.playerScreenHandler.slots.size();
      }
  
      private static boolean isInvalid(GameStateChangeS2CPacket packet) {
          return packet.getReason() == GameStateChangeS2CPacket.DEMO_MESSAGE_SHOWN;
      }
  
      private static boolean isInvalid(WorldBorderInitializeS2CPacket packet) {
          return isSizeLerpTargetInvalid(packet.getSizeLerpTarget());
      }
  
      private static boolean isInvalid(WorldBorderInterpolateSizeS2CPacket packet) {
          return isSizeLerpTargetInvalid(packet.getSizeLerpTarget());
      }
  
      private static boolean isInvalid(WorldBorderSizeChangedS2CPacket packet) {
          return isSizeLerpTargetInvalid(packet.getSizeLerpTarget());
      }
  
      private static boolean isInvalid(ItemPickupAnimationS2CPacket packet) {
          return !(MCUtil.getPlayNetHandler().getWorld().getEntityById(packet.getCollectorEntityId()) instanceof LivingEntity);
      }
  
      private static boolean isSizeLerpTargetInvalid(double value) {
          return value < 0 || value > 60_000_000;
      }
  
      private static boolean isInvalid(EntityStatusS2CPacket packet) {
          if (packet.getStatus() == EntityStatuses.PLAY_GUARDIAN_ATTACK_SOUND &&
              !(packet.getEntity(MC.world) instanceof GuardianEntity)
          ) {
              return true;
          }
  
          return packet.getStatus() == EntityStatuses.START_DIGGING &&
              !(packet.getEntity(MC.world) instanceof SnifferEntity);
      }
  
      private static boolean isInvalid(EntityAnimationS2CPacket packet) {
          Entity entity = MCUtil.getPlayNetHandler().getWorld().getEntityById(packet.getEntityId());
          return switch (packet.getAnimationId()) {
              case SWING_MAIN_HAND, SWING_OFF_HAND -> !(entity instanceof LivingEntity);
              case WAKE_UP -> !(entity instanceof PlayerEntity);
              default -> false;
          };
      }
  
  }
  