diff --git a/build.gradle b/build.gradle
index 543f802..1cc6d24 100644
--- a/build.gradle
+++ b/build.gradle
@@ -15,6 +15,9 @@ repositories {
     maven {
         url "https://repo.extendedclip.com/content/repositories/placeholderapi/"
     }
+    maven {
+        url "https://repo.codemc.io/repository/maven-snapshots/"
+    }
     maven {
         url "https://jitpack.io/"
     }
@@ -24,8 +27,6 @@ dependencies {
     compileOnly "org.spigotmc:spigot-api:1.19.4-R0.1-SNAPSHOT"
     compileOnly "me.clip:placeholderapi:2.11.1"
 
-    //noinspection GradlePackageUpdate
-    compileOnly "io.netty:netty-all:4.1.87.Final"
     compileOnly "com.mojang:authlib:3.4.40"
     compileOnly "com.mojang:datafixerupper:6.0.8"
 
@@ -35,6 +36,7 @@ dependencies {
     implementation "org.bstats:bstats-bukkit:3.0.2"
     implementation "com.github.robertlit:SpigotResourcesAPI:2.0"
     implementation "net.kyori:adventure-platform-bukkit:4.3.0"
+    implementation "com.github.retrooper.packetevents:spigot:2.0.0-SNAPSHOT"
 }
 
 group "lol.pyr"
@@ -53,6 +55,7 @@ shadowJar {
     relocate "org.checkerframework", "lol.pyr.znpcsplus.lib.checkerframework"
     relocate "javax.annotation", "lol.pyr.znpcsplus.lib.javaxannotation"
     relocate "com.google", "lol.pyr.znpcsplus.lib.google"
+    relocate "com.github.retrooper.packetevents", "lol.pyr.znpcsplus.lib.packetevents"
     minimize()
 }
 
diff --git a/src/main/java/io/github/znetworkw/znpcservers/listeners/PlayerListener.java b/src/main/java/io/github/znetworkw/znpcservers/listeners/PlayerListener.java
index 1133e35..6e2a168 100644
--- a/src/main/java/io/github/znetworkw/znpcservers/listeners/PlayerListener.java
+++ b/src/main/java/io/github/znetworkw/znpcservers/listeners/PlayerListener.java
@@ -1,7 +1,7 @@
 package io.github.znetworkw.znpcservers.listeners;
 
 import io.github.znetworkw.znpcservers.npc.conversation.ConversationModel;
-import io.github.znetworkw.znpcservers.npc.event.NPCInteractEvent;
+import io.github.znetworkw.znpcservers.npc.interaction.NPCInteractEvent;
 import io.github.znetworkw.znpcservers.user.EventService;
 import io.github.znetworkw.znpcservers.user.ZUser;
 import org.bukkit.event.EventHandler;
@@ -12,7 +12,6 @@ import org.bukkit.event.player.PlayerJoinEvent;
 import org.bukkit.event.player.PlayerQuitEvent;
 import org.bukkit.plugin.Plugin;
 
-@SuppressWarnings("deprecation")
 public class PlayerListener implements Listener {
     public PlayerListener(Plugin serversNPC) {
         serversNPC.getServer().getPluginManager().registerEvents(this, serversNPC);
diff --git a/src/main/java/io/github/znetworkw/znpcservers/npc/NPC.java b/src/main/java/io/github/znetworkw/znpcservers/npc/NPC.java
index 16b9cc3..0e75e4b 100644
--- a/src/main/java/io/github/znetworkw/znpcservers/npc/NPC.java
+++ b/src/main/java/io/github/znetworkw/znpcservers/npc/NPC.java
@@ -8,7 +8,7 @@ import io.github.znetworkw.znpcservers.UnexpectedCallException;
 import io.github.znetworkw.znpcservers.reflection.Reflections;
 import io.github.znetworkw.znpcservers.npc.conversation.ConversationModel;
 import io.github.znetworkw.znpcservers.npc.hologram.Hologram;
-import io.github.znetworkw.znpcservers.npc.packet.PacketCache;
+import io.github.znetworkw.znpcservers.npc.nms.PacketCache;
 import io.github.znetworkw.znpcservers.user.ZUser;
 import io.github.znetworkw.znpcservers.utility.Utils;
 import io.github.znetworkw.znpcservers.utility.location.ZLocation;
@@ -176,7 +176,7 @@ public class NPC {
         try {
             Object nmsWorld = Reflections.GET_HANDLE_WORLD_METHOD.get().invoke(getLocation().getWorld());
             boolean isPlayer = (npcType == NPCType.PLAYER);
-            this.nmsEntity = isPlayer ? this.packets.getProxyInstance().getPlayerPacket(nmsWorld, this.gameProfile) : (Utils.versionNewer(14) ? npcType.getConstructor().newInstance(npcType.getNmsEntityType(), nmsWorld) : npcType.getConstructor().newInstance(nmsWorld));
+            this.nmsEntity = isPlayer ? this.packets.getNms().getPlayerPacket(nmsWorld, this.gameProfile) : (Utils.versionNewer(14) ? npcType.getConstructor().newInstance(npcType.getNmsEntityType(), nmsWorld) : npcType.getConstructor().newInstance(nmsWorld));
             this.bukkitEntity = Reflections.GET_BUKKIT_ENTITY_METHOD.get().invoke(this.nmsEntity);
             this.uuid = (UUID) Reflections.GET_UNIQUE_ID_METHOD.get().invoke(this.nmsEntity, new Object[0]);
             if (isPlayer) {
@@ -193,7 +193,7 @@ public class NPC {
             this.packets.flushCache("spawnPacket", "removeTab");
             this.entityID = (Integer) Reflections.GET_ENTITY_ID.get().invoke(this.nmsEntity, new Object[0]);
             FunctionFactory.findFunctionsForNpc(this).forEach(function -> function.resolve(this));
-            getPackets().getProxyInstance().update(this.packets);
+            getPackets().getNms().update(this.packets);
             this.hologram.createHologram();
         } catch (ReflectiveOperationException operationException) {
             throw new UnexpectedCallException(operationException);
@@ -208,20 +208,20 @@ public class NPC {
             this.viewers.add(user);
             boolean npcIsPlayer = (this.npcPojo.getNpcType() == NPCType.PLAYER);
             if (FunctionFactory.isTrue(this, "glow") || npcIsPlayer) {
-                ImmutableList<Object> scoreboardPackets = this.packets.getProxyInstance().updateScoreboard(this);
+                ImmutableList<Object> scoreboardPackets = this.packets.getNms().updateScoreboard(this);
                 scoreboardPackets.forEach(p -> Utils.sendPackets(user, p));
             }
             if (npcIsPlayer) {
                 if (FunctionFactory.isTrue(this, "mirror")) updateProfile(user.getGameProfile().getProperties());
                 Utils.sendPackets(user, this.tabConstructor, this.updateTabConstructor);
             }
-            Utils.sendPackets(user, this.packets.getProxyInstance().getSpawnPacket(this.nmsEntity, npcIsPlayer));
+            Utils.sendPackets(user, this.packets.getNms().getSpawnPacket(this.nmsEntity, npcIsPlayer));
             if (FunctionFactory.isTrue(this, "holo")) this.hologram.spawn(user);
             updateMetadata(Collections.singleton(user));
             sendEquipPackets(user);
             lookAt(user, getLocation(), true);
             if (npcIsPlayer) {
-                Object removeTabPacket = this.packets.getProxyInstance().getTabRemovePacket(this.nmsEntity);
+                Object removeTabPacket = this.packets.getNms().getTabRemovePacket(this.nmsEntity);
                 ZNPCsPlus.SCHEDULER.scheduleSyncDelayedTask(() -> Utils.sendPackets(user, removeTabPacket, this.updateTabConstructor), 60);
             }
         } catch (ReflectiveOperationException operationException) {
@@ -238,9 +238,9 @@ public class NPC {
 
     private void handleDelete(ZUser user) {
         try {
-            if (this.npcPojo.getNpcType() == NPCType.PLAYER) this.packets.getProxyInstance().getTabRemovePacket(this.nmsEntity);
+            if (this.npcPojo.getNpcType() == NPCType.PLAYER) this.packets.getNms().getTabRemovePacket(this.nmsEntity);
             this.hologram.delete(user);
-            Utils.sendPackets(user, this.packets.getProxyInstance().getDestroyPacket(this.entityID));
+            Utils.sendPackets(user, this.packets.getNms().getDestroyPacket(this.entityID));
         } catch (ReflectiveOperationException operationException) {
             throw new UnexpectedCallException(operationException);
         }
@@ -267,7 +267,7 @@ public class NPC {
 
     protected void updateMetadata(Iterable<ZUser> users) {
         try {
-            Object metaData = this.packets.getProxyInstance().getMetadataPacket(this.entityID, this.nmsEntity);
+            Object metaData = this.packets.getNms().getMetadataPacket(this.entityID, this.nmsEntity);
             for (ZUser user : users) Utils.sendPackets(user, metaData);
         } catch (ReflectiveOperationException operationException) {
             operationException.getCause().printStackTrace();
@@ -290,7 +290,7 @@ public class NPC {
     public void sendEquipPackets(ZUser zUser) {
         if (this.npcPojo.getNpcEquip().isEmpty()) return;
         try {
-            ImmutableList<Object> equipPackets = this.packets.getProxyInstance().getEquipPackets(this);
+            ImmutableList<Object> equipPackets = this.packets.getNms().getEquipPackets(this);
             equipPackets.forEach(o -> Utils.sendPackets(zUser, o));
         } catch (ReflectiveOperationException operationException) {
             throw new UnexpectedCallException(operationException.getCause());
@@ -314,7 +314,7 @@ public class NPC {
     }
 
     public Location getLocation() {
-        if (this.npcPath != null && this.npcPath.getLocation() != null) return  this.npcPath.getLocation().bukkitLocation();
+        if (this.npcPath != null && this.npcPath.getLocation() != null) return this.npcPath.getLocation().bukkitLocation();
         return this.npcPojo.getLocation().bukkitLocation();
     }
 }
diff --git a/src/main/java/io/github/znetworkw/znpcservers/npc/NPCAction.java b/src/main/java/io/github/znetworkw/znpcservers/npc/NPCAction.java
index 4ba683b..1470c0b 100644
--- a/src/main/java/io/github/znetworkw/znpcservers/npc/NPCAction.java
+++ b/src/main/java/io/github/znetworkw/znpcservers/npc/NPCAction.java
@@ -2,7 +2,7 @@ package io.github.znetworkw.znpcservers.npc;
 
 import com.google.common.base.MoreObjects;
 import lol.pyr.znpcsplus.ZNPCsPlus;
-import io.github.znetworkw.znpcservers.npc.event.ClickType;
+import io.github.znetworkw.znpcservers.npc.interaction.ClickType;
 import io.github.znetworkw.znpcservers.user.ZUser;
 import io.github.znetworkw.znpcservers.utility.Utils;
 import org.bukkit.Bukkit;
diff --git a/src/main/java/io/github/znetworkw/znpcservers/npc/function/GlowFunction.java b/src/main/java/io/github/znetworkw/znpcservers/npc/function/GlowFunction.java
index 6bbac8f..df96570 100644
--- a/src/main/java/io/github/znetworkw/znpcservers/npc/function/GlowFunction.java
+++ b/src/main/java/io/github/znetworkw/znpcservers/npc/function/GlowFunction.java
@@ -26,7 +26,7 @@ public class GlowFunction extends NPCFunction {
                     .get().invoke(npc.getNmsEntity()), Reflections.DATA_WATCHER_OBJECT_CONSTRUCTOR
                     .get().newInstance(0, Reflections.DATA_WATCHER_REGISTER_FIELD
                     .get()), (byte) (!FunctionFactory.isTrue(npc, this) ? 64 : 0));
-            npc.getPackets().getProxyInstance().update(npc.getPackets());
+            npc.getPackets().getNms().update(npc.getPackets());
             npc.deleteViewers();
             return NPCFunction.ResultType.SUCCESS;
         } catch (ReflectiveOperationException operationException) {
@@ -35,7 +35,7 @@ public class GlowFunction extends NPCFunction {
     }
 
     protected boolean allow(NPC npc) {
-        return npc.getPackets().getProxyInstance().allowGlowColor();
+        return npc.getPackets().getNms().allowGlowColor();
     }
 
     public NPCFunction.ResultType resolve(NPC npc) {
diff --git a/src/main/java/io/github/znetworkw/znpcservers/npc/hologram/Hologram.java b/src/main/java/io/github/znetworkw/znpcservers/npc/hologram/Hologram.java
index acd22e3..aba678b 100644
--- a/src/main/java/io/github/znetworkw/znpcservers/npc/hologram/Hologram.java
+++ b/src/main/java/io/github/znetworkw/znpcservers/npc/hologram/Hologram.java
@@ -56,7 +56,7 @@ public class Hologram {
     public void spawn(ZUser user) {
         this.hologramLines.forEach(hologramLine -> {
             try {
-                Object entityPlayerPacketSpawn = this.npc.getPackets().getProxyInstance().getHologramSpawnPacket(hologramLine.armorStand);
+                Object entityPlayerPacketSpawn = this.npc.getPackets().getNms().getHologramSpawnPacket(hologramLine.armorStand);
                 Utils.sendPackets(user, entityPlayerPacketSpawn);
             } catch (ReflectiveOperationException operationException) {
                 delete(user);
@@ -67,7 +67,7 @@ public class Hologram {
     public void delete(ZUser user) {
         this.hologramLines.forEach(hologramLine -> {
             try {
-                Utils.sendPackets(user, this.npc.getPackets().getProxyInstance().getDestroyPacket(hologramLine.id));
+                Utils.sendPackets(user, this.npc.getPackets().getNms().getDestroyPacket(hologramLine.id));
             } catch (ReflectiveOperationException operationException) {
                 throw new UnexpectedCallException(operationException);
             }
@@ -78,7 +78,7 @@ public class Hologram {
         for (HologramLine hologramLine : this.hologramLines) {
             try {
                 updateLine(hologramLine.line, hologramLine.armorStand, user);
-                Object metaData = this.npc.getPackets().getProxyInstance().getMetadataPacket(hologramLine.id, hologramLine.armorStand);
+                Object metaData = this.npc.getPackets().getNms().getMetadataPacket(hologramLine.id, hologramLine.armorStand);
                 Utils.sendPackets(user, metaData);
             } catch (ReflectiveOperationException operationException) {
                 throw new UnexpectedCallException(operationException);
diff --git a/src/main/java/io/github/znetworkw/znpcservers/npc/event/ClickType.java b/src/main/java/io/github/znetworkw/znpcservers/npc/interaction/ClickType.java
similarity index 81%
rename from src/main/java/io/github/znetworkw/znpcservers/npc/event/ClickType.java
rename to src/main/java/io/github/znetworkw/znpcservers/npc/interaction/ClickType.java
index cc95318..716cc27 100644
--- a/src/main/java/io/github/znetworkw/znpcservers/npc/event/ClickType.java
+++ b/src/main/java/io/github/znetworkw/znpcservers/npc/interaction/ClickType.java
@@ -1,4 +1,4 @@
-package io.github.znetworkw.znpcservers.npc.event;
+package io.github.znetworkw.znpcservers.npc.interaction;
 
 public enum ClickType {
     RIGHT, LEFT, DEFAULT;
diff --git a/src/main/java/io/github/znetworkw/znpcservers/npc/interaction/InteractionPacketListener.java b/src/main/java/io/github/znetworkw/znpcservers/npc/interaction/InteractionPacketListener.java
new file mode 100644
index 0000000..4401457
--- /dev/null
+++ b/src/main/java/io/github/znetworkw/znpcservers/npc/interaction/InteractionPacketListener.java
@@ -0,0 +1,46 @@
+package io.github.znetworkw.znpcservers.npc.interaction;
+
+import com.github.retrooper.packetevents.event.PacketListener;
+import com.github.retrooper.packetevents.event.PacketReceiveEvent;
+import com.github.retrooper.packetevents.protocol.packettype.PacketType;
+import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
+import io.github.znetworkw.znpcservers.npc.NPC;
+import io.github.znetworkw.znpcservers.npc.NPCAction;
+import io.github.znetworkw.znpcservers.user.ZUser;
+import lol.pyr.znpcsplus.ZNPCsPlus;
+import org.bukkit.Bukkit;
+
+import java.util.List;
+
+public class InteractionPacketListener implements PacketListener {
+    @Override
+    public void onPacketReceive(PacketReceiveEvent event) {
+        if (event.getPacketType() != PacketType.Play.Client.INTERACT_ENTITY) return;
+
+        WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event);
+        ZUser user = ZUser.find(event.getUser().getUUID());
+
+        long lastInteract = System.nanoTime() - user.getLastInteract();
+        if (user.getLastInteract() != 0L && lastInteract < 1000000000L) return;
+
+        NPC npc = NPC.all().stream().filter(n -> n.getEntityID() == packet.getEntityId()).findFirst().orElse(null);
+        if (npc == null) return;
+
+        ClickType clickType = ClickType.forName(packet.getAction().name());
+        user.updateLastInteract();
+        ZNPCsPlus.SCHEDULER.runTask(() -> {
+            Bukkit.getServer().getPluginManager().callEvent(new NPCInteractEvent(user.toPlayer(), clickType, npc));
+            List<NPCAction> actions = npc.getNpcPojo().getClickActions();
+            if (actions == null) return;
+            for (NPCAction action : actions) {
+                if (action.getClickType() != ClickType.DEFAULT && action.getClickType() != clickType) continue;
+                if (action.getDelay() > 0) {
+                    int actionId = npc.getNpcPojo().getClickActions().indexOf(action);
+                    if (user.getLastClicked().containsKey(actionId) && System.nanoTime() - user.getLastClicked().get(actionId) < action.getFixedDelay()) continue;
+                    user.getLastClicked().put(actionId, System.nanoTime());
+                }
+                action.run(user, action.getAction());
+            }
+        });
+    }
+}
diff --git a/src/main/java/io/github/znetworkw/znpcservers/npc/event/NPCInteractEvent.java b/src/main/java/io/github/znetworkw/znpcservers/npc/interaction/NPCInteractEvent.java
similarity index 95%
rename from src/main/java/io/github/znetworkw/znpcservers/npc/event/NPCInteractEvent.java
rename to src/main/java/io/github/znetworkw/znpcservers/npc/interaction/NPCInteractEvent.java
index 5a73bac..e1cd4b1 100644
--- a/src/main/java/io/github/znetworkw/znpcservers/npc/event/NPCInteractEvent.java
+++ b/src/main/java/io/github/znetworkw/znpcservers/npc/interaction/NPCInteractEvent.java
@@ -1,4 +1,4 @@
-package io.github.znetworkw.znpcservers.npc.event;
+package io.github.znetworkw.znpcservers.npc.interaction;
 
 import io.github.znetworkw.znpcservers.npc.NPC;
 import org.bukkit.entity.Player;
diff --git a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/Packet.java b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMS.java
similarity index 98%
rename from src/main/java/io/github/znetworkw/znpcservers/npc/packet/Packet.java
rename to src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMS.java
index 4eea9f7..7f7bdd7 100644
--- a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/Packet.java
+++ b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMS.java
@@ -1,4 +1,4 @@
-package io.github.znetworkw.znpcservers.npc.packet;
+package io.github.znetworkw.znpcservers.npc.nms;
 
 import com.google.common.collect.ImmutableList;
 import com.mojang.authlib.GameProfile;
@@ -14,7 +14,7 @@ import org.bukkit.inventory.ItemStack;
 import java.util.Collection;
 import java.util.Collections;
 
-public interface Packet {
+public interface NMS {
     int version();
 
     @PacketValue(keyName = "playerPacket")
diff --git a/src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMSFactory.java b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMSFactory.java
new file mode 100644
index 0000000..811895d
--- /dev/null
+++ b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMSFactory.java
@@ -0,0 +1,19 @@
+package io.github.znetworkw.znpcservers.npc.nms;
+
+import com.google.common.collect.ImmutableSet;
+import io.github.znetworkw.znpcservers.utility.Utils;
+
+import java.util.Comparator;
+
+public final class NMSFactory {
+    public static final ImmutableSet<NMS> ALL = ImmutableSet.of(new NMSV8(), new NMSV9(), new NMSV16(), new NMSV17(), new NMSV18(), new NMSV19());
+
+    public static final NMS NMS_FOR_CURRENT_VERSION = findPacketForVersion(Utils.BUKKIT_VERSION);
+
+    public static NMS findPacketForVersion(int version) {
+        return ALL.stream()
+                .filter(NMS -> (version >= NMS.version()))
+                .max(Comparator.comparing(NMS::version))
+                .orElseThrow(() -> new IllegalArgumentException("No packet instance found for version: " + version));
+    }
+}
diff --git a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketV16.java b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMSV16.java
similarity index 91%
rename from src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketV16.java
rename to src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMSV16.java
index c3b6bec..a1662a0 100644
--- a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketV16.java
+++ b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMSV16.java
@@ -1,4 +1,4 @@
-package io.github.znetworkw.znpcservers.npc.packet;
+package io.github.znetworkw.znpcservers.npc.nms;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
@@ -11,7 +11,7 @@ import org.bukkit.inventory.ItemStack;
 import java.util.List;
 import java.util.Map;
 
-public class PacketV16 extends PacketV9 {
+public class NMSV16 extends NMSV9 {
     public int version() {
         return 16;
     }
diff --git a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketV17.java b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMSV17.java
similarity index 90%
rename from src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketV17.java
rename to src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMSV17.java
index 4faf681..27b902f 100644
--- a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketV17.java
+++ b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMSV17.java
@@ -1,4 +1,4 @@
-package io.github.znetworkw.znpcservers.npc.packet;
+package io.github.znetworkw.znpcservers.npc.nms;
 
 import com.mojang.authlib.GameProfile;
 import io.github.znetworkw.znpcservers.reflection.Reflections;
@@ -6,7 +6,7 @@ import io.github.znetworkw.znpcservers.npc.NPC;
 import io.github.znetworkw.znpcservers.utility.Utils;
 import org.bukkit.Bukkit;
 
-public class PacketV17 extends PacketV16 {
+public class NMSV17 extends NMSV16 {
     public int version() {
         return 17;
     }
diff --git a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketV18.java b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMSV18.java
similarity index 82%
rename from src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketV18.java
rename to src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMSV18.java
index a5abba0..8a45d78 100644
--- a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketV18.java
+++ b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMSV18.java
@@ -1,10 +1,10 @@
-package io.github.znetworkw.znpcservers.npc.packet;
+package io.github.znetworkw.znpcservers.npc.nms;
 
 import io.github.znetworkw.znpcservers.reflection.Reflections;
 import io.github.znetworkw.znpcservers.npc.NPC;
 import io.github.znetworkw.znpcservers.utility.Utils;
 
-public class PacketV18 extends PacketV17 {
+public class NMSV18 extends NMSV17 {
     public int version() {
         return 18;
     }
diff --git a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketV19.java b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMSV19.java
similarity index 87%
rename from src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketV19.java
rename to src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMSV19.java
index d8de7b6..4323348 100644
--- a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketV19.java
+++ b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMSV19.java
@@ -1,10 +1,10 @@
-package io.github.znetworkw.znpcservers.npc.packet;
+package io.github.znetworkw.znpcservers.npc.nms;
 
 import com.mojang.authlib.GameProfile;
 import io.github.znetworkw.znpcservers.reflection.Reflections;
 import org.bukkit.Bukkit;
 
-public class PacketV19 extends PacketV18 {
+public class NMSV19 extends NMSV18 {
     public int version() {
         return 19;
     }
diff --git a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketV8.java b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMSV8.java
similarity index 97%
rename from src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketV8.java
rename to src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMSV8.java
index 08bbd65..dce9679 100644
--- a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketV8.java
+++ b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMSV8.java
@@ -1,4 +1,4 @@
-package io.github.znetworkw.znpcservers.npc.packet;
+package io.github.znetworkw.znpcservers.npc.nms;
 
 import com.google.common.collect.ImmutableList;
 import com.mojang.authlib.GameProfile;
@@ -12,7 +12,7 @@ import org.bukkit.inventory.ItemStack;
 import java.lang.reflect.Constructor;
 import java.util.Map;
 
-public class PacketV8 implements Packet {
+public class NMSV8 implements NMS {
     public int version() {
         return 8;
     }
diff --git a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketV9.java b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMSV9.java
similarity index 95%
rename from src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketV9.java
rename to src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMSV9.java
index 04963c9..2188645 100644
--- a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketV9.java
+++ b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/NMSV9.java
@@ -1,4 +1,4 @@
-package io.github.znetworkw.znpcservers.npc.packet;
+package io.github.znetworkw.znpcservers.npc.nms;
 
 import com.google.common.collect.ImmutableList;
 import io.github.znetworkw.znpcservers.reflection.Reflections;
@@ -9,7 +9,7 @@ import org.bukkit.inventory.ItemStack;
 
 import java.util.Map;
 
-public class PacketV9 extends PacketV8 {
+public class NMSV9 extends NMSV8 {
     public int version() {
         return 9;
     }
diff --git a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketCache.java b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/PacketCache.java
similarity index 76%
rename from src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketCache.java
rename to src/main/java/io/github/znetworkw/znpcservers/npc/nms/PacketCache.java
index ac755e6..bb9daf7 100644
--- a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketCache.java
+++ b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/PacketCache.java
@@ -1,4 +1,4 @@
-package io.github.znetworkw.znpcservers.npc.packet;
+package io.github.znetworkw.znpcservers.npc.nms;
 
 import com.google.common.collect.ImmutableMap;
 
@@ -16,7 +16,7 @@ public class PacketCache {
 
     static {
         ImmutableMap.Builder<Method, PacketValue> methodPacketValueBuilder = ImmutableMap.builder();
-        for (Method method : Packet.class.getMethods()) {
+        for (Method method : NMS.class.getMethods()) {
             if (method.isAnnotationPresent(PacketValue.class))
                 methodPacketValueBuilder.put(method, method.getAnnotation(PacketValue.class));
         }
@@ -25,26 +25,26 @@ public class PacketCache {
 
     private final Map<String, Object> packetResultCache = new ConcurrentHashMap<>();
 
-    private final Packet proxyInstance;
+    private final NMS nms;
 
-    public PacketCache(Packet packet) {
-        this.proxyInstance = newProxyInstance(packet);
+    public PacketCache(NMS NMS) {
+        this.nms = newNMSInstance(NMS);
     }
 
     public PacketCache() {
-        this(PacketFactory.PACKET_FOR_CURRENT_VERSION);
+        this(NMSFactory.NMS_FOR_CURRENT_VERSION);
     }
 
-    public Packet getProxyInstance() {
-        return this.proxyInstance;
+    public NMS getNms() {
+        return this.nms;
     }
 
-    protected Packet newProxyInstance(Packet packet) {
-        return (Packet) Proxy.newProxyInstance(packet
-                .getClass().getClassLoader(), new Class[]{Packet.class}, new PacketHandler(this, packet));
+    protected NMS newNMSInstance(NMS NMS) {
+        return (NMS) Proxy.newProxyInstance(NMS
+                .getClass().getClassLoader(), new Class[]{NMS.class}, new PacketHandler(this, NMS));
     }
 
-    private Object getOrCache(Packet instance, Method method, Object[] args) {
+    private Object getOrCache(NMS instance, Method method, Object[] args) {
         if (!VALUE_LOOKUP_BY_NAME.containsKey(method))
             throw new IllegalStateException("value not found for method: " + method.getName());
         PacketValue packetValue = VALUE_LOOKUP_BY_NAME.get(method);
@@ -73,9 +73,9 @@ public class PacketCache {
     private static class PacketHandler implements InvocationHandler {
         private final PacketCache packetCache;
 
-        private final Packet packets;
+        private final NMS packets;
 
-        public PacketHandler(PacketCache packetCache, Packet packets) {
+        public PacketHandler(PacketCache packetCache, NMS packets) {
             this.packetCache = packetCache;
             this.packets = packets;
         }
diff --git a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketValue.java b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/PacketValue.java
similarity index 86%
rename from src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketValue.java
rename to src/main/java/io/github/znetworkw/znpcservers/npc/nms/PacketValue.java
index 9121afc..a9f4769 100644
--- a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketValue.java
+++ b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/PacketValue.java
@@ -1,4 +1,4 @@
-package io.github.znetworkw.znpcservers.npc.packet;
+package io.github.znetworkw.znpcservers.npc.nms;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
diff --git a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/ValueType.java b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/ValueType.java
similarity index 90%
rename from src/main/java/io/github/znetworkw/znpcservers/npc/packet/ValueType.java
rename to src/main/java/io/github/znetworkw/znpcservers/npc/nms/ValueType.java
index 0f7c5f9..808d9a6 100644
--- a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/ValueType.java
+++ b/src/main/java/io/github/znetworkw/znpcservers/npc/nms/ValueType.java
@@ -1,4 +1,4 @@
-package io.github.znetworkw.znpcservers.npc.packet;
+package io.github.znetworkw.znpcservers.npc.nms;
 
 import java.util.Arrays;
 
diff --git a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketFactory.java b/src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketFactory.java
deleted file mode 100644
index 4689965..0000000
--- a/src/main/java/io/github/znetworkw/znpcservers/npc/packet/PacketFactory.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package io.github.znetworkw.znpcservers.npc.packet;
-
-import com.google.common.collect.ImmutableSet;
-import io.github.znetworkw.znpcservers.utility.Utils;
-
-import java.util.Comparator;
-
-public final class PacketFactory {
-    public static final ImmutableSet<Packet> ALL = ImmutableSet.of(new PacketV8(), new PacketV9(), new PacketV16(), new PacketV17(), new PacketV18(), new PacketV19());
-
-    public static final Packet PACKET_FOR_CURRENT_VERSION = findPacketForVersion(Utils.BUKKIT_VERSION);
-
-    public static Packet findPacketForVersion(int version) {
-        return ALL.stream()
-                .filter(packet -> (version >= packet.version()))
-                .max(Comparator.comparing(Packet::version))
-                .orElseThrow(() -> new IllegalArgumentException("No packet instance found for version: " + version));
-    }
-}
diff --git a/src/main/java/io/github/znetworkw/znpcservers/reflection/Reflections.java b/src/main/java/io/github/znetworkw/znpcservers/reflection/Reflections.java
index fc4728b..40003b0 100644
--- a/src/main/java/io/github/znetworkw/znpcservers/reflection/Reflections.java
+++ b/src/main/java/io/github/znetworkw/znpcservers/reflection/Reflections.java
@@ -6,7 +6,6 @@ import io.github.znetworkw.znpcservers.reflection.types.ConstructorReflection;
 import io.github.znetworkw.znpcservers.reflection.types.FieldReflection;
 import io.github.znetworkw.znpcservers.reflection.types.MethodReflection;
 import io.github.znetworkw.znpcservers.utility.Utils;
-import io.netty.channel.Channel;
 import org.bukkit.Bukkit;
 import org.bukkit.command.CommandMap;
 import org.bukkit.inventory.ItemStack;
@@ -23,9 +22,6 @@ import java.util.UUID;
  * inaccessible things from the server jar like packets, raw entity classes, etc.
  */
 public final class Reflections {
-    public static final Class<?> PACKET_PLAY_IN_USE_ENTITY_CLASS = new ClassReflection(new ReflectionBuilder(ReflectionPackage.PACKET)
-            .withClassName("PacketPlayInUseEntity")).get();
-
     public static final Class<?> ENUM_PLAYER_INFO_CLASS = new ClassReflection(new ReflectionBuilder(ReflectionPackage.PACKET)
             .withClassName("PacketPlayOutPlayerInfo$EnumPlayerInfoAction")
             .withClassName("ClientboundPlayerInfoUpdatePacket$a")).get();
@@ -409,9 +405,6 @@ public final class Reflections {
     public static final Class<?> PLAYER_CONNECTION_CLASS = new ClassReflection(new ReflectionBuilder(ReflectionPackage.SERVER_NETWORK)
             .withClassName("PlayerConnection")).get();
 
-    public static final Class<?> NETWORK_MANAGER_CLASS = new ClassReflection(new ReflectionBuilder(ReflectionPackage.NETWORK)
-            .withClassName("NetworkManager")).get();
-
     public static final Class<?> PACKET_PLAY_OUT_PLAYER_INFO_CLASS = new ClassReflection(new ReflectionBuilder(ReflectionPackage.PACKET)
             .withClassName("PacketPlayOutPlayerInfo")
             .withClassName("ClientboundPlayerInfoUpdatePacket")).get();
@@ -674,19 +667,6 @@ public final class Reflections {
             .withClassName(ENTITY_PLAYER_CLASS)
             .withFieldName((Utils.BUKKIT_VERSION > 16) ? "b" : "playerConnection"));
 
-    public static final ReflectionLazyLoader<Field> NETWORK_MANAGER_FIELD = new FieldReflection(new ReflectionBuilder(ReflectionPackage.PACKET)
-            .withClassName(PLAYER_CONNECTION_CLASS)
-            .withFieldName((Utils.BUKKIT_VERSION > 16) ? "a" : "networkManager")
-            .withExpectResult(NETWORK_MANAGER_CLASS));
-
-    public static final ReflectionLazyLoader<Field> CHANNEL_FIELD = new FieldReflection(new ReflectionBuilder(ReflectionPackage.SERVER_NETWORK)
-            .withClassName(NETWORK_MANAGER_CLASS)
-            .withExpectResult(Channel.class));
-
-    public static final ReflectionLazyLoader<Field> PACKET_IN_USE_ENTITY_ID_FIELD = new FieldReflection(new ReflectionBuilder(ReflectionPackage.PACKET)
-            .withClassName("PacketPlayInUseEntity")
-            .withFieldName("a"));
-
     public static final ReflectionLazyLoader<Object> ADD_PLAYER_FIELD = new FieldReflection(new ReflectionBuilder(ReflectionPackage.PACKET)
             .withClassName("PacketPlayOutPlayerInfo$EnumPlayerInfoAction")
             .withClassName("ClientboundPlayerInfoUpdatePacket$a")
diff --git a/src/main/java/io/github/znetworkw/znpcservers/user/ZUser.java b/src/main/java/io/github/znetworkw/znpcservers/user/ZUser.java
index bd01706..04e586f 100644
--- a/src/main/java/io/github/znetworkw/znpcservers/user/ZUser.java
+++ b/src/main/java/io/github/znetworkw/znpcservers/user/ZUser.java
@@ -1,20 +1,10 @@
 package io.github.znetworkw.znpcservers.user;
 
 import com.mojang.authlib.GameProfile;
-import io.github.znetworkw.znpcservers.exception.ChannelRegistrationException;
 import io.github.znetworkw.znpcservers.npc.NPC;
-import io.github.znetworkw.znpcservers.npc.NPCAction;
-import io.github.znetworkw.znpcservers.npc.event.ClickType;
-import io.github.znetworkw.znpcservers.npc.event.NPCInteractEvent;
 import io.github.znetworkw.znpcservers.reflection.Reflections;
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.MessageToMessageDecoder;
-import lol.pyr.znpcsplus.ZNPCsPlus;
 import org.bukkit.Bukkit;
-import org.bukkit.ChatColor;
 import org.bukkit.entity.Player;
-import org.bukkit.scheduler.BukkitRunnable;
 
 import java.lang.reflect.InvocationTargetException;
 import java.util.*;
@@ -47,52 +37,6 @@ public class ZUser {
         } catch (IllegalAccessException | InvocationTargetException e) {
             throw new IllegalStateException("can't create user for player " + uuid.toString(), e.getCause());
         }
-        if (tryRegisterChannel() != null) ZNPCsPlus.SCHEDULER.runTaskTimer(new ChannelRegistrationFallbackTask(this), 3);
-    }
-
-    private static class ChannelRegistrationFallbackTask extends BukkitRunnable {
-        private final ZUser user;
-        private int tries = 5;
-
-        private ChannelRegistrationFallbackTask(ZUser user) {
-            this.user = user;
-        }
-
-        @Override
-        public void run() {
-            ChannelRegistrationException ex = user.tryRegisterChannel();
-            Player player = user.toPlayer();
-            if (player == null) {
-                tries--;
-                return;
-            }
-            else if (!player.isOnline() || ex == null) {
-                cancel();
-                return;
-            }
-            if (tries-- > 0) return;
-            cancel();
-            player.kickPlayer(ChatColor.RED + "[ZNPCsPlus]\n" +
-                    ChatColor.WHITE + "Couldn't inject interaction detector to channel\n" +
-                    ChatColor.WHITE + "Please report this at https://github.com/Pyrbu/ZNPCsPlus");
-            ZNPCsPlus.LOGGER.severe("Couldn't inject interaction detector to channel for player " + player.getName() + " (" + player.getUniqueId() + ")");
-            ZNPCsPlus.LOGGER.severe("Channel names: " + ex.getChannelNames());
-            ex.printStackTrace();
-        }
-    }
-
-    private ChannelRegistrationException tryRegisterChannel() {
-        Channel channel = null;
-        try {
-            channel = (Channel) Reflections.CHANNEL_FIELD.get().get(Reflections.NETWORK_MANAGER_FIELD.get().get(this.playerConnection));
-            if (channel.pipeline().names().contains("npc_interact")) channel.pipeline().remove("npc_interact");
-            channel.pipeline().addAfter("decoder", "npc_interact", new ZNPCSocketDecoder());
-            return null;
-        } catch (IllegalAccessException e) {
-            throw new RuntimeException("illegal access exception while trying to register npc_interact channel");
-        } catch (NoSuchElementException e) {
-            return new ChannelRegistrationException(e, channel == null ? List.of() : channel.pipeline().names());
-        }
     }
 
     public static ZUser find(UUID uuid) {
@@ -141,40 +85,15 @@ public class ZUser {
         return Bukkit.getPlayer(this.uuid);
     }
 
-    class ZNPCSocketDecoder extends MessageToMessageDecoder<Object> {
-        protected void decode(ChannelHandlerContext channelHandlerContext, Object packet, List<Object> out) throws Exception {
-            out.add(packet);
-            if (packet.getClass() == Reflections.PACKET_PLAY_IN_USE_ENTITY_CLASS) {
-                long lastInteractNanos = System.nanoTime() - ZUser.this.lastInteract;
-                if (ZUser.this.lastInteract != 0L && lastInteractNanos < 1000000000L)
-                    return;
-                int entityId = Reflections.PACKET_IN_USE_ENTITY_ID_FIELD.get().getInt(packet);
-                NPC npc = NPC.all().stream().filter(npc1 -> (npc1.getEntityID() == entityId)).findFirst().orElse(null);
-                if (npc == null)
-                    return;
-                ClickType clickName = ClickType.forName(npc.getPackets().getProxyInstance().getClickType(packet).toString());
-                ZUser.this.lastInteract = System.nanoTime();
-                ZNPCsPlus.SCHEDULER.scheduleSyncDelayedTask(() -> {
-                    Bukkit.getServer().getPluginManager().callEvent(new NPCInteractEvent(ZUser.this.toPlayer(), clickName, npc));
-                    List<NPCAction> actions = npc.getNpcPojo().getClickActions();
-                    if (actions == null || actions.isEmpty())
-                        return;
-                    for (NPCAction npcAction : actions) {
-                        if (npcAction.getClickType() != ClickType.DEFAULT && clickName != npcAction.getClickType())
-                            continue;
-                        if (npcAction.getDelay() > 0) {
-                            int actionId = npc.getNpcPojo().getClickActions().indexOf(npcAction);
-                            if (ZUser.this.lastClicked.containsKey(actionId)) {
-                                long lastClickNanos = System.nanoTime() - ZUser.this.lastClicked.get(actionId);
-                                if (lastClickNanos < npcAction.getFixedDelay())
-                                    continue;
-                            }
-                            ZUser.this.lastClicked.put(actionId, System.nanoTime());
-                        }
-                        npcAction.run(ZUser.this, npcAction.getAction());
-                    }
-                }, 1);
-            }
-        }
+    public long getLastInteract() {
+        return lastInteract;
+    }
+
+    public Map<Integer, Long> getLastClicked() {
+        return lastClicked;
+    }
+
+    public void updateLastInteract() {
+        this.lastInteract = System.nanoTime();
     }
 }
diff --git a/src/main/java/io/github/znetworkw/znpcservers/utility/Utils.java b/src/main/java/io/github/znetworkw/znpcservers/utility/Utils.java
index 5526203..318aa58 100644
--- a/src/main/java/io/github/znetworkw/znpcservers/utility/Utils.java
+++ b/src/main/java/io/github/znetworkw/znpcservers/utility/Utils.java
@@ -49,7 +49,7 @@ public final class Utils {
     }
 
     public static void sendTitle(Player player, String title, String subTitle) {
-        player.sendTitle(toColor(title), toColor(subTitle), 1, 3, 1);
+        player.sendTitle(toColor(title), toColor(subTitle), 20, 60, 20);
     }
 
     public static void setValue(Object fieldInstance, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
diff --git a/src/main/java/lol/pyr/znpcsplus/ZNPCsPlus.java b/src/main/java/lol/pyr/znpcsplus/ZNPCsPlus.java
index 57c4026..3df94f7 100644
--- a/src/main/java/lol/pyr/znpcsplus/ZNPCsPlus.java
+++ b/src/main/java/lol/pyr/znpcsplus/ZNPCsPlus.java
@@ -1,7 +1,10 @@
 package lol.pyr.znpcsplus;
 
+import com.github.retrooper.packetevents.PacketEvents;
+import com.github.retrooper.packetevents.event.PacketListenerPriority;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
+import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
 import io.github.znetworkw.znpcservers.commands.list.DefaultCommand;
 import io.github.znetworkw.znpcservers.configuration.Configuration;
 import io.github.znetworkw.znpcservers.configuration.ConfigurationConstants;
@@ -11,6 +14,7 @@ import io.github.znetworkw.znpcservers.npc.NPC;
 import io.github.znetworkw.znpcservers.npc.NPCModel;
 import io.github.znetworkw.znpcservers.npc.NPCPath;
 import io.github.znetworkw.znpcservers.npc.NPCType;
+import io.github.znetworkw.znpcservers.npc.interaction.InteractionPacketListener;
 import io.github.znetworkw.znpcservers.npc.task.NPCPositionTask;
 import io.github.znetworkw.znpcservers.npc.task.NPCSaveTask;
 import io.github.znetworkw.znpcservers.npc.task.NPCVisibilityTask;
@@ -70,6 +74,8 @@ public class ZNPCsPlus extends JavaPlugin {
 
     @Override
     public void onLoad() {
+        PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this));
+        PacketEvents.getAPI().getSettings().checkForUpdates(false);
         LOGGER = getLogger();
         PLUGIN_FOLDER = getDataFolder();
         PATH_FOLDER = new File(PLUGIN_FOLDER, "paths");
@@ -104,9 +110,13 @@ public class ZNPCsPlus extends JavaPlugin {
             }
         }
 
-        log(ChatColor.WHITE + " * Initializing adventure...");
+        log(ChatColor.WHITE + " * Initializing Adventure...");
         ADVENTURE = BukkitAudiences.create(this);
 
+        log(ChatColor.WHITE + " * Initializing PacketEvents...");
+        PacketEvents.getAPI().getEventManager().registerListener(new InteractionPacketListener(), PacketListenerPriority.MONITOR);
+        PacketEvents.getAPI().init();
+
         PLUGIN_FOLDER.mkdirs();
         PATH_FOLDER.mkdirs();
 
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index f9ea653..80127b9 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -3,5 +3,13 @@ main: lol.pyr.znpcsplus.ZNPCsPlus
 load: POSTWORLD
 version: ${version}
 api-version: 1.13
-softdepend: [PlaceholderAPI, ServersNPC]
+softdepend:
+  - PlaceholderAPI
+  - ServersNPC
+  - ProtocolLib
+  - ProtocolSupport
+  - ViaVersion
+  - ViaBackwards
+  - ViaRewind
+  - Geyser-Spigot
 authors: [Gonzalez (g0b#3830), Pyr (Pyr#6969)]
\ No newline at end of file