diff --git a/game/client/src/main/java/net/minecraft/client/gui/ScreenSignEditor.java b/game/client/src/main/java/net/minecraft/client/gui/ScreenSignEditor.java
index 4017742cd..c9d89fa4e 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/ScreenSignEditor.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/ScreenSignEditor.java
@@ -100,7 +100,7 @@ public class ScreenSignEditor extends Screen {
             Objects.requireNonNull(this.mc.getSendQueue())
                 .addToSendQueue(
                     new PacketSignUpdate(
-                        this.entitySign.x, this.entitySign.y, this.entitySign.z,
+                        this.entitySign.tilePos.x, this.entitySign.tilePos.y, this.entitySign.tilePos.z,
                         this.entitySign.signText,
                         this.entitySign.getPicture().getId(),
                         this.entitySign.getColor().id
diff --git a/game/client/src/main/java/net/minecraft/client/gui/achievements/data/AchievementPages.java b/game/client/src/main/java/net/minecraft/client/gui/achievements/data/AchievementPages.java
index c7b8a463f..bcbcff900 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/achievements/data/AchievementPages.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/achievements/data/AchievementPages.java
@@ -25,6 +25,7 @@ public class AchievementPages {
         overworldPage.addAchievement(Achievements.BUILD_PICKAXE, 4, 2);
         overworldPage.addAchievement(Achievements.BUILD_FURNACE, 3, 4);
         overworldPage.addAchievement(Achievements.ACQUIRE_IRON, 1, 4);
+        overworldPage.addAchievement(Achievements.DOWN_THE_DRAIN, 0, 6);
         overworldPage.addAchievement(Achievements.GET_DIAMONDS, -1, 4);
         overworldPage.addAchievement(Achievements.BUILD_HOE, 1, -3);
         overworldPage.addAchievement(Achievements.MAKE_BREAD, -2, -3);
diff --git a/game/client/src/main/java/net/minecraft/client/gui/modelviewer/categories/entries/block/BlockEntryMobSpawner.java b/game/client/src/main/java/net/minecraft/client/gui/modelviewer/categories/entries/block/BlockEntryMobSpawner.java
index 5ddb23ef6..3c8c3051c 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/modelviewer/categories/entries/block/BlockEntryMobSpawner.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/modelviewer/categories/entries/block/BlockEntryMobSpawner.java
@@ -24,9 +24,9 @@ public class BlockEntryMobSpawner extends BlockEntry{
     public void onTick(World world, int meta) {
         TileEntityMobSpawner spawner = (TileEntityMobSpawner) DisplayChunk.displayTileEntity;
         spawner.yaw2 = spawner.yaw;
-        double xPos = (double) spawner.x + spawner.worldObj.rand.nextFloat();
-        double yPos = (double) spawner.y + spawner.worldObj.rand.nextFloat();
-        double zPos = (double) spawner.z + spawner.worldObj.rand.nextFloat();
+        double xPos = (double) spawner.tilePos.x + spawner.worldObj.rand.nextFloat();
+        double yPos = (double) spawner.tilePos.y + spawner.worldObj.rand.nextFloat();
+        double zPos = (double) spawner.tilePos.z + spawner.worldObj.rand.nextFloat();
         spawner.worldObj.spawnParticle("smoke", xPos, yPos, zPos, 0.0D, 0.0D, 0.0D, 0);
         spawner.worldObj.spawnParticle("flame", xPos, yPos, zPos, 0.0D, 0.0D, 0.0D, 0);
         for(spawner.yaw += 1000F / ((float)spawner.delay + 200F); spawner.yaw > 360D;)
diff --git a/game/client/src/main/java/net/minecraft/client/net/handler/PacketHandlerClient.java b/game/client/src/main/java/net/minecraft/client/net/handler/PacketHandlerClient.java
index 42ac2295b..b400c9f3c 100644
--- a/game/client/src/main/java/net/minecraft/client/net/handler/PacketHandlerClient.java
+++ b/game/client/src/main/java/net/minecraft/client/net/handler/PacketHandlerClient.java
@@ -1047,7 +1047,7 @@ public class PacketHandlerClient extends PacketHandler {
         if (existingTileEntity == null || existingTileEntity.getClass() != clazz) {
             TileEntity tileEntity = TileEntityDispatcher.createAndLoadEntity(packetTileEntityData.tag);
             if (tileEntity != null) {
-                this.mc.currentWorld.setTileEntity(tileEntity.x, tileEntity.y, tileEntity.z, tileEntity);
+                this.mc.currentWorld.setTileEntity(tileEntity.tilePos.x, tileEntity.tilePos.y, tileEntity.tilePos.z, tileEntity);
             }
         } else {
             existingTileEntity.readFromNBT(packetTileEntityData.tag);
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 b31fd88ca..84a74fbc6 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
@@ -107,13 +107,13 @@ public final class TileEntityRenderDispatcher {
             return;
         if (!renderer.isVisible(tileEntity, camera, partialTick)) return;
 
-        GLRenderer.setLightmapCoord1i(world.getLightmapCoord(tileEntity.x, tileEntity.y, tileEntity.z, 0));
+        GLRenderer.setLightmapCoord1i(world.getLightmapCoord(tileEntity.tilePos.x, tileEntity.tilePos.y, tileEntity.tilePos.z, 0));
         GLRenderer.setColor3f(1f, 1f, 1f);
         renderer.doRender(tessellator,
             tileEntity,
-            (double) tileEntity.x - renderPosX,
-            (double) tileEntity.y - renderPosY,
-            (double) tileEntity.z - renderPosZ, partialTick);
+            (double) tileEntity.tilePos.x - renderPosX,
+            (double) tileEntity.tilePos.y - renderPosY,
+            (double) tileEntity.tilePos.z - renderPosZ, partialTick);
     }
 
     public <T extends TileEntity> void renderTileEntity(final @NotNull TessellatorGeneral tessellator, final @NotNull T tileEntity, final double x, final double y, final double z, final float partialTick) {
diff --git a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererFlag.java b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererFlag.java
index 689925dbf..2e72c4d8b 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererFlag.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererFlag.java
@@ -28,7 +28,7 @@ public class TileEntityRendererFlag extends TileEntityRenderer<TileEntityFlag> {
     @Override
     public void doRender(final @NotNull TessellatorGeneral tessellator, @NotNull final TileEntityFlag tileEntity, final double x, final double y, final double z, final float partialTick) {
         final World theWorld = Minecraft.getMinecraft().currentWorld;
-        final int blockUnder = theWorld.getBlockId(tileEntity.x, tileEntity.y - 1, tileEntity.z);
+        final int blockUnder = theWorld.getBlockId(tileEntity.tilePos.x, tileEntity.tilePos.y - 1, tileEntity.tilePos.z);
         final boolean shortPole = blockUnder == Blocks.FLAG.id();
 
         final WindProvider theWind = this.mc.currentWorld.getWorldType().getWindManager();
diff --git a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererFlowerJar.java b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererFlowerJar.java
index 50f8675f0..ca69da086 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererFlowerJar.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererFlowerJar.java
@@ -26,7 +26,7 @@ public class TileEntityRendererFlowerJar extends TileEntityRenderer<TileEntityFl
         GLRenderer.enableState(State.BLEND);
         GLRenderer.setBlendFunc(BlendFactor.SRC_ALPHA, BlendFactor.ONE_MINUS_SRC_ALPHA);
         Lighting.disable();
-        BlockModelDispatcher.getInstance().getDispatch(block).renderStandalone(tessellator, tileEntity.flowerData, tileEntity.worldObj.getLightmapCoord(tileEntity.x, tileEntity.y, tileEntity.z, 0));
+        BlockModelDispatcher.getInstance().getDispatch(block).renderStandalone(tessellator, tileEntity.flowerData, tileEntity.worldObj.getLightmapCoord(tileEntity.tilePos.x, tileEntity.tilePos.y, tileEntity.tilePos.z, 0));
         Lighting.enableLight();
         GLRenderer.disableState(State.BLEND);
         GLRenderer.popFrame();
diff --git a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMeshGold.java b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMeshGold.java
index 58c4fea60..05852117a 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMeshGold.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMeshGold.java
@@ -1,6 +1,8 @@
 package net.minecraft.client.render.tileentity;
 
 import net.minecraft.client.option.GameSettings;
+import net.minecraft.client.render.RenderGlobal;
+import net.minecraft.client.render.renderer.DrawMode;
 import net.minecraft.client.render.renderer.GLRenderer;
 import net.minecraft.core.block.entity.TileEntityMeshGold;
 
@@ -9,23 +11,23 @@ import net.minecraft.client.render.item.model.ItemModelDispatcher;
 import net.minecraft.client.render.tessellator.TessellatorGeneral;
 import net.minecraft.core.util.helper.MathHelper;
 import net.minecraft.core.world.pos.TilePos;
+import org.joml.primitives.AABBd;
 
 public class TileEntityRendererMeshGold extends TileEntityRenderer<TileEntityMeshGold> {
 
     @Override
-    public void doRender(TessellatorGeneral tessellator, TileEntityMeshGold tileEntity, double x, double y, double z, float renderPartialTicks) {
+    public void doRender(TessellatorGeneral tessellator, TileEntityMeshGold tileEntity, double x, double y, double z, float partialTicks) {
         if (tileEntity.filterItem == null) return;
         GLRenderer.pushFrame();
 
-        final float bobbingOffset = MathHelper.sin((tileEntity.ticksRan + renderPartialTicks) / 10F) * 0.1F + 0.1F;
-        final float yaw = (float) Math.toDegrees(((tileEntity.ticksRan + renderPartialTicks) / 20F));
+        final float bobbingOffset = MathHelper.sin((tileEntity.ticksRan + partialTicks) / 10F) * 0.1F + 0.1F;
+        final float yaw = (float) Math.toDegrees(((tileEntity.ticksRan + partialTicks) / 20F));
 
         GLRenderer.modelM4f().translate((float) x + 0.5f, (float) y + bobbingOffset + 0.25f, (float) z + 0.5f);
 
-
         ItemModelDispatcher.getInstance().getDispatch(tileEntity.filterItem).renderItemEntity(tessellator, tileEntity.filterItem,
             GameSettings.ITEMS_3D.value, 1, tileEntity.ticksRan, yaw,
-            Minecraft.getMinecraft().currentWorld.getLightIndex(new TilePos(tileEntity.x, tileEntity.y, tileEntity.z), 0), renderPartialTicks);
+            Minecraft.getMinecraft().currentWorld.getLightIndex(new TilePos(tileEntity.tilePos.x, tileEntity.tilePos.y, tileEntity.tilePos.z), 0), partialTicks);
         GLRenderer.popFrame();
     }
 }
diff --git a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMobSpawner.java b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMobSpawner.java
index a76932b2f..b67e16baf 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMobSpawner.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMobSpawner.java
@@ -46,7 +46,7 @@ public class TileEntityRendererMobSpawner extends TileEntityRenderer<TileEntityM
             GLRenderer.modelM4f().translate(0.0F, -0.4F, 0.0F);
             GLRenderer.modelM4f().scale(f1, f1, f1);
 //            System.out.printf("X: %f, Y: %f, Z: %f\n", x, y, z);
-            entity.moveTo(mobSpawner.x, mobSpawner.y, mobSpawner.z, 0.0F, 0.0F);
+            entity.moveTo(mobSpawner.tilePos.x, mobSpawner.tilePos.y, mobSpawner.tilePos.z, 0.0F, 0.0F);
             EntityRenderer.renderShadows = false;
             EntityRendererDispatcher.instance.renderEntityWithPosYaw(tessellator, entity, 0.0D, 0.0D, 0.0D, 0.0F, partialTick);
             EntityRenderer.renderShadows = true;
diff --git a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMovingPiston.java b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMovingPiston.java
index aac3c8ada..496689bb9 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMovingPiston.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMovingPiston.java
@@ -38,14 +38,14 @@ public class TileEntityRendererMovingPiston extends TileEntityRenderer<TileEntit
             if (this.container == null || this.container.world != tileEntity.worldObj) {
                 this.container = new BlocksContainer(tileEntity.worldObj);
             }
-            TilePos tileEntPos = new TilePos(tileEntity.x, tileEntity.y, tileEntity.z);
+            TilePos tileEntPos = new TilePos(tileEntity.tilePos.x, tileEntity.tilePos.y, tileEntity.tilePos.z);
             TextureRegistry.worldAtlas.bind();
             GLRenderer.setShader(Shaders.ITEM);
             GLRenderer.setBlendFunc(BlendFactor.SRC_ALPHA, BlendFactor.ONE_MINUS_SRC_ALPHA);
             GLRenderer.enableState(State.BLEND);
             GLRenderer.disableState(State.CULL_FACE);
             tessellator.startDrawingQuads();
-            tessellator.setTranslation(((float) x - (float) tileEntity.x) + tileEntity.getXOff(partialTick), ((float) y - (float) tileEntity.y) + tileEntity.getYOff(partialTick), ((float) z - (float) tileEntity.z) + tileEntity.getZOff(partialTick));
+            tessellator.setTranslation(((float) x - (float) tileEntity.tilePos.x) + tileEntity.getXOff(partialTick), ((float) y - (float) tileEntity.tilePos.y) + tileEntity.getYOff(partialTick), ((float) z - (float) tileEntity.tilePos.z) + tileEntity.getZOff(partialTick));
             if (Block.hasLogicClass(block, BlockLogicPistonHead.class) && tileEntity.getProgress(partialTick) < 0.5F) {
                 ((BlockModelGenericPistonHead<?>) BlockModelDispatcher.getInstance().getDispatch(block)).renderPistonHeadNoCulling(tessellator, tileEntity.worldObj, tileEntity.getBlockMeta(), tileEntPos, false);
             } else if (tileEntity.isSourcePiston() && !tileEntity.isExtending()) {
@@ -55,10 +55,10 @@ public class TileEntityRendererMovingPiston extends TileEntityRenderer<TileEntit
                 } else {
                     ((BlockModelGenericPistonHead<?>) BlockModelDispatcher.getInstance().getDispatch(Blocks.PISTON_HEAD)).renderPistonHeadNoCulling(tessellator, tileEntity.worldObj, tileEntity.getBlockMeta(), tileEntPos, tileEntity.getProgress(partialTick) < 0.5F);
                 }
-                tessellator.setTranslation((float) x - (float) tileEntity.x, (float) y - (float) tileEntity.y, (float) z - (float) tileEntity.z);
+                tessellator.setTranslation((float) x - (float) tileEntity.tilePos.x, (float) y - (float) tileEntity.tilePos.y, (float) z - (float) tileEntity.tilePos.z);
                 pistonBase.renderHeadless(tessellator, tileEntity.worldObj, tileEntPos);
             } else {
-                this.container.setBlock(tileEntity.x, tileEntity.y, tileEntity.z, block.id(), tileEntity.getMovedData(), tileEntity.getMovedEntity());
+                this.container.setBlock(tileEntity.tilePos.x, tileEntity.tilePos.y, tileEntity.tilePos.z, block.id(), tileEntity.getMovedData(), tileEntity.getMovedEntity());
                 BlockModelDispatcher.getInstance().getDispatch(block).renderNoCulling(tessellator, this.container, tileEntPos);
                 this.container.setLightReferenceEntity(null);
                 this.container.clear();
diff --git a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java
index 4d5093546..2380339f6 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java
@@ -194,7 +194,7 @@ public class TileEntityRendererSign extends TileEntityRenderer<TileEntitySign> {
 
     @Override
     public boolean isVisible(@NotNull final TileEntitySign tileEntity, final @NotNull ICamera camera, final float partialTick) {
-        return camera.getFrustum().isVisible(new AABBd(tileEntity.x, tileEntity.y, tileEntity.z, tileEntity.x + 1, tileEntity.y + 1, tileEntity.z + 1), partialTick);
+        return camera.getFrustum().isVisible(new AABBd(tileEntity.tilePos.x, tileEntity.tilePos.y, tileEntity.tilePos.z, tileEntity.tilePos.x + 1, tileEntity.tilePos.y + 1, tileEntity.tilePos.z + 1), partialTick);
     }
 
     public static class BufferedTextMeshRenderer {
diff --git a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererStatue.java b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererStatue.java
index acddbcc65..3fb385560 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererStatue.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererStatue.java
@@ -75,7 +75,7 @@ public class TileEntityRendererStatue extends TileEntityRenderer<TileEntityStatu
                 renderArmor(tessellator, armorWearer, i, pose);
             }
         }
-        renderHeldItem(heldStack, Minecraft.getMinecraft().currentWorld.getLightIndex(new TilePos(statue.x, statue.y, statue.z), 0), isPigman);
+        renderHeldItem(heldStack, Minecraft.getMinecraft().currentWorld.getLightIndex(new TilePos(statue.tilePos.x, statue.tilePos.y, statue.tilePos.z), 0), isPigman);
         GLRenderer.popFrame();
 
         GLRenderer.enableState(State.CULL_FACE);
@@ -108,7 +108,7 @@ public class TileEntityRendererStatue extends TileEntityRenderer<TileEntityStatu
     @Override
     public void doRender(@NotNull final TessellatorGeneral tessellator, @NotNull final TileEntityStatue tileEntity, final double x, final double y, final double z, final float partialTick) {
         renderAt(tessellator, tileEntity, tileEntity.getHeldItem(), tileEntity.getBlockMeta(), tileEntity.getBlock(), tileEntity.getPose(), tileEntity, x, y, z);
-        renderShadow(Minecraft.getMinecraft().currentWorld, tessellator, tileEntity.x, tileEntity.y, tileEntity.z, x, y, z, 2.0f, partialTick);
+        renderShadow(Minecraft.getMinecraft().currentWorld, tessellator, tileEntity.tilePos.x, tileEntity.tilePos.y, tileEntity.tilePos.z, x, y, z, 2.0f, partialTick);
     }
 
     private boolean renderArmor(final @NotNull TessellatorGeneral tessellator, final @NotNull IArmorWearing<? super HumanArmorShape> armorWearer, final int layer, final TileEntityStatue.@NotNull Pose pose) {
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 f0209e5ad..e72e72249 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
@@ -28,6 +28,9 @@ public abstract class Achievements
     public static Achievement ACQUIRE_IRON =
         new Achievement(NamespaceID.fromPool("minecraft", "acquire_iron"), "acquireIron", Items.INGOT_IRON, BUILD_FURNACE)
             .registerAchievement();
+    public static Achievement DOWN_THE_DRAIN =
+            new Achievement(NamespaceID.fromPool("minecraft", "down_the_drain"), "downTheDrain", Blocks.MESH, ACQUIRE_IRON)
+                    .registerAchievement();
     public static Achievement GET_DIAMONDS =
         new Achievement(NamespaceID.fromPool("minecraft", "get_diamonds"), "getDiamonds", Items.DIAMOND, ACQUIRE_IRON)
             .setType(Achievement.TYPE_SPECIAL)
diff --git a/game/core/src/main/java/net/minecraft/core/achievement/stat/StatList.java b/game/core/src/main/java/net/minecraft/core/achievement/stat/StatList.java
index 37b867c95..d0d43ad62 100644
--- a/game/core/src/main/java/net/minecraft/core/achievement/stat/StatList.java
+++ b/game/core/src/main/java/net/minecraft/core/achievement/stat/StatList.java
@@ -308,6 +308,8 @@ public abstract class StatList
                         return Achievements.OPEN_GUIDEBOOK;
                     case 23:
                         return Achievements.CAUGHT_EM_ALL;
+                    case 24:
+                        return Achievements.DOWN_THE_DRAIN;
                 }
             }
 
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 3406d37cc..1700187a6 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
@@ -141,6 +141,11 @@ public abstract class BlockLogicFluid extends BlockLogic {
         this.fluid.animationTick(this, world, tilePos, rand);
     }
 
+    //used to detect if the water is a stream (including "flowing" source blocks), or still
+    public boolean hasFlowVector(final @NotNull WorldSource source, final @NotNull TilePosc tilePos){
+        return !Double.isNaN(getSlopeAngle(source, tilePos));
+    }
+
     public double getSlopeAngle(final @NotNull WorldSource source, final @NotNull TilePosc tilePos) {
         final Vector3dc vec = getFlow(source, tilePos);
         if (vec.x() == 0.0D && vec.z() == 0.0D) {
@@ -241,4 +246,8 @@ public abstract class BlockLogicFluid extends BlockLogic {
 
         return false;
     }
+
+    public boolean isFullBlock(@NotNull World world, TilePos tilePos){
+        return getFluidHeight(world, tilePos) == 1;
+    }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicMesh.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicMesh.java
index fe5ad1884..99ea73638 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicMesh.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicMesh.java
@@ -1,5 +1,6 @@
 package net.minecraft.core.block;
 
+import net.minecraft.core.block.entity.TileEntityMesh;
 import net.minecraft.core.block.material.Materials;
 import net.minecraft.core.entity.Entity;
 import net.minecraft.core.entity.EntityItem;
@@ -13,6 +14,7 @@ public class BlockLogicMesh extends BlockLogicTransparent
     public BlockLogicMesh(@NotNull Block<?> block)
     {
         super(block, Materials.METAL);
+        block.withEntity(TileEntityMesh::new);
     }
     
     @Override
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntity.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntity.java
index c2ec0bd7b..698bf55f3 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntity.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntity.java
@@ -14,6 +14,7 @@ import net.minecraft.core.util.helper.MathHelper;
 import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.world.ICarriable;
 import net.minecraft.core.world.World;
+import net.minecraft.core.world.pos.TilePos;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -21,9 +22,7 @@ public abstract class TileEntity implements ICarriable
 {
     public @Nullable World worldObj; // Null when loading data from nbt and null when held by entity
     public @Nullable CarriedBlock carriedBlock;
-    public int x; // TODO replace with tilePos
-    public int y;
-    public int z;
+    public TilePos tilePos = new TilePos();
     protected boolean tileEntityInvalid;
 
     public TileEntity()
@@ -42,10 +41,10 @@ public abstract class TileEntity implements ICarriable
             int id = getBlockId();
             if (id != 0 && Blocks.getBlock(id).isSignalSource()) {
                 for (Side s : Side.sides){
-                    worldObj.notifyBlocksOfNeighborChange(x + s.getOffsetX(), y + s.getOffsetY(), z + s.getOffsetZ(), getBlockId());
+                    worldObj.notifyBlocksOfNeighborChange(tilePos.x + s.getOffsetX(), tilePos.y + s.getOffsetY(), tilePos.z + s.getOffsetZ(), getBlockId());
                 }
             }
-            worldObj.updateTileEntityChunkAndSendToPlayer(x, y, z, this);
+            worldObj.updateTileEntityChunkAndSendToPlayer(tilePos.x, tilePos.y, tilePos.z, this);
         }
     }
     
@@ -58,7 +57,7 @@ public abstract class TileEntity implements ICarriable
         if (carriedBlock != null) {
             return carriedBlock.blockId;
         } else {
-            return worldObj.getBlockId(x, y, z);
+            return worldObj.getBlockId(tilePos.x, tilePos.y, tilePos.z);
         }
     }
 
@@ -66,7 +65,7 @@ public abstract class TileEntity implements ICarriable
         if (carriedBlock != null) {
             return carriedBlock.block();
         } else {
-            return worldObj.getBlock(x, y, z);
+            return worldObj.getBlock(tilePos.x, tilePos.y, tilePos.z);
         }
     }
 
@@ -74,16 +73,16 @@ public abstract class TileEntity implements ICarriable
         if (carriedBlock != null) {
             return carriedBlock.metadata;
         } else {
-            return worldObj.getBlockMetadata(x, y, z);
+            return worldObj.getBlockMetadata(tilePos.x, tilePos.y, tilePos.z);
         }
     }
 
 
     public double getDistanceFrom(double x, double y, double z)
     {
-        double dx = ((double) this.x + 0.5D) - x;
-        double dy = ((double) this.y + 0.5D) - y;
-        double dz = ((double) this.z + 0.5D) - z;
+        double dx = ((double) this.tilePos.x + 0.5D) - x;
+        double dy = ((double) this.tilePos.y + 0.5D) - y;
+        double dz = ((double) this.tilePos.z + 0.5D) - z;
         return dx * dx + dy * dy + dz * dz;
     }
 
@@ -104,9 +103,9 @@ public abstract class TileEntity implements ICarriable
 
     public final void readFromNBT(@NotNull CompoundTag compoundTag)
     {
-        x = compoundTag.getInteger("x");
-        y = compoundTag.getInteger("y");
-        z = compoundTag.getInteger("z");
+        tilePos.x = compoundTag.getInteger("x");
+        tilePos.y = compoundTag.getInteger("y");
+        tilePos.z = compoundTag.getInteger("z");
         readAdditionalData(compoundTag);
     }
 
@@ -121,9 +120,9 @@ public abstract class TileEntity implements ICarriable
         } else
         {
             compoundTag.putString("id", name.toString());
-            compoundTag.putInt("x", x);
-            compoundTag.putInt("y", y);
-            compoundTag.putInt("z", z);
+            compoundTag.putInt("x", tilePos.x);
+            compoundTag.putInt("y", tilePos.y);
+            compoundTag.putInt("z", tilePos.z);
             writeAdditionalData(compoundTag);
         }
     }
@@ -142,26 +141,26 @@ public abstract class TileEntity implements ICarriable
     @Override
     public boolean tryPlace(World world, Entity holder, int blockX, int blockY, int blockZ, Side side, double xPlaced, double yPlaced) {
         CarriedBlock carriedBlock = this.carriedBlock;
-        x = blockX + side.getOffsetX();
-        y = blockY + side.getOffsetY();
-        z = blockZ + side.getOffsetZ();
+        tilePos.x = blockX + side.getOffsetX();
+        tilePos.y = blockY + side.getOffsetY();
+        tilePos.z = blockZ + side.getOffsetZ();
 
-        Block<?> currentBlock = world.getBlock(x, y, z);
+        Block<?> currentBlock = world.getBlock(tilePos.x, tilePos.y, tilePos.z);
         if (currentBlock != null && !currentBlock.hasTag(BlockTags.PLACE_OVERWRITES)) return false;
-        world.setBlockAndMetadata(x, y, z, carriedBlock.blockId, carriedBlock.metadata);
+        world.setBlockAndMetadata(tilePos.x, tilePos.y, tilePos.z, carriedBlock.blockId, carriedBlock.metadata);
 
         worldObj = world;
         this.validate();
-        world.removeBlockTileEntity(x, y, z);
-        world.setTileEntity(x, y, z, this);
-        Block<?> b = world.getBlock(x, y, z);
+        world.removeBlockTileEntity(tilePos.x, tilePos.y, tilePos.z);
+        world.setTileEntity(tilePos.x, tilePos.y, tilePos.z, this);
+        Block<?> b = world.getBlock(tilePos.x, tilePos.y, tilePos.z);
         if (b != null && holder instanceof Mob) {
-            b.onBlockPlacedByMob(world, x, y, z, side, (Mob) holder, xPlaced, yPlaced);
+            b.onBlockPlacedByMob(world, tilePos.x, tilePos.y, tilePos.z, side, (Mob) holder, xPlaced, yPlaced);
         }
-        world.notifyBlockChange(x, y, z, carriedBlock.blockId);
+        world.notifyBlockChange(tilePos.x, tilePos.y, tilePos.z, carriedBlock.blockId);
         if (carriedBlock.blockId != 0 && Blocks.getBlock(carriedBlock.blockId).isSignalSource()) {
             for (Side s : Side.sides){
-                world.notifyBlocksOfNeighborChange(x + s.getOffsetX(), y + s.getOffsetY(), z + s.getOffsetZ(), getBlockId());
+                world.notifyBlocksOfNeighborChange(tilePos.x + s.getOffsetX(), tilePos.y + s.getOffsetY(), tilePos.z + s.getOffsetZ(), getBlockId());
             }
         }
         return true;
@@ -194,15 +193,15 @@ public abstract class TileEntity implements ICarriable
 
     @Override
     public ICarriable pickup(World world, Entity holder) {
-        Block<?> currentBlock = world.getBlock(x, y, z);
-        int currentMeta = world.getBlockMetadata(x, y, z);
-        world.removeBlockTileEntity(x, y, z);
-        world.setBlockRaw(x, y, z, 0);
-        world.notifyBlockChange(x, y, z, 0);
+        Block<?> currentBlock = world.getBlock(tilePos.x, tilePos.y, tilePos.z);
+        int currentMeta = world.getBlockMetadata(tilePos.x, tilePos.y, tilePos.z);
+        world.removeBlockTileEntity(tilePos.x, tilePos.y, tilePos.z);
+        world.setBlockRaw(tilePos.x, tilePos.y, tilePos.z, 0);
+        world.notifyBlockChange(tilePos.x, tilePos.y, tilePos.z, 0);
         int id = getBlockId();
         if (currentBlock.isSignalSource()) {
             for (Side s : Side.sides){
-                worldObj.notifyBlocksOfNeighborChange(x + s.getOffsetX(), y + s.getOffsetY(), z + s.getOffsetZ(), getBlockId());
+                worldObj.notifyBlocksOfNeighborChange(tilePos.x + s.getOffsetX(), tilePos.y + s.getOffsetY(), tilePos.z + s.getOffsetZ(), getBlockId());
             }
         }
         this.worldObj = null;
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityActivator.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityActivator.java
index a370135b2..de011dcfe 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityActivator.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityActivator.java
@@ -184,11 +184,11 @@ public class TileEntityActivator extends TileEntity implements Container {
     @Override
     public boolean stillValid(@NotNull Player player)
     {
-        if(worldObj == null || worldObj.getTileEntity(x, y, z) != this)
+        if(worldObj == null || worldObj.getTileEntity(tilePos.x, tilePos.y, tilePos.z) != this)
         {
             return false;
         }
-        return player.distanceToSqr((double) x + 0.5D, (double) y + 0.5D, (double) z + 0.5D) <= 64D;
+        return player.distanceToSqr((double) tilePos.x + 0.5D, (double) tilePos.y + 0.5D, (double) tilePos.z + 0.5D) <= 64D;
     }
 
     @Override
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 1944ba820..be9c13798 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
@@ -33,7 +33,7 @@ public class TileEntityBasket extends TileEntity {
         int currentNumInside = calcNumUnitsInside();
         if (currentNumInside != numUnitsInside && worldObj != null) {
             this.numUnitsInside = currentNumInside;
-            worldObj.notifyBlockChange(x, y, z, worldObj.getBlockId(x, y, z));
+            worldObj.notifyBlockChange(tilePos.x, tilePos.y, tilePos.z, worldObj.getBlockId(tilePos.x, tilePos.y, tilePos.z));
             setChanged();
         }
     }
@@ -69,7 +69,7 @@ public class TileEntityBasket extends TileEntity {
             return;
         }
 
-        final EntityItem item = new EntityItem(workingWorld, this.x + f, this.y + f1, this.z + f2, itemstack);
+        final EntityItem item = new EntityItem(workingWorld, this.tilePos.x + f, this.tilePos.y + f1, this.tilePos.z + f2, itemstack);
         final float f3 = 0.05F;
         item.xd = (float) rand.nextGaussian() * f3;
         item.yd = (float) rand.nextGaussian() * f3 + 0.25F;
@@ -106,7 +106,7 @@ public class TileEntityBasket extends TileEntity {
         }
 
         updateNumUnits();
-        world.notifyBlockChange(this.x, this.y, this.z, Blocks.BASKET.id());
+        world.notifyBlockChange(this.tilePos.x, this.tilePos.y, this.tilePos.z, Blocks.BASKET.id());
     }
 
     @Override
@@ -133,9 +133,9 @@ public class TileEntityBasket extends TileEntity {
         final World workingWorld;
         if (this.worldObj != null) {
             workingWorld = this.worldObj;
-            posX = this.x;
-            posY = this.y;
-            posZ = this.z;
+            posX = this.tilePos.x;
+            posY = this.tilePos.y;
+            posZ = this.tilePos.z;
         } else if (this.carriedBlock != null) {
             workingWorld = this.carriedBlock.world;
             posX = this.carriedBlock.holder.x - 0.5;
@@ -167,7 +167,7 @@ public class TileEntityBasket extends TileEntity {
 
         if (shouldUpdate) {
             if (this.worldObj != null) {
-                this.worldObj.notifyBlockChange(this.x, this.y, this.z, Blocks.BASKET.id());
+                this.worldObj.notifyBlockChange(this.tilePos.x, this.tilePos.y, this.tilePos.z, Blocks.BASKET.id());
             }
             updateNumUnits();
         }
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityChest.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityChest.java
index 4b48855bf..3102e18bd 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityChest.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityChest.java
@@ -125,11 +125,11 @@ public class TileEntityChest extends TileEntity
     @Override
 	public boolean stillValid(@NotNull Player player)
     {
-        if(worldObj == null || worldObj.getTileEntity(x, y, z) != this)
+        if(worldObj == null || worldObj.getTileEntity(tilePos.x, tilePos.y, tilePos.z) != this)
         {
             return false;
         }
-        return player.distanceToSqr((double) x + 0.5D, (double) y + 0.5D, (double) z + 0.5D) <= 64D;
+        return player.distanceToSqr((double) tilePos.x + 0.5D, (double) tilePos.y + 0.5D, (double) tilePos.z + 0.5D) <= 64D;
     }
 
     @Override
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityDispatcher.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityDispatcher.java
index 3d141b458..6137d822f 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityDispatcher.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityDispatcher.java
@@ -87,6 +87,7 @@ public class TileEntityDispatcher {
         addMapping(TileEntitySeat.class, NamespaceID.fromPool("minecraft", "seat"));
         addMapping(TileEntityFlowerJar.class, NamespaceID.fromPool("minecraft", "jar_flower"));
         addMapping(TileEntityJarButterfly.class, NamespaceID.fromPool("minecraft", "jar_butterfly"));
+        addMapping(TileEntityMesh.class, NamespaceID.fromPool("minecraft", "mesh"));
         addMapping(TileEntityMeshGold.class, NamespaceID.fromPool("minecraft", "mesh_gold"));
         addMapping(TileEntityStatue.class, NamespaceID.fromPool("minecraft", "statue_stone"));
     }
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityDispenser.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityDispenser.java
index dd7fa3cd0..ffb16000b 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityDispenser.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityDispenser.java
@@ -171,11 +171,11 @@ public class TileEntityDispenser extends TileEntity
     @Override
 	public boolean stillValid(@NotNull Player player)
     {
-        if(worldObj == null || worldObj.getTileEntity(x, y, z) != this)
+        if(worldObj == null || worldObj.getTileEntity(tilePos.x, tilePos.y, tilePos.z) != this)
         {
             return false;
         }
-        return player.distanceToSqr((double) x + 0.5D, (double) y + 0.5D, (double) z + 0.5D) <= 64D;
+        return player.distanceToSqr((double) tilePos.x + 0.5D, (double) tilePos.y + 0.5D, (double) tilePos.z + 0.5D) <= 64D;
     }
 
     @Override
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFlag.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFlag.java
index 75eb6db52..ffeb31d92 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFlag.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFlag.java
@@ -265,11 +265,11 @@ public class TileEntityFlag extends TileEntity
     @Override
     public boolean stillValid(@NotNull Player player)
     {
-        if(worldObj == null || worldObj.getTileEntity(x, y, z) != this)
+        if(worldObj == null || worldObj.getTileEntity(tilePos.x, tilePos.y, tilePos.z) != this)
         {
             return false;
         }
-        return player.distanceToSqr((double) x + 0.5D, (double) y + 0.5D, (double) z + 0.5D) <= 64D;
+        return player.distanceToSqr((double) tilePos.x + 0.5D, (double) tilePos.y + 0.5D, (double) tilePos.z + 0.5D) <= 64D;
     }
 
     @Override
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 167b9015d..f2fab04d6 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
@@ -67,7 +67,7 @@ public class TileEntityFurnace extends TileEntity
                 ItemStack itemstack = furnaceItemStacks[slot];
                 furnaceItemStacks[slot] = null;
                 if (worldObj != null && slot == SLOT_RESULT) {
-                    worldObj.markBlockNeedsUpdate(x, y, z);
+                    worldObj.markBlockNeedsUpdate(tilePos.x, tilePos.y, tilePos.z);
                 }
                 return itemstack;
             }
@@ -76,7 +76,7 @@ public class TileEntityFurnace extends TileEntity
             {
                 furnaceItemStacks[slot] = null;
                 if (worldObj != null && slot == SLOT_RESULT) {
-                    worldObj.markBlockNeedsUpdate(x, y, z);
+                    worldObj.markBlockNeedsUpdate(tilePos.x, tilePos.y, tilePos.z);
                 }
             }
             return itemstack1;
@@ -95,7 +95,7 @@ public class TileEntityFurnace extends TileEntity
             stack.stackSize = getMaxStackSize();
         }
         if (worldObj != null && slot == SLOT_RESULT && stack == null) {
-            worldObj.markBlockNeedsUpdate(x, y, z);
+            worldObj.markBlockNeedsUpdate(tilePos.x, tilePos.y, tilePos.z);
         }
     }
 
@@ -183,7 +183,7 @@ public class TileEntityFurnace extends TileEntity
         }
         if(worldObj == null || !worldObj.isClientSide)
         {
-        	if(worldObj == null || worldObj.getBlockId(x, y, z) == Blocks.FURNACE_STONE_IDLE.id()) {
+        	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--;
@@ -305,13 +305,13 @@ public class TileEntityFurnace extends TileEntity
             furnaceItemStacks[SLOT_INGREDIENT] = null;
         }
         if (worldObj != null && wasEmpty && furnaceItemStacks[SLOT_RESULT] != null) {
-            worldObj.markBlockNeedsUpdate(x, y, z);
+            worldObj.markBlockNeedsUpdate(tilePos.x, tilePos.y, tilePos.z);
         }
     }
 
     protected void updateFurnace(boolean forceLit){
         if (worldObj != null) {
-            BlockLogicFurnace.updateFurnaceBlockState(worldObj, new TilePos(x, y, z), forceLit | (currentBurnTime > 0));
+            BlockLogicFurnace.updateFurnaceBlockState(worldObj, tilePos, forceLit | (currentBurnTime > 0));
         } else if (carriedBlock != null) {
             carriedBlock.blockId = (forceLit | (currentBurnTime > 0)) ? Blocks.FURNACE_STONE_ACTIVE.id() : Blocks.FURNACE_STONE_IDLE.id();
         }
@@ -327,11 +327,11 @@ public class TileEntityFurnace extends TileEntity
     @Override
 	public boolean stillValid(@NotNull Player player)
     {
-        if(worldObj == null || worldObj.getTileEntity(x, y, z) != this)
+        if(worldObj == null || worldObj.getTileEntity(tilePos.x, tilePos.y, tilePos.z) != this)
         {
             return false;
         }
-        return player.distanceToSqr((double) x + 0.5D, (double) y + 0.5D, (double) z + 0.5D) <= 64D;
+        return player.distanceToSqr((double) tilePos.x + 0.5D, (double) tilePos.y + 0.5D, (double) tilePos.z + 0.5D) <= 64D;
     }
 
     @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 92f9329b4..209aeb4bf 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
@@ -67,7 +67,7 @@ public class TileEntityFurnaceBlast extends TileEntity
                 final ItemStack itemstack = this.inventory[slot];
                 this.inventory[slot] = null;
                 if (this.worldObj != null && slot == SLOT_RESULT) {
-                    this.worldObj.markBlockNeedsUpdate(this.x, this.y, this.z);
+                    this.worldObj.markBlockNeedsUpdate(this.tilePos.x, this.tilePos.y, this.tilePos.z);
                 }
                 return itemstack;
             }
@@ -75,7 +75,7 @@ public class TileEntityFurnaceBlast extends TileEntity
             if (this.inventory[slot].stackSize <= 0) {
                 this.inventory[slot] = null;
                 if (this.worldObj != null && slot == SLOT_RESULT) {
-                    this.worldObj.markBlockNeedsUpdate(this.x, this.y, this.z);
+                    this.worldObj.markBlockNeedsUpdate(this.tilePos.x, this.tilePos.y, this.tilePos.z);
                 }
             }
             return itemstack1;
@@ -91,7 +91,7 @@ public class TileEntityFurnaceBlast extends TileEntity
             stack.stackSize = getMaxStackSize();
         }
         if (this.worldObj != null && slot == SLOT_RESULT && stack == null) {
-            this.worldObj.markBlockNeedsUpdate(this.x, this.y, this.z);
+            this.worldObj.markBlockNeedsUpdate(this.tilePos.x, this.tilePos.y, this.tilePos.z);
         }
     }
 
@@ -167,7 +167,7 @@ public class TileEntityFurnaceBlast extends TileEntity
             this.currentBurnTime--;
         }
         if (this.worldObj == null || !this.worldObj.isClientSide) {
-            if (this.worldObj == null || this.worldObj.getBlockId(this.x, this.y, this.z) == Blocks.FURNACE_BLAST_IDLE.id()) {
+            if (this.worldObj == null || this.worldObj.getBlockId(this.tilePos.x, this.tilePos.y, this.tilePos.z) == Blocks.FURNACE_BLAST_IDLE.id()) {
                 if (this.currentBurnTime == 0 && this.inventory[SLOT_LEFT] == null && this.inventory[SLOT_RIGHT] == null) {
                     if (this.inventory[SLOT_FUEL] != null && this.inventory[SLOT_FUEL].itemID == Blocks.COBBLE_NETHERRACK.id()) {
                         this.inventory[SLOT_FUEL].stackSize--;
@@ -273,7 +273,7 @@ public class TileEntityFurnaceBlast extends TileEntity
 
     protected void updateFurnace(final boolean forceLit) {
         if (this.worldObj != null) {
-            BlockLogicFurnaceBlast.updateFurnaceBlockState(this.worldObj, new TilePos(this.x, this.y, this.z), forceLit | (this.currentBurnTime > 0));
+            BlockLogicFurnaceBlast.updateFurnaceBlockState(this.worldObj, new TilePos(this.tilePos.x, this.tilePos.y, this.tilePos.z), forceLit | (this.currentBurnTime > 0));
         } else if (this.carriedBlock != null) {
             this.carriedBlock.blockId = (forceLit | (this.currentBurnTime > 0)) ? Blocks.FURNACE_BLAST_ACTIVE.id() : Blocks.FURNACE_BLAST_IDLE.id();
         }
@@ -287,10 +287,10 @@ public class TileEntityFurnaceBlast extends TileEntity
 
     @Override
     public boolean stillValid(final @NotNull Player player) {
-        if (this.worldObj == null || this.worldObj.getTileEntity(this.x, this.y, this.z) != this) {
+        if (this.worldObj == null || this.worldObj.getTileEntity(this.tilePos.x, this.tilePos.y, this.tilePos.z) != this) {
             return false;
         }
-        return player.distanceToSqr((double) this.x + 0.5D, (double) this.y + 0.5D, (double) this.z + 0.5D) <= 64D;
+        return player.distanceToSqr((double) this.tilePos.x + 0.5D, (double) this.tilePos.y + 0.5D, (double) this.tilePos.z + 0.5D) <= 64D;
     }
 
     @Override
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityMesh.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityMesh.java
new file mode 100644
index 000000000..68574af82
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityMesh.java
@@ -0,0 +1,266 @@
+package net.minecraft.core.block.entity;
+import com.mojang.nbt.tags.CompoundTag;
+import net.minecraft.core.achievement.Achievements;
+import net.minecraft.core.block.Blocks;
+import net.minecraft.core.current.wire.WireHandler;
+import net.minecraft.core.entity.Entity;
+import net.minecraft.core.entity.EntityItem;
+import net.minecraft.core.entity.player.Player;
+import net.minecraft.core.util.helper.Axis;
+import net.minecraft.core.util.helper.Direction;
+import net.minecraft.core.world.pos.TilePos;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.joml.primitives.AABBd;
+import org.joml.primitives.AABBdc;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class TileEntityMesh extends TileEntity {
+    public final @NotNull List<EntityItem> trackedItemsSiphoning = new ArrayList<>(List.of());
+    public final int siphonWidth = 5;
+    public final int siphonHeight = 2;
+    public int ticksRan = 0;
+    public TileEntityMesh() {
+    }
+
+    @Override
+    public void tick()
+    {
+        if (this.worldObj == null || this.worldObj.isClientSide) {
+            return;
+        }
+
+        Map<Direction, Boolean> validSiphonDirections = Arrays.stream(WireHandler.Directions.ALL).collect(Collectors.toMap(direction -> direction, this::isValidWater));
+        handleSiphon(validSiphonDirections);
+
+        if (ticksRan == 0){
+            ticksRan = worldObj.rand.nextInt(360);
+        }
+        ticksRan++;
+    }
+
+    @Override
+    public void readAdditionalData(@NotNull CompoundTag compoundTag) {
+
+    }
+
+    @Override
+    public void writeAdditionalData(@NotNull CompoundTag compoundTag) {
+
+    }
+
+    protected void handleSiphon(@NotNull Map<Direction, Boolean> validSiphonDirections){
+        //null-checks were already made
+        assert this.worldObj != null;
+
+        Direction direction = getSingleValidFace(validSiphonDirections);
+        if(direction == Direction.NONE){return;}
+        AABBdc siphonBox = getSiphonBox(tilePos, direction);
+        List<Entity> entitiesWithinSiphonBox = this.worldObj.getEntitiesWithinAABBExcludingEntity(null, siphonBox);
+        removeTrackedItems(entitiesWithinSiphonBox);
+
+        for (Entity entity : entitiesWithinSiphonBox)
+        {
+            if (entity instanceof EntityItem item && entity.isInWater())
+            {
+                if(item.meshSiphoning == null && item.siphonDirection == Direction.NONE){
+                    item.siphonDirection = direction.getOpposite();
+                    item.meshSiphoning = this;
+                }
+
+                if(direction.getOpposite().equals(item.siphonDirection) && item.meshSiphoning == this){
+
+                    Player player = entity.world.getClosestPlayer(tilePos.x, tilePos.y, tilePos.z, 12);
+                    if(player != null) {
+                        player.triggerAchievement(Achievements.DOWN_THE_DRAIN);
+                    }
+
+                    siphonItemIntoBlock(item, direction);
+                    addItemsToBeTracked(item);
+                }
+            }
+        }
+    }
+
+    protected void removeTrackedItems(@NotNull List<Entity> entitiesWithinSiphonBox){
+        Iterator<EntityItem> iterator = trackedItemsSiphoning.iterator();
+        while (iterator.hasNext()) {
+            EntityItem item = iterator.next();
+            if (!entitiesWithinSiphonBox.contains(item)) {
+                item.siphonDirection = Direction.NONE;
+                item.meshSiphoning = null;
+                iterator.remove();
+                break;
+            }
+        }
+    }
+
+    protected void addItemsToBeTracked(@NotNull EntityItem item){
+        if(!trackedItemsSiphoning.contains(item))
+            trackedItemsSiphoning.add((item));
+    }
+
+    protected @NotNull Direction getSingleValidFace(@NotNull Map<Direction, Boolean> validSiphonDirections){
+        //If none are valid or if more than one side is valid, returns Direction.NONE
+        //else, returns the valid Direction
+
+        Direction direction = Direction.NONE;
+
+        if(validSiphonDirections.values().stream().noneMatch(val -> val) ||
+        validSiphonDirections.values().stream().filter(val -> val).count() > 1) {
+            return direction;
+        }
+
+        for (Map.Entry<Direction, Boolean> entry : validSiphonDirections.entrySet()) {
+            if (entry.getValue()) {
+                direction = entry.getKey();
+                break;
+            }
+        }
+
+        if (direction == Direction.DOWN){
+            return Direction.NONE;
+        }
+
+        return direction;
+    }
+
+    protected AABBdc getSiphonBox(TilePos tilePos, @NotNull Direction facing){
+        // get the center of the block
+        double centerX = tilePos.x + 0.5;
+        double centerY = tilePos.y + 0.5;
+        double centerZ = tilePos.z + 0.5;
+
+        // move the center to the edge of the block face
+        centerX += facing.getOffsetX() * 0.5;
+        centerY += facing.getOffsetY() * 0.5;
+        centerZ += facing.getOffsetZ() * 0.5;
+
+        // move height distance further in the facing direction
+        double farX = centerX + (facing.getOffsetX() * siphonHeight);
+        double farY = centerY + (facing.getOffsetY() * siphonHeight);
+        double farZ = centerZ + (facing.getOffsetZ() * siphonHeight);
+
+
+        // need a half width since in middle of block
+        double hw = siphonWidth / 2.0;
+
+        // expand/inflate
+        // (would be amazing if AABBd had .inflate(), but we are decrepit old bastards don't even get me started on the mental processing it took to work on this shitty ass code for the first time I need sleep and want to die)
+        double minX = Math.min(centerX, farX);
+        double minY = Math.min(centerY, farY);
+        double minZ = Math.min(centerZ, farZ);
+
+        double maxX = Math.max(centerX, farX);
+        double maxY = Math.max(centerY, farY);
+        double maxZ = Math.max(centerZ, farZ);
+
+        // expand based on the axis we are NOT facing
+        if (facing.getAxis() != Axis.X) {
+            minX -= hw;
+            maxX += hw;
+        }
+        if (facing.getAxis() != Axis.Y) {
+            minY -= hw;
+            maxY += hw;
+        }
+        if (facing.getAxis() != Axis.Z) {
+            minZ -= hw;
+            maxZ += hw;
+        }
+
+        //yay we returned it goodnight sleep tight I need food
+        return new AABBd(minX, minY, minZ, maxX, maxY, maxZ);
+    }
+
+    protected void siphonItemIntoBlock(@NotNull EntityItem item, @NotNull Direction direction){
+
+        double targetX = this.tilePos.x + 0.5;
+        double targetY = this.tilePos.y + 0.5;
+        double targetZ = this.tilePos.z + 0.5;
+
+        //difference vectors
+        double diffX = targetX - item.x;
+        double diffY = targetY - item.y;
+        double diffZ = targetZ - item.z;
+
+        //distance from item
+        double dist = Math.sqrt(diffX * diffX + diffY * diffY + diffZ * diffZ);
+
+        //we don't divide by zero in this household
+        if (dist > 0.01) {
+
+            //Siphon variables yippee!!!!
+            double maxRange         = Math.max(siphonWidth / 2.0, siphonHeight);
+            double clampedDist      = Math.min(dist, maxRange);
+            double proximityFactor  = 1.0 + (maxRange / (clampedDist + 0.5));
+            double pullStrength     = 0.05;
+            double dynSwirlStrength = (pullStrength * 1.5) * proximityFactor;
+
+            /*
+            //normalize the pull speed and apply the dynamic speed multiplier
+            double pullX = (diffX / dist) * speedMultiplier;
+            double pullY = (diffY / dist) * speedMultiplier;
+            double pullZ = (diffZ / dist) * speedMultiplier;
+            */
+
+            //non-normalize implementation
+            double speedMultiplier  = pullStrength * proximityFactor;
+            double pullX = diffX  * speedMultiplier;
+            double pullY = diffY  * speedMultiplier;
+            double pullZ = diffZ  * speedMultiplier;
+
+            //axis check to make sure the swirl behaves correctly depending on facing direction
+            double sX = 0, sY = 0, sZ = 0;
+            if (direction.getAxis() == Axis.Y) {
+                sX = -diffZ;
+                sZ = diffX;
+            } else if (direction.getAxis() == Axis.Z) {
+                sX = -diffY;
+                sY = diffX;
+            } else {
+                sY = -diffZ;
+                sZ = diffY;
+            }
+
+            double swirlDist = Math.sqrt(sX * sX + sY * sY + sZ * sZ);
+            if (swirlDist > 0.01) {
+                // swirl also gets faster as it gets closer
+                sX = (sX / swirlDist) * dynSwirlStrength;
+                sY = (sY / swirlDist) * dynSwirlStrength;
+                sZ = (sZ / swirlDist) * dynSwirlStrength;
+            }
+
+            item.xd = pullX + sX;
+            item.yd = pullY + sY;
+            item.zd = pullZ + sZ;
+
+            //kinda gave up here, don't know what I was on but it kinda works so..?
+            double slowStrength = 0.5 - (0.2 * (1.0 - (clampedDist / maxRange)));
+            switch (direction) {
+                case UP    -> {if (item.yd < 0) item.yd *= slowStrength;}
+                case DOWN  -> {if (item.yd > 0) item.yd *= slowStrength;}
+                case NORTH -> {if (item.zd > 0) item.zd *= slowStrength;}
+                case SOUTH -> {if (item.zd < 0) item.zd *= slowStrength;}
+                case WEST  -> {if (item.xd > 0) item.xd *= slowStrength;}
+                case EAST  -> {if (item.xd < 0) item.xd *= slowStrength;}
+            }
+        }
+    }
+
+    protected boolean isValidWater(@NotNull Direction direction){
+        if(worldObj == null){
+            return false;
+        }
+
+        //check both if block in 'direction' is water, and if it is a full block of water
+        if(worldObj.getBlockType(new TilePos(tilePos).add(direction)) == Blocks.FLUID_WATER_STILL){
+            return !Blocks.FLUID_WATER_STILL.getLogic().hasFlowVector(worldObj, new TilePos(tilePos).add(direction));
+        }
+        return false;
+    }
+
+}
+
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityMeshGold.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityMeshGold.java
index d5564e64c..c3fe1a39a 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityMeshGold.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityMeshGold.java
@@ -1,30 +1,69 @@
 package net.minecraft.core.block.entity;
 
 import com.mojang.nbt.tags.CompoundTag;
+import net.minecraft.core.achievement.Achievements;
+import net.minecraft.core.current.wire.WireHandler;
+import net.minecraft.core.entity.Entity;
 import net.minecraft.core.entity.EntityItem;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.net.packet.Packet;
 import net.minecraft.core.net.packet.PacketTileEntityData;
+import net.minecraft.core.util.helper.Direction;
 import net.minecraft.core.world.World;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
+import org.joml.primitives.AABBdc;
 
-public class TileEntityMeshGold extends TileEntity {
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class TileEntityMeshGold extends TileEntityMesh {
     @Nullable
     public ItemStack filterItem;
     public int ticksRan = 0;
     public TileEntityMeshGold() {
     }
 
+    @Override
     public void tick()
     {
+        if (this.worldObj == null || this.worldObj.isClientSide) {
+            return;
+        }
+
+        if(filterItem != null){
+            Map<Direction, Boolean> validSiphonDirections = Arrays.stream(WireHandler.Directions.ALL).collect(Collectors.toMap(direction -> direction, this::isValidWater));
+            handleSiphon(validSiphonDirections);
+        }
+
         if (ticksRan == 0){
             ticksRan = worldObj.rand.nextInt(360);
         }
         ticksRan++;
     }
 
+    @Override
+    public void readAdditionalData(@NotNull CompoundTag compoundTag)
+    {
+        if (compoundTag.getBoolean("hasItem")){
+            filterItem = ItemStack.readItemStackFromNbt(compoundTag.getCompound("item"));
+        } else {
+            filterItem = null;
+        }
+    }
+
+    @Override
+    public void writeAdditionalData(@NotNull CompoundTag compoundTag)
+    {
+        if (filterItem != null){
+            compoundTag.putCompound("item", filterItem.writeToNBT(new CompoundTag()));
+            compoundTag.putBoolean("hasItem", true);
+        }
+    }
+
     public boolean setFilterItem(Player player, @Nullable ItemStack stack){
         boolean success = false;
         boolean wasNull = filterItem == null;
@@ -45,22 +84,40 @@ public class TileEntityMeshGold extends TileEntity {
         setChanged();
         return success | !wasNull;
     }
-    @Override
-    public void readAdditionalData(@NotNull CompoundTag compoundTag)
-    {
-        if (compoundTag.getBoolean("hasItem")){
-            filterItem = ItemStack.readItemStackFromNbt(compoundTag.getCompound("item"));
-        } else {
-            filterItem = null;
-        }
-    }
 
     @Override
-    public void writeAdditionalData(@NotNull CompoundTag compoundTag)
-    {
-        if (filterItem != null){
-            compoundTag.putCompound("item", filterItem.writeToNBT(new CompoundTag()));
-            compoundTag.putBoolean("hasItem", true);
+    protected void handleSiphon(@NotNull Map<Direction, Boolean> validSiphonDirections){
+        //null-checks were already made
+        assert this.worldObj != null;
+        assert this.filterItem != null;
+
+        Direction direction = getSingleValidFace(validSiphonDirections);
+        if(direction == Direction.NONE){return;}
+        AABBdc siphonBox = getSiphonBox(tilePos, direction);
+        List<Entity> entitiesWithinSiphonBox = this.worldObj.getEntitiesWithinAABBExcludingEntity(null, siphonBox);
+        removeTrackedItems(entitiesWithinSiphonBox);
+
+        for (Entity entity : entitiesWithinSiphonBox)
+        {
+            if (entity instanceof EntityItem item && entity.isInWater())
+            {
+                if(item.meshSiphoning == null && item.siphonDirection == Direction.NONE){
+                    item.siphonDirection = direction.getOpposite();
+                    item.meshSiphoning = this;
+                }
+                if(direction.getOpposite().equals(item.siphonDirection) && item.meshSiphoning == this) {
+                    if (item.item.getItem() == this.filterItem.getItem() && item.item.getMetadata() == this.filterItem.getMetadata()) {
+
+                        Player player = entity.world.getClosestPlayer(tilePos.x, tilePos.y, tilePos.z, 12);
+                        if(player != null) {
+                            player.triggerAchievement(Achievements.DOWN_THE_DRAIN);
+                        }
+
+                        siphonItemIntoBlock(item, direction);
+                        addItemsToBeTracked(item);
+                    }
+                }
+            }
         }
     }
 
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityMobSpawner.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityMobSpawner.java
index d41a4f95f..d5382c169 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityMobSpawner.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityMobSpawner.java
@@ -48,20 +48,20 @@ public class TileEntityMobSpawner extends TileEntity {
     }
 
     public boolean anyPlayerInRange() {
-        return this.worldObj != null && this.worldObj.getClosestPlayer((double) this.x + 0.5D, (double) this.y + 0.5D, (double) this.z + 0.5D, 16D) != null;
+        return this.worldObj != null && this.worldObj.getClosestPlayer((double) this.tilePos.x + 0.5D, (double) this.tilePos.y + 0.5D, (double) this.tilePos.z + 0.5D, 16D) != null;
     }
 
     @Override
     public void tick() {
-        if (this.worldObj.getBlockId(this.x, this.y, this.z) != Blocks.MOBSPAWNER.id())
+        if (this.worldObj.getBlockId(this.tilePos.x, this.tilePos.y, this.tilePos.z) != Blocks.MOBSPAWNER.id())
             return; // dumb fix for mp particle bug, need proper way to inform client to remove tileEntity
         this.yaw2 = this.yaw;
         if (!anyPlayerInRange()) {
             return;
         }
-        double xPos = (double) this.x + this.worldObj.rand.nextFloat();
-        double yPos = (double) this.y + this.worldObj.rand.nextFloat();
-        double zPos = (double) this.z + this.worldObj.rand.nextFloat();
+        double xPos = (double) this.tilePos.x + this.worldObj.rand.nextFloat();
+        double yPos = (double) this.tilePos.y + this.worldObj.rand.nextFloat();
+        double zPos = (double) this.tilePos.z + this.worldObj.rand.nextFloat();
         this.worldObj.spawnParticle("smoke", xPos, yPos, zPos, 0.0D, 0.0D, 0.0D, 0);
         this.worldObj.spawnParticle("flame", xPos, yPos, zPos, 0.0D, 0.0D, 0.0D, 0);
         for (this.yaw += 1000F / ((float) this.delay + 200F); this.yaw > 360D; ) {
@@ -93,16 +93,16 @@ public class TileEntityMobSpawner extends TileEntity {
                 if (mob == null) {
                     return;
                 }
-                AABBd checkBB = new AABBd(this.x, this.y, this.z, this.x + 1, this.y + 1, this.z + 1);
+                AABBd checkBB = new AABBd(this.tilePos.x, this.tilePos.y, this.tilePos.z, this.tilePos.x + 1, this.tilePos.y + 1, this.tilePos.z + 1);
                 MathHelper.aabbGrow(checkBB, 8, 4, 8, checkBB);
                 int nearbySpawnCount = this.worldObj.getEntitiesWithinAABB(mob.getClass(), checkBB).size();
                 if (nearbySpawnCount >= 6) {
                     updateDelay();
                     return;
                 }
-                double d6 = (double) this.x + (this.worldObj.rand.nextDouble() - this.worldObj.rand.nextDouble()) * 4D;
-                double d7 = (this.y + this.worldObj.rand.nextInt(3)) - 1;
-                double d8 = (double) this.z + (this.worldObj.rand.nextDouble() - this.worldObj.rand.nextDouble()) * 4D;
+                double d6 = (double) this.tilePos.x + (this.worldObj.rand.nextDouble() - this.worldObj.rand.nextDouble()) * 4D;
+                double d7 = (this.tilePos.y + this.worldObj.rand.nextInt(3)) - 1;
+                double d8 = (double) this.tilePos.z + (this.worldObj.rand.nextDouble() - this.worldObj.rand.nextDouble()) * 4D;
                 mob.moveTo(d6, d7, d8, this.worldObj.rand.nextFloat() * 360F, 0.0F);
                 if (!mob.canSpawnHere()) {
                     updateDelay();
@@ -111,9 +111,9 @@ public class TileEntityMobSpawner extends TileEntity {
                 mob.spawnInit();
                 this.worldObj.entityJoinedWorld(mob);
                 for (int k = 0; k < 20; k++) {
-                    double d1 = (double) this.x + 0.5D + ((double) this.worldObj.rand.nextFloat() - 0.5D) * 2D;
-                    double d3 = (double) this.y + 0.5D + ((double) this.worldObj.rand.nextFloat() - 0.5D) * 2D;
-                    double d5 = (double) this.z + 0.5D + ((double) this.worldObj.rand.nextFloat() - 0.5D) * 2D;
+                    double d1 = (double) this.tilePos.x + 0.5D + ((double) this.worldObj.rand.nextFloat() - 0.5D) * 2D;
+                    double d3 = (double) this.tilePos.y + 0.5D + ((double) this.worldObj.rand.nextFloat() - 0.5D) * 2D;
+                    double d5 = (double) this.tilePos.z + 0.5D + ((double) this.worldObj.rand.nextFloat() - 0.5D) * 2D;
                     this.worldObj.spawnParticle("smoke", d1, d3, d5, 0.0D, 0.0D, 0.0D, 0);
                     this.worldObj.spawnParticle("flame", d1, d3, d5, 0.0D, 0.0D, 0.0D, 0);
                     this.worldObj.playSoundAtEntity(null, mob, "tile.mobspawner.spawn", 0.025F, 0.75F);
@@ -129,16 +129,16 @@ public class TileEntityMobSpawner extends TileEntity {
 
     @Override
     public Packet getDescriptionPacket() {
-        return new PacketSetMobSpawner(new TilePos(this.x, this.y, this.z), EntityDispatcher.getInstance().entryForId(this.mobId));
+        return new PacketSetMobSpawner(new TilePos(this.tilePos.x, this.tilePos.y, this.tilePos.z), EntityDispatcher.getInstance().entryForId(this.mobId));
     }
 
     public int countNearbySpawners() {
         int r = 4;
         int count = 0;
         TilePos tilePos = new TilePos(0, 0, 0);
-        for (tilePos.x = this.x -r; tilePos.x <= this.x + r; tilePos.x++) {
-            for (tilePos.y = this.y -r; tilePos.y <= this.y + r; tilePos.y++) {
-                for (tilePos.z = this.z -r; tilePos.z <= this.z + r; tilePos.z++) {
+        for (tilePos.x = this.tilePos.x -r; tilePos.x <= this.tilePos.x + r; tilePos.x++) {
+            for (tilePos.y = this.tilePos.y -r; tilePos.y <= this.tilePos.y + r; tilePos.y++) {
+                for (tilePos.z = this.tilePos.z -r; tilePos.z <= this.tilePos.z + r; tilePos.z++) {
                     if (this.worldObj.getBlockType(tilePos) == Blocks.MOBSPAWNER) {
                         count++;
                     }
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityNoteblock.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityNoteblock.java
index 29dfd3d60..34aa63190 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityNoteblock.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityNoteblock.java
@@ -19,8 +19,8 @@ public class TileEntityNoteblock extends TileEntity
 
     @Override
     public void tick() {
-        if (worldObj.getBlock(x, y, z) == Blocks.NOTEBLOCK) { // Set note block pitch
-            worldObj.setBlockMetadata(x, y, z, BlockLogicNote.setNote(0, note));
+        if (worldObj.getBlock(tilePos.x, tilePos.y, tilePos.z) == Blocks.NOTEBLOCK) { // Set note block pitch
+            worldObj.setBlockMetadata(tilePos.x, tilePos.y, tilePos.z, BlockLogicNote.setNote(0, note));
         }
         invalidate(); // Delete self
     }
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntitySeat.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntitySeat.java
index 96c2e51bc..865105139 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntitySeat.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntitySeat.java
@@ -35,7 +35,7 @@ public class TileEntitySeat
         }
 
         // TODO the server really needs a better way of telling the client that a tile entity no longer exists, but this works to fix the client thinking that the seat remains if its broken in multiplayer
-        if (worldObj != null && worldObj.getBlock(x, y, z) != seat) {
+        if (worldObj != null && worldObj.getBlock(tilePos.x, tilePos.y, tilePos.z) != seat) {
             ejectRider();
         }
     }
@@ -59,9 +59,9 @@ public class TileEntitySeat
             y = carriedBlock.holder.y;
             z = carriedBlock.holder.z;
         } else {
-            x = this.x;
-            y = this.y;
-            z = this.z;
+            x = this.tilePos.x;
+            y = this.tilePos.y;
+            z = this.tilePos.z;
         }
 
         if (isSafe(x, y + 1, z)) {
@@ -96,7 +96,7 @@ public class TileEntitySeat
         if (carriedBlock != null) {
             passenger.setPos(carriedBlock.holder.x, carriedBlock.holder.y + passenger.getRidingHeight(), carriedBlock.holder.z);
         } else {
-            passenger.setPos(this.x + 0.5, this.y + 0.5 + passenger.getRidingHeight(), z + 0.5);
+            passenger.setPos(this.tilePos.x + 0.5, this.tilePos.y + 0.5 + passenger.getRidingHeight(), tilePos.z + 0.5);
         }
     }
 
@@ -120,7 +120,7 @@ public class TileEntitySeat
         if (carriedBlock != null) {
             entity.moveTo(carriedBlock.holder.x, carriedBlock.holder.y + 2, carriedBlock.holder.z, entity.yRot, entity.xRot);
         } else {
-            entity.moveTo(this.x, this.y + 2, this.z, entity.yRot, entity.xRot);
+            entity.moveTo(this.tilePos.x, this.tilePos.y + 2, this.tilePos.z, entity.yRot, entity.xRot);
         }
     }
 
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntitySensor.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntitySensor.java
index 53f8f7553..c9fa51b5b 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntitySensor.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntitySensor.java
@@ -69,17 +69,16 @@ public class TileEntitySensor extends TileEntity {
             return;
         }
 
-        int id = this.worldObj.getBlockId(this.x, this.y, this.z);
-        int meta = this.worldObj.getBlockMetadata(this.x, this.y, this.z);
+        int id = this.worldObj.getBlockId(this.tilePos.x, this.tilePos.y, this.tilePos.z);
+        int meta = this.worldObj.getBlockMetadata(this.tilePos.x, this.tilePos.y, this.tilePos.z);
 
         boolean shouldBeActive = false;
         Direction facing = BlockLogicVeryRotatable.metaToDirection(meta);
-        int effectiveRange = getSightRange(this.worldObj, this.x, this.y, this.z, facing);
+        int effectiveRange = getSightRange(this.worldObj, this.tilePos.x, this.tilePos.y, this.tilePos.z, facing);
         if (effectiveRange > 0) {
-            AABBdc detectionBox = getDetectionBox(this.x, this.y, this.z, facing, effectiveRange);
+            AABBdc detectionBox = getDetectionBox(this.tilePos.x, this.tilePos.y, this.tilePos.z, facing, effectiveRange);
             List<Entity> list = this.worldObj.getEntitiesWithinAABBExcludingEntity(null, detectionBox);
-            for (int i = 0; i < list.size(); i++) {
-                Entity entity = list.get(i);
+            for (Entity entity : list) {
                 if (entity.canInteract()) {
                     if (entity instanceof Player player) {
                         player.triggerAchievement(Achievements.GET_SENSED);
@@ -90,7 +89,7 @@ public class TileEntitySensor extends TileEntity {
         }
 
         if (shouldBeActive && id == Blocks.MOTION_SENSOR_IDLE.id()) {
-            this.worldObj.playSoundEffect(null, SoundCategory.WORLD_SOUNDS, this.x + 0.5, this.y + 0.5, this.z + 0.5, "tile.sensor_block.sense", 1f, this.worldObj.rand.nextFloat());
+            this.worldObj.playSoundEffect(null, SoundCategory.WORLD_SOUNDS, this.tilePos.x + 0.5, this.tilePos.y + 0.5, this.tilePos.z + 0.5, "tile.sensor_block.sense", 1f, this.worldObj.rand.nextFloat());
             updateSensorBlockState(true, this.worldObj);
         }
         if (!shouldBeActive && id == Blocks.MOTION_SENSOR_ACTIVE.id()) {
@@ -135,7 +134,7 @@ public class TileEntitySensor extends TileEntity {
         }
 
         if (shouldBeActive && id == Blocks.MOTION_SENSOR_IDLE.id()) {
-            world.playSoundEffect(null, SoundCategory.WORLD_SOUNDS, this.x + 0.5, this.y + 0.5, this.z + 0.5, "tile.sensor_block.sense", 1f, world.rand.nextFloat());
+            world.playSoundEffect(null, SoundCategory.WORLD_SOUNDS, this.tilePos.x + 0.5, this.tilePos.y + 0.5, this.tilePos.z + 0.5, "tile.sensor_block.sense", 1f, world.rand.nextFloat());
             updateSensorBlockState(true, world);
         }
         if (!shouldBeActive && id == Blocks.MOTION_SENSOR_ACTIVE.id()) {
@@ -151,16 +150,16 @@ public class TileEntitySensor extends TileEntity {
                 this.carriedBlock.blockId = Blocks.MOTION_SENSOR_IDLE.id();
             }
         } else {
-            int meta = world.getBlockMetadata(this.x, this.y, this.z);
+            int meta = world.getBlockMetadata(this.tilePos.x, this.tilePos.y, this.tilePos.z);
             int blockId;
             if (active) {
                 blockId = Blocks.MOTION_SENSOR_ACTIVE.id();
             } else {
                 blockId = Blocks.MOTION_SENSOR_IDLE.id();
             }
-            world.setBlock(this.x, this.y, this.z, blockId);
-            world.setBlockMetadata(this.x, this.y, this.z, meta);
-            world.notifyBlockChange(this.x, this.y, this.z, blockId);
+            world.setBlock(this.tilePos.x, this.tilePos.y, this.tilePos.z, blockId);
+            world.setBlockMetadata(this.tilePos.x, this.tilePos.y, this.tilePos.z, meta);
+            world.notifyBlockChange(this.tilePos.x, this.tilePos.y, this.tilePos.z, blockId);
         }
     }
 }
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 6901bc2bf..cf28b7b55 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
@@ -195,7 +195,7 @@ public class TileEntityTrommel extends TileEntity
         }
         if(!isClient)
         {
-        	if(this.worldObj == null || this.worldObj.getBlockId(this.x, this.y, this.z) == Blocks.TROMMEL_IDLE.id()) {
+        	if(this.worldObj == null || this.worldObj.getBlockId(this.tilePos.x, this.tilePos.y, this.tilePos.z) == Blocks.TROMMEL_IDLE.id()) {
             	if(this.currentItemBurnTime == 0 && this.itemStacks[SLOTS_INGREDIENT[0]] == null && this.itemStacks[SLOTS_INGREDIENT[1]] == null && this.itemStacks[SLOTS_INGREDIENT[2]] == null && this.itemStacks[SLOTS_INGREDIENT[3]] == null) {
             		if(this.itemStacks[SLOT_FUEL] != null && this.itemStacks[SLOT_FUEL].itemID == Blocks.COBBLE_NETHERRACK.id()) {
                         this.itemStacks[SLOT_FUEL].stackSize--;
@@ -203,7 +203,7 @@ public class TileEntityTrommel extends TileEntity
                             this.itemStacks[SLOT_FUEL] = null;
                         }
             			if (this.worldObj != null) {
-                            BlockLogicTrommel.updateTrommelBlockState(this.worldObj, new TilePos(this.x, this.y, this.z), true);
+                            BlockLogicTrommel.updateTrommelBlockState(this.worldObj, new TilePos(this.tilePos.x, this.tilePos.y, this.tilePos.z), true);
                         } else if (this.carriedBlock != null) {
                             this.carriedBlock.blockId = Blocks.TROMMEL_ACTIVE.id();
                         }
@@ -252,7 +252,7 @@ public class TileEntityTrommel extends TileEntity
             {
                 flag1 = true;
                 if (this.worldObj != null) {
-                    BlockLogicTrommel.updateTrommelBlockState(this.worldObj, new TilePos(this.x, this.y, this.z), this.burnTime > 0);
+                    BlockLogicTrommel.updateTrommelBlockState(this.worldObj, new TilePos(this.tilePos.x, this.tilePos.y, this.tilePos.z), this.burnTime > 0);
                 } else if (this.carriedBlock != null) {
                     this.carriedBlock.blockId = this.burnTime > 0 ? Blocks.TROMMEL_ACTIVE.id() : Blocks.TROMMEL_IDLE.id();
                 }
@@ -285,7 +285,7 @@ public class TileEntityTrommel extends TileEntity
         Direction direction = Direction.NONE;
         if (this.worldObj != null) {
             // 90-degree rotation CCW around the y-axis to get the direction of the output slot
-            switch (BlockLogicRotatable.getDirectionFromMeta(this.worldObj.getBlockMetadata(this.x, this.y, this.z))) {
+            switch (BlockLogicRotatable.getDirectionFromMeta(this.worldObj.getBlockMetadata(this.tilePos.x, this.tilePos.y, this.tilePos.z))) {
                 case NORTH: {
                     direction = Direction.WEST;
                     break;
@@ -309,9 +309,9 @@ public class TileEntityTrommel extends TileEntity
         {
         	// Check if chest is adjacent
 
-        	int adjacentId = this.worldObj != null ? this.worldObj.getBlockId(this.x + direction.getOffsetX(), this.y + direction.getOffsetY(), this.z + direction.getOffsetZ()) : 0;
+        	int adjacentId = this.worldObj != null ? this.worldObj.getBlockId(this.tilePos.x + direction.getOffsetX(), this.tilePos.y + direction.getOffsetY(), this.tilePos.z + direction.getOffsetZ()) : 0;
         	if (this.worldObj != null && Block.hasLogicClass(Blocks.blocksList[adjacentId], BlockLogicChest.class)) {
-                Container chest = BlockLogicChest.getInventory(this.worldObj, new TilePos(this.x + direction.getOffsetX(), this.y + direction.getOffsetY(), this.z + direction.getOffsetZ()));
+                Container chest = BlockLogicChest.getInventory(this.worldObj, new TilePos(this.tilePos.x + direction.getOffsetX(), this.tilePos.y + direction.getOffsetY(), this.tilePos.z + direction.getOffsetZ()));
 
                 for (int i = 0; i < chest.getContainerSize(); i++)
                 {
@@ -341,13 +341,13 @@ public class TileEntityTrommel extends TileEntity
 
             if(itemResult.stackSize > 0) {
                 if (this.worldObj != null) {
-                    if (this.worldObj.isAirBlock(new TilePos(this.x, this.y, this.z).add(direction))) {
-                        EntityItem droppedItem = new EntityItem(this.worldObj, this.x + 0.5 + direction.getOffsetX() * 0.5, this.y + 0.5 + direction.getOffsetY() * 0.5, this.z + 0.5 + direction.getOffsetZ() * 0.5, itemResult);
+                    if (this.worldObj.isAirBlock(new TilePos(this.tilePos.x, this.tilePos.y, this.tilePos.z).add(direction))) {
+                        EntityItem droppedItem = new EntityItem(this.worldObj, this.tilePos.x + 0.5 + direction.getOffsetX() * 0.5, this.tilePos.y + 0.5 + direction.getOffsetY() * 0.5, this.tilePos.z + 0.5 + direction.getOffsetZ() * 0.5, itemResult);
                         droppedItem.pickupDelay = 10;
                         droppedItem.fling(direction.getOffsetX() * 0.3125, direction.getOffsetY() * 0.3125, direction.getOffsetZ() * 0.3125, 1);
                         this.worldObj.entityJoinedWorld(droppedItem);
                     } else {
-                        this.worldObj.dropItem(this.x, this.y, this.z, itemResult);
+                        this.worldObj.dropItem(this.tilePos.x, this.tilePos.y, this.tilePos.z, itemResult);
                     }
                 } else if (this.carriedBlock != null) {
                     this.carriedBlock.world.dropItem(MathHelper.floor(this.carriedBlock.holder.x), MathHelper.floor(this.carriedBlock.holder.y), MathHelper.floor(this.carriedBlock.holder.z), itemResult);
@@ -366,11 +366,11 @@ public class TileEntityTrommel extends TileEntity
             mobSlime.yd = (float) this.random.nextGaussian() * f3 + 0.2F;
             mobSlime.zd = (float) this.random.nextGaussian() * f3;
             if (this.worldObj != null) {
-                if (this.worldObj.isAirBlock(new TilePos(this.x, this.y, this.z).add(direction))) {
-                    mobSlime.moveTo(this.x + 0.5 + direction.getOffsetX(), this.y + 0.5 + direction.getOffsetY(), this.z + 0.5 + direction.getOffsetZ(), this.random.nextFloat() * 360F, 0.0F);
+                if (this.worldObj.isAirBlock(new TilePos(this.tilePos.x, this.tilePos.y, this.tilePos.z).add(direction))) {
+                    mobSlime.moveTo(this.tilePos.x + 0.5 + direction.getOffsetX(), this.tilePos.y + 0.5 + direction.getOffsetY(), this.tilePos.z + 0.5 + direction.getOffsetZ(), this.random.nextFloat() * 360F, 0.0F);
                     mobSlime.fling(direction.getOffsetX() * 0.75, direction.getOffsetY() * 0.75, direction.getOffsetZ() * 0.75, 1);
                 } else {
-                    mobSlime.moveTo(this.x + (double)f, this.y + 1D, this.z + (double)f1, this.random.nextFloat() * 360F, 0.0F);
+                    mobSlime.moveTo(this.tilePos.x + (double)f, this.tilePos.y + 1D, this.tilePos.z + (double)f1, this.random.nextFloat() * 360F, 0.0F);
                 }
                 this.worldObj.entityJoinedWorld(mobSlime);
             } else if (this.carriedBlock != null) {
@@ -416,11 +416,11 @@ public class TileEntityTrommel extends TileEntity
     @Override
 	public boolean stillValid(@NotNull Player player)
     {
-        if(this.worldObj == null || this.worldObj.getTileEntity(this.x, this.y, this.z) != this)
+        if(this.worldObj == null || this.worldObj.getTileEntity(this.tilePos.x, this.tilePos.y, this.tilePos.z) != this)
         {
             return false;
         }
-        return player.distanceToSqr((double) this.x + 0.5D, (double) this.y + 0.5D, (double) this.z + 0.5D) <= 64D;
+        return player.distanceToSqr((double) this.tilePos.x + 0.5D, (double) this.tilePos.y + 0.5D, (double) this.tilePos.z + 0.5D) <= 64D;
     }
 
     @Override
diff --git a/game/core/src/main/java/net/minecraft/core/block/piston/TileEntityMovingPistonBlock.java b/game/core/src/main/java/net/minecraft/core/block/piston/TileEntityMovingPistonBlock.java
index e1c81ee17..c4dfde503 100644
--- a/game/core/src/main/java/net/minecraft/core/block/piston/TileEntityMovingPistonBlock.java
+++ b/game/core/src/main/java/net/minecraft/core/block/piston/TileEntityMovingPistonBlock.java
@@ -123,7 +123,7 @@ public class TileEntityMovingPistonBlock extends TileEntity
         } else {
             stretch = 1.0F - stretch;
         }
-        AABBdc aabb = Blocks.PISTON_MOVING.getLogic().getCollisionShapeFromTileEntity(this.worldObj, new TilePos(this.x, this.y, this.z), this.movedBlock, stretch, this.direction);
+        AABBdc aabb = Blocks.PISTON_MOVING.getLogic().getCollisionShapeFromTileEntity(this.worldObj, new TilePos(this.tilePos.x, this.tilePos.y, this.tilePos.z), this.movedBlock, stretch, this.direction);
         if (aabb == null) return;
 
         List<Entity> list = this.worldObj.getEntitiesWithinAABBExcludingEntity(null, aabb);
@@ -139,7 +139,7 @@ public class TileEntityMovingPistonBlock extends TileEntity
     }
 
     public void finalTick() {
-        TilePos selfPos = new TilePos(this.x, this.y, this.z);
+        TilePos selfPos = new TilePos(this.tilePos.x, this.tilePos.y, this.tilePos.z);
         assert this.worldObj != null;
 
         this.progressO = this.progress = 1.0F;
@@ -153,9 +153,9 @@ public class TileEntityMovingPistonBlock extends TileEntity
                     oldEnt.invalidate();
                 }
                 this.movedEntity.validate();
-                this.movedEntity.x = this.x;
-                this.movedEntity.y = this.y;
-                this.movedEntity.z = this.z;
+                this.movedEntity.tilePos.x = this.tilePos.x;
+                this.movedEntity.tilePos.y = this.tilePos.y;
+                this.movedEntity.tilePos.z = this.tilePos.z;
                 this.worldObj.replaceTileEntity(selfPos, this.movedEntity);
             }
 
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 b2ad0861b..0565659ba 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
@@ -19,11 +19,7 @@ import net.minecraft.core.enums.EnumBlockSoundEffectType;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.lang.I18n;
 import net.minecraft.core.util.collection.NamespaceID;
-import net.minecraft.core.util.helper.DamageType;
-import net.minecraft.core.util.helper.Direction;
-import net.minecraft.core.util.helper.DyeColor;
-import net.minecraft.core.util.helper.LightIndexHelper;
-import net.minecraft.core.util.helper.MathHelper;
+import net.minecraft.core.util.helper.*;
 import net.minecraft.core.world.ICarriable;
 import net.minecraft.core.world.IVehicle;
 import net.minecraft.core.world.World;
@@ -240,26 +236,28 @@ public abstract class Entity
         this.zo = this.z;
         this.xRotO = this.xRot;
         this.yRotO = this.yRot;
+
         // Removed at jonks request, should work though if it ever wants to be uncommented
-//        if (isSprinting())
-//        {
-//            int x = MathHelper.floor_double(this.x);
-//            int y = MathHelper.floor_double(this.y - 0.2 - (double)heightOffset);
-//            int z = MathHelper.floor_double(this.z);
-//            int floorBlock = world.getBlockId(x, y, z);
-//            int meta = world.getBlockMetadata(x, y, z);
-//            if (floorBlock > 0)
-//            {
-//                world.spawnParticle("block",
-//                    this.x + ((double)random.nextFloat() - 0.5D) * (double)bbWidth,
-//                    bb.minY + 0.1,
-//                    this.z + ((double)random.nextFloat() - 0.5D) * (double)bbWidth,
-//                    -xd * 4D,
-//                    1.5D,
-//                    -zd * 4D,
-//                        BlockParticleHelper.encodeBlockData(floorBlock, meta, Side.TOP));
-//            }
-//        }
+        // ^ IDK bro just crashes the game now
+/*        if (isSprinting())
+        {
+            int x = MathHelper.floor(this.x);
+            int y = MathHelper.floor(this.y - 0.2 - (double)heightOffset);
+            int z = MathHelper.floor(this.z);
+            int floorBlock = world.getBlockId(x, y, z);
+            int meta = world.getBlockMetadata(x, y, z);
+            if (floorBlock > 0)
+            {
+                world.spawnParticle("block",
+                    this.x + ((double)random.nextFloat() - 0.5D) * (double)bbWidth,
+                    bb.minY + 0.1,
+                    this.z + ((double)random.nextFloat() - 0.5D) * (double)bbWidth,
+                    -xd * 4D,
+                    1.5D,
+                    -zd * 4D,
+                        BlockParticleHelper.encodeBlockData(floorBlock, meta, Side.TOP));
+            }
+        }*/
 
         checkOnWater(true);
 
diff --git a/game/core/src/main/java/net/minecraft/core/entity/EntityFallingBlock.java b/game/core/src/main/java/net/minecraft/core/entity/EntityFallingBlock.java
index d16e36dfc..4b483ea9f 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/EntityFallingBlock.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/EntityFallingBlock.java
@@ -164,9 +164,9 @@ public class EntityFallingBlock extends Entity
                             oldEnt.invalidate();
                         }
                         this.carriedBlock.entity.validate();
-                        this.carriedBlock.entity.x = blockPos.x();
-                        this.carriedBlock.entity.y = blockPos.y();
-                        this.carriedBlock.entity.z = blockPos.z();
+                        this.carriedBlock.entity.tilePos.x = blockPos.x();
+                        this.carriedBlock.entity.tilePos.y = blockPos.y();
+                        this.carriedBlock.entity.tilePos.z = blockPos.z();
                         this.carriedBlock.entity.worldObj = this.world;
                         this.carriedBlock.entity.carriedBlock = null;
                         this.world.replaceTileEntity(blockPos, this.carriedBlock.entity);
diff --git a/game/core/src/main/java/net/minecraft/core/entity/EntityItem.java b/game/core/src/main/java/net/minecraft/core/entity/EntityItem.java
index 18c3dc302..3e1a09e98 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/EntityItem.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/EntityItem.java
@@ -1,6 +1,7 @@
 package net.minecraft.core.entity;
 
 import java.util.List;
+import java.util.Objects;
 
 import com.mojang.nbt.tags.CompoundTag;
 import net.minecraft.core.achievement.Achievements;
@@ -8,6 +9,8 @@ import net.minecraft.core.achievement.stat.StatList;
 import net.minecraft.core.block.Block;
 import net.minecraft.core.block.BlockLogicLog;
 import net.minecraft.core.block.Blocks;
+import net.minecraft.core.block.entity.TileEntityMesh;
+import net.minecraft.core.block.entity.TileEntityMeshGold;
 import net.minecraft.core.block.material.Materials;
 import net.minecraft.core.data.gamerule.GameRules;
 import net.minecraft.core.entity.player.Player;
@@ -16,6 +19,7 @@ import net.minecraft.core.item.Items;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.item.tag.ItemTags;
 import net.minecraft.core.util.helper.DamageType;
+import net.minecraft.core.util.helper.Direction;
 import net.minecraft.core.util.helper.MathHelper;
 import net.minecraft.core.world.World;
 import net.minecraft.core.world.pos.TilePos;
@@ -27,12 +31,13 @@ public class EntityItem extends Entity
     public ItemStack item;
     public int lifetime;
     public int age;
+    public Direction siphonDirection = Direction.NONE;
+    public TileEntityMesh meshSiphoning = null;
     public int pickupDelay;
     private int health;
     public float bobOffset;
     public int basketPickupDelay = 0;
     public static boolean enableItemClumping = true;
-    private final TilePos queryPos = new TilePos();
 
     public EntityItem(@NotNull World world, double x, double y, double z, @NotNull ItemStack itemstack) {
         super(world);
@@ -95,37 +100,25 @@ public class EntityItem extends Entity
         {
             this.pickupDelay--;
         }
-        boolean inLava = isInLava();
+
+        final TilePos tilePosUnderItem = new TilePos(this.x, this.y - 1, this.z);
+        final TilePos tilePosItemIsInside = new TilePos(this.x, this.y, this.z);
+        final @NotNull Block<?> blockUnderItem = this.world.getBlockType(tilePosUnderItem);
+        final @NotNull Block<?> blockItemIsIn = this.world.getBlockType(tilePosItemIsInside);
+
+        boolean isInFlowingWater = false;
+        if(blockItemIsIn == Blocks.FLUID_WATER_STILL)
+        {
+            isInFlowingWater = Blocks.FLUID_WATER_STILL.getLogic().hasFlowVector(this.world, new TilePos(this.x, this.y, this.z));
+        }
+
+        handleFriction(blockUnderItem, blockItemIsIn, isInFlowingWater);
+        handleBuoyancy(blockUnderItem, tilePosUnderItem, isInFlowingWater);
+
         this.xo = this.x;
         this.yo = this.y;
         this.zo = this.z;
-        boolean throughBelow = !this.world.getBlockType(this.queryPos.set(MathHelper.floor(this.x), MathHelper.floor(this.y) - 1, MathHelper.floor(this.z))).collidesWithEntity(this, this.world, this.queryPos);
-        if (this.item.getItem().hasTag(ItemTags.IS_FIRE_PROOF)) {
-            if (((this.wasInWater || inLava) && !throughBelow)) {
-                this.yd += 0.02D;
-            } else if (this.wasInWater || inLava){
-                this.yd += (-0.04D * 3.5);
-            } else {
-                this.yd += (-0.04D * 1);
-            }
-        } else {
-
-            if ((this.wasInWater && !throughBelow)) {
-                this.yd += 0.02D;
-            } else if (this.wasInWater) {
-                this.yd += (-0.04D * 3.5);
-            } else {
-                this.yd += (-0.04D * 1);
-            }
 
-            if(isInLava())
-            {
-                this.yd = 0.2D;
-                this.xd = (this.random.nextFloat() - this.random.nextFloat()) * 0.2F;
-                this.zd = (this.random.nextFloat() - this.random.nextFloat()) * 0.2F;
-                this.world.playSoundAtEntity(null, this, "random.fizz", 0.4F, 2.0F + this.random.nextFloat() * 0.4F);
-            }
-        }
         checkAndPushInTile(this.x, (this.bb.minY + this.bb.maxY) / 2D, this.z);
         move(this.xd, this.yd, this.zd);
         
@@ -136,34 +129,7 @@ public class EntityItem extends Entity
                 }		
         	}
         }
-        
-        float friction = 0.98F;
-        if(this.onGround)
-        {
-            friction = 0.588F;
-            int blockId = this.world.getBlockId(MathHelper.floor(this.x), MathHelper.floor(this.bb.minY) - 1, MathHelper.floor(this.z));
-            if(blockId > 0)
-            {
-                friction = Blocks.blocksList[blockId].friction * 0.98F;
-            }
-        }
-        this.xd *= friction;
-        this.yd *= 0.98D;
-        this.zd *= friction;
-
-        if (this.wasInWater)
-        {
-            if(this.yd < 0.0f) {
-                this.xd *= 0.95f;
-                this.yd *= 0.45f;
-                this.zd *= 0.95f;
-            }
-        }
 
-        if(this.onGround)
-        {
-            this.yd *= -0.5D;
-        }
         this.age++;
         if(this.age >= this.lifetime && this.lifetime >= 0 && !this.item.getItem().hasTag(ItemTags.IS_PERSISTENT)) // Negative lifetime indicates items should last forever
         {
@@ -323,6 +289,114 @@ public class EntityItem extends Entity
     		}
     	}
     }
+
+    private void handleFriction(@NotNull Block<?> blockUnderItem, @NotNull Block<?> blockItemIsIn, boolean isInFlowingWater){
+
+        //All mentions of friction in comments use the ENGLISH sense of the word, as for some reason
+        //  adding friction to any value in game actually LOWERS the literal friction the item is receiving, and vice versa
+        float friction = 0.98F;
+
+        //Set friction of item to the block below it (unless in air)
+        if(this.onGround)
+        {
+            friction = 0.588F;
+            if(blockUnderItem.getMaterial() != Materials.AIR){
+                friction = blockUnderItem.friction;
+            }
+        }
+
+        //Items in streams of water get their friction set to the block underneath them/the stream.
+        //All blocks get are given significantly less friction, and ice is given slightly less friction to
+        //  improve the efficiency/speed of each respective water stream
+        if(isInFlowingWater)
+        {
+            if(blockUnderItem != Blocks.FLUID_WATER_STILL)
+            {
+                friction = blockUnderItem.friction;
+            }
+
+            if(blockUnderItem.getMaterial() != Materials.ICE)
+            {
+                friction += 0.3f;
+            }
+            else
+            {
+                friction *= 1.005f;
+            }
+        }
+
+        this.xd *= friction;
+        this.yd *= 0.98D;
+        this.zd *= friction;
+
+    }
+
+    private void handleBuoyancy(Block<?> blockUnderItem, TilePos tilePosUnderItem, boolean inFlowingLiquid){
+
+        double midpoint = this.y % 1d + 0.14;
+        float fluidHeight = Blocks.FLUID_WATER_STILL.getLogic().getFluidHeight(this.world, new TilePos(this.x, this.y, this.z));
+
+        //Adjust the height at which the item believes itself to be in the water for buoyancy purposes
+        //  (so that the bobbing height is closer to the center of its bb)
+        boolean isInWater = false;
+        if(fluidHeight > midpoint || fluidHeight == 1){
+            isInWater = this.isInWater();
+        }
+
+        //self-explanatory :wolfblock:
+        if (isInWater)
+        {
+            //gradually slows the item down laterally
+            this.xd *= 0.98f;
+            this.zd *= 0.98f;
+
+            //if not in flowing liquid, float upwards gradually
+            if(!inFlowingLiquid)
+            {
+                this.yd += 0.001;
+            }
+
+            if(blockUnderItem == Blocks.MESH)
+            {
+                this.yd -= 0.02D;
+            }
+
+            if(blockUnderItem == Blocks.MESH_GOLD)
+            {
+                ItemStack filterItem = ((TileEntityMeshGold) Objects.requireNonNull(world.getTileEntity(tilePosUnderItem))).filterItem;
+                if(filterItem != null){
+                    if (this.item.getItem() == filterItem.getItem() && this.item.getMetadata() == filterItem.getMetadata()){
+                        this.yd -= 0.02D;
+                    }
+                }
+            }
+        }
+        //gravity out of water
+        else
+        {
+            this.yd -= 0.04D;
+        }
+
+
+
+/*        if (item.getItem().hasTag(ItemTags.IS_FIRE_PROOF)) {
+            this.yd += (this.wasInWater || inLava) ? 0.001D : -0.04D;
+        } else {
+            this.yd += this.wasInWater ? 0.001D : -0.04D;
+
+            if(isInLava())
+            {
+                this.yd = 0.2D;
+                this.xd = (this.random.nextFloat() - this.random.nextFloat()) * 0.2F;
+                this.zd = (this.random.nextFloat() - this.random.nextFloat()) * 0.2F;
+                this.world.playSoundAtEntity(null, this, "random.fizz", 0.4F, 2.0F + this.random.nextFloat() * 0.4F);
+            }
+        }*/
+    }
+
+    public boolean isBeingSiphoned(){
+        return siphonDirection != Direction.NONE;
+    }
     
     private static void combineItems(EntityItem entityItem1, EntityItem entityItem2) {
     	entityItem1.item.stackSize += entityItem2.item.stackSize;
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 f84c19180..7ae6a7b18 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
@@ -1491,12 +1491,12 @@ public abstract class World implements MutableWorldSource {
         this.updatingTileEntities = true;
         for (int i = this.tileEntityList.size() - 1; i >= 0; i--) {
             final TileEntity tileEntity = this.tileEntityList.get(i);
-            if (!tileEntity.isInvalid() && this.isBlockLoaded(tileEntity.x, tileEntity.y, tileEntity.z)) {
+            if (!tileEntity.isInvalid() && this.isBlockLoaded(tileEntity.tilePos.x, tileEntity.tilePos.y, tileEntity.tilePos.z)) {
                 tileEntity.tick();
             }
             if (tileEntity.isInvalid()) {
                 this.tileEntityList.remove(i);
-                final Chunk chunk = this.getChunkFromChunkCoords(Math.floorDiv(tileEntity.x, Chunk.CHUNK_SIZE_X), Math.floorDiv(tileEntity.z, Chunk.CHUNK_SIZE_Z));
+                final Chunk chunk = this.getChunkFromChunkCoords(Math.floorDiv(tileEntity.tilePos.x, Chunk.CHUNK_SIZE_X), Math.floorDiv(tileEntity.tilePos.z, Chunk.CHUNK_SIZE_Z));
                 if (chunk != null) {
                     chunk.removeTileEntity(tileEntity);
                 }
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 472c9b5ed..467d406c1 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
@@ -609,7 +609,7 @@ public class Chunk {
     }
 
     public void addTileEntity(final @NotNull TileEntity tileEntity) {
-        final @NotNull TilePos tilePos = new TilePos(tileEntity.x, tileEntity.y, tileEntity.z);
+        final @NotNull TilePos tilePos = new TilePos(tileEntity.tilePos.x, tileEntity.tilePos.y, tileEntity.tilePos.z);
 
         this.setTileEntity(new ChunkTilePos(tilePos), tileEntity);
         if (this.isLoaded) {
@@ -624,9 +624,9 @@ public class Chunk {
         final @NotNull TilePos worldPos = new TilePos(this.pos, tilePos);
 
         tileEntity.worldObj = this.world;
-        tileEntity.x = worldPos.x;
-        tileEntity.y = worldPos.y;
-        tileEntity.z = worldPos.z;
+        tileEntity.tilePos.x = worldPos.x;
+        tileEntity.tilePos.y = worldPos.y;
+        tileEntity.tilePos.z = worldPos.z;
 
         final @Nullable Block<?> block = this.getBlock(tilePos);
         if (!block.isEntityTile) {
@@ -669,7 +669,7 @@ public class Chunk {
 
     public void removeTileEntity(@Nullable TileEntity tileEntity) {
         if (this.isLoaded && tileEntity != null && this.tileEntityMap.containsValue(tileEntity)) {
-            final @NotNull ChunkTilePos tilePos = new ChunkTilePos(tileEntity.x, tileEntity.y, tileEntity.z);
+            final @NotNull ChunkTilePos tilePos = new ChunkTilePos(tileEntity.tilePos.x, tileEntity.tilePos.y, tileEntity.tilePos.z);
             this.tileEntityMap.remove(tilePos);
             tileEntity.invalidate();
         }
diff --git a/game/core/src/main/java/net/minecraft/core/world/chunk/DisplayChunk.java b/game/core/src/main/java/net/minecraft/core/world/chunk/DisplayChunk.java
index bf17d9bf7..b61afb705 100644
--- a/game/core/src/main/java/net/minecraft/core/world/chunk/DisplayChunk.java
+++ b/game/core/src/main/java/net/minecraft/core/world/chunk/DisplayChunk.java
@@ -53,9 +53,9 @@ public class DisplayChunk extends EmptyChunk{
         if (tilePos.x() == displayBlockPosX && tilePos.y() == displayBlockPosY && tilePos.z() == displayBlockPosZ) {
             displayTileEntity = tileEntity;
             displayTileEntity.worldObj = this.world;
-            displayTileEntity.x = tilePos.x();
-            displayTileEntity.y = tilePos.y();
-            displayTileEntity.z = tilePos.z();
+            displayTileEntity.tilePos.x = tilePos.x();
+            displayTileEntity.tilePos.y = tilePos.y();
+            displayTileEntity.tilePos.z = tilePos.z();
         }
 
         return true;
@@ -81,9 +81,9 @@ public class DisplayChunk extends EmptyChunk{
         displayTileEntity = entity;
         if (displayTileEntity != null){
             displayTileEntity.worldObj = world;
-            displayTileEntity.x = DisplayChunk.displayBlockPosX;
-            displayTileEntity.y = DisplayChunk.displayBlockPosY;
-            displayTileEntity.z = DisplayChunk.displayBlockPosZ;
+            displayTileEntity.tilePos.x = DisplayChunk.displayBlockPosX;
+            displayTileEntity.tilePos.y = DisplayChunk.displayBlockPosY;
+            displayTileEntity.tilePos.z = DisplayChunk.displayBlockPosZ;
         }
     }
 
diff --git a/game/core/src/main/java/net/minecraft/core/world/saveddata/maps/ItemMapSavedData.java b/game/core/src/main/java/net/minecraft/core/world/saveddata/maps/ItemMapSavedData.java
index ce758471b..866a571e9 100644
--- a/game/core/src/main/java/net/minecraft/core/world/saveddata/maps/ItemMapSavedData.java
+++ b/game/core/src/main/java/net/minecraft/core/world/saveddata/maps/ItemMapSavedData.java
@@ -170,8 +170,8 @@ public class ItemMapSavedData extends SavedData
         if(mapWaypoints.size() >= MAX_WAYPOINTS)
             return FlagError.FULL;
 
-        float xd = (float)(flag.x - (double) x) / (float)(1 << scale);
-        float zd = (float)(flag.z - (double) z) / (float)(1 << scale);
+        float xd = (float)(flag.tilePos.x - (double) x) / (float)(1 << scale);
+        float zd = (float)(flag.tilePos.z - (double) z) / (float)(1 << scale);
         int maxWidth = 64;
         int maxHeight = 64;
 
@@ -191,7 +191,7 @@ public class ItemMapSavedData extends SavedData
             }
         }
 
-        mapWaypoints.add(new MapWaypoint(xCoord, zCoord, flag.x, flag.y, flag.z, colors));
+        mapWaypoints.add(new MapWaypoint(xCoord, zCoord, flag.tilePos.x, flag.tilePos.y, flag.tilePos.z, colors));
         return FlagError.NONE;
     }
 
diff --git a/game/core/src/main/resources/lang/en_US/stats.lang b/game/core/src/main/resources/lang/en_US/stats.lang
index 8543920a4..1fafe463e 100644
--- a/game/core/src/main/resources/lang/en_US/stats.lang
+++ b/game/core/src/main/resources/lang/en_US/stats.lang
@@ -52,6 +52,8 @@ achievement.buildFurnace=Hot Topic
 achievement.buildFurnace.desc=Construct a furnace out of eight stone blocks.
 achievement.acquireIron=Acquire Hardware
 achievement.acquireIron.desc=Smelt an iron ingot.
+achievement.downTheDrain=Down the Drain
+achievement.downTheDrain.desc=Use a mesh block underwater to siphon items.
 achievement.buildHoe=Time to Farm!
 achievement.buildHoe.desc=Use planks and sticks to make a hoe.
 achievement.makeBread=Bake Bread
diff --git a/game/server/src/main/java/net/minecraft/server/entity/player/PlayerServer.java b/game/server/src/main/java/net/minecraft/server/entity/player/PlayerServer.java
index b031a1053..e19d0734f 100644
--- a/game/server/src/main/java/net/minecraft/server/entity/player/PlayerServer.java
+++ b/game/server/src/main/java/net/minecraft/server/entity/player/PlayerServer.java
@@ -469,7 +469,7 @@ public class PlayerServer extends Player implements ContainerListener {
             playerNetServerHandler.sendPacket(new PacketSetRiding(this, (Entity) this.vehicle));
         } else if (vehicle instanceof TileEntity) {
             TileEntity tileEntity = (TileEntity) vehicle;
-            playerNetServerHandler.sendPacket(new PacketSetRiding(this, tileEntity.x, tileEntity.y, tileEntity.z));
+            playerNetServerHandler.sendPacket(new PacketSetRiding(this, tileEntity.tilePos.x, tileEntity.tilePos.y, tileEntity.tilePos.z));
         }
         playerNetServerHandler.teleport(x, y, z);
     }
@@ -581,7 +581,7 @@ public class PlayerServer extends Player implements ContainerListener {
     public void displayFlagEditorScreen(TileEntityFlag tileEntity)
     {
         getNextWindowId();
-        playerNetServerHandler.sendPacket(new PacketFlagOpen(currentWindowId, tileEntity.x, tileEntity.y, tileEntity.z));
+        playerNetServerHandler.sendPacket(new PacketFlagOpen(currentWindowId, tileEntity.tilePos.x, tileEntity.tilePos.y, tileEntity.tilePos.z));
         containerMenu.onCraftGuiClosed(this);
         containerMenu = new MenuFlag(inventory, tileEntity);
         containerMenu.containerId = currentWindowId;
diff --git a/game/server/src/main/java/net/minecraft/server/net/handler/PacketHandlerServer.java b/game/server/src/main/java/net/minecraft/server/net/handler/PacketHandlerServer.java
index 4d3a15a6e..c2a475017 100644
--- a/game/server/src/main/java/net/minecraft/server/net/handler/PacketHandlerServer.java
+++ b/game/server/src/main/java/net/minecraft/server/net/handler/PacketHandlerServer.java
@@ -761,12 +761,12 @@ public class PacketHandlerServer extends PacketHandler
                     System.arraycopy(packetCustomPayload.data, 1, flag.flagColors, 0, TileEntityFlag.CANVAS_WIDTH * TileEntityFlag.CANVAS_HEIGHT);
                     flag.setFlipped(flipped);
 
-                    this.playerEntity.world.markBlockNeedsUpdate(flag.x, flag.y, flag.z);
+                    this.playerEntity.world.markBlockNeedsUpdate(flag.tilePos.x, flag.tilePos.y, flag.tilePos.z);
                 } else {
                     LOGGER.warn("Player '{}' tried editing a flag that belongs to '{}'!", this.playerEntity.username, flag.owner);
 
                     // Let the client know that the flag design has been rejected
-                    this.playerEntity.world.markBlockNeedsUpdate(flag.x, flag.y, flag.z);
+                    this.playerEntity.world.markBlockNeedsUpdate(flag.tilePos.x, flag.tilePos.y, flag.tilePos.z);
                 }
             }
         } else if (PacketCustomPayload.CHANNEL_WAND_MONSTER.equals(packetCustomPayload.channel)) {
diff --git a/game/server/src/main/java/net/minecraft/server/world/WorldServer.java b/game/server/src/main/java/net/minecraft/server/world/WorldServer.java
index 869c23985..eb17bae0a 100644
--- a/game/server/src/main/java/net/minecraft/server/world/WorldServer.java
+++ b/game/server/src/main/java/net/minecraft/server/world/WorldServer.java
@@ -84,7 +84,7 @@ public class WorldServer extends World {
         for(int i = 0; i < tileEntityList.size(); i++)
         {
             TileEntity tileEntity = tileEntityList.get(i);
-            if(tileEntity.x >= minX && tileEntity.y >= minY && tileEntity.z >= minZ && tileEntity.x < maxX && tileEntity.y < maxY && tileEntity.z < maxZ)
+            if(tileEntity.tilePos.x >= minX && tileEntity.tilePos.y >= minY && tileEntity.tilePos.z >= minZ && tileEntity.tilePos.x < maxX && tileEntity.tilePos.y < maxY && tileEntity.tilePos.z < maxZ)
             {
                 arraylist.add(tileEntity);
             }
