diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
index 5394a23..1478f40 100644
--- a/config/checkstyle/checkstyle.xml
+++ b/config/checkstyle/checkstyle.xml
@@ -11,6 +11,5 @@
         </module>
         <module name="FinalClass"/>
         <module name="FinalParameters"/>
-	<module name="SuppressionCommentFilter"/>
     </module>
-</module>
+</module>
\ No newline at end of file
diff --git a/src/main/java/me/allinkdev/deviousmod/mixin/client/network/PacketSendAndReceive.java b/src/main/java/me/allinkdev/deviousmod/mixin/client/network/PacketSendAndReceive.java
new file mode 100644
index 0000000..7d22990
--- /dev/null
+++ b/src/main/java/me/allinkdev/deviousmod/mixin/client/network/PacketSendAndReceive.java
@@ -0,0 +1,26 @@
+package me.allinkdev.deviousmod.mixin.client.network;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPipeline;
+import me.allinkdev.deviousmod.packet.PacketHandler;
+import me.allinkdev.deviousmod.packet.PrePacketHandler;
+import net.minecraft.network.ClientConnection;
+import net.minecraft.network.NetworkSide;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(ClientConnection.class)
+public final class PacketSendAndReceive {
+    @Inject(method = "addHandlers", at = @At(value = "TAIL"))
+    private static void onAddHandlers(final ChannelPipeline pipeline, final NetworkSide side, final CallbackInfo ci) {
+        if (!side.equals(NetworkSide.CLIENTBOUND)) return;
+        pipeline.addFirst(new PrePacketHandler());
+    }
+
+    @Inject(method = "channelActive", at = @At(value = "TAIL"))
+    private void onChannelActive(final ChannelHandlerContext context, final CallbackInfo ci) {
+        context.pipeline().addBefore("packet_handler", null, new PacketHandler());
+    }
+}
diff --git a/src/main/java/me/allinkdev/deviousmod/mixin/client/network/packet/PacketSendAndReceive.java b/src/main/java/me/allinkdev/deviousmod/mixin/client/network/packet/PacketSendAndReceive.java
deleted file mode 100644
index ffdaa52..0000000
--- a/src/main/java/me/allinkdev/deviousmod/mixin/client/network/packet/PacketSendAndReceive.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package me.allinkdev.deviousmod.mixin.client.network.packet;
-
-import io.netty.channel.ChannelHandlerContext;
-import me.allinkdev.deviousmod.event.network.packet.impl.PacketC2SEvent;
-import me.allinkdev.deviousmod.event.network.packet.impl.PacketS2CEvent;
-import me.allinkdev.deviousmod.util.EventUtil;
-import net.minecraft.network.ClientConnection;
-import net.minecraft.network.PacketCallbacks;
-import net.minecraft.network.packet.Packet;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-
-@Mixin(ClientConnection.class)
-public final class PacketSendAndReceive {
-    @Inject(method = "channelRead0(Lio/netty/channel/ChannelHandlerContext;Lnet/minecraft/network/packet/Packet;)V", at = @At("HEAD"), cancellable = true)
-    //CHECKSTYLE:OFF
-    public void onReceive(final ChannelHandlerContext channelHandlerContext, Packet<?> packet, final CallbackInfo ci) {
-    //CHECKSTYLE:ON
-	final var event = EventUtil.postEvent(new PacketS2CEvent(packet));
-        final var eventPacket = event.getPacket();
-
-        if (event.isCancelled() || eventPacket == null) ci.cancel();
-        packet = eventPacket;
-    }
-
-    @Inject(method = "send(Lnet/minecraft/network/packet/Packet;Lnet/minecraft/network/PacketCallbacks;)V", at = @At("HEAD"), cancellable = true)
-    //CHECKSTYLE:OFF
-    private void onSend(Packet<?> packet, final PacketCallbacks callbacks, final CallbackInfo ci) {
-    //CHECKSTYLE:ON
-	final var event = EventUtil.postEvent(new PacketC2SEvent(packet));
-        final var eventPacket = event.getPacket();
-
-        if (event.isCancelled() || eventPacket == null) ci.cancel();
-        packet = eventPacket;
-    }
-}
diff --git a/src/main/java/me/allinkdev/deviousmod/mixin/client/network/packet/RawPacketReceive.java b/src/main/java/me/allinkdev/deviousmod/mixin/client/network/packet/RawPacketReceive.java
deleted file mode 100644
index f0ea4a8..0000000
--- a/src/main/java/me/allinkdev/deviousmod/mixin/client/network/packet/RawPacketReceive.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package me.allinkdev.deviousmod.mixin.client.network.packet;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-import io.netty.channel.ChannelHandlerContext;
-import me.allinkdev.deviousmod.event.network.packet.impl.PrePacketS2CEvent;
-import me.allinkdev.deviousmod.util.EventUtil;
-import net.minecraft.network.DecoderHandler;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-
-import java.util.Arrays;
-import java.util.List;
-
-@Mixin(DecoderHandler.class)
-public class RawPacketReceive {
-
-    @Inject(method = "decode", at = @At("HEAD"))
-    private void onDecode(final ChannelHandlerContext ctx, final ByteBuf buf, final List<Object> objects, final CallbackInfo ci) {
-        final byte[] array = Unpooled.copiedBuffer(buf.copy()).array();
-        EventUtil.postEvent(new PrePacketS2CEvent(Arrays.copyOf(array, array.length)));
-    }
-}
diff --git a/src/main/java/me/allinkdev/deviousmod/mixin/client/network/packet/RawPacketSend.java b/src/main/java/me/allinkdev/deviousmod/mixin/client/network/packet/RawPacketSend.java
deleted file mode 100644
index e6f0640..0000000
--- a/src/main/java/me/allinkdev/deviousmod/mixin/client/network/packet/RawPacketSend.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package me.allinkdev.deviousmod.mixin.client.network.packet;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-import io.netty.channel.ChannelHandlerContext;
-import me.allinkdev.deviousmod.event.network.packet.impl.PrePacketC2SEvent;
-import me.allinkdev.deviousmod.util.EventUtil;
-import net.minecraft.network.PacketEncoder;
-import net.minecraft.network.packet.Packet;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-
-import java.util.Arrays;
-
-@Mixin(PacketEncoder.class)
-public class RawPacketSend {
-
-    @Inject(method = "encode(Lio/netty/channel/ChannelHandlerContext;Lnet/minecraft/network/packet/Packet;Lio/netty/buffer/ByteBuf;)V", at = @At("HEAD"))
-    private void onDecode(final ChannelHandlerContext channelHandlerContext, final Packet<?> packet, final ByteBuf byteBuf, final CallbackInfo ci) {
-        final byte[] array = Unpooled.copiedBuffer(byteBuf.copy()).array();
-        EventUtil.postEvent(new PrePacketC2SEvent(Arrays.copyOf(array, array.length)));
-    }
-}
diff --git a/src/main/java/me/allinkdev/deviousmod/packet/PacketHandler.java b/src/main/java/me/allinkdev/deviousmod/packet/PacketHandler.java
new file mode 100644
index 0000000..02f8ef0
--- /dev/null
+++ b/src/main/java/me/allinkdev/deviousmod/packet/PacketHandler.java
@@ -0,0 +1,40 @@
+package me.allinkdev.deviousmod.packet;
+
+import io.netty.channel.ChannelDuplexHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPromise;
+import me.allinkdev.deviousmod.event.network.packet.impl.PacketC2SEvent;
+import me.allinkdev.deviousmod.event.network.packet.impl.PacketS2CEvent;
+import me.allinkdev.deviousmod.util.EventUtil;
+import net.minecraft.network.packet.Packet;
+import org.jetbrains.annotations.NotNull;
+
+public final class PacketHandler extends ChannelDuplexHandler {
+    @Override
+    public void channelRead(@NotNull final ChannelHandlerContext ctx, @NotNull final Object msg) throws Exception {
+        if (!(msg instanceof final Packet<?> packet)) {
+            super.channelRead(ctx, msg);
+            return;
+        }
+
+        final PacketS2CEvent event = EventUtil.postEvent(new PacketS2CEvent(packet));
+        final Packet<?> eventPacket = event.getPacket();
+
+        if (event.isCancelled() || eventPacket == null) return;
+        super.channelRead(ctx, eventPacket);
+    }
+
+    @Override
+    public void write(final ChannelHandlerContext ctx, final Object msg, final ChannelPromise promise) throws Exception {
+        if (!(msg instanceof final Packet<?> packet)) {
+            super.write(ctx, msg, promise);
+            return;
+        }
+
+        final PacketC2SEvent event = EventUtil.postEvent(new PacketC2SEvent(packet));
+        final Packet<?> eventPacket = event.getPacket();
+
+        if (event.isCancelled() || eventPacket == null) return;
+        super.write(ctx, eventPacket, promise);
+    }
+}
diff --git a/src/main/java/me/allinkdev/deviousmod/packet/PrePacketHandler.java b/src/main/java/me/allinkdev/deviousmod/packet/PrePacketHandler.java
new file mode 100644
index 0000000..4639f89
--- /dev/null
+++ b/src/main/java/me/allinkdev/deviousmod/packet/PrePacketHandler.java
@@ -0,0 +1,40 @@
+package me.allinkdev.deviousmod.packet;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelDuplexHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPromise;
+import me.allinkdev.deviousmod.DeviousMod;
+import me.allinkdev.deviousmod.event.network.packet.impl.PrePacketC2SEvent;
+import me.allinkdev.deviousmod.event.network.packet.impl.PrePacketS2CEvent;
+import me.allinkdev.deviousmod.util.EventUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Arrays;
+
+public final class PrePacketHandler extends ChannelDuplexHandler {
+    @Override
+    public void write(final ChannelHandlerContext ctx, final Object msg, final ChannelPromise promise) throws Exception {
+        if (msg instanceof final ByteBuf byteBuf) {
+            final byte[] array = Unpooled.copiedBuffer(byteBuf.copy()).array();
+            EventUtil.postEvent(new PrePacketC2SEvent(Arrays.copyOf(array, array.length)));
+        } else {
+            DeviousMod.LOGGER.warn("Received non-ByteBuf object ({}) in pre packet C2S handler.", msg.getClass().getSimpleName());
+        }
+
+        super.write(ctx, msg, promise);
+    }
+
+    @Override
+    public void channelRead(final @NotNull ChannelHandlerContext ctx, final @NotNull Object msg) throws Exception {
+        if (msg instanceof final ByteBuf byteBuf) {
+            final byte[] array = Unpooled.copiedBuffer(byteBuf.copy()).array();
+            EventUtil.postEvent(new PrePacketS2CEvent(Arrays.copyOf(array, array.length)));
+        } else {
+            DeviousMod.LOGGER.warn("Received non-ByteBuf object ({}) in pre S2C packet handler.", msg.getClass().getSimpleName());
+        }
+
+        super.channelRead(ctx, msg);
+    }
+}
diff --git a/src/main/resources/deviousmod.mixins.json b/src/main/resources/deviousmod.mixins.json
index b16045e..923b56e 100644
--- a/src/main/resources/deviousmod.mixins.json
+++ b/src/main/resources/deviousmod.mixins.json
@@ -19,6 +19,7 @@
     "client.network.CommandCompletionStealer",
     "client.network.CommandDispatcherListener",
     "client.network.CommandInterceptor",
+    "client.network.PacketSendAndReceive",
     "client.network.TimeListener",
     "client.network.packet.PacketSendAndReceive",
     "client.particle.ParticleRenderListener",
@@ -52,8 +53,6 @@
     "client.entity.PlayerEntityMixin",
     "client.network.ClientConnectionMixin",
     "client.network.NetworkStateMixin",
-    "client.network.packet.RawPacketReceive",
-    "client.network.packet.RawPacketSend",
     "client.render.world.SodiumWorldRenderListener",
     "patches.auth.PromiscuousTextureUrlBlocker",
     "patches.block.DecoratedPotCrashFix",