diff --git a/game/client/src/main/java/net/minecraft/client/player/controller/PlayerController.java b/game/client/src/main/java/net/minecraft/client/player/controller/PlayerController.java
index 0b94d52bb..109f4a818 100644
--- a/game/client/src/main/java/net/minecraft/client/player/controller/PlayerController.java
+++ b/game/client/src/main/java/net/minecraft/client/player/controller/PlayerController.java
@@ -102,7 +102,12 @@ public abstract class PlayerController {
         }
 
         final Block<?> mineBlock = world.getBlockType(tilePos);
-        if (!mineBlock.canCollideCheck(world.getBlockData(tilePos), false)) return;
+        if (!mineBlock.canCollideCheck(world.getBlockData(tilePos), false)) {
+            this.destroyProgress = 0.0f;
+            this.oDestroyProgress = 0.0f;
+            this.setMineBlock(null);
+            return;
+        }
 
         this.syncCurrentPlayItem();
 
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicDoor.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicDoor.java
index 00f5e7676..b9a16b98a 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicDoor.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicDoor.java
@@ -61,14 +61,14 @@ public class BlockLogicDoor
     }
 
     @Override
-    protected int getMatcherEquivalenceData(final int data) {
+    protected int getMatcherEquivalenceData(int data) {
         final int MASK_NON_COLOR = MASK_OPENED | MASK_HINGE | MASK_ROTATION;
-        int d1 = data & ~MASK_NON_COLOR;
-        int d2 = data &  MASK_NON_COLOR;
         assert (MASK_HINGE >>> 1) == MASK_OPENED;
-        d2 ^= d2 >>> 1; // flips the opened bit if hinge bit is set
-        d1 |= d2;
-        return d1 & ~(MASK_HINGE | MASK_ROTATION);
+        int data2 = data | ((data & MASK_HINGE) >>> 1); // flips the opened bit if hinge bit is set
+        data &= ~MASK_NON_COLOR;
+        data2 &= MASK_NON_COLOR;
+        data |= data2;
+        return data;
     }
 
     @Override
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicFlower.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicFlower.java
index 340eaa8f8..0faccf85a 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicFlower.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicFlower.java
@@ -162,6 +162,6 @@ public class BlockLogicFlower
 
     @Override
     protected int getMatcherEquivalenceData(int data) {
-        return data & ~((this.killedByWeather) ? MASK_PERMANENT : 0);
+        return data & ~ MASK_PERMANENT;
     }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicJar.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicJar.java
index 2160d52ec..ae7d3de80 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicJar.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicJar.java
@@ -18,6 +18,8 @@ import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.world.World;
 import net.minecraft.core.world.WorldSource;
 import net.minecraft.core.world.pos.TilePosc;
+import net.minecraft.core.world.wind.WindProvider;
+
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.joml.primitives.AABBd;
@@ -101,6 +103,7 @@ public class BlockLogicJar extends BlockLogic implements ISupportable {
                     }
 
                     flowerJar.flowerData = BlockLogicFlowerStackable.setPermanent(BlockLogicFlowerStackable.setStackCount(metadata, currentStackCount + 1), true);
+                    world.notifyBlocksOfNeighborChange(tilePos, this.block);
                     world.playBlockSoundEffect(player, tilePos.x() + 0.5f, tilePos.y() + 0.5f, tilePos.z() + 0.5f, this.block, EnumBlockSoundEffectType.PLACE);
                     player.getHeldItem().consumeItem(player);
                     return true;
@@ -132,4 +135,21 @@ public class BlockLogicJar extends BlockLogic implements ISupportable {
     public @NotNull ISupport getSupportConstraint(@NotNull World world, @NotNull TilePosc tilePos, @NotNull Side side) {
         return PartialSupport.INSTANCE.center();
     }
+
+    @Override
+    public boolean isEquivalent(@NotNull World world, @NotNull TilePosc thisPos, @NotNull TilePosc thatPos) {
+        assert world.getBlockType(thisPos) == this.block;
+        if (world.getBlockType(thatPos) != this.block) return false;
+        final int thisData, thatData; {
+            thisData = world.getBlockData(thisPos);
+            thatData = world.getBlockData(thatPos);
+        }
+        if ((thisData | thatData) == 0) return true;
+        if (!(world.getTileEntity(thisPos) instanceof TileEntityFlowerJar thisFlowerJar) ||
+            !(world.getTileEntity(thatPos) instanceof TileEntityFlowerJar thatFlowerJar))
+            return false;
+        if (thisFlowerJar.flowerInPot != thatFlowerJar.flowerInPot) return false;
+        if (thisFlowerJar.flowerData != thatFlowerJar.flowerData) return false;
+        return true;
+    }
 }
\ No newline at end of file
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 a0286ec70..2fec961c1 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
@@ -174,11 +174,6 @@ public class BlockLogicLamp extends BlockLogic implements IPainted {
 
     @Override
     protected boolean isSameBlockForMatcher(final @NotNull Block<?> other) {
-        return other.getLogic() instanceof BlockLogicLamp;
-    }
-
-    @Override
-    protected int getMatcherEquivalenceData(final int data) {
-        return (this.isActive ? 1 : 0) | (data & 0x0F);
+        return other.getLogic() instanceof BlockLogicLamp lamp && this.isActive == lamp.isActive;
     }
 }
\ 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 00d53ac83..e1066c02f 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
@@ -2,6 +2,7 @@ package net.minecraft.core.block;
 
 import net.minecraft.core.block.entity.TileEntity;
 import net.minecraft.core.block.material.Materials;
+import net.minecraft.core.block.motion.CarriedBlock;
 import net.minecraft.core.block.piston.IMovingBlock;
 import net.minecraft.core.block.piston.MovingLine;
 import net.minecraft.core.block.piston.TileEntityMovingPistonBlock;
@@ -105,6 +106,12 @@ public class BlockLogicMatcher extends BlockLogicAxisAligned {
         this.scheduleUpdate(world, targetPos);
     }
 
+    @Override
+    public void onFlung(@NotNull World world, @NotNull TilePosc sourcePos, int data, @NotNull CarriedBlock block) {
+        // technically it should poweroff 4 ticks later, but we don't have clean abstract block update scheduling yet
+        block.blockId = Blocks.MATCHER.id();
+    }
+
     public void updateState(@NotNull World world, @NotNull TilePosc tilePos) {
         Axis a = BlockLogicAxisAligned.metaToAxis(world.getBlockData(tilePos) & BlockLogicAxisAligned.MASK_DIRECTION);
 
diff --git a/game/core/src/main/java/net/minecraft/core/entity/Entity.java b/game/core/src/main/java/net/minecraft/core/entity/Entity.java
index 5a6dfade0..ef904d406 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/Entity.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/Entity.java
@@ -465,12 +465,12 @@ public abstract class Entity
 
         double bbWidth = this.bb.maxX - this.bb.minX - 0.05;
         if (isSneaking()) { // Edge safety behavior
-            if (zd != 0.0) {
-                workingBox1.setMin(this.bb.minX, this.bb.minY - 1.0, this.bb.minZ);
-                workingBox1.setMax(this.bb.maxX, this.bb.minY, this.bb.maxZ);
-                MathHelper.aabbExpand(workingBox1, 0.0, 0.0, zd, workingBox1);
-                List<AABBdc> collidingBoxes = this.world.getCubes(this, workingBox1);
-                if (!collidingBoxes.isEmpty()) {
+            workingBox1.setMin(this.bb.minX, this.bb.minY - 1.0, this.bb.minZ);
+            workingBox1.setMax(this.bb.maxX, this.bb.minY, this.bb.maxZ);
+            MathHelper.aabbExpand(workingBox1, xd, 0.0, zd, workingBox1);
+            List<AABBdc> collidingBoxes = this.world.getCubes(this, workingBox1);
+            if (!collidingBoxes.isEmpty()) {
+                if (xd != 0.0) {
                     double limitX = 0.0;
                     for (int i = 0; i < collidingBoxes.size(); i++) {
                         AABBdc bb1 = collidingBoxes.get(i);
@@ -486,12 +486,7 @@ public abstract class Entity
                         oldXd = xd = Math.min(xd, limitX);
                     }
                 }
-            }
-            if (zd != 0.0) {
-                AABBd floor = new AABBd(this.bb.minX, this.bb.minY - 1.0, this.bb.minZ, this.bb.maxX, this.bb.minY, this.bb.maxZ);
-                MathHelper.aabbExpand(floor, 0.0, 0.0, zd, floor);
-                List<AABBdc> collidingBoxes = this.world.getCubes(this, floor);
-                if (!collidingBoxes.isEmpty()) {
+                if (zd != 0.0) {
                     double limitZ = 0.0;
                     for (int i = 0; i < collidingBoxes.size(); i++) {
                         AABBdc bb1 = collidingBoxes.get(i);
@@ -511,17 +506,6 @@ public abstract class Entity
         }
 
         List<AABBdc> cubes = this.world.getCubes(this, MathHelper.aabbExpand(this.bb, xd, yd, zd, workingBox2));
-        List<Entity> solidEntities = this.world.getEntitiesWithinAABBExcludingEntity(this, MathHelper.aabbExpand(this.bb, xd, yd, zd, workingBox2));
-
-        for (int i = 0; i < solidEntities.size(); i++) {
-            Entity solidEnt = solidEntities.get(i);
-
-            if (solidEnt.hasCollision() && solidEnt.getCollisionAABB() != null) {
-                if (this.collidesWith(solidEnt) && solidEnt.collidesWith(this)) {
-                    cubes.add(solidEnt.getCollisionAABB());
-                }
-            }
-        }
 
         // Check collision on Y Axis
         for (int i = 0; i < cubes.size(); i++) {
diff --git a/game/core/src/main/java/net/minecraft/core/entity/ICollidable.java b/game/core/src/main/java/net/minecraft/core/entity/ICollidable.java
new file mode 100644
index 000000000..ef892352b
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/entity/ICollidable.java
@@ -0,0 +1,8 @@
+package net.minecraft.core.entity;
+
+import org.jetbrains.annotations.Nullable;
+import org.joml.primitives.AABBdc;
+
+public interface ICollidable {
+    @Nullable AABBdc getCollisionAABB();
+}
diff --git a/game/core/src/main/java/net/minecraft/core/entity/vehicle/EntityBoat.java b/game/core/src/main/java/net/minecraft/core/entity/vehicle/EntityBoat.java
index b5694f797..1536ee0d6 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/vehicle/EntityBoat.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/vehicle/EntityBoat.java
@@ -7,6 +7,7 @@ import net.minecraft.core.block.entity.TileEntityFlag;
 import net.minecraft.core.block.material.Materials;
 import net.minecraft.core.block.tag.BlockTags;
 import net.minecraft.core.entity.Entity;
+import net.minecraft.core.entity.ICollidable;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.item.Items;
 import net.minecraft.core.item.ItemStack;
@@ -23,7 +24,7 @@ import org.joml.primitives.AABBdc;
 
 import java.util.List;
 
-public class EntityBoat extends Entity {
+public class EntityBoat extends Entity implements ICollidable {
     private static final double MAX_BOAT_SPEED = 0.8D;
     private static final double ACCELERATION = 0.0065D;
     private static final float MAX_ROTATION_SPEED = 5f;
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 5ed5c8f78..19fb9d14e 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
@@ -17,6 +17,7 @@ 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.util.phys.HitResult;
 import net.minecraft.core.world.LevelListener;
 import net.minecraft.core.world.World;
 import net.minecraft.core.world.pos.TilePos;
@@ -236,10 +237,11 @@ public abstract class ItemBucket extends ItemFood implements IItemContainer {
 
     @Override
     public boolean interactsWithFluid(@NotNull ItemStack selfStack) {
-        final NamespaceID currentStateId = getState(selfStack);
-        final BucketState currentState = getBucketState(currentStateId);
-        if (!currentState.canPlace() && currentState.isEdible()) return false;
-        return STATE_EMPTY.equals(currentStateId) || getCharges(selfStack) < this.maxCharges;
+        return false;
+        // final NamespaceID currentStateId = getState(selfStack);
+        // final BucketState currentState = getBucketState(currentStateId);
+        // if (!currentState.canPlace() && currentState.isEdible()) return false;
+        // return STATE_EMPTY.equals(currentStateId) || getCharges(selfStack) < this.maxCharges;
     }
 
     @Override
@@ -250,10 +252,59 @@ public abstract class ItemBucket extends ItemFood implements IItemContainer {
         return currentState.canPlace() || !currentState.isEdible();
     }
 
+    @Override
+    public @Nullable ItemStack onUse(@NotNull ItemStack selfStack, @NotNull World world, @NotNull Player player) {
+        final NamespaceID currentStateId = getState(selfStack);
+        final BucketState currentState = getBucketState(currentStateId);
+
+        if (currentState.isEdible() && !currentState.canPlace()) {
+            return handleAirClick(selfStack, world, player);
+        }
+
+        final var reachDistance = player.getGamemode().getBlockReachDistance();
+        final HitResult hitResult = player.rayCast(reachDistance, 1f, true, false, false);
+        if (!(hitResult instanceof HitResult.Tile hit)) {
+            assert !(hitResult instanceof HitResult.Entity);
+            return handleAirClick(selfStack, world, player);
+        }
+        final boolean mayBeSource = world.getBlockData(hit.tilePos) == 0;
+        if (mayBeSource) pick_src: {
+            final NamespaceID targetStateId = getFluidStateAt(world, hit.tilePos);
+            if (targetStateId == null) break pick_src;
+            
+            final boolean isEmptyScoop = STATE_EMPTY.equals(currentStateId);
+            if (!isEmptyScoop) {
+                final boolean isRefillScoop = 
+                    currentStateId.equals(targetStateId) &&
+                    getCharges(selfStack) < this.maxCharges;
+                if (!isRefillScoop) break pick_src;
+            }
+            final var res = tryPickupFluid(selfStack, world, player, hit.tilePos, targetStateId, new UseResult());
+            if (res.used) player.swingItem();
+            return res.itemStack;
+        }
+
+        if (currentState.canPlace()) {
+            final NamespaceID aimedFluid = getFluidStateAt(world, hit.tilePos);
+            if (currentStateId.equals(aimedFluid) && getCharges(selfStack) < this.maxCharges) {
+                return selfStack;
+            }
+
+            final HitResult hitResultNoFluid = player.rayCast(reachDistance, 1f, false, false, false);
+            if (hitResultNoFluid instanceof HitResult.Tile hit2) {
+                final var res = tryPlaceFluid(selfStack, world, player, hit2.tilePos, hit2.side, new UseResult());
+                if (res.used) player.swingItem();
+                return res.itemStack;
+            }
+        }
+
+        return handleAirClick(selfStack, world, player);
+    }
+
     @Override
     public boolean useOnEntity(@NotNull ItemStack selfStack, @NotNull Player player, @NotNull Mob mob) {
-        NamespaceID currentStateId = getState(selfStack);
-        BucketState currentState = getBucketState(currentStateId);
+        final NamespaceID currentStateId = getState(selfStack);
+        final BucketState currentState = getBucketState(currentStateId);
 
         if (currentState.isEdible() && !currentState.canPlace()) return false;
 
@@ -267,56 +318,56 @@ public abstract class ItemBucket extends ItemFood implements IItemContainer {
         return isDifferent;
     }
 
-    @Override
-    public boolean onUseOnBlock(@NotNull ItemStack selfStack, @NotNull World world, @Nullable Player player, @NotNull TilePosc tilePos, @NotNull Side side, double xHit, double yHit) {
-        final NamespaceID currentStateId = getState(selfStack);
-        final BucketState currentState = getBucketState(currentStateId);
+    // @Override
+    // public boolean onUseOnBlock(@NotNull ItemStack selfStack, @NotNull World world, @Nullable Player player, @NotNull TilePosc tilePos, @NotNull Side side, double xHit, double yHit) {
+    //     final NamespaceID currentStateId = getState(selfStack);
+    //     final BucketState currentState = getBucketState(currentStateId);
 
-        if (currentState.isEdible() && !currentState.canPlace()) return false;
+    //     if (currentState.isEdible() && !currentState.canPlace()) return false;
 
-        final @NotNull UseResult result;
+    //     final @NotNull UseResult result;
 
-        interface ctx { @NotNull UseResult result = new UseResult(); }
-        interact: synchronized(ctx.result) {
-            ctx.result.used = false;
-            ctx.result.itemStack = null;
+    //     interface ctx { @NotNull UseResult result = new UseResult(); }
+    //     interact: synchronized(ctx.result) {
+    //         ctx.result.used = false;
+    //         ctx.result.itemStack = null;
 
-            if (world.getBlockData(tilePos) == 0) {
-                final NamespaceID targetStateId = getFluidStateAt(world, tilePos);
+    //         if (world.getBlockData(tilePos) == 0) {
+    //             final NamespaceID targetStateId = getFluidStateAt(world, tilePos);
     
-                final boolean isEmptyScoop = STATE_EMPTY.equals(currentStateId);
-                final boolean isRefillScoop = !isEmptyScoop &&
-                    currentStateId.equals(targetStateId) && getCharges(selfStack) < this.maxCharges;
+    //             final boolean isEmptyScoop = STATE_EMPTY.equals(currentStateId);
+    //             final boolean isRefillScoop = !isEmptyScoop &&
+    //                 currentStateId.equals(targetStateId) && getCharges(selfStack) < this.maxCharges;
     
-                if (targetStateId != null && (isEmptyScoop || isRefillScoop)) {
-                    result = tryPickupFluid(selfStack, world, player, tilePos, targetStateId, ctx.result);
-                    break interact;
-                }
-            }
+    //             if (targetStateId != null && (isEmptyScoop || isRefillScoop)) {
+    //                 result = tryPickupFluid(selfStack, world, player, tilePos, targetStateId, ctx.result);
+    //                 break interact;
+    //             }
+    //         }
             
-            if (currentState.canPlace()) {
-                final NamespaceID aimedFluid = getFluidStateAt(world, tilePos);
-                if (currentStateId.equals(aimedFluid) && getCharges(selfStack) < this.maxCharges) {
-                    result = ctx.result;
-                    break interact;
-                }
-
-                result = tryPlaceFluid(selfStack, world, player, tilePos, side, ctx.result);
-                break interact;
-            }
-            return false;
-        }
-
-        if (result.used && result.itemStack != null) {
-            player.inventory.setItem(player.inventory.getCurrentSlot(), result.itemStack);
-        }
-        return result.used;
-    }
-
-    @Override
-    public ItemStack onUse(@NotNull ItemStack itemStack, @NotNull World world, @NotNull Player player) {
-        return handleAirClick(itemStack, world, player);
-    }
+    //         if (currentState.canPlace()) {
+    //             final NamespaceID aimedFluid = getFluidStateAt(world, tilePos);
+    //             if (currentStateId.equals(aimedFluid) && getCharges(selfStack) < this.maxCharges) {
+    //                 result = ctx.result;
+    //                 break interact;
+    //             }
+
+    //             result = tryPlaceFluid(selfStack, world, player, tilePos, side, ctx.result);
+    //             break interact;
+    //         }
+    //         return false;
+    //     }
+
+    //     if (result.used && result.itemStack != null) {
+    //         player.inventory.setItem(player.inventory.getCurrentSlot(), result.itemStack);
+    //     }
+    //     return result.used;
+    // }
+
+    // @Override
+    // public ItemStack onUse(@NotNull ItemStack itemStack, @NotNull World world, @NotNull Player player) {
+    //     return handleAirClick(itemStack, world, player);
+    // }
 
     protected static class UseResult {
         public boolean used = false;
@@ -352,6 +403,7 @@ public abstract class ItemBucket extends ItemFood implements IItemContainer {
         @NotNull ItemStack itemStack, @NotNull World world, @NotNull Player player, @NotNull TilePosc tilePos, @NotNull Side side,
         @NotNull UseResult result
     ) {
+        result.itemStack = itemStack;
         if (!world.canMineBlock(player, tilePos)) return result;
 
         final NamespaceID stateId = getState(itemStack);
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 0e2a2d058..e892660e7 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
@@ -1433,8 +1433,9 @@ public abstract class World implements MutableWorldSource {
         }
 
         final double radius = 0.25D;
-        final List<Entity> entities = this.getEntitiesWithinAABBExcludingEntity(entity, MathHelper.aabbGrow(aabb, radius, radius, radius, workingBox));
+        final List<Entity> entities = this.getCollidableEntitiesWithinAABB(MathHelper.aabbGrow(aabb, radius, radius, radius, workingBox));
         for (final Entity e : entities) {
+            if (e == entity) continue;
             final AABBdc entityBB = e.getCollisionAABB();
             if (entityBB != null && entityBB.intersectsAABB((AABBd) aabb)) {
                 this.collidingBoundingBoxes.add(entityBB);
@@ -1813,15 +1814,12 @@ public abstract class World implements MutableWorldSource {
             for (tilePos.y = minY; tilePos.y < maxY; tilePos.y++) {
                 for (tilePos.z = minZ; tilePos.z < maxZ; tilePos.z++) {
                     final @NotNull Block<?> block = this.getBlockType(tilePos);
-                    if (!this.isMaterialInBB(aabb, material)) {
-                        continue;
-                    }
+                    if (block.getMaterial() != material) continue;
+                    if (!aabb.intersectsAABB(block.getBoundsFromState(this, tilePos).translate(tilePos.x, tilePos.y, tilePos.z, this.workingBox))) continue;
                     flag = true;
                     block.onEntityInside(this, tilePos, entity, velDir);
                 }
-
             }
-
         }
 
         if (velDir.length() > 0.0D && addVelocity) {
@@ -2338,6 +2336,23 @@ public abstract class World implements MutableWorldSource {
         this.randomDisplayUpdates(new TilePos(x, y, z));
     }
 
+    public @NotNull List<@NotNull Entity> getCollidableEntitiesWithinAABB(@NotNull final AABBdc aabb) {
+        this.entityBuffer.clear();
+        final int minX = MathHelper.floor((aabb.minX() - 2D) / Chunk.CHUNK_SIZE_X);
+        final int maxX = MathHelper.floor((aabb.maxX() + 2D) / Chunk.CHUNK_SIZE_X);
+        final int minZ = MathHelper.floor((aabb.minZ() - 2D) / Chunk.CHUNK_SIZE_Z);
+        final int maxZ = MathHelper.floor((aabb.maxZ() + 2D) / Chunk.CHUNK_SIZE_Z);
+        for (int x = minX; x <= maxX; x++) {
+            for (int z = minZ; z <= maxZ; z++) {
+                if (this.isChunkLoaded(x, z)) {
+                    this.getChunkFromChunkCoords(x, z).getCollidableEntitiesWithin(aabb, this.entityBuffer);
+                }
+            }
+        }
+
+        return this.entityBuffer;
+    }
+
     public @NotNull List<@NotNull Entity> getEntitiesWithinAABBExcludingEntity(@Nullable final Entity entity, @NotNull final AABBdc aabb) {
         this.entityBuffer.clear();
         final int minX = MathHelper.floor((aabb.minX() - 2D) / Chunk.CHUNK_SIZE_X);
diff --git a/game/core/src/main/java/net/minecraft/core/world/chunk/Chunk.java b/game/core/src/main/java/net/minecraft/core/world/chunk/Chunk.java
index f8628f9ff..b3675369c 100644
--- a/game/core/src/main/java/net/minecraft/core/world/chunk/Chunk.java
+++ b/game/core/src/main/java/net/minecraft/core/world/chunk/Chunk.java
@@ -738,6 +738,10 @@ public class Chunk {
         this.isModified = true;
     }
 
+    public final @NotNull ChunkSection @NotNull[] getSectionsDirect() {
+        return this.sections;
+    }
+
     public void getEntitiesWithin(@Nullable Entity toExclude, @NotNull AABBdc aabb, @NotNull List<@NotNull Entity> entities) {
         int minSection = Math.max(0, MathHelper.floor((aabb.minY() - 2D) / 16D));
         int maxSection = Math.min(MathHelper.floor((aabb.maxY() + 2D) / 16D), CHUNK_SECTIONS - 1);
@@ -746,6 +750,14 @@ public class Chunk {
         }
     }
 
+    public void getCollidableEntitiesWithin(@NotNull AABBdc aabb, @NotNull List<@NotNull Entity> entities) {
+        int minSection = Math.max(0, MathHelper.floor((aabb.minY() - 2D) / 16D));
+        int maxSection = Math.min(MathHelper.floor((aabb.maxY() + 2D) / 16D), CHUNK_SECTIONS - 1);
+        for (int section = minSection; section <= maxSection; section++) {
+            getSection(section).getCollidableEntitiesWithin(aabb, entities);
+        }
+    }
+
     public <T extends Entity> void getEntitiesWithin(@NotNull Class<T> ofClass, @NotNull AABBdc aabb, @NotNull List<@NotNull T> entities) {
         int minSection = Math.max(0, MathHelper.floor((aabb.minY() - 2D) / 16D));
         int maxSection = Math.min(MathHelper.floor((aabb.maxY() + 2D) / 16D), CHUNK_SECTIONS - 1);
diff --git a/game/core/src/main/java/net/minecraft/core/world/chunk/ChunkSection.java b/game/core/src/main/java/net/minecraft/core/world/chunk/ChunkSection.java
index 698845cb7..d5b3e4ec5 100644
--- a/game/core/src/main/java/net/minecraft/core/world/chunk/ChunkSection.java
+++ b/game/core/src/main/java/net/minecraft/core/world/chunk/ChunkSection.java
@@ -3,6 +3,7 @@ package net.minecraft.core.world.chunk;
 import net.minecraft.core.block.Blocks;
 import net.minecraft.core.data.registry.Registries;
 import net.minecraft.core.entity.Entity;
+import net.minecraft.core.entity.ICollidable;
 import net.minecraft.core.enums.LightLayer;
 import net.minecraft.core.util.helper.LightIndexHelper;
 import net.minecraft.core.world.World;
@@ -33,6 +34,8 @@ public class ChunkSection {
     public byte @NotNull [] biome = new byte[Chunk.CHUNK_SIZE_X * Chunk.CHUNK_SIZE_Z * (SECTION_SIZE_Y >> 3)];
     public @NotNull List<@NotNull Entity> entities = new ArrayList<>();
 
+    public @NotNull List<@NotNull Entity> collidableEntities = new ArrayList<>();
+
     public ChunkSection(final @NotNull Chunk chunk, final int yPosition) {
         this.chunk = chunk;
         this.yPosition = yPosition;
@@ -222,10 +225,12 @@ public class ChunkSection {
 
     public void addEntity(final @NotNull Entity entity) {
         this.entities.add(entity);
+        if (entity instanceof ICollidable) this.collidableEntities.add(entity);
     }
 
     public void removeEntity(final @NotNull Entity entity) {
         this.entities.remove(entity);
+        this.collidableEntities.remove(entity);
     }
 
     public void getEntitiesWithin(final @Nullable Entity toExclude, final @NotNull AABBdc aabb, final @NotNull List<@NotNull Entity> entities) {
@@ -236,6 +241,14 @@ public class ChunkSection {
         }
     }
 
+    public void getCollidableEntitiesWithin(final @NotNull AABBdc aabb, final @NotNull List<@NotNull Entity> entities) {
+        for (final @NotNull Entity e : this.collidableEntities) {
+            if (e.bb.intersectsAABB((AABBd) aabb)) {
+                entities.add(e);
+            }
+        }
+    }
+
     public <T extends Entity> void getEntitiesWithin(@NotNull Class<T> ofClass, @NotNull AABBdc aabb, @NotNull List<@NotNull T> entities) {
         for (Entity e : this.entities) {
             if (ofClass.isAssignableFrom(e.getClass()) && e.bb.intersectsAABB((AABBd) aabb)) {
diff --git a/util/datagen/src/main/java/net/minecraft/datagen/SmeltingGenerator.java b/util/datagen/src/main/java/net/minecraft/datagen/SmeltingGenerator.java
index ce7c50a54..bf4db129c 100644
--- a/util/datagen/src/main/java/net/minecraft/datagen/SmeltingGenerator.java
+++ b/util/datagen/src/main/java/net/minecraft/datagen/SmeltingGenerator.java
@@ -57,30 +57,24 @@ public class SmeltingGenerator {
 
         blastRecipe("log_to_scorched_log", "minecraft:logs", Blocks.BLOCK_ASH.getDefaultStack(), Blocks.LOG_SCORCHED.getDefaultStack());
 
-        addOreConversions(BlockLogicOreCoal.variantMap);
-        addOreConversions(BlockLogicOreIron.variantMap);
-        addOreConversions(BlockLogicOreGold.variantMap);
-        addOreConversions(BlockLogicOreLapis.variantMap);
-        addOreConversions(BlockLogicOreRedstone.variantMap);
-        addOreConversions(BlockLogicOreDiamond.variantMap);
-        addOreConversions(BlockLogicOreNetherCoal.variantMap);
+        addOreConversions(BlockLogicOreCoal.variantMap, "minecraft:coal_ores");
+        addOreConversions(BlockLogicOreIron.variantMap, "minecraft:iron_ores");
+        addOreConversions(BlockLogicOreGold.variantMap, "minecraft:gold_ores");
+        addOreConversions(BlockLogicOreLapis.variantMap, "minecraft:lapis_ores");
+        addOreConversions(BlockLogicOreRedstone.variantMap, "minecraft:redstone_ores");
+        addOreConversions(BlockLogicOreDiamond.variantMap, "minecraft:diamond_ores");
+        addOreConversions(BlockLogicOreNetherCoal.variantMap, "minecraft:nethercoal_ores");
     }
 
-    private static void addOreConversions(Int2IntArrayMap variantMap) {
-        for (int fromParentId : variantMap.keySet()) {
-            for (int toParentId : variantMap.keySet()) {
-                if (fromParentId == toParentId) continue;
+    private static void addOreConversions(Int2IntArrayMap variantMap, String oreGroup) {
+        for (int toParentId : variantMap.keySet()) {
+            final Block<?> toParent = Blocks.getBlock(toParentId);
+            final Block<?> toChild = Blocks.getBlock(variantMap.get(toParentId));
 
-                final Block<?> toParent = Blocks.getBlock(toParentId);
-
-                final Block<?> fromChild = Blocks.getBlock(variantMap.get(fromParentId));
-                final Block<?> toChild = Blocks.getBlock(variantMap.get(toParentId));
-
-                blastRecipe(
-                    String.format("%s_to_%s", fromChild.getKey(), toChild.getKey()),
-                    toParent.getDefaultStack(), fromChild.getDefaultStack(), toChild.getDefaultStack()
-                );
-            }
+            blastRecipe(
+                String.format("%s_to_%s", oreGroup, toChild.getKey()),
+                toParent.getDefaultStack(), oreGroup, toChild.getDefaultStack()
+            );
         }
     }
 
@@ -104,6 +98,10 @@ public class SmeltingGenerator {
         RecipeBuilder.BlastFurnace(RecipesGenerator.CORE_NAMESPACE).setInput(inputA, inputB).create(id, output);
     }
 
+    private static void blastRecipe(String id, ItemStack inputA, String inputB, ItemStack output) {
+        RecipeBuilder.BlastFurnace(RecipesGenerator.CORE_NAMESPACE).setInput(new RecipeSymbol(inputA), new RecipeSymbol(inputB)).create(id, output);
+    }
+
     private static void blastRecipe(String id, String inputGroup, ItemStack inputStack, ItemStack output) {
         RecipeBuilder.BlastFurnace(RecipesGenerator.CORE_NAMESPACE).setInput(new RecipeSymbol(inputGroup), new RecipeSymbol(inputStack)).create(id, output);
     }
