diff --git a/bootstrap/fabric/build.gradle.kts b/bootstrap/fabric/build.gradle.kts
index 890f5d656..af9c4547d 100644
--- a/bootstrap/fabric/build.gradle.kts
+++ b/bootstrap/fabric/build.gradle.kts
@@ -66,6 +66,22 @@ tasks {
         relocate("org.yaml", "org.geysermc.relocate.yaml") // https://github.com/CardboardPowered/cardboard/issues/139
         relocate("com.fasterxml.jackson", "org.geysermc.relocate.jackson")
         relocate("net.kyori", "org.geysermc.relocate.kyori")
+
+        dependencies {
+            // Exclude everything EXCEPT KQueue and some DNS stuff required for HAProxyc
+            exclude(dependency("io.netty:netty-transport-classes-epoll:.*"))
+            exclude(dependency("io.netty:netty-transport-native-epoll:.*"))
+            exclude(dependency("io.netty:netty-transport-native-unix-common:.*"))
+            exclude(dependency("io.netty:netty-transport-native-kqueue:.*"))
+            exclude(dependency("io.netty:netty-handler:.*"))
+            exclude(dependency("io.netty:netty-common:.*"))
+            exclude(dependency("io.netty:netty-buffer:.*"))
+            exclude(dependency("io.netty:netty-resolver:.*"))
+            exclude(dependency("io.netty:netty-transport:.*"))
+            exclude(dependency("io.netty:netty-codec:.*"))
+            exclude(dependency("io.netty:netty-resolver-dns:.*"))
+            exclude(dependency("io.netty:netty-resolver-dns-native-macos:.*"))
+        }
     }
 
     remapJar {
diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts
index b5ef4e69e..da4e5af33 100644
--- a/bootstrap/spigot/build.gradle.kts
+++ b/bootstrap/spigot/build.gradle.kts
@@ -44,6 +44,7 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
 
         // We cannot shade Netty, or else native libraries will not load
         // Needed because older Spigot builds do not provide the haproxy module
+        exclude(dependency("io.netty:netty-transport-classes-epoll:.*"))
         exclude(dependency("io.netty:netty-transport-native-epoll:.*"))
         exclude(dependency("io.netty:netty-transport-native-unix-common:.*"))
         exclude(dependency("io.netty:netty-transport-native-kqueue:.*"))
diff --git a/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts
index 395beb104..dde85c33a 100644
--- a/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts
+++ b/build-logic/src/main/kotlin/geyser.shadow-conventions.gradle.kts
@@ -24,6 +24,11 @@ tasks {
                     exclude(dependency(string))
                 }
             }
+
+            sJar.dependencies {
+                exclude(dependency("org.checkerframework:checker-qual:.*"))
+                exclude(dependency("org.jetbrains:annotations:.*"))
+            }
         }
     }
     named("build") {
diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java
index a74f3f7a2..24cce78fe 100644
--- a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java
+++ b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java
@@ -304,6 +304,7 @@ public class ItemRegistryPopulator {
             Set<String> javaOnlyItems = new ObjectOpenHashSet<>();
             Collections.addAll(javaOnlyItems, "minecraft:spectral_arrow", "minecraft:debug_stick",
                     "minecraft:knowledge_book", "minecraft:tipped_arrow", "minecraft:bundle");
+            javaOnlyItems.add("minecraft:decorated_pot"); // TODO 1.19.80 resolve probs?
             if (!customItemsAllowed) {
                 javaOnlyItems.add("minecraft:furnace_minecart");
             }
diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java
index 9a233fad3..d88d1720c 100644
--- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java
+++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java
@@ -738,7 +738,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
                 return;
             }
 
-            connectDownstream();
+            try {
+                connectDownstream();
+            } catch (Throwable t) {
+                t.printStackTrace();
+            }
         });
     }
 
@@ -782,7 +786,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
                 return;
             }
 
-            connectDownstream();
+            try {
+                connectDownstream();
+            } catch (Throwable t) {
+                t.printStackTrace();
+            }
         });
     }
 
@@ -856,7 +864,12 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
                         selectedProfile,
                         service.getAccessToken()
                 );
-                connectDownstream();
+                try {
+                    connectDownstream();
+                } catch (Throwable t) {
+                    t.printStackTrace();
+                    return false;
+                }
 
                 // Save our refresh token for later use
                 geyser.saveRefreshToken(bedrockUsername(), service.getRefreshToken());
diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/EnchantmentTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/EnchantmentTranslator.java
index 204981965..5a61b483d 100644
--- a/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/EnchantmentTranslator.java
+++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/item/nbt/EnchantmentTranslator.java
@@ -25,11 +25,14 @@
 
 package org.geysermc.geyser.translator.inventory.item.nbt;
 
+import com.github.steveice10.mc.protocol.data.game.Identifier;
 import com.github.steveice10.opennbt.tag.builtin.*;
 import org.geysermc.geyser.GeyserImpl;
 import org.geysermc.geyser.inventory.item.Enchantment;
 import org.geysermc.geyser.registry.type.ItemMapping;
 import org.geysermc.geyser.session.GeyserSession;
+import org.geysermc.geyser.text.ChatColor;
+import org.geysermc.geyser.text.MinecraftLocale;
 import org.geysermc.geyser.translator.inventory.item.ItemRemapper;
 import org.geysermc.geyser.translator.inventory.item.NbtItemStackTranslator;
 
@@ -43,28 +46,27 @@ public class EnchantmentTranslator extends NbtItemStackTranslator {
     @Override
     public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) {
         List<Tag> newTags = new ArrayList<>();
-        Tag enchantmentTag = itemTag.get("Enchantments");
+        Tag enchantmentTag = itemTag.remove("Enchantments");
         if (enchantmentTag instanceof ListTag listTag) {
             for (Tag tag : listTag.getValue()) {
                 if (!(tag instanceof CompoundTag)) continue;
-
-                CompoundTag bedrockTag = remapEnchantment((CompoundTag) tag);
-                newTags.add(bedrockTag);
-            }
-            itemTag.remove("Enchantments");
-        }
-        enchantmentTag = itemTag.get("StoredEnchantments");
-        if (enchantmentTag instanceof ListTag listTag) {
-            for (Tag tag : listTag.getValue()) {
-                if (!(tag instanceof CompoundTag)) continue;
-
-                CompoundTag bedrockTag = remapEnchantment((CompoundTag) tag);
+                CompoundTag bedrockTag = remapEnchantment(session, (CompoundTag) tag, itemTag);
+                if (bedrockTag != null) {
+                    newTags.add(bedrockTag);
+                }
+            }
+        }
+
+        // TODO consolidate this into EnchantedBookTranslator
+        enchantmentTag = itemTag.remove("StoredEnchantments");
+        if (enchantmentTag instanceof ListTag listTag) {
+            for (Tag tag : listTag.getValue()) {
+                if (!(tag instanceof CompoundTag)) continue;
+                CompoundTag bedrockTag = remapEnchantment(session, (CompoundTag) tag, itemTag);
                 if (bedrockTag != null) {
-                    bedrockTag.put(new ShortTag("GeyserStoredEnchantment", (short) 0));
                     newTags.add(bedrockTag);
                 }
             }
-            itemTag.remove("StoredEnchantments");
         }
 
         if (!newTags.isEmpty()) {
@@ -99,7 +101,6 @@ public class EnchantmentTranslator extends NbtItemStackTranslator {
                 javaValue.put("lvl", new IntTag("lvl", levelTag != null ? levelTag.getValue() : 1));
                 javaTag.setValue(javaValue);
 
-
                 if (geyserStoredEnchantmentTag != null) {
                     tagValue.remove("GeyserStoredEnchantment");
                     storedEnchantments.add(javaTag);
@@ -120,13 +121,20 @@ public class EnchantmentTranslator extends NbtItemStackTranslator {
     }
 
 
-    private CompoundTag remapEnchantment(CompoundTag tag) {
+    private CompoundTag remapEnchantment(GeyserSession session, CompoundTag tag, CompoundTag rootTag) {
         Tag javaEnchId = tag.get("id");
         if (!(javaEnchId instanceof StringTag))
             return null;
 
         Enchantment enchantment = Enchantment.getByJavaIdentifier(((StringTag) javaEnchId).getValue());
         if (enchantment == null) {
+            if (Identifier.formalize((String) javaEnchId.getValue()).equals("minecraft:sweeping")) {
+                Tag javaEnchLvl = tag.get("lvl");
+                int sweepingLvl = javaEnchLvl != null && javaEnchLvl.getValue() instanceof Number lvl ? lvl.intValue() : 0;
+
+                addSweeping(session, rootTag, sweepingLvl);
+                return null;
+            }
             GeyserImpl.getInstance().getLogger().debug("Unknown Java enchantment while NBT item translating: " + javaEnchId.getValue());
             return null;
         }
@@ -140,4 +148,21 @@ public class EnchantmentTranslator extends NbtItemStackTranslator {
         return bedrockTag;
     }
 
-}
+    private void addSweeping(GeyserSession session, CompoundTag itemTag, int level) {
+        CompoundTag displayTag = itemTag.get("display");
+        if (displayTag == null) {
+            displayTag = new CompoundTag("display");
+            itemTag.put(displayTag);
+        }
+        ListTag loreTag = displayTag.get("Lore");
+        if (loreTag == null) {
+            loreTag = new ListTag("Lore");
+            displayTag.put(loreTag);
+        }
+
+        String sweepingTranslation = MinecraftLocale.getLocaleString("enchantment.minecraft.sweeping", session.locale());
+        String lvlTranslation = MinecraftLocale.getLocaleString("enchantment.level." + level, session.locale());
+
+        loreTag.add(new StringTag("", ChatColor.RESET + ChatColor.GRAY + sweepingTranslation + " " + lvlTranslation));
+    }
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaGameEventTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaGameEventTranslator.java
index 05e14c41b..7019838c1 100644
--- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaGameEventTranslator.java
+++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaGameEventTranslator.java
@@ -47,21 +47,23 @@ import org.geysermc.geyser.translator.protocol.Translator;
 
 @Translator(packet = ClientboundGameEventPacket.class)
 public class JavaGameEventTranslator extends PacketTranslator<ClientboundGameEventPacket> {
+    // Strength of rainstorms and thunderstorms is a 0-1 float on Java, while on Bedrock it is a 0-65535 int
+    private static final int MAX_STORM_STRENGTH = 65535;
 
     @Override
     public void translate(GeyserSession session, ClientboundGameEventPacket packet) {
         PlayerEntity entity = session.getPlayerEntity();
 
         switch (packet.getNotification()) {
+            // Yes, START_RAIN and STOP_RAIN are swapped in terms of what they cause the client to do.
+            // This is how the Mojang mappings name them, so we go with it
+            // It seems Mojang's intent was that START_RAIN would set the rain strength to 0 so that it can then be incremeneted on a gradient by the server
+            // The inverse is true for STOP_RAIN
+            // This is indeed the behavior of the vanilla server
+            // However, it seems most server software (at least Spigot and Paper) did not go along with this
+            // As a result many developers use these packets for the opposite of what their names implies
+            // Behavior last verified with Java 1.19.4 and Bedrock 1.19.71
             case START_RAIN:
-                LevelEventPacket startRainPacket = new LevelEventPacket();
-                startRainPacket.setType(LevelEventType.START_RAINING);
-                startRainPacket.setData(Integer.MAX_VALUE);
-                startRainPacket.setPosition(Vector3f.ZERO);
-                session.sendUpstreamPacket(startRainPacket);
-                session.setRaining(true);
-                break;
-            case STOP_RAIN:
                 LevelEventPacket stopRainPacket = new LevelEventPacket();
                 stopRainPacket.setType(LevelEventType.STOP_RAINING);
                 stopRainPacket.setData(0);
@@ -69,34 +71,35 @@ public class JavaGameEventTranslator extends PacketTranslator<ClientboundGameEve
                 session.sendUpstreamPacket(stopRainPacket);
                 session.setRaining(false);
                 break;
+            case STOP_RAIN:
+                LevelEventPacket startRainPacket = new LevelEventPacket();
+                startRainPacket.setType(LevelEventType.START_RAINING);
+                startRainPacket.setData(MAX_STORM_STRENGTH);
+                startRainPacket.setPosition(Vector3f.ZERO);
+                session.sendUpstreamPacket(startRainPacket);
+                session.setRaining(true);
+                break;
             case RAIN_STRENGTH:
-                // While the above values are used, they CANNOT BE TRUSTED on a vanilla server as they are swapped around
-                // Spigot and forks implement it correctly
-                // Rain strength is your best way for determining if there is any rain
-                RainStrengthValue value = (RainStrengthValue) packet.getValue();
-                boolean isCurrentlyRaining = value.getStrength() > 0f;
-                // Java sends the rain level. Bedrock doesn't care, so we don't care if it's already raining.
-                if (isCurrentlyRaining != session.isRaining()) {
-                    LevelEventPacket changeRainPacket = new LevelEventPacket();
-                    changeRainPacket.setType(isCurrentlyRaining ? LevelEventType.START_RAINING : LevelEventType.STOP_RAINING);
-                    changeRainPacket.setData(Integer.MAX_VALUE); // Dunno what this does; used to be implemented with ThreadLocalRandom
-                    changeRainPacket.setPosition(Vector3f.ZERO);
-                    session.sendUpstreamPacket(changeRainPacket);
-                    session.setRaining(isCurrentlyRaining);
-                }
+                float rainStrength = ((RainStrengthValue) packet.getValue()).getStrength();
+                boolean isCurrentlyRaining = rainStrength > 0f;
+                LevelEventPacket changeRainPacket = new LevelEventPacket();
+                changeRainPacket.setType(isCurrentlyRaining ? LevelEventType.START_RAINING : LevelEventType.STOP_RAINING);
+                // This is the rain strength on LevelEventType.START_RAINING, but can be any value on LevelEventType.STOP_RAINING
+                changeRainPacket.setData((int) (rainStrength * MAX_STORM_STRENGTH));
+                changeRainPacket.setPosition(Vector3f.ZERO);
+                session.sendUpstreamPacket(changeRainPacket);
+                session.setRaining(isCurrentlyRaining);
                 break;
             case THUNDER_STRENGTH:
                 // See above, same process
-                ThunderStrengthValue thunderValue = (ThunderStrengthValue) packet.getValue();
-                boolean isCurrentlyThundering = thunderValue.getStrength() > 0f;
-                if (isCurrentlyThundering != session.isThunder()) {
-                    LevelEventPacket changeThunderPacket = new LevelEventPacket();
-                    changeThunderPacket.setType(isCurrentlyThundering ? LevelEventType.START_THUNDERSTORM : LevelEventType.STOP_THUNDERSTORM);
-                    changeThunderPacket.setData(Integer.MAX_VALUE);
-                    changeThunderPacket.setPosition(Vector3f.ZERO);
-                    session.sendUpstreamPacket(changeThunderPacket);
-                    session.setThunder(isCurrentlyThundering);
-                }
+                float thunderStrength = ((ThunderStrengthValue) packet.getValue()).getStrength();
+                boolean isCurrentlyThundering = thunderStrength > 0f;
+                LevelEventPacket changeThunderPacket = new LevelEventPacket();
+                changeThunderPacket.setType(isCurrentlyThundering ? LevelEventType.START_THUNDERSTORM : LevelEventType.STOP_THUNDERSTORM);
+                changeThunderPacket.setData((int) (thunderStrength * MAX_STORM_STRENGTH));
+                changeThunderPacket.setPosition(Vector3f.ZERO);
+                session.sendUpstreamPacket(changeThunderPacket);
+                session.setThunder(isCurrentlyThundering);
                 break;
             case CHANGE_GAMEMODE:
                 GameMode gameMode = (GameMode) packet.getValue();