diff --git a/README.md b/README.md
index a8e757e..0ee8f8a 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@ Standalone proxy which allows players to join EVERY minecraft server version (Cl
 
 To download the latest version, go to the [Releases section](#executable-jar-file) and download the latest version.  
 Using it is very simple, just run the jar file, and it will start a user interface where everything can be configured.  
-For a full user guide go to the [Usage for Players](#usage-for-players--gui-) section or the [Usage for Server Owners](#usage-for-server-owners--cli-) section.
+For a full user guide go to the [Usage for Players](#usage-for-players-gui) section or the [Usage for Server Owners](#usage-for-server-owners-cli) section.
 
 ## Supported Server versions
 - Classic (c0.0.15 - c0.30 including [CPE](https://wiki.vg/Classic_Protocol_Extension))
@@ -12,6 +12,7 @@ For a full user guide go to the [Usage for Players](#usage-for-players--gui-) se
 - Release (1.0.0 - 1.19.4)
 - April Fools (3D Shareware, 20w14infinite)
 - Combat Snapshots (Combat Test 8c)
+- Bedrock Edition 1.19.70 (In development)
 
 ## Supported Client versions
 - Release (1.7.2 - 1.19.4)
@@ -53,11 +54,12 @@ Here is an example command to allow players to join on yourserverip:25568 and co
 
 ### Configuring the protocol translation
 To change the protocol translation settings/features you can look into the ViaProtocolHack folder.
-You will find 4 config files there:
+You will find 5 config files there:
 - viaversion.yml (ViaVersion)
 - config.yml (ViaBackwards)
 - viarewind.yml (ViaRewind)
 - vialegacy.yml (ViaLegacy)
+- viabedrock.yml (ViaBedrock)
 
 ### Developer Plugin API
 ViaProxy has a plugin API which allows you to create plugins for ViaProxy.  
diff --git a/build.gradle b/build.gradle
index b03b1b8..33d4d92 100644
--- a/build.gradle
+++ b/build.gradle
@@ -37,6 +37,18 @@ repositories {
         name = "Lenni0451"
         url "https://maven.lenni0451.net/releases"
     }
+    maven {
+        name = "Lenni0451 Snapshots"
+        url "https://maven.lenni0451.net/snapshots"
+    }
+    maven {
+        name = "OpenCollab Releases"
+        url = "https://repo.opencollab.dev/maven-releases/"
+    }
+    maven {
+        name = "OpenCollab Snapshots"
+        url = "https://repo.opencollab.dev/maven-snapshots/"
+    }
     maven {
         name = "ViaVersion"
         url "https://repo.viaversion.com"
@@ -66,9 +78,15 @@ dependencies {
         exclude group: "com.google.guava", module: "guava"
     }
     include "com.viaversion:viarewind-core:2.0.4-SNAPSHOT"
-    include "net.raphimc:ViaLegacy:2.2.10"
+    include "net.raphimc:ViaLegacy:2.2.11"
     include "net.raphimc:ViaAprilFools:2.0.6"
-    include("net.raphimc:ViaProtocolHack:2.2.2") {
+    include("net.raphimc:ViaBedrock:0.0.1-SNAPSHOT") {
+        exclude group: "io.netty", module: "netty-codec-http"
+        exclude group: "com.vdurmont", module: "semver4j"
+        exclude group: "io.jsonwebtoken", module: "jjwt-impl"
+        exclude group: "io.jsonwebtoken", module: "jjwt-gson"
+    }
+    include("net.raphimc:ViaProtocolHack:2.2.3") {
         exclude group: "org.slf4j", module: "slf4j-api"
     }
 
@@ -95,6 +113,12 @@ dependencies {
         exclude group: "com.google.code.gson", module: "gson"
     }
     include "com.vdurmont:semver4j:3.1.0"
+    include("org.cloudburstmc.netty:netty-transport-raknet:1.0.0.CR1-SNAPSHOT") {
+        exclude group: "io.netty", module: "netty-common"
+        exclude group: "io.netty", module: "netty-buffer"
+        exclude group: "io.netty", module: "netty-codec"
+        exclude group: "io.netty", module: "netty-transport"
+    }
 }
 
 blossom {
diff --git a/src/main/java/net/raphimc/viaproxy/ViaProxy.java b/src/main/java/net/raphimc/viaproxy/ViaProxy.java
index 22b2ae9..7c3f812 100644
--- a/src/main/java/net/raphimc/viaproxy/ViaProxy.java
+++ b/src/main/java/net/raphimc/viaproxy/ViaProxy.java
@@ -38,9 +38,10 @@ import net.raphimc.viaproxy.cli.options.Options;
 import net.raphimc.viaproxy.injection.Java17ToJava8;
 import net.raphimc.viaproxy.plugins.PluginManager;
 import net.raphimc.viaproxy.plugins.events.Client2ProxyHandlerCreationEvent;
-import net.raphimc.viaproxy.proxy.ProxyConnection;
+import net.raphimc.viaproxy.proxy.EventListener;
 import net.raphimc.viaproxy.proxy.client2proxy.Client2ProxyChannelInitializer;
 import net.raphimc.viaproxy.proxy.client2proxy.Client2ProxyHandler;
+import net.raphimc.viaproxy.proxy.session.ProxyConnection;
 import net.raphimc.viaproxy.saves.SaveManager;
 import net.raphimc.viaproxy.tasks.AccountRefreshTask;
 import net.raphimc.viaproxy.tasks.LoaderTask;
@@ -97,6 +98,7 @@ public class ViaProxy {
         loadNetty();
         saveManager = new SaveManager();
         PluginManager.loadPlugins();
+        PluginManager.EVENT_MANAGER.register(EventListener.class);
 
         Thread loaderThread = new Thread(new LoaderTask(), "ViaProtocolHack-Loader");
         Thread accountRefreshThread = new Thread(new AccountRefreshTask(saveManager), "AccountRefresh");
diff --git a/src/main/java/net/raphimc/viaproxy/injection/transformer/VersionEnumTransformer.java b/src/main/java/net/raphimc/viaproxy/injection/transformer/VersionEnumTransformer.java
deleted file mode 100644
index f3fae3b..0000000
--- a/src/main/java/net/raphimc/viaproxy/injection/transformer/VersionEnumTransformer.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * This file is part of ViaProxy - https://github.com/RaphiMC/ViaProxy
- * Copyright (C) 2023 RK_01/RaphiMC and contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package net.raphimc.viaproxy.injection.transformer;
-
-import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
-import net.lenni0451.classtransform.annotations.CTransformer;
-import net.lenni0451.classtransform.annotations.injection.COverride;
-import net.raphimc.viaprotocolhack.util.VersionEnum;
-import net.raphimc.viaproxy.plugins.PluginManager;
-import net.raphimc.viaproxy.plugins.ViaProxyPlugin;
-
-@CTransformer(VersionEnum.class)
-public abstract class VersionEnumTransformer {
-
-    @COverride
-    private static ProtocolVersion getViaBedrockProtocol(final String name) {
-        try {
-            Class<?> clazz = null;
-            for (ViaProxyPlugin plugin : PluginManager.getPlugins()) {
-                try {
-                    clazz = Class.forName("net.raphimc.viabedrock.api.BedrockProtocolVersion", true, plugin.getClass().getClassLoader());
-                    break;
-                } catch (Throwable ignored) {
-                }
-            }
-            if (clazz != null) {
-                return (ProtocolVersion) clazz.getField(name).get(null);
-            }
-        } catch (Throwable ignored) {
-        }
-        return ProtocolVersion.unknown;
-    }
-
-}
diff --git a/src/main/java/net/raphimc/viaproxy/plugins/events/FillPlayerDataEvent.java b/src/main/java/net/raphimc/viaproxy/plugins/events/FillPlayerDataEvent.java
index cf11743..2d61dcc 100644
--- a/src/main/java/net/raphimc/viaproxy/plugins/events/FillPlayerDataEvent.java
+++ b/src/main/java/net/raphimc/viaproxy/plugins/events/FillPlayerDataEvent.java
@@ -17,7 +17,7 @@
  */
 package net.raphimc.viaproxy.plugins.events;
 
-import net.raphimc.viaproxy.proxy.ProxyConnection;
+import net.raphimc.viaproxy.proxy.session.ProxyConnection;
 
 public class FillPlayerDataEvent {
 
diff --git a/src/main/java/net/raphimc/viaproxy/protocolhack/ProtocolHack.java b/src/main/java/net/raphimc/viaproxy/protocolhack/ProtocolHack.java
index 9c1bfeb..19d478f 100644
--- a/src/main/java/net/raphimc/viaproxy/protocolhack/ProtocolHack.java
+++ b/src/main/java/net/raphimc/viaproxy/protocolhack/ProtocolHack.java
@@ -18,12 +18,9 @@
 package net.raphimc.viaproxy.protocolhack;
 
 import net.raphimc.viaprotocolhack.ViaProtocolHack;
-import net.raphimc.viaprotocolhack.impl.platform.ViaBackwardsPlatformImpl;
-import net.raphimc.viaprotocolhack.impl.platform.ViaLegacyPlatformImpl;
-import net.raphimc.viaprotocolhack.impl.platform.ViaRewindPlatformImpl;
+import net.raphimc.viaprotocolhack.impl.platform.*;
 import net.raphimc.viaproxy.plugins.PluginManager;
 import net.raphimc.viaproxy.plugins.events.ProtocolHackInitEvent;
-import net.raphimc.viaproxy.protocolhack.impl.ViaAprilFoolsPlatformImpl;
 import net.raphimc.viaproxy.protocolhack.impl.ViaProxyVPLoader;
 import net.raphimc.viaproxy.protocolhack.impl.ViaProxyViaVersionPlatformImpl;
 
@@ -36,7 +33,7 @@ public class ProtocolHack {
 
     public static void init() {
         patchConfigs();
-        final Supplier<?>[] platformSuppliers = PluginManager.EVENT_MANAGER.call(new ProtocolHackInitEvent(ViaBackwardsPlatformImpl::new, ViaRewindPlatformImpl::new, ViaLegacyPlatformImpl::new, ViaAprilFoolsPlatformImpl::new)).getPlatformSuppliers().toArray(new Supplier[0]);
+        final Supplier<?>[] platformSuppliers = PluginManager.EVENT_MANAGER.call(new ProtocolHackInitEvent(ViaBackwardsPlatformImpl::new, ViaRewindPlatformImpl::new, ViaLegacyPlatformImpl::new, ViaAprilFoolsPlatformImpl::new, ViaBedrockPlatformImpl::new)).getPlatformSuppliers().toArray(new Supplier[0]);
         ViaProtocolHack.init(new ViaProxyViaVersionPlatformImpl(), new ViaProxyVPLoader(), null, null, platformSuppliers);
     }
 
diff --git a/src/main/java/net/raphimc/viaproxy/protocolhack/impl/ViaAprilFoolsPlatformImpl.java b/src/main/java/net/raphimc/viaproxy/protocolhack/impl/ViaProxyVPHPipeline.java
similarity index 56%
rename from src/main/java/net/raphimc/viaproxy/protocolhack/impl/ViaAprilFoolsPlatformImpl.java
rename to src/main/java/net/raphimc/viaproxy/protocolhack/impl/ViaProxyVPHPipeline.java
index dfb3f61..32a755b 100644
--- a/src/main/java/net/raphimc/viaproxy/protocolhack/impl/ViaAprilFoolsPlatformImpl.java
+++ b/src/main/java/net/raphimc/viaproxy/protocolhack/impl/ViaProxyVPHPipeline.java
@@ -17,30 +17,30 @@
  */
 package net.raphimc.viaproxy.protocolhack.impl;
 
-import com.viaversion.viaversion.api.Via;
-import net.raphimc.viaaprilfools.platform.ViaAprilFoolsPlatform;
-import net.raphimc.viaprotocolhack.util.JLoggerToSLF4J;
-import org.slf4j.LoggerFactory;
+import com.viaversion.viaversion.api.connection.UserConnection;
+import net.raphimc.netminecraft.constants.MCPipeline;
+import net.raphimc.viaprotocolhack.netty.VPHPipeline;
+import net.raphimc.viaprotocolhack.util.VersionEnum;
 
-import java.io.File;
-import java.util.logging.Logger;
+public class ViaProxyVPHPipeline extends VPHPipeline {
 
-public class ViaAprilFoolsPlatformImpl implements ViaAprilFoolsPlatform {
-
-    private static final Logger LOGGER = new JLoggerToSLF4J(LoggerFactory.getLogger("ViaAprilFools"));
-
-    public ViaAprilFoolsPlatformImpl() {
-        this.init(this.getDataFolder());
+    public ViaProxyVPHPipeline(UserConnection user, VersionEnum version) {
+        super(user, version);
     }
 
     @Override
-    public Logger getLogger() {
-        return LOGGER;
+    protected String compressionCodecName() {
+        return MCPipeline.COMPRESSION_HANDLER_NAME;
     }
 
     @Override
-    public File getDataFolder() {
-        return Via.getPlatform().getDataFolder();
+    protected String packetCodecName() {
+        return MCPipeline.PACKET_CODEC_HANDLER_NAME;
+    }
+
+    @Override
+    protected String lengthCodecName() {
+        return MCPipeline.SIZER_HANDLER_NAME;
     }
 
 }
diff --git a/src/main/java/net/raphimc/viaproxy/protocolhack/impl/ViaProxyVPLoader.java b/src/main/java/net/raphimc/viaproxy/protocolhack/impl/ViaProxyVPLoader.java
index f81f6be..57a19be 100644
--- a/src/main/java/net/raphimc/viaproxy/protocolhack/impl/ViaProxyVPLoader.java
+++ b/src/main/java/net/raphimc/viaproxy/protocolhack/impl/ViaProxyVPLoader.java
@@ -21,6 +21,7 @@ import com.viaversion.viaversion.api.Via;
 import com.viaversion.viaversion.api.protocol.version.VersionProvider;
 import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.CompressionProvider;
 import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.HandItemProvider;
+import net.raphimc.viabedrock.protocol.providers.NettyPipelineProvider;
 import net.raphimc.vialegacy.protocols.classic.protocola1_0_15toc0_28_30.providers.ClassicCustomCommandProvider;
 import net.raphimc.vialegacy.protocols.classic.protocola1_0_15toc0_28_30.providers.ClassicMPPassProvider;
 import net.raphimc.vialegacy.protocols.classic.protocola1_0_15toc0_28_30.providers.ClassicWorldHeightProvider;
@@ -38,10 +39,12 @@ public class ViaProxyVPLoader extends VPHLoader {
     public void load() {
         super.load();
 
+        // ViaVersion
         Via.getManager().getProviders().use(CompressionProvider.class, new ViaProxyCompressionProvider());
         Via.getManager().getProviders().use(HandItemProvider.class, new ViaProxyHandItemProvider());
         Via.getManager().getProviders().use(VersionProvider.class, new ViaProxyVersionProvider());
 
+        // ViaLegacy
         Via.getManager().getProviders().use(GameProfileFetcher.class, new ViaProxyGameProfileFetcher());
         Via.getManager().getProviders().use(EncryptionProvider.class, new ViaProxyEncryptionProvider());
         Via.getManager().getProviders().use(OldAuthProvider.class, new ViaProxyOldAuthProvider());
@@ -49,6 +52,10 @@ public class ViaProxyVPLoader extends VPHLoader {
         Via.getManager().getProviders().use(ClassicCustomCommandProvider.class, new ViaProxyClassicCustomCommandProvider());
         Via.getManager().getProviders().use(ClassicMPPassProvider.class, new ViaProxyClassicMPPassProvider());
 
+        // ViaBedrock
+        Via.getManager().getProviders().use(NettyPipelineProvider.class, new ViaProxyNettyPipelineProvider());
+
+        // ViaProxy plugins
         PluginManager.EVENT_MANAGER.call(new ViaLoadingEvent());
     }
 
diff --git a/src/main/java/net/raphimc/viaproxy/protocolhack/impl/ViaProxyViaDecodeHandler.java b/src/main/java/net/raphimc/viaproxy/protocolhack/impl/ViaProxyViaDecodeHandler.java
deleted file mode 100644
index 92ede79..0000000
--- a/src/main/java/net/raphimc/viaproxy/protocolhack/impl/ViaProxyViaDecodeHandler.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * This file is part of ViaProxy - https://github.com/RaphiMC/ViaProxy
- * Copyright (C) 2023 RK_01/RaphiMC and contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package net.raphimc.viaproxy.protocolhack.impl;
-
-import com.viaversion.viaversion.api.connection.UserConnection;
-import com.viaversion.viaversion.exception.CancelCodecException;
-import com.viaversion.viaversion.util.PipelineUtil;
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.ChannelHandlerContext;
-import net.raphimc.viaprotocolhack.netty.VPHDecodeHandler;
-import net.raphimc.viaproxy.util.logging.Logger;
-
-import java.util.List;
-
-public class ViaProxyViaDecodeHandler extends VPHDecodeHandler {
-
-    public ViaProxyViaDecodeHandler(final UserConnection info) {
-        super(info);
-    }
-
-    @Override
-    protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> out) throws Exception {
-        try {
-            super.decode(ctx, bytebuf, out);
-        } catch (Throwable e) {
-            if (PipelineUtil.containsCause(e, CancelCodecException.class)) throw e;
-            Logger.LOGGER.error("ProtocolHack Packet Error occurred", e);
-            Logger.u_err("ProtocolHack Error", this.user, "Caught unhandled exception: " + e.getClass().getSimpleName());
-        }
-    }
-
-}
diff --git a/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyClassicMPPassProvider.java b/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyClassicMPPassProvider.java
index c0ed743..f29e573 100644
--- a/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyClassicMPPassProvider.java
+++ b/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyClassicMPPassProvider.java
@@ -25,7 +25,7 @@ import net.raphimc.vialegacy.protocols.classic.protocola1_0_15toc0_28_30.provide
 import net.raphimc.vialegacy.protocols.release.protocol1_3_1_2to1_2_4_5.providers.OldAuthProvider;
 import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.storage.HandshakeStorage;
 import net.raphimc.viaproxy.cli.options.Options;
-import net.raphimc.viaproxy.proxy.ProxyConnection;
+import net.raphimc.viaproxy.proxy.session.ProxyConnection;
 
 import java.net.InetAddress;
 import java.net.URL;
diff --git a/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyClassicWorldHeightProvider.java b/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyClassicWorldHeightProvider.java
index 24899bd..1a2e759 100644
--- a/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyClassicWorldHeightProvider.java
+++ b/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyClassicWorldHeightProvider.java
@@ -20,7 +20,7 @@ package net.raphimc.viaproxy.protocolhack.providers;
 import com.viaversion.viaversion.api.connection.UserConnection;
 import net.raphimc.vialegacy.protocols.classic.protocola1_0_15toc0_28_30.providers.ClassicWorldHeightProvider;
 import net.raphimc.viaprotocolhack.util.VersionEnum;
-import net.raphimc.viaproxy.proxy.ProxyConnection;
+import net.raphimc.viaproxy.proxy.session.ProxyConnection;
 
 public class ViaProxyClassicWorldHeightProvider extends ClassicWorldHeightProvider {
 
diff --git a/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyEncryptionProvider.java b/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyEncryptionProvider.java
index 59fa69f..88736af 100644
--- a/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyEncryptionProvider.java
+++ b/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyEncryptionProvider.java
@@ -19,7 +19,7 @@ package net.raphimc.viaproxy.protocolhack.providers;
 
 import com.viaversion.viaversion.api.connection.UserConnection;
 import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.providers.EncryptionProvider;
-import net.raphimc.viaproxy.proxy.ProxyConnection;
+import net.raphimc.viaproxy.proxy.session.ProxyConnection;
 
 public class ViaProxyEncryptionProvider extends EncryptionProvider {
 
diff --git a/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyNettyPipelineProvider.java b/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyNettyPipelineProvider.java
new file mode 100644
index 0000000..fc8d3fa
--- /dev/null
+++ b/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyNettyPipelineProvider.java
@@ -0,0 +1,69 @@
+/*
+ * This file is part of ViaProxy - https://github.com/RaphiMC/ViaProxy
+ * Copyright (C) 2023 RK_01/RaphiMC and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package net.raphimc.viaproxy.protocolhack.providers;
+
+import com.viaversion.viaversion.api.connection.UserConnection;
+import io.netty.channel.Channel;
+import net.raphimc.netminecraft.constants.MCPipeline;
+import net.raphimc.viabedrock.netty.AesEncryption;
+import net.raphimc.viabedrock.netty.SnappyCompression;
+import net.raphimc.viabedrock.netty.ZLibCompression;
+import net.raphimc.viabedrock.protocol.providers.NettyPipelineProvider;
+import net.raphimc.viaprotocolhack.netty.VPHPipeline;
+import net.raphimc.viaproxy.proxy.session.ProxyConnection;
+
+import javax.crypto.SecretKey;
+
+public class ViaProxyNettyPipelineProvider extends NettyPipelineProvider {
+
+    @Override
+    public void enableCompression(UserConnection user, int threshold, int algorithm) {
+        final ProxyConnection proxyConnection = ProxyConnection.fromUserConnection(user);
+        final Channel channel = proxyConnection.getChannel();
+
+        try {
+            channel.pipeline().remove(MCPipeline.COMPRESSION_HANDLER_NAME);
+            switch (algorithm) {
+                case 0:
+                    channel.pipeline().addBefore(MCPipeline.SIZER_HANDLER_NAME, MCPipeline.COMPRESSION_HANDLER_NAME, new ZLibCompression());
+                    break;
+                case 1:
+                    channel.pipeline().addBefore(MCPipeline.SIZER_HANDLER_NAME, MCPipeline.COMPRESSION_HANDLER_NAME, new SnappyCompression());
+                    break;
+                default:
+                    throw new IllegalStateException("Invalid compression algorithm: " + algorithm);
+            }
+        } catch (Throwable e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void enableEncryption(UserConnection user, SecretKey key) {
+        final ProxyConnection proxyConnection = ProxyConnection.fromUserConnection(user);
+        final Channel channel = proxyConnection.getChannel();
+
+        try {
+            channel.pipeline().remove(MCPipeline.ENCRYPTION_HANDLER_NAME);
+            channel.pipeline().addAfter(VPHPipeline.VIABEDROCK_FRAME_ENCAPSULATION_HANDLER_NAME, MCPipeline.ENCRYPTION_HANDLER_NAME, new AesEncryption(key));
+        } catch (Throwable e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+}
diff --git a/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyOldAuthProvider.java b/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyOldAuthProvider.java
index 147db8a..e678100 100644
--- a/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyOldAuthProvider.java
+++ b/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyOldAuthProvider.java
@@ -19,8 +19,8 @@ package net.raphimc.viaproxy.protocolhack.providers;
 
 import com.viaversion.viaversion.api.connection.UserConnection;
 import net.raphimc.vialegacy.protocols.release.protocol1_3_1_2to1_2_4_5.providers.OldAuthProvider;
-import net.raphimc.viaproxy.proxy.ProxyConnection;
 import net.raphimc.viaproxy.proxy.external_interface.ExternalInterface;
+import net.raphimc.viaproxy.proxy.session.ProxyConnection;
 
 public class ViaProxyOldAuthProvider extends OldAuthProvider {
 
diff --git a/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyVersionProvider.java b/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyVersionProvider.java
index 13d40c2..3ffa41a 100644
--- a/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyVersionProvider.java
+++ b/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyVersionProvider.java
@@ -19,7 +19,7 @@ package net.raphimc.viaproxy.protocolhack.providers;
 
 import com.viaversion.viaversion.api.connection.UserConnection;
 import com.viaversion.viaversion.protocols.base.BaseVersionProvider;
-import net.raphimc.viaproxy.proxy.ProxyConnection;
+import net.raphimc.viaproxy.proxy.session.ProxyConnection;
 
 public class ViaProxyVersionProvider extends BaseVersionProvider {
 
diff --git a/src/main/java/net/raphimc/viaproxy/protocolhack/viaproxy/raknet_fix/FixedUnconnectedPingEncoder.java b/src/main/java/net/raphimc/viaproxy/protocolhack/viaproxy/raknet_fix/FixedUnconnectedPingEncoder.java
new file mode 100644
index 0000000..51c75f1
--- /dev/null
+++ b/src/main/java/net/raphimc/viaproxy/protocolhack/viaproxy/raknet_fix/FixedUnconnectedPingEncoder.java
@@ -0,0 +1,60 @@
+/*
+ * This file is part of ViaProxy - https://github.com/RaphiMC/ViaProxy
+ * Copyright (C) 2023 RK_01/RaphiMC and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.raphimc.viaproxy.protocolhack.viaproxy.raknet_fix;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelOutboundHandlerAdapter;
+import io.netty.channel.ChannelPromise;
+import io.netty.channel.socket.DatagramPacket;
+import org.cloudburstmc.netty.channel.raknet.RakClientChannel;
+import org.cloudburstmc.netty.channel.raknet.RakPing;
+import org.cloudburstmc.netty.channel.raknet.config.RakChannelOption;
+
+import static org.cloudburstmc.netty.channel.raknet.RakConstants.ID_UNCONNECTED_PING;
+
+// Temporary fix until the library fixes the issue
+public class FixedUnconnectedPingEncoder extends ChannelOutboundHandlerAdapter {
+
+    private final RakClientChannel rakClientChannel;
+
+    public FixedUnconnectedPingEncoder(final RakClientChannel rakClientChannel) {
+        this.rakClientChannel = rakClientChannel;
+    }
+
+    @Override
+    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
+        if (!(msg instanceof RakPing)) {
+            ctx.write(msg, promise);
+            return;
+        }
+
+        RakPing ping = (RakPing) msg;
+        ByteBuf magicBuf = this.rakClientChannel.config().getOption(RakChannelOption.RAK_UNCONNECTED_MAGIC);
+        long guid = this.rakClientChannel.config().getOption(RakChannelOption.RAK_GUID);
+
+        ByteBuf pingBuffer = ctx.alloc().ioBuffer(magicBuf.readableBytes() + 17);
+        pingBuffer.writeByte(ID_UNCONNECTED_PING);
+        pingBuffer.writeLong(ping.getPingTime());
+        pingBuffer.writeBytes(magicBuf, magicBuf.readerIndex(), magicBuf.readableBytes());
+        pingBuffer.writeLong(guid);
+        ctx.write(new DatagramPacket(pingBuffer, ping.getSender()));
+    }
+
+}
diff --git a/src/main/java/net/raphimc/viaproxy/protocolhack/viaproxy/raknet_fix/FixedUnconnectedPongDecoder.java b/src/main/java/net/raphimc/viaproxy/protocolhack/viaproxy/raknet_fix/FixedUnconnectedPongDecoder.java
new file mode 100644
index 0000000..7f7c9dc
--- /dev/null
+++ b/src/main/java/net/raphimc/viaproxy/protocolhack/viaproxy/raknet_fix/FixedUnconnectedPongDecoder.java
@@ -0,0 +1,74 @@
+/*
+ * This file is part of ViaProxy - https://github.com/RaphiMC/ViaProxy
+ * Copyright (C) 2023 RK_01/RaphiMC and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.raphimc.viaproxy.protocolhack.viaproxy.raknet_fix;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.socket.DatagramPacket;
+import org.cloudburstmc.netty.channel.raknet.RakClientChannel;
+import org.cloudburstmc.netty.channel.raknet.RakPong;
+import org.cloudburstmc.netty.channel.raknet.config.RakChannelOption;
+import org.cloudburstmc.netty.handler.codec.raknet.AdvancedChannelInboundHandler;
+
+import static org.cloudburstmc.netty.channel.raknet.RakConstants.ID_UNCONNECTED_PONG;
+
+// Temporary fix until the library fixes the issue
+public class FixedUnconnectedPongDecoder extends AdvancedChannelInboundHandler<DatagramPacket> {
+
+    private final RakClientChannel rakClientChannel;
+
+    public FixedUnconnectedPongDecoder(final RakClientChannel rakClientChannel) {
+        this.rakClientChannel = rakClientChannel;
+    }
+
+    @Override
+    protected boolean acceptInboundMessage(ChannelHandlerContext ctx, Object msg) throws Exception {
+        if (!super.acceptInboundMessage(ctx, msg)) {
+            return false;
+        }
+
+        DatagramPacket packet = (DatagramPacket) msg;
+        ByteBuf buf = packet.content();
+        return buf.isReadable() && buf.getUnsignedByte(buf.readerIndex()) == ID_UNCONNECTED_PONG;
+    }
+
+    @Override
+    protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
+        ByteBuf buf = packet.content();
+        buf.readUnsignedByte(); // Packet ID
+
+        long pingTime = buf.readLong();
+        long guid = buf.readLong();
+
+        ByteBuf magicBuf = this.rakClientChannel.config().getOption(RakChannelOption.RAK_UNCONNECTED_MAGIC);
+        if (!buf.isReadable(magicBuf.readableBytes()) || !ByteBufUtil.equals(buf.readSlice(magicBuf.readableBytes()), magicBuf)) {
+            // Magic does not match
+            return;
+        }
+
+        ByteBuf pongData = Unpooled.EMPTY_BUFFER;
+        if (buf.isReadable(2)) { // Length
+            pongData = buf.readRetainedSlice(buf.readUnsignedShort());
+        }
+        ctx.fireChannelRead(new RakPong(pingTime, guid, pongData, packet.sender()));
+    }
+
+}
diff --git a/src/main/java/net/raphimc/viaproxy/proxy/EventListener.java b/src/main/java/net/raphimc/viaproxy/proxy/EventListener.java
new file mode 100644
index 0000000..c547442
--- /dev/null
+++ b/src/main/java/net/raphimc/viaproxy/proxy/EventListener.java
@@ -0,0 +1,33 @@
+/*
+ * This file is part of ViaProxy - https://github.com/RaphiMC/ViaProxy
+ * Copyright (C) 2023 RK_01/RaphiMC and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package net.raphimc.viaproxy.proxy;
+
+import net.lenni0451.lambdaevents.EventHandler;
+import net.raphimc.viaprotocolhack.util.VersionEnum;
+import net.raphimc.viaproxy.plugins.events.GetDefaultPortEvent;
+
+public class EventListener {
+
+    @EventHandler
+    public static void onGetDefaultPort(final GetDefaultPortEvent event) {
+        if (event.getServerVersion().equals(VersionEnum.bedrockLatest)) {
+            event.setDefaultPort(19132);
+        }
+    }
+
+}
diff --git a/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/Client2ProxyHandler.java b/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/Client2ProxyHandler.java
index 7c11cbd..9beffa8 100644
--- a/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/Client2ProxyHandler.java
+++ b/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/Client2ProxyHandler.java
@@ -23,6 +23,7 @@ import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandler;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.SimpleChannelInboundHandler;
 import net.raphimc.netminecraft.constants.ConnectionState;
@@ -44,12 +45,14 @@ import net.raphimc.viaproxy.plugins.events.PreConnectEvent;
 import net.raphimc.viaproxy.plugins.events.Proxy2ServerHandlerCreationEvent;
 import net.raphimc.viaproxy.plugins.events.ResolveSrvEvent;
 import net.raphimc.viaproxy.proxy.LoginState;
-import net.raphimc.viaproxy.proxy.ProxyConnection;
 import net.raphimc.viaproxy.proxy.external_interface.AuthLibServices;
 import net.raphimc.viaproxy.proxy.external_interface.ExternalInterface;
 import net.raphimc.viaproxy.proxy.external_interface.OpenAuthModConstants;
 import net.raphimc.viaproxy.proxy.proxy2server.Proxy2ServerChannelInitializer;
 import net.raphimc.viaproxy.proxy.proxy2server.Proxy2ServerHandler;
+import net.raphimc.viaproxy.proxy.session.BedrockProxyConnection;
+import net.raphimc.viaproxy.proxy.session.DummyProxyConnection;
+import net.raphimc.viaproxy.proxy.session.ProxyConnection;
 import net.raphimc.viaproxy.proxy.util.CloseAndReturn;
 import net.raphimc.viaproxy.proxy.util.ExceptionUtil;
 import net.raphimc.viaproxy.util.ArrayHelper;
@@ -67,6 +70,7 @@ import java.security.spec.InvalidKeySpecException;
 import java.time.Instant;
 import java.util.Arrays;
 import java.util.Random;
+import java.util.function.Supplier;
 import java.util.regex.Pattern;
 
 public class Client2ProxyHandler extends SimpleChannelInboundHandler<IPacket> {
@@ -90,8 +94,7 @@ public class Client2ProxyHandler extends SimpleChannelInboundHandler<IPacket> {
         super.channelActive(ctx);
 
         RANDOM.nextBytes(this.verifyToken);
-        this.proxyConnection = new ProxyConnection(() -> PluginManager.EVENT_MANAGER.call(new Proxy2ServerHandlerCreationEvent(new Proxy2ServerHandler())).getHandler(), Proxy2ServerChannelInitializer::new, ctx.channel());
-        ctx.channel().attr(ProxyConnection.PROXY_CONNECTION_ATTRIBUTE_KEY).set(this.proxyConnection);
+        this.proxyConnection = new DummyProxyConnection(ctx.channel());
 
         ViaProxy.c2pChannels.add(ctx.channel());
     }
@@ -99,6 +102,7 @@ public class Client2ProxyHandler extends SimpleChannelInboundHandler<IPacket> {
     @Override
     public void channelInactive(ChannelHandlerContext ctx) throws Exception {
         super.channelInactive(ctx);
+        if (this.proxyConnection instanceof DummyProxyConnection) return;
 
         try {
             this.proxyConnection.getChannel().close();
@@ -162,6 +166,7 @@ public class Client2ProxyHandler extends SimpleChannelInboundHandler<IPacket> {
         String connectIP = Options.CONNECT_ADDRESS;
         int connectPort = Options.CONNECT_PORT;
         VersionEnum serverVersion = Options.PROTOCOL_VERSION;
+        String classicMpPass = null;
 
         if (Options.INTERNAL_SRV_MODE) {
             final ArrayHelper arrayHelper = ArrayHelper.instanceOf(address.split("\7"));
@@ -169,7 +174,7 @@ public class Client2ProxyHandler extends SimpleChannelInboundHandler<IPacket> {
             connectPort = arrayHelper.getInteger(1);
             final String versionString = arrayHelper.get(2);
             if (arrayHelper.isIndexValid(3)) {
-                this.proxyConnection.setClassicMpPass(arrayHelper.getString(3));
+                classicMpPass = arrayHelper.getString(3);
             }
             for (VersionEnum v : VersionEnum.getAllVersions()) {
                 if (v.getName().equalsIgnoreCase(versionString)) {
@@ -209,7 +214,7 @@ public class Client2ProxyHandler extends SimpleChannelInboundHandler<IPacket> {
         connectPort = resolveSrvEvent.getPort();
 
         final ServerAddress serverAddress;
-        if (resolveSrvEvent.isCancelled() || serverVersion.isOlderThan(VersionEnum.r1_3_1tor1_3_2)) {
+        if (resolveSrvEvent.isCancelled() || serverVersion.isOlderThan(VersionEnum.r1_3_1tor1_3_2) || serverVersion.equals(VersionEnum.bedrockLatest)) {
             serverAddress = new ServerAddress(connectIP, connectPort);
         } else {
             serverAddress = ServerAddress.fromSRV(connectIP + ":" + connectPort);
@@ -220,6 +225,17 @@ public class Client2ProxyHandler extends SimpleChannelInboundHandler<IPacket> {
             this.proxyConnection.kickClient(preConnectEvent.getCancelMessage());
         }
 
+        final Supplier<ChannelHandler> handlerSupplier = () -> PluginManager.EVENT_MANAGER.call(new Proxy2ServerHandlerCreationEvent(new Proxy2ServerHandler())).getHandler();
+        if (serverVersion.equals(VersionEnum.bedrockLatest)) {
+            this.proxyConnection = new BedrockProxyConnection(handlerSupplier, Proxy2ServerChannelInitializer::new, this.proxyConnection.getC2P());
+        } else {
+            this.proxyConnection = new ProxyConnection(handlerSupplier, Proxy2ServerChannelInitializer::new, this.proxyConnection.getC2P());
+        }
+        this.proxyConnection.getC2P().attr(ProxyConnection.PROXY_CONNECTION_ATTRIBUTE_KEY).set(this.proxyConnection);
+        this.proxyConnection.setClientVersion(clientVersion);
+        this.proxyConnection.setConnectionState(packet.intendedState);
+        this.proxyConnection.setClassicMpPass(classicMpPass);
+
         Logger.u_info("connect", this.proxyConnection.getC2P().remoteAddress(), this.proxyConnection.getGameProfile(), "[" + clientVersion.getName() + " <-> " + serverVersion.getName() + "] Connecting to " + serverAddress.getAddress() + ":" + serverAddress.getPort());
         try {
             this.proxyConnection.connectToServer(serverAddress, serverVersion);
diff --git a/src/main/java/net/raphimc/viaproxy/proxy/external_interface/ExternalInterface.java b/src/main/java/net/raphimc/viaproxy/proxy/external_interface/ExternalInterface.java
index 1be9670..ae025b5 100644
--- a/src/main/java/net/raphimc/viaproxy/proxy/external_interface/ExternalInterface.java
+++ b/src/main/java/net/raphimc/viaproxy/proxy/external_interface/ExternalInterface.java
@@ -29,11 +29,13 @@ import com.viaversion.viaversion.api.connection.UserConnection;
 import com.viaversion.viaversion.api.minecraft.ProfileKey;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
+import net.raphimc.mcauth.step.bedrock.StepMCChain;
 import net.raphimc.netminecraft.netty.crypto.CryptUtil;
 import net.raphimc.netminecraft.packet.PacketTypes;
 import net.raphimc.netminecraft.packet.impl.login.C2SLoginHelloPacket1_19_3;
 import net.raphimc.netminecraft.packet.impl.login.C2SLoginHelloPacket1_7;
 import net.raphimc.netminecraft.packet.impl.login.C2SLoginKeyPacket1_19;
+import net.raphimc.viabedrock.protocol.storage.AuthChainData;
 import net.raphimc.viaprotocolhack.util.VersionEnum;
 import net.raphimc.viaproxy.cli.options.Options;
 import net.raphimc.viaproxy.plugins.PluginManager;
@@ -41,7 +43,8 @@ import net.raphimc.viaproxy.plugins.events.FillPlayerDataEvent;
 import net.raphimc.viaproxy.protocolhack.viaproxy.signature.storage.ChatSession1_19_0;
 import net.raphimc.viaproxy.protocolhack.viaproxy.signature.storage.ChatSession1_19_1;
 import net.raphimc.viaproxy.protocolhack.viaproxy.signature.storage.ChatSession1_19_3;
-import net.raphimc.viaproxy.proxy.ProxyConnection;
+import net.raphimc.viaproxy.proxy.session.ProxyConnection;
+import net.raphimc.viaproxy.saves.impl.accounts.BedrockAccount;
 import net.raphimc.viaproxy.saves.impl.accounts.MicrosoftAccount;
 import net.raphimc.viaproxy.util.LocalSocketClient;
 import net.raphimc.viaproxy.util.logging.Logger;
@@ -86,10 +89,10 @@ public class ExternalInterface {
             }
         } else if (Options.MC_ACCOUNT != null) {
             proxyConnection.setGameProfile(Options.MC_ACCOUNT.getGameProfile());
+            final UserConnection user = proxyConnection.getUserConnection();
 
             if (Options.MC_ACCOUNT instanceof MicrosoftAccount && proxyConnection.getServerVersion().isBetweenInclusive(VersionEnum.r1_19, VersionEnum.r1_19_3)) {
                 final MicrosoftAccount microsoftAccount = (MicrosoftAccount) Options.MC_ACCOUNT;
-                final UserConnection user = proxyConnection.getUserConnection();
                 final UserApiService userApiService = AuthLibServices.AUTHENTICATION_SERVICE.createUserApiService(microsoftAccount.getMcProfile().prevResult().prevResult().access_token());
                 final KeyPairResponse keyPair = userApiService.getKeyPair();
                 if (keyPair != null) {
@@ -111,6 +114,10 @@ public class ExternalInterface {
                         throw new InsecurePublicKeyException.MissingException();
                     }
                 }
+            } else if (Options.MC_ACCOUNT instanceof BedrockAccount && proxyConnection.getServerVersion().equals(VersionEnum.bedrockLatest)) {
+                final BedrockAccount bedrockAccount = (BedrockAccount) Options.MC_ACCOUNT;
+                final StepMCChain.MCChain mcChain = bedrockAccount.getMcChain();
+                user.put(new AuthChainData(user, mcChain.mojangJwt(), mcChain.identityJwt(), mcChain.publicKey(), mcChain.privateKey()));
             }
         }
 
diff --git a/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerChannelInitializer.java b/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerChannelInitializer.java
index a164a68..32b6ea9 100644
--- a/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerChannelInitializer.java
+++ b/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerChannelInitializer.java
@@ -29,18 +29,12 @@ import io.netty.handler.proxy.Socks5ProxyHandler;
 import net.raphimc.netminecraft.constants.MCPipeline;
 import net.raphimc.netminecraft.netty.connection.MinecraftChannelInitializer;
 import net.raphimc.netminecraft.packet.registry.PacketRegistryUtil;
-import net.raphimc.vialegacy.netty.PreNettyDecoder;
-import net.raphimc.vialegacy.netty.PreNettyEncoder;
-import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.baseprotocols.PreNettyBaseProtocol;
-import net.raphimc.viaprotocolhack.netty.VPHEncodeHandler;
-import net.raphimc.viaprotocolhack.netty.VPHPipeline;
-import net.raphimc.viaprotocolhack.util.VersionEnum;
 import net.raphimc.viaproxy.cli.options.Options;
 import net.raphimc.viaproxy.plugins.PluginManager;
 import net.raphimc.viaproxy.plugins.events.Proxy2ServerChannelInitializeEvent;
 import net.raphimc.viaproxy.plugins.events.types.ITyped;
-import net.raphimc.viaproxy.protocolhack.impl.ViaProxyViaDecodeHandler;
-import net.raphimc.viaproxy.proxy.ProxyConnection;
+import net.raphimc.viaproxy.protocolhack.impl.ViaProxyVPHPipeline;
+import net.raphimc.viaproxy.proxy.session.ProxyConnection;
 
 import java.net.InetSocketAddress;
 import java.net.URI;
@@ -70,14 +64,7 @@ public class Proxy2ServerChannelInitializer extends MinecraftChannelInitializer
 
         super.initChannel(channel);
         channel.attr(MCPipeline.PACKET_REGISTRY_ATTRIBUTE_KEY).set(PacketRegistryUtil.getHandshakeRegistry(true));
-        channel.pipeline().addBefore(MCPipeline.PACKET_CODEC_HANDLER_NAME, VPHPipeline.ENCODER_HANDLER_NAME, new VPHEncodeHandler(user));
-        channel.pipeline().addBefore(MCPipeline.PACKET_CODEC_HANDLER_NAME, VPHPipeline.DECODER_HANDLER_NAME, new ViaProxyViaDecodeHandler(user));
-
-        if (ProxyConnection.fromChannel(channel).getServerVersion().isOlderThanOrEqualTo(VersionEnum.r1_6_4)) {
-            user.getProtocolInfo().getPipeline().add(PreNettyBaseProtocol.INSTANCE);
-            channel.pipeline().addBefore(MCPipeline.SIZER_HANDLER_NAME, VPHPipeline.PRE_NETTY_ENCODER_HANDLER_NAME, new PreNettyEncoder(user));
-            channel.pipeline().addBefore(MCPipeline.SIZER_HANDLER_NAME, VPHPipeline.PRE_NETTY_DECODER_HANDLER_NAME, new PreNettyDecoder(user));
-        }
+        channel.pipeline().addLast(new ViaProxyVPHPipeline(user, ProxyConnection.fromChannel(channel).getServerVersion()));
 
         if (PluginManager.EVENT_MANAGER.call(new Proxy2ServerChannelInitializeEvent(ITyped.Type.POST, channel)).isCancelled()) {
             channel.close();
diff --git a/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerHandler.java b/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerHandler.java
index 8e913fa..86cf1ab 100644
--- a/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerHandler.java
+++ b/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerHandler.java
@@ -37,8 +37,8 @@ import net.raphimc.netminecraft.packet.impl.login.*;
 import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.storage.ProtocolMetadataStorage;
 import net.raphimc.viaprotocolhack.util.VersionEnum;
 import net.raphimc.viaproxy.cli.options.Options;
-import net.raphimc.viaproxy.proxy.ProxyConnection;
 import net.raphimc.viaproxy.proxy.external_interface.ExternalInterface;
+import net.raphimc.viaproxy.proxy.session.ProxyConnection;
 import net.raphimc.viaproxy.proxy.util.ExceptionUtil;
 import net.raphimc.viaproxy.util.logging.Logger;
 
diff --git a/src/main/java/net/raphimc/viaproxy/proxy/session/BedrockProxyConnection.java b/src/main/java/net/raphimc/viaproxy/proxy/session/BedrockProxyConnection.java
new file mode 100644
index 0000000..c5c1ba6
--- /dev/null
+++ b/src/main/java/net/raphimc/viaproxy/proxy/session/BedrockProxyConnection.java
@@ -0,0 +1,108 @@
+/*
+ * This file is part of ViaProxy - https://github.com/RaphiMC/ViaProxy
+ * Copyright (C) 2023 RK_01/RaphiMC and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package net.raphimc.viaproxy.proxy.session;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.epoll.Epoll;
+import io.netty.channel.epoll.EpollDatagramChannel;
+import io.netty.channel.socket.nio.NioDatagramChannel;
+import net.lenni0451.reflect.stream.RStream;
+import net.raphimc.netminecraft.constants.ConnectionState;
+import net.raphimc.netminecraft.constants.MCPipeline;
+import net.raphimc.netminecraft.util.LazyLoadBase;
+import net.raphimc.netminecraft.util.ServerAddress;
+import net.raphimc.viaprotocolhack.netty.VPHPipeline;
+import net.raphimc.viaprotocolhack.netty.viabedrock.PingEncapsulationCodec;
+import net.raphimc.viaprotocolhack.util.VersionEnum;
+import net.raphimc.viaproxy.protocolhack.viaproxy.raknet_fix.FixedUnconnectedPingEncoder;
+import net.raphimc.viaproxy.protocolhack.viaproxy.raknet_fix.FixedUnconnectedPongDecoder;
+import org.cloudburstmc.netty.channel.raknet.RakChannelFactory;
+import org.cloudburstmc.netty.channel.raknet.RakClientChannel;
+import org.cloudburstmc.netty.channel.raknet.config.RakChannelOption;
+import org.cloudburstmc.netty.handler.codec.raknet.common.UnconnectedPingEncoder;
+import org.cloudburstmc.netty.handler.codec.raknet.common.UnconnectedPongDecoder;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public class BedrockProxyConnection extends ProxyConnection {
+
+    public BedrockProxyConnection(Supplier<ChannelHandler> handlerSupplier, Function<Supplier<ChannelHandler>, ChannelInitializer<Channel>> channelInitializerSupplier, Channel c2p) {
+        super(handlerSupplier, channelInitializerSupplier, c2p);
+    }
+
+    @Override
+    public void initialize(Bootstrap bootstrap) {
+        if (Epoll.isAvailable()) {
+            bootstrap
+                    .group(LazyLoadBase.CLIENT_EPOLL_EVENTLOOP.getValue())
+                    .channelFactory(RakChannelFactory.client(EpollDatagramChannel.class));
+        } else {
+            bootstrap
+                    .group(LazyLoadBase.CLIENT_NIO_EVENTLOOP.getValue())
+                    .channelFactory(RakChannelFactory.client(NioDatagramChannel.class));
+        }
+
+        bootstrap
+                .option(RakChannelOption.CONNECT_TIMEOUT_MILLIS, 4_000)
+                .option(RakChannelOption.IP_TOS, 0x18)
+                .option(RakChannelOption.RAK_PROTOCOL_VERSION, 11)
+                .option(RakChannelOption.RAK_CONNECT_TIMEOUT, 4_000L)
+                .option(RakChannelOption.RAK_SESSION_TIMEOUT, 30_000L)
+                .option(RakChannelOption.RAK_GUID, ThreadLocalRandom.current().nextLong())
+                .attr(ProxyConnection.PROXY_CONNECTION_ATTRIBUTE_KEY, this)
+                .handler(this.channelInitializerSupplier.apply(this.handlerSupplier));
+
+        this.channelFuture = bootstrap.register().syncUninterruptibly();
+    }
+
+    @Override
+    public void connectToServer(ServerAddress serverAddress, VersionEnum targetVersion) {
+        if (this.getConnectionState() == ConnectionState.STATUS) {
+            RStream.of(this).withSuper().fields().by("serverAddress").set(serverAddress);
+            RStream.of(this).withSuper().fields().by("serverVersion").set(targetVersion);
+            this.ping(serverAddress);
+        } else {
+            super.connectToServer(serverAddress, targetVersion);
+        }
+    }
+
+    private void ping(final ServerAddress serverAddress) {
+        if (this.channelFuture == null) {
+            this.initialize(new Bootstrap());
+        }
+        this.getChannel().bind(new InetSocketAddress(0)).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE).syncUninterruptibly();
+        final RakClientChannel channel = (RakClientChannel) this.getChannel();
+
+        { // Temporary fix for the ping encoder
+            channel.parent().pipeline().replace(UnconnectedPingEncoder.NAME, UnconnectedPingEncoder.NAME, new FixedUnconnectedPingEncoder(channel));
+            channel.parent().pipeline().replace(UnconnectedPongDecoder.NAME, UnconnectedPongDecoder.NAME, new FixedUnconnectedPongDecoder(channel));
+        }
+
+        this.getChannel().pipeline().replace(VPHPipeline.VIABEDROCK_FRAME_ENCAPSULATION_HANDLER_NAME, "ping_encapsulation", new PingEncapsulationCodec(serverAddress.toSocketAddress()));
+        this.getChannel().pipeline().remove(VPHPipeline.VIABEDROCK_PACKET_ENCAPSULATION_HANDLER_NAME);
+        this.getChannel().pipeline().remove(MCPipeline.SIZER_HANDLER_NAME);
+    }
+
+}
diff --git a/src/main/java/net/raphimc/viaproxy/proxy/session/DummyProxyConnection.java b/src/main/java/net/raphimc/viaproxy/proxy/session/DummyProxyConnection.java
new file mode 100644
index 0000000..592676f
--- /dev/null
+++ b/src/main/java/net/raphimc/viaproxy/proxy/session/DummyProxyConnection.java
@@ -0,0 +1,129 @@
+/*
+ * This file is part of ViaProxy - https://github.com/RaphiMC/ViaProxy
+ * Copyright (C) 2023 RK_01/RaphiMC and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package net.raphimc.viaproxy.proxy.session;
+
+import com.mojang.authlib.GameProfile;
+import com.viaversion.viaversion.api.connection.UserConnection;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import net.raphimc.netminecraft.constants.ConnectionState;
+import net.raphimc.netminecraft.packet.impl.login.C2SLoginHelloPacket1_7;
+import net.raphimc.netminecraft.util.ServerAddress;
+import net.raphimc.viaprotocolhack.util.VersionEnum;
+
+import java.security.Key;
+import java.util.concurrent.CompletableFuture;
+
+public class DummyProxyConnection extends ProxyConnection {
+
+    public DummyProxyConnection(final Channel c2p) {
+        super(null, null, c2p);
+    }
+
+    @Override
+    public void initialize(Bootstrap bootstrap) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void connectToServer(ServerAddress serverAddress, VersionEnum targetVersion) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ServerAddress getServerAddress() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public VersionEnum getServerVersion() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setKeyForPreNettyEncryption(Key key) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void enablePreNettyEncryption() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setGameProfile(GameProfile gameProfile) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public GameProfile getGameProfile() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setLoginHelloPacket(C2SLoginHelloPacket1_7 loginHelloPacket) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public C2SLoginHelloPacket1_7 getLoginHelloPacket() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setUserConnection(UserConnection userConnection) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public UserConnection getUserConnection() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ConnectionState getConnectionState() {
+        return ConnectionState.HANDSHAKING;
+    }
+
+    @Override
+    public CompletableFuture<ByteBuf> sendCustomPayload(String channel, ByteBuf data) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean handleCustomPayload(int id, ByteBuf data) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setClassicMpPass(String classicMpPass) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getClassicMpPass() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isClosed() {
+        return false;
+    }
+
+}
diff --git a/src/main/java/net/raphimc/viaproxy/proxy/ProxyConnection.java b/src/main/java/net/raphimc/viaproxy/proxy/session/ProxyConnection.java
similarity index 99%
rename from src/main/java/net/raphimc/viaproxy/proxy/ProxyConnection.java
rename to src/main/java/net/raphimc/viaproxy/proxy/session/ProxyConnection.java
index 0177178..f7cce15 100644
--- a/src/main/java/net/raphimc/viaproxy/proxy/ProxyConnection.java
+++ b/src/main/java/net/raphimc/viaproxy/proxy/session/ProxyConnection.java
@@ -15,7 +15,7 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
-package net.raphimc.viaproxy.proxy;
+package net.raphimc.viaproxy.proxy.session;
 
 import com.mojang.authlib.GameProfile;
 import com.viaversion.viaversion.api.connection.UserConnection;
diff --git a/src/main/java/net/raphimc/viaproxy/proxy/util/ExceptionUtil.java b/src/main/java/net/raphimc/viaproxy/proxy/util/ExceptionUtil.java
index 35150f9..51b7384 100644
--- a/src/main/java/net/raphimc/viaproxy/proxy/util/ExceptionUtil.java
+++ b/src/main/java/net/raphimc/viaproxy/proxy/util/ExceptionUtil.java
@@ -20,7 +20,7 @@ package net.raphimc.viaproxy.proxy.util;
 import com.viaversion.viaversion.exception.InformativeException;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.handler.codec.EncoderException;
-import net.raphimc.viaproxy.proxy.ProxyConnection;
+import net.raphimc.viaproxy.proxy.session.ProxyConnection;
 import net.raphimc.viaproxy.util.logging.Logger;
 
 import java.io.IOException;