content: package anticope.rejects.modules;
  
  import anticope.rejects.MeteorRejectsAddon;
  import anticope.rejects.events.TeleportParticleEvent;
  import meteordevelopment.meteorclient.events.entity.player.FinishUsingItemEvent;
  import meteordevelopment.meteorclient.events.packets.PacketEvent;
  import meteordevelopment.meteorclient.events.render.Render3DEvent;
  import meteordevelopment.meteorclient.events.world.TickEvent;
  import meteordevelopment.meteorclient.settings.*;
  import meteordevelopment.meteorclient.systems.modules.Module;
  import meteordevelopment.meteorclient.utils.Utils;
  import meteordevelopment.meteorclient.utils.misc.Keybind;
  import meteordevelopment.meteorclient.utils.render.RenderUtils;
  import meteordevelopment.meteorclient.utils.render.color.SettingColor;
  import meteordevelopment.orbit.EventHandler;
  import net.minecraft.item.Items;
  import net.minecraft.network.packet.c2s.play.TeleportConfirmC2SPacket;
  import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket;
  import net.minecraft.util.math.Vec3d;
  
  import java.util.LinkedList;
  import java.util.Queue;
  
  public class ChorusExploit extends Module {
      private final SettingGroup sgGeneral = settings.getDefaultGroup();
      private final SettingGroup sgRender = settings.createGroup("Render");
  
      private final Setting<PositionMode> positionMode = sgGeneral.add(new EnumSetting.Builder<PositionMode>()
          .name("position-mode")
          .description("How your teleport position is calculated.")
          .defaultValue(PositionMode.Particle)
          .build()
      );
  
      private final Setting<Boolean> onItemSwitch = sgGeneral.add(new BoolSetting.Builder()
          .name("tp-on-switch")
          .description("Teleports you when you switch items.")
          .defaultValue(true)
          .build()
      );
  
      private final Setting<Boolean> onDeactivate = sgGeneral.add(new BoolSetting.Builder()
          .name("tp-on-deactivate")
          .description("Teleports you when the module is deactivated.")
          .defaultValue(false)
          .build()
      );
  
      private final Setting<Keybind> onKey = sgGeneral.add(new KeybindSetting.Builder()
          .name("on-key")
          .description("Teleports when a key is pressed.")
          .defaultValue(Keybind.none())
          .action(this::sendPackets)
          .build()
      );
  
      private final Setting<Boolean> autoTeleport = sgGeneral.add(new BoolSetting.Builder()
          .name("automatically-teleport")
          .description("Automatically teleports you after a fixed number of ticks.")
          .defaultValue(false)
          .build()
      );
  
      private final Setting<Integer> ticksToTeleport = sgGeneral.add(new IntSetting.Builder()
          .name("ticks-to-teleport")
          .description("The amount of ticks to wait before automatically teleporting.")
          .defaultValue(40)
          .min(0)
          .sliderMax(100)
          .visible(autoTeleport::get)
          .build()
      );
  
      //render
      private final Setting<Boolean> renderActual = sgRender.add(new BoolSetting.Builder()
          .name("set-position")
          .description("Sets you clientside to your actual position.")
          .defaultValue(true)
          .build()
      );
  
      private final Setting<Boolean> fakeplayerOnDestination = sgRender.add(new BoolSetting.Builder()
          .name("fakeplayer-on-destination")
          .description("Creates a fakeplayer at the destination.")
          .defaultValue(true)
          .build()
      );
  
      private final Setting<Boolean> drawLine = sgRender.add(new BoolSetting.Builder()
          .name("draw-line")
          .description("Draws a line to where you are going to be.")
          .defaultValue(true)
          .build()
      );
  
      private final Setting<SettingColor> lineColour = sgRender.add(new ColorSetting.Builder()
          .name("line-color")
          .description("The lines color.")
          .defaultValue(new SettingColor(205, 205, 205, 127))
          .visible(drawLine::get)
          .build()
      );
  
      private int slot;
      private int delay = 0;
      private boolean ateChorus, sending, gotPosition = false;
      private double posX, posY, posZ, cposX, cposY, cposZ;
      private final Queue<TeleportConfirmC2SPacket> telePackets = new LinkedList<>();
  
  
      public ChorusExploit() {
          super(MeteorRejectsAddon.CATEGORY, "chorus-exploit", "Delays teleporting with a chorus fruit.");
      }
  
      @Override
      public void onActivate() {
          ateChorus = false;
          delay = 0;
          telePackets.clear();
          gotPosition = false;
      }
  
      @Override
      public void onDeactivate() {
          if (Utils.canUpdate() && ateChorus && onDeactivate.get()) {
              sendPackets();
          }
          telePackets.clear();
          gotPosition = false;
      }
  
      @EventHandler
      private void onPacketSend(PacketEvent.Send event) {
          if (event.packet instanceof TeleportConfirmC2SPacket telepacket && ateChorus && !sending) {
              telePackets.add(telepacket);
              event.cancel();
          }
      }
  
      @EventHandler
      private void onPacketRecieve(PacketEvent.Receive event) {
          if (event.packet instanceof PlayerPositionLookS2CPacket posPacket && ateChorus) {
              event.setCancelled(true);
              if (positionMode.get() == PositionMode.PosLook) {
                  Vec3d pos = posPacket.change().position();
                  cposX = pos.x;
                  cposY = pos.y;
                  cposZ = pos.z;
                  gotPosition = true;
              }
          }
      }
  
      @EventHandler
      private void onTick(TickEvent.Pre event) {
          if (ateChorus) {
              delay++;
              if (!mc.player.getPos().equals(new Vec3d(posX, posY, posZ)) && renderActual.get()) {
                  mc.player.setPos(posX, posY, posZ);
              }
  
              if (autoTeleport.get() && delay >= ticksToTeleport.get()) {
                  sendPackets();
              }
  
              if (onItemSwitch.get() && slot != mc.player.getInventory().selectedSlot) {
                  sendPackets();
              }
          }
      }
  
      @EventHandler
      private void onEat(FinishUsingItemEvent event) {
          if (event.itemStack.getItem().equals(Items.CHORUS_FRUIT)) {
              posX = mc.player.getX();
              posY = mc.player.getY();
              posZ = mc.player.getZ();
              ateChorus = true;
              slot = mc.player.getInventory().selectedSlot;
          }
      }
  
      @EventHandler
      private void onRender3D(Render3DEvent event) {
          if (drawLine.get() && ateChorus && gotPosition) {
              event.renderer.line(RenderUtils.center.x, RenderUtils.center.y, RenderUtils.center.z, cposX, cposY + 1, cposZ, lineColour.get());
          }
      }
  
      @EventHandler
      private void onTeleportParticle(TeleportParticleEvent event) {
          if (ateChorus && positionMode.get() == PositionMode.Particle) {
              cposX = event.x;
              cposY = event.y;
              cposZ = event.z;
              gotPosition = true;
          }
      }
  
      private void sendPackets() {
          sending = true;
  
          while (!telePackets.isEmpty()) {
              mc.getNetworkHandler().sendPacket(telePackets.poll());
          }
  
          delay = 0;
          ateChorus = false;
          sending = false;
          gotPosition = false;
      }
  
      @Override
      public String getInfoString() {
          if (autoTeleport.get() && ateChorus) return String.valueOf(ticksToTeleport.get() - delay);
          return null;
      }
  
      public enum PositionMode {
          Particle,
          PosLook,
          None
      }
  }