diff --git a/game/client/src/main/java/net/betterthanadventure/utils/DataDumper.java b/game/client/src/main/java/net/betterthanadventure/utils/DataDumper.java
index e989e6a4a..5e06c9c25 100644
--- a/game/client/src/main/java/net/betterthanadventure/utils/DataDumper.java
+++ b/game/client/src/main/java/net/betterthanadventure/utils/DataDumper.java
@@ -15,6 +15,7 @@ import net.minecraft.core.entity.SpawnListEntry;
 import net.minecraft.core.enums.MobCategory;
 import net.minecraft.core.item.ItemFood;
 import net.minecraft.core.item.Item;
+import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.item.Items;
 import net.minecraft.core.item.tag.ItemTags;
 import net.minecraft.core.item.tool.ItemToolPickaxe;
@@ -117,7 +118,7 @@ public class DataDumper implements MinecraftAccessor {
                 .append(item.id).append(",")
                 .append(item.getKey()).append(",")
                 .append(item.getMaxDamage()).append(",")
-                .append(item instanceof ItemFood ? ((ItemFood) item).getHealAmount() : "").append(",")
+                .append(item instanceof ItemFood ? ((ItemFood) item).getHealAmount(new ItemStack(item)) : "").append(",")
                 .append(item.getItemStackLimit(null)).append(",")
                 .append("\"").append(translator.translateKey(item.getKey() + ".name")).append("\"").append(",")
                 .append("\"").append(translator.translateKey(item.getKey() + ".desc")).append("\"").append(",");
diff --git a/game/client/src/main/java/net/minecraft/client/gui/container/ScreenContainerAbstract.java b/game/client/src/main/java/net/minecraft/client/gui/container/ScreenContainerAbstract.java
index 224ca0a73..dcd58660a 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/container/ScreenContainerAbstract.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/container/ScreenContainerAbstract.java
@@ -432,7 +432,7 @@ public abstract class ScreenContainerAbstract extends Screen {
                     break;
                 }
             }
-            isFuel = LookupFuelFurnace.instance.getFuelYield(clickedItemId) > 0;
+            isFuel = LookupFuelFurnace.instance.getFuelYield(stackInSlot) > 0;
 
             if (isIngredient) {
                 target = 1; // Into the ingredient slot
@@ -453,7 +453,7 @@ public abstract class ScreenContainerAbstract extends Screen {
                     break;
                 }
             }
-            isFuel = LookupFuelFurnaceBlast.instance.getFuelYield(clickedItemId) > 0;
+            isFuel = LookupFuelFurnaceBlast.instance.getFuelYield(stackInSlot) > 0;
 
             if (isIngredient) {
                 target = 1; // Into the ingredient slot
@@ -462,7 +462,7 @@ public abstract class ScreenContainerAbstract extends Screen {
             }
         }
         if (this.inventorySlots instanceof MenuTrommel) {
-            if (LookupFuelFurnace.instance.getFuelYield(clickedItemId) > 0) {
+            if (LookupFuelFurnace.instance.getFuelYield(stackInSlot) > 0) {
                 target = 2; // Into the fuel slot
             } else {
                 target = 1; // Into the item slots
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentHealthBar.java b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentHealthBar.java
index e35788124..a30230736 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentHealthBar.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentHealthBar.java
@@ -9,11 +9,10 @@ import net.minecraft.client.render.renderer.State;
 import net.minecraft.client.render.texture.stitcher.TextureRegistry;
 import net.minecraft.core.block.Block;
 import net.minecraft.core.block.BlockLogicEdible;
-import net.minecraft.core.item.Item;
-import net.minecraft.core.item.ItemFood;
-import net.minecraft.core.item.ItemStack;
+import net.minecraft.core.item.*;
 import net.minecraft.core.player.gamemode.Gamemodes;
 import net.minecraft.core.util.phys.HitResult;
+import org.lwjgl.opengl.GL11;
 
 import java.util.Random;
 
@@ -70,106 +69,91 @@ public class HudComponentHealthBar extends HudComponentMovable {
         int x = this.getLayout().getComponentX(this, xSizeScreen);
         int y = this.getLayout().getComponentY(this, ySizeScreen);
 
-        // Setup rendering
         GLRenderer.setColor4f(1.0f, 1.0f, 1.0f, 1.0f);
         GLRenderer.enableState(State.BLEND);
 
         boolean heartsFlash = (mc.thePlayer.heartsFlashTime / 3) % 2 == 1;
-        if (mc.thePlayer.heartsFlashTime < 10) {
+        if (mc.thePlayer.heartsFlashTime < 10)
+        {
             heartsFlash = false;
         }
 
         int health = mc.thePlayer.getHealth();
         int prevHealth = mc.thePlayer.prevHealth;
-        this.random.setSeed(hud.updateCounter * 0x4c627L);
+        random.setSeed(hud.updateCounter * 0x4c627L);
         boolean isHardcore = mc.thePlayer.getGamemode() == Gamemodes.HARDCORE;
 
         boolean isVertical = this.isVertical();
         boolean isFlipped = this.isFlipped();
-        
+
         String styleFolder = isHardcore ? "hardcore/" : "survival/";
-        
-        String halfSuffix = "";
-        if (isVertical) {
-            halfSuffix = isFlipped ? "_vertical_flipped" : "_vertical";
-        } else if (isFlipped) {
-            halfSuffix = "_flipped";
-        }
+        String halfSuffix = isVertical ? (isFlipped ? "_vertical_flipped" : "_vertical") : (isFlipped ? "_flipped" : "");
 
         for (int i = 0; i < 10; i++) {
-            int heartOffset = 0;
-            if (heartsFlash) {
-                heartOffset = 1;
-            }
+            int heartOffset = heartsFlash ? 1 : 0;
+            int xHeart = x;
+            int yHeart = y;
 
-            int xHeart;
-            int yHeart;
-
-            // Coordinate logic applying both vertical and flipped states
             if (isVertical) {
-                xHeart = x;
-                yHeart = isFlipped ? (y + i * 8) : (y + (9 - i) * 8); 
-                if (health <= 4) {
-                    xHeart += this.random.nextInt(2);  // Shake horizontally
-                }
+                yHeart = isFlipped ? (y + i * 8) : (y + (9 - i) * 8);
+                if (health <= 4) xHeart += this.random.nextInt(2);
             } else {
-                xHeart = isFlipped ? (x + (9 - i) * 8) : (x + i * 8); 
-                yHeart = y;
-                if (health <= 4) {
-                    yHeart += this.random.nextInt(2); // Shake vertically
-                }
+                xHeart = isFlipped ? (x + (9 - i) * 8) : (x + i * 8);
+                if (health <= 4) yHeart += random.nextInt(2);
             }
-            
-            hud.drawGuiIcon(xHeart, yHeart, 9, 9, heartOffset == 0 ? TextureRegistry.getTexture("minecraft:gui/hud/heart/container") : TextureRegistry.getTexture("minecraft:gui/hud/heart/container_blinking"));
 
-            // Flashing Damage/Heal Overlay
+            hud.drawGuiIcon(xHeart, yHeart, 9, 9, heartOffset == 0 ?
+                    TextureRegistry.getTexture("minecraft:gui/hud/heart/container") :
+                    TextureRegistry.getTexture("minecraft:gui/hud/heart/container_blinking"));
+
             if (heartsFlash) {
                 if (i * 2 + 1 < prevHealth) {
                     hud.drawGuiIcon(xHeart, yHeart, 9, 9, TextureRegistry.getTexture("minecraft:gui/hud/heart/" + styleFolder + "full_blinking"));
-                }
-                if (i * 2 + 1 == prevHealth) {
+                } else if (i * 2 + 1 == prevHealth) {
                     hud.drawGuiIcon(xHeart, yHeart, 9, 9, TextureRegistry.getTexture("minecraft:gui/hud/heart/" + styleFolder + "half_blinking" + halfSuffix));
                 }
             }
 
-            // Base Health
             if (i * 2 + 1 < health) {
                 hud.drawGuiIcon(xHeart, yHeart, 9, 9, TextureRegistry.getTexture("minecraft:gui/hud/heart/" + styleFolder + "full"));
-            }
-            if (i * 2 + 1 == health) {
+            } else if (i * 2 + 1 == health) {
                 hud.drawGuiIcon(xHeart, yHeart, 9, 9, TextureRegistry.getTexture("minecraft:gui/hud/heart/" + styleFolder + "half" + halfSuffix));
             }
 
-            // Food Regen Overlay
             if (GameSettings.FOOD_HEALTH_REGEN_OVERLAY.value) {
-                int healing = 0;
-                ItemStack held = mc.thePlayer.getHeldItem();
-                if (held != null) {
-                    Item heldItem = held.getItem();
-                    if (heldItem instanceof ItemFood) {
-                        healing = ((ItemFood) heldItem).getHealAmount();
-                    }
-                }
-                if (mc.objectMouseOver instanceof HitResult.Tile hitTile) {
-                    Block<?> b = mc.currentWorld.getBlockType(hitTile.tilePos);
-                    if (Block.getLogicClass(b) instanceof BlockLogicEdible edible) {
-                        healing = edible.getHealAmount(mc.currentWorld, hitTile.tilePos);
+                int healing = getPotentialHealing();
+                if (healing > 0 && i * 2 + 1 >= health) {
+                    if (i * 2 + 1 == health) {
+                        hud.drawGuiIcon(xHeart, yHeart, 9, 9, TextureRegistry.getTexture("minecraft:gui/hud/heart/" + styleFolder + "preview/half_right" + halfSuffix));
+                    } else if (i * 2 + 1 < health + healing) {
+                        hud.drawGuiIcon(xHeart, yHeart, 9, 9, TextureRegistry.getTexture("minecraft:gui/hud/heart/" + styleFolder + "preview/full"));
+                    } else if (i * 2 + 1 == health + healing) {
+                        hud.drawGuiIcon(xHeart, yHeart, 9, 9, TextureRegistry.getTexture("minecraft:gui/hud/heart/" + styleFolder + "preview/half" + halfSuffix));
                     }
                 }
+            }
+        }
+        GLRenderer.disableState(State.BLEND);
+    }
 
-                if (healing > 0) {
-                    if (i * 2 + 1 >= health) {
-                        if (i * 2 + 1 == health) {
-                            hud.drawGuiIcon(xHeart, yHeart, 9, 9, TextureRegistry.getTexture("minecraft:gui/hud/heart/" + styleFolder + "preview/half_right" + halfSuffix));
-                        } else if (i * 2 + 1 < health + healing) {
-                            hud.drawGuiIcon(xHeart, yHeart, 9, 9, TextureRegistry.getTexture("minecraft:gui/hud/heart/" + styleFolder + "preview/full"));
-                        } else if (i * 2 + 1 == health + healing) {
-                            hud.drawGuiIcon(xHeart, yHeart, 9, 9, TextureRegistry.getTexture("minecraft:gui/hud/heart/" + styleFolder + "preview/half" + halfSuffix));
-                        }
-                    }
-                }
+    private int getPotentialHealing() {
+        int healing = 0;
+
+        ItemStack held = mc.thePlayer.getHeldItem();
+
+        if (held != null && held.getItem() instanceof ItemFood food) {
+            healing = food.getHealAmount(held);
+        }
+
+        if (mc.objectMouseOver instanceof HitResult.Tile hitTile) {
+            Block<?> block = mc.currentWorld.getBlockType(hitTile.tilePos);
+
+            if (Block.getLogicClass(block) instanceof BlockLogicEdible edible) {
+                healing = edible.getHealAmount(mc.currentWorld, hitTile.tilePos);
             }
         }
+
+        return healing;
     }
 
     @Override
@@ -206,10 +190,10 @@ public class HudComponentHealthBar extends HudComponentMovable {
                 yHeart = y;
             }
 
-            // Container 
+            // Container
             gui.drawGuiIcon(xHeart, yHeart, 9, 9, TextureRegistry.getTexture("minecraft:gui/hud/heart/container"));
-
-            if (i * 2 + 1 < health) {
+            if (i * 2 + 1 < health)
+            {
                 gui.drawGuiIcon(xHeart, yHeart, 9, 9, TextureRegistry.getTexture("minecraft:gui/hud/heart/" + styleFolder + "full"));
             }
             if (i * 2 + 1 == health) {
diff --git a/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java b/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java
index 0209477d9..e140ae371 100644
--- a/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java
+++ b/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java
@@ -31,7 +31,6 @@ import net.minecraft.client.render.texture.stitcher.TextureRegistry;
 import net.minecraft.client.render.worldtype.WorldTypeFX;
 import net.minecraft.client.render.worldtype.WorldTypeFXDispatcher;
 import net.minecraft.client.world.WorldClient;
-import net.minecraft.core.Global;
 import net.minecraft.core.block.Block;
 import net.minecraft.core.block.Blocks;
 import net.minecraft.core.block.entity.TileEntity;
@@ -1945,9 +1944,59 @@ public class RenderGlobal
             case EVENT_PISTON_RETRACT: // Piston retract
                 this.world.playSoundEffect(null, SoundCategory.WORLD_SOUNDS, (double) x + 0.5F, (double) y + 0.5F, (double) z + 0.5F, "tile.piston.in", 0.5F, this.world.rand.nextFloat() * 0.25F + 0.6F);
                 break;
-            default:
+            case EVENT_CONDUIT_PORT_PARTICLES: // Conduit port open particles
+                Side s1 = unpackEventConduitSide(data);
+                int xO = s1.getOffsetX();
+                int yO = s1.getOffsetY();
+                int zO = s1.getOffsetZ();
+                double xP = (double) x + (double) xO * 0.6D + 0.5D;
+                double yP = (double) y + (double) yO * 0.6D + 0.5D;
+                double zP = (double) z + (double) zO * 0.6D + 0.5D;
+                for (int i = 0; i < 2; i++) {
+                    double rO = random.nextDouble() * 0.2D + 0.01D;
+                    double fx = xP + (double) xO * 0.01D + (random.nextDouble() - 0.5D) * (double) zO * 0.5D;
+                    double fy = yP + (double) yO * 0.01D + (random.nextDouble() - 0.5D) * (double) yO * 0.5D;
+                    double fz = zP + (double) zO * 0.01D + (random.nextDouble() - 0.5D) * (double) xO * 0.5D;
+                    double mx = (double) xO * rO;
+                    double my = (double) yO * rO;
+                    double mz = (double) zO * rO;
+                    addParticle("reddust", fx, fy, fz, mx, my, mz, unpackEventConduitStrength(data), false);
+                }
+
                 break;
+            case EVENT_CONDUIT_SURFACE_PARTICLES: //conduit ambient particles
+                int surfaceStrength = unpackEventConduitStrength(data);
+
+                if (surfaceStrength <= 0) {
+                    break;
+                }
+
+                double d = 0.0625;
+
+                Direction dir = Direction.directions[random.nextInt(Direction.directions.length)];
+
+                double lx = (double) x + random.nextFloat();
+                double ly = (double) y + random.nextFloat();
+                double lz = (double) z + random.nextFloat();
+
+                if (!this.world.isBlockOpaqueCube(x + dir.getOffsetX(), y + dir.getOffsetY(), z + dir.getOffsetZ())) {
+                    switch (dir) {
+                        case UP -> ly = (y + 1) + d;
+                        case DOWN -> ly = (y) - d;
+                        case SOUTH -> lz = (z + 1) + d;
+                        case NORTH -> lz = (z) - d;
+                        case EAST -> lx = (x + 1) + d;
+                        case WEST -> lx = (x) - d;
+                    }
+                }
+
+                if (lx < x || lx > (x + 1) || ly < 0.0D || ly > (y + 1) || lz < z || lz > (z + 1)) {
+                    addParticle("reddust", lx, ly, lz, 0, 0, 0, surfaceStrength, false);
+                }
 
+                break;
+            default:
+                break;
         }
     }
 
@@ -1959,4 +2008,13 @@ public class RenderGlobal
     public void entityRemoved(Entity entity) {
     }
 
+    public static Side unpackEventConduitSide(int packed) {
+        int sideId = (packed >> 4) & 0x07;
+        return Side.getSideById(sideId);
+    }
+
+    public static int unpackEventConduitStrength(int packed) {
+        return packed & 0x0F;
+    }
+
 }
diff --git a/game/client/src/main/java/net/minecraft/client/render/TileEntityRenderDispatcher.java b/game/client/src/main/java/net/minecraft/client/render/TileEntityRenderDispatcher.java
index 84a74fbc6..235f6d164 100644
--- a/game/client/src/main/java/net/minecraft/client/render/TileEntityRenderDispatcher.java
+++ b/game/client/src/main/java/net/minecraft/client/render/TileEntityRenderDispatcher.java
@@ -62,6 +62,7 @@ public final class TileEntityRenderDispatcher {
     }
 
     @SuppressWarnings("unchecked") // trust me this is safe, Notch did it first
+    // Instills me with much confidence :wolfblock: ^
     public <T extends TileEntity> TileEntityRenderer<T> getRenderer(final Class<? extends TileEntity> e) {
         TileEntityRenderer<? extends TileEntity> r = this.renderers.get(e);
         if (r == null && e != TileEntity.class) {
diff --git a/game/client/src/main/java/net/minecraft/client/render/block/color/BlockColorDispatcher.java b/game/client/src/main/java/net/minecraft/client/render/block/color/BlockColorDispatcher.java
index 14f8493b7..023ec10ad 100644
--- a/game/client/src/main/java/net/minecraft/client/render/block/color/BlockColorDispatcher.java
+++ b/game/client/src/main/java/net/minecraft/client/render/block/color/BlockColorDispatcher.java
@@ -48,6 +48,7 @@ public final class BlockColorDispatcher
         addDispatch(Blocks.WIRE_REDSTONE, new BlockColorWireRedstone());
         addDispatch(Blocks.TALLGRASS, new BlockColorCustom(Colorizers.grass));
         addDispatch(Blocks.TALLGRASS_FERN, new BlockColorCustom(Colorizers.grass));
+        addDispatch(Blocks.RUBYGLASS_CONDUIT, new BlockColorRubyglassConduit());
     }
 
 
diff --git a/game/client/src/main/java/net/minecraft/client/render/block/color/BlockColorRubyglassConduit.java b/game/client/src/main/java/net/minecraft/client/render/block/color/BlockColorRubyglassConduit.java
new file mode 100644
index 000000000..679cad0ca
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/block/color/BlockColorRubyglassConduit.java
@@ -0,0 +1,63 @@
+package net.minecraft.client.render.block.color;
+
+import com.mojang.logging.LogUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.core.block.BlockLogicRubyglassConduit;
+import net.minecraft.core.world.WorldSource;
+import net.minecraft.core.world.pos.TilePosc;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.InputStream;
+
+public class BlockColorRubyglassConduit extends BlockColor {
+
+    private static final org.slf4j.Logger LOGGER = LogUtils.getLogger();
+    private final int[] powerColors = new int[11];
+
+    public BlockColorRubyglassConduit() {
+        loadPalette();
+    }
+
+    private void loadPalette() {
+        boolean loadedSuccessfully = false;
+        String palettePath = "/assets/minecraft/textures/misc/colors_conduit.png";
+
+        try (InputStream stream = Minecraft.getMinecraft().texturePackList.getResourceAsStream(palettePath)) {
+            if (stream != null) {
+                BufferedImage image = ImageIO.read(stream);
+
+                for (int i = 0; i < 11; i++) {
+                    int x = Math.min(i, image.getWidth() - 1);
+                    powerColors[i] = image.getRGB(x, 0) & 0xFFFFFF;
+                }
+                loadedSuccessfully = true;
+            }
+        } catch (Exception e) {
+            LOGGER.error("Failed to load conduit palette at {}", palettePath, e);
+        }
+
+        if (!loadedSuccessfully) {
+            powerColors[0] = 0x330000;
+            int minRed = 80;
+            int maxRed = 255;
+            for (int strength = 1; strength <= 10; strength++) {
+                int redValue = minRed + (int)(((strength - 1) / 9.0f) * (maxRed - minRed));
+                powerColors[strength] = (redValue << 16);
+            }
+        }
+    }
+
+    @Override
+    public int getFallbackColor(int meta, int tintIndex) {
+        return powerColors[0];
+    }
+
+    @Override
+    public int getWorldColor(WorldSource world, TilePosc pos, int tintIndex) {
+        int data = world.getBlockData(pos);
+        int strength = data / BlockLogicRubyglassConduit.STATE_COUNT;
+        strength = Math.max(0, Math.min(10, strength));
+        return powerColors[strength];
+    }
+}
\ No newline at end of file
diff --git a/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelDispatcher.java b/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelDispatcher.java
index d451b9d60..af8a9e2a4 100644
--- a/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelDispatcher.java
+++ b/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelDispatcher.java
@@ -7,7 +7,6 @@ import net.minecraft.client.render.texture.stitcher.IconCoordinate;
 import net.minecraft.client.util.dispatch.Dispatcher;
 import net.minecraft.core.block.Block;
 import net.minecraft.core.block.Blocks;
-import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.world.World;
 import org.jetbrains.annotations.NotNull;
 import org.slf4j.Logger;
@@ -348,7 +347,7 @@ public final class BlockModelDispatcher
         addDispatch(new BlockModelGeneric<>(Blocks.RUBYGLASS_NODE, loadDataModel("minecraft:block/rubyglass/node")));
         addDispatch(new BlockModelGeneric<>(Blocks.BLOCK_RUBYGLASS, loadDataModel("minecraft:block/rubyglass/block")).forceCullSelf(true));
         addDispatch(new BlockModelCrystalBud<>(Blocks.RUBYGLASS_SPROUT, loadDataModel("minecraft:block/rubyglass/sprout")).render3D(false));
-        addDispatch(new BlockModelGeneric<>(Blocks.RUBYGLASS_CIRCUIT, loadDataModel("minecraft:block/rubyglass/circuit")));
+        addDispatch(new BlockModelRubyglassConduit<>(Blocks.RUBYGLASS_CONDUIT));
 
         addDispatch(new BlockModelGeneric<>(Blocks.ORE_NETHERCOAL_NETHERRACK, loadDataModel("minecraft:block/ore/nethercoal/netherrack")));
         addDispatch(new BlockModelGeneric<>(Blocks.ORE_NETHERCOAL_BASALT, loadDataModel("minecraft:block/ore/nethercoal/basalt")));
diff --git a/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelRubyglassConduit.java b/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelRubyglassConduit.java
new file mode 100644
index 000000000..5faddc642
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelRubyglassConduit.java
@@ -0,0 +1,101 @@
+package net.minecraft.client.render.block.model;
+
+import net.minecraft.client.render.block.model.generic.BlockModelGeneric;
+import net.minecraft.client.render.tessellator.TessellatorGeneral;
+import net.minecraft.client.render.texture.stitcher.IconCoordinate;
+import net.minecraft.core.block.Block;
+import net.minecraft.core.block.BlockLogicRubyglassConduit;
+import net.minecraft.core.world.WorldSource;
+import net.minecraft.core.world.pos.TilePosc;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.useless.dragonfly.models.block.StaticBlockModel;
+
+public class BlockModelRubyglassConduit<T extends BlockLogicRubyglassConduit> extends BlockModelGeneric<T> {
+
+    private record ModelState(StaticBlockModel model, int rotX, int rotY, int rotZ) { }
+
+    private final ModelState[] stateMapOn = new ModelState[BlockLogicRubyglassConduit.STATE_COUNT];
+    private final ModelState[] stateMapOff = new ModelState[BlockLogicRubyglassConduit.STATE_COUNT];
+
+    public BlockModelRubyglassConduit(Block<T> block) {
+        super(block, BlockModelDispatcher.loadDataModel("minecraft:block/rubyglass/conduit/conduit_core").asModel());
+
+        StaticBlockModel onCore = BlockModelDispatcher.loadDataModel("minecraft:block/rubyglass/conduit/conduit_core").asModel();
+        StaticBlockModel onCap = BlockModelDispatcher.loadDataModel("minecraft:block/rubyglass/conduit/conduit_cap").asModel();
+        StaticBlockModel onStraight = BlockModelDispatcher.loadDataModel("minecraft:block/rubyglass/conduit/conduit_straight").asModel();
+        StaticBlockModel onCorner = BlockModelDispatcher.loadDataModel("minecraft:block/rubyglass/conduit/conduit_corner").asModel();
+
+        StaticBlockModel offCore = BlockModelDispatcher.loadDataModel("minecraft:block/rubyglass/conduit/conduit_core_off").asModel();
+        StaticBlockModel offCap = BlockModelDispatcher.loadDataModel("minecraft:block/rubyglass/conduit/conduit_cap_off").asModel();
+        StaticBlockModel offStraight = BlockModelDispatcher.loadDataModel("minecraft:block/rubyglass/conduit/conduit_straight_off").asModel();
+        StaticBlockModel offCorner = BlockModelDispatcher.loadDataModel("minecraft:block/rubyglass/conduit/conduit_corner_off").asModel();
+
+        populateStateMap(stateMapOn, onCore, onCap, onStraight, onCorner);
+        populateStateMap(stateMapOff, offCore, offCap, offStraight, offCorner);
+    }
+
+    private void populateStateMap(ModelState[] map, StaticBlockModel core, StaticBlockModel cap, StaticBlockModel straight, StaticBlockModel corner) {
+        // 0: Core
+        map[0] = new ModelState(core, 0, 0, 0);
+
+        // 1-6: End Caps
+        map[1] = new ModelState(cap, 0, 0, 0);
+        map[2] = new ModelState(cap, 2, 0, 0);
+        map[3] = new ModelState(cap, 1, 0, 0);
+        map[4] = new ModelState(cap, 3, 0, 0);
+        map[5] = new ModelState(cap, 0, 0, 3);
+        map[6] = new ModelState(cap, 0, 0, 1);
+
+        // 7-9: Straights
+        map[7] = new ModelState(straight, 0, 0, 0);
+        map[8] = new ModelState(straight, 1, 0, 0);
+        map[9] = new ModelState(straight, 0, 0, 1);
+
+        // 10-13: Bottom Corners
+        map[10] = new ModelState(corner, 0, 0, 0);
+        map[11] = new ModelState(corner, 0, 2, 0);
+        map[12] = new ModelState(corner, 0, 1, 0);
+        map[13] = new ModelState(corner, 0, 3, 0);
+
+        // 14-17: Top Corners
+        map[14] = new ModelState(corner, 2, 2, 0);
+        map[15] = new ModelState(corner, 2, 0, 0);
+        map[16] = new ModelState(corner, 2, 3, 0);
+        map[17] = new ModelState(corner, 2, 1, 0);
+
+        // 18-21: Horizontal Corners
+        map[18] = new ModelState(corner, 0, 0, 3);
+        map[19] = new ModelState(corner, 0, 0, 1);
+        map[20] = new ModelState(corner, 0, 2, 3);
+        map[21] = new ModelState(corner, 0, 2, 1);
+    }
+
+    @Override
+    public @NotNull StaticBlockModel getModelFromData(int data) {
+        int state = data % BlockLogicRubyglassConduit.STATE_COUNT;
+        int strength = data / BlockLogicRubyglassConduit.STATE_COUNT;
+
+        if (strength == 0) {
+            return stateMapOff[state].model;
+        } else {
+            return stateMapOn[state].model;
+        }
+    }
+
+    @Override
+    public boolean renderAttached(final @NotNull TessellatorGeneral tessellator, @NotNull final WorldSource worldSource, @NotNull TilePosc tilePos, boolean cullFaces, final @Nullable IconCoordinate overrideTexture) {
+        int data = worldSource.getBlockData(tilePos);
+        int state = data % BlockLogicRubyglassConduit.STATE_COUNT;
+        int strength = data / BlockLogicRubyglassConduit.STATE_COUNT;
+
+        ModelState activeState;
+        if (strength == 0) {
+            activeState = stateMapOff[state];
+        } else {
+            activeState = stateMapOn[state];
+        }
+
+        return activeState.model.renderAttached(this, tessellator, worldSource, tilePos, activeState.rotX, activeState.rotY, activeState.rotZ, 0, 0, 0, false, cullFaces, overrideTexture);
+    }
+}
\ No newline at end of file
diff --git a/game/client/src/main/java/net/minecraft/client/render/block/model/RenderBlocks.java b/game/client/src/main/java/net/minecraft/client/render/block/model/RenderBlocks.java
index f29dc48b6..81a98d504 100644
--- a/game/client/src/main/java/net/minecraft/client/render/block/model/RenderBlocks.java
+++ b/game/client/src/main/java/net/minecraft/client/render/block/model/RenderBlocks.java
@@ -21,7 +21,8 @@ import static net.minecraft.client.render.block.model.BlockModel.FULL_CUBE_THRES
 
 public final class RenderBlocks {
     public final @NotNull Minecraft mc;
-    public IconCoordinate overrideBlockTexture;
+    public @Nullable IconCoordinate overrideBlockTexture;
+    public @Nullable Integer overrideLightmap;
     public boolean flipTexture;
     public boolean renderAllFaces;
     public byte renderBitMask;
@@ -57,6 +58,7 @@ public final class RenderBlocks {
 
     public RenderBlocks() {
         this.overrideBlockTexture = null;
+        this.overrideLightmap = null;
         this.flipTexture = false;
         this.renderAllFaces = false;
         this.useInventoryTint = true;
@@ -77,6 +79,7 @@ public final class RenderBlocks {
         this.useInventoryTint = true;
         this.flipTexture = false;
         this.overrideBlockTexture = null;
+        this.overrideLightmap = null;
         this.uvRotateEast = 0;
         this.uvRotateWest = 0;
         this.uvRotateSouth = 0;
@@ -248,54 +251,62 @@ public final class RenderBlocks {
                               int dirX, int dirY, int dirZ, float depth,
                               int topX, int topY, int topZ, float topP, float botP,
                               int lefX, int lefY, int lefZ, float lefP, float rigP) {
+        this.colorRedTopLeft = this.colorRedBottomLeft = this.colorRedBottomRight = this.colorRedTopRight = r;
+        this.colorGreenTopLeft = this.colorGreenBottomLeft = this.colorGreenBottomRight = this.colorGreenTopRight = g;
+        this.colorBlueTopLeft = this.colorBlueBottomLeft = this.colorBlueBottomRight = this.colorBlueTopRight = b;
+
         boolean ao = GameSettings.AMBIENT_OCCLUSION.value && block.emission == 0;
         boolean isFullCube = depth <= FULL_CUBE_THRESHOLD;
 
-        if (ao) {
-            int dirX2 = dirX;
-            int dirY2 = dirY;
-            int dirZ2 = dirZ;
-
-            if (!isFullCube) {
-                dirX2 = 0;
-                dirY2 = 0;
-                dirZ2 = 0;
-            }
-
-            boolean topT = this.cache.getOpacity(dirX2 + topX, dirY2 + topY, dirZ2 + topZ);
-            boolean botT = this.cache.getOpacity(dirX2 - topX, dirY2 - topY, dirZ2 - topZ);
-            boolean lefT = this.cache.getOpacity(dirX2 + lefX, dirY2 + lefY, dirZ2 + lefZ);
-            boolean rigT = this.cache.getOpacity(dirX2 - lefX, dirY2 - lefY, dirZ2 - lefZ);
-
-            boolean topLefT = this.cache.getOpacity(dirX2 + topX + lefX, dirY2 + topY + lefY, dirZ2 + topZ + lefZ);
-            boolean topRigT = this.cache.getOpacity(dirX2 + topX - lefX, dirY2 + topY - lefY, dirZ2 + topZ - lefZ);
-            boolean botLefT = this.cache.getOpacity(dirX2 - topX + lefX, dirY2 - topY + lefY, dirZ2 - topZ + lefZ);
-            boolean botRigT = this.cache.getOpacity(dirX2 - topX - lefX, dirY2 - topY - lefY, dirZ2 - topZ - lefZ);
-
-            byte lmcCen = this.cache.getLightIndex(dirX2, dirY2, dirZ2);
-
-            byte lmcTop = topT ? lmcCen : this.cache.getLightIndex(dirX2 + topX, dirY2 + topY, dirZ2 + topZ);
-            byte lmcBot = botT ? lmcCen : this.cache.getLightIndex(dirX2 - topX, dirY2 - topY, dirZ2 - topZ);
-            byte lmcLef = lefT ? lmcCen : this.cache.getLightIndex(dirX2 + lefX, dirY2 + lefY, dirZ2 + lefZ);
-            byte lmcRig = rigT ? lmcCen : this.cache.getLightIndex(dirX2 - lefX, dirY2 - lefY, dirZ2 - lefZ);
-
-            byte lmcTopLef = topT && lefT ? lmcLef : (topLefT ? lmcCen : this.cache.getLightIndex(dirX2 + topX + lefX, dirY2 + topY + lefY, dirZ2 + topZ + lefZ));
-            byte lmcBotLef = botT && lefT ? lmcLef : (botLefT ? lmcCen : this.cache.getLightIndex(dirX2 - topX + lefX, dirY2 - topY + lefY, dirZ2 - topZ + lefZ));
-            byte lmcTopRig = topT && rigT ? lmcRig : (topRigT ? lmcCen : this.cache.getLightIndex(dirX2 + topX - lefX, dirY2 + topY - lefY, dirZ2 + topZ - lefZ));
-            byte lmcBotRig = botT && rigT ? lmcRig : (botRigT ? lmcCen : this.cache.getLightIndex(dirX2 - topX - lefX, dirY2 - topY - lefY, dirZ2 - topZ - lefZ));
-
-            this.lightIndexTopLeft = LightIndexHelper.avg(lmcCen, lmcLef, lmcTop, lmcTopLef);
-            this.lightIndexTopRight = LightIndexHelper.avg(lmcCen, lmcRig, lmcTop, lmcTopRig);
-            this.lightIndexBottomLeft = LightIndexHelper.avg(lmcCen, lmcLef, lmcBot, lmcBotLef);
-            this.lightIndexBottomRight = LightIndexHelper.avg(lmcCen, lmcRig, lmcBot, lmcBotRig);
-        } else {
-            int lmc;
-            if (!isFullCube) {
-                lmc = block.getLightIndex(worldSource, tilePos);
+        if (this.overrideLightmap == null) {
+            if (ao) {
+                int dirX2 = dirX;
+                int dirY2 = dirY;
+                int dirZ2 = dirZ;
+
+                if (!isFullCube) {
+                    dirX2 = 0;
+                    dirY2 = 0;
+                    dirZ2 = 0;
+                }
+
+                boolean topT = this.cache.getOpacity(dirX2 + topX, dirY2 + topY, dirZ2 + topZ);
+                boolean botT = this.cache.getOpacity(dirX2 - topX, dirY2 - topY, dirZ2 - topZ);
+                boolean lefT = this.cache.getOpacity(dirX2 + lefX, dirY2 + lefY, dirZ2 + lefZ);
+                boolean rigT = this.cache.getOpacity(dirX2 - lefX, dirY2 - lefY, dirZ2 - lefZ);
+
+                boolean topLefT = this.cache.getOpacity(dirX2 + topX + lefX, dirY2 + topY + lefY, dirZ2 + topZ + lefZ);
+                boolean topRigT = this.cache.getOpacity(dirX2 + topX - lefX, dirY2 + topY - lefY, dirZ2 + topZ - lefZ);
+                boolean botLefT = this.cache.getOpacity(dirX2 - topX + lefX, dirY2 - topY + lefY, dirZ2 - topZ + lefZ);
+                boolean botRigT = this.cache.getOpacity(dirX2 - topX - lefX, dirY2 - topY - lefY, dirZ2 - topZ - lefZ);
+
+                byte lmcCen = this.cache.getLightIndex(dirX2, dirY2, dirZ2);
+
+                byte lmcTop = topT ? lmcCen : this.cache.getLightIndex(dirX2 + topX, dirY2 + topY, dirZ2 + topZ);
+                byte lmcBot = botT ? lmcCen : this.cache.getLightIndex(dirX2 - topX, dirY2 - topY, dirZ2 - topZ);
+                byte lmcLef = lefT ? lmcCen : this.cache.getLightIndex(dirX2 + lefX, dirY2 + lefY, dirZ2 + lefZ);
+                byte lmcRig = rigT ? lmcCen : this.cache.getLightIndex(dirX2 - lefX, dirY2 - lefY, dirZ2 - lefZ);
+
+                byte lmcTopLef = topT && lefT ? lmcLef : (topLefT ? lmcCen : this.cache.getLightIndex(dirX2 + topX + lefX, dirY2 + topY + lefY, dirZ2 + topZ + lefZ));
+                byte lmcBotLef = botT && lefT ? lmcLef : (botLefT ? lmcCen : this.cache.getLightIndex(dirX2 - topX + lefX, dirY2 - topY + lefY, dirZ2 - topZ + lefZ));
+                byte lmcTopRig = topT && rigT ? lmcRig : (topRigT ? lmcCen : this.cache.getLightIndex(dirX2 + topX - lefX, dirY2 + topY - lefY, dirZ2 + topZ - lefZ));
+                byte lmcBotRig = botT && rigT ? lmcRig : (botRigT ? lmcCen : this.cache.getLightIndex(dirX2 - topX - lefX, dirY2 - topY - lefY, dirZ2 - topZ - lefZ));
+
+                this.lightIndexTopLeft = LightIndexHelper.avg(lmcCen, lmcLef, lmcTop, lmcTopLef);
+                this.lightIndexTopRight = LightIndexHelper.avg(lmcCen, lmcRig, lmcTop, lmcTopRig);
+                this.lightIndexBottomLeft = LightIndexHelper.avg(lmcCen, lmcLef, lmcBot, lmcBotLef);
+                this.lightIndexBottomRight = LightIndexHelper.avg(lmcCen, lmcRig, lmcBot, lmcBotRig);
             } else {
-                lmc = block.getLightIndex(worldSource, tilePos.add(dirX, dirY, dirZ, new TilePos()));
+                int lmc;
+                if (!isFullCube) {
+                    lmc = block.getLightIndex(worldSource, tilePos);
+                } else {
+                    lmc = block.getLightIndex(worldSource, tilePos.add(dirX, dirY, dirZ, new TilePos()));
+                }
+                this.lightIndexTopLeft = this.lightIndexBottomLeft = this.lightIndexBottomRight = this.lightIndexTopRight = lmc;
             }
-            this.lightIndexTopLeft = this.lightIndexBottomLeft = this.lightIndexBottomRight = this.lightIndexTopRight = lmc;
+        } else {
+            this.lightIndexTopLeft = this.lightIndexBottomLeft = this.lightIndexBottomRight = this.lightIndexTopRight = this.overrideLightmap;
         }
 
         float lightTR;
@@ -357,9 +368,6 @@ public final class RenderBlocks {
             lightTL = lightBL = lightBR = lightTR = brightness;
         }
 
-        this.colorRedTopLeft = this.colorRedBottomLeft = this.colorRedBottomRight = this.colorRedTopRight = r;
-        this.colorGreenTopLeft = this.colorGreenBottomLeft = this.colorGreenBottomRight = this.colorGreenTopRight = g;
-        this.colorBlueTopLeft = this.colorBlueBottomLeft = this.colorBlueBottomRight = this.colorBlueTopRight = b;
         float tl = topP * lightTL + (1 - topP) * lightBL;
         float tr = topP * lightTR + (1 - topP) * lightBR;
         float bl = botP * lightTL + (1 - botP) * lightBL;
diff --git a/game/client/src/main/java/net/minecraft/client/render/dynamictexture/DynamicTextureCustom.java b/game/client/src/main/java/net/minecraft/client/render/dynamictexture/DynamicTextureCustom.java
index 1d87a4884..73a8af518 100644
--- a/game/client/src/main/java/net/minecraft/client/render/dynamictexture/DynamicTextureCustom.java
+++ b/game/client/src/main/java/net/minecraft/client/render/dynamictexture/DynamicTextureCustom.java
@@ -15,20 +15,20 @@ public class DynamicTextureCustom extends DynamicTexture {
 	private static final Logger LOGGER = LogUtils.getLogger();
 	private static final Random rand = new Random();
 	
-	private final int frameCount;
-	private final byte[][] frameData;
+	protected final int frameCount;
+	protected final byte[][] frameData;
 
-	private final AnimationProperties properties;
+    protected final AnimationProperties properties;
 
-	private int currentIndex = 0;
-	private final int indices;
+    protected int currentIndex = 0;
+    protected final int indices;
 
-	private int currentFrame = -1;
-	private int previousFrame = -1;
-	private int delayUntilNextFrame = 0;
-	private int frameLength = 0;
+    protected int currentFrame = -1;
+    protected int previousFrame = -1;
+    protected int delayUntilNextFrame = 0;
+    protected int frameLength = 0;
 
-	private final boolean tickWhenPaused;
+    protected final boolean tickWhenPaused;
 	
 	public DynamicTextureCustom(IconCoordinate texture, AnimationProperties animationProperties) {
 		super(texture);
diff --git a/game/client/src/main/java/net/minecraft/client/render/item/model/ItemModelBucket.java b/game/client/src/main/java/net/minecraft/client/render/item/model/ItemModelBucket.java
new file mode 100644
index 000000000..a0a6d2f40
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/item/model/ItemModelBucket.java
@@ -0,0 +1,69 @@
+package net.minecraft.client.render.item.model;
+
+import net.minecraft.client.render.texture.stitcher.IconCoordinate;
+import net.minecraft.client.render.texture.stitcher.TextureRegistry;
+import net.minecraft.core.entity.Entity;
+import net.minecraft.core.item.Item;
+import net.minecraft.core.item.ItemBucket;
+import net.minecraft.core.item.ItemStack;
+import net.minecraft.core.util.collection.NamespaceID;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public abstract class ItemModelBucket extends ItemModelStandard {
+
+    protected final Map<NamespaceID, IconCoordinate[]> bucketIcons = new HashMap<>();
+
+    public ItemModelBucket(Item item, String namespace) {
+        super(item, namespace);
+    }
+
+    protected void initializeIcons(String namespace, String prefix, int maxCharges, Set<NamespaceID> validStates) {
+        IconCoordinate[] emptyArray = new IconCoordinate[1];
+        emptyArray[0] = TextureRegistry.getTexture(namespace + ":item/" + prefix + "/empty");
+        this.bucketIcons.put(ItemBucket.STATE_EMPTY, emptyArray);
+
+        for (NamespaceID stateId : validStates) {
+            if (ItemBucket.STATE_EMPTY.equals(stateId)) continue;
+
+            String stateName = stateId.value();
+            IconCoordinate[] chargesArray = new IconCoordinate[maxCharges + 1];
+
+            for (int c = 1; c <= maxCharges; c++) {
+                String path = namespace + ":item/" + prefix + "/" + stateName + "_" + c;
+                chargesArray[c] = TextureRegistry.getTexture(path);
+            }
+            this.bucketIcons.put(stateId, chargesArray);
+        }
+    }
+
+    @Override
+    public @NotNull IconCoordinate getIcon(@Nullable Entity entity, ItemStack itemStack) {
+        NamespaceID state = ItemBucket.getState(itemStack);
+        int charges = ItemBucket.getCharges(itemStack);
+
+        int maxForThisBucket = 1;
+        if (itemStack.getItem() instanceof ItemBucket bucket) {
+            maxForThisBucket = bucket.maxCharges;
+        }
+
+        if (!ItemBucket.STATE_EMPTY.equals(state) && charges <= 0) {
+            long cycleTime = 620;
+            charges = (int) ((System.currentTimeMillis() / cycleTime) % maxForThisBucket) + 1;
+        }
+
+        charges = Math.max(0, Math.min(charges, maxForThisBucket));
+        IconCoordinate[] iconsForState = bucketIcons.get(state);
+
+        if (iconsForState == null || charges >= iconsForState.length) {
+            return super.getIcon(entity, itemStack);
+        }
+
+        IconCoordinate icon = iconsForState[charges];
+        return (icon != null) ? icon : super.getIcon(entity, itemStack);
+    }
+}
\ No newline at end of file
diff --git a/game/client/src/main/java/net/minecraft/client/render/item/model/ItemModelBucketIron.java b/game/client/src/main/java/net/minecraft/client/render/item/model/ItemModelBucketIron.java
new file mode 100644
index 000000000..0e6313688
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/item/model/ItemModelBucketIron.java
@@ -0,0 +1,20 @@
+package net.minecraft.client.render.item.model;
+
+import net.minecraft.core.item.Item;
+import net.minecraft.core.item.ItemBucket;
+import net.minecraft.core.util.collection.NamespaceID;
+
+import java.util.Set;
+
+public class ItemModelBucketIron extends ItemModelBucket {
+    public ItemModelBucketIron(Item item, String namespace) {
+        super(item, namespace);
+        Set<NamespaceID> validStates = Set.of(
+            ItemBucket.STATE_WATER,
+            ItemBucket.STATE_LAVA,
+            ItemBucket.STATE_MILK,
+            ItemBucket.STATE_ICECREAM
+        );
+        this.initializeIcons(namespace, "bucket_iron", 1, validStates);
+    }
+}
\ No newline at end of file
diff --git a/game/client/src/main/java/net/minecraft/client/render/item/model/ItemModelBucketSteel.java b/game/client/src/main/java/net/minecraft/client/render/item/model/ItemModelBucketSteel.java
new file mode 100644
index 000000000..dff72dccb
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/item/model/ItemModelBucketSteel.java
@@ -0,0 +1,21 @@
+package net.minecraft.client.render.item.model;
+
+import net.minecraft.core.item.Item;
+import net.minecraft.core.item.ItemBucket;
+import net.minecraft.core.util.collection.NamespaceID;
+
+import java.util.Set;
+
+public class ItemModelBucketSteel extends ItemModelBucket {
+    public ItemModelBucketSteel(Item item, String namespace) {
+        super(item, namespace);
+        Set<NamespaceID> validStates = Set.of(
+            ItemBucket.STATE_WATER,
+            ItemBucket.STATE_LAVA,
+            ItemBucket.STATE_MILK,
+            ItemBucket.STATE_ICECREAM
+        );
+
+        this.initializeIcons(namespace, "bucket_steel", 3, validStates);
+    }
+}
\ No newline at end of file
diff --git a/game/client/src/main/java/net/minecraft/client/render/item/model/ItemModelDispatcher.java b/game/client/src/main/java/net/minecraft/client/render/item/model/ItemModelDispatcher.java
index 8c5006024..0621b6019 100644
--- a/game/client/src/main/java/net/minecraft/client/render/item/model/ItemModelDispatcher.java
+++ b/game/client/src/main/java/net/minecraft/client/render/item/model/ItemModelDispatcher.java
@@ -241,9 +241,10 @@ public class ItemModelDispatcher extends Dispatcher<Item, ItemModel> {
         addDispatch(new ItemModelStandard(Items.DOOR_OAK, "minecraft"));
         addDispatch(new ItemModelStandard(Items.DOOR_GLASS, "minecraft"));
         addDispatch(new ItemModelDoorPainted(Items.DOOR_OAK_PAINTED));
-        addDispatch(new ItemModelStandard(Items.BUCKET, "minecraft"));
-        addDispatch(new ItemModelStandard(Items.BUCKET_WATER, "minecraft"));
-        addDispatch(new ItemModelStandard(Items.BUCKET_LAVA, "minecraft").setFullBright());
+        addDispatch(new ItemModelBucketIron(Items.BUCKET_IRON, "minecraft"));
+        addDispatch(new ItemModelBucketSteel(Items.BUCKET_STEEL, "minecraft"));
+      //addDispatch(new ItemModelStandard(Items.BUCKET_WATER, "minecraft"));
+      //addDispatch(new ItemModelStandard(Items.BUCKET_LAVA, "minecraft").setFullBright());
         addDispatch(new ItemModelStandard(Items.MINECART, "minecraft"));
         addDispatch(new ItemModelStandard(Items.SADDLE, "minecraft"));
         addDispatch(new ItemModelStandard(Items.DOOR_IRON, "minecraft"));
@@ -251,7 +252,7 @@ public class ItemModelDispatcher extends Dispatcher<Item, ItemModel> {
         addDispatch(new ItemModelStandard(Items.AMMO_SNOWBALL, "minecraft"));
         addDispatch(new ItemModelStandard(Items.BOAT, "minecraft"));
         addDispatch(new ItemModelStandard(Items.LEATHER, "minecraft"));
-        addDispatch(new ItemModelStandard(Items.BUCKET_MILK, "minecraft"));
+      //addDispatch(new ItemModelStandard(Items.BUCKET_MILK, "minecraft"));
         addDispatch(new ItemModelStandard(Items.BRICK_CLAY, "minecraft"));
         addDispatch(new ItemModelStandard(Items.CLAY, "minecraft"));
         addDispatch(new ItemModelStandard(Items.SUGARCANE, "minecraft"));
@@ -345,7 +346,7 @@ public class ItemModelDispatcher extends Dispatcher<Item, ItemModel> {
         addDispatch(new ItemModelStandard(Items.OLIVINE, "minecraft"));
         addDispatch(new ItemModelStandard(Items.AMMO_PEBBLE, "minecraft"));
         addDispatch(new ItemModelStandard(Items.FOOD_CHERRY, "minecraft"));
-        addDispatch(new ItemModelStandard(Items.BUCKET_ICECREAM, "minecraft"));
+      //addDispatch(new ItemModelStandard(Items.BUCKET_ICECREAM, "minecraft"));
         addDispatch(new ItemModelStandard(Items.AMMO_ARROW_PURPLE, "minecraft"));
         addDispatch(new ItemModelLabel(Items.LABEL, "minecraft"));
         addDispatch(new ItemModelStandard(Items.JAR, "minecraft"));
diff --git a/game/client/src/main/java/net/minecraft/client/render/texture/generator/TextureGeneratorRubyglassConduit.java b/game/client/src/main/java/net/minecraft/client/render/texture/generator/TextureGeneratorRubyglassConduit.java
new file mode 100644
index 000000000..fa494b0f2
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/texture/generator/TextureGeneratorRubyglassConduit.java
@@ -0,0 +1,81 @@
+package net.minecraft.client.render.texture.generator;
+
+import net.minecraft.client.Minecraft;
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.InputStream;
+
+public class TextureGeneratorRubyglassConduit {
+
+    private static int[] palette = null;
+
+    public static BufferedImage generateConduitTexture(String shape, int strength, String layer) {
+        loadPalette();
+        int color = palette[Math.max(0, Math.min(10, strength))];
+
+        String basePath = "/assets/minecraft/textures/block/rubyglass/conduit/" + shape + "_base.png";
+        String maskPath = "/assets/minecraft/textures/block/rubyglass/conduit/" + shape + "_mask.png";
+
+        try {
+            BufferedImage baseImg = loadImage(basePath);
+            BufferedImage maskImg = loadImage(maskPath);
+
+            if (baseImg == null || maskImg == null) return null;
+
+            int width = baseImg.getWidth();
+            int height = baseImg.getHeight();
+            BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+
+            for (int x = 0; x < width; x++) {
+                for (int y = 0; y < height; y++) {
+                    int basePixel = baseImg.getRGB(x, y);
+                    int maskPixel = maskImg.getRGB(x, y);
+
+                    boolean isRedstone = ((maskPixel >> 24) & 0xFF) > 128 && ((maskPixel >> 16) & 0xFF) > 128;
+
+                    if (layer.equals("emissive")) {
+                        if (isRedstone) {
+                            result.setRGB(x, y, (0xFF << 24) | color);
+                        } else {
+                            result.setRGB(x, y, 0x00000000);
+                        }
+                    } else {
+                        if (isRedstone) {
+                            result.setRGB(x, y, (0xFF << 24) | color);
+                        } else {
+                            result.setRGB(x, y, basePixel);
+                        }
+                    }
+                }
+            }
+            return result;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    private static BufferedImage loadImage(String path) {
+        try (InputStream stream = Minecraft.getMinecraft().texturePackList.getResourceAsStream(path)) {
+            if (stream != null) return ImageIO.read(stream);
+        } catch (Exception ignored) {}
+        return null;
+    }
+
+    private static void loadPalette() {
+        if (palette != null) return;
+        palette = new int[11];
+
+        try {
+            BufferedImage img = loadImage("/assets/minecraft/textures/block/rubyglass/palette.png");
+            if (img != null) {
+                for (int i = 0; i < 11; i++) {
+                    int x = Math.min(i, img.getWidth() - 1);
+                    palette[i] = img.getRGB(x, 0) & 0xFFFFFF;
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/game/client/src/main/java/net/minecraft/client/render/texture/stitcher/AtlasStitcher.java b/game/client/src/main/java/net/minecraft/client/render/texture/stitcher/AtlasStitcher.java
index d1c4725ca..107722067 100644
--- a/game/client/src/main/java/net/minecraft/client/render/texture/stitcher/AtlasStitcher.java
+++ b/game/client/src/main/java/net/minecraft/client/render/texture/stitcher/AtlasStitcher.java
@@ -229,7 +229,7 @@ public class AtlasStitcher {
         }
         BufferedImage image = CustomAtlasHandler.getTextureOverride(Minecraft.getMinecraft().texturePackList, path + ".png");
         if(image == null) {
-            TexturePack pack = Minecraft.getMinecraft().texturePackList.getHighestPriorityTexturePackWithFile(path + ".png");
+            TexturePack pack = Minecraft.getMinecraft().texturePackList.getHighestPriorityTexturePackWithFile(source + ".png");
             if (pack != null) {
                 for (int attempt = 0; attempt < 3; attempt++) {
                     try (InputStream stream = pack.getResourceAsStream(path)) {
diff --git a/game/client/src/main/java/net/minecraft/client/render/tileentity/UnusedRendererRubyglassConduit.java b/game/client/src/main/java/net/minecraft/client/render/tileentity/UnusedRendererRubyglassConduit.java
new file mode 100644
index 000000000..27ca73ba7
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/tileentity/UnusedRendererRubyglassConduit.java
@@ -0,0 +1,280 @@
+package net.minecraft.client.render.tileentity;
+
+class UnusedRendererRubyglassConduit {
+
+    /*private static final long MAGIC_PRIME_X = 3129871L;
+    private static final long MAGIC_PRIME_Y = 116129781L;
+    private long fastRandomSeed;
+
+    public void doRender(TessellatorGeneral tessellator, TileEntityRubyglassConduit conduitTileEntity, double x, double y, double z, float partialTick) {
+        Player player = Minecraft.getMinecraft().thePlayer;
+
+        if (!shouldRender(conduitTileEntity, player, partialTick))
+            return;
+
+        Side[] openSides = conduitTileEntity.openSides;
+        if (openSides == null || openSides.length == 0)
+            return;
+
+        float powerRatio = conduitTileEntity.powerRatio;
+        int strength = conduitTileEntity.strength;
+
+        // pre-calculate colors and thicknesses so it's not happening every frame
+        final float baseRed = 1.0f;
+        final float baseBlue = 0.04f + (0.2f * powerRatio);
+        final float baseGreen = 0.3f * powerRatio;
+
+        final float innerGreen = Math.min(1.0f, baseGreen + 0.25f);
+        final float innerBlue = Math.min(1.0f, baseBlue + 0.25f);
+        final float coreGreen = Math.min(1.0f, baseGreen + 0.55f);
+        final float coreBlue = Math.min(1.0f, baseBlue + 0.55f);
+
+        final float weakCoreGreen = coreGreen * 0.5f;
+        final float weakCoreBlue = coreBlue * 0.5f;
+
+        final double baseThickness = 0.02 + (0.05 * (powerRatio * powerRatio));
+        final double outerThickness = baseThickness * 3.0;
+        final double innerThickness = baseThickness * 1.5;
+        final double coreThickness = baseThickness * 0.4;
+
+        //for corner joint connections
+        double coreDirectionX = 0;
+        double coreDirectionY = 0;
+        double coreDirectionZ = 0;
+        for (Side side : openSides) {
+            coreDirectionX += side.getOffsetX();
+            coreDirectionY += side.getOffsetY();
+            coreDirectionZ += side.getOffsetZ();
+        }
+
+        setupGL(x, y, z);
+
+        if (conduitTileEntity.worldObj == null) return;
+
+        this.fastRandomSeed = (conduitTileEntity.worldObj.getTotalWorldTime() / 2L)
+            ^ (long) conduitTileEntity.tilePos.x * MAGIC_PRIME_X
+            ^ (long) conduitTileEntity.tilePos.y * MAGIC_PRIME_Y
+            ^ (long) conduitTileEntity.tilePos.z;
+
+        // calculate relative camera position to the block
+        double cameraX = player.x - conduitTileEntity.tilePos.x;
+        double cameraY = (player.y + player.getHeadHeight()) - conduitTileEntity.tilePos.y;
+        double cameraZ = player.z - conduitTileEntity.tilePos.z;
+
+        tessellator.startDrawingQuads();
+        for (Side side : openSides) {
+            renderBeamSide(
+                tessellator, side, cameraX, cameraY, cameraZ, strength,
+                baseRed, baseBlue, baseGreen, innerGreen, innerBlue,
+                coreGreen, coreBlue, weakCoreGreen, weakCoreBlue,
+                outerThickness, innerThickness, coreThickness,
+                coreDirectionX, coreDirectionY, coreDirectionZ
+            );
+        }
+        tessellator.draw();
+
+        releaseGL();
+    }
+
+    private double nextJitterFormula(int strength) {
+        fastRandomSeed = (fastRandomSeed * 25214903917L + 11L) & ((1L << 48) - 1);
+        double baseJitter = (((double) fastRandomSeed / (1L << 48)) - 0.5) * 0.22;
+
+        //calmer when off/weak
+        return strength == 0 ? baseJitter * 0.25 : baseJitter;
+    }
+
+    private boolean shouldRender(TileEntityRubyglassConduit conduitTileEntity, Player player, float partialTick) {
+        if (player == null) return false;
+
+        ItemStack boots = player.inventory.armorItemInSlot(HumanArmorShape.BOOTS);
+        if (boots == null || boots.getItem() != Blocks.BLOCK_RUBYGLASS.asItem())
+            return false;
+
+        return !shouldCull(conduitTileEntity, player, partialTick);
+    }
+
+    private void setupGL(double x, double y, double z) {
+        GLRenderer.pushFrame();
+        GLRenderer.modelM4f().translate((float) x, (float) y, (float) z);
+        GLRenderer.setShader(Shaders.COLOR);
+        GLRenderer.globalSetLightEnabled(false);
+        GLRenderer.enableState(State.BLEND);
+        GLRenderer.setBlendFunc(BlendFactor.SRC_ALPHA, BlendFactor.ONE_MINUS_SRC_ALPHA);
+        GLRenderer.setDepthMask(false);
+    }
+
+    private void releaseGL() {
+        GLRenderer.setDepthMask(true);
+        GLRenderer.disableState(State.BLEND);
+        GLRenderer.globalSetLightEnabled(true);
+        GLRenderer.popFrame();
+    }
+
+    private void renderBeamSide(
+        TessellatorGeneral tessellator,
+        Side side, double cameraX, double cameraY, double cameraZ,
+        int strength, float baseRed, float baseBlue, float baseGreen,
+        float innerGreen, float innerBlue, float coreGreen, float coreBlue,
+        float weakCoreGreen, float weakCoreBlue,
+        double outerThickness, double innerThickness, double coreThickness,
+        double coreDirectionX, double coreDirectionY, double coreDirectionZ) {
+
+        double startX = 0.5, startY = 0.5, startZ = 0.5;
+        double endX = 0.5 + (side.getOffsetX() * 0.5);
+        double endY = 0.5 + (side.getOffsetY() * 0.5);
+        double endZ = 0.5 + (side.getOffsetZ() * 0.5);
+
+        int segments = 2;
+        int totalPoints = segments + 1;
+
+        double[] pointX = new double[totalPoints];
+        double[] pointY = new double[totalPoints];
+        double[] pointZ = new double[totalPoints];
+
+        boolean jitterX = side.getOffsetX() == 0;
+        boolean jitterY = side.getOffsetY() == 0;
+        boolean jitterZ = side.getOffsetZ() == 0;
+
+        // calculate coordinate points of the beam
+        pointX[0] = startX;
+        pointY[0] = startY;
+        pointZ[0] = startZ;
+
+        for (int i = 1; i < segments; i++) {
+            double progress = i / (double) segments;
+            pointX[i] = startX + (endX - startX) * progress;
+            pointY[i] = startY + (endY - startY) * progress;
+            pointZ[i] = startZ + (endZ - startZ) * progress;
+
+            if (jitterX) pointX[i] += nextJitterFormula(strength);
+            if (jitterY) pointY[i] += nextJitterFormula(strength);
+            if (jitterZ) pointZ[i] += nextJitterFormula(strength);
+        }
+
+        pointX[segments] = endX;
+        pointY[segments] = endY;
+        pointZ[segments] = endZ;
+
+        double[] rightVectorX = new double[totalPoints];
+        double[] rightVectorY = new double[totalPoints];
+        double[] rightVectorZ = new double[totalPoints];
+
+        for (int i = 0; i < totalPoints; i++) {
+            double directionX, directionY, directionZ;
+
+            //joint connection
+            if (i == 0) {
+                //corners
+                if (coreDirectionX == 0 && coreDirectionY == 0 && coreDirectionZ == 0) {
+                    directionX = side.getOffsetX();
+                    directionY = side.getOffsetY();
+                    directionZ = side.getOffsetZ();
+                } else {
+                    directionX = coreDirectionX;
+                    directionY = coreDirectionY;
+                    directionZ = coreDirectionZ;
+                }
+            } else if (i == totalPoints - 1) {
+                //edge of each block
+                directionX = side.getOffsetX();
+                directionY = side.getOffsetY();
+                directionZ = side.getOffsetZ();
+            } else {
+                //between segments
+                directionX = pointX[i + 1] - pointX[i - 1];
+                directionY = pointY[i + 1] - pointY[i - 1];
+                directionZ = pointZ[i + 1] - pointZ[i - 1];
+            }
+
+            double directionLength = Math.sqrt(directionX * directionX + directionY * directionY + directionZ * directionZ);
+            if (directionLength != 0) {
+                directionX /= directionLength;
+                directionY /= directionLength;
+                directionZ /= directionLength;
+            }
+
+            double toCameraX = cameraX - pointX[i];
+            double toCameraY = cameraY - pointY[i];
+            double toCameraZ = cameraZ - pointZ[i];
+
+            rightVectorX[i] = directionY * toCameraZ - directionZ * toCameraY;
+            rightVectorY[i] = directionZ * toCameraX - directionX * toCameraZ;
+            rightVectorZ[i] = directionX * toCameraY - directionY * toCameraX;
+
+            double rightVectorLength = Math.sqrt(rightVectorX[i] * rightVectorX[i] + rightVectorY[i] * rightVectorY[i] + rightVectorZ[i] * rightVectorZ[i]);
+            if (rightVectorLength != 0) {
+                rightVectorX[i] /= rightVectorLength;
+                rightVectorY[i] /= rightVectorLength;
+                rightVectorZ[i] /= rightVectorLength;
+            } else {
+                rightVectorX[i] = 1;
+                rightVectorY[i] = 0;
+                rightVectorZ[i] = 0;
+            }
+        }
+
+        //draw as continuous "ribbon"
+        if (strength != 0) {
+            tessellator.setColor4f(baseRed, baseGreen, baseBlue, 0.15f);
+            drawRibbonLayer(tessellator, pointX, pointY, pointZ, rightVectorX, rightVectorY, rightVectorZ, outerThickness);
+        }
+
+        tessellator.setColor4f(baseRed, innerGreen, innerBlue, 0.4f);
+        drawRibbonLayer(tessellator, pointX, pointY, pointZ, rightVectorX, rightVectorY, rightVectorZ, innerThickness);
+
+        if (strength != 0) {
+            tessellator.setColor4f(baseRed, coreGreen, coreBlue, 1.0f);
+        } else {
+            tessellator.setColor4f(1.0f, weakCoreGreen, weakCoreBlue, 1.0f);
+        }
+        drawRibbonLayer(tessellator, pointX, pointY, pointZ, rightVectorX, rightVectorY, rightVectorZ, coreThickness);
+    }
+
+    private void drawRibbonLayer(
+    TessellatorGeneral tessellator,
+    double[] pointX, double[] pointY, double[] pointZ,
+    double[] rightVectorX, double[] rightVectorY, double[] rightVectorZ,
+    double thickness) {
+
+        for (int i = 0; i < pointX.length - 1; i++) {
+            int nextIndex = i + 1;
+
+            double currentPointX = pointX[i];
+            double currentPointY = pointY[i];
+            double currentPointZ = pointZ[i];
+
+            double currentRightOffsetX = rightVectorX[i] * thickness;
+            double currentRightOffsetY = rightVectorY[i] * thickness;
+            double currentRightOffsetZ = rightVectorZ[i] * thickness;
+
+            double nextPointX = pointX[nextIndex];
+            double nextPointY = pointY[nextIndex];
+            double nextPointZ = pointZ[nextIndex];
+
+            double nextRightOffsetX = rightVectorX[nextIndex] * thickness;
+            double nextRightOffsetY = rightVectorY[nextIndex] * thickness;
+            double nextRightOffsetZ = rightVectorZ[nextIndex] * thickness;
+
+            tessellator.addVertex(currentPointX + currentRightOffsetX, currentPointY + currentRightOffsetY, currentPointZ + currentRightOffsetZ);
+            tessellator.addVertex(nextPointX + nextRightOffsetX, nextPointY + nextRightOffsetY, nextPointZ + nextRightOffsetZ);
+            tessellator.addVertex(nextPointX - nextRightOffsetX, nextPointY - nextRightOffsetY, nextPointZ - nextRightOffsetZ);
+            tessellator.addVertex(currentPointX - currentRightOffsetX, currentPointY - currentRightOffsetY, currentPointZ - currentRightOffsetZ);
+        }
+    }
+
+    private boolean shouldCull(TileEntityRubyglassConduit conduitTileEntity, Player player, float partialTick) {
+        double deltaX = (conduitTileEntity.tilePos.x + 0.5) - player.x;
+        double deltaY = (conduitTileEntity.tilePos.y + 0.5) - (player.y + player.getHeadHeight());
+        double deltaZ = (conduitTileEntity.tilePos.z + 0.5) - player.z;
+
+        double distanceSquared = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
+        if (distanceSquared > 100) return true;
+
+        Vector3dc lookVector = player.getViewVector(partialTick);
+        if (lookVector == null) return true;
+
+        double dotProduct = (deltaX * lookVector.x() + deltaY * lookVector.y() + deltaZ * lookVector.z());
+        return dotProduct < 0;
+    }*/
+}
\ No newline at end of file
diff --git a/game/client/src/main/java/net/minecraft/client/render/window/GameWindowGLFW.java b/game/client/src/main/java/net/minecraft/client/render/window/GameWindowGLFW.java
index ce70ed33c..9cabce59c 100644
--- a/game/client/src/main/java/net/minecraft/client/render/window/GameWindowGLFW.java
+++ b/game/client/src/main/java/net/minecraft/client/render/window/GameWindowGLFW.java
@@ -499,6 +499,7 @@ public class GameWindowGLFW implements GameWindow {
             if (this.glDebugMessageCallback != null) this.glDebugMessageCallback.free();
             this.stbiIconImages.forEach(STBImage::stbi_image_free);
         }
+        GLFW.glfwTerminate();
     }
 
     public static class MonitorInfo {
diff --git a/game/client/src/main/java/net/minecraft/client/util/helper/Colors.java b/game/client/src/main/java/net/minecraft/client/util/helper/Colors.java
index baac5a316..2993e33f2 100644
--- a/game/client/src/main/java/net/minecraft/client/util/helper/Colors.java
+++ b/game/client/src/main/java/net/minecraft/client/util/helper/Colors.java
@@ -20,6 +20,7 @@ public abstract class Colors {
 	public static final Color[] allFlagColors = new Color[16];
 	public static final Color[] allSignColors = new Color[16];
 	public static final Color[] allRedstoneColors = new Color[16];
+	public static final Color[] allConduitColors = new Color[11];
 	public static final Color[] allPortalColors = new Color[16];
 
 	public static void loadDummyColors() {
@@ -30,6 +31,7 @@ public abstract class Colors {
 		Arrays.fill(allFlagColors, white);
 		Arrays.fill(allSignColors, white);
 		Arrays.fill(allRedstoneColors, white);
+		Arrays.fill(allConduitColors, white);
 		Arrays.fill(allPortalColors, white);
 	}
 
@@ -40,6 +42,7 @@ public abstract class Colors {
 		fillColorArray("/assets/minecraft/textures/misc/colors_lamps.png", allLampColors);
 		fillColorArray("/assets/minecraft/textures/misc/colors_flag.png", allFlagColors);
 		fillColorArray("/assets/minecraft/textures/misc/colors_redstone.png", allRedstoneColors);
+		fillColorArray("/assets/minecraft/textures/misc/colors_conduit.png", allConduitColors);
 		fillColorArray("/assets/minecraft/textures/misc/colors_portal.png", allPortalColors);
 	}
 	
diff --git a/game/client/src/main/java/net/minecraft/client/world/WorldClient.java b/game/client/src/main/java/net/minecraft/client/world/WorldClient.java
index 0d935ae3a..c2bc17f4a 100644
--- a/game/client/src/main/java/net/minecraft/client/world/WorldClient.java
+++ b/game/client/src/main/java/net/minecraft/client/world/WorldClient.java
@@ -8,27 +8,17 @@ import net.minecraft.client.render.worldtype.WorldTypeFXDispatcher;
 import net.minecraft.client.world.chunk.provider.ChunkProviderDynamic;
 import net.minecraft.client.world.chunk.provider.ChunkProviderStatic;
 import net.minecraft.client.world.chunk.provider.ChunkProviderThreadedClient;
-import net.minecraft.client.world.chunk.provider.ChunkProviderViewer;
-import net.minecraft.core.Global;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.util.helper.MathHelper;
 import net.minecraft.core.world.Dimension;
 import net.minecraft.core.world.World;
-import net.minecraft.core.world.biome.Biome;
 import net.minecraft.core.world.chunk.ChunkLoader;
 import net.minecraft.core.world.chunk.provider.ChunkProvider;
 import net.minecraft.core.world.pos.TilePos;
-import net.minecraft.core.world.save.LevelData;
 import net.minecraft.core.world.save.LevelStorage;
-import net.minecraft.core.world.save.LevelStorageDummy;
-import net.minecraft.core.world.saveddata.SavedDataStorage;
-import net.minecraft.core.world.season.SeasonManager;
 import net.minecraft.core.world.season.Seasons;
 import net.minecraft.core.world.settings.WorldConfiguration;
-import net.minecraft.core.world.type.WorldType;
-import net.minecraft.core.world.type.WorldTypes;
 import net.minecraft.core.world.weather.IPrecipitation;
-import net.minecraft.core.world.weather.WeatherManager;
 import net.minecraft.core.world.weather.Weathers;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
diff --git a/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/circuit.json b/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/circuit.json
deleted file mode 100644
index 84ef01a62..000000000
--- a/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/circuit.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "parent": "minecraft:block/cube_all",
-  "textures": {
-    "all": "minecraft:block/rubyglass/circuit"
-  }
-}
\ No newline at end of file
diff --git a/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_cap.json b/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_cap.json
new file mode 100644
index 000000000..04f04c2a6
--- /dev/null
+++ b/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_cap.json
@@ -0,0 +1,40 @@
+{
+  "format_version": "1.21.6",
+  "credit": "Made with Blockbench",
+  "textures": {
+    "closed": "minecraft:block/rubyglass/conduit/closed/closed",
+    "closed_overlay": "minecraft:block/rubyglass/conduit/closed/closed_overlay",
+    "open": "minecraft:block/rubyglass/conduit/open/open",
+    "open_overlay": "minecraft:block/rubyglass/conduit/open/open_overlay",
+    "plug": "minecraft:block/rubyglass/conduit/plug/plug",
+    "plug_overlay": "minecraft:block/rubyglass/conduit/plug/plug_overlay",
+    "particle": "minecraft:block/rubyglass/conduit/closed/closed"
+  },
+  "elements": [
+    {
+      "name": "Conduit Casing",
+      "from": [0, 0, 0],
+      "to": [16, 16, 16],
+      "faces": {
+        "north": {"uv": [0, 0, 16, 16], "rotation": 270, "texture": "#plug", "cullface": "north"},
+        "east": {"uv": [0, 0, 16, 16], "rotation": 270, "texture": "#plug", "cullface": "east"},
+        "south": {"uv": [0, 0, 16, 16], "rotation": 270, "texture": "#plug", "cullface": "south"},
+        "west": {"uv": [0, 0, 16, 16], "rotation": 270, "texture": "#plug", "cullface": "west"},
+        "up": {"uv": [0, 0, 16, 16], "texture": "#closed", "cullface": "up"},
+        "down": {"uv": [0, 0, 16, 16], "texture": "#open", "cullface": "down"}
+      }
+    },
+    {
+      "name": "Redstone Overlay",
+      "from": [0, 0, 0],
+      "to": [16, 16, 16],
+      "faces": {
+        "north": {"uv": [0, 0, 16, 16], "rotation": 270, "texture": "#plug_overlay", "cullface": "north", "tintindex": 0, "shade": false},
+        "east": {"uv": [0, 0, 16, 16], "rotation": 270, "texture": "#plug_overlay", "cullface": "east", "tintindex": 0, "shade": false},
+        "south": {"uv": [0, 0, 16, 16], "rotation": 270, "texture": "#plug_overlay", "cullface": "south", "tintindex": 0, "shade": false},
+        "west": {"uv": [0, 0, 16, 16], "rotation": 270, "texture": "#plug_overlay", "cullface": "west", "tintindex": 0, "shade": false},
+        "down": {"uv": [0, 0, 16, 16], "texture": "#open_overlay", "cullface": "down", "tintindex": 0, "shade": false}
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_cap_off.json b/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_cap_off.json
new file mode 100644
index 000000000..c823c86d8
--- /dev/null
+++ b/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_cap_off.json
@@ -0,0 +1,27 @@
+{
+  "format_version": "1.21.6",
+  "credit": "Made with Blockbench",
+  "textures": {
+    "closed": "minecraft:block/rubyglass/conduit/closed/closed",
+    "closed_overlay": "minecraft:block/rubyglass/conduit/closed/closed_overlay",
+    "open": "minecraft:block/rubyglass/conduit/open/open",
+    "open_overlay": "minecraft:block/rubyglass/conduit/open/open_overlay",
+    "plug": "minecraft:block/rubyglass/conduit/plug/plug",
+    "particle": "minecraft:block/rubyglass/conduit/closed/closed"
+  },
+  "elements": [
+    {
+      "name": "Conduit Casing",
+      "from": [0, 0, 0],
+      "to": [16, 16, 16],
+      "faces": {
+        "north": {"uv": [0, 0, 16, 16], "rotation": 270, "texture": "#plug", "cullface": "north"},
+        "east": {"uv": [0, 0, 16, 16], "rotation": 270, "texture": "#plug", "cullface": "east"},
+        "south": {"uv": [0, 0, 16, 16], "rotation": 270, "texture": "#plug", "cullface": "south"},
+        "west": {"uv": [0, 0, 16, 16], "rotation": 270, "texture": "#plug", "cullface": "west"},
+        "up": {"uv": [0, 0, 16, 16], "texture": "#closed", "cullface": "up"},
+        "down": {"uv": [0, 0, 16, 16], "texture": "#open", "cullface": "down"}
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_core.json b/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_core.json
new file mode 100644
index 000000000..b3b4a9ed0
--- /dev/null
+++ b/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_core.json
@@ -0,0 +1,44 @@
+{
+  "format_version": "1.21.6",
+  "credit": "Made with Blockbench",
+  "display": {
+    "gui": { "rotation": [ 30, 225, 0 ], "translation": [ 0, 0, 0 ], "scale": [ 0.625, 0.625, 0.625 ] },
+    "ground": { "rotation": [ 0, 0, 0 ], "translation": [ 0, 3, 0 ], "scale": [ 0.25, 0.25, 0.25 ] },
+    "fixed": { "rotation": [ 0, 0, 0 ], "translation": [ 0, 0, 0 ], "scale": [ 0.5, 0.5, 0.5 ] },
+    "thirdperson_righthand": { "rotation": [ 75, 45, 0 ], "translation": [ 0, 2.5, 0 ], "scale": [ 0.375, 0.375, 0.375 ] },
+    "firstperson_righthand": { "rotation": [ 0, 45, 0 ], "translation": [ 0, 0, 0 ], "scale": [ 0.4, 0.4, 0.4 ] }
+  },
+  "textures": {
+    "closed": "minecraft:block/rubyglass/conduit/closed/closed",
+    "closed_overlay": "minecraft:block/rubyglass/conduit/closed/closed_overlay",
+    "particle": "minecraft:block/rubyglass/conduit/closed/closed"
+  },
+  "elements": [
+    {
+      "name": "Conduit Casing",
+      "from": [0, 0, 0],
+      "to": [16, 16, 16],
+      "faces": {
+        "north": {"uv": [0, 0, 16, 16], "texture": "#closed", "cullface": "north"},
+        "east": {"uv": [0, 0, 16, 16], "texture": "#closed", "cullface": "east"},
+        "south": {"uv": [0, 0, 16, 16], "texture": "#closed", "cullface": "south"},
+        "west": {"uv": [0, 0, 16, 16], "texture": "#closed", "cullface": "west"},
+        "up": {"uv": [0, 0, 16, 16], "texture": "#closed", "cullface": "up"},
+        "down": {"uv": [0, 0, 16, 16], "texture": "#closed", "cullface": "down"}
+      }
+    },
+    {
+      "name": "Redstone Overlay",
+      "from": [0, 0, 0],
+      "to": [16, 16, 16],
+      "faces": {
+        "north": {"uv": [0, 0, 16, 16], "texture": "#closed_overlay", "cullface": "north", "tintindex": 0, "shade": false},
+        "east": {"uv": [0, 0, 16, 16], "texture": "#closed_overlay", "cullface": "east", "tintindex": 0, "shade": false},
+        "south": {"uv": [0, 0, 16, 16], "texture": "#closed_overlay", "cullface": "south", "tintindex": 0, "shade": false},
+        "west": {"uv": [0, 0, 16, 16], "texture": "#closed_overlay", "cullface": "west", "tintindex": 0, "shade": false},
+        "up": {"uv": [0, 0, 16, 16], "texture": "#closed_overlay", "cullface": "up", "tintindex": 0, "shade": false},
+        "down": {"uv": [0, 0, 16, 16], "texture": "#closed_overlay", "cullface": "down", "tintindex": 0, "shade": false}
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_core_off.json b/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_core_off.json
new file mode 100644
index 000000000..b84b0ad51
--- /dev/null
+++ b/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_core_off.json
@@ -0,0 +1,31 @@
+{
+  "format_version": "1.21.6",
+  "credit": "Made with Blockbench",
+  "display": {
+    "gui": { "rotation": [ 30, 225, 0 ], "translation": [ 0, 0, 0 ], "scale": [ 0.625, 0.625, 0.625 ] },
+    "ground": { "rotation": [ 0, 0, 0 ], "translation": [ 0, 3, 0 ], "scale": [ 0.25, 0.25, 0.25 ] },
+    "fixed": { "rotation": [ 0, 0, 0 ], "translation": [ 0, 0, 0 ], "scale": [ 0.5, 0.5, 0.5 ] },
+    "thirdperson_righthand": { "rotation": [ 75, 45, 0 ], "translation": [ 0, 2.5, 0 ], "scale": [ 0.375, 0.375, 0.375 ] },
+    "firstperson_righthand": { "rotation": [ 0, 45, 0 ], "translation": [ 0, 0, 0 ], "scale": [ 0.4, 0.4, 0.4 ] }
+  },
+  "textures": {
+    "closed": "minecraft:block/rubyglass/conduit/closed/closed",
+    "closed_overlay": "minecraft:block/rubyglass/conduit/closed/closed_overlay",
+    "particle": "minecraft:block/rubyglass/conduit/closed/closed"
+  },
+  "elements": [
+    {
+      "name": "Conduit Casing",
+      "from": [0, 0, 0],
+      "to": [16, 16, 16],
+      "faces": {
+        "north": {"uv": [0, 0, 16, 16], "texture": "#closed", "cullface": "north"},
+        "east": {"uv": [0, 0, 16, 16], "texture": "#closed", "cullface": "east"},
+        "south": {"uv": [0, 0, 16, 16], "texture": "#closed", "cullface": "south"},
+        "west": {"uv": [0, 0, 16, 16], "texture": "#closed", "cullface": "west"},
+        "up": {"uv": [0, 0, 16, 16], "texture": "#closed", "cullface": "up"},
+        "down": {"uv": [0, 0, 16, 16], "texture": "#closed", "cullface": "down"}
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_corner.json b/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_corner.json
new file mode 100644
index 000000000..70b5cc7bd
--- /dev/null
+++ b/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_corner.json
@@ -0,0 +1,41 @@
+{
+  "format_version": "1.21.6",
+  "credit": "Made with Blockbench",
+  "textures": {
+    "corner": "minecraft:block/rubyglass/conduit/corner/corner",
+    "corner_overlay": "minecraft:block/rubyglass/conduit/corner/corner_overlay",
+    "open": "minecraft:block/rubyglass/conduit/open/open",
+    "open_overlay": "minecraft:block/rubyglass/conduit/open/open_overlay",
+    "plug": "minecraft:block/rubyglass/conduit/plug/plug",
+    "plug_overlay": "minecraft:block/rubyglass/conduit/plug/plug_overlay",
+    "particle": "minecraft:block/rubyglass/conduit/closed/closed"
+  },
+  "elements": [
+    {
+      "name": "Conduit Casing",
+      "from": [0, 0, 0],
+      "to": [16, 16, 16],
+      "faces": {
+        "north": {"uv": [0, 0, 16, 16], "texture": "#open", "cullface": "north"},
+        "east": {"uv": [0, 0, 16, 16], "texture": "#corner", "cullface": "east"},
+        "south": {"uv": [0, 0, 16, 16], "rotation": 270, "texture": "#plug", "cullface": "south"},
+        "west": {"uv": [16, 0, 0, 16], "texture": "#corner", "cullface": "west"},
+        "up": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#plug", "cullface": "up"},
+        "down": {"uv": [0, 0, 16, 16], "texture": "#open", "cullface": "down"}
+      }
+    },
+    {
+      "name": "Redstone Overlay",
+      "from": [0, 0, 0],
+      "to": [16, 16, 16],
+      "faces": {
+        "north": {"uv": [0, 0, 16, 16], "texture": "#open_overlay", "cullface": "north", "tintindex": 0, "shade": false},
+        "east": {"uv": [0, 0, 16, 16], "texture": "#corner_overlay", "cullface": "east", "tintindex": 0, "shade": false},
+        "south": {"uv": [0, 0, 16, 16], "rotation": 270, "texture": "#plug_overlay", "cullface": "south", "tintindex": 0, "shade": false},
+        "west": {"uv": [16, 0, 0, 16], "texture": "#corner_overlay", "cullface": "west", "tintindex": 0, "shade": false},
+        "up": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#plug_overlay", "cullface": "up", "tintindex": 0, "shade": false},
+        "down": {"uv": [0, 0, 16, 16], "texture": "#open_overlay", "cullface": "down", "tintindex": 0, "shade": false}
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_corner_off.json b/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_corner_off.json
new file mode 100644
index 000000000..2c6590b5b
--- /dev/null
+++ b/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_corner_off.json
@@ -0,0 +1,28 @@
+{
+  "format_version": "1.21.6",
+  "credit": "Made with Blockbench",
+  "textures": {
+    "corner": "minecraft:block/rubyglass/conduit/corner/corner",
+    "corner_overlay": "minecraft:block/rubyglass/conduit/corner/corner_overlay",
+    "open": "minecraft:block/rubyglass/conduit/open/open",
+    "open_overlay": "minecraft:block/rubyglass/conduit/open/open_overlay",
+    "plug": "minecraft:block/rubyglass/conduit/plug/plug",
+    "plug_overlay": "minecraft:block/rubyglass/conduit/plug/plug_overlay",
+    "particle": "minecraft:block/rubyglass/conduit/closed/closed"
+  },
+  "elements": [
+    {
+      "name": "Conduit Casing",
+      "from": [0, 0, 0],
+      "to": [16, 16, 16],
+      "faces": {
+        "north": {"uv": [0, 0, 16, 16], "texture": "#open", "cullface": "north"},
+        "east": {"uv": [0, 0, 16, 16], "texture": "#corner", "cullface": "east"},
+        "south": {"uv": [0, 0, 16, 16], "rotation": 270, "texture": "#plug", "cullface": "south"},
+        "west": {"uv": [16, 0, 0, 16], "texture": "#corner", "cullface": "west"},
+        "up": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#plug", "cullface": "up"},
+        "down": {"uv": [0, 0, 16, 16], "texture": "#open", "cullface": "down"}
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_straight.json b/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_straight.json
new file mode 100644
index 000000000..b10f4b890
--- /dev/null
+++ b/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_straight.json
@@ -0,0 +1,39 @@
+{
+  "format_version": "1.21.6",
+  "credit": "Made with Blockbench",
+  "textures": {
+    "open": "minecraft:block/rubyglass/conduit/open/open",
+    "open_overlay": "minecraft:block/rubyglass/conduit/open/open_overlay",
+    "straight": "minecraft:block/rubyglass/conduit/straight/straight",
+    "straight_overlay": "minecraft:block/rubyglass/conduit/straight/straight_overlay",
+    "particle": "minecraft:block/rubyglass/conduit/closed/closed"
+  },
+  "elements": [
+    {
+      "name": "Conduit Casing",
+      "from": [0, 0, 0],
+      "to": [16, 16, 16],
+      "faces": {
+        "north": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#straight", "cullface": "north"},
+        "east": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#straight", "cullface": "east"},
+        "south": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#straight", "cullface": "south"},
+        "west": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#straight", "cullface": "west"},
+        "up": {"uv": [0, 0, 16, 16], "texture": "#open", "cullface": "up"},
+        "down": {"uv": [0, 0, 16, 16], "texture": "#open", "cullface": "down"}
+      }
+    },
+    {
+      "name": "Redstone Overlay",
+      "from": [0, 0, 0],
+      "to": [16, 16, 16],
+      "faces": {
+        "north": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#straight_overlay", "cullface": "north", "tintindex": 0, "shade": false},
+        "east": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#straight_overlay", "cullface": "east", "tintindex": 0, "shade": false},
+        "south": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#straight_overlay", "cullface": "south", "tintindex": 0, "shade": false},
+        "west": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#straight_overlay", "cullface": "west", "tintindex": 0, "shade": false},
+        "up": {"uv": [0, 0, 16, 16], "texture": "#open_overlay", "cullface": "up", "tintindex": 0, "shade": false},
+        "down": {"uv": [0, 0, 16, 16], "texture": "#open_overlay", "cullface": "down", "tintindex": 0, "shade": false}
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_straight_off.json b/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_straight_off.json
new file mode 100644
index 000000000..f2cd1b03d
--- /dev/null
+++ b/game/client/src/main/resources/assets/minecraft/models/block/rubyglass/conduit/conduit_straight_off.json
@@ -0,0 +1,26 @@
+{
+  "format_version": "1.21.6",
+  "credit": "Made with Blockbench",
+  "textures": {
+    "open": "minecraft:block/rubyglass/conduit/open/open",
+    "open_overlay": "minecraft:block/rubyglass/conduit/open/open_overlay",
+    "straight": "minecraft:block/rubyglass/conduit/straight/straight",
+    "straight_overlay": "minecraft:block/rubyglass/conduit/straight/straight_overlay",
+    "particle": "minecraft:block/rubyglass/conduit/closed/closed"
+  },
+  "elements": [
+    {
+      "name": "Conduit Casing",
+      "from": [0, 0, 0],
+      "to": [16, 16, 16],
+      "faces": {
+        "north": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#straight", "cullface": "north"},
+        "east": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#straight", "cullface": "east"},
+        "south": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#straight", "cullface": "south"},
+        "west": {"uv": [0, 0, 16, 16], "rotation": 90, "texture": "#straight", "cullface": "west"},
+        "up": {"uv": [0, 0, 16, 16], "texture": "#open", "cullface": "up"},
+        "down": {"uv": [0, 0, 16, 16], "texture": "#open", "cullface": "down"}
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/circuit.emiss.png b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/circuit.emiss.png
deleted file mode 100644
index 4e1cbce88..000000000
Binary files a/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/circuit.emiss.png and /dev/null differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/circuit.png b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/circuit.png
deleted file mode 100644
index 2e0638446..000000000
Binary files a/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/circuit.png and /dev/null differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/closed/closed.png b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/closed/closed.png
new file mode 100644
index 000000000..6ee6ba2f1
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/closed/closed.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/closed/closed_overlay.emiss.png b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/closed/closed_overlay.emiss.png
new file mode 100644
index 000000000..73b5ded4c
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/closed/closed_overlay.emiss.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/closed/closed_overlay.png b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/closed/closed_overlay.png
new file mode 100644
index 000000000..21861dd34
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/closed/closed_overlay.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/corner/corner.png b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/corner/corner.png
new file mode 100644
index 000000000..e53199bca
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/corner/corner.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/corner/corner_overlay.emiss.png b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/corner/corner_overlay.emiss.png
new file mode 100644
index 000000000..256713ea1
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/corner/corner_overlay.emiss.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/corner/corner_overlay.png b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/corner/corner_overlay.png
new file mode 100644
index 000000000..9c26af9cc
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/corner/corner_overlay.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/open/open.png b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/open/open.png
new file mode 100644
index 000000000..29d057355
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/open/open.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/open/open_overlay.emiss.png b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/open/open_overlay.emiss.png
new file mode 100644
index 000000000..a58287878
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/open/open_overlay.emiss.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/open/open_overlay.png b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/open/open_overlay.png
new file mode 100644
index 000000000..4558bfef0
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/open/open_overlay.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/plug/plug.png b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/plug/plug.png
new file mode 100644
index 000000000..996d2a5b2
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/plug/plug.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/plug/plug_overlay.emiss.png b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/plug/plug_overlay.emiss.png
new file mode 100644
index 000000000..b87b4713f
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/plug/plug_overlay.emiss.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/plug/plug_overlay.png b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/plug/plug_overlay.png
new file mode 100644
index 000000000..63f0acae3
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/plug/plug_overlay.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/straight/straight.png b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/straight/straight.png
new file mode 100644
index 000000000..39bdded55
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/straight/straight.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/straight/straight_overlay.emiss.png b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/straight/straight_overlay.emiss.png
new file mode 100644
index 000000000..08574d449
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/straight/straight_overlay.emiss.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/straight/straight_overlay.png b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/straight/straight_overlay.png
new file mode 100644
index 000000000..a072df79a
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/rubyglass/conduit/straight/straight_overlay.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket.png
deleted file mode 100644
index 13e9d12b4..000000000
Binary files a/game/client/src/main/resources/assets/minecraft/textures/item/bucket.png and /dev/null differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_icecream.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_icecream.png
deleted file mode 100644
index 1d0d4619e..000000000
Binary files a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_icecream.png and /dev/null differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_iron/empty.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_iron/empty.png
new file mode 100644
index 000000000..2f85139e7
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_iron/empty.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_iron/icecream_1.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_iron/icecream_1.png
new file mode 100644
index 000000000..9460a1678
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_iron/icecream_1.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_iron/lava_1.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_iron/lava_1.png
new file mode 100644
index 000000000..64e5b5bfc
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_iron/lava_1.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_iron/milk_1.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_iron/milk_1.png
new file mode 100644
index 000000000..a5cda20b6
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_iron/milk_1.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_iron/water_1.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_iron/water_1.png
new file mode 100644
index 000000000..565b495a7
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_iron/water_1.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_lava.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_lava.png
deleted file mode 100644
index 6d3d261ef..000000000
Binary files a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_lava.png and /dev/null differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_milk.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_milk.png
deleted file mode 100644
index 05a47069c..000000000
Binary files a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_milk.png and /dev/null differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/empty.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/empty.png
new file mode 100644
index 000000000..e6bf60ca8
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/empty.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/icecream_1.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/icecream_1.png
new file mode 100644
index 000000000..ace144950
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/icecream_1.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/icecream_2.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/icecream_2.png
new file mode 100644
index 000000000..eeed898a0
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/icecream_2.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/icecream_3.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/icecream_3.png
new file mode 100644
index 000000000..099ba0503
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/icecream_3.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/lava_1.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/lava_1.png
new file mode 100644
index 000000000..8d24f4717
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/lava_1.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/lava_2.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/lava_2.png
new file mode 100644
index 000000000..d2fcd159d
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/lava_2.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/lava_3.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/lava_3.png
new file mode 100644
index 000000000..2b765f2a6
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/lava_3.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/milk_1.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/milk_1.png
new file mode 100644
index 000000000..a7d88514c
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/milk_1.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/milk_2.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/milk_2.png
new file mode 100644
index 000000000..2ba8cbd29
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/milk_2.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/milk_3.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/milk_3.png
new file mode 100644
index 000000000..f695e6d4b
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/milk_3.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/water_1.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/water_1.png
new file mode 100644
index 000000000..d1ab77267
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/water_1.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/water_2.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/water_2.png
new file mode 100644
index 000000000..f5266e3ef
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/water_2.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/water_3.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/water_3.png
new file mode 100644
index 000000000..ddf5f7bc5
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_steel/water_3.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_water.png b/game/client/src/main/resources/assets/minecraft/textures/item/bucket_water.png
deleted file mode 100644
index 5cbaaba54..000000000
Binary files a/game/client/src/main/resources/assets/minecraft/textures/item/bucket_water.png and /dev/null differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/misc/colors_conduit.png b/game/client/src/main/resources/assets/minecraft/textures/misc/colors_conduit.png
new file mode 100644
index 000000000..5f2489fb5
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/misc/colors_conduit.png differ
diff --git a/game/core/src/main/java/net/minecraft/core/achievement/Achievement.java b/game/core/src/main/java/net/minecraft/core/achievement/Achievement.java
index 2783692d9..fbddff06b 100644
--- a/game/core/src/main/java/net/minecraft/core/achievement/Achievement.java
+++ b/game/core/src/main/java/net/minecraft/core/achievement/Achievement.java
@@ -3,6 +3,7 @@ package net.minecraft.core.achievement;
 import net.minecraft.core.achievement.stat.StatDescFormatter;
 import net.minecraft.core.achievement.stat.Stat;
 import net.minecraft.core.item.IItemConvertible;
+import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.lang.I18n;
 import net.minecraft.core.util.collection.NamespaceID;
 
@@ -17,21 +18,20 @@ public class Achievement extends Stat
     private StatDescFormatter statStringFormatter;
     public final int iconItemId;
     private Type type = TYPE_NORMAL;
+    public final ItemStack iconStack;
 
-    public Achievement(NamespaceID id, String name, IItemConvertible icon, Achievement parent)
-    {
-        this(id, name, icon.asItem().id, parent);
-    }
-
-    public Achievement(NamespaceID id, String name, int iconItemId, Achievement parent)
-    {
+    public Achievement(NamespaceID id, String name, ItemStack icon, Achievement parent) {
         super(NamespaceID.fromPool(id.namespace(), "achievement/" + id.value()), "achievement." + name);
-        this.iconItemId = iconItemId;
+        this.iconStack = icon;
+        this.iconItemId = icon.itemID;
         this.description = "achievement." + name;
-
         this.parent = parent;
     }
 
+    public Achievement(NamespaceID id, String name, IItemConvertible icon, Achievement parent) {
+        this(id, name, new ItemStack(icon), parent);
+    }
+
     public Achievement setClientsideAchievement() {
         clientside = true;
         return this;
diff --git a/game/core/src/main/java/net/minecraft/core/achievement/Achievements.java b/game/core/src/main/java/net/minecraft/core/achievement/Achievements.java
index e72e72249..1fa32fe63 100644
--- a/game/core/src/main/java/net/minecraft/core/achievement/Achievements.java
+++ b/game/core/src/main/java/net/minecraft/core/achievement/Achievements.java
@@ -1,6 +1,7 @@
 package net.minecraft.core.achievement;
 
 import net.minecraft.core.block.Blocks;
+import net.minecraft.core.item.ItemBucket;
 import net.minecraft.core.item.Items;
 import net.minecraft.core.util.collection.NamespaceID;
 
@@ -45,7 +46,7 @@ public abstract class Achievements
         new Achievement(NamespaceID.fromPool("minecraft", "bake_cake"), "bakeCake", Items.FOOD_CAKE, BUILD_HOE)
             .registerAchievement();
     public static Achievement CRAFT_ICECREAM =
-        new Achievement(NamespaceID.fromPool("minecraft", "craft_icecream"), "craftIcecream", Items.BUCKET_ICECREAM, BUILD_HOE)
+        new Achievement(NamespaceID.fromPool("minecraft", "craft_icecream"), "craftIcecream", ItemBucket.createItemStack(Items.BUCKET_IRON, ItemBucket.STATE_ICECREAM, 1), BUILD_HOE)
             .registerAchievement();
     public static Achievement CRAFT_PUMPKIN_PIE =
         new Achievement(NamespaceID.fromPool("minecraft", "craft_pumpkin_pie"), "craftPumpkinPie", Items.FOOD_PUMPKIN_PIE, BUILD_HOE)
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicActivator.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicActivator.java
index c273b10ea..46c4fb18b 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicActivator.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicActivator.java
@@ -18,6 +18,8 @@ import org.jetbrains.annotations.Nullable;
 import java.util.Objects;
 import java.util.Random;
 
+import static net.minecraft.core.block.BlockLogicRubyglassConduit.packEventConduitData;
+
 public class BlockLogicActivator
     extends BlockLogicVeryRotatable {
     public static final int MASK_POWERED = 0b0000_1000;
@@ -86,6 +88,13 @@ public class BlockLogicActivator
         }
     }
 
+    @Override
+    public void animationTick(@NotNull World world, @NotNull TilePosc tilePos, @NotNull Random rand) {
+        final int meta = world.getBlockData(tilePos);
+        if ((meta & MASK_POWERED) == 0) {return;}
+        world.playBlockEvent(tilePos, LevelListener.EVENT_CONDUIT_SURFACE_PARTICLES, packEventConduitData(null, 10));
+    }
+
     @Override
     public void updateTick(final @NotNull World world, final @NotNull TilePosc tilePos, final @NotNull Random rand, final boolean isRandomTick) {
         this.useItem(world, tilePos, rand);
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicFluid.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicFluid.java
index 0d8bbaf2c..f64634037 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicFluid.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicFluid.java
@@ -181,8 +181,8 @@ public abstract class BlockLogicFluid extends BlockLogic {
         return null;
     }
 
-    protected static void fizz(final @NotNull World world, @NotNull TilePosc tilePos) {
-        world.playSoundEffect(null, SoundCategory.WORLD_SOUNDS, (float) tilePos.x() + 0.5F, (float) tilePos.y() + 0.5F, (float) tilePos.z() + 0.5F, "random.fizz", 0.5F, 2.6F + (world.rand.nextFloat() - world.rand.nextFloat()) * 0.8F);
+    public static void fizz(final @NotNull World world, @NotNull TilePosc tilePos) {
+        world.playSoundEffect(null, SoundCategory.WORLD_SOUNDS, (float) tilePos.x() + 0.5F, (float) tilePos.y() + 0.5F, (float) tilePos.z() + 0.5F, "random.fizz", 1F, 2.6F + (world.rand.nextFloat() - world.rand.nextFloat()) * 0.8F);
         for (int i = 0; i < 8; i++) {
             world.spawnParticle("largesmoke", (double) tilePos.x() + Math.random(), (double) tilePos.y() + 1.2D, (double) tilePos.z() + Math.random(), 0.0D, 0.0D, 0.0D, 0, false);
         }
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicLamp.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicLamp.java
index 365d71345..01794530b 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicLamp.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicLamp.java
@@ -133,4 +133,15 @@ public class BlockLogicLamp extends BlockLogic implements IPainted {
     public void removeDye(@NotNull World world, @NotNull TilePosc tilePos) {
         world.setBlockDataNotify(tilePos, 0);
     }
+
+    @Override
+    public boolean isEquivalent(final @NotNull World world, final @NotNull TilePosc thisPos, final @NotNull TilePosc tilePos) {
+        Block<?> block = world.getBlockType(tilePos);
+
+        if (block.getLogic() instanceof BlockLogicLamp logicOfLamp) {
+            return this.isActive == logicOfLamp.isActive;
+        }
+
+        return false;
+    }
 }
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicMatcher.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicMatcher.java
index 39aef4bda..852fcacc4 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicMatcher.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicMatcher.java
@@ -9,6 +9,7 @@ import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.util.helper.Axis;
 import net.minecraft.core.util.helper.Direction;
 import net.minecraft.core.util.helper.Side;
+import net.minecraft.core.world.LevelListener;
 import net.minecraft.core.world.World;
 import net.minecraft.core.world.WorldSource;
 import net.minecraft.core.world.pos.TilePos;
@@ -18,6 +19,9 @@ import org.jetbrains.annotations.Nullable;
 
 import java.util.Random;
 
+import static net.minecraft.core.block.BlockLogicFarmland.isWet;
+import static net.minecraft.core.block.BlockLogicRubyglassConduit.packEventConduitData;
+
 public class BlockLogicMatcher extends BlockLogicAxisAligned {
     public final boolean isActive;
 
@@ -85,11 +89,7 @@ public class BlockLogicMatcher extends BlockLogicAxisAligned {
 
         boolean backMatchesFront;
         Block<?> fBlock = world.getBlockType(front);
-        if (fBlock == null) {
-            backMatchesFront = world.getBlockType(back) == null;
-        } else {
-            backMatchesFront = fBlock.isEquivalent(world, front, back);
-        }
+        backMatchesFront = fBlock.isEquivalent(world, front, back);
 
         if (!this.isActive && backMatchesFront) {
             world.setBlockTypeDataNotify(tilePos, Blocks.MATCHER_ACTIVE, world.getBlockData(tilePos));
@@ -100,33 +100,8 @@ public class BlockLogicMatcher extends BlockLogicAxisAligned {
 
     @Override
     public void animationTick(@NotNull World world, @NotNull TilePosc tilePos, @NotNull Random rand) {
-        if (!this.isActive) {
-            return;
-        }
-
-        final int brightness = 10;
-        double d = 0.0625;
-        for (int i = 0; i < Direction.directions.length; i++) {
-            Direction dir = Direction.directions[i];
-            double px = (double) tilePos.x() + rand.nextFloat();
-            double py = (double) tilePos.y() + rand.nextFloat();
-            double pz = (double) tilePos.z() + rand.nextFloat();
-
-            if (!world.isBlockOpaqueCube(tilePos.add(dir, new TilePos()))) {
-                switch (dir) {
-                    case UP -> py = (tilePos.y() + 1) + d;
-                    case DOWN -> py = (tilePos.y()) - d;
-                    case SOUTH -> pz = (tilePos.z() + 1) + d;
-                    case NORTH -> pz = (tilePos.z()) - d;
-                    case EAST -> px = (tilePos.x() + 1) + d;
-                    case WEST -> px = (tilePos.x()) - d;
-                }
-            }
-
-            if (px < tilePos.x() || px > (tilePos.x() + 1) || py < 0.0D || py > (tilePos.y() + 1) || pz < tilePos.z() || pz > (tilePos.z() + 1)) {
-                world.spawnParticle("reddust", px, py, pz, 0, 0, 0, brightness, false);
-            }
-        }
+        if (!this.isActive) {return;}
+        world.playBlockEvent(tilePos, LevelListener.EVENT_CONDUIT_SURFACE_PARTICLES, packEventConduitData(null, 10));
     }
 
     @Override
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicRubyglassConduit.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicRubyglassConduit.java
new file mode 100644
index 000000000..8e527e43f
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicRubyglassConduit.java
@@ -0,0 +1,490 @@
+package net.minecraft.core.block;
+
+import net.minecraft.core.block.material.Material;
+import net.minecraft.core.entity.Mob;
+import net.minecraft.core.entity.player.Player;
+import net.minecraft.core.sound.SoundCategory;
+import net.minecraft.core.util.helper.Direction;
+import net.minecraft.core.util.helper.Side;
+import net.minecraft.core.world.LevelListener;
+import net.minecraft.core.world.World;
+import net.minecraft.core.world.WorldSource;
+import net.minecraft.core.world.pos.TilePos;
+import net.minecraft.core.world.pos.TilePosc;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class BlockLogicRubyglassConduit extends BlockLogic {
+
+    public static final Side[][] STATE_TABLE = {
+            {},                         // 0
+            {Side.BOTTOM}, {Side.TOP},  // 1-2
+            {Side.NORTH}, {Side.SOUTH}, // 3-4
+            {Side.WEST}, {Side.EAST},   // 5-6
+
+            {Side.BOTTOM, Side.TOP},    // 7 Y-Axis
+            {Side.NORTH, Side.SOUTH},   // 8 Z-Axis
+            {Side.WEST, Side.EAST},     // 9 X-Axis
+
+            {Side.BOTTOM, Side.NORTH}, {Side.BOTTOM, Side.SOUTH}, // 10-11
+            {Side.BOTTOM, Side.WEST}, {Side.BOTTOM, Side.EAST},   // 12-13
+            {Side.TOP, Side.NORTH}, {Side.TOP, Side.SOUTH},       // 14-15
+            {Side.TOP, Side.WEST}, {Side.TOP, Side.EAST},         // 16-17
+            {Side.NORTH, Side.WEST}, {Side.NORTH, Side.EAST},     // 18-19
+            {Side.SOUTH, Side.WEST}, {Side.SOUTH, Side.EAST}      // 20-21
+    };
+
+    //PROPAGATION_LENGTH can ONLY BE A MAXIMUM OF 10. Anymore and the data will become mangled.
+    //if changed after implementation, data of existing conduits will become mangled without a converter.
+    //TODO There are a couple of hardcoded segments of code that do not account for PROPAGATION_LENGTH
+    private static final int PROPAGATION_LENGTH = 10;
+    public static final int STATE_COUNT = 22;
+
+    public BlockLogicRubyglassConduit(@NotNull Block<?> block, @NotNull Material material) {
+        super(block, material);
+    }
+
+    @Override
+    public int tickDelay() {
+        return 0;
+    }
+
+    @Override
+    public void updateTick(final @NotNull World world, final @NotNull TilePosc tilePos, final @NotNull Random rand, final boolean isRandomTick) {
+        onNeighborChanged(world, tilePos, this.block);
+    }
+
+    @Override
+    public void animationTick(final @NotNull World world, final @NotNull TilePosc tilePos, final @NotNull Random rand) {
+        int data = world.getBlockData(tilePos);
+        int strength = data / STATE_COUNT;
+
+        if (strength > 0) {
+            world.playBlockEvent(tilePos, LevelListener.EVENT_CONDUIT_SURFACE_PARTICLES, packEventConduitData(Side.NONE, strength));
+        }
+    }
+
+    @Override
+    public boolean isSolidRender() {
+        return false;
+    }
+
+    @Override
+    public boolean isCubeShaped() {
+        return false;
+    }
+
+    @Override
+    public boolean isSignalSource() {return true;}
+
+    @Override
+    public void onNeighborChanged(final @NotNull World world, final @NotNull TilePosc cTilePos, final @NotNull Block<?> block) {
+        if (world.isClientSide) return;
+        world.getConduitHandler().onConduitUpdated(cTilePos);
+    }
+
+    @Override
+    public void onPlacedByWorld(@NotNull World world, @NotNull TilePosc tilePos) {
+        super.onPlacedByWorld(world, tilePos);
+        if (!world.isClientSide) {
+            world.getConduitHandler().onConduitAdded(tilePos);
+        }
+    }
+
+    @Override
+    public void onRemoved(@NotNull World world, @NotNull TilePosc tilePos, int data) {
+        super.onRemoved(world, tilePos, data);
+        if (!world.isClientSide) {
+            world.getConduitHandler().onConduitRemoved(tilePos, data);
+        }
+    }
+
+
+    /**
+     * Defines the placement and routing behavior of the conduit when placed into the world by an entity.
+     * <p>
+     * This method handles two distinct placement modes:
+     * <ul>
+     * <li><b>Sneaking (Manual Placement):</b> Bypasses auto-connection. If placed against a closed conduit port,
+     * it will force that port open and connect to it. Otherwise, it defaults to routing a connection from the
+     * clicked block face toward the placing entity.</li>
+     * <li><b>Standard (Auto-connection):</b> Scans adjacent blocks for valid, open conduit ports.
+     * <ul>
+     * <li><i>1 Valid Neighbor:</i> Routes from the neighbor toward the entity.</li>
+     * <li><i>2 Valid Neighbors:</i> Creates a straight or corner connection linking both neighbors.</li>
+     * <li><i>3+ Valid Neighbors:</i> Defaults to routing from the block placed against toward the entity.</li>
+     * </ul>
+     * </li>
+     * </ul>
+     * @param world          World the block is being placed in.
+     * @param cTilePos       The position where this new conduit is being placed.
+     * @param sidePlacedOnto The face of the existing block that the entity clicked against to place this block.
+     * @param mob            The entity (usually a Player) placing the block.
+     * @param xHit           The X coordinate of the cursor hit on the block face.
+     * @param yHit           The Y coordinate of the cursor hit on the block face.
+     */
+    @Override
+    public void onPlacedByMob(final @NotNull World world, final @NotNull TilePosc cTilePos, final @NotNull Side sidePlacedOnto, final @NotNull Mob mob, final double xHit, final double yHit) {
+        Side sideFacingPlayer = Direction.getDirection(mob).getOpposite().getSide();
+        Side sideClickedAgainst = sidePlacedOnto.getOpposite();
+
+        int stateToPlaceAs = 0;
+        if (mob.isSneaking()) {
+            boolean openedClosedFace = false;
+            TilePos clickedPos = new TilePos(cTilePos.x() + sideClickedAgainst.getOffsetX(), cTilePos.y() + sideClickedAgainst.getOffsetY(), cTilePos.z() + sideClickedAgainst.getOffsetZ());
+
+            if (world.getBlockType(clickedPos) == this.block) {
+                int clickedData = world.getBlockData(clickedPos);
+
+                if (!isSideOpen(clickedData, sidePlacedOnto)) {
+                    int clickedState = clickedData % STATE_COUNT;
+                    int clickedStrength = clickedData / STATE_COUNT;
+                    int newClickedState = getNextState(clickedState, sidePlacedOnto);
+
+                    if (newClickedState != clickedState) {
+                        world.setBlockDataNotify(clickedPos, clickedStrength * STATE_COUNT + newClickedState);
+                        this.onNeighborChanged(world, clickedPos, this.block);
+                        openedClosedFace = true;
+                    }
+
+                }
+            }
+
+            if (openedClosedFace) {
+                stateToPlaceAs = getNextState(0, sideClickedAgainst);
+            } else {
+                if (sideClickedAgainst != sideFacingPlayer && sideFacingPlayer != Side.NONE) {
+                    stateToPlaceAs = getStateFromTwoSides(sideClickedAgainst, sideFacingPlayer);
+                } else {
+                    stateToPlaceAs = getNextState(0, sideClickedAgainst);
+                }
+            }
+        }
+        else {
+            List<Side> validSides = new ArrayList<>();
+
+            for (Side side : Side.values()) {
+                TilePos nTilePos = new TilePos(cTilePos.x() + side.getOffsetX(), cTilePos.y() + side.getOffsetY(), cTilePos.z() + side.getOffsetZ());
+
+                if (world.getBlockType(nTilePos) == this.block) {
+                    int nData = world.getBlockData(nTilePos);
+
+                    if (isSideOpen(nData, side.getOpposite())) {
+                        validSides.add(side);
+                    }
+                }
+            }
+
+            int numOfValidSides = validSides.size();
+
+            if (numOfValidSides == 1) {
+                Side autoConnectedSide = validSides.get(0);
+
+                if (sideFacingPlayer != autoConnectedSide && sideFacingPlayer != Side.NONE) {
+                    stateToPlaceAs = getStateFromTwoSides(autoConnectedSide, sideFacingPlayer);
+                } else {
+                    stateToPlaceAs = getNextState(0, autoConnectedSide);
+                }
+            } else if (numOfValidSides == 2) {
+                stateToPlaceAs = getStateFromTwoSides(validSides.get(0), validSides.get(1));
+            } else if (numOfValidSides > 2) {
+                TilePos nTilePos = new TilePos(cTilePos).add(sideClickedAgainst);
+
+                boolean isMatchingNeighbor = world.getBlockType(nTilePos) == this.block;
+                boolean isNeighborSideOpen = isSideOpen(world.getBlockData(nTilePos), sidePlacedOnto);
+
+                if (isMatchingNeighbor && isNeighborSideOpen) {
+                    if (sideFacingPlayer != sideClickedAgainst && sideFacingPlayer != Side.NONE) {
+                        stateToPlaceAs = getStateFromTwoSides(sideClickedAgainst, sideFacingPlayer);
+                    } else {
+                        stateToPlaceAs = getNextState(0, sideClickedAgainst);
+                    }
+                }
+            }
+        }
+
+        if (stateToPlaceAs != 0) {
+            world.setBlockDataNotify(cTilePos, stateToPlaceAs);
+            this.onNeighborChanged(world, cTilePos, this.block);
+        }
+    }
+
+
+    /**
+     * Handles right-click interactions with an existing conduit in the world.
+     * <p>
+     * This method controls the manual toggling of conduit connection ports. The interaction succeeds
+     * and toggles the port state under the following conditions:
+     * <ul>
+     * <li>The player is holding nothing (empty hand).</li>
+     * <li>The player is holding another conduit, but clicks on a <b>closed</b> port.</li>
+     * </ul>
+     * <p>
+     * If the player is holding a different block/item, or holding a conduit and clicks an <b>open</b> port,
+     * this method yields the interaction to allow standard block placement or item usage.
+     * <p>
+     * Successful interactions will update the block state, trigger neighbor updates, play activation
+     * sound effects, and spawn visual port particles.
+     * @param world    The world containing the block.
+     * @param cTilePos The position of the conduit being interacted with.
+     * @param player   The player interacting with the block.
+     * @param side     The specific face of the conduit that was clicked (can be null if interacted via indirect means).
+     * @param xHit     The precise X coordinate of the cursor hit on the block face.
+     * @param yHit     The precise Y coordinate of the cursor hit on the block face.
+     * @return {@code true} if the interaction was consumed (port toggled); {@code false} if the interaction
+     * should pass through to default item/block placement logic.
+     */
+    @Override
+    public boolean onInteracted(final @NotNull World world, final @NotNull TilePosc cTilePos, final @NotNull Player player, final @Nullable Side side, final double xHit, final double yHit) {
+        if(side == null) return false;
+
+        boolean holdingNothing = player.getHeldItem() == null;
+        boolean holdingConduit = !holdingNothing && player.getHeldItem().itemID == this.block.id();
+
+        if (!holdingNothing && !holdingConduit) {
+            return false;
+        }
+
+        int cData = world.getBlockData(cTilePos);
+
+        if (holdingConduit && isSideOpen(cData, side)) {
+            return false;
+        }
+
+        int cState = cData % STATE_COUNT;
+        int cStrength = cData / STATE_COUNT;
+
+        int nextState = getNextState(cState, side);
+
+        if (nextState != cState) {
+            world.setBlockDataNotify(cTilePos, cStrength * STATE_COUNT + nextState);
+            onNeighborChanged(world, cTilePos, this.block);
+
+            world.playSoundEffect(player, SoundCategory.WORLD_SOUNDS, cTilePos.x(), cTilePos.y(), cTilePos.z(), "tile.activator.use", 1, 1.2f);
+            world.playSoundEffect(player, SoundCategory.WORLD_SOUNDS, cTilePos.x(), cTilePos.y(), cTilePos.z(), "note.hat", 0.7f, 0.4f);
+            world.playBlockEvent(cTilePos, LevelListener.EVENT_CONDUIT_PORT_PARTICLES, packEventConduitData(side, cStrength));
+
+            return true;
+        }
+
+        return false;
+    }
+
+    public static byte packEventConduitData(Side side, int strength) {
+        if (side == null) side = Side.NONE;
+        int s = Math.max(0, Math.min(10, strength));
+        int sideId = side.getId() & 0x07;
+        return (byte) ((sideId << 4) | (s & 0x0F));
+    }
+
+    @Override
+    public boolean isEmittingSignal(final @NotNull WorldSource world, final @NotNull TilePosc tilePos, final @NotNull Side side) {
+        int data = world.getBlockData(tilePos);
+        int currentPower = data / STATE_COUNT;
+
+        if (currentPower == 0) {
+            return false;
+        }
+
+        //can only emit signal out of an open face
+        Side conduitFace = side.getOpposite();
+        return isSideOpen(data, conduitFace);
+    }
+
+    @Override
+    public boolean isEmittingDirectSignal(final @NotNull World world, final @NotNull TilePosc tilePos, final @NotNull Side side) {
+        int data = world.getBlockData(tilePos);
+        int currentPower = data / STATE_COUNT;
+
+        if (currentPower == 0) {
+            return false;
+        }
+
+        //can only emit signal out of an open face
+        Side conduitFace = side.getOpposite();
+        return isSideOpen(data, conduitFace);
+    }
+
+
+    /**
+     * Dynamically determines the next state of a conduit based on a queried side (interacted with, placed against, trying to connect to).
+     * @param currentState The current state of the conduit
+     * @param sideQueried The side that was queried.
+     * @return The new conduit state based on the interaction
+     */
+    private int getNextState(int currentState, @NotNull Side sideQueried) {
+        Side[] nestState = STATE_TABLE[currentState];
+
+        for (Side side : nestState) {
+            if (side == sideQueried) {
+                if (nestState.length == 1) return 0;
+                Side remainingSide = (nestState[0] == sideQueried) ? nestState[1] : nestState[0];
+                return remainingSide.getId() + 1;
+            }
+        }
+
+        if (nestState.length == 0) {
+            return sideQueried.getId() + 1;
+        }
+        else if (nestState.length == 1) {
+            return getStateFromTwoSides(nestState[0], sideQueried);
+        }
+
+        return currentState;
+    }
+
+    /**
+     * Determines a state that contains the two provided sides.
+     * @param s1 The first open side
+     * @param s2 The second open side
+     * @return The conduit state that matches the two side parameters or 0 if no match is found
+     */
+    private int getStateFromTwoSides(@NotNull Side s1, @NotNull Side s2) {
+        for (int i = 7; i < STATE_TABLE.length; i++) {
+            Side[] pair = STATE_TABLE[i];
+
+            boolean match1 = (pair[0] == s1 || pair[1] == s1);
+            boolean match2 = (pair[0] == s2 || pair[1] == s2);
+
+            if (match1 && match2) {
+                return i;
+            }
+        }
+        return 0;
+    }
+
+
+
+    /**
+     * Determines the incoming signal strength from a neighboring block on any given side of the conduit and converts it. <div></div>
+     * n = neighbor (ex. nTilePos = neighboring blocks TilePos) <div></div>
+     * c = conduit (ex. cStrength = this conduit Signal Strength) <div></div>
+     * @param world The world
+     * @param cTilePos The position of the conduit
+     * @param side The side to check
+     * @return A converted signal value from 0 to 7
+     */
+    public static int getAndConvertNeighborSignal(@NotNull WorldSource world, @NotNull TilePosc cTilePos, @NotNull Side side) {
+
+        TilePos nTilePos = new TilePos(cTilePos.x() + side.getOffsetX(), cTilePos.y() + side.getOffsetY(), cTilePos.z() + side.getOffsetZ());
+        Block<?> nBlockType = world.getBlockType(nTilePos);
+
+        int cStrength = world.getBlockData(cTilePos) / STATE_COUNT;
+
+        if(nBlockType == Blocks.WIRE_REDSTONE) {
+            int nData = world.getBlockData(nTilePos);
+            int nStrength = nData & BlockLogicWireRedstone.MASK_POWER;
+            return signalConverter(nStrength, false);
+        }
+
+        if (nBlockType == Blocks.RUBYGLASS_CONDUIT) {
+            int nData = world.getBlockData(nTilePos);
+            if (isSideOpen(nData, side.getOpposite())) {
+                int nStrength = nData / STATE_COUNT;
+
+                if (nStrength <= cStrength) return 0;
+
+                return nStrength - 1;
+            }
+            return 0;
+        }
+
+        if (world instanceof World world1) {
+
+            boolean isPowerCube = nBlockType ==
+                    Blocks.BLOCK_REDSTONE ||
+                    nBlockType == Blocks.PUMPKIN_REDSTONE ||
+                    nBlockType == Blocks.MATCHER_ACTIVE;
+
+            if (world1.isBlockNormalCube(nTilePos) && !isPowerCube) {
+                for (Side s : Side.sides) {
+                    TilePos queryPos = new TilePos(nTilePos.x() + s.getOffsetX(), nTilePos.y() + s.getOffsetY(), nTilePos.z() + s.getOffsetZ());
+                    Block<?> queryBlock = world1.getBlockType(queryPos);
+
+                    if (queryBlock != Blocks.WIRE_REDSTONE && queryBlock != Blocks.RUBYGLASS_CONDUIT) {
+                        if (queryBlock.isEmittingDirectSignal(world1, queryPos, s)) {
+                            return PROPAGATION_LENGTH;
+                        }
+                    }
+                }
+            } else {
+                if (nBlockType.isEmittingSignal(world1, nTilePos, side)) {
+                    return PROPAGATION_LENGTH;
+                }
+            }
+        } else {
+            if (nBlockType.isSignalSource() && nBlockType.isEmittingSignal(world, nTilePos, side)) {
+                return PROPAGATION_LENGTH;
+            }
+        }
+        return 0;
+    }
+
+    /**
+     * Determines a conduit power level output toward a given side, or 0 if that side is closed. <div></div>
+     *      c = conduit (ex. cStrength = this conduit Signal Strength) <div></div>
+     * @param cData The metadata of the conduit.
+     * @param sideTowardReceiver The side of the conduit facing the block asking for power.
+     * @return Conduit power (0-{@link #PROPAGATION_LENGTH})
+     */
+    public static int getOutputPower(int cData, @NotNull Side sideTowardReceiver) {
+        return isSideOpen(cData, sideTowardReceiver) ? cData / STATE_COUNT : 0;
+    }
+
+    /**
+     * Determines if a specific side on a conduit is open. <div></div>
+     *      c = conduit (ex. cStrength = this conduit Signal Strength) <div></div>
+     * @param cData The metadata of the conduit to check.
+     * @param side The side to check.
+     * @return true if the side is open, false otherwise.
+     */
+    public static boolean isSideOpen(int cData, @NotNull Side side) {
+        int cState = cData % STATE_COUNT;
+
+        if (cState == 0) {
+            return false;
+        }
+
+        Side[] openSides = STATE_TABLE[cState];
+
+        for (Side s : openSides) {
+            if (s == side) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Determines which sides on a given conduit are open. <div></div>
+     *      c = conduit (ex. cStrength = this conduit Signal Strength) <div></div>
+     * @param cData The metadata of the conduit to check.
+     * @return a {@link Side} {@link Array} of sides that are open.
+     */
+    public static Side[] getOpenSides(int cData){
+        return STATE_TABLE[cData % STATE_COUNT];
+    }
+
+    public static final int[] REDSTONE_TO_CONDUIT = {0, 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 10};
+    public static final int[] CONDUIT_TO_REDSTONE = {0, 1, 2, 4, 5, 7, 8, 10, 11, 13, 14};
+
+    /**
+     * Converts signal strengths between redstone and conduit.
+     * @param strength The signal strength to convert
+     * @param conduitToRedstone Whether to convert conduit -> redstone or vice versa
+     * @return The converted signal strength (0-10)->(0-15) or (0-15)->(0-10)
+     */
+    public static int signalConverter(int strength, boolean conduitToRedstone) {
+        return conduitToRedstone
+                ? CONDUIT_TO_REDSTONE[Math.max(0, Math.min(PROPAGATION_LENGTH, strength))]
+                : REDSTONE_TO_CONDUIT[Math.max(0, Math.min(15, strength))];
+    }
+}
+
+
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicWireRedstone.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicWireRedstone.java
index 149ddad2f..04bb1f4eb 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicWireRedstone.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicWireRedstone.java
@@ -9,6 +9,7 @@ import net.minecraft.core.current.util.BlockState;
 import net.minecraft.core.enums.EnumDropCause;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.item.Items;
+import net.minecraft.core.sound.SoundCategory;
 import net.minecraft.core.util.helper.Axis;
 import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.world.World;
@@ -264,6 +265,11 @@ public class BlockLogicWireRedstone extends BlockLogic implements ISupportable {
     }
 
     private int checkTarget(@NotNull World world, @NotNull TilePos tilePos, int sourceStrength) {
+        if(world.getBlockType(tilePos) == Blocks.RUBYGLASS_CONDUIT){
+            int cStrength = world.getBlockData(tilePos) / BlockLogicRubyglassConduit.STATE_COUNT;
+            return Math.max(BlockLogicRubyglassConduit.signalConverter(cStrength, true), sourceStrength);
+        }
+
         if (world.getBlockType(tilePos) != this.block) {
             return sourceStrength;
         }
@@ -314,33 +320,33 @@ public class BlockLogicWireRedstone extends BlockLogic implements ISupportable {
         TilePos queryPos = new TilePos();
         boolean negXShouldConnectTo =
             shouldConnectTo(source, tilePos.add(-1, 0, 0, queryPos), Side.WEST) ||
-                shouldConnectToDiagonal(source, tilePos.add(-1, -1, 0, queryPos), Side.WEST, Side.BOTTOM);
+                    shouldConnectToDiagonal(source, tilePos.add(-1, -1, 0, queryPos), Side.WEST, Side.BOTTOM);
 
         boolean posXShouldConnectTo =
             shouldConnectTo(source, tilePos.add(1, 0, 0, queryPos), Side.EAST) ||
-                shouldConnectToDiagonal(source, tilePos.add(1, -1, 0, queryPos), Side.EAST, Side.BOTTOM);
+                    shouldConnectToDiagonal(source, tilePos.add(1, -1, 0, queryPos), Side.EAST, Side.BOTTOM);
 
         boolean negZShouldConnectTo =
             shouldConnectTo(source, tilePos.add(0, 0, -1, queryPos), Side.NORTH) ||
-                shouldConnectToDiagonal(source, tilePos.add(0, -1, -1, queryPos), Side.NORTH, Side.BOTTOM);
+                    shouldConnectToDiagonal(source, tilePos.add(0, -1, -1, queryPos), Side.NORTH, Side.BOTTOM);
 
         boolean posZShouldConnectTo =
             shouldConnectTo(source, tilePos.add(0, 0, 1, queryPos), Side.SOUTH) ||
-                shouldConnectToDiagonal(source, tilePos.add(0, -1, 1, queryPos), Side.SOUTH, Side.BOTTOM);
+                    shouldConnectToDiagonal(source, tilePos.add(0, -1, 1, queryPos), Side.SOUTH, Side.BOTTOM);
 
         if (!source.isBlockNormalCube(tilePos.up(queryPos))) {
             if (isClimbableWall(source, tilePos.add(-1, 0, 0, queryPos), Side.WEST) &&
                 shouldConnectToDiagonal(source, tilePos.add(-1, 1, 0, queryPos), Side.WEST, Side.TOP))
-            {negXShouldConnectTo = true;}
+                    {negXShouldConnectTo = true;}
             if (isClimbableWall(source, tilePos.add(1, 0, 0, queryPos), Side.EAST) &&
                 shouldConnectToDiagonal(source, tilePos.add(1, 1, 0, queryPos), Side.EAST, Side.TOP))
-            {posXShouldConnectTo = true;}
+                    {posXShouldConnectTo = true;}
             if (isClimbableWall(source, tilePos.add(0, 0, -1, queryPos), Side.NORTH) &&
                 shouldConnectToDiagonal(source, tilePos.add(0, 1, -1, queryPos), Side.NORTH, Side.TOP))
-            {negZShouldConnectTo = true;}
+                    {negZShouldConnectTo = true;}
             if (isClimbableWall(source, tilePos.add(0, 0, 1, queryPos), Side.SOUTH) &&
                 shouldConnectToDiagonal(source, tilePos.add(0, 1, 1, queryPos), Side.SOUTH, Side.TOP))
-            {posZShouldConnectTo = true;}
+                    {posZShouldConnectTo = true;}
         }
         // Make wire emit block updates when a redirection happens
         if (source instanceof World world) {
@@ -420,6 +426,9 @@ public class BlockLogicWireRedstone extends BlockLogic implements ISupportable {
             final Side direction = BlockLogicRepeater.getSideFromMeta(source.getBlockData(tilePos));
             return side == direction || side == direction.getOpposite();
         }
+        if (block == Blocks.RUBYGLASS_CONDUIT) {
+            return BlockLogicRubyglassConduit.isSideOpen(source.getBlockData(tilePos), side.getOpposite());
+        }
         return block.isSignalSource();
     }
 
@@ -436,7 +445,7 @@ public class BlockLogicWireRedstone extends BlockLogic implements ISupportable {
             return false;
         }
 
-        // Most of the code below is relevant to fixing the visual behavior
+        //Most of the code below is relevant to fixing the visual behavior
         // of diagonal connections on both transparent and non-transparent blocks
         // (now that it is done get me out of this class :) I don't want to touch it again)
 
@@ -512,21 +521,32 @@ public class BlockLogicWireRedstone extends BlockLogic implements ISupportable {
     }
 
     /** Helper to determine if a block acts as a solid cube for redstone climbing. <div></div> **/
-    private static boolean isSolidWallForWire(@NotNull WorldSource world, @NotNull TilePos pos, @NotNull Side faceToCheck) {
+    private static boolean isSolidWallForWire(WorldSource world, TilePos pos, Side faceToCheck) {
         if (world.isBlockNormalCube(pos)) return true;
-        //leave separate
+        if (world.getBlockType(pos) == Blocks.RUBYGLASS_CONDUIT) {
+            return !BlockLogicRubyglassConduit.isSideOpen(world.getBlockData(pos), faceToCheck);
+        }
         return false;
     }
 
-    private static boolean isBlockingConduit(@NotNull WorldSource world, @NotNull TilePos pos, @NotNull Side openSide) {
-        //leave in preparation of conduit logic
+    private static boolean isBlockingConduit(WorldSource world, TilePos pos, Side openSide) {
+        if (world.getBlockType(pos) == Blocks.RUBYGLASS_CONDUIT) {
+            return BlockLogicRubyglassConduit.isSideOpen(world.getBlockData(pos), openSide);
+        }
         return false;
     }
 
-    /** Determines if redstone dust is allowed to visually climb the side of a block. **/
-    public static boolean isClimbableWall(@NotNull WorldSource world, @NotNull TilePos wallTilePos, @NotNull Side directionToWall) {
-        if (world.isBlockNormalCube(wallTilePos)) return true;
-        //leave separate
+    /**
+     * Determines if redstone dust is allowed to visually climb the side of a block.
+     * Conduits, being an exception, can ONLY be climbed if the face pointing toward the bottom dust is closed.
+     */
+    public static boolean isClimbableWall(WorldSource world, TilePos wallTilePos, Side directionToWall) {
+        if (world.isBlockNormalCube(wallTilePos)) {
+            return true;
+        }
+        if (world.getBlockType(wallTilePos) == Blocks.RUBYGLASS_CONDUIT) {
+            return !BlockLogicRubyglassConduit.isSideOpen(world.getBlockData(wallTilePos), directionToWall.getOpposite());
+        }
         return false;
     }
 
diff --git a/game/core/src/main/java/net/minecraft/core/block/Blocks.java b/game/core/src/main/java/net/minecraft/core/block/Blocks.java
index d1594da1b..40b7fd06f 100644
--- a/game/core/src/main/java/net/minecraft/core/block/Blocks.java
+++ b/game/core/src/main/java/net/minecraft/core/block/Blocks.java
@@ -429,7 +429,7 @@ public final class Blocks {
 
     public static final @NotNull Block<?> BEDROCK = register("bedrock", "minecraft:block/bedrock", 260, (b) -> new BlockLogicBedrock(b, Materials.STONE))
         .withSound(BlockSounds.STONE).withSetUnbreakable().withBlastResistance(6000000F).withImmovableFlagSet()
-        .withTags(BlockTags.PISTON_CRUSHING, BlockTags.CHAINLINK_FENCES_CONNECT);
+        .withTags(BlockTags.PISTON_CRUSHING, BlockTags.CHAINLINK_FENCES_CONNECT, BlockTags.PREVENT_MOB_SPAWNS);
     public static final @NotNull Block<?> BONESHALE = register("boneshale", "minecraft:block/boneshale", 261, (b) -> new BlockLogicBoneShale(b, Materials.STONE))
         .withSound(BlockSounds.STONE).withHardness(16F).withBlastResistance(6000000F).withImmovableFlagSet()
         .withTags(BlockTags.MINEABLE_BY_PICKAXE, BlockTags.CHAINLINK_FENCES_CONNECT, BlockTags.NETHER_MOBS_SPAWN);
@@ -790,10 +790,10 @@ public final class Blocks {
         .withTags(BlockTags.MINEABLE_BY_AXE, BlockTags.PREVENT_MOB_SPAWNS);
 
     public static final @NotNull Block<BlockLogicMotionSensor> MOTION_SENSOR_IDLE = register("motionsensor.idle", "minecraft:block/motion_sensor_idle", 500, (BlockLogicSupplier<BlockLogicMotionSensor>)(b) -> new BlockLogicMotionSensor(b, false))
-        .withSound(BlockSounds.STONE).withHardness(2.0F).withBlastResistance(10F).withDisabledNeighborNotifyOnMetadataChange().withOverrideColor(MaterialColor.stone)
+        .withSound(BlockSounds.STONE).withHardness(1.5F).withBlastResistance(10F).withDisabledNeighborNotifyOnMetadataChange().withOverrideColor(MaterialColor.stone)
         .withTags(BlockTags.MINEABLE_BY_PICKAXE, BlockTags.CHAINLINK_FENCES_CONNECT);
     public static final @NotNull Block<BlockLogicMotionSensor> MOTION_SENSOR_ACTIVE = register("motionsensor.active", "minecraft:block/motion_sensor_active", 501, (BlockLogicSupplier<BlockLogicMotionSensor>)(b) -> new BlockLogicMotionSensor(b, true))
-        .withSound(BlockSounds.STONE).withLightEmission(0.3F).withHardness(2.0F).withBlastResistance(10F).withOverrideColor(MaterialColor.stone).setStatParent(() -> MOTION_SENSOR_IDLE)
+        .withSound(BlockSounds.STONE).withLightEmission(0.3F).withHardness(1.5F).withBlastResistance(10F).withOverrideColor(MaterialColor.stone).setStatParent(() -> MOTION_SENSOR_IDLE)
         .withTags(BlockTags.NOT_IN_CREATIVE_MENU, BlockTags.MINEABLE_BY_PICKAXE, BlockTags.CHAINLINK_FENCES_CONNECT);
 
     public static final @NotNull Block<BlockLogicRepeater> REPEATER_IDLE = register("repeater.idle", "minecraft:block/repeater_idle", 510, (BlockLogicSupplier<BlockLogicRepeater>)b -> new BlockLogicRepeater(b, false))
@@ -848,11 +848,11 @@ public final class Blocks {
         .withTags(BlockTags.MINEABLE_BY_PICKAXE);
 
     public static final @NotNull Block<?> DISPENSER_COBBLE_STONE = register("dispenser.cobble.stone", "minecraft:block/dispenser_cobble_stone", 560, BlockLogicDispenser::new)
-        .withSound(BlockSounds.STONE).withHardness(3.5F).withDisabledNeighborNotifyOnMetadataChange()
-        .withTags(BlockTags.MINEABLE_BY_PICKAXE);
+        .withSound(BlockSounds.STONE).withHardness(1.5F).withDisabledNeighborNotifyOnMetadataChange()
+        .withTags(BlockTags.MINEABLE_BY_PICKAXE, BlockTags.PREVENT_MOB_SPAWNS, BlockTags.CHAINLINK_FENCES_CONNECT);
     public static final @NotNull Block<?> ACTIVATOR_COBBLE_NETHERRACK = register("activator.cobble.netherrack", "minecraft:block/activator_cobble_netherrack", 561, BlockLogicActivator::new)
-        .withSound(BlockSounds.STONE).withHardness(3.5F).withDisabledNeighborNotifyOnMetadataChange()
-        .withTags(BlockTags.MINEABLE_BY_PICKAXE);
+        .withSound(BlockSounds.STONE).withHardness(1.5F).withDisabledNeighborNotifyOnMetadataChange()
+        .withTags(BlockTags.MINEABLE_BY_PICKAXE, BlockTags.CHAINLINK_FENCES_CONNECT, BlockTags.PREVENT_MOB_SPAWNS);
 
     public static final @NotNull Block<?> TRAPDOOR_PLANKS_OAK = register("trapdoor.planks.oak", "minecraft:block/trapdoor_planks_oak", 570, (b) -> new BlockLogicTrapDoor(b, Materials.WOOD))
         .withSound(BlockSounds.WOOD).withHardness(3F).withDisabledNeighborNotifyOnMetadataChange()
@@ -1142,9 +1142,9 @@ public final class Blocks {
         .withSound(BlockSounds.GLASS).withDisabledNeighborNotifyOnMetadataChange().withLightEmission(0.5F)
         .withTags(BlockTags.MINEABLE_BY_PICKAXE)
         .withOverrideColor(MaterialColor.rubyglass);
-    public static final Block<?> RUBYGLASS_CIRCUIT = register("rubyglass.circuit", "minecraft:block/rubyglass_circuit", 824, (b) -> new BlockLogic(b, Materials.STONE))
-        .withSound(BlockSounds.STONE).withDisabledNeighborNotifyOnMetadataChange().withHardness(1F)
-        .withTags(BlockTags.MINEABLE_BY_PICKAXE, BlockTags.INFINITE_BURN, BlockTags.CAVES_CUT_THROUGH, BlockTags.NETHER_MOBS_SPAWN)
+    public static final Block<BlockLogicRubyglassConduit> RUBYGLASS_CONDUIT = register("rubyglass.conduit", "minecraft:block/rubyglass_conduit", 824, (BlockLogicSupplier<BlockLogicRubyglassConduit>) b -> new BlockLogicRubyglassConduit(b, Materials.STONE))
+        .withSound(BlockSounds.STONE).withHardness(1.5F).withDisabledNeighborNotifyOnMetadataChange()
+        .withTags(BlockTags.MINEABLE_BY_PICKAXE, BlockTags.CHAINLINK_FENCES_CONNECT, BlockTags.PREVENT_MOB_SPAWNS)
         .withOverrideColor(MaterialColor.rubyglass);
 
     public static final Block<?> RUBYGLASS_NODE = register("rubyglass.node", "minecraft:block/rubyglass_node", 826, (b) -> new BlockLogicOreRubyglass(b))
@@ -1373,11 +1373,11 @@ public final class Blocks {
         .withTags(BlockTags.MINEABLE_BY_PICKAXE);
 
     public static final @NotNull Block<BlockLogic> MATCHER = register("matcher", "minecraft:block/matcher", 1040, (b) -> new BlockLogicMatcher(b, false))
-        .withSound(BlockSounds.STONE).withHardness(3.5F).withDisabledNeighborNotifyOnMetadataChange()
-        .withTags(BlockTags.MINEABLE_BY_PICKAXE);
+        .withSound(BlockSounds.STONE).withHardness(1.5F).withDisabledNeighborNotifyOnMetadataChange()
+        .withTags(BlockTags.MINEABLE_BY_PICKAXE, BlockTags.CHAINLINK_FENCES_CONNECT, BlockTags.PREVENT_MOB_SPAWNS);
     public static final @NotNull Block<BlockLogic> MATCHER_ACTIVE = register("matcher.active", "minecraft:block/matcher_active", 1041, (b) -> new BlockLogicMatcher(b, true))
-        .withSound(BlockSounds.STONE).withHardness(3.5F).withDisabledNeighborNotifyOnMetadataChange()
-        .withTags(BlockTags.MINEABLE_BY_PICKAXE, BlockTags.NOT_IN_CREATIVE_MENU);
+        .withSound(BlockSounds.STONE).withHardness(1.5F).withDisabledNeighborNotifyOnMetadataChange()
+        .withTags(BlockTags.MINEABLE_BY_PICKAXE, BlockTags.NOT_IN_CREATIVE_MENU, BlockTags.CHAINLINK_FENCES_CONNECT, BlockTags.PREVENT_MOB_SPAWNS);
 
     public static final @NotNull Block<BlockLogicStairs> STAIRS_BRICK_DIAMOND = register("stairs.brick.diamond", "minecraft:block/stairs_brick_diamond", 1049, (BlockLogicSupplier<BlockLogicStairs>)b -> new BlockLogicStairs(b, Blocks.BRICK_DIAMOND))
         .withSound(BlockSounds.PERMAFROST).withDisabledNeighborNotifyOnMetadataChange().withLitInteriorSurface(true)
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityBasket.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityBasket.java
index be9c13798..67bee6f83 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityBasket.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityBasket.java
@@ -4,27 +4,24 @@ import java.util.*;
 
 import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
 import it.unimi.dsi.fastutil.objects.Object2IntMap;
-import net.minecraft.core.block.Block;
 import net.minecraft.core.block.Blocks;
-import net.minecraft.core.block.motion.CarriedBlock;
-import net.minecraft.core.block.tag.BlockTags;
 import net.minecraft.core.entity.Entity;
 import net.minecraft.core.entity.EntityItem;
-import net.minecraft.core.entity.Mob;
 import net.minecraft.core.entity.player.Player;
+import net.minecraft.core.world.save.conversion.ItemConverters;
 import net.minecraft.core.item.Item;
 import net.minecraft.core.item.ItemStack;
 import com.mojang.nbt.tags.CompoundTag;
 import com.mojang.nbt.tags.ListTag;
 import net.minecraft.core.net.packet.Packet;
 import net.minecraft.core.net.packet.PacketTileEntityData;
-import net.minecraft.core.util.helper.Side;
-import net.minecraft.core.world.ICarriable;
 import net.minecraft.core.world.World;
 import org.jetbrains.annotations.NotNull;
 import org.joml.primitives.AABBd;
 import org.joml.primitives.AABBdc;
 
+import static net.minecraft.core.world.save.conversion.ItemConverters.legacyBucketConverter;
+
 public class TileEntityBasket extends TileEntity {
     private int numUnitsInside = 0;
     private final Object2IntMap<BasketEntry> contents = new Object2IntArrayMap<>();
@@ -51,6 +48,9 @@ public class TileEntityBasket extends TileEntity {
     }
 
     private int getItemSizeUnits(final Item item) {
+        if (item == null) {
+            return 0;
+        }
         return 64 / item.getItemStackLimit(null);
     }
 
@@ -255,6 +255,9 @@ public class TileEntityBasket extends TileEntity {
         }
 
         public static BasketEntry read(final CompoundTag tag) {
+
+            legacyBucketConverter(tag);
+
             final int id = tag.getShort("id");
             final int damage = tag.getShort("Damage");
             final CompoundTag data = tag.getCompound("Data");
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnace.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnace.java
index f2fab04d6..3a407f60c 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnace.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnace.java
@@ -12,6 +12,7 @@ import net.minecraft.core.data.registry.recipe.entry.RecipeEntryFurnace;
 import net.minecraft.core.entity.Entity;
 import net.minecraft.core.entity.EntityItem;
 import net.minecraft.core.entity.player.Player;
+import net.minecraft.core.item.ItemBucket;
 import net.minecraft.core.item.Items;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.net.packet.PacketTileEntityData;
@@ -19,7 +20,6 @@ import net.minecraft.core.net.packet.Packet;
 import net.minecraft.core.player.inventory.container.Container;
 import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.world.World;
-import net.minecraft.core.world.pos.TilePos;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -172,45 +172,45 @@ public class TileEntityFurnace extends TileEntity
     }
 
     @Override
-	public void tick()
-    {
+    public void tick() {
         boolean isBurnTimeHigherThan0 = currentBurnTime > 0;
         boolean furnaceUpdated = false;
 
-        if(currentBurnTime > 0)
-        {
+        if(currentBurnTime > 0) {
             currentBurnTime--;
         }
-        if(worldObj == null || !worldObj.isClientSide)
-        {
-        	if(worldObj == null || worldObj.getBlockId(tilePos.x, tilePos.y, tilePos.z) == Blocks.FURNACE_STONE_IDLE.id()) {
-            	if(currentBurnTime == 0 && furnaceItemStacks[SLOT_INGREDIENT] == null) {
-            		if(furnaceItemStacks[SLOT_FUEL] != null && furnaceItemStacks[SLOT_FUEL].itemID == Blocks.COBBLE_NETHERRACK.id()) {
-            			furnaceItemStacks[SLOT_FUEL].stackSize--;
-            			if(furnaceItemStacks[SLOT_FUEL].stackSize <= 0) furnaceItemStacks[SLOT_FUEL] = null;
-            			updateFurnace(true);
-            			furnaceUpdated = true;
-            		}
-            	}
-        	}
-        	
-            if(currentBurnTime == 0 && furnaceItemStacks[SLOT_FUEL] != null && canSmelt())
-            {
+
+        if(worldObj == null || !worldObj.isClientSide) {
+            if(worldObj == null || worldObj.getBlockId(tilePos.x, tilePos.y, tilePos.z) == Blocks.FURNACE_STONE_IDLE.id()) {
+                if(currentBurnTime == 0 && furnaceItemStacks[SLOT_INGREDIENT] == null) {
+                    if(furnaceItemStacks[SLOT_FUEL] != null && furnaceItemStacks[SLOT_FUEL].itemID == Blocks.COBBLE_NETHERRACK.id()) {
+                        furnaceItemStacks[SLOT_FUEL].stackSize--;
+                        if(furnaceItemStacks[SLOT_FUEL].stackSize <= 0) furnaceItemStacks[SLOT_FUEL] = null;
+                        updateFurnace(true);
+                        furnaceUpdated = true;
+                    }
+                }
+            }
+
+            if(currentBurnTime == 0 && furnaceItemStacks[SLOT_FUEL] != null && canSmelt()) {
                 maxBurnTime = currentBurnTime = getBurnTimeFromItem(furnaceItemStacks[SLOT_FUEL]);
-                if(currentBurnTime > 0)
-                {
+                if(currentBurnTime > 0) {
                     furnaceUpdated = true;
-                    if(furnaceItemStacks[SLOT_FUEL] != null)
-                    {
-                        if (furnaceItemStacks[SLOT_FUEL].getItem() == Items.BUCKET_LAVA)
-                        {
-                            furnaceItemStacks[SLOT_FUEL] = new ItemStack(Items.BUCKET);
+
+                    if(furnaceItemStacks[SLOT_FUEL] != null) {
+                        ItemStack fuelStack = furnaceItemStacks[SLOT_FUEL];
+
+                        if (fuelStack.getItem() instanceof ItemBucket && ItemBucket.getState(fuelStack) == ItemBucket.STATE_LAVA) {
+
+                            ItemBucket.setCharges(fuelStack, ItemBucket.getCharges(fuelStack) - 1);
+
+                            if (ItemBucket.getCharges(fuelStack) == 0) {
+                                ItemBucket.setState(fuelStack, ItemBucket.STATE_EMPTY);
+                            }
                         }
-                        else
-                        {
-                            furnaceItemStacks[SLOT_FUEL].stackSize--;
-                            if(furnaceItemStacks[SLOT_FUEL].stackSize <= 0)
-                            {
+                        else {
+                            fuelStack.stackSize--;
+                            if(fuelStack.stackSize <= 0) {
                                 furnaceItemStacks[SLOT_FUEL] = null;
                             }
                         }
@@ -218,26 +218,24 @@ public class TileEntityFurnace extends TileEntity
                 }
             }
 
-            if(isBurning() && canSmelt())
-            {
+            if(isBurning() && canSmelt()) {
                 currentCookTime++;
-                if(currentCookTime == maxCookTime)
-                {
+                if(currentCookTime == maxCookTime) {
                     currentCookTime = 0;
                     smeltItem();
                     furnaceUpdated = true;
                 }
-            } else
+            } else {
                 currentCookTime = 0;
+            }
 
-            if(isBurnTimeHigherThan0 != (currentBurnTime > 0))
-            {
+            if(isBurnTimeHigherThan0 != (currentBurnTime > 0)) {
                 furnaceUpdated = true;
                 updateFurnace(false);
             }
         }
-        if(furnaceUpdated)
-        {
+
+        if(furnaceUpdated) {
             setChanged();
         }
     }
@@ -317,11 +315,9 @@ public class TileEntityFurnace extends TileEntity
         }
     }
 
-    private int getBurnTimeFromItem(ItemStack itemStack)
-    {
-        if(itemStack == null)
-            return 0;
-        return LookupFuelFurnace.instance.getFuelYield(itemStack.getItem().id);
+    private int getBurnTimeFromItem(ItemStack itemStack) {
+        if (itemStack == null) return 0;
+        return LookupFuelFurnace.instance.getFuelYield(itemStack);
     }
 
     @Override
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnaceBlast.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnaceBlast.java
index 209aeb4bf..5414b53e6 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnaceBlast.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnaceBlast.java
@@ -279,10 +279,9 @@ public class TileEntityFurnaceBlast extends TileEntity
         }
     }
 
-    private int getBurnTimeFromItem(final ItemStack itemStack) {
-        if (itemStack == null)
-            return 0;
-        return LookupFuelFurnaceBlast.instance.getFuelYield(itemStack.getItem().id);
+    private int getBurnTimeFromItem(ItemStack itemStack) {
+        if (itemStack == null) return 0;
+        return LookupFuelFurnaceBlast.instance.getFuelYield(itemStack);
     }
 
     @Override
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityTrommel.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityTrommel.java
index cf28b7b55..39d1731ce 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityTrommel.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityTrommel.java
@@ -11,7 +11,6 @@ import net.minecraft.core.block.BlockLogicTrommel;
 import net.minecraft.core.block.motion.CarriedBlock;
 import net.minecraft.core.block.Block;
 import net.minecraft.core.crafting.LookupFuelFurnace;
-import net.minecraft.core.data.DataLoader;
 import net.minecraft.core.data.registry.recipe.entry.RecipeEntryTrommel;
 import net.minecraft.core.data.registry.Registries;
 import net.minecraft.core.entity.Entity;
@@ -19,15 +18,14 @@ import net.minecraft.core.entity.EntityItem;
 import net.minecraft.core.entity.monster.MobSlime;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.item.Item;
+import net.minecraft.core.item.ItemBucket;
 import net.minecraft.core.item.Items;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.player.inventory.container.Container;
 import net.minecraft.core.util.helper.Direction;
 import net.minecraft.core.util.helper.MathHelper;
-import net.minecraft.core.world.Dimension;
 import net.minecraft.core.world.World;
 import net.minecraft.core.world.pos.TilePos;
-import net.minecraft.core.world.save.LevelStorageDummy;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
@@ -214,23 +212,27 @@ public class TileEntityTrommel extends TileEntity
             if(!canProduce(SLOTS_INGREDIENT[this.nextToSieve])) {
                 this.nextToSieve = (this.nextToSieve + 1) % SLOTS_INGREDIENT.length;
             }
-            if(this.burnTime == 0 && canProduce(SLOTS_INGREDIENT[this.nextToSieve]))
-            {
+            if(this.burnTime == 0 && canProduce(SLOTS_INGREDIENT[this.nextToSieve])) {
                 this.currentItemBurnTime = this.burnTime = getItemBurnTime(this.itemStacks[SLOT_FUEL]);
-                if(this.burnTime > 0)
-                {
+
+                if(this.burnTime > 0) {
                     flag1 = true;
-                    if(this.itemStacks[SLOT_FUEL] != null)
-                    {
-                    	if(this.itemStacks[SLOT_FUEL].getItem() == Items.BUCKET_LAVA) {
-                            this.itemStacks[SLOT_FUEL] = new ItemStack(Items.BUCKET);
-                    	}else {
-                            this.itemStacks[SLOT_FUEL].stackSize--;
-                            if(this.itemStacks[SLOT_FUEL].stackSize <= 0)
-                            {
+                    if(this.itemStacks[SLOT_FUEL] != null) {
+                        ItemStack fuelStack = this.itemStacks[SLOT_FUEL];
+
+                        if (fuelStack.getItem() instanceof ItemBucket && ItemBucket.getState(fuelStack) == ItemBucket.STATE_LAVA) {
+
+                            ItemBucket.setCharges(fuelStack, ItemBucket.getCharges(fuelStack) - 1);
+
+                            if (ItemBucket.getCharges(fuelStack) == 0) {
+                                ItemBucket.setState(fuelStack, ItemBucket.STATE_EMPTY);
+                            }
+                        } else {
+                            fuelStack.stackSize--;
+                            if(fuelStack.stackSize <= 0) {
                                 this.itemStacks[SLOT_FUEL] = null;
                             }
-                    	}
+                        }
                     }
                 }
             }
@@ -408,9 +410,8 @@ public class TileEntityTrommel extends TileEntity
 
     private int getItemBurnTime(ItemStack itemStack)
     {
-        if(itemStack == null)
-            return 0;
-        return LookupFuelFurnace.instance.getFuelYield(itemStack.getItem().id);
+        if(itemStack == null) return 0;
+        return LookupFuelFurnace.instance.getFuelYield(itemStack);
     }
 
     @Override
diff --git a/game/core/src/main/java/net/minecraft/core/crafting/LookupFuelFurnace.java b/game/core/src/main/java/net/minecraft/core/crafting/LookupFuelFurnace.java
index 1ca5ca011..addeb075d 100644
--- a/game/core/src/main/java/net/minecraft/core/crafting/LookupFuelFurnace.java
+++ b/game/core/src/main/java/net/minecraft/core/crafting/LookupFuelFurnace.java
@@ -3,15 +3,19 @@ package net.minecraft.core.crafting;
 import it.unimi.dsi.fastutil.ints.Int2IntMap;
 import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
 import net.minecraft.core.item.Items;
+import net.minecraft.core.item.ItemStack;
+import net.minecraft.core.item.ItemBucket;
 import net.minecraft.core.block.Blocks;
 
-import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.Map;
+import java.util.function.Predicate;
 
 public class LookupFuelFurnace {
 
 	public static final LookupFuelFurnace instance = new LookupFuelFurnace();
 	protected final Int2IntMap fuelList = new Int2IntOpenHashMap();
+	protected final Map<Predicate<ItemStack>, Integer> advancedFuelList = new LinkedHashMap<>();
 
 	protected LookupFuelFurnace() {
 		register();
@@ -83,17 +87,33 @@ public class LookupFuelFurnace {
 		addFuelEntry(Items.COAL.id, 1600);
 		addFuelEntry(Items.NETHERCOAL.id, 6400);
 
-		addFuelEntry(Items.BUCKET_LAVA.id, 20000);
+		addAdvancedFuelEntry(stack -> stack.getItem() instanceof ItemBucket && ItemBucket.getState(stack) == ItemBucket.STATE_LAVA, 12800);
 	}
 
 	public void addFuelEntry(int id, int fuelYield) {
 		fuelList.put(id, fuelYield);
 	}
 
+	public void addAdvancedFuelEntry(Predicate<ItemStack> condition, int fuelYield) {
+		advancedFuelList.put(condition, fuelYield);
+	}
+
 	public int getFuelYield(int id) {
 		return fuelList.getOrDefault(id, 0);
 	}
 
+	public int getFuelYield(ItemStack stack) {
+		if (stack == null) return 0;
+
+		for (Map.Entry<Predicate<ItemStack>, Integer> entry : advancedFuelList.entrySet()) {
+			if (entry.getKey().test(stack)) {
+				return entry.getValue();
+			}
+		}
+
+		return getFuelYield(stack.getItem().id);
+	}
+
 	public Int2IntMap getFuelList() {
 		return fuelList;
 	}
diff --git a/game/core/src/main/java/net/minecraft/core/current/conduit/ConduitHandler.java b/game/core/src/main/java/net/minecraft/core/current/conduit/ConduitHandler.java
new file mode 100644
index 000000000..d0a6ea9b8
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/current/conduit/ConduitHandler.java
@@ -0,0 +1,181 @@
+package net.minecraft.core.current.conduit;
+
+import net.minecraft.core.block.Block;
+import net.minecraft.core.block.Blocks;
+import net.minecraft.core.block.BlockLogicRubyglassConduit;
+import net.minecraft.core.util.helper.Side;
+import net.minecraft.core.world.World;
+import net.minecraft.core.world.pos.TilePos;
+import net.minecraft.core.world.pos.TilePosc;
+
+import java.util.*;
+
+
+/**
+ * One-Dimensional Implementation of Alternate Current for rubyglass conduits. <div></div>
+ * Handles the logic and updates for any given conduit network and seamlessly integrates with other redstone components. <div></div>
+ *
+ * @author Melon Mojito (@melon_mojito)
+ */
+public class ConduitHandler {
+    private final World world;
+    private boolean updating = false;
+    private final Queue<TilePos> updateQueue = new LinkedList<>();
+
+    public ConduitHandler(World world) {
+        this.world = world;
+    }
+
+    public void onConduitUpdated(TilePosc pos) {
+        queueUpdate(new TilePos(pos));
+    }
+
+    public void onConduitAdded(TilePosc pos) {
+        queueUpdate(new TilePos(pos));
+    }
+
+    public void onConduitRemoved(TilePosc pos, int oldData) {
+        //update the sides this conduit was connected to
+        for (Side side : BlockLogicRubyglassConduit.getOpenSides(oldData)) {
+            TilePos nPos = new TilePos(pos).add(side);
+            if (world.getBlockType(nPos) == Blocks.RUBYGLASS_CONDUIT) {
+                queueUpdate(nPos);
+            }
+        }
+    }
+
+    private void queueUpdate(TilePos pos) {
+        updateQueue.add(pos);
+        if (!updating) {
+            processQueue();
+        }
+    }
+
+    private void processQueue() {
+        updating = true;
+        try {
+            while (!updateQueue.isEmpty()) {
+                TilePos startPos = updateQueue.poll();
+                // Ensure the block is still a conduit before updating the network,
+                // as it could have been broken while sitting in the queue.
+                if (world.getBlockType(startPos) == Blocks.RUBYGLASS_CONDUIT) {
+                    updateNetwork(startPos);
+                }
+            }
+        } finally {
+            updating = false;
+        }
+    }
+
+    private void updateNetwork(TilePos startPos) {
+        //build the network
+        Set<TilePos> network = new HashSet<>();
+        Queue<TilePos> toSearch = new LinkedList<>();
+        toSearch.add(startPos);
+
+        while (!toSearch.isEmpty()) {
+            TilePos pos = toSearch.poll();
+            if (network.add(pos)) {
+                int data = world.getBlockData(pos);
+                for (Side side : BlockLogicRubyglassConduit.getOpenSides(data)) {
+                    TilePos nPos = new TilePos(pos).add(side);
+                    if (world.getBlockType(nPos) == Blocks.RUBYGLASS_CONDUIT &&
+                            BlockLogicRubyglassConduit.isSideOpen(world.getBlockData(nPos), side.getOpposite())) {
+                        toSearch.add(nPos);
+                    }
+                }
+            }
+        }
+
+        //external sources: levers, buttons, etc.
+        Map<TilePos, Integer> externalPower = new HashMap<>();
+        for (TilePos tilePos : network) {
+            int data = world.getBlockData(tilePos);
+            int currentPower = data / BlockLogicRubyglassConduit.STATE_COUNT;
+            int maxExternalPower = 0;
+
+            for (Side side : BlockLogicRubyglassConduit.getOpenSides(data)) {
+                TilePos nTilePos = new TilePos(tilePos).add(side);
+                Block<?> nBlockType = world.getBlockType(nTilePos);
+
+                if (nBlockType != Blocks.RUBYGLASS_CONDUIT) {
+                    int external = BlockLogicRubyglassConduit.getAndConvertNeighborSignal(world, tilePos, side);
+
+                    //break the recursive loop for redstone dust
+                    if (nBlockType == Blocks.WIRE_REDSTONE) {
+                        if (external < currentPower) {
+                            external = 0;
+                        }
+                    }
+
+                    if (external > maxExternalPower) maxExternalPower = external;
+                }
+            }
+            externalPower.put(tilePos, maxExternalPower);
+        }
+
+        //propagate from roots with priority
+        Map<TilePos, Integer> newPowerLevels = new HashMap<>();
+        for (TilePos tilePos : network) newPowerLevels.put(tilePos, 0);
+
+        PriorityQueue<ConduitNode> powerQueue = new PriorityQueue<>((a, b) -> Integer.compare(b.currentPower, a.currentPower));
+        for (Map.Entry<TilePos, Integer> entry : externalPower.entrySet()) {
+            if (entry.getValue() > 0) {
+                powerQueue.add(new ConduitNode(entry.getKey(), entry.getValue()));
+            }
+        }
+
+        while (!powerQueue.isEmpty()) {
+            ConduitNode node = powerQueue.poll();
+            //ignore weaker path
+            if (node.currentPower <= newPowerLevels.get(node.tilePos)) continue;
+
+            newPowerLevels.put(node.tilePos, node.currentPower);
+
+            //propagate power down the line
+            if (node.currentPower > 1) {
+                int data = world.getBlockData(node.tilePos);
+                for (Side side : BlockLogicRubyglassConduit.getOpenSides(data)) {
+                    TilePos nPos = new TilePos(node.tilePos).add(side);
+                    if (network.contains(nPos)) {
+                        powerQueue.add(new ConduitNode(nPos, node.currentPower - 1));
+                    }
+                }
+            }
+        }
+
+        Set<TilePos> blocksToNotify = new HashSet<>();
+        for (TilePos tilePos : network) {
+            int oldData = world.getBlockData(tilePos);
+            int oldPower = oldData / BlockLogicRubyglassConduit.STATE_COUNT;
+            int newPower = newPowerLevels.get(tilePos);
+
+            if (oldPower != newPower) {
+                int state = oldData % BlockLogicRubyglassConduit.STATE_COUNT;
+
+                //suppress recursive neighbor updates while data is being set
+                world.noNeighborUpdate = true;
+                world.setBlockDataNotify(tilePos, newPower * BlockLogicRubyglassConduit.STATE_COUNT + state);
+                world.noNeighborUpdate = false;
+
+                //queue updates ONLY for blocks facing the open sides that ARE NOT conduits in this network
+                for (Side side : BlockLogicRubyglassConduit.getOpenSides(state)) {
+                    TilePos targetPos = new TilePos(tilePos).add(side);
+                    if (!network.contains(targetPos)) {
+                        blocksToNotify.add(targetPos);
+                    }
+                }
+            }
+        }
+
+        //notify all neighbors after the network is done with its logic/updates
+        for (TilePos nTilePos : blocksToNotify) {
+            Block<?> neighborBlockType = world.getBlockType(nTilePos);
+            neighborBlockType.onNeighborChanged(world, nTilePos, Blocks.RUBYGLASS_CONDUIT);
+
+            if (neighborBlockType != Blocks.WIRE_REDSTONE) {
+                world.notifyBlocksOfNeighborChange(nTilePos, Blocks.RUBYGLASS_CONDUIT);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/current/conduit/ConduitNode.java b/game/core/src/main/java/net/minecraft/core/current/conduit/ConduitNode.java
new file mode 100644
index 000000000..2a829e0e1
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/current/conduit/ConduitNode.java
@@ -0,0 +1,13 @@
+package net.minecraft.core.current.conduit;
+
+import net.minecraft.core.world.pos.TilePos;
+
+public class ConduitNode {
+    TilePos tilePos;
+    int currentPower;
+
+    ConduitNode(TilePos tilePos, int currentPower) {
+        this.tilePos = tilePos;
+        this.currentPower = currentPower;
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/current/wire/Node.java b/game/core/src/main/java/net/minecraft/core/current/wire/Node.java
index 87ae2910b..efabe2edc 100644
--- a/game/core/src/main/java/net/minecraft/core/current/wire/Node.java
+++ b/game/core/src/main/java/net/minecraft/core/current/wire/Node.java
@@ -15,7 +15,7 @@ import java.util.Arrays;
  *
  * @author Space Walker
  */
-class Node {
+public class Node {
 
     // flags that encode the Node type
     private static final int CONDUCTOR = 0b01;
diff --git a/game/core/src/main/java/net/minecraft/core/current/wire/PriorityQueue.java b/game/core/src/main/java/net/minecraft/core/current/wire/PriorityQueue.java
index 5f38cce11..30b00cf1b 100644
--- a/game/core/src/main/java/net/minecraft/core/current/wire/PriorityQueue.java
+++ b/game/core/src/main/java/net/minecraft/core/current/wire/PriorityQueue.java
@@ -7,7 +7,7 @@ import java.util.AbstractQueue;
 import java.util.Arrays;
 import java.util.Iterator;
 
-class PriorityQueue extends AbstractQueue<Node> {
+public class PriorityQueue extends AbstractQueue<Node> {
 
 	private static final int OFFSET = -BlockLogicWireRedstone.SIGNAL_MIN;
 
diff --git a/game/core/src/main/java/net/minecraft/core/current/wire/SimpleQueue.java b/game/core/src/main/java/net/minecraft/core/current/wire/SimpleQueue.java
index 0fcef5f66..405861d9c 100644
--- a/game/core/src/main/java/net/minecraft/core/current/wire/SimpleQueue.java
+++ b/game/core/src/main/java/net/minecraft/core/current/wire/SimpleQueue.java
@@ -3,7 +3,7 @@ package net.minecraft.core.current.wire;
 import java.util.AbstractQueue;
 import java.util.Iterator;
 
-class SimpleQueue extends AbstractQueue<WireNode> {
+public class SimpleQueue extends AbstractQueue<WireNode> {
 
 	private WireNode head;
 	private WireNode tail;
diff --git a/game/core/src/main/java/net/minecraft/core/current/wire/WireConnection.java b/game/core/src/main/java/net/minecraft/core/current/wire/WireConnection.java
index b2bb579e0..86c6fa7bd 100644
--- a/game/core/src/main/java/net/minecraft/core/current/wire/WireConnection.java
+++ b/game/core/src/main/java/net/minecraft/core/current/wire/WireConnection.java
@@ -7,7 +7,7 @@ package net.minecraft.core.current.wire;
  * 
  * @author Space Walker
  */
-class WireConnection {
+public class WireConnection {
 
 	/** The connected wire. */
 	final WireNode wire;
diff --git a/game/core/src/main/java/net/minecraft/core/current/wire/WireConnectionManager.java b/game/core/src/main/java/net/minecraft/core/current/wire/WireConnectionManager.java
index a1309f169..4725ac1a7 100644
--- a/game/core/src/main/java/net/minecraft/core/current/wire/WireConnectionManager.java
+++ b/game/core/src/main/java/net/minecraft/core/current/wire/WireConnectionManager.java
@@ -1,12 +1,15 @@
 package net.minecraft.core.current.wire;
 
+import net.minecraft.core.block.BlockLogicRubyglassConduit;
+import net.minecraft.core.block.Blocks;
 import net.minecraft.core.current.wire.WireHandler.Directions;
 import net.minecraft.core.current.wire.WireHandler.NodeProvider;
+import net.minecraft.core.util.helper.Side;
 
 import java.util.Arrays;
 import java.util.function.Consumer;
 
-class WireConnectionManager {
+public class WireConnectionManager {
 
 	/** The owner of these connections. */
 	final WireNode owner;
@@ -44,8 +47,15 @@ class WireConnectionManager {
 			clear();
 		}
 
-		boolean belowIsConductor = nodes.getNeighbor(owner, Directions.DOWN).isConductor();
-		boolean aboveIsConductor = nodes.getNeighbor(owner, Directions.UP).isConductor();
+		//tried my best to keep original AltCurrent naming schemes when changing code here
+
+		Node ownerDown = nodes.getNeighbor(owner, Directions.DOWN);
+		boolean belowIsConductor = ownerDown.isConductor();
+
+		Node ownerUp = nodes.getNeighbor(owner, Directions.UP);
+		boolean aboveIsConductor = ownerUp.isConductor();
+		// Check if the block directly above the bottom wire is an open conduit facing down
+		boolean aboveIsOpenDown = isConduitOpenTowards(ownerUp, Directions.DOWN);
 
 		for (int iDir = 0; iDir < Directions.HORIZONTAL.length; iDir++) {
 			Node neighbor = nodes.getNeighbor(owner, iDir);
@@ -57,19 +67,35 @@ class WireConnectionManager {
 			}
 
 			boolean sideIsConductor = neighbor.isConductor();
+			int iOppDir = Directions.iOpposite(iDir);
+
+			//looking downwards logic
+			boolean blockedDown = sideIsConductor ||
+					isConduitOpenTowards(ownerDown, iDir) ||
+					isConduitOpenTowards(neighbor, Directions.UP);
 
-			if (!sideIsConductor) {
+			if (!blockedDown) {
 				Node node = nodes.getNeighbor(neighbor, Directions.DOWN);
 
 				if (node.isWire()) {
-					add(node.asWire(), iDir, belowIsConductor, true);
+					boolean offerDown = belowIsConductor ||
+							(ownerDown.state.is(Blocks.RUBYGLASS_CONDUIT) && !isConduitOpenTowards(ownerDown, iDir));
+					add(node.asWire(), iDir, offerDown, true);
 				}
 			}
-			if (!aboveIsConductor) {
+
+			//looking upwards logic
+			boolean blockedUp = aboveIsConductor ||
+					aboveIsOpenDown ||
+					isConduitOpenTowards(neighbor, iOppDir);
+
+			if (!blockedUp) {
 				Node node = nodes.getNeighbor(neighbor, Directions.UP);
 
 				if (node.isWire()) {
-					add(node.asWire(), iDir, true, sideIsConductor);
+					boolean acceptUp = sideIsConductor ||
+							(neighbor.state.is(Blocks.RUBYGLASS_CONDUIT) && !isConduitOpenTowards(neighbor, iOppDir));
+					add(node.asWire(), iDir, true, acceptUp);
 				}
 			}
 		}
@@ -79,6 +105,29 @@ class WireConnectionManager {
 		}
 	}
 
+	/**
+	 * Checks if a node is a Rubyglass Conduit and if a specific directional port is open.
+	 * @param node The node to check
+	 * @param iDir The Alternate Current direction we are querying
+	 * @return true if the node is a conduit AND the port facing iDir is open
+	 */
+	private boolean isConduitOpenTowards(Node node, int iDir) {
+		if (node.state.is(Blocks.RUBYGLASS_CONDUIT)) {
+			Side face = Side.NONE;
+            face = switch (iDir) {
+                case Directions.WEST -> Side.WEST;
+                case Directions.NORTH -> Side.NORTH;
+                case Directions.EAST -> Side.EAST;
+                case Directions.SOUTH -> Side.SOUTH;
+                case Directions.DOWN -> Side.BOTTOM;
+                case Directions.UP -> Side.TOP;
+                default -> face;
+            };
+			return face != Side.NONE && BlockLogicRubyglassConduit.isSideOpen(node.state.get(), face);
+		}
+		return false;
+	}
+
 	private void clear() {
 		Arrays.fill(heads, null);
 
diff --git a/game/core/src/main/java/net/minecraft/core/current/wire/WireHandler.java b/game/core/src/main/java/net/minecraft/core/current/wire/WireHandler.java
index 6884d9b05..61f8c1841 100644
--- a/game/core/src/main/java/net/minecraft/core/current/wire/WireHandler.java
+++ b/game/core/src/main/java/net/minecraft/core/current/wire/WireHandler.java
@@ -1,5 +1,6 @@
 package net.minecraft.core.current.wire;
 
+import net.minecraft.core.block.BlockLogicRubyglassConduit;
 import net.minecraft.core.block.BlockLogicWireRedstone;
 import net.minecraft.core.block.Block;
 import net.minecraft.core.block.Blocks;
@@ -754,6 +755,32 @@ public class WireHandler {
                 continue;
             }
 
+            //conduit logic
+            if (neighboringNode.state.is(Blocks.RUBYGLASS_CONDUIT)) {
+                int cData = neighboringNode.state.get();
+
+                Side conduitFace = getSide(iDir);
+
+                if (BlockLogicRubyglassConduit.isSideOpen(cData, conduitFace)) {
+                    int cStrength = cData / BlockLogicRubyglassConduit.STATE_COUNT;
+                    int convertedStrength = BlockLogicRubyglassConduit.signalConverter(cStrength, true);
+
+                    //step down the signal strength when passing from conduit to wire
+                    convertedStrength = Math.max(0, convertedStrength - 1);
+
+                    //if signal is less than current power, set to 0
+                    if (convertedStrength < wire.currentPower) {
+                        convertedStrength = 0;
+                    }
+
+                    if (convertedStrength > maxExternalPower) {
+                        maxExternalPower = convertedStrength;
+                    }
+                }
+                //this continue took me hours to find. I hate my life
+                continue;
+            }
+
             if (neighboringNode.isConductor() && hasDirectSignalTo(wire, neighboringNode, Directions.iOpposite(iDir))) {
                 maxExternalPower = POWER_MAX;
             }
@@ -769,9 +796,9 @@ public class WireHandler {
         }
     }
 
-    private static @NotNull Side getSideFromDirection(int iDirection) {
+    private static @NotNull Side getSide(int iDir) {
         Side wireSide = Side.NONE;
-        wireSide = switch (iDirection) {
+        wireSide = switch (iDir) {
             case Directions.WEST -> Side.WEST;
             case Directions.NORTH -> Side.NORTH;
             case Directions.EAST -> Side.EAST;
@@ -818,6 +845,10 @@ public class WireHandler {
         for (int iDir : Directions.I_EXCEPT[except]) {
             Node neighbor = getNeighbor(node, iDir);
 
+            if (neighbor.state.is(Blocks.WIRE_REDSTONE) || neighbor.state.is(Blocks.RUBYGLASS_CONDUIT)) {
+                continue;
+            }
+
             if (neighbor.isSignalSource() && neighbor.state.hasDirectSignal(this.world, neighbor.pos, Directions.ALL[iDir])) {
                 return true;
             }
diff --git a/game/core/src/main/java/net/minecraft/core/current/wire/WireNode.java b/game/core/src/main/java/net/minecraft/core/current/wire/WireNode.java
index b949ee832..0f8529fca 100644
--- a/game/core/src/main/java/net/minecraft/core/current/wire/WireNode.java
+++ b/game/core/src/main/java/net/minecraft/core/current/wire/WireNode.java
@@ -17,7 +17,7 @@ import org.slf4j.Logger;
  *
  * @author Space Walker
  */
-class WireNode extends Node {
+public class WireNode extends Node {
     private static final Logger LOGGER = LogUtils.getLogger();
 
     final WireConnectionManager connections;
diff --git a/game/core/src/main/java/net/minecraft/core/current/wire/WorldHelper.java b/game/core/src/main/java/net/minecraft/core/current/wire/WorldHelper.java
index ade1e452a..5c61fc059 100644
--- a/game/core/src/main/java/net/minecraft/core/current/wire/WorldHelper.java
+++ b/game/core/src/main/java/net/minecraft/core/current/wire/WorldHelper.java
@@ -8,7 +8,7 @@ import net.minecraft.core.world.pos.TilePos;
 import net.minecraft.core.world.pos.TilePosc;
 import org.jetbrains.annotations.NotNull;
 
-class WorldHelper {
+public class WorldHelper {
 
     private static final int Y_MIN = 0;
 
diff --git a/game/core/src/main/java/net/minecraft/core/data/registry/Registries.java b/game/core/src/main/java/net/minecraft/core/data/registry/Registries.java
index 3a9aa57b1..6d0139818 100644
--- a/game/core/src/main/java/net/minecraft/core/data/registry/Registries.java
+++ b/game/core/src/main/java/net/minecraft/core/data/registry/Registries.java
@@ -6,6 +6,7 @@ import net.minecraft.core.data.registry.recipe.RecipeEntryBase;
 import net.minecraft.core.data.registry.recipe.RecipeRegistry;
 import net.minecraft.core.data.registry.recipe.entry.*;
 import net.minecraft.core.item.Item;
+import net.minecraft.core.item.ItemBucket;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.data.gamerule.GameRule;
 import net.minecraft.core.data.gamerule.GameRules;
@@ -174,6 +175,24 @@ public class Registries extends Registry<Registry<?>> {
                 new ItemStack(Items.TOOL_SWORD_DIAMOND, 1, -1),
                 new ItemStack(Items.TOOL_SWORD_STEEL, 1, -1)));
 
+        // Find every single ItemBucket currently registered in the game
+        List<ItemBucket> allBuckets = new ArrayList<>();
+        for (Item item : Item.itemsList) {
+            if (item instanceof ItemBucket bucket) {
+                allBuckets.add(bucket);
+            }
+        }
+
+        // Dynamically register ItemGroups for all bucket states
+        for (NamespaceID stateId : ItemBucket.getRegisteredStateIds()) {
+            if (ItemBucket.STATE_EMPTY.equals(stateId)) continue;
+            List<ItemStack> variations = new ArrayList<>();
+            for (ItemBucket bucket : allBuckets) {
+                variations.add(ItemBucket.createRecipeInput(bucket, stateId));
+            }
+            ITEM_GROUPS.register(stateId.namespace() + ":" + stateId.value() + "_buckets", variations);
+        }
+
         if (RECIPES == null) new RecipeRegistry();
     }
 
diff --git a/game/core/src/main/java/net/minecraft/core/data/registry/recipe/RecipeSymbol.java b/game/core/src/main/java/net/minecraft/core/data/registry/recipe/RecipeSymbol.java
index a6e35927b..297c4c08a 100644
--- a/game/core/src/main/java/net/minecraft/core/data/registry/recipe/RecipeSymbol.java
+++ b/game/core/src/main/java/net/minecraft/core/data/registry/recipe/RecipeSymbol.java
@@ -1,5 +1,7 @@
 package net.minecraft.core.data.registry.recipe;
 
+import com.mojang.nbt.tags.CompoundTag;
+import com.mojang.nbt.tags.Tag;
 import net.minecraft.core.data.registry.Registries;
 import net.minecraft.core.item.ItemStack;
 
@@ -154,19 +156,39 @@ public class RecipeSymbol {
         return null;
     }
 
-    public boolean matches(ItemStack stack){
-        if(stack == null) return false;
+    public boolean matches(ItemStack stack) {
+        if (stack == null) return false;
         List<ItemStack> stacks = resolve();
-        boolean found = false;
+
         for (ItemStack resolvedStack : stacks) {
-            boolean foundId = resolvedStack.itemID == stack.itemID;
-            boolean foundMeta = resolvedStack.getMetadata() == -1 || resolvedStack.getMetadata() == stack.getMetadata();
-            if (foundId && foundMeta) {
-                found = true;
-                break;
+            if (resolvedStack.itemID != stack.itemID) continue;
+            if (resolvedStack.getMetadata() != -1 && resolvedStack.getMetadata() != stack.getMetadata()) continue;
+
+            CompoundTag resolvedData = resolvedStack.getData();
+            if (resolvedData.getValue().isEmpty()) return true;
+
+            if (isNBTPresentAndEqual(resolvedData, stack.getData())) return true;
+        }
+        return false;
+    }
+
+    /**
+     * Checks if all tags in the requirement are present in the tagToCompare tag
+     * If present, the values of the tags must be equal to return true
+     * @param requirement The requirement tag
+     * @param tagToCompare The tag to compare
+     * @return True if all tags are present and equal, false otherwise
+     */
+    private boolean isNBTPresentAndEqual(CompoundTag requirement, CompoundTag tagToCompare) {
+        for (String key : requirement.getValue().keySet()) {
+            Tag<?> requirementTag = requirement.getTag(key);
+            Tag<?> comparrisonTag = tagToCompare.getTag(key);
+
+            if (comparrisonTag == null || !comparrisonTag.equals(requirementTag)) {
+                return false;
             }
         }
-        return found;
+        return true;
     }
 
     public char getSymbol() {
diff --git a/game/core/src/main/java/net/minecraft/core/data/registry/recipe/entry/RecipeEntryCrafting.java b/game/core/src/main/java/net/minecraft/core/data/registry/recipe/entry/RecipeEntryCrafting.java
index 548e63057..c8796b640 100644
--- a/game/core/src/main/java/net/minecraft/core/data/registry/recipe/entry/RecipeEntryCrafting.java
+++ b/game/core/src/main/java/net/minecraft/core/data/registry/recipe/entry/RecipeEntryCrafting.java
@@ -2,6 +2,7 @@ package net.minecraft.core.data.registry.recipe.entry;
 
 import net.minecraft.core.data.registry.recipe.SearchQuery;
 import net.minecraft.core.data.registry.recipe.RecipeEntryBase;
+import net.minecraft.core.item.IItemContainer;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.player.inventory.container.ContainerCrafting;
 
@@ -35,9 +36,38 @@ public abstract class RecipeEntryCrafting<I,O> extends RecipeEntryBase<I,O,Void>
 
     public abstract boolean matchesQuery(SearchQuery query);
 
+    public boolean consumesContainer() {
+        return false;
+    }
+
+    public ItemStack[] onCraftResult(ContainerCrafting containerCrafting) {
+        ItemStack[] returnStack = new ItemStack[9];
+        for (int i = 0; i < containerCrafting.getContainerSize(); i++) {
+            ItemStack itemstack1 = containerCrafting.getItem(i);
+            if (itemstack1 == null) {
+                continue;
+            }
+
+            ItemStack gridStackSnapshot = ItemStack.copyItemStack(itemstack1);
+            containerCrafting.removeItem(i, 1);
+
+            if (!consumesContainer()) {
+
+                if (gridStackSnapshot.getItem() instanceof IItemContainer customContainer) {
+                    if (customContainer.hasContainerItem(gridStackSnapshot)) {
+                        containerCrafting.setItem(i, customContainer.getContainerItemStack(gridStackSnapshot));
+                    }
+                }
+                else if (gridStackSnapshot.getItem().hasContainerItem()) {
+                    assert gridStackSnapshot.getItem().getContainerItem() != null;
+                    containerCrafting.setItem(i, new ItemStack(gridStackSnapshot.getItem().getContainerItem()));
+                }
+            }
+        }
+        return returnStack;
+    }
+
     public abstract ItemStack getCraftingResult(ContainerCrafting containerCrafting);
 
     public abstract int getRecipeSize();
-
-    public abstract ItemStack[] onCraftResult(ContainerCrafting containerCrafting);
 }
diff --git a/game/core/src/main/java/net/minecraft/core/data/registry/recipe/entry/RecipeEntryCraftingShaped.java b/game/core/src/main/java/net/minecraft/core/data/registry/recipe/entry/RecipeEntryCraftingShaped.java
index 2a9c91fb3..b15a81821 100644
--- a/game/core/src/main/java/net/minecraft/core/data/registry/recipe/entry/RecipeEntryCraftingShaped.java
+++ b/game/core/src/main/java/net/minecraft/core/data/registry/recipe/entry/RecipeEntryCraftingShaped.java
@@ -9,6 +9,7 @@ import net.minecraft.core.data.registry.recipe.RecipeNamespace;
 import net.minecraft.core.data.registry.recipe.RecipeSymbol;
 import net.minecraft.core.data.registry.recipe.adapter.RecipeCraftingShapedJsonAdapter;
 import net.minecraft.core.data.registry.recipe.adapter.RecipeJsonAdapter;
+import net.minecraft.core.item.IItemContainer;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.player.inventory.container.ContainerCrafting;
 
@@ -193,19 +194,8 @@ public class RecipeEntryCraftingShaped extends RecipeEntryCrafting<RecipeSymbol[
     }
 
     @Override
-    public ItemStack[] onCraftResult(ContainerCrafting containerCrafting) {
-        ItemStack[] returnStack = new ItemStack[9];
-        for (int i = 0; i < containerCrafting.getContainerSize(); i++) {
-            ItemStack itemstack1 = containerCrafting.getItem(i);
-            if (itemstack1 == null) {
-                continue;
-            }
-            containerCrafting.removeItem(i, 1);
-            if (!consumeContainerItem && itemstack1.getItem().hasContainerItem()) {
-                containerCrafting.setItem(i, new ItemStack(itemstack1.getItem().getContainerItem()));
-            }
-        }
-        return returnStack;
+    public boolean consumesContainer() {
+        return this.consumeContainerItem;
     }
 
     @Override
diff --git a/game/core/src/main/java/net/minecraft/core/data/registry/recipe/entry/RecipeEntryCraftingShapeless.java b/game/core/src/main/java/net/minecraft/core/data/registry/recipe/entry/RecipeEntryCraftingShapeless.java
index c2cee07e5..d1f1b88bb 100644
--- a/game/core/src/main/java/net/minecraft/core/data/registry/recipe/entry/RecipeEntryCraftingShapeless.java
+++ b/game/core/src/main/java/net/minecraft/core/data/registry/recipe/entry/RecipeEntryCraftingShapeless.java
@@ -8,6 +8,7 @@ import net.minecraft.core.data.registry.recipe.RecipeNamespace;
 import net.minecraft.core.data.registry.recipe.RecipeSymbol;
 import net.minecraft.core.data.registry.recipe.adapter.RecipeCraftingShapelessJsonAdapter;
 import net.minecraft.core.data.registry.recipe.adapter.RecipeJsonAdapter;
+import net.minecraft.core.item.IItemContainer;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.player.inventory.container.ContainerCrafting;
 
@@ -140,25 +141,6 @@ public class RecipeEntryCraftingShapeless extends RecipeEntryCrafting<List<Recip
         return false;
     }
 
-    @Override
-    public ItemStack[] onCraftResult(ContainerCrafting containerCrafting){
-        ItemStack[] returnStack = new ItemStack[9];
-        for(int i = 0; i < containerCrafting.getContainerSize(); i++)
-        {
-            ItemStack itemStack = containerCrafting.getItem(i);
-            if(itemStack == null)
-            {
-                continue;
-            }
-            containerCrafting.removeItem(i, 1);
-            if(itemStack.getItem().hasContainerItem())
-            {
-                containerCrafting.setItem(i, new ItemStack(itemStack.getItem().getContainerItem()));
-            }
-        }
-        return returnStack;
-    }
-
     @Override
     public ItemStack getCraftingResult(ContainerCrafting containerCrafting)
     {
diff --git a/game/core/src/main/java/net/minecraft/core/entity/ConsumedFood.java b/game/core/src/main/java/net/minecraft/core/entity/ConsumedFood.java
index 1529a76ea..06bc29485 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/ConsumedFood.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/ConsumedFood.java
@@ -1,41 +1,46 @@
 package net.minecraft.core.entity;
 
 import net.minecraft.core.item.ItemFood;
+import net.minecraft.core.item.ItemStack;
 import org.jetbrains.annotations.NotNull;
 
 public class ConsumedFood {
     private final @NotNull Mob entity;
-    private final @NotNull ItemFood food;
+    private final @NotNull ItemStack stack;
+    private final @NotNull ItemFood foodItem;
     private int healRemaining;
     private int tickCounter = 0;
 
-    public ConsumedFood(@NotNull Mob entity, @NotNull ItemFood food) {
+    public ConsumedFood(@NotNull Mob entity, @NotNull ItemStack stack) {
         this.entity = entity;
-        this.food = food;
+        this.stack = stack;
 
-        this.healRemaining = food.getHealAmount();
+        this.foodItem = (ItemFood) stack.getItem();
+
+        this.healRemaining = this.foodItem.getHealAmount(this.stack);
     }
 
-    public void addFood() {
-        healRemaining += food.getHealAmount();
+    public void addFood(@NotNull ItemStack additionalStack) {
+        this.healRemaining += this.foodItem.getHealAmount(additionalStack);
     }
 
-    public int getHealRemaining(){
+    public int getHealRemaining() {
         return this.healRemaining;
     }
 
     public boolean isFinished() {
-        return healRemaining <= 0;
+        return this.healRemaining <= 0;
     }
 
     public void tick() {
         if (isFinished()) return;
 
-        tickCounter++;
-        if (tickCounter >= food.getTicksPerHeal()) {
-            tickCounter = 0;
-            healRemaining--;
-            entity.heal(1);
+        this.tickCounter++;
+
+        if (this.tickCounter >= this.foodItem.getTicksPerHeal(this.stack)) {
+            this.tickCounter = 0;
+            this.healRemaining--;
+            this.entity.heal(1);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/entity/Mob.java b/game/core/src/main/java/net/minecraft/core/entity/Mob.java
index e857d5465..6b58551ce 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/Mob.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/Mob.java
@@ -40,7 +40,7 @@ public abstract class Mob extends Entity {
     public static boolean SPAWN_NO_AI = false;
     public static final int DATA_VARIANT = 1;
     public static final int DATA_HEALTH = 2;
-    protected @NotNull Map<ItemFood, ConsumedFood> consumedFood = new HashMap<>();
+    protected Map<String, ConsumedFood> consumedFood = new HashMap<>();
     public int heartsHalvesLife;
     //    public float timeOffs;
 //    public float rotA;
@@ -318,15 +318,15 @@ public abstract class Mob extends Entity {
 
             }
         }
-        List<ItemFood> finishedFood = new ArrayList<>();
-        for (Map.Entry<ItemFood, ConsumedFood> entry : this.consumedFood.entrySet()) {
+        List<String> finishedFood = new ArrayList<>();
+        for (Map.Entry<String, ConsumedFood> entry : this.consumedFood.entrySet()) {
             entry.getValue().tick();
             if (entry.getValue().isFinished()) {
                 finishedFood.add(entry.getKey());
             }
         }
-        for (ItemFood food : finishedFood) {
-            this.consumedFood.remove(food);
+        for (String foodKey : finishedFood) {
+            this.consumedFood.remove(foodKey);
         }
         this.prevRotationUnused = this.unusedRotation1;
         this.yBodyRotO = this.yBodyRot;
@@ -480,15 +480,20 @@ public abstract class Mob extends Entity {
         return totalHealingRemaining;
     }
 
-    public void eatFood(ItemFood food) {
-        if (food == null) return;
-        if (food.getTicksPerHeal() == 0 || this.world.getGameRuleValue(GameRules.INSTANT_HEALING)) {
-            heal(food.getHealAmount());
+    public void eatFood(ItemStack stack) {
+        if (stack == null || !(stack.getItem() instanceof ItemFood)) return;
+
+        ItemFood foodItem = (ItemFood) stack.getItem();
+
+        if (foodItem.getTicksPerHeal(stack) == 0 || this.world.getGameRuleValue(GameRules.INSTANT_HEALING)) {
+            heal(foodItem.getHealAmount(stack));
         } else {
-            if (this.consumedFood.containsKey(food)) {
-                this.consumedFood.get(food).addFood();
+            String key = stack.getItemKey();
+
+            if (this.consumedFood.containsKey(key)) {
+                this.consumedFood.get(key).addFood(stack);
             } else {
-                this.consumedFood.put(food, new ConsumedFood(this, food));
+                this.consumedFood.put(key, new ConsumedFood(this, stack));
             }
         }
     }
diff --git a/game/core/src/main/java/net/minecraft/core/entity/animal/MobCow.java b/game/core/src/main/java/net/minecraft/core/entity/animal/MobCow.java
index 7ea0ed3d0..12e6eb0e4 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/animal/MobCow.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/animal/MobCow.java
@@ -1,11 +1,10 @@
 package net.minecraft.core.entity.animal;
 
 import net.minecraft.core.WeightedRandomLootObject;
-import net.minecraft.core.entity.AgedMob;
-import net.minecraft.core.entity.MobAge;
 import net.minecraft.core.entity.player.Player;
+import net.minecraft.core.item.ItemBucket;
+import net.minecraft.core.item.ItemBucketSteel;
 import net.minecraft.core.item.Items;
-import net.minecraft.core.item.ItemBucketEmpty;
 import net.minecraft.core.item.ItemStack;
 import com.mojang.nbt.tags.CompoundTag;
 import net.minecraft.core.item.tag.ItemTags;
@@ -80,14 +79,14 @@ public class MobCow
     }
 
     @Override
-	public boolean interact(@NotNull Player player)
-    {
+    public boolean interact(@NotNull Player player) {
         ItemStack itemstack = player.inventory.getCurrentItem();
-        if(itemstack != null && itemstack.itemID == Items.BUCKET.id)
-        {
-            ItemBucketEmpty.useBucket(player, new ItemStack(Items.BUCKET_MILK));
+
+        if (itemstack != null && itemstack.getItem() instanceof ItemBucket) {
+            ItemBucket.useBucket(itemstack, player, this.world, ItemBucket.STATE_MILK);
             return true;
         }
+
         return super.interact(player);
     }
 
diff --git a/game/core/src/main/java/net/minecraft/core/entity/animal/MobWolf.java b/game/core/src/main/java/net/minecraft/core/entity/animal/MobWolf.java
index c7c69cf40..a4665d0a3 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/animal/MobWolf.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/animal/MobWolf.java
@@ -656,7 +656,7 @@ public class MobWolf
                             player.inventory.setItem(player.inventory.getCurrentSlot(), null);
                         }
                     }
-                    heal(itemfood.getHealAmount());
+                    heal(itemfood.getHealAmount(itemstack));
                     return true;
                 }
             }
diff --git a/game/core/src/main/java/net/minecraft/core/entity/monster/MobSnowman.java b/game/core/src/main/java/net/minecraft/core/entity/monster/MobSnowman.java
index 0dec555e9..b9d2ce69d 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/monster/MobSnowman.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/monster/MobSnowman.java
@@ -4,6 +4,9 @@ import net.minecraft.core.WeightedRandomLootObject;
 import net.minecraft.core.block.Blocks;
 import net.minecraft.core.entity.Entity;
 import net.minecraft.core.entity.projectile.ProjectileSnowball;
+import net.minecraft.core.item.ItemBucket;
+import net.minecraft.core.item.ItemBucketSteel;
+import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.util.helper.MathHelper;
 import net.minecraft.core.item.Items;
 import net.minecraft.core.world.World;
@@ -103,11 +106,13 @@ public class MobSnowman extends MobMonster
     }
 
     @Override
-    protected void dropDeathItems()
-    {
-        if (random.nextInt(1000) == 0){
-            dropItem(Items.BUCKET_ICECREAM.id, 1);
+    protected void dropDeathItems() {
+        if (random.nextInt(1000) == 0) {
+            dropItem(ItemBucket.createItemStack(Items.BUCKET_IRON, ItemBucket.STATE_ICECREAM, 1), 0.0F);
+        } else if (random.nextInt(8192) == 0) {
+            dropItem(ItemBucket.createItemStack(Items.BUCKET_STEEL, ItemBucket.STATE_ICECREAM, 3), 0.0F);
         }
+
         super.dropDeathItems();
     }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/entity/player/Player.java b/game/core/src/main/java/net/minecraft/core/entity/player/Player.java
index ad5c30688..6a57f5029 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/player/Player.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/player/Player.java
@@ -35,6 +35,7 @@ import net.minecraft.core.enums.ArtType;
 import net.minecraft.core.enums.EnumDropCause;
 import net.minecraft.core.enums.EnumSleepStatus;
 import net.minecraft.core.item.Item;
+import net.minecraft.core.item.ItemBucket;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.item.Items;
 import net.minecraft.core.item.tool.ItemToolSword;
diff --git a/game/core/src/main/java/net/minecraft/core/entity/vehicle/EntityMinecart.java b/game/core/src/main/java/net/minecraft/core/entity/vehicle/EntityMinecart.java
index 09b9cfc3e..fabb99dc7 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/vehicle/EntityMinecart.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/vehicle/EntityMinecart.java
@@ -802,11 +802,11 @@ public class EntityMinecart extends Entity implements Container {
                 break;
             case FURNACE_CART:
                 ItemStack itemstack = player.inventory.getCurrentItem();
-                if (itemstack != null && LookupFuelFurnace.instance.getFuelYield(itemstack.itemID) > 0 && itemstack.consumeItem(player)) {
+                if (itemstack != null && LookupFuelFurnace.instance.getFuelYield(itemstack) > 0 && itemstack.consumeItem(player)) {
                     if (itemstack.stackSize <= 0) {
                         player.inventory.setItem(player.inventory.getCurrentSlot(), null);
                     }
-                    setFuel(getFuel() + MathHelper.round(((LookupFuelFurnace.instance.getFuelYield(itemstack.itemID) / 1600F) * 1200)));
+                    setFuel(getFuel() + MathHelper.round(((LookupFuelFurnace.instance.getFuelYield(itemstack) / 1600F) * 1200)));
                 }
                 this.xPush = this.x - player.x;
                 this.zPush = this.z - player.z;
diff --git a/game/core/src/main/java/net/minecraft/core/item/IFood.java b/game/core/src/main/java/net/minecraft/core/item/IFood.java
new file mode 100644
index 000000000..14cce954e
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/item/IFood.java
@@ -0,0 +1,10 @@
+package net.minecraft.core.item;
+
+public interface IFood {
+
+    int getHealAmount();
+
+    int getTicksPerHeal();
+
+    boolean getIsWolfsFavoriteMeat();
+}
diff --git a/game/core/src/main/java/net/minecraft/core/item/IItemContainer.java b/game/core/src/main/java/net/minecraft/core/item/IItemContainer.java
new file mode 100644
index 000000000..377480961
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/item/IItemContainer.java
@@ -0,0 +1,9 @@
+package net.minecraft.core.item;
+
+public interface IItemContainer {
+
+    boolean hasContainerItem(ItemStack stack);
+
+    ItemStack getContainerItemStack(ItemStack stack);
+
+}
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/item/ItemBucket.java b/game/core/src/main/java/net/minecraft/core/item/ItemBucket.java
index 9418e8ead..cd66d85aa 100644
--- a/game/core/src/main/java/net/minecraft/core/item/ItemBucket.java
+++ b/game/core/src/main/java/net/minecraft/core/item/ItemBucket.java
@@ -1,98 +1,493 @@
 package net.minecraft.core.item;
 
-import net.minecraft.core.block.entity.TileEntityActivator;
-import net.minecraft.core.util.helper.Direction;
-import net.minecraft.core.util.phys.HitResult;
+import com.mojang.nbt.tags.CompoundTag;
 import net.minecraft.core.block.Block;
+import net.minecraft.core.block.BlockLogicFluid;
 import net.minecraft.core.block.Blocks;
+import net.minecraft.core.block.entity.TileEntityActivator;
+import net.minecraft.core.block.material.Materials;
 import net.minecraft.core.block.tag.BlockTags;
+import net.minecraft.core.entity.animal.MobCow;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.enums.EnumDropCause;
 import net.minecraft.core.sound.SoundCategory;
+import net.minecraft.core.util.HardIllegalArgumentException;
+import net.minecraft.core.util.collection.NamespaceID;
+import net.minecraft.core.util.helper.Direction;
 import net.minecraft.core.util.helper.Side;
-import net.minecraft.core.world.Dimension;
+import net.minecraft.core.util.phys.AABB;
+import net.minecraft.core.util.phys.HitResult;
 import net.minecraft.core.world.World;
-import net.minecraft.core.world.biome.Biome;
-import net.minecraft.core.world.biome.BiomeTags;
 import net.minecraft.core.world.pos.TilePos;
 import net.minecraft.core.world.pos.TilePosc;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
+import org.joml.primitives.AABBd;
+import org.joml.primitives.AABBdc;
+
+import java.util.*;
+
+public abstract class ItemBucket extends ItemFood implements IItemContainer {
+
+    public record BucketState(
+            String name,
+            boolean isEdible,
+            int healAmount,
+            int ticksPerHeal,
+            @Nullable Block<?> fluidBlock,
+            boolean canPlace
+    ) {
+
+
+        /**
+         * Constructor for Food/Edible Buckets
+         */
+        public BucketState(String name, int healAmount, int ticksPerHeal) {
+            this(name, true, healAmount, ticksPerHeal, null, false);
+        }
+        /**
+         * Constructor for Fluid Buckets
+         */
+        public BucketState(String name, @Nullable Block<?> fluidBlock, boolean canPlace) {
+            this(name, false, 0, 0, fluidBlock, canPlace);
+        }
+    }
+
+    private static final Map<NamespaceID, BucketState> STATE_REGISTRY = new LinkedHashMap<>();
+    public static final NamespaceID STATE_EMPTY = new NamespaceID("minecraft", "empty");
+    public static final NamespaceID STATE_WATER = new NamespaceID("minecraft", "water");
+    public static final NamespaceID STATE_LAVA  = new NamespaceID("minecraft", "lava");
+    public static final NamespaceID STATE_MILK  = new NamespaceID("minecraft", "milk");
+    public static final NamespaceID STATE_ICECREAM = new NamespaceID("minecraft", "icecream");
+
+    static {
+        registerState(STATE_EMPTY, new BucketState("empty", null, false));
+        registerState(STATE_WATER, new BucketState("water", Blocks.FLUID_WATER_FLOWING, true));
+        registerState(STATE_LAVA, new BucketState("lava", Blocks.FLUID_LAVA_FLOWING, true));
+        registerState(STATE_MILK, new BucketState("milk", 0, 0));
+        registerState(STATE_ICECREAM, new BucketState("icecream", 15, 4));
+    }
+
+    public static NamespaceID registerState(NamespaceID id, BucketState state) {
+        if (STATE_REGISTRY.containsKey(id)) {
+            throw new IllegalArgumentException("Bucket state '" + id + "' is already registered!");
+        }
+        STATE_REGISTRY.put(id, state);
+        return id;
+    }
+
+    public static BucketState getBucketState(NamespaceID id) {
+        if (id == null || !STATE_REGISTRY.containsKey(id)) {
+            return STATE_REGISTRY.get(STATE_EMPTY);
+        }
+        return STATE_REGISTRY.get(id);
+    }
+
+    public static String getStateName(NamespaceID id) {
+        return id.value();
+    }
+
+    public static Set<NamespaceID> getRegisteredStateIds() {
+        return STATE_REGISTRY.keySet();
+    }
 
-import java.util.Random;
+    public static boolean isFluid(NamespaceID id) { return getBucketState(id).fluidBlock() != null; }
+    public static boolean isEdible(NamespaceID id) { return getBucketState(id).isEdible(); }
+    public static int getMaxStates() { return STATE_REGISTRY.size(); }
 
-public class ItemBucket extends Item {
-    private final @Nullable Block<?> blockToPlace;
+    @Override
+    public int getHealAmount(ItemStack stack) {return getBucketState(getState(stack)).healAmount();}
+    @Override
+    public int getTicksPerHeal(ItemStack stack) {return getBucketState(getState(stack)).ticksPerHeal();}
+
+    @Nullable
+    private NamespaceID getFluidStateAt(World world, TilePosc tilePos) {
+        if (world.getBlockMaterial(tilePos) == Materials.WATER) return STATE_WATER;
+        if (world.getBlockMaterial(tilePos) == Materials.LAVA) return STATE_LAVA;
+        return null;
+    }
+
+    public final int maxCharges;
 
-    public ItemBucket(@NotNull String name, @NotNull String namespaceId, int id, @Nullable Block<?> blockToPlace) {
-        super(name, namespaceId, id);
-        this.maxStackSize = 1;
-        this.blockToPlace = blockToPlace;
+    public ItemBucket(@NotNull String name, @NotNull String namespaceId, int id, int maxCharges) {
+        super(name, namespaceId, id, 0, 0, false, 64);
+        this.maxCharges = maxCharges;
+        this.maxStackSize = 64;
     }
 
     @Override
-    public @Nullable ItemStack onUse(@NotNull ItemStack selfStack, @NotNull World world, @NotNull Player player) {
-        if (this.blockToPlace == null) {
-            // Milk
-            return new ItemStack(Items.BUCKET);
+    public @NotNull String getLanguageKey(@NotNull ItemStack itemStack){
+        NamespaceID state = getState(itemStack);
+        if (STATE_EMPTY.equals(state)) return super.getKey();
+        return super.getKey() + "." + getStateName(state).replace("_", "");
+    }
+
+    @Override
+    public ItemStack onUse(@NotNull ItemStack itemStack, @NotNull World world, @NotNull Player player) {
+        NamespaceID currentStateId = getState(itemStack);
+        BucketState currentState = getBucketState(currentStateId);
+
+        if (currentState.isEdible() && !currentState.canPlace()) {
+            return handleAirClick(itemStack, world, player);
         }
 
         double reachDistance = player.getGamemode().getBlockReachDistance();
-        HitResult rayCast = player.rayCast(reachDistance, 1f, false, false, false);
+        HitResult hitResultFluid = player.rayCast(reachDistance, 1f, true, false, false);
+        HitResult hitResultNoFluid = player.rayCast(reachDistance, 1f, false, false, false);
 
-        if (!(rayCast instanceof HitResult.Tile hitTile)) {
-            return selfStack;
+        if (hitResultFluid == null) {
+            return handleAirClick(itemStack, world, player);
+        } else if (hitResultFluid instanceof HitResult.Entity) {
+            return handleEntityClick(itemStack, world, player);
+        } else if (hitResultFluid instanceof HitResult.Tile hitTileFluid) {
+            TilePosc tilePos = hitTileFluid.tilePos;
+            boolean isSourceBlock = world.getBlockData(tilePos) == 0;
+
+            if (isSourceBlock) {
+                NamespaceID targetStateId = getFluidStateAt(world, tilePos);
+
+                boolean isEmptyScoop = STATE_EMPTY.equals(currentStateId);
+                boolean isRefillScoop = currentStateId.equals(targetStateId) && getCharges(itemStack) < this.maxCharges;
+
+                if (targetStateId != null && (isEmptyScoop || isRefillScoop)) {
+                    return tryPickupFluid(itemStack, world, player, tilePos, targetStateId);
+                }
+            }
+
+            if (currentState.canPlace() && hitResultNoFluid instanceof HitResult.Tile hitTileNoFluid) {
+                return tryPlaceFluid(itemStack, world, player, hitTileNoFluid);
+            }
         }
 
-        TilePos rayCastPos = new TilePos(hitTile.tilePos);
-        if (!world.canMineBlock(player, rayCastPos)) {
-            return selfStack;
+        return handleAirClick(itemStack, world, player);
+    }
+
+    protected ItemStack tryPickupFluid(ItemStack itemStack, World world, Player player, TilePosc tilePos, NamespaceID targetState) {
+        if (!world.canMineBlock(player, tilePos)) return itemStack;
+
+        world.setBlockTypeNotify(tilePos, Blocks.AIR);
+        player.swingItem();
+        world.playSoundEffect(player, SoundCategory.WORLD_SOUNDS, tilePos.x() + 0.5F, tilePos.y() + 0.5F, tilePos.z() + 0.5F, "item.pickup", 2.2f, 0.5f);
+
+        if (itemStack.stackSize > 1) {
+            return splitBucket(itemStack, player, targetState, world);
+        } else {
+            NamespaceID currentState = getState(itemStack);
+            if (STATE_EMPTY.equals(currentState)) {
+                setState(itemStack, targetState);
+                setCharges(itemStack, 1);
+            } else {
+                setCharges(itemStack, getCharges(itemStack) + 1);
+            }
+            return itemStack;
         }
+    }
 
-        Block<?> block = world.getBlockType(rayCastPos);
-        if (!block.hasTag(BlockTags.PLACE_OVERWRITES) && !block.hasTag(BlockTags.BROKEN_BY_FLUIDS)) {
+    protected ItemStack tryPlaceFluid(ItemStack itemStack, World world, Player player, HitResult.Tile hitTile) {
+        TilePos rayCastPos = new TilePos(hitTile.tilePos);
+        if (!world.canMineBlock(player, rayCastPos)) return itemStack;
+
+        Block<?> blockAtPos = world.getBlockType(rayCastPos);
+        if (!blockAtPos.hasTag(BlockTags.PLACE_OVERWRITES) && !blockAtPos.hasTag(BlockTags.BROKEN_BY_FLUIDS)) {
             Side side = hitTile.side;
             rayCastPos.x += side.getOffsetX();
             rayCastPos.y += side.getOffsetY();
             rayCastPos.z += side.getOffsetZ();
         }
 
-        if (rayCastPos.y < 0 || rayCastPos.y >= world.getHeightBlocks()) {
-            return selfStack;
-        }
+        if (rayCastPos.y < 0 || rayCastPos.y >= world.getHeightBlocks()) return itemStack;
 
         if (world.isAirBlock(rayCastPos) || !world.getBlockMaterial(rayCastPos).isSolid()) {
-            if (this.blockToPlace == Blocks.FLUID_WATER_FLOWING) {
-                world.playSoundEffect(player, SoundCategory.WORLD_SOUNDS, rayCastPos.x + 0.5F, rayCastPos.y + 0.5F, rayCastPos.z + 0.5F, "liquid.splash", 0.5f, 1.0f);
+            NamespaceID stateId = getState(itemStack);
+            BucketState bucketState = getBucketState(stateId);
+
+            if (bucketState.fluidBlock() != null) {
+
+                if(STATE_LAVA.equals(stateId)){
+                    BlockLogicFluid.fizz(world, rayCastPos);
+                    world.playSoundEffect(player, SoundCategory.WORLD_SOUNDS, rayCastPos.x + 0.5F, rayCastPos.y + 0.5F, rayCastPos.z + 0.5F, "liquid.lavapop", 1f, 1f);
+                }
+                if(STATE_WATER.equals(stateId)){
+                    world.playSoundEffect(player, SoundCategory.WORLD_SOUNDS, rayCastPos.x + 0.5F, rayCastPos.y + 0.5F, rayCastPos.z + 0.5F, "liquid.splash", 0.65f, 1f);
+                }
+
+                final Block<?> replacedBlock = world.getBlockType(rayCastPos);
+                final int replacedData = world.getBlockData(rayCastPos);
+
+                world.setBlockTypeNotify(rayCastPos, bucketState.fluidBlock());
+                replacedBlock.dropWithCause(world, EnumDropCause.WORLD, rayCastPos, replacedData, null, null);
+
+                player.swingItem();
+
+                if (player.getGamemode().hasBlockConsumption()) {
+                    setCharges(itemStack, getCharges(itemStack) - 1);
+                    if (getCharges(itemStack) == 0) setState(itemStack, STATE_EMPTY);
+                }
             }
-            final var replacedBlock = world.getBlockType(rayCastPos);
-            final var replacedMeta = world.getBlockData(rayCastPos);
-            world.setBlockTypeNotify(rayCastPos, this.blockToPlace);
-            replacedBlock.dropWithCause(world, EnumDropCause.WORLD, rayCastPos, replacedMeta, null, null);
-                // is this being called afterwards intentional? 
-
-            player.swingItem();
-            if (player.getGamemode().hasBlockConsumption()) {
-                return new ItemStack(Items.BUCKET);
+        }
+        return itemStack;
+    }
+
+    protected ItemStack handleAirClick(ItemStack itemStack, World world, Player player) {
+        NamespaceID state = getState(itemStack);
+        if (isEdible(state)) {
+            return handleEdibleConsumption(itemStack, world, player);
+        }
+        return itemStack;
+    }
+
+    protected ItemStack handleEntityClick(ItemStack itemStack, World world, Player player) {
+        return handleAirClick(itemStack, world, player);
+    }
+
+    protected ItemStack handleEdibleConsumption(@NotNull ItemStack itemStack, @NotNull World world, @NotNull Player player) {
+        NamespaceID state = getState(itemStack);
+        BucketState bucketState = getBucketState(state);
+
+        if (player.getHealth() < player.getMaxHealth() || STATE_MILK.equals(state)) {
+            player.eatFood(itemStack);
+            String sound = bucketState.ticksPerHeal() >= 10 ? "random.bite_extended" : "random.bite";
+            world.playSoundAtEntity(player, player, sound, 0.5f + (itemRand.nextFloat() - itemRand.nextFloat()) * 0.1f, 1.1f + (itemRand.nextFloat() - itemRand.nextFloat()) * 0.1f);
+
+            if (itemStack.stackSize == 1) {
+                return this.getContainerItemStack(itemStack);
+            } else {
+                itemStack.stackSize--;
+                ItemStack container = this.getContainerItemStack(itemStack);
+                player.inventory.insertItem(container, true);
             }
         }
+        return itemStack;
+    }
+
+    public static void useBucket(ItemStack itemStack, Player player, World world, NamespaceID targetState) {
+        boolean isStacked = itemStack.stackSize > 1;
+        NamespaceID currentState = getState(itemStack);
+
+        int limit = (itemStack.getItem() instanceof ItemBucket bucket) ? bucket.maxCharges : 1;
 
-        return selfStack;
+        if (STATE_EMPTY.equals(currentState) || (currentState.equals(targetState) && getCharges(itemStack) < limit)) {
+            if (!isStacked) {
+                if (STATE_EMPTY.equals(currentState)) {
+                    setState(itemStack, targetState);
+                }
+                setCharges(itemStack, getCharges(itemStack) + 1);
+            } else {
+                splitBucket(itemStack, player, targetState, world);
+            }
+        }
+    }
+
+    protected static ItemStack splitBucket(@NotNull ItemStack itemStack, @NotNull Player player, NamespaceID state, @NotNull World world) {
+        itemStack.stackSize--;
+        ItemStack splitItemStack = new ItemStack(itemStack.getItem(), 1);
+        setState(splitItemStack, state);
+        setCharges(splitItemStack, 1);
+
+        player.inventory.insertItem(splitItemStack, true);
+        if (splitItemStack.stackSize > 0) {
+            world.dropItem((int) player.x, (int) player.y, (int) player.z, splitItemStack);
+        }
+        return itemStack;
+    }
+
+    public static int getCharges(@NotNull ItemStack stack) {
+        return stack.getData().getIntegerOrDefault("Charges", 0);
+    }
+
+    public static void setCharges(@NotNull ItemStack stack, int charges) {
+        CompoundTag tag = stack.getData();
+        if (charges <= 0) {
+            tag.getValue().remove("State");
+            tag.getValue().remove("Charges");
+        } else {
+            tag.putInt("Charges", charges);
+        }
+    }
+
+    public static NamespaceID getState(@NotNull ItemStack stack) {
+        CompoundTag tag = stack.getData();
+        if (tag.containsKey("State")) {
+            String stateStr = tag.getString("State");
+            if (stateStr.contains(":")) {
+                try {
+                    return new NamespaceID(stateStr);
+                } catch (HardIllegalArgumentException ignored) {
+                }
+            }
+        }
+        return STATE_EMPTY;
+    }
+
+    public static void setState(@NotNull ItemStack stack, NamespaceID state) {
+        CompoundTag tag = stack.getData();
+        if (state == null || STATE_EMPTY.equals(state)) {
+            tag.getValue().remove("State");
+            tag.getValue().remove("Charges");
+        } else {
+            tag.putString("State", state.toString());
+        }
     }
 
     @Override
-    public void onUseByActivator(@NotNull ItemStack selfStack, @NotNull World world, @NotNull TileEntityActivator activator, @NotNull Random random, @NotNull TilePosc blockPos, @NotNull Direction direction, double offX, double offY, double offZ) {
-        if (this.blockToPlace == null) {
-            selfStack.itemID = Items.BUCKET.id;
-            return;
+    public int getItemStackLimit(@Nullable ItemStack itemStack) {
+        return (itemStack == null || STATE_EMPTY.equals(getState(itemStack))) ? 64 : 1;
+    }
+
+    @Override
+    public boolean hasContainerItem(ItemStack stack) {
+        return true;
+    }
+
+    @Override
+    public ItemStack getContainerItemStack(ItemStack stack) {
+        ItemStack leftover = stack.copy();
+        leftover.stackSize = 1;
+
+        int currentCharges = getCharges(leftover);
+        if (currentCharges > 1) {
+            setCharges(leftover, currentCharges - 1);
+        } else {
+            setState(leftover, STATE_EMPTY);
         }
+
+        return leftover;
+    }
+
+    public static ItemStack createItemStack(Item type, NamespaceID state, int charges) {
+        ItemStack stack = new ItemStack(type);
+        setState(stack, state);
+        setCharges(stack, charges);
+        return stack;
+    }
+
+    public static ItemStack createRecipeInput(Item type, NamespaceID state) {
+        ItemStack stack = new ItemStack(type);
+        setState(stack, state);
+        return stack;
+    }
+
+    @Override
+    public void onUseByActivator(@NotNull ItemStack selfStack, @NotNull World world, @NotNull TileEntityActivator activator, @NotNull Random random, @NotNull TilePosc blockPos, @NotNull Direction direction, double offX, double offY, double offZ) {
+        NamespaceID currentStateId = getState(selfStack);
+        BucketState currentState = getBucketState(currentStateId);
+
         TilePos posAhead = blockPos.add(direction, new TilePos());
-        Block<?> b = world.getBlockType(posAhead);
-        if (b.hasTag(BlockTags.PLACE_OVERWRITES) || b.hasTag(BlockTags.BROKEN_BY_FLUIDS)) {
-            world.setBlockTypeNotify(posAhead, this.blockToPlace);
-            world.getBlockType(posAhead).dropWithCause(world, EnumDropCause.WORLD, posAhead, world.getBlockData(posAhead), null, null);
+        Block<?> blockAhead = world.getBlockType(posAhead);
+
+        boolean isEmptyScoop = STATE_EMPTY.equals(currentStateId);
+        boolean isMilkScoop = STATE_MILK.equals(currentStateId) && getCharges(selfStack) < this.maxCharges;
+
+        if (isEmptyScoop || isMilkScoop) {
+            AABBd cowArea = new AABBd(posAhead.x, posAhead.y, posAhead.z, posAhead.x + 1, posAhead.y + 1, posAhead.z + 1);
+            List<MobCow> cows = world.getEntitiesWithinAABB(MobCow.class, cowArea);
+
+            if (!cows.isEmpty()) {
+                if (selfStack.stackSize > 1 && !canActivatorAccept(activator, selfStack.getItem(), STATE_MILK)) {
+                    return;
+                }
+
+                world.playSoundEffect(null, SoundCategory.WORLD_SOUNDS, posAhead.x + 0.5F, posAhead.y + 0.5F, posAhead.z + 0.5F, "item.pickup", 2.2f, 0.5f);
+                handleActivatorFill(selfStack, STATE_MILK, activator);
+                return;
+            }
+        }
+
+        boolean isSourceBlock = world.getBlockData(posAhead) == 0;
+        if (isSourceBlock) {
+            NamespaceID targetStateId = getFluidStateAt(world, posAhead);
+            boolean isRefillScoop = currentStateId.equals(targetStateId) && getCharges(selfStack) < this.maxCharges;
+
+            if (targetStateId != null && (isEmptyScoop || isRefillScoop)) {
+                if (selfStack.stackSize > 1 && !canActivatorAccept(activator, selfStack.getItem(), targetStateId)) {
+                    return;
+                }
 
-            selfStack.itemID = Items.BUCKET.id;
+                world.setBlockTypeNotify(posAhead, Blocks.AIR);
+                world.playSoundEffect(null, SoundCategory.WORLD_SOUNDS, posAhead.x + 0.5F, posAhead.y + 0.5F, posAhead.z + 0.5F, "item.pickup", 2.2f, 0.5f);
+
+                handleActivatorFill(selfStack, targetStateId, activator);
+                return;
+            }
+        }
+
+        if (currentState.canPlace() && currentState.fluidBlock() != null) {
+            if (blockAhead.hasTag(BlockTags.PLACE_OVERWRITES) || blockAhead.hasTag(BlockTags.BROKEN_BY_FLUIDS)) {
+
+                if (STATE_LAVA.equals(currentStateId)) {
+                    BlockLogicFluid.fizz(world, posAhead);
+                    world.playSoundEffect(null, SoundCategory.WORLD_SOUNDS, posAhead.x + 0.5F, posAhead.y + 0.5F, posAhead.z + 0.5F, "liquid.lavapop", 1f, 1f);
+                } else if (STATE_WATER.equals(currentStateId)) {
+                    world.playSoundEffect(null, SoundCategory.WORLD_SOUNDS, posAhead.x + 0.5F, posAhead.y + 0.5F, posAhead.z + 0.5F, "liquid.splash", 0.65f, 1f);
+                }
+
+                int replacedData = world.getBlockData(posAhead);
+                world.setBlockTypeNotify(posAhead, currentState.fluidBlock());
+                blockAhead.dropWithCause(world, EnumDropCause.WORLD, posAhead, replacedData, null, null);
+
+                setCharges(selfStack, getCharges(selfStack) - 1);
+                if (getCharges(selfStack) == 0) {
+                    setState(selfStack, STATE_EMPTY);
+                }
+            }
+        }
+    }
+
+    /**
+     * Handles stack splitting and filling for the activator.
+     * Assumes inventory space has already been verified via canActivatorAccept.
+     */
+    private void handleActivatorFill(ItemStack selfStack, NamespaceID targetStateId, TileEntityActivator activator) {
+        NamespaceID currentStateId = getState(selfStack);
+
+        if (selfStack.stackSize > 1) {
+            selfStack.stackSize--;
+
+            ItemStack filledBucket = new ItemStack(selfStack.getItem(), 1);
+            setState(filledBucket, targetStateId);
+            setCharges(filledBucket, 1);
+
+            for (int i = 0; i < activator.getContainerSize(); i++) {
+                if (activator.locked(i)) continue;
+
+                ItemStack slotStack = activator.getItem(i);
+
+                if (slotStack == null) {
+                    activator.setItem(i, filledBucket);
+                    break;
+                }
+                else if (slotStack.itemID == filledBucket.itemID && getState(slotStack).equals(getState(filledBucket)) && slotStack.stackSize < slotStack.getMaxStackSize()) {
+                    slotStack.stackSize++;
+                    break;
+                }
+            }
+        } else {
+            if (STATE_EMPTY.equals(currentStateId)) {
+                setState(selfStack, targetStateId);
+                setCharges(selfStack, 1);
+            } else {
+                setCharges(selfStack, getCharges(selfStack) + 1);
+            }
+        }
+    }
+
+    /**
+     * Checks if the activator has a valid, unlocked slot to accept the specified filled bucket.
+     */
+    private boolean canActivatorAccept(TileEntityActivator activator, Item item, NamespaceID targetState) {
+        for (int i = 0; i < activator.getContainerSize(); i++) {
+            if (activator.locked(i)) continue;
+
+            ItemStack slotStack = activator.getItem(i);
+
+            if (slotStack == null) {
+                return true;
+            }
+
+            if (slotStack.itemID == item.id && getState(slotStack).equals(targetState) && slotStack.stackSize < slotStack.getMaxStackSize()) {
+                return true;
+            }
         }
+        return false;
     }
-}
+}
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/item/ItemBucketEmpty.java b/game/core/src/main/java/net/minecraft/core/item/ItemBucketEmpty.java
deleted file mode 100644
index a13f2fa63..000000000
--- a/game/core/src/main/java/net/minecraft/core/item/ItemBucketEmpty.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package net.minecraft.core.item;
-
-import net.minecraft.core.block.Blocks;
-import net.minecraft.core.block.entity.TileEntityActivator;
-import net.minecraft.core.block.material.Materials;
-import net.minecraft.core.entity.animal.MobCow;
-import net.minecraft.core.entity.player.Player;
-import net.minecraft.core.util.helper.Direction;
-import net.minecraft.core.util.phys.HitResult;
-import net.minecraft.core.world.World;
-import net.minecraft.core.world.pos.TilePos;
-import net.minecraft.core.world.pos.TilePosc;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.joml.primitives.AABBd;
-import org.joml.primitives.AABBdc;
-
-import java.util.List;
-import java.util.Random;
-
-public class ItemBucketEmpty extends Item {
-
-    public ItemBucketEmpty(@NotNull String name, @NotNull String namespaceId, int id) {
-        super(name, namespaceId, id);
-        this.maxStackSize = 64;
-    }
-
-    @Override
-    public @Nullable ItemStack onUse(@NotNull ItemStack selfStack, @NotNull World world, @NotNull Player player) {
-        double reachDistance = player.getGamemode().getBlockReachDistance();
-        HitResult hitResult = player.rayCast(reachDistance, 1f, true, false, false);
-
-        if (hitResult == null) {
-            return selfStack;
-        }
-        if (hitResult instanceof HitResult.Tile hitTile) {
-            if (!world.canMineBlock(player, hitTile.tilePos)) {
-                return selfStack;
-            } else if (world.getBlockMaterial(hitTile.tilePos) == Materials.WATER && world.getBlockData(hitTile.tilePos) == 0) {
-                if (useBucket(player, new ItemStack(Items.BUCKET_WATER))) {
-                    world.setBlockTypeNotify(hitTile.tilePos, Blocks.AIR);
-                    player.swingItem();
-                }
-            } else if (world.getBlockMaterial(hitTile.tilePos) == Materials.LAVA && world.getBlockData(hitTile.tilePos) == 0) {
-                if (useBucket(player, new ItemStack(Items.BUCKET_LAVA))) {
-                    world.setBlockTypeNotify(hitTile.tilePos, Blocks.AIR);
-                    player.swingItem();
-                }
-            }
-        }
-        return selfStack;
-    }
-
-    @Override
-    public void onUseByActivator(@NotNull ItemStack selfStack, @NotNull World world, @NotNull TileEntityActivator activator, @NotNull Random random, @NotNull TilePosc blockPos, @NotNull Direction direction, double offX, double offY, double offZ) {
-        if (selfStack.stackSize > 1) { // TODO figure out wtf to do for stacked buckets for this
-            return;
-        }
-
-        TilePos shiftedPos = blockPos.add(direction, new TilePos());
-        if (world.getBlockMaterial(shiftedPos) == Materials.WATER && world.getBlockData(shiftedPos) == 0) {
-            world.setBlockTypeNotify(shiftedPos, Blocks.AIR);
-            selfStack.itemID = Items.BUCKET_WATER.id;
-            return;
-        } else if (world.getBlockMaterial(shiftedPos) == Materials.LAVA && world.getBlockData(shiftedPos) == 0) {
-            world.setBlockTypeNotify(shiftedPos, Blocks.AIR);
-            selfStack.itemID = Items.BUCKET_LAVA.id;
-            return;
-        }
-
-        AABBdc box = new AABBd(shiftedPos.x, shiftedPos.y, shiftedPos.z, shiftedPos.x + 1, shiftedPos.y + 1, shiftedPos.z + 1);
-        List<MobCow> entities = world.getEntitiesWithinAABB(MobCow.class, box);
-        if (!entities.isEmpty()) {
-            selfStack.itemID = Items.BUCKET_MILK.id;
-        }
-    }
-
-    public static boolean useBucket(@NotNull Player player, @NotNull ItemStack itemToGive) {
-        ItemStack currentItem = player.inventory.getCurrentItem();
-        if (currentItem == null || currentItem.stackSize <= 1) {
-            player.inventory.setItem(player.inventory.getCurrentSlot(), itemToGive);
-            return true;
-        } else {
-            player.inventory.insertItem(itemToGive, true);
-            if (itemToGive.stackSize < 1) {
-                player.inventory.getCurrentItem().consumeItem(player);
-                return true;
-            }
-        }
-        return false;
-    }
-
-}
diff --git a/game/core/src/main/java/net/minecraft/core/item/ItemBucketIceCream.java b/game/core/src/main/java/net/minecraft/core/item/ItemBucketIceCream.java
deleted file mode 100644
index acd284640..000000000
--- a/game/core/src/main/java/net/minecraft/core/item/ItemBucketIceCream.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package net.minecraft.core.item;
-
-import net.minecraft.core.entity.player.Player;
-import net.minecraft.core.world.World;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-public class ItemBucketIceCream extends ItemFood {
-
-    public ItemBucketIceCream(@NotNull String name, @NotNull String namespaceId, int id, int healAmount, int ticksPerHeal) {
-        super(name, namespaceId, id, healAmount, ticksPerHeal, false, 1);
-        this.maxStackSize = 1;
-    }
-    @Override
-    public @Nullable ItemStack onUse(@NotNull ItemStack selfStack, @NotNull World world, @NotNull Player player) {
-        if (player.getHealth() < player.getMaxHealth()){
-            super.onUse(selfStack, world, player);
-            return new ItemStack(Items.BUCKET);
-        }
-        return selfStack;
-    }
-}
diff --git a/game/core/src/main/java/net/minecraft/core/item/ItemBucketIron.java b/game/core/src/main/java/net/minecraft/core/item/ItemBucketIron.java
new file mode 100644
index 000000000..95905d071
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/item/ItemBucketIron.java
@@ -0,0 +1,12 @@
+package net.minecraft.core.item;
+
+import org.jetbrains.annotations.NotNull;
+
+public class ItemBucketIron extends ItemBucket {
+
+    public static final int MAX_CHARGES = 1;
+
+    public ItemBucketIron(@NotNull String name, @NotNull String namespaceId, int id) {
+        super(name, namespaceId, id, MAX_CHARGES);
+    }
+}
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/item/ItemBucketSteel.java b/game/core/src/main/java/net/minecraft/core/item/ItemBucketSteel.java
new file mode 100644
index 000000000..11b5e8032
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/item/ItemBucketSteel.java
@@ -0,0 +1,12 @@
+package net.minecraft.core.item;
+
+import org.jetbrains.annotations.NotNull;
+
+public class ItemBucketSteel extends ItemBucket {
+
+    public static final int MAX_CHARGES = 3;
+
+    public ItemBucketSteel(@NotNull String name, @NotNull String namespaceId, int id) {
+        super(name, namespaceId, id, MAX_CHARGES);
+    }
+}
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/item/ItemFood.java b/game/core/src/main/java/net/minecraft/core/item/ItemFood.java
index 505f80cab..93a510a53 100644
--- a/game/core/src/main/java/net/minecraft/core/item/ItemFood.java
+++ b/game/core/src/main/java/net/minecraft/core/item/ItemFood.java
@@ -15,7 +15,6 @@ public class ItemFood extends Item {
         this.healAmount = healAmount;
         this.ticksPerHeal = ticksPerHeal;
         this.isWolfsFavoriteMeat = favouriteWolfMeat;
-
         this.maxStackSize = maxStackSize;
     }
 
@@ -24,18 +23,17 @@ public class ItemFood extends Item {
         if(player.getHealth() < player.getMaxHealth()
             && player.getHealth() + player.getTotalHealingRemaining() < player.getMaxHealth()
             && selfStack.consumeItem(player)) {
-            player.eatFood(this);
-            world.playSoundAtEntity(player, player, this.ticksPerHeal >= 10 ? "random.bite_extended" : "random.bite", 0.5f + (itemRand.nextFloat() - itemRand.nextFloat()) * 0.1f, 1.1f + (itemRand.nextFloat() - itemRand.nextFloat()) * 0.1f);
+            player.eatFood(selfStack);
+            world.playSoundAtEntity(player, player, this.getTicksPerHeal(selfStack) >= 10 ? "random.bite_extended" : "random.bite", 0.5f + (itemRand.nextFloat() - itemRand.nextFloat()) * 0.1f, 1.1f + (itemRand.nextFloat() - itemRand.nextFloat()) * 0.1f);
         }
         return selfStack;
     }
 
-    public int getHealAmount()
-    {
+    public int getHealAmount(ItemStack stack) {
         return this.healAmount;
     }
 
-    public int getTicksPerHeal() {
+    public int getTicksPerHeal(ItemStack stack) {
         return this.ticksPerHeal;
     }
 
diff --git a/game/core/src/main/java/net/minecraft/core/item/ItemStack.java b/game/core/src/main/java/net/minecraft/core/item/ItemStack.java
index 94adda0c5..acd647ec6 100644
--- a/game/core/src/main/java/net/minecraft/core/item/ItemStack.java
+++ b/game/core/src/main/java/net/minecraft/core/item/ItemStack.java
@@ -20,6 +20,8 @@ import org.jetbrains.annotations.Nullable;
 
 import java.util.Objects;
 
+import static net.minecraft.core.world.save.conversion.ItemConverters.legacyBucketConverter;
+
 
 public final class ItemStack {
     public int stackSize;
@@ -405,6 +407,9 @@ public final class ItemStack {
         if(nbt == null) {
             return null;
         }
+
+        legacyBucketConverter(nbt);
+
         ItemStack stack = new ItemStack();
         stack.readFromNBT(nbt);
         if(stack.stackSize <= 0) {
@@ -486,4 +491,8 @@ public final class ItemStack {
         this.tag = tag;
 	}
 
+    public @NotNull ItemStack withTagValue(@NotNull String key, int value) {
+        this.tag.putInt(key, value);
+        return this;
+    }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/item/Items.java b/game/core/src/main/java/net/minecraft/core/item/Items.java
index a9a2f7b42..895ae1556 100644
--- a/game/core/src/main/java/net/minecraft/core/item/Items.java
+++ b/game/core/src/main/java/net/minecraft/core/item/Items.java
@@ -60,6 +60,13 @@ public final class Items {
     public static Item WHEAT;
     public static Item FOOD_BREAD;
 
+    public static Item BUCKET_IRON;
+    public static Item BUCKET_STEEL;
+    //public static Item BUCKET_WATER;
+    //public static Item BUCKET_LAVA;
+    //public static Item BUCKET_MILK;
+    //public static Item BUCKET_ICECREAM;
+
     public static Item ARMOR_HELMET_LEATHER;
     public static Item ARMOR_CHESTPLATE_LEATHER;
     public static Item ARMOR_LEGGINGS_LEATHER;
@@ -94,9 +101,6 @@ public final class Items {
     public static Item FOOD_APPLE_GOLD;
     public static Item SIGN;
     public static Item DOOR_OAK;
-    public static Item BUCKET;
-    public static Item BUCKET_WATER;
-    public static Item BUCKET_LAVA;
     public static Item MINECART;
     public static Item SADDLE;
     public static Item DOOR_IRON;
@@ -104,7 +108,6 @@ public final class Items {
     public static Item AMMO_SNOWBALL;
     public static Item BOAT;
     public static Item LEATHER;
-    public static Item BUCKET_MILK;
     public static Item BRICK_CLAY;
     public static Item CLAY;
     public static Item SUGARCANE;
@@ -171,7 +174,6 @@ public final class Items {
     public static Item OLIVINE;
     public static Item AMMO_PEBBLE;
     public static Item FOOD_CHERRY;
-    public static Item BUCKET_ICECREAM;
     public static Item AMMO_ARROW_PURPLE;
     public static Item LABEL;
     public static Item JAR;
@@ -326,9 +328,9 @@ public final class Items {
         FOOD_APPLE_GOLD = (new ItemFood("food.apple.gold", "minecraft:item/food_apple_gold", 16450, 42, 0, false, 1));
         SIGN = (new ItemSign("sign", "minecraft:item/sign", 16451));
         DOOR_OAK = (new ItemDoor("door.oak", "minecraft:item/door_oak", 16452, Blocks.DOOR_PLANKS_OAK_BOTTOM, Blocks.DOOR_PLANKS_OAK_TOP));
-        BUCKET = (new ItemBucketEmpty("bucket", "minecraft:item/bucket", 16453));
-        BUCKET_WATER = (new ItemBucket("bucket.water", "minecraft:item/bucket_water", 16454, Blocks.FLUID_WATER_FLOWING)).setContainerItem(BUCKET);
-        BUCKET_LAVA = (new ItemBucket("bucket.lava", "minecraft:item/bucket_lava", 16455, Blocks.FLUID_LAVA_FLOWING)).setContainerItem(BUCKET);
+        //16453 DO NOT TOUCH THIS ID, LEGACY BUCKET CONVERTS FROM IT
+        //16454 DO NOT TOUCH THIS ID, LEGACY BUCKET CONVERTS FROM IT
+        //16455 DO NOT TOUCH THIS ID, LEGACY BUCKET CONVERTS FROM IT
         MINECART = (new ItemMinecart("minecart", "minecraft:item/minecart", 16456, EntityMinecart.PASSENGER_CART));
         SADDLE = (new ItemSaddle("saddle", "minecraft:item/saddle", 16457));
         DOOR_IRON = (new ItemDoor("door.iron", "minecraft:item/door_iron", 16458, Blocks.DOOR_IRON_BOTTOM, Blocks.DOOR_IRON_TOP));
@@ -336,7 +338,7 @@ public final class Items {
         AMMO_SNOWBALL = (new ItemSnowball("ammo.snowball", "minecraft:item/ammo_snowball", 16460));
         BOAT = (new ItemBoat("boat", "minecraft:item/boat", 16461));
         LEATHER = (new Item("leather", "minecraft:item/leather", 16462));
-        BUCKET_MILK = (new ItemBucket("bucket.milk", "minecraft:item/bucket_milk", 16463, null)).setContainerItem(BUCKET);
+        //16463 DO NOT TOUCH THIS ID, LEGACY BUCKET CONVERTS FROM IT
         BRICK_CLAY = (new Item("brick.clay", "minecraft:item/brick_clay", 16464));
         CLAY = (new Item("clay", "minecraft:item/clay", 16465));
         SUGARCANE = (new ItemPlaceable("sugarcane", "minecraft:item/sugarcane", 16466, Blocks.SUGARCANE));
@@ -403,7 +405,7 @@ public final class Items {
         OLIVINE = (new Item("olivine", "minecraft:item/olivine", 16513));
         AMMO_PEBBLE = (new ItemPebble("ammo.pebble", "minecraft:item/ammo_pebble", 16514));
         FOOD_CHERRY = (new ItemFood("food.cherry", "minecraft:item/food_cherry", 16515, 2, 2, false, 2));
-        BUCKET_ICECREAM = (new ItemBucketIceCream("bucket.icecream", "minecraft:item/bucket_icecream", 16516, 10, 4)).setContainerItem(BUCKET);
+        //16516 DO NOT TOUCH THIS ID, LEGACY BUCKET CONVERTS FROM IT
         AMMO_ARROW_PURPLE = (new Item("ammo.arrow.purple", "minecraft:item/ammo_arrow_purple", 16517)).withTags(ItemTags.NOT_IN_CREATIVE_MENU);
         LABEL = (new ItemLabel("label", "minecraft:item/label", 16518));
         JAR = (new ItemJar("jar", "minecraft:item/jar", 16519));
@@ -450,5 +452,8 @@ public final class Items {
         ARMOR_WOLF_DIAMOND = new ItemArmor<>("armor.wolf.diamond", "minecraft:item/armor_wolf_diamond", 16559, ArmorMaterial.DIAMOND, WolfArmorShape.BODY);
         ARMOR_WOLF_GOLD = new ItemArmor<>("armor.wolf.gold", "minecraft:item/armor_wolf_gold", 16560, ArmorMaterial.GOLD, WolfArmorShape.BODY);
         ARMOR_WOLF_STEEL = new ItemArmor<>("armor.wolf.steel", "minecraft:item/armor_wolf_steel", 16561, ArmorMaterial.STEEL, WolfArmorShape.BODY);
+
+        BUCKET_IRON = (new ItemBucketIron("bucket.iron", "minecraft:item/bucket_iron", 16562));
+        BUCKET_STEEL = (new ItemBucketSteel("bucket.steel", "minecraft:item/bucket_steel", 16563));
     }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuInventoryCreative.java b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuInventoryCreative.java
index 67c963377..a721eea58 100644
--- a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuInventoryCreative.java
+++ b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuInventoryCreative.java
@@ -3,15 +3,13 @@ package net.minecraft.core.player.inventory.menu;
 import net.minecraft.core.block.Block;
 import net.minecraft.core.block.IPainted;
 import net.minecraft.core.block.tag.BlockTags;
-import net.minecraft.core.item.ItemCoal;
+import net.minecraft.core.item.*;
 import net.minecraft.core.item.tag.ItemTags;
 import net.minecraft.core.lang.I18n;
-import net.minecraft.core.item.Item;
-import net.minecraft.core.item.Items;
-import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.block.Blocks;
 import net.minecraft.core.player.inventory.container.ContainerInventory;
 import net.minecraft.core.player.inventory.slot.SlotCreative;
+import net.minecraft.core.util.collection.NamespaceID;
 import net.minecraft.core.util.helper.DyeColor;
 
 import java.util.ArrayList;
@@ -156,6 +154,16 @@ public class MenuInventoryCreative extends MenuInventory
 				for (DyeColor color : DyeColor.values()) {
 					creativeItems.add(new ItemStack(item, 1, color.itemMeta));
 				}
+           } else if (item instanceof ItemBucket bucket) {
+				creativeItems.add(new ItemStack(item));
+				for (NamespaceID stateId : ItemBucket.getRegisteredStateIds()) {
+					if (ItemBucket.STATE_EMPTY.equals(stateId)) continue;
+					ItemStack itemStack = new ItemStack(item, 1);
+					ItemBucket.setState(itemStack, stateId);
+					ItemBucket.setCharges(itemStack, bucket.maxCharges);
+
+					creativeItems.add(itemStack);
+				}
 			} else {
 				creativeItems.add(new ItemStack(item));
 			}
diff --git a/game/core/src/main/java/net/minecraft/core/player/inventory/slot/SlotResult.java b/game/core/src/main/java/net/minecraft/core/player/inventory/slot/SlotResult.java
index cd181212d..2b7fabb42 100644
--- a/game/core/src/main/java/net/minecraft/core/player/inventory/slot/SlotResult.java
+++ b/game/core/src/main/java/net/minecraft/core/player/inventory/slot/SlotResult.java
@@ -4,9 +4,7 @@ import net.minecraft.core.achievement.Achievements;
 import net.minecraft.core.block.Blocks;
 import net.minecraft.core.data.registry.Registries;
 import net.minecraft.core.entity.player.Player;
-import net.minecraft.core.item.Item;
-import net.minecraft.core.item.Items;
-import net.minecraft.core.item.ItemStack;
+import net.minecraft.core.item.*;
 import net.minecraft.core.item.tool.ItemToolHoe;
 import net.minecraft.core.item.tool.ItemToolPickaxe;
 import net.minecraft.core.item.tool.ItemToolSword;
@@ -41,7 +39,7 @@ public class SlotResult extends Slot
 		if(item.id == Blocks.FURNACE_STONE_IDLE.id()) thePlayer.addStat(Achievements.BUILD_FURNACE, 1);
 		if(item.id == Items.FOOD_BREAD.id) thePlayer.addStat(Achievements.MAKE_BREAD, 1);
 		if(item.id == Items.FOOD_CAKE.id) thePlayer.addStat(Achievements.BAKE_CAKE, 1);
-		if(item.id == Items.BUCKET_ICECREAM.id) thePlayer.addStat(Achievements.CRAFT_ICECREAM, 1);
+		if(item instanceof ItemBucket && ItemBucket.getState(itemStack) == ItemBucket.STATE_ICECREAM) thePlayer.addStat(Achievements.CRAFT_ICECREAM, 1);
 		if(item.id == Items.FOOD_PUMPKIN_PIE.id) thePlayer.addStat(Achievements.CRAFT_PUMPKIN_PIE, 1);
 		if(item.id == Items.HANDCANNON_UNLOADED.id) thePlayer.addStat(Achievements.CRAFT_HANDCANNON, 1);
 		if(item instanceof ItemToolHoe) thePlayer.addStat(Achievements.BUILD_HOE, 1);
diff --git a/game/core/src/main/java/net/minecraft/core/world/LevelListener.java b/game/core/src/main/java/net/minecraft/core/world/LevelListener.java
index 80eeaa56b..9312da9e5 100644
--- a/game/core/src/main/java/net/minecraft/core/world/LevelListener.java
+++ b/game/core/src/main/java/net/minecraft/core/world/LevelListener.java
@@ -48,6 +48,20 @@ public interface LevelListener
      * data: unused
      */
     int EVENT_PISTON_RETRACT = 1007;
+    /**
+     * data: is a packed byte containing a {@link net.minecraft.core.util.helper.Side} and a conduit signal strength (0-10) <div></div>
+     * Bits 0-3: Signal Strength (Value: 0-10) <div>
+     * Bits 4-6: Side ID (Value: 0-5, or 7 for NONE) <div>
+     * Bit 7: Unused (Reserved) <div></div>
+     */
+    int EVENT_CONDUIT_PORT_PARTICLES = 1008;
+    /**
+     * data: is a packed byte containing a {@link net.minecraft.core.util.helper.Side} and a conduit signal strength (0-10) <div></div>
+     * Bits 0-3: Signal Strength (Value: 0-10) <div>
+     * Bits 4-6: Side ID (Value: 0-5, or 7 for NONE) <div>
+     * Bit 7: Unused (Reserved) <div></div>
+     */
+    int EVENT_CONDUIT_SURFACE_PARTICLES = 1009;
 
 	/**
 	 * Redraw block in singleplayer and send block change packet in multiplayer. <br>
diff --git a/game/core/src/main/java/net/minecraft/core/world/World.java b/game/core/src/main/java/net/minecraft/core/world/World.java
index abd382df0..97881ef47 100644
--- a/game/core/src/main/java/net/minecraft/core/world/World.java
+++ b/game/core/src/main/java/net/minecraft/core/world/World.java
@@ -12,6 +12,7 @@ import net.minecraft.core.block.material.Material;
 import net.minecraft.core.block.material.Materials;
 import net.minecraft.core.block.support.ISupport;
 import net.minecraft.core.block.tag.BlockTags;
+import net.minecraft.core.current.conduit.ConduitHandler;
 import net.minecraft.core.current.wire.WireHandler;
 import net.minecraft.core.data.gamerule.GameRule;
 import net.minecraft.core.data.gamerule.GameRules;
@@ -147,6 +148,7 @@ public abstract class World implements MutableWorldSource {
     private final @NotNull WeatherManager weatherManager;
     private final @NotNull CommandManager commandManager = new CommandManager(isServer);
     private final @NotNull WireHandler wireHandler = new WireHandler(this);
+    private final @NotNull ConduitHandler conduitHandler = new ConduitHandler(this);
     private final @NotNull LightingEngine lightingEngine = new LightingEngineLegacy(this);
 
     private World(
@@ -304,6 +306,10 @@ public abstract class World implements MutableWorldSource {
         return this.wireHandler;
     }
 
+    public @NotNull ConduitHandler getConduitHandler() {
+        return this.conduitHandler;
+    }
+
     protected abstract @NotNull ChunkProvider createChunkProvider(@NotNull LevelStorage levelStorage); /* {
         final ChunkLoader chunkLoader = this.levelStorage.getChunkLoader(this.dimension);
         return accessor.createChunkProvider(this, chunkLoader);
@@ -2026,6 +2032,7 @@ public abstract class World implements MutableWorldSource {
         if (this.getGameRuleValue(GameRules.DO_DAY_CYCLE)) {
             this.levelData.setWorldTime(this.levelData.getWorldTime() + 1L);
         }
+        this.levelData.setTotalWorldTime(this.levelData.getTotalWorldTime() + 1L);
 
         this.runtime++;
 
@@ -2593,6 +2600,10 @@ public abstract class World implements MutableWorldSource {
         return this.levelData.getWorldTime();
     }
 
+    public long getTotalWorldTime() {
+        return this.levelData.getTotalWorldTime();
+    }
+
     public Difficulty getDifficulty() {
         return this.levelData.getDifficulty();
     }
diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/chunk/skyblock/ChunkDecoratorSkyblock.java b/game/core/src/main/java/net/minecraft/core/world/generate/chunk/skyblock/ChunkDecoratorSkyblock.java
index 8ebb9d387..22f1bdc6b 100644
--- a/game/core/src/main/java/net/minecraft/core/world/generate/chunk/skyblock/ChunkDecoratorSkyblock.java
+++ b/game/core/src/main/java/net/minecraft/core/world/generate/chunk/skyblock/ChunkDecoratorSkyblock.java
@@ -3,6 +3,7 @@ package net.minecraft.core.world.generate.chunk.skyblock;
 import net.minecraft.core.block.Blocks;
 import net.minecraft.core.block.entity.TileEntity;
 import net.minecraft.core.block.entity.TileEntityChest;
+import net.minecraft.core.item.ItemBucket;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.item.Items;
 import net.minecraft.core.world.chunk.Chunk;
@@ -24,7 +25,7 @@ public class ChunkDecoratorSkyblock
             chunk.setBlockIDWithMetadata(4 & (Chunk.CHUNK_SIZE_X - 1), 67, -3 & (Chunk.CHUNK_SIZE_Z - 1), Blocks.CHEST_PLANKS_OAK.id(), 3);
             TileEntity tileEntity = chunk.getTileEntity(4 & (Chunk.CHUNK_SIZE_X - 1), 67, -3 & (Chunk.CHUNK_SIZE_Z - 1));
             if (tileEntity instanceof TileEntityChest chest) {
-                chest.setItem(0, new ItemStack(Items.BUCKET_LAVA, 1));
+                chest.setItem(0, ItemBucket.createItemStack(Items.BUCKET_IRON, ItemBucket.STATE_LAVA, 1));
                 chest.setItem(1, new ItemStack(Blocks.ICE, 1));
             }
         }
diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/feature/WorldFeatureDungeon.java b/game/core/src/main/java/net/minecraft/core/world/generate/feature/WorldFeatureDungeon.java
index 3e7b5eeac..326a5a857 100644
--- a/game/core/src/main/java/net/minecraft/core/world/generate/feature/WorldFeatureDungeon.java
+++ b/game/core/src/main/java/net/minecraft/core/world/generate/feature/WorldFeatureDungeon.java
@@ -35,7 +35,7 @@ public class WorldFeatureDungeon extends WorldFeature {
         this.chestLoot.addEntry(new WeightedRandomLootObject(Items.WHEAT.getDefaultStack(), 1, 4), 100);
         this.chestLoot.addEntry(new WeightedRandomLootObject(Items.GUNPOWDER.getDefaultStack(), 1, 4), 100);
         this.chestLoot.addEntry(new WeightedRandomLootObject(Items.STRING.getDefaultStack(), 1, 4), 100);
-        this.chestLoot.addEntry(new WeightedRandomLootObject(Items.BUCKET.getDefaultStack()), 100);
+        this.chestLoot.addEntry(new WeightedRandomLootObject(Items.BUCKET_IRON.getDefaultStack()), 100);
         this.chestLoot.addEntry(new WeightedRandomLootObject(Items.FOOD_APPLE_GOLD.getDefaultStack()), 1);
         this.chestLoot.addEntry(new WeightedRandomLootObject(Items.DUST_REDSTONE.getDefaultStack(), 1, 4), 50);
         for (int i = 0; i < 9; i++) {
diff --git a/game/core/src/main/java/net/minecraft/core/world/pos/TilePosc.java b/game/core/src/main/java/net/minecraft/core/world/pos/TilePosc.java
index d700240a4..8824a90f0 100644
--- a/game/core/src/main/java/net/minecraft/core/world/pos/TilePosc.java
+++ b/game/core/src/main/java/net/minecraft/core/world/pos/TilePosc.java
@@ -93,4 +93,5 @@ public interface TilePosc {
 
         return this.x() == tilePos.x() && this.y() == tilePos.y() && this.z() == tilePos.z();
     }
+
 }
diff --git a/game/core/src/main/java/net/minecraft/core/world/save/LevelData.java b/game/core/src/main/java/net/minecraft/core/world/save/LevelData.java
index 76247fd7c..cbc14fe5c 100644
--- a/game/core/src/main/java/net/minecraft/core/world/save/LevelData.java
+++ b/game/core/src/main/java/net/minecraft/core/world/save/LevelData.java
@@ -53,6 +53,11 @@ public class LevelData {
             tag.getInteger("SpawnZ")
         );
         data.worldTime = tag.getLong("Time");
+        if (tag.containsKey("TotalTime")) {
+            data.totalWorldTime = tag.getLong("TotalTime");
+        } else {
+            data.totalWorldTime = data.worldTime;
+        }
         data.lastTimePlayed = tag.getLong("LastPlayed");
         data.sizeOnDisk = tag.getLong("SizeOnDisk");
         data.saveVersion = tag.getInteger("version");
@@ -77,6 +82,7 @@ public class LevelData {
         out.putInt("SpawnY", levelData.spawnPos.y);
         out.putInt("SpawnZ", levelData.spawnPos.z);
         out.putLong("Time", levelData.worldTime);
+        out.putLong("TotalTime", levelData.totalWorldTime);
         out.putLong("SizeOnDisk", levelData.sizeOnDisk);
         out.putLong("LastPlayed", System.currentTimeMillis());
         out.putString("LevelName", levelData.levelName);
@@ -132,6 +138,7 @@ public class LevelData {
     private final @NotNull SpawnerConfig spawnerConfig;
 
     private long worldTime;
+    private long totalWorldTime;
     private long lastTimePlayed;
     private long sizeOnDisk;
     private @Nullable UUID lastPlayerUUID;
@@ -183,6 +190,7 @@ public class LevelData {
 
         this.spawnPos.set(levelData.spawnPos);
         this.worldTime = levelData.worldTime;
+        this.totalWorldTime = levelData.totalWorldTime;
         this.lastTimePlayed = levelData.lastTimePlayed;
         this.sizeOnDisk = levelData.sizeOnDisk;
         this.playerTag = levelData.playerTag;
@@ -214,6 +222,14 @@ public class LevelData {
         this.worldTime = time;
     }
 
+    public long getTotalWorldTime() {
+        return this.totalWorldTime;
+    }
+
+    public void setTotalWorldTime(final long time) {
+        this.totalWorldTime = time;
+    }
+
     public long getSizeOnDisk() {
         return this.sizeOnDisk;
     }
diff --git a/game/core/src/main/java/net/minecraft/core/world/save/conversion/ItemConverters.java b/game/core/src/main/java/net/minecraft/core/world/save/conversion/ItemConverters.java
new file mode 100644
index 000000000..b419ad579
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/save/conversion/ItemConverters.java
@@ -0,0 +1,44 @@
+package net.minecraft.core.world.save.conversion;
+
+import com.mojang.nbt.tags.CompoundTag;
+import net.minecraft.core.item.ItemBucket;
+import net.minecraft.core.util.collection.NamespaceID;
+
+public class ItemConverters {
+    /**
+     * Migrates legacy bucket IDs to the new NBT-based Bucket system.
+     */
+    public static void legacyBucketConverter(CompoundTag tag) {
+        short id = tag.getShort("id");
+        NamespaceID newState = null;
+
+        short ironBucketId = 16562;
+
+        if (id == 16453) {
+            tag.putShort("id", ironBucketId);
+        }
+        else if (id == 16454) { // Legacy Water
+            tag.putShort("id", ironBucketId);
+            newState = ItemBucket.STATE_WATER;
+        }
+        else if (id == 16455) { // Legacy Lava
+            tag.putShort("id", ironBucketId);
+            newState = ItemBucket.STATE_LAVA;
+        }
+        else if (id == 16463) { // Legacy Milk
+            tag.putShort("id", ironBucketId);
+            newState = ItemBucket.STATE_MILK;
+        }
+        else if (id == 16516) { // Legacy Ice Cream
+            tag.putShort("id", ironBucketId);
+            newState = ItemBucket.STATE_ICECREAM;
+        }
+
+        if (newState != null) {
+            CompoundTag dataTag = tag.containsKey("Data") ? tag.getCompound("Data") : new CompoundTag();
+            dataTag.putString("State", newState.toString());
+            dataTag.putInt("Charges", 1);
+            tag.putCompound("Data", dataTag);
+        }
+    }
+}
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/world/weather/WeatherManager.java b/game/core/src/main/java/net/minecraft/core/world/weather/WeatherManager.java
index 12ac246e3..a6f2a4100 100644
--- a/game/core/src/main/java/net/minecraft/core/world/weather/WeatherManager.java
+++ b/game/core/src/main/java/net/minecraft/core/world/weather/WeatherManager.java
@@ -113,8 +113,7 @@ public class WeatherManager {
         } else if (this.nextWeather != null && this.weatherIntensity >= 0.0f) {
             this.weatherIntensity -= 0.002f;
             if (!this.world.isClientSide) {
-                if (this.currentWeather instanceof IPrecipitation) {
-                    final @NotNull IPrecipitation precipitation = (IPrecipitation) this.currentWeather;
+                if (this.currentWeather instanceof @NotNull IPrecipitation precipitation) {
                     if (this.world.dayCanHaveRainbow &&
                         this.world.rainbowTicks <= 0 &&
                         this.weatherIntensity < 0.5f &&
diff --git a/game/core/src/main/resources/assets/minecraft/lang/en_US/item.lang b/game/core/src/main/resources/assets/minecraft/lang/en_US/item.lang
index 739522bf8..1783d0edc 100644
--- a/game/core/src/main/resources/assets/minecraft/lang/en_US/item.lang
+++ b/game/core/src/main/resources/assets/minecraft/lang/en_US/item.lang
@@ -208,12 +208,6 @@ item.sign.painted.white.desc=Mark your land, or write a message.
 
 item.door.oak.name=Wooden Door
 item.door.oak.desc=Every house needs one.
-item.bucket.name=Bucket
-item.bucket.desc=Used to hold various liquids.
-item.bucket.water.name=Water Bucket
-item.bucket.water.desc=A bucket full of the deep blue.
-item.bucket.lava.name=Lava Bucket
-item.bucket.lava.desc=A bucket full of molten rock. Hot!
 item.minecart.name=Minecart
 item.minecart.desc=An iron vehicle for exploring the deep underground.
 item.saddle.name=Saddle
@@ -228,8 +222,6 @@ item.boat.name=Boat
 item.boat.desc=Sail the seven seas!
 item.leather.name=Leather
 item.leather.desc=A flat cow. Can be used to make armor.
-item.bucket.milk.name=Bucket of Milk
-item.bucket.milk.desc=Good for your bones.
 item.brick.clay.name=Clay Brick
 item.brick.clay.desc=Put 4 of these together to create a solid foundation.
 item.clay.name=Clay
@@ -369,8 +361,6 @@ item.ammo.pebble.name=Pebble
 item.ammo.pebble.desc=A chunk of rock found buried in the soil.
 item.food.cherry.name=Cherries
 item.food.cherry.desc=Red berries that come in pairs.
-item.bucket.icecream.name=Bucket of Ice Cream
-item.bucket.icecream.desc=A frosty treat sprinkled with chocolate chips and a cherry!
 item.ammo.fireball.name=Fire Charge
 item.ammo.fireball.desc=Ammo for a hand cannon. Scatters fire over a wide area.
 item.ammo.arrow.purple.name=Purple Arrow
@@ -378,6 +368,28 @@ item.ammo.arrow.purple.desc=How did you get this?!
 item.label.name=Label
 item.label.desc=Allows you to name items and creatures. Can be dyed.
 
+item.bucket.iron.name=Iron Bucket
+item.bucket.iron.desc=Used to hold various liquids.
+item.bucket.iron.water.name=Iron Water Bucket
+item.bucket.iron.water.desc=A bucket full of the deep blue.
+item.bucket.iron.lava.name=Iron Lava Bucket
+item.bucket.iron.lava.desc=A bucket full of molten rock. Hot!
+item.bucket.iron.milk.name=Iron Bucket of Milk
+item.bucket.iron.milk.desc=Good for your bones.
+item.bucket.iron.icecream.name=Iron Bucket of Ice Cream
+item.bucket.iron.icecream.desc=A frosty treat sprinkled with chocolate chips and a cherry!
+
+item.bucket.steel.name=Steel Bucket
+item.bucket.steel.desc=Used to hold various liquid.
+item.bucket.steel.water.name=Steel Water Bucket
+item.bucket.steel.water.desc=A large bucket full of the deep blue.
+item.bucket.steel.lava.name=Steel Lava Bucket
+item.bucket.steel.lava.desc=A large bucket full of molten rock. Hot!
+item.bucket.steel.milk.name=Steel Bucket of Milk
+item.bucket.steel.milk.desc=Excessively good for your bones!
+item.bucket.steel.icecream.name=Steel Bucket of Ice Cream
+item.bucket.steel.icecream.desc=Maybe a little too much ice cream...
+
 item.candle.soulwax.name=Soul Wax Candle
 item.candle.soulwax.desc=A waxy brown candle that glows with an unnatural light.
 
diff --git a/game/core/src/main/resources/assets/minecraft/lang/en_US/tile.lang b/game/core/src/main/resources/assets/minecraft/lang/en_US/tile.lang
index 711231fb9..887050e81 100644
--- a/game/core/src/main/resources/assets/minecraft/lang/en_US/tile.lang
+++ b/game/core/src/main/resources/assets/minecraft/lang/en_US/tile.lang
@@ -1442,8 +1442,8 @@ tile.block.rubyglass.desc=A translucent glass-like crystal brimming with energy.
 tile.rubyglass.sprout.name=Rubyglass Bud
 tile.rubyglass.sprout.desc=A small bud of rubyglass, branching out in many directions.
 
-tile.rubyglass.circuit.name=Rubyglass Circuit
-tile.rubyglass.circuit.desc=Netherrack etched with lines of rubyglass. It resonates with latent power.
+tile.rubyglass.conduit.name=Rubyglass Conduit
+tile.rubyglass.conduit.desc=Netherrack etched with lines of rubyglass. It resonates with latent power.
 
 tile.vent.name=Chimney
 tile.vent.desc=A rocky column that emits a stream of smoke.
diff --git a/util/datagen/src/main/java/net/minecraft/datagen/WorkbenchGenerator.java b/util/datagen/src/main/java/net/minecraft/datagen/WorkbenchGenerator.java
index d663aa894..b1a0c2994 100644
--- a/util/datagen/src/main/java/net/minecraft/datagen/WorkbenchGenerator.java
+++ b/util/datagen/src/main/java/net/minecraft/datagen/WorkbenchGenerator.java
@@ -1,12 +1,9 @@
 package net.minecraft.datagen;
 
 import net.minecraft.core.block.Block;
-import net.minecraft.core.data.registry.recipe.entry.RecipeEntryCraftingWithTool;
-import net.minecraft.core.data.registry.recipe.entry.RecipeEntryDyeing;
-import net.minecraft.core.data.registry.recipe.entry.RecipeEntryRepairable;
-import net.minecraft.core.data.registry.recipe.entry.RecipeEntryScrap;
-import net.minecraft.core.data.registry.recipe.entry.RecipeEntryUndyeing;
+import net.minecraft.core.data.registry.recipe.entry.*;
 import net.minecraft.core.item.Item;
+import net.minecraft.core.item.ItemBucket;
 import net.minecraft.datagen.recipeBuilders.RecipeBuilder;
 import net.minecraft.datagen.recipeBuilders.RecipeBuilderShaped;
 import net.minecraft.core.block.Blocks;
@@ -150,12 +147,12 @@ class WorkbenchGenerator {
 
         RecipeBuilder.Shaped(CORE_NAMESPACE)
             .setShape(
-                "#S#",
-                "#N#",
-                "#R#")
+                "###",
+                "CSR",
+                "###")
             .addInput('#', Blocks.COBBLE_NETHERRACK)
             .addInput('S', Blocks.SOULSAND)
-            .addInput('N', Items.RUBYGLASS_CRYSTAL)
+            .addInput('C', Blocks.RUBYGLASS_CONDUIT)
             .addInput('R', Items.DUST_REDSTONE)
             .create("activator", Blocks.ACTIVATOR_COBBLE_NETHERRACK.getDefaultStack());
 
@@ -373,12 +370,12 @@ class WorkbenchGenerator {
 
         RecipeBuilder.Shapeless(CORE_NAMESPACE)
             .addInput(Blocks.SPONGE_DRY)
-            .addInput(Items.BUCKET_WATER)
+            .addInput("minecraft:water_buckets")
             .create("sponge_to_wet_sponge", Blocks.SPONGE_WET.getDefaultStack());
 
         RecipeBuilder.Shapeless(CORE_NAMESPACE)
             .addInput(Blocks.PUMICE_DRY)
-            .addInput(Items.BUCKET_LAVA)
+            .addInput("minecraft:lava_buckets")
             .create("pumice_to_wet_pumice", Blocks.PUMICE_WET.getDefaultStack());
 
         RecipeBuilder.Shapeless(CORE_NAMESPACE)
@@ -529,7 +526,15 @@ class WorkbenchGenerator {
                 "# #",
                 " # ")
             .addInput('#', Items.INGOT_IRON)
-            .create("bucket", Items.BUCKET.getDefaultStack());
+            .create("iron_bucket", Items.BUCKET_IRON.getDefaultStack());
+
+        RecipeBuilder.Shaped(CORE_NAMESPACE)
+            .setShape(
+                    "# #",
+                    "*#*")
+            .addInput('#', Items.INGOT_STEEL)
+            .addInput('*', Items.INGOT_STEEL_CRUDE)
+            .create("steel_bucket", Items.BUCKET_STEEL.getDefaultStack());
 
         RecipeBuilder.Shaped(CORE_NAMESPACE)
             .setShape(
@@ -1060,33 +1065,32 @@ class WorkbenchGenerator {
             .addInput(new ItemStack(Items.DYE, 1, DyeColor.BROWN.itemMeta))
             .create("cookie", new ItemStack(Items.FOOD_COOKIE, 16));
 
-        RecipeBuilderShaped cake = RecipeBuilder.Shaped(CORE_NAMESPACE)
+        RecipeBuilderShaped cakeBase = RecipeBuilder.Shaped(CORE_NAMESPACE)
             .setShape(
                 "AAA",
                 "BEB",
                 "CCC")
-            .setConsumeContainer(false);
-
-        cake.addInput('A', Items.BUCKET_MILK)
+            .setConsumeContainer(false)
+            .addInput('A', "minecraft:milk_buckets")
             .addInput('B', Items.DUST_SUGAR)
-            .addInput('E', Items.EGG_CHICKEN)
-            .addInput('C', Items.DOUGH)
-            .create("cake", Items.FOOD_CAKE.getDefaultStack());
+            .addInput('C', Items.DOUGH);
 
-        cake.addInput('A', Items.BUCKET_MILK)
-            .addInput('B', Items.DUST_SUGAR)
-            .addInput('E', Blocks.PUMPKIN)
-            .addInput('C', Items.DOUGH)
-            .create("pumpkin_pie", Items.FOOD_PUMPKIN_PIE.getDefaultStack());
+        cakeBase.addInput('E', Items.EGG_CHICKEN).create("cake", Items.FOOD_CAKE.getDefaultStack());
+        cakeBase.addInput('E', Blocks.PUMPKIN).create("pumpkin_pie", Items.FOOD_PUMPKIN_PIE.getDefaultStack());
 
-        RecipeBuilder.Shaped(CORE_NAMESPACE)
-            .setShape(
-                "XXX",
-                "X#X",
-                "XXX")
-            .addInput('X', Blocks.BLOCK_GOLD)
-            .addInput('#', Items.FOOD_APPLE)
-            .create("golden_apple", Items.FOOD_APPLE_GOLD.getDefaultStack());
+        generateIceCreamProgression(Items.BUCKET_IRON, "iron");
+        generateIceCreamProgression(Items.BUCKET_STEEL, "steel");
+
+        RecipeBuilder.Shapeless(CORE_NAMESPACE).addInput(Items.SUGARCANE).create("sugar_cane_to_sugar", new ItemStack(Items.DUST_SUGAR, 3));
+    }
+
+    /**
+     * Dynamically generates the Ice Cream crafting progression for any given bucket type.
+     * @param bucketItem The Item instance of the bucket (Items.BUCKET_STEEL, Items.BUCKET_IRON, etc.)
+     * @param materialName The string name for the recipe ID ("steel", "iron", etc.)
+     */
+    public static void generateIceCreamProgression(Item bucketItem, String materialName) {
+        int max = ((ItemBucket) bucketItem).maxCharges;
 
         RecipeBuilder.Shaped(CORE_NAMESPACE)
             .setShape(
@@ -1096,11 +1100,23 @@ class WorkbenchGenerator {
             .addInput('Y', Items.FOOD_CHERRY)
             .addInput('O', new ItemStack(Items.DYE, 1, DyeColor.BROWN.itemMeta))
             .addInput('#', Items.AMMO_SNOWBALL)
-            .addInput('X', Items.BUCKET_MILK)
+            .addInput('X', ItemBucket.createRecipeInput(bucketItem, ItemBucket.STATE_MILK))
             .setConsumeContainer(true)
-            .create("ice_cream_bucket", Items.BUCKET_ICECREAM.getDefaultStack());
+            .create("icecream_bucket_" + materialName + "_from_milk", ItemBucket.createItemStack(bucketItem, ItemBucket.STATE_ICECREAM, 1));
 
-        RecipeBuilder.Shapeless(CORE_NAMESPACE).addInput(Items.SUGARCANE).create("sugar_cane_to_sugar", new ItemStack(Items.DUST_SUGAR, 3));
+        for (int currentCharge = 1; currentCharge < max; currentCharge++) {
+            RecipeBuilder.Shaped(CORE_NAMESPACE)
+                .setShape(
+                    " Y ",
+                    "#O#",
+                    " X ")
+                .addInput('Y', Items.FOOD_CHERRY)
+                .addInput('O', new ItemStack(Items.DYE, 1, DyeColor.BROWN.itemMeta))
+                .addInput('#', Items.AMMO_SNOWBALL)
+                .addInput('X', ItemBucket.createItemStack(bucketItem, ItemBucket.STATE_ICECREAM, currentCharge))
+                .setConsumeContainer(true)
+                .create("icecream_bucket_" + materialName + "_upgrade_" + currentCharge + "_to_" + (currentCharge + 1), ItemBucket.createItemStack(bucketItem, ItemBucket.STATE_ICECREAM, currentCharge + 1));
+        }
     }
 
     private static void generateDyes() {
@@ -1329,31 +1345,32 @@ class WorkbenchGenerator {
         RecipeBuilder.Shaped(CORE_NAMESPACE)
             .setShape(
                 "#Q#",
-                "#S#",
-                "#R#"
+                "#C#",
+                "#Q#"
             )
             .addInput('#', Blocks.COBBLE_NETHERRACK)
             .addInput('Q', Items.QUARTZ)
-            .addInput('R', Items.RUBYGLASS_CRYSTAL)
-            .addInput('S', Blocks.SOULSAND)
+            .addInput('C', Blocks.RUBYGLASS_CONDUIT)
             .create("matcher", new ItemStack(Blocks.MATCHER, 1));
 
         RecipeBuilder.Shaped(CORE_NAMESPACE)
                 .setShape(
                         "XOX",
-                        "OOO",
+                        "ORO",
                         "XOX")
+                .addInput('R', Items.DUST_REDSTONE)
                 .addInput('O', Items.RUBYGLASS_CRYSTAL)
                 .addInput('X', Blocks.COBBLE_NETHERRACK)
-                .create("rubyglass_circuit", new ItemStack(Blocks.RUBYGLASS_CIRCUIT, 1));
+                .create("rubyglass_conduit", new ItemStack(Blocks.RUBYGLASS_CONDUIT, 1));
 
         RecipeBuilder.Shaped(CORE_NAMESPACE)
                 .setShape(
                         "###",
-                        "#RB",
+                        "CQB",
                         "###")
-                .addInput('#', Blocks.COBBLE_NETHERRACK)
-                .addInput('R', Items.RUBYGLASS_CRYSTAL)
+                .addInput('#', Blocks.COBBLE_STONE_MOSSY)
+                .addInput('C', Blocks.RUBYGLASS_CONDUIT)
+                .addInput('Q', Items.QUARTZ)
                 .addInput('B', Items.BONE)
                 .create("motion_sensor", Blocks.MOTION_SENSOR_IDLE.getDefaultStack());
     }
diff --git a/util/datagen/src/main/java/net/minecraft/datagen/recipeBuilders/RecipeBuilderShaped.java b/util/datagen/src/main/java/net/minecraft/datagen/recipeBuilders/RecipeBuilderShaped.java
index 9765b4c1b..1bafdf30a 100644
--- a/util/datagen/src/main/java/net/minecraft/datagen/recipeBuilders/RecipeBuilderShaped.java
+++ b/util/datagen/src/main/java/net/minecraft/datagen/recipeBuilders/RecipeBuilderShaped.java
@@ -20,17 +20,18 @@ public class RecipeBuilderShaped extends RecipeBuilderBase{
     protected int height;
     protected boolean consumeContainer = DEFAULT_CONSUME_CONTAINER;
     protected boolean allowMirrored = DEFAULT_ALLOW_MIRROR;
-    protected final HashMap<Character, RecipeSymbol> symbolShapedMap = new HashMap<>();
+    protected HashMap<Character, RecipeSymbol> symbolShapedMap = new HashMap<>();
+
     /**
-     * Used for creating new shaped workbench recipes.
-     * @param modID Namespace to create recipe under
+     * Used for creating new-shaped workbench recipes.
+     * @param modID Namespace to create a recipe under
      */
     public RecipeBuilderShaped(String modID){
         super(modID);
     }
     /**
-     * Used for creating new shaped workbench recipes.
-     * @param modID Namespace to create recipe under
+     * Used for creating new-shaped workbench recipes.
+     * @param modID Namespace to create a recipe under
      * @param shape Recipe shape in symbol representation
      */
     public RecipeBuilderShaped(String modID, String... shape) {
@@ -101,22 +102,21 @@ public class RecipeBuilderShaped extends RecipeBuilderBase{
     /**
      * Specifies whether the recipe should consume container items.
      * <pre>{@code
-     *     RecipeBuilderShaped("minecraft")
-     *       .setShape(
-     *          "BBB",
-     *          "SES",
-     *          "WWW")
-     *       .addInput('B', Item.bucketMilk)
-     *       .addInput('S', Item.dustSugar)
-     *       .addInput('E', Item.eggChicken)
-     *       .addInput('W', Item.wheat)
-     *       .setConsumeContainer(false) // Recipe will return empty buckets when crafted
-     *       .create("cake", Item.cake.getDefaultStack());
+     * RecipeBuilder.Shaped("minecraft")
+     *      .setShape(
+     *          "AAA",
+     *          "BEB",
+     *          "CCC")
+     *      .addInput('A', "minecraft:milk_buckets")
+     *      .addInput('B', Items.DUST_SUGAR)
+     *      .addInput('E', Items.EGG_CHICKEN)
+     *      .addInput('C', Items.DOUGH)
+     *      .setConsumeContainer(false) // Recipe will leave empty or decremented buckets behind
+     *      .create("cake", Items.FOOD_CAKE.getDefaultStack());
      * }</pre>
      * @param consumeContainer Should consume ContainerItem
      * @return Copy of {@link RecipeBuilderShaped}
      */
-    @SuppressWarnings({"unused"})
     public RecipeBuilderShaped setConsumeContainer(boolean consumeContainer){
         RecipeBuilderShaped builder = this.clone(this);
         builder.consumeContainer = consumeContainer;
@@ -129,6 +129,13 @@ public class RecipeBuilderShaped extends RecipeBuilderBase{
         return builder;
     }
 
+    @Override
+    public RecipeBuilderShaped clone() {
+        RecipeBuilderShaped cloned = (RecipeBuilderShaped) super.clone();
+        cloned.symbolShapedMap = new HashMap<>(this.symbolShapedMap);
+        return cloned;
+    }
+
     /**
      * Assigns an item to an item symbol defined in the shape (see {@link #setShape(String...)})
      * @param templateSymbol Item symbol character
@@ -184,8 +191,8 @@ public class RecipeBuilderShaped extends RecipeBuilderBase{
     @SuppressWarnings({"unused"})
     public RecipeBuilderShaped addInput(char templateSymbol, RecipeSymbol symbol){
         if (templateSymbol == ' ') throw new IllegalArgumentException("Cannot assign item to protected symbol ' ' pick a different symbol for your recipe input");
-        RecipeBuilderShaped builder = this.clone(this);
-        symbolShapedMap.put(templateSymbol, symbol);
+        RecipeBuilderShaped builder = this.clone();
+        builder.symbolShapedMap.put(templateSymbol, symbol);
         return builder;
     }
 
@@ -199,15 +206,15 @@ public class RecipeBuilderShaped extends RecipeBuilderBase{
                 if (shape[y].length() > x) {
                     cha = shape[y].charAt(x);
                 }
-                RecipeSymbol tempplate = symbolShapedMap.get(cha);
-                if (tempplate == null){
+                RecipeSymbol template = symbolShapedMap.get(cha);
+                if (template == null){
                     recipe[x + y * width] = null;
                 } else {
-                    if(tempplate.getItemGroup() == null){
-                        RecipeSymbol s =  new RecipeSymbol(cha == null ? ' ' : cha, tempplate.getStack());
+                    if(template.getItemGroup() == null){
+                        RecipeSymbol s =  new RecipeSymbol(cha == null ? ' ' : cha, template.getStack());
                         recipe[x + y * width] = s;
                     } else {
-                        recipe[x + y * width] = new RecipeSymbol(cha == null ? ' ' : cha, tempplate.getStack(), tempplate.getItemGroup());
+                        recipe[x + y * width] = new RecipeSymbol(cha == null ? ' ' : cha, template.getStack(), template.getItemGroup());
                     }
 
                 }
diff --git a/util/datagen/src/main/java/net/minecraft/datagen/recipeBuilders/RecipeBuilderShapeless.java b/util/datagen/src/main/java/net/minecraft/datagen/recipeBuilders/RecipeBuilderShapeless.java
index c9c644c2f..f73b51f76 100644
--- a/util/datagen/src/main/java/net/minecraft/datagen/recipeBuilders/RecipeBuilderShapeless.java
+++ b/util/datagen/src/main/java/net/minecraft/datagen/recipeBuilders/RecipeBuilderShapeless.java
@@ -12,15 +12,24 @@ import java.util.ArrayList;
 import java.util.List;
 
 public class RecipeBuilderShapeless extends RecipeBuilderBase{
-    private final List<RecipeSymbol> symbolShapelessList = new ArrayList<>();
+
+    private List<RecipeSymbol> symbolShapelessList = new ArrayList<>();
+
     /**
      * Used for creating new shapeless workbench recipes.
-     * @param modID Namespace to create recipe under
+     * @param modID Namespace to create a recipe under
      */
     public RecipeBuilderShapeless(String modID) {
         super(modID);
     }
 
+    @Override
+    public RecipeBuilderShapeless clone() {
+        RecipeBuilderShapeless cloned = (RecipeBuilderShapeless) super.clone();
+        cloned.symbolShapelessList = new ArrayList<>(this.symbolShapelessList);
+        return cloned;
+    }
+
     /**
      * @param stack Item to add to recipe's item list
      * @return Copy of {@link RecipeBuilderShapeless}
@@ -66,10 +75,11 @@ public class RecipeBuilderShapeless extends RecipeBuilderBase{
      */
     @SuppressWarnings({"unused"})
     public RecipeBuilderShapeless addInput(RecipeSymbol symbol){
-        RecipeBuilderShapeless builder = this.clone(this);
-        symbolShapelessList.add(symbol);
+        RecipeBuilderShapeless builder = this.clone();
+        builder.symbolShapelessList.add(symbol);
         return builder;
     }
+
     @Override
     @SuppressWarnings({"unused", "unchecked"})
     public void create(String recipeID, ItemStack outputStack) {
