diff --git a/game/client/src/main/java/net/minecraft/client/Minecraft.java b/game/client/src/main/java/net/minecraft/client/Minecraft.java
index bbe419bee..552af5c17 100644
--- a/game/client/src/main/java/net/minecraft/client/Minecraft.java
+++ b/game/client/src/main/java/net/minecraft/client/Minecraft.java
@@ -121,35 +121,7 @@ import net.minecraft.core.lang.I18n;
 import net.minecraft.core.net.PlayerProfile;
 import net.minecraft.core.net.command.CommandManager;
 import net.minecraft.core.net.command.TextFormatting;
-import net.minecraft.core.net.command.commands.CommandAchievement;
-import net.minecraft.core.net.command.commands.CommandBiome;
-import net.minecraft.core.net.command.commands.CommandChunk;
-import net.minecraft.core.net.command.commands.CommandClear;
-import net.minecraft.core.net.command.commands.CommandClone;
-import net.minecraft.core.net.command.commands.CommandDamage;
-import net.minecraft.core.net.command.commands.CommandFill;
-import net.minecraft.core.net.command.commands.CommandGameMode;
-import net.minecraft.core.net.command.commands.CommandGameRule;
-import net.minecraft.core.net.command.commands.CommandGive;
-import net.minecraft.core.net.command.commands.CommandHeal;
-import net.minecraft.core.net.command.commands.CommandHelp;
-import net.minecraft.core.net.command.commands.CommandKill;
-import net.minecraft.core.net.command.commands.CommandMessage;
-import net.minecraft.core.net.command.commands.CommandMobSpawning;
-import net.minecraft.core.net.command.commands.CommandParticle;
-import net.minecraft.core.net.command.commands.CommandPlace;
-import net.minecraft.core.net.command.commands.CommandPlaySound;
-import net.minecraft.core.net.command.commands.CommandSay;
-import net.minecraft.core.net.command.commands.CommandSeed;
-import net.minecraft.core.net.command.commands.CommandSetBlock;
-import net.minecraft.core.net.command.commands.CommandSetSpawn;
-import net.minecraft.core.net.command.commands.CommandSpawn;
-import net.minecraft.core.net.command.commands.CommandSummon;
-import net.minecraft.core.net.command.commands.CommandTeleport;
-import net.minecraft.core.net.command.commands.CommandTellRaw;
-import net.minecraft.core.net.command.commands.CommandTestFor;
-import net.minecraft.core.net.command.commands.CommandTime;
-import net.minecraft.core.net.command.commands.CommandWeather;
+import net.minecraft.core.net.command.commands.*;
 import net.minecraft.core.net.command.util.CommandHelper;
 import net.minecraft.core.player.Session;
 import net.minecraft.core.player.gamemode.Gamemode;
@@ -703,6 +675,7 @@ public class Minecraft
         CommandManager.registerCommand(new CommandMobSpawning());
         CommandManager.registerCommand(new CommandPlaySound());
         CommandManager.registerCommand(new CommandParticle());
+        CommandManager.registerCommand(new CommandStructure());
     }
 
     public @NotNull Matrix4f getGuiProjectionMatrix(@NotNull Matrix4f dest) {
diff --git a/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelDispatcher.java b/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelDispatcher.java
index 6d4620437..b05d1763a 100644
--- a/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelDispatcher.java
+++ b/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelDispatcher.java
@@ -566,6 +566,8 @@ public final class BlockModelDispatcher
         addDispatch(new BlockModelGenericSlab<>(Blocks.SLAB_BRICK_GLOOMSTONE, loadDataModel("minecraft:block/slab/brick_gloomstone/lower"), loadDataModel("minecraft:block/slab/brick_gloomstone/upper"), loadDataModel("minecraft:block/slab/brick_gloomstone/full")));
         addDispatch(new BlockModelGenericStairs<>(Blocks.STAIRS_COBBLE_GLOOMSTONE, loadDataModel("minecraft:block/stairs/cobbled_gloomstone")));
         addDispatch(new BlockModelGenericStairs<>(Blocks.STAIRS_BRICK_GLOOMSTONE, loadDataModel("minecraft:block/stairs/brick_gloomstone")));
+
+        addDispatch(new BlockModelGenericFlowstone<>(Blocks.FLOWSTONE, "minecraft:block/flowstone"));
     }
 }
 
diff --git a/game/client/src/main/java/net/minecraft/client/render/block/model/generic/BlockModelGenericFlowstone.java b/game/client/src/main/java/net/minecraft/client/render/block/model/generic/BlockModelGenericFlowstone.java
new file mode 100644
index 000000000..b9b41076c
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/block/model/generic/BlockModelGenericFlowstone.java
@@ -0,0 +1,36 @@
+package net.minecraft.client.render.block.model.generic;
+
+import net.minecraft.client.render.block.model.BlockModelDispatcher;
+import net.minecraft.client.render.tessellator.TessellatorGeneral;
+import net.minecraft.client.render.texture.stitcher.IconCoordinate;
+import net.minecraft.core.block.Block;
+import net.minecraft.core.block.BlockLogicFlowstone;
+import net.minecraft.core.world.WorldSource;
+import net.minecraft.core.world.pos.TilePosc;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.useless.dragonfly.models.block.StaticBlockModel;
+
+public class BlockModelGenericFlowstone<T extends BlockLogicFlowstone> extends BlockModelGeneric<T> {
+    public final @NotNull StaticBlockModel[] sizes;
+
+    public BlockModelGenericFlowstone(final @NotNull Block<T> block, final @NotNull String baseKey) {
+        super(block, BlockModelDispatcher.loadDataModel(baseKey + "/16"));
+
+        this.sizes = new StaticBlockModel[] {
+            BlockModelDispatcher.loadDataModel(baseKey + "/16").asModel(),
+            BlockModelDispatcher.loadDataModel(baseKey + "/12").asModel(),
+            BlockModelDispatcher.loadDataModel(baseKey + "/8").asModel(),
+            BlockModelDispatcher.loadDataModel(baseKey + "/4").asModel(),
+        };
+    }
+
+    @Override
+    public @NotNull StaticBlockModel getModelFromData(final int data) {
+        if (data < this.sizes.length) {
+            return this.sizes[data];
+        } else {
+            return this.sizes[0];
+        }
+    }
+}
diff --git a/game/client/src/main/java/net/minecraft/client/render/renderer/Shaders.java b/game/client/src/main/java/net/minecraft/client/render/renderer/Shaders.java
index a7501c872..e4c1bf876 100644
--- a/game/client/src/main/java/net/minecraft/client/render/renderer/Shaders.java
+++ b/game/client/src/main/java/net/minecraft/client/render/renderer/Shaders.java
@@ -52,7 +52,7 @@ public final class Shaders {
                 entry.getValue().bind();
                 entry.getValue().uniformInt("colortex", 0);
                 entry.getValue().uniformInt("lighttex", 1);
-                entry.getValue().uniformInt("emmisivetex", 2);
+                entry.getValue().uniformInt("emissivetex", 2);
                 entry.getValue().uniformInt("colormasktex", 3);
                 GL41.glUseProgram(0);
             } catch (Exception e) {
@@ -74,7 +74,7 @@ public final class Shaders {
                 GL41.glUniform1i(entry.getValue().getUniform("lighttex"), 1);
                 entry.getValue().uniformInt("colortex", 0);
                 entry.getValue().uniformInt("lighttex", 1);
-                entry.getValue().uniformInt("emmisivetex", 2);
+                entry.getValue().uniformInt("emissivetex", 2);
                 entry.getValue().uniformInt("colormasktex", 3);
                 GL41.glUseProgram(0);
             } catch (Exception e) {
diff --git a/game/client/src/main/resources/assets/minecraft/models/block/flowstone/12.json b/game/client/src/main/resources/assets/minecraft/models/block/flowstone/12.json
new file mode 100644
index 000000000..ccb5c324f
--- /dev/null
+++ b/game/client/src/main/resources/assets/minecraft/models/block/flowstone/12.json
@@ -0,0 +1,21 @@
+{
+  "textures": {
+    "side": "minecraft:block/flowstone/side",
+    "end": "minecraft:block/flowstone/end",
+    "particle": "#side"
+  },
+  "elements": [
+    {
+      "from": [ 2, 0, 2 ],
+      "to": [ 14, 16, 14 ],
+      "faces": {
+        "down":  { "uv": [ 2, 2, 14, 14 ], "texture": "#end", "cullface": "down" },
+        "up":    { "uv": [ 2, 2, 14, 14 ], "texture": "#end", "cullface": "up" },
+        "north": { "uv": [ 2, 0, 14, 16 ], "texture": "#side" },
+        "south": { "uv": [ 2, 0, 14, 16 ], "texture": "#side" },
+        "west":  { "uv": [ 2, 0, 14, 16 ], "texture": "#side" },
+        "east":  { "uv": [ 2, 0, 14, 16 ], "texture": "#side" }
+      }
+    }
+  ]
+}
diff --git a/game/client/src/main/resources/assets/minecraft/models/block/flowstone/16.json b/game/client/src/main/resources/assets/minecraft/models/block/flowstone/16.json
new file mode 100644
index 000000000..8607b25bc
--- /dev/null
+++ b/game/client/src/main/resources/assets/minecraft/models/block/flowstone/16.json
@@ -0,0 +1,7 @@
+{
+  "parent": "minecraft:block/cube_column",
+  "textures": {
+    "side": "minecraft:block/flowstone/side",
+    "end": "minecraft:block/flowstone/end"
+  }
+}
diff --git a/game/client/src/main/resources/assets/minecraft/models/block/flowstone/4.json b/game/client/src/main/resources/assets/minecraft/models/block/flowstone/4.json
new file mode 100644
index 000000000..a46c9a4c3
--- /dev/null
+++ b/game/client/src/main/resources/assets/minecraft/models/block/flowstone/4.json
@@ -0,0 +1,21 @@
+{
+  "textures": {
+    "side": "minecraft:block/flowstone/side",
+    "end": "minecraft:block/flowstone/end",
+    "particle": "#side"
+  },
+  "elements": [
+    {
+      "from": [ 6, 0, 6 ],
+      "to": [ 10, 16, 10 ],
+      "faces": {
+        "down":  { "uv": [ 6, 6, 10, 10 ], "texture": "#end", "cullface": "down" },
+        "up":    { "uv": [ 6, 6, 10, 10 ], "texture": "#end", "cullface": "up" },
+        "north": { "uv": [ 6, 0, 10, 16 ], "texture": "#side" },
+        "south": { "uv": [ 6, 0, 10, 16 ], "texture": "#side" },
+        "west":  { "uv": [ 6, 0, 10, 16 ], "texture": "#side" },
+        "east":  { "uv": [ 6, 0, 10, 16 ], "texture": "#side" }
+      }
+    }
+  ]
+}
diff --git a/game/client/src/main/resources/assets/minecraft/models/block/flowstone/8.json b/game/client/src/main/resources/assets/minecraft/models/block/flowstone/8.json
new file mode 100644
index 000000000..a82f19691
--- /dev/null
+++ b/game/client/src/main/resources/assets/minecraft/models/block/flowstone/8.json
@@ -0,0 +1,21 @@
+{
+  "textures": {
+    "side": "minecraft:block/flowstone/side",
+    "end": "minecraft:block/flowstone/end",
+    "particle": "#side"
+  },
+  "elements": [
+    {
+      "from": [ 4, 0, 4 ],
+      "to": [ 12, 16, 12 ],
+      "faces": {
+        "down":  { "uv": [ 4, 4, 12, 12 ], "texture": "#end", "cullface": "down" },
+        "up":    { "uv": [ 4, 4, 12, 12 ], "texture": "#end", "cullface": "up" },
+        "north": { "uv": [ 4, 0, 12, 16 ], "texture": "#side" },
+        "south": { "uv": [ 4, 0, 12, 16 ], "texture": "#side" },
+        "west":  { "uv": [ 4, 0, 12, 16 ], "texture": "#side" },
+        "east":  { "uv": [ 4, 0, 12, 16 ], "texture": "#side" }
+      }
+    }
+  ]
+}
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/flowstone/end.png b/game/client/src/main/resources/assets/minecraft/textures/block/flowstone/end.png
new file mode 100644
index 000000000..906205893
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/flowstone/end.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/flowstone/side.png b/game/client/src/main/resources/assets/minecraft/textures/block/flowstone/side.png
new file mode 100644
index 000000000..c17fb3b15
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/flowstone/side.png differ
diff --git a/game/client/src/main/resources/shaders/item.fsh b/game/client/src/main/resources/shaders/item.fsh
index 03bdcc37c..14117c218 100644
--- a/game/client/src/main/resources/shaders/item.fsh
+++ b/game/client/src/main/resources/shaders/item.fsh
@@ -29,7 +29,7 @@ layout (std140) uniform Matrices {
 
 uniform sampler2D colortex;
 uniform sampler2D lighttex;
-uniform sampler2D emmisivetex;
+uniform sampler2D emissivetex;
 uniform sampler2D colormasktex;
 
 float distance() {
@@ -70,8 +70,8 @@ vec4 computeFog(vec4 color) {
 }
 
 void main() {
-    vec4 emmisiveColor = texture(emmisivetex, TexCoord);
-    vec4 lightColor = texture(lighttex, vec2(max(LightMapCoord.x, emmisiveColor.r), max(LightMapCoord.y, emmisiveColor.g)));
+    vec4 emissiveColor = texture(emissivetex, TexCoord);
+    vec4 lightColor = texture(lighttex, vec2(max(LightMapCoord.x, emissiveColor.r), max(LightMapCoord.y, emissiveColor.g)));
     vec4 texColor = texture(colortex, TexCoord);
     vec4 maskColor = texture(colormasktex, TexCoord);
 
diff --git a/game/client/src/main/resources/shaders/particle.fsh b/game/client/src/main/resources/shaders/particle.fsh
index f7a726d72..eff39b487 100644
--- a/game/client/src/main/resources/shaders/particle.fsh
+++ b/game/client/src/main/resources/shaders/particle.fsh
@@ -26,7 +26,7 @@ layout (std140) uniform Matrices {
 
 uniform sampler2D colortex;
 uniform sampler2D lighttex;
-uniform sampler2D emmisivetex;
+uniform sampler2D emissivetex;
 uniform sampler2D colormasktex;
 
 float distance() {
@@ -67,8 +67,8 @@ vec4 computeFog(vec4 color) {
 }
 
 void main() {
-    vec4 emmisiveColor = texture(emmisivetex, TexCoord);
-    vec4 lightColor = texture(lighttex, vec2(max(LightMapCoord.x, emmisiveColor.r), max(LightMapCoord.y, emmisiveColor.g)));
+    vec4 emissiveColor = texture(emissivetex, TexCoord);
+    vec4 lightColor = texture(lighttex, vec2(max(LightMapCoord.x, emissiveColor.r), max(LightMapCoord.y, emissiveColor.g)));
     vec4 texColor = texture(colortex, TexCoord);
     vec4 maskColor = texture(colormasktex, TexCoord);
 
diff --git a/game/client/src/main/resources/shaders/terrain.fsh b/game/client/src/main/resources/shaders/terrain.fsh
index b1e31ee04..8c75124a7 100644
--- a/game/client/src/main/resources/shaders/terrain.fsh
+++ b/game/client/src/main/resources/shaders/terrain.fsh
@@ -30,7 +30,7 @@ layout (std140) uniform Matrices {
 
 uniform sampler2D colortex;
 uniform sampler2D lighttex;
-uniform sampler2D emmisivetex;
+uniform sampler2D emissivetex;
 uniform sampler2D colormasktex;
 
 float distance() {
@@ -71,8 +71,8 @@ vec4 computeFog(vec4 color) {
 }
 
 void main() {
-    vec4 emmisiveColor = texture(emmisivetex, TexCoord);
-    vec4 lightColor = texture(lighttex, vec2(max(LightMapCoord.x, emmisiveColor.r), max(LightMapCoord.y, emmisiveColor.g)));
+    vec4 emissiveColor = texture(emissivetex, TexCoord);
+    vec4 lightColor = texture(lighttex, vec2(max(LightMapCoord.x, emissiveColor.r), max(LightMapCoord.y, emissiveColor.g)));
     vec4 texColor = texture(colortex, TexCoord);
     vec4 maskColor = texture(colormasktex, TexCoord);
 
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicFlowstone.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicFlowstone.java
new file mode 100644
index 000000000..46d9a6a2f
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicFlowstone.java
@@ -0,0 +1,144 @@
+package net.minecraft.core.block;
+
+import net.minecraft.core.block.material.Materials;
+import net.minecraft.core.block.motion.CarriedBlock;
+import net.minecraft.core.block.tag.BlockTags;
+import net.minecraft.core.entity.EntityFallingBlock;
+import net.minecraft.core.entity.player.Player;
+import net.minecraft.core.item.ItemStack;
+import net.minecraft.core.util.helper.Side;
+import net.minecraft.core.world.World;
+import net.minecraft.core.world.WorldSource;
+import net.minecraft.core.world.pos.TilePos;
+import net.minecraft.core.world.pos.TilePosc;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.joml.primitives.AABBd;
+import org.joml.primitives.AABBdc;
+
+import java.util.Random;
+
+public class BlockLogicFlowstone extends BlockLogic {
+    public BlockLogicFlowstone(final @NotNull Block<?> block) {
+        super(block, Materials.STONE);
+    }
+
+    @Override
+    public int getPlacedData(final @Nullable Player player, final @NotNull ItemStack itemStack, final @NotNull World world, final @NotNull TilePosc tilePos, final @NotNull Side side, final double xHit, final double yHit) {
+        return itemStack.getMetadata();
+    }
+
+    @Override
+    public @NotNull AABBdc getBoundsFromState(final @NotNull WorldSource source, final @NotNull TilePosc tilePos) {
+        final int data = source.getBlockData(tilePos);
+
+        if (data == 0 || data > 3) {
+            return new AABBd(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
+        } else {
+            final double offset = 2.0 * data;
+            return new AABBd(offset / 16.0, 0.0, offset / 16.0, (16.0 - offset) / 16.0, 1.0, (16.0 - offset) / 16.0);
+        }
+    }
+
+    @Override
+    public boolean isSolidRender() {
+        return false;
+    }
+
+    @Override
+    public boolean isCubeShaped() {
+        return false;
+    }
+
+    @Override
+    public void onPlacedByWorld(final @NotNull World world, final @NotNull TilePosc tilePos) {
+        world.scheduleBlockUpdate(tilePos, this.block, this.tickDelay());
+    }
+
+    @Override
+    public void onNeighborChanged(final @NotNull World world, final @NotNull TilePosc tilePos, final @NotNull Block<?> block) {
+        world.scheduleBlockUpdate(tilePos, this.block, this.tickDelay());
+    }
+
+    @Override
+    public void updateTick(final @NotNull World world, final @NotNull TilePosc tilePos, final @NotNull Random rand, final boolean isRandomTick) {
+        this.tryToFall(world, tilePos);
+    }
+
+    private void tryToFall(final @NotNull World world, final @NotNull TilePosc tilePos) {
+        boolean topCanFall = false;
+        boolean bottomCanFall = false;
+        int y1 = -1;
+        int y0 = -1;
+
+        final @NotNull TilePos queryPos = new TilePos();
+
+        // Check if top block of stack is unsupported
+        for (int y = tilePos.y(); y < world.getHeightBlocks(); y++) {
+            queryPos.set(tilePos.x(), y + 1, tilePos.z());
+            if (canFall(world, queryPos)) {
+                topCanFall = true;
+                y1 = y;
+                break;
+            } else if (world.getBlockType(queryPos).getLogic() != this) {
+                return;
+            }
+        }
+
+        if (!topCanFall) {
+            return;
+        }
+
+        // Check if bottom block of stack is unsupported
+        for (int y = tilePos.y(); y > 0; y--) {
+            queryPos.set(tilePos.x(), y - 1, tilePos.z());
+            if (canFall(world, queryPos)) {
+                bottomCanFall = true;
+                y0 = y;
+                break;
+            } else if (world.getBlockType(queryPos).getLogic() != this) {
+                return;
+            }
+        }
+
+        if (!bottomCanFall) {
+            return;
+        }
+
+        // Make stack fall
+        for (int y = y0; y <= y1; y++) {
+            queryPos.set(tilePos.x(), y, tilePos.z());
+            final int data = world.getBlockData(queryPos);
+            final @NotNull EntityFallingBlock entity = new EntityFallingBlock(world, tilePos.x() + 0.5F, y + 0.5F, tilePos.z() + 0.5F, this.block.id(), data, null);
+            world.entityJoinedWorld(entity);
+        }
+    }
+
+    @Override
+    public int tickDelay() {
+        return 3;
+    }
+
+    @Override
+    public void animationTick(final @NotNull World world, final @NotNull TilePosc tilePos, final @NotNull Random rand) {
+        final int data = world.getBlockData(tilePos);
+
+        if (world.getBlockType(tilePos.down(new TilePos())) == Blocks.AIR && rand.nextInt(20) == 0) {
+            final double scale = 1.0 - (2.0 / 16.0) * data;
+            final double cx = (tilePos.x() + 0.5) - scale / 2.0;
+            final double cz = (tilePos.z() + 0.5) - scale / 2.0;
+            final double dx = rand.nextDouble();
+            final double dz = rand.nextDouble();
+
+            world.spawnParticle("dripWater", cx + scale * dx, tilePos.y() - 0.05f, cz + scale * dz, 0.0, 0.0, 0.0, 0);
+        }
+    }
+
+    private static boolean canFall(final @NotNull World world, final @NotNull TilePosc tilePos) {
+        final @NotNull Block<?> block = world.getBlockType(tilePos);
+        if (block == Blocks.FLOWSTONE) {
+            return false;
+        }
+        return block.hasTag(BlockTags.PLACE_OVERWRITES);
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicSand.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicSand.java
index 40e14f78c..d01bd9f25 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicSand.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicSand.java
@@ -60,9 +60,8 @@ public class BlockLogicSand extends BlockLogic {
         return 3;
     }
 
-    public static boolean canFallBelow(@NotNull World world, @NotNull TilePos tilePos) {
+    private static boolean canFallBelow(@NotNull World world, @NotNull TilePos tilePos) {
         Block<?> block = world.getBlockType(tilePos);
         return block == Blocks.AIR || block.hasTag(BlockTags.PLACE_OVERWRITES);
     }
-
 }
diff --git a/game/core/src/main/java/net/minecraft/core/block/Blocks.java b/game/core/src/main/java/net/minecraft/core/block/Blocks.java
index b5b8353c9..0b86bfc75 100644
--- a/game/core/src/main/java/net/minecraft/core/block/Blocks.java
+++ b/game/core/src/main/java/net/minecraft/core/block/Blocks.java
@@ -1270,19 +1270,6 @@ public final class Blocks {
         .withSound(BlockSounds.STONE).withDisabledNeighborNotifyOnMetadataChange().withLitInteriorSurface(true)
         .withTags(BlockTags.MINEABLE_BY_PICKAXE);
 
-    public static final @NotNull Block<BlockLogicStairs> STAIRS_BRICK_IRON = register("stairs.brick.iron", "minecraft:block/stairs_brick_iron", 1050, (BlockLogicSupplier<BlockLogicStairs>)b -> new BlockLogicStairs(b, Blocks.BRICK_IRON))
-        .withSound(BlockSounds.METAL).withDisabledNeighborNotifyOnMetadataChange().withLitInteriorSurface(true)
-        .withTags(BlockTags.MINEABLE_BY_PICKAXE);
-    public static final @NotNull Block<BlockLogicStairs> STAIRS_BRICK_GOLD = register("stairs.brick.gold", "minecraft:block/stairs_brick_gold", 1051, (BlockLogicSupplier<BlockLogicStairs>)b -> new BlockLogicStairs(b, Blocks.BRICK_GOLD))
-        .withSound(BlockSounds.METAL).withDisabledNeighborNotifyOnMetadataChange().withLitInteriorSurface(true)
-        .withTags(BlockTags.MINEABLE_BY_PICKAXE);
-    public static final @NotNull Block<BlockLogicStairs> STAIRS_BRICK_STEEL = register("stairs.brick.steel", "minecraft:block/stairs_brick_steel", 1052, (BlockLogicSupplier<BlockLogicStairs>)b -> new BlockLogicStairs(b, Blocks.BRICK_STEEL))
-        .withSound(BlockSounds.METAL).withDisabledNeighborNotifyOnMetadataChange().withLitInteriorSurface(true)
-        .withTags(BlockTags.MINEABLE_BY_PICKAXE);
-    public static final @NotNull Block<BlockLogicStairs> STAIRS_BRICK_LAPIS = register("stairs.brick.lapis", "minecraft:block/stairs_brick_lapis", 1053, (BlockLogicSupplier<BlockLogicStairs>)b -> new BlockLogicStairs(b, Blocks.BRICK_LAPIS))
-        .withSound(BlockSounds.STONE).withDisabledNeighborNotifyOnMetadataChange().withLitInteriorSurface(true)
-        .withTags(BlockTags.MINEABLE_BY_PICKAXE);
-
     public static final @NotNull Block<BlockLogicStatue> STATUE_STONE_LOWER = register("statue.stone.lower", "minecraft:block/statue_stone_lower", 1010, (BlockLogicSupplier<BlockLogicStatue>) b -> new BlockLogicStatue(b, Materials.STONE, true, () -> Items.STATUE_STONE))
         .withSound(BlockSounds.STONE).withHardness(1.5F)
         .withTags(BlockTags.MINEABLE_BY_PICKAXE, BlockTags.NOT_IN_CREATIVE_MENU);
@@ -1358,11 +1345,27 @@ public final class Blocks {
     public static final @NotNull Block<BlockLogic> MATCHER = register("matcher", "minecraft:block/matcher", 1040, (b) -> new BlockLogicMatcher(b, false))
         .withSound(BlockSounds.STONE).withHardness(3.5F).withDisabledNeighborNotifyOnMetadataChange()
         .withTags(BlockTags.MINEABLE_BY_PICKAXE);
-
     public static final @NotNull Block<BlockLogic> MATCHER_ACTIVE = register("matcher.active", "minecraft:block/matcher_active", 1041, (b) -> new BlockLogicMatcher(b, true))
         .withSound(BlockSounds.STONE).withHardness(3.5F).withDisabledNeighborNotifyOnMetadataChange()
         .withTags(BlockTags.MINEABLE_BY_PICKAXE, BlockTags.NOT_IN_CREATIVE_MENU);
 
+    public static final @NotNull Block<BlockLogicStairs> STAIRS_BRICK_IRON = register("stairs.brick.iron", "minecraft:block/stairs_brick_iron", 1050, (BlockLogicSupplier<BlockLogicStairs>)b -> new BlockLogicStairs(b, Blocks.BRICK_IRON))
+        .withSound(BlockSounds.METAL).withDisabledNeighborNotifyOnMetadataChange().withLitInteriorSurface(true)
+        .withTags(BlockTags.MINEABLE_BY_PICKAXE);
+    public static final @NotNull Block<BlockLogicStairs> STAIRS_BRICK_GOLD = register("stairs.brick.gold", "minecraft:block/stairs_brick_gold", 1051, (BlockLogicSupplier<BlockLogicStairs>)b -> new BlockLogicStairs(b, Blocks.BRICK_GOLD))
+        .withSound(BlockSounds.METAL).withDisabledNeighborNotifyOnMetadataChange().withLitInteriorSurface(true)
+        .withTags(BlockTags.MINEABLE_BY_PICKAXE);
+    public static final @NotNull Block<BlockLogicStairs> STAIRS_BRICK_STEEL = register("stairs.brick.steel", "minecraft:block/stairs_brick_steel", 1052, (BlockLogicSupplier<BlockLogicStairs>)b -> new BlockLogicStairs(b, Blocks.BRICK_STEEL))
+        .withSound(BlockSounds.METAL).withDisabledNeighborNotifyOnMetadataChange().withLitInteriorSurface(true)
+        .withTags(BlockTags.MINEABLE_BY_PICKAXE);
+    public static final @NotNull Block<BlockLogicStairs> STAIRS_BRICK_LAPIS = register("stairs.brick.lapis", "minecraft:block/stairs_brick_lapis", 1053, (BlockLogicSupplier<BlockLogicStairs>)b -> new BlockLogicStairs(b, Blocks.BRICK_LAPIS))
+        .withSound(BlockSounds.STONE).withDisabledNeighborNotifyOnMetadataChange().withLitInteriorSurface(true)
+        .withTags(BlockTags.MINEABLE_BY_PICKAXE);
+
+    public static final @NotNull Block<BlockLogicFlowstone> FLOWSTONE = register("flowstone", "minecraft:block/flowstone", 1060, BlockLogicFlowstone::new)
+        .withSound(BlockSounds.STONE).withHardness(1.5F).withDisabledNeighborNotifyOnMetadataChange()
+        .withTags(BlockTags.MINEABLE_BY_PICKAXE);
+
     public static <T extends BlockLogic> Block<T> register(String key, String namespaceId, int id, BlockLogicSupplier<T> logicSupplier) {
         Block<T> container = new Block<>(id, key, namespaceId, logicSupplier);
 
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 2ceaec495..5bc9423c0 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
@@ -23,9 +23,19 @@ import org.jetbrains.annotations.Nullable;
 
 public class EntityFallingBlock extends Entity
 {
+    public interface FallChecker {
+        boolean canFallInto(final @NotNull World world, final @NotNull CarriedBlock carriedBlock, final @NotNull TilePosc destination);
+    }
+
+    public static boolean defaultFallCheck(final @NotNull World world, final @NotNull CarriedBlock carriedBlock, final @NotNull TilePosc destination) {
+        final @NotNull Block<?> block = world.getBlockType(destination);
+        return !world.canBlockIdBePlacedAt(carriedBlock.blockId, new TilePos(destination).up(), true, Side.TOP) || block.hasTag(BlockTags.PLACE_OVERWRITES);
+    }
+
     public CarriedBlock carriedBlock;
     public int fallTime;
     public boolean hasRemovedBlock = false;
+    public @NotNull FallChecker fallChecker = EntityFallingBlock::defaultFallCheck;
 
     public EntityFallingBlock(World world)
     {
@@ -138,8 +148,12 @@ public class EntityFallingBlock extends Entity
                 Entity rider = getPassenger();
                 ejectRider();
                 remove();
-                if((!this.world.canBlockIdBePlacedAt(this.carriedBlock.blockId, blockPos, true, Side.TOP) || BlockLogicSand.canFallBelow(this.world, blockPos.down(new TilePos())) || !this.world.setBlockType(blockPos, Blocks.getBlock(this.carriedBlock.blockId))) && !this.world.isClientSide)
-                {
+                if(
+                    (
+                        this.fallChecker.canFallInto(this.world, this.carriedBlock, blockPos.down(new TilePos())) ||
+                        !this.world.setBlockType(blockPos, Blocks.getBlock(this.carriedBlock.blockId))
+                    ) && !this.world.isClientSide
+                ) {
                     if(this.hasRemovedBlock) {
                         drop();
                     }
diff --git a/game/core/src/main/java/net/minecraft/core/item/ItemMap.java b/game/core/src/main/java/net/minecraft/core/item/ItemMap.java
index dde2ff1d0..fc64a62c0 100644
--- a/game/core/src/main/java/net/minecraft/core/item/ItemMap.java
+++ b/game/core/src/main/java/net/minecraft/core/item/ItemMap.java
@@ -63,7 +63,7 @@ public class ItemMap extends Item implements IComplexItem
 			mapData.x = world.getLevelData().getSpawnPos().x();
 			mapData.z = world.getLevelData().getSpawnPos().z();
 			mapData.scale = scale;
-			mapData.dimension = (byte)world.dimension.id;
+			mapData.dimension = world.dimension.id;
 			mapData.setDirty();
             world.setSavedData(s, mapData);
         }
@@ -107,7 +107,7 @@ public class ItemMap extends Item implements IComplexItem
             mapData.x = world.getLevelData().getSpawnPos().x();
             mapData.z = world.getLevelData().getSpawnPos().z();
             mapData.scale = scale;
-            mapData.dimension = (byte)world.dimension.id;
+            mapData.dimension = world.dimension.id;
             mapData.setDirty();
             world.setSavedData(s, mapData);
         }
@@ -366,7 +366,7 @@ public class ItemMap extends Item implements IComplexItem
 			mapdata.x = (MathHelper.floor(player.x) >> 4) << 4;
 			mapdata.z = (MathHelper.floor(player.z) >> 4) << 4;
 			mapdata.scale = i;
-			mapdata.dimension = (byte)world.dimension.id;
+			mapdata.dimension = world.dimension.id;
 			mapdata.setDirty();
 		}
 		itemstack.getData().putBoolean("initialized", true);
diff --git a/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandPlace.java b/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandPlace.java
index a851a76dd..679620224 100644
--- a/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandPlace.java
+++ b/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandPlace.java
@@ -22,35 +22,35 @@ public class CommandPlace implements CommandManager.CommandRegistry {
 
     public void register(CommandDispatcher<CommandSource> dispatcher) {
         CommandNode<CommandSource> command = dispatcher.register(ArgumentBuilderLiteral.<CommandSource>literal("place")
-                .requires(CommandSource::hasAdmin)
-                .then(ArgumentBuilderRequired.<CommandSource, WorldFeatureInterface>argument("feature", ArgumentTypeWorldFeature.worldFeature())
-                        .then(ArgumentBuilderRequired.<CommandSource, IntegerCoordinates>argument("position", ArgumentTypeIntegerCoordinates.intCoordinates())
-                                .executes(c -> {
-                                    CommandSource source = c.getSource();
-                                    WorldFeatureInterface feature = c.getArgument("feature", WorldFeatureInterface.class);
-                                    IntegerCoordinates coordinates = c.getArgument("position", IntegerCoordinates.class);
-                                    World world = source.getWorld();
+            .requires(CommandSource::hasAdmin)
+            .then(ArgumentBuilderRequired.<CommandSource, WorldFeatureInterface>argument("feature", ArgumentTypeWorldFeature.worldFeature())
+                .then(ArgumentBuilderRequired.<CommandSource, IntegerCoordinates>argument("position", ArgumentTypeIntegerCoordinates.intCoordinates())
+                    .executes(c -> {
+                        CommandSource source = c.getSource();
+                        WorldFeatureInterface feature = c.getArgument("feature", WorldFeatureInterface.class);
+                        IntegerCoordinates coordinates = c.getArgument("position", IntegerCoordinates.class);
+                        World world = source.getWorld();
 
-                                    if (coordinates == null) throw CommandExceptions.notInWorld().create();
+                        if (coordinates == null) throw CommandExceptions.notInWorld().create();
 
-                                    int x = coordinates.getX(source);
-                                    int y = coordinates.getY(source, true);
-                                    int z = coordinates.getZ(source);
+                        int x = coordinates.getX(source);
+                        int y = coordinates.getY(source, true);
+                        int z = coordinates.getZ(source);
 
-                                    boolean success = feature.place(world, source.getWorld().rand, new TilePos(x, y, z));
-                                    if (success) {
-                                        world.allChanged(true, false);
-                                        source.sendTranslatableMessage("command.commands.place.success", feature.getClass().getSimpleName().substring(12), x, y, z);
-                                    } else {
-                                        throw FAILURE.create();
-                                    }
-                                    return Command.SINGLE_SUCCESS;
-                                }))));
+                        boolean success = feature.place(world, source.getWorld().rand, new TilePos(x, y, z));
+                        if (success) {
+                            world.allChanged(true, false);
+                            source.sendTranslatableMessage("command.commands.place.success", feature.getClass().getSimpleName().substring(12), x, y, z);
+                        } else {
+                            throw FAILURE.create();
+                        }
+                        return Command.SINGLE_SUCCESS;
+                    }))));
         dispatcher.register(ArgumentBuilderLiteral.<CommandSource>literal("generate")
-                .requires(CommandSource::hasAdmin)
-                .redirect(command));
+            .requires(CommandSource::hasAdmin)
+            .redirect(command));
         dispatcher.register(ArgumentBuilderLiteral.<CommandSource>literal("gen")
-                .requires(CommandSource::hasAdmin)
-                .redirect(command));
+            .requires(CommandSource::hasAdmin)
+            .redirect(command));
     }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandStructure.java b/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandStructure.java
new file mode 100644
index 000000000..178936acc
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandStructure.java
@@ -0,0 +1,91 @@
+package net.minecraft.core.net.command.commands;
+
+import com.mojang.brigadier.Command;
+import com.mojang.brigadier.CommandDispatcher;
+import com.mojang.brigadier.arguments.ArgumentTypeString;
+import com.mojang.brigadier.builder.ArgumentBuilderLiteral;
+import com.mojang.brigadier.builder.ArgumentBuilderRequired;
+import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
+import com.mojang.brigadier.tree.CommandNode;
+import net.minecraft.core.lang.I18n;
+import net.minecraft.core.net.command.CommandManager;
+import net.minecraft.core.net.command.CommandSource;
+import net.minecraft.core.net.command.arguments.ArgumentTypeIntegerCoordinates;
+import net.minecraft.core.net.command.helpers.IntegerCoordinates;
+import net.minecraft.core.world.pos.TilePos;
+import net.minecraft.core.world.structure.EditableStructure;
+import net.minecraft.core.world.structure.SavedStructure;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.joml.Vector3i;
+
+public class CommandStructure implements CommandManager.CommandRegistry {
+    private static final @NotNull SimpleCommandExceptionType SAVE_FAILURE = new SimpleCommandExceptionType(() -> I18n.getInstance().translateKey("command.commands.structure.save.exception_failure"));
+    private static final @NotNull SimpleCommandExceptionType LOAD_FAILURE = new SimpleCommandExceptionType(() -> I18n.getInstance().translateKey("command.commands.structure.load.exception_failure"));
+
+    @Override
+    public void register(CommandDispatcher<CommandSource> dispatcher) {
+        final @NotNull CommandNode<CommandSource> command = dispatcher.register(
+            ArgumentBuilderLiteral.<CommandSource>literal("structure")
+                .requires(CommandSource::hasAdmin)
+                .then(ArgumentBuilderLiteral.<CommandSource>literal("save")
+                    .then(ArgumentBuilderRequired.<CommandSource, String>argument("id", ArgumentTypeString.string())
+                        .then(ArgumentBuilderRequired.<CommandSource, IntegerCoordinates>argument("position", ArgumentTypeIntegerCoordinates.intCoordinates())
+                            .then(ArgumentBuilderRequired.<CommandSource, IntegerCoordinates>argument("size", ArgumentTypeIntegerCoordinates.intCoordinates())
+                                .then(ArgumentBuilderRequired.<CommandSource, IntegerCoordinates>argument("origin", ArgumentTypeIntegerCoordinates.intCoordinates())
+                                    .executes(c -> {
+                                        final @NotNull CommandSource source = c.getSource();
+                                        final @NotNull String id = c.getArgument("id", String.class);
+                                        final @NotNull IntegerCoordinates position = c.getArgument("position", IntegerCoordinates.class);
+                                        final @NotNull IntegerCoordinates size = c.getArgument("size", IntegerCoordinates.class);
+                                        final @NotNull IntegerCoordinates origin = c.getArgument("origin", IntegerCoordinates.class);
+
+                                        if (
+                                            SavedStructure.save(
+                                                source.getWorld(),
+                                                id,
+                                                new TilePos(position.getX(source), position.getY(source, true), position.getZ(source)),
+                                                new Vector3i(size.getX(source), size.getY(source, false), size.getZ(source)),
+                                                new Vector3i(origin.getX(source), origin.getY(source, false), origin.getZ(source))
+                                            )
+                                        ) {
+                                            source.sendTranslatableMessage("command.commands.structure.save.success");
+                                        } else {
+                                            throw SAVE_FAILURE.create();
+                                        }
+
+                                        return Command.SINGLE_SUCCESS;
+                                    })
+                                )
+                            )
+                        )
+                    )
+                )
+                .then(ArgumentBuilderLiteral.<CommandSource>literal("load")
+                    .then(ArgumentBuilderRequired.<CommandSource, String>argument("id", ArgumentTypeString.string())
+                        .then(ArgumentBuilderRequired.<CommandSource, IntegerCoordinates>argument("position", ArgumentTypeIntegerCoordinates.intCoordinates())
+                            .executes(c -> {
+                                final @NotNull CommandSource source = c.getSource();
+                                final @NotNull String id = c.getArgument("id", String.class);
+                                final @NotNull IntegerCoordinates position = c.getArgument("position", IntegerCoordinates.class);
+
+                                final @Nullable SavedStructure structure = SavedStructure.load(id);
+                                if (structure == null) {
+                                    throw LOAD_FAILURE.create();
+                                }
+
+                                final @NotNull EditableStructure editableStructure = new EditableStructure(structure);
+                                editableStructure.place(
+                                    source.getWorld(),
+                                    new TilePos(position.getX(source), position.getY(source, true), position.getZ(source))
+                                );
+                                source.sendTranslatableMessage("command.commands.structure.load.success");
+
+                                return Command.SINGLE_SUCCESS;
+                            })
+                        )
+                    )
+                )
+        );
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/net/packet/PacketLogin.java b/game/core/src/main/java/net/minecraft/core/net/packet/PacketLogin.java
index 4a849cb01..3f1aa1f30 100644
--- a/game/core/src/main/java/net/minecraft/core/net/packet/PacketLogin.java
+++ b/game/core/src/main/java/net/minecraft/core/net/packet/PacketLogin.java
@@ -11,8 +11,8 @@ public class PacketLogin extends Packet
     public String username;
     public UUID uuid;
     public long worldSeed;
-    public byte dimensionId;
-    public byte worldTypeId;
+    public int dimensionId;
+    public int worldTypeId;
     public int packetDelay;
     public String publicKey;
 
@@ -20,7 +20,7 @@ public class PacketLogin extends Packet
     {
     }
 
-    public PacketLogin(String username, UUID uuid, int playerEntityIdOrProtocolVersion, long worldSeed, byte dimensionId, byte worldTypeId, int packetDelay, String publicKey)
+    public PacketLogin(String username, UUID uuid, int playerEntityIdOrProtocolVersion, long worldSeed, int dimensionId, int worldTypeId, int packetDelay, String publicKey)
     {
         this.username = username;
         this.uuid = uuid;
@@ -49,8 +49,8 @@ public class PacketLogin extends Packet
         uuid = readUUID(dis);
         publicKey = readStringUTF8(dis, MAX_AES_KEY_SIZE);
         worldSeed = dis.readLong();
-        dimensionId = dis.readByte();
-        worldTypeId = dis.readByte();
+        dimensionId = dis.readInt();
+        worldTypeId = dis.readInt();
         packetDelay = dis.readByte();
     }
 
@@ -63,8 +63,8 @@ public class PacketLogin extends Packet
         writeUUID(uuid, dos);
         writeStringUTF8(publicKey, dos);
         dos.writeLong(worldSeed);
-        dos.writeByte(dimensionId);
-        dos.writeByte(worldTypeId);
+        dos.writeInt(dimensionId);
+        dos.writeInt(worldTypeId);
         dos.writeByte(packetDelay);
     }
 
@@ -77,6 +77,6 @@ public class PacketLogin extends Packet
     @Override
 	public int getEstimatedSize()
     {
-        return 4 + username.length() + 8 + 1 + 1 + 1;
+        return 4 + username.length() + 8 + 1 + 4 + 4;
     }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/net/packet/PacketRespawn.java b/game/core/src/main/java/net/minecraft/core/net/packet/PacketRespawn.java
index a1b53779e..c6f68db4c 100644
--- a/game/core/src/main/java/net/minecraft/core/net/packet/PacketRespawn.java
+++ b/game/core/src/main/java/net/minecraft/core/net/packet/PacketRespawn.java
@@ -6,15 +6,15 @@ import java.io.*;
 
 public class PacketRespawn extends Packet
 {
-    public byte respawnDimensionId;
-    public byte respawnWorldTypeId;
+    public int respawnDimensionId;
+    public int respawnWorldTypeId;
 
 
     public PacketRespawn()
     {
     }
 
-    public PacketRespawn(byte respawnDimensionId, byte respawnWorldTypeId)
+    public PacketRespawn(int respawnDimensionId, int respawnWorldTypeId)
     {
         this.respawnDimensionId = respawnDimensionId;
         this.respawnWorldTypeId = respawnWorldTypeId;
@@ -30,21 +30,21 @@ public class PacketRespawn extends Packet
 	public void read(DataInputStream dis)
         throws IOException
     {
-        respawnDimensionId = dis.readByte();
-        respawnWorldTypeId = dis.readByte();
+        respawnDimensionId = dis.readInt();
+        respawnWorldTypeId = dis.readInt();
     }
 
     @Override
 	public void write(DataOutputStream dos)
         throws IOException
     {
-        dos.writeByte(respawnDimensionId);
-        dos.writeByte(respawnWorldTypeId);
+        dos.writeInt(respawnDimensionId);
+        dos.writeInt(respawnWorldTypeId);
     }
 
     @Override
 	public int getEstimatedSize()
     {
-        return 2;
+        return 8;
     }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuInventoryCreative.java b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuInventoryCreative.java
index e75bfa955..67c963377 100644
--- a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuInventoryCreative.java
+++ b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuInventoryCreative.java
@@ -134,6 +134,10 @@ public class MenuInventoryCreative extends MenuInventory
 				for (DyeColor color : DyeColor.values()) {
 					creativeItems.add(new ItemStack(block, 1, painted.toMetadata(color)));
 				}
+			} else if (block == Blocks.FLOWSTONE) {
+				for (int i = 0; i < 4; i++) {
+					creativeItems.add(new ItemStack(block, 1, i));
+				}
 			} else {
 				creativeItems.add(new ItemStack(block));
 			}
diff --git a/game/core/src/main/java/net/minecraft/core/world/data/ChunkNibbleArray.java b/game/core/src/main/java/net/minecraft/core/world/data/ChunkNibbleArray.java
index 0f9015968..31b2ea215 100644
--- a/game/core/src/main/java/net/minecraft/core/world/data/ChunkNibbleArray.java
+++ b/game/core/src/main/java/net/minecraft/core/world/data/ChunkNibbleArray.java
@@ -18,7 +18,11 @@ public class ChunkNibbleArray
         this.xSize = xSize;
         this.ySize = ySize;
         this.zSize = zSize;
-        this.data = new byte[(xSize * ySize * zSize) >> 1];
+        int size = (xSize * ySize * zSize) / 2;
+        if ((xSize * ySize * zSize) % 2 != 0) {
+            size++;
+        }
+        this.data = new byte[size];
     }
 
     public ChunkNibbleArray(int xSize, int ySize, int zSize, byte[] data)
@@ -26,7 +30,11 @@ public class ChunkNibbleArray
         this.xSize = xSize;
         this.ySize = ySize;
         this.zSize = zSize;
-        if (data.length == ((xSize * ySize * zSize) >> 1))
+        int size = (xSize * ySize * zSize) / 2;
+        if ((xSize * ySize * zSize) % 2 != 0) {
+            size++;
+        }
+        if (data.length == size)
         {
             this.data = data;
         }
@@ -71,6 +79,10 @@ public class ChunkNibbleArray
 //    @Override
     public boolean isValid()
     {
-        return this.data != null && this.data.length == ((this.xSize * this.ySize * this.zSize) >> 1);
+        int size = (xSize * ySize * zSize) / 2;
+        if ((xSize * ySize * zSize) % 2 != 0) {
+            size++;
+        }
+        return this.data != null && this.data.length == size;
     }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/chunk/perlin/nether/ChunkDecoratorNether.java b/game/core/src/main/java/net/minecraft/core/world/generate/chunk/perlin/nether/ChunkDecoratorNether.java
index 425f3f468..2c2707ee7 100644
--- a/game/core/src/main/java/net/minecraft/core/world/generate/chunk/perlin/nether/ChunkDecoratorNether.java
+++ b/game/core/src/main/java/net/minecraft/core/world/generate/chunk/perlin/nether/ChunkDecoratorNether.java
@@ -24,6 +24,11 @@ public class ChunkDecoratorNether
 
     @Override
     public void registerDecorations() {
+        this.register("minecraft:decoration/nether/default/speleothems",
+            new ChunkDecorationBuilder(new WorldFeatureSpeleothems(12, true, 12, true))
+                .withPositionSelector(PositionSelectors.UpperHeightRangeUniform)
+                .withPlacementMethod(new PlacementMethod.TriesPerChunk(4))
+        );
         this.register("minecraft:decoration/nether/default/lava_lake",
                 new ChunkDecorationBuilder(new WorldFeatureLake(Blocks.FLUID_LAVA_STILL.id()))
                         .withPositionSelector(PositionSelectors.UpperHeightRangeUniform)
diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/feature/WorldFeatureSpeleothems.java b/game/core/src/main/java/net/minecraft/core/world/generate/feature/WorldFeatureSpeleothems.java
new file mode 100644
index 000000000..f8920e365
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/generate/feature/WorldFeatureSpeleothems.java
@@ -0,0 +1,177 @@
+package net.minecraft.core.world.generate.feature;
+
+import net.minecraft.core.block.Block;
+import net.minecraft.core.block.Blocks;
+import net.minecraft.core.util.helper.MathHelper;
+import net.minecraft.core.world.World;
+import net.minecraft.core.world.pos.TilePos;
+import net.minecraft.core.world.pos.TilePosc;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Random;
+
+public class WorldFeatureSpeleothems extends WorldFeature {
+    protected final int maxRadius;
+    protected final boolean generateBottom;
+    protected final int maxHeight;
+    protected final boolean checkDepth;
+
+    public WorldFeatureSpeleothems(final int maxRadius, final boolean generateBottom, final int maxHeight, final boolean checkDepth) {
+        this.maxRadius = maxRadius;
+        this.generateBottom = generateBottom;
+        this.maxHeight = maxHeight;
+        this.checkDepth = checkDepth;
+    }
+
+    @Override
+    public boolean place(final @NotNull World world, final @NotNull Random random, int x, int y, int z) {
+        final @NotNull TilePosc tilePos = new TilePos(x, y, z);
+        final @NotNull TilePos queryPos = new TilePos();
+
+        final @NotNull Block<?> baseBlock = Blocks.COBBLE_NETHERRACK;
+
+        if (world.getBlockType(tilePos) == Blocks.AIR) {
+            tilePos.up(queryPos);
+            if (world.getBlockType(queryPos) != Blocks.AIR) {
+                y = y + 1;
+            } else
+                return false;
+
+        }
+
+        final int radius = random.nextInt(this.maxRadius) + this.maxRadius; // Radius 5-10
+        final int depth = radius * 2;
+
+        // Check we have enough space underneath proportionally to our radius
+        if (this.checkDepth) {
+            for (int i = y - 1; i > y - depth; i--) {
+                queryPos.set(x, i, z);
+                if (world.getBlockType(queryPos) != Blocks.AIR) {
+                    return false;
+                }
+            }
+        }
+
+        // Determine the number of base blocks we need to generate
+        final int numBaseBlocks = (radius * 2) * (radius * 2);
+
+        for (int i = 0; i < numBaseBlocks; i++) {
+            // Determine our base block's location
+            final int dx = radius - random.nextInt(radius * 2);
+            int dy = 0;
+            final int dz = radius - random.nextInt(radius * 2);
+
+            if (world.getBlockType(queryPos.set(x + dx, y, z + dz)) == baseBlock) {
+                continue;
+            }
+
+            int distance = dx * dz;
+
+            // Make sure our distance is never 0
+            if (distance == 0) {
+                if (dx == 0 && dz == 0) {
+                    distance = 1;
+                } else if (dx == 0) {
+                    distance = dz;
+                } else if (dz == 0) {
+                    distance = dx;
+                }
+            }
+
+            // Bias so we're more likely to generate base blocks closer to the middle
+            if (random.nextInt(Math.abs(distance)) != 0) {
+                i--;
+                continue;
+            }
+
+            // Move up one block if needed
+            if (world.getBlockType(queryPos.set(x + dx, y, z + dz)) == Blocks.AIR) {
+                dy = 1;
+                if (world.getBlockType(queryPos.set(x + dx, y + 1, z + dz)) == Blocks.AIR) {
+                    continue;
+                }
+            }
+
+            // Move down one block if needed
+            if (world.getBlockType(queryPos.set(x + dx, y - 1, z + dz)) != Blocks.AIR) {
+                dy = -1;
+                if (world.getBlockType(queryPos.set(x + dx, y - 2, z + dz)) != Blocks.AIR) {
+                    continue;
+                }
+            }
+
+            // Set base block
+            world.setBlockTypeNotify(queryPos.set(x + dx, y + dy, z + dz), baseBlock);
+
+            // Generate stalactite
+            if (random.nextInt(5) == 0) {
+                int height = random.nextInt(depth - radius - 1) + 1; // 2-12
+
+                if (this.maxHeight > -1 && height > this.maxHeight) {
+                    height = this.maxHeight;
+                }
+                this.placeSpeleothem(world, queryPos.set(x + dx, y + dy, z + dz), height);
+
+                // Possibly generate stalagmite
+                if (this.generateBottom) {
+                    if (random.nextInt(3) == 0) {
+                        int bottom = -1;
+
+                        for (int j = height + 1; j < depth * 2; j++) {
+                            if (world.getBlockType(queryPos.set(x + dx, y + dy - j, z + dz)) != Blocks.AIR) {
+                                bottom = y + dy - j;
+                                break;
+                            }
+                        }
+
+                        if (bottom != -1) {
+                            this.placeSpeleothem(world, queryPos.set(x + dx, bottom, z + dz), -height / 2);
+                        }
+                    }
+                }
+            }
+        }
+
+        return true;
+    }
+
+    private boolean placeSpeleothem(final @NotNull World world, final @NotNull TilePosc pos, final int height) {
+        if (height == 0) {
+            return false;
+        }
+
+        final @NotNull Block<?> block = Blocks.FLOWSTONE;
+
+        final @NotNull TilePos queryPos = new TilePos(pos);
+        final int absHeight = Math.abs(height);
+        final int dy = height < 0 ? 1 : -1;
+
+        // Check space is clear
+        for (int i = 0; i < absHeight; i++) {
+            queryPos.y += dy;
+            if (world.getBlockType(queryPos) != Blocks.AIR) {
+                return false;
+            }
+        }
+
+        // Handle edge cases first so we get nice-looking short speleothems
+        queryPos.set(pos);
+        if (absHeight <= 4) {
+            int data = 4 - absHeight;
+            for (int i = 0; i < absHeight; i++) {
+                queryPos.y += dy;
+                world.setBlockTypeData(queryPos, block, data);
+                data++;
+            }
+        } else {
+            for (int i = 0; i < absHeight; i++) {
+                final float progress = (float) i / (float) absHeight;
+                final int data = (int) (progress * 4);
+                queryPos.y += dy;
+                world.setBlockTypeData(queryPos, block, data);
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/Operation.java b/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/Operation.java
new file mode 100644
index 000000000..540b6ff4c
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/Operation.java
@@ -0,0 +1,10 @@
+package net.minecraft.core.world.generate.feature.weatherable;
+
+import net.minecraft.core.world.structure.EditableStructure;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Random;
+
+public interface Operation {
+    void apply(final @NotNull EditableStructure structure, final @NotNull Random random);
+}
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/WorldFeatureWeatherable.java b/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/WorldFeatureWeatherable.java
new file mode 100644
index 000000000..00fd35588
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/WorldFeatureWeatherable.java
@@ -0,0 +1,70 @@
+package net.minecraft.core.world.generate.feature.weatherable;
+
+import net.minecraft.core.block.Blocks;
+import net.minecraft.core.world.World;
+import net.minecraft.core.world.generate.feature.MethodParametersAnnotation;
+import net.minecraft.core.world.generate.feature.WorldFeature;
+import net.minecraft.core.world.generate.feature.WorldFeatureInterface;
+import net.minecraft.core.world.generate.feature.weatherable.operation.*;
+import net.minecraft.core.world.pos.TilePosc;
+import net.minecraft.core.world.structure.EditableStructure;
+import net.minecraft.core.world.structure.SavedStructure;
+import net.minecraft.core.world.structure.Structure;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Random;
+
+public class WorldFeatureWeatherable
+    implements WorldFeatureInterface
+{
+    private static final int MIN_OPERATIONS_TO_APPLY = 100;
+    private static final int MAX_OPERATIONS_TO_APPLY = 500;
+    private static final @NotNull Operation @NotNull[] OPERATIONS = new Operation[] {
+        new RotOperation(),
+        new CrumbleOperation(),
+        new SinkOperation(),
+        new HoleOperation(),
+        new CollapseOperation(),
+        new BlastOperation()
+    };
+
+    private final int age;
+    private final @NotNull String id;
+
+    @MethodParametersAnnotation(names = {"age", "id"})
+    public WorldFeatureWeatherable(final int age, final @NotNull String id) {
+        this.age = age;
+        this.id = id;
+    }
+
+    @Override
+    public boolean place(final @NotNull World world, final @NotNull Random random, final @NotNull TilePosc tilePos) {
+        if (world.getBlockType(tilePos) != Blocks.GRASS) {
+            return false;
+        }
+
+        final @Nullable SavedStructure structure = SavedStructure.load(id);
+        if (structure == null) {
+            return false;
+        }
+
+        final @NotNull EditableStructure editableStructure = new EditableStructure(structure);
+        final var size = editableStructure.getSize();
+        final int maxOperations = size.x() * size.z() * this.age;
+        final int minOperations = maxOperations / 2;
+        final int operations = minOperations + random.nextInt(maxOperations - minOperations);
+        for (int i = 0; i < operations; i++) {
+            OPERATIONS[random.nextInt(OPERATIONS.length)].apply(editableStructure, random);
+        }
+        new FallOperation().apply(editableStructure, random);
+        final @NotNull MossOperation mossOperation = new MossOperation();
+        int mossOperations = age * 100;
+        for (int i = 0; i < mossOperations; i++) {
+            mossOperation.apply(editableStructure, random);
+        }
+        editableStructure.place(world, tilePos);
+
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/BlastOperation.java b/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/BlastOperation.java
new file mode 100644
index 000000000..3479e4531
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/BlastOperation.java
@@ -0,0 +1,38 @@
+package net.minecraft.core.world.generate.feature.weatherable.operation;
+
+import net.minecraft.core.block.Blocks;
+import net.minecraft.core.world.structure.EditableStructure;
+import net.minecraft.core.world.generate.feature.weatherable.Operation;
+import org.jetbrains.annotations.NotNull;
+import org.joml.Vector3i;
+
+import java.util.Random;
+
+public class BlastOperation implements Operation {
+    @Override
+    public void apply(final @NotNull EditableStructure structure, final @NotNull Random random) {
+        if (random.nextInt(100) != 0) {
+            return;
+        }
+
+        final var queryPos = new Vector3i();
+
+        final var size = structure.getSize();
+
+        final int x = random.nextInt(size.x());
+        final int y = random.nextInt(size.y());
+        final int z = random.nextInt(size.z());
+
+        final int xSize = random.nextInt(4) + 1;
+        final int ySize = random.nextInt(4) + 1;
+        final int zSize = random.nextInt(4) + 1;
+
+        for (int i = 0; i < xSize; i++) {
+            for (int j = 0; j < ySize; j++) {
+                for (int k = 0; k < zSize; k++) {
+                    structure.setBlockType(queryPos.set(x + i, y + j, z + k), Blocks.AIR);
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/CollapseOperation.java b/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/CollapseOperation.java
new file mode 100644
index 000000000..4c131914e
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/CollapseOperation.java
@@ -0,0 +1,46 @@
+package net.minecraft.core.world.generate.feature.weatherable.operation;
+
+import net.minecraft.core.block.Blocks;
+import net.minecraft.core.world.structure.EditableStructure;
+import net.minecraft.core.world.generate.feature.weatherable.Operation;
+import org.jetbrains.annotations.NotNull;
+import org.joml.Vector3i;
+
+import java.util.Random;
+
+public class CollapseOperation implements Operation {
+    @Override
+    public void apply(@NotNull EditableStructure structure, @NotNull Random random) {
+        final var size = structure.getSize();
+        final int x = random.nextInt(size.x());
+        final int z = random.nextInt(size.z());
+
+        int y0 = size.y() - 1;
+        int y1 = size.y() - 1;
+
+        final @NotNull Vector3i queryPos = new Vector3i();
+
+        while (structure.getBlockType(queryPos.set(x, y1, z)) == Blocks.AIR && y1 > 0) {
+            y1--;
+            y0--;
+        }
+        while (structure.getBlockType(queryPos.set(x, y0 - 1, z)) != Blocks.AIR && y0 > 0) {
+            y0--;
+        }
+        int collapsedY0 = y0;
+        while (structure.getBlockType(queryPos.set(x, collapsedY0 - 1, z)) == Blocks.AIR && collapsedY0 > 0) {
+            collapsedY0--;
+        }
+        int fallDistance = y0 - collapsedY0;
+        int collapsedY1 = y1 - fallDistance;
+
+        // Copy to collapsed
+        for (int y = collapsedY0; y <= collapsedY1; y++) {
+            structure.setBlockTypeData(new Vector3i(x, y, z), structure.getBlockType(queryPos.set(x, y + fallDistance, z)), structure.getBlockData(queryPos));
+        }
+        // Clear top
+        for (int y = collapsedY1 + 1; y <= y1; y++) {
+            structure.setBlockType(queryPos.set(x, y, z), Blocks.AIR);
+        }
+    }
+}
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/CrumbleOperation.java b/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/CrumbleOperation.java
new file mode 100644
index 000000000..b1a9b6b68
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/CrumbleOperation.java
@@ -0,0 +1,42 @@
+package net.minecraft.core.world.generate.feature.weatherable.operation;
+
+import net.minecraft.core.block.Blocks;
+import net.minecraft.core.world.structure.EditableStructure;
+import net.minecraft.core.world.generate.feature.weatherable.Operation;
+import net.minecraft.core.world.structure.Structure;
+import org.jetbrains.annotations.NotNull;
+import org.joml.Vector3i;
+
+import java.util.Random;
+
+public class CrumbleOperation implements Operation {
+    private static final int MAX_ITERATIONS = 100;
+
+    @Override
+    public void apply(final @NotNull EditableStructure structure, final @NotNull Random random) {
+        int iterations = 0;
+
+        final @NotNull Vector3i queryPos = new Vector3i();
+
+        final var size = structure.getSize();
+        int x = 0, y = 0, z = 0;
+        do {
+            x = random.nextInt(size.x());
+            y = random.nextInt(size.y());
+            z = random.nextInt(size.z());
+            iterations++;
+        } while (structure.getBlockType(queryPos.set(x, y, z)) == Blocks.AIR && iterations < MAX_ITERATIONS);
+        if (iterations >= MAX_ITERATIONS) {
+            return;
+        }
+        int toCrumble = random.nextInt(5);
+        int i = 0;
+        while (structure.getBlockType(queryPos.set(x, y + toCrumble + i, z)) != Blocks.AIR) {
+            structure.setBlockTypeData(new Vector3i(x, y + i, z), structure.getBlockType(queryPos.set(x, y + toCrumble + i, z)), structure.getBlockData(queryPos));
+            i++;
+        }
+        for (int j = 0; j < toCrumble; j++) {
+            structure.setBlockType(queryPos.set(x, y + i - j, z), Blocks.AIR);
+        }
+    }
+}
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/FallOperation.java b/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/FallOperation.java
new file mode 100644
index 000000000..f03a4a704
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/FallOperation.java
@@ -0,0 +1,53 @@
+package net.minecraft.core.world.generate.feature.weatherable.operation;
+
+import net.minecraft.core.block.Blocks;
+import net.minecraft.core.world.structure.EditableStructure;
+import net.minecraft.core.world.generate.feature.weatherable.Operation;
+import org.jetbrains.annotations.Blocking;
+import org.jetbrains.annotations.NotNull;
+import org.joml.Vector3i;
+import org.joml.Vector3ic;
+
+import java.util.Random;
+
+public class FallOperation implements Operation {
+    @Override
+    public void apply(final @NotNull EditableStructure structure, final @NotNull Random random) {
+        final @NotNull Vector3i queryPos = new Vector3i();
+        final var size = structure.getSize();
+
+        for (int x = 0; x < size.x(); x++) {
+            for (int y = 0; y < size.y(); y++) {
+                for (int z = 0; z < size.z(); z++) {
+                    if (shouldFall(structure, queryPos.set(x, y, z))) {
+                        fall(structure, queryPos);
+                    }
+                }
+            }
+        }
+    }
+
+    private boolean shouldFall(final @NotNull EditableStructure structure, final @NotNull Vector3ic pos) {
+        final @NotNull Vector3i queryPos = new Vector3i(pos);
+
+        int total = 0;
+        if (structure.getBlockType(pos.add(-1, 0, 0, queryPos)) != Blocks.AIR) total++;
+        if (structure.getBlockType(pos.add(+1, 0, 0, queryPos)) != Blocks.AIR) total++;
+        if (structure.getBlockType(pos.add(0, -1, 0, queryPos)) != Blocks.AIR) total++;
+        if (structure.getBlockType(pos.add(0, +1, 0, queryPos)) != Blocks.AIR) total++;
+        if (structure.getBlockType(pos.add(0, 0, -1, queryPos)) != Blocks.AIR) total++;
+        if (structure.getBlockType(pos.add(0, 0, +1, queryPos)) != Blocks.AIR) total++;
+
+        return total < 3;
+    }
+
+    private void fall(final @NotNull EditableStructure structure, final @NotNull Vector3ic pos) {
+        final @NotNull Vector3i queryPos = new Vector3i(pos);
+
+        int i = 1;
+        while (structure.getBlockType(queryPos.set(pos.x(), pos.y() - i, pos.z())) == Blocks.AIR && pos.y() - i > 0) {
+            i++;
+        }
+        structure.setBlockTypeData(pos, structure.getBlockType(queryPos.set(pos.x(), pos.y() - i + 1, pos.z())), structure.getBlockData(queryPos));
+    }
+}
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/HoleOperation.java b/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/HoleOperation.java
new file mode 100644
index 000000000..3fa4e3cfe
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/HoleOperation.java
@@ -0,0 +1,25 @@
+package net.minecraft.core.world.generate.feature.weatherable.operation;
+
+import net.minecraft.core.block.Blocks;
+import net.minecraft.core.world.structure.EditableStructure;
+import net.minecraft.core.world.generate.feature.weatherable.Operation;
+import net.minecraft.core.world.structure.Structure;
+import org.jetbrains.annotations.NotNull;
+import org.joml.Vector3i;
+
+import java.util.Random;
+
+public class HoleOperation
+    implements Operation {
+
+    @Override
+    public void apply(final @NotNull EditableStructure structure, final @NotNull Random random) {
+        final var size = structure.getSize();
+        final @NotNull Vector3i pos = new Vector3i(
+            random.nextInt(size.x()),
+            random.nextInt(size.y()),
+            random.nextInt(size.z())
+        );
+        structure.setBlockType(pos, Blocks.AIR);
+    }
+}
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/MossOperation.java b/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/MossOperation.java
new file mode 100644
index 000000000..a7dd878ff
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/MossOperation.java
@@ -0,0 +1,56 @@
+package net.minecraft.core.world.generate.feature.weatherable.operation;
+
+import net.minecraft.core.block.Block;
+import net.minecraft.core.block.Blocks;
+import net.minecraft.core.world.structure.EditableStructure;
+import net.minecraft.core.world.generate.feature.weatherable.Operation;
+import net.minecraft.core.world.structure.Structure;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.joml.Vector3i;
+
+import java.util.Random;
+
+public class MossOperation implements Operation {
+    private static final int MAX_ITERATIONS = 100;
+
+    @Override
+    public void apply(final @NotNull EditableStructure structure, final @NotNull Random random) {
+        int iterations = 0;
+
+        final var size = structure.getSize();
+        final var origin = structure.getOrigin();
+        int sizeAbove = size.y() - origin.y();
+        int x, y, z;
+        final var queryPos = new Vector3i();
+        do {
+            queryPos.set(
+                x = random.nextInt(size.x()),
+                y = origin.y() + random.nextInt(random.nextInt(sizeAbove) + 1),
+                z = random.nextInt(size.z())
+            );
+            iterations++;
+        } while (structure.getBlockType(queryPos) == Blocks.AIR && iterations < MAX_ITERATIONS);
+        if (iterations >= MAX_ITERATIONS) {
+            return;
+        }
+
+        final @Nullable Block<?> toMoss = toMoss(structure.getBlockType(queryPos.set(x, y, z)));
+        if (toMoss == null) {
+            return;
+        }
+        structure.setBlockType(queryPos, toMoss);
+    }
+
+    private @Nullable Block<?> toMoss(final @NotNull Block<?> block) {
+        if (block == Blocks.STONE) {
+            return Blocks.MOSS_STONE;
+        } else if (block == Blocks.COBBLE_STONE) {
+            return Blocks.COBBLE_STONE_MOSSY;
+        } else if (block == Blocks.BRICK_STONE_POLISHED) {
+            return Blocks.BRICK_STONE_POLISHED_MOSSY;
+        } else {
+            return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/RotOperation.java b/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/RotOperation.java
new file mode 100644
index 000000000..24589821b
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/RotOperation.java
@@ -0,0 +1,42 @@
+package net.minecraft.core.world.generate.feature.weatherable.operation;
+
+import net.minecraft.core.block.Block;
+import net.minecraft.core.block.Blocks;
+import net.minecraft.core.world.structure.EditableStructure;
+import net.minecraft.core.world.generate.feature.weatherable.Operation;
+import net.minecraft.core.world.structure.Structure;
+import org.jetbrains.annotations.NotNull;
+import org.joml.Vector3i;
+
+import java.util.Random;
+
+public class RotOperation implements Operation {
+    private static final int MAX_ITERATIONS = 100;
+
+    @Override
+    public void apply(@NotNull EditableStructure structure, @NotNull Random random) {
+        int iterations = 0;
+
+        final @NotNull Vector3i queryPos = new Vector3i();
+
+        final var size = structure.getSize();
+        do {
+            queryPos.set(
+                random.nextInt(size.x()),
+                random.nextInt(size.y()),
+                random.nextInt(size.z())
+            );
+            iterations++;
+        } while (structure.getBlockType(queryPos) == Blocks.AIR && iterations < MAX_ITERATIONS);
+        if (iterations >= MAX_ITERATIONS) {
+            return;
+        }
+        if (canRot(structure.getBlockType(queryPos))) {
+            structure.setBlockType(queryPos, Blocks.AIR);
+        }
+    }
+
+    private boolean canRot(final @NotNull Block<?> block) {
+        return block == Blocks.PLANKS_OAK;
+    }
+}
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/SinkOperation.java b/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/SinkOperation.java
new file mode 100644
index 000000000..097bf1881
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/generate/feature/weatherable/operation/SinkOperation.java
@@ -0,0 +1,24 @@
+package net.minecraft.core.world.generate.feature.weatherable.operation;
+
+import net.minecraft.core.world.structure.EditableStructure;
+import net.minecraft.core.world.generate.feature.weatherable.Operation;
+import net.minecraft.core.world.structure.Structure;
+import org.jetbrains.annotations.NotNull;
+import org.joml.Vector3i;
+
+import java.util.Random;
+
+public class SinkOperation implements Operation {
+    @Override
+    public void apply(@NotNull EditableStructure structure, @NotNull Random random) {
+        final var origin = new Vector3i(structure.getOrigin());
+        final var size = structure.getSize();
+        if (origin.y() >= size.y() - 3) {
+            return;
+        }
+        if (random.nextInt(1000 / size.y()) == 0) {
+            origin.add(0, 1, 0);
+            structure.setOrigin(origin);
+        }
+    }
+}
\ No newline at end of file
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 e90dcfe49..ce758471b 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
@@ -1,11 +1,10 @@
 package net.minecraft.core.world.saveddata.maps;
 
+import com.mojang.nbt.tags.*;
 import net.minecraft.core.block.Blocks;
 import net.minecraft.core.block.entity.TileEntityFlag;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.item.ItemStack;
-import com.mojang.nbt.tags.CompoundTag;
-import com.mojang.nbt.tags.ListTag;
 import net.minecraft.core.util.helper.MathHelper;
 import net.minecraft.core.world.World;
 import net.minecraft.core.world.saveddata.SavedData;
@@ -18,7 +17,7 @@ public class ItemMapSavedData extends SavedData
     public static final byte MAX_SCALE = 4;
     public int x;
     public int z;
-    public byte dimension;
+    public int dimension;
     public byte scale;
     public byte[] colors;
     public int tick;
@@ -41,7 +40,17 @@ public class ItemMapSavedData extends SavedData
     @Override
 	public void load(CompoundTag tag)
     {
-        dimension = tag.getByte("dimension");
+        Tag<?> dimTag = tag.getTagOrDefault("dimension",null);
+        if(dimTag != null){
+            if(dimTag instanceof ByteTag byteTag){
+                //backwards compatibility reasons
+                dimension = byteTag.getValue();
+            } else if(dimTag instanceof IntTag intTag){
+                dimension = intTag.getValue();
+            }
+        } else {
+            dimension = 0;
+        }
         x = tag.getInteger("xCenter");
         z = tag.getInteger("zCenter");
         scale = tag.getByte("scale");
@@ -87,7 +96,7 @@ public class ItemMapSavedData extends SavedData
     @Override
 	public void save(CompoundTag tag)
     {
-        tag.putByte("dimension", dimension);
+        tag.putInt("dimension", dimension);
         tag.putInt("xCenter", x);
         tag.putInt("zCenter", z);
         tag.putByte("scale", scale);
diff --git a/game/core/src/main/java/net/minecraft/core/world/structure/DummyStructure.java b/game/core/src/main/java/net/minecraft/core/world/structure/DummyStructure.java
new file mode 100644
index 000000000..b9fe5975a
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/structure/DummyStructure.java
@@ -0,0 +1,59 @@
+package net.minecraft.core.world.structure;
+
+import net.minecraft.core.block.Block;
+import net.minecraft.core.block.Blocks;
+import net.minecraft.core.world.pos.TilePosc;
+import org.jetbrains.annotations.NotNull;
+import org.joml.Vector3i;
+import org.joml.Vector3ic;
+
+public class DummyStructure implements Structure {
+    private final @NotNull Vector3i size;
+    private final @NotNull Vector3i origin;
+
+    public DummyStructure(final @NotNull Vector3i size) {
+        this.size = size;
+
+        this.origin = new Vector3i(size.x / 2, 1, size.z / 2);
+    }
+
+    @Override
+    public @NotNull Vector3ic getSize() {
+        return this.size;
+    }
+
+    @Override
+    public @NotNull Vector3ic getOrigin() {
+        return this.origin;
+    }
+
+    @Override
+    public @NotNull Block<?> getBlockType(final @NotNull Vector3ic pos) {
+        if ((pos.y() == 1 || pos.y() == 2) && pos.x() == 0 && pos.z() == this.size.z / 2) {
+            return Blocks.AIR;
+        } else if ((pos.x() == 0 && pos.z() == 0) || (pos.x() == this.size.x - 1 && pos.z() == this.size.z - 1) || (pos.x() == 0 && pos.z() == this.size.z - 1) || (pos.x() == this.size.x - 1 && pos.z() == 0)) {
+            return Blocks.BRICK_STONE_POLISHED;
+        } else if (pos.x() == 0 || pos.x() == this.size.x - 1 || pos.z() == 0 || pos.z() == this.size.z - 1) {
+            if (pos.y() % 6 == 0) {
+                return Blocks.STONE_CARVED;
+            }
+            int total = ((pos.x() * 3) + (pos.y() * 2) + pos.z()) ^ 0x12345678;
+            if (total % 3 == 0) {
+                return Blocks.STONE;
+            } else if (total % 3 == 1) {
+                return Blocks.COBBLE_STONE;
+            } else {
+                return Blocks.BRICK_STONE;
+            }
+        } else if (pos.y() == 0 || pos.y() == this.size.y - 1) {
+            return Blocks.PLANKS_OAK;
+        } else {
+            return Blocks.AIR;
+        }
+    }
+
+    @Override
+    public int getBlockData(final @NotNull Vector3ic pos) {
+        return 0;
+    }
+}
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/world/structure/EditableStructure.java b/game/core/src/main/java/net/minecraft/core/world/structure/EditableStructure.java
new file mode 100644
index 000000000..e48857c4d
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/structure/EditableStructure.java
@@ -0,0 +1,118 @@
+package net.minecraft.core.world.structure;
+
+import net.minecraft.core.block.Block;
+import net.minecraft.core.block.Blocks;
+import net.minecraft.core.block.tag.BlockTags;
+import net.minecraft.core.world.World;
+import net.minecraft.core.world.pos.TilePos;
+import net.minecraft.core.world.pos.TilePosc;
+import org.jetbrains.annotations.NotNull;
+import org.joml.Vector3i;
+import org.joml.Vector3ic;
+
+public class EditableStructure implements Structure {
+    private final @NotNull Structure parent;
+    private final @NotNull Vector3i origin;
+    private final int[] blocks;
+    private final int[] data;
+
+    public EditableStructure(final @NotNull Structure parent) {
+        this.parent = parent;
+
+        this.origin = new Vector3i(parent.getOrigin());
+
+        final @NotNull Vector3ic size = parent.getSize();
+        this.blocks = new int[size.x() * size.y() * size.z()];
+        this.data = new int[size.x() * size.y() * size.z()];
+
+        final @NotNull Vector3i temp = new Vector3i();
+        for (int x = 0; x < size.x(); x++) {
+            for (int y = 0; y < size.y(); y++) {
+                for (int z = 0; z < size.z(); z++) {
+                    temp.set(x, y, z);
+                    this.blocks[toIndex(temp)] = parent.getBlockType(temp).id();
+                    this.data[toIndex(temp)] = parent.getBlockData(temp);
+                }
+            }
+        }
+    }
+
+    @Override
+    public @NotNull Vector3ic getSize() {
+        return this.parent.getSize();
+    }
+
+    @Override
+    public @NotNull Vector3ic getOrigin() {
+        return this.origin;
+    }
+
+    public void setOrigin(@NotNull Vector3ic origin) {
+        this.origin.set(origin);
+    }
+
+    private int toIndex(final @NotNull Vector3ic pos) {
+        final @NotNull Vector3ic size = getSize();
+        return (pos.y() * size.z() * size.x()) + (pos.z() * size.x()) + pos.x();
+    }
+
+    @Override
+    public @NotNull Block<?> getBlockType(final @NotNull Vector3ic pos) {
+        final @NotNull Vector3ic size = getSize();
+        if (pos.x() < 0 || pos.y() < 0 || pos.z() < 0 || pos.x() >= size.x() || pos.y() >= size.y() || pos.z() >= size.z()) {
+            return Blocks.AIR;
+        }
+        return Blocks.getBlock(this.blocks[toIndex(pos)]);
+    }
+
+    @Override
+    public int getBlockData(final @NotNull Vector3ic pos) {
+        final @NotNull Vector3ic size = getSize();
+        if (pos.x() < 0 || pos.y() < 0 || pos.z() < 0 || pos.x() >= size.x() || pos.y() >= size.y() || pos.z() >= size.z()) {
+            return 0;
+        }
+        return this.data[toIndex(pos)];
+    }
+
+    public void setBlockType(final @NotNull Vector3ic pos, final @NotNull Block<?> block) {
+        setBlockTypeData(pos, block, 0);
+    }
+
+    public void setBlockData(final @NotNull Vector3ic pos, final int data) {
+        final @NotNull Vector3ic size = getSize();
+        if (pos.x() < 0 || pos.y() < 0 || pos.z() < 0 || pos.x() >= size.x() || pos.y() >= size.y() || pos.z() >= size.z()) {
+            return;
+        }
+        this.data[toIndex(pos)] = data;
+    }
+
+    public void setBlockTypeData(final @NotNull Vector3ic pos, final @NotNull Block<?> block, int data) {
+        final @NotNull Vector3ic size = getSize();
+        if (pos.x() < 0 || pos.y() < 0 || pos.z() < 0 || pos.x() >= size.x() || pos.y() >= size.y() || pos.z() >= size.z()) {
+            return;
+        }
+        this.blocks[toIndex(pos)] = block.id();
+        this.data[toIndex(pos)] = data;
+    }
+
+    public void place(final @NotNull World world, final @NotNull TilePosc pos) {
+        final @NotNull Vector3ic size = getSize();
+        final @NotNull Vector3ic origin = getOrigin();
+
+        final @NotNull TilePosc relativePos = new TilePos(pos.x() - origin.x(), pos.y() - origin.y(), pos.z() - origin.z());
+
+        final @NotNull TilePos tempTilePos = new TilePos();
+        final @NotNull Vector3i tempVector = new Vector3i();
+        for (int dx = 0; dx < size.x(); dx++) {
+            for (int dy = 0; dy < size.y(); dy++) {
+                for (int dz = 0; dz < size.z(); dz++) {
+                    final @NotNull Block<?> block = getBlockType(tempVector.set(dx, dy, dz));
+                    final int data = getBlockData(tempVector);
+                    if (!block.hasTag(BlockTags.PLACE_OVERWRITES)) {
+                        world.setBlockTypeData(tempTilePos.set(relativePos).add(tempVector), block, data);
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/world/structure/SavedStructure.java b/game/core/src/main/java/net/minecraft/core/world/structure/SavedStructure.java
new file mode 100644
index 000000000..7dc501d62
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/structure/SavedStructure.java
@@ -0,0 +1,192 @@
+package net.minecraft.core.world.structure;
+
+import com.mojang.nbt.tags.CompoundTag;
+import com.mojang.nbt.NbtIo;
+import net.minecraft.core.Global;
+import net.minecraft.core.block.Block;
+import net.minecraft.core.block.Blocks;
+import net.minecraft.core.world.World;
+import net.minecraft.core.world.data.ChunkNibbleArray;
+import net.minecraft.core.world.pos.TilePos;
+import net.minecraft.core.world.pos.TilePosc;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.joml.Vector3i;
+import org.joml.Vector3ic;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.Map;
+
+public class SavedStructure implements Structure {
+    private static final @NotNull Map<String, SavedStructure> STRUCTURES = new HashMap<>();
+
+    private final @NotNull Vector3i size = new Vector3i();
+    private final @NotNull Vector3i origin = new Vector3i();
+    private final short @NotNull [] blocks;
+    private final @NotNull ChunkNibbleArray data;
+
+    public SavedStructure(final @NotNull Vector3ic size, final @NotNull Vector3ic origin) {
+        this(size, origin, new short[size.x() * size.y() * size.z()], new ChunkNibbleArray(size.x(), size.y(), size.z()));
+    }
+
+    private SavedStructure(final @NotNull Vector3ic size, final @NotNull Vector3ic origin, final short @NotNull [] blocks, final @NotNull ChunkNibbleArray data) {
+        this.size.set(size);
+        this.origin.set(origin);
+        this.blocks = blocks;
+        this.data = data;
+    }
+
+    @Override
+    public @NotNull Vector3ic getSize() {
+        return this.size;
+    }
+
+    @Override
+    public @NotNull Vector3ic getOrigin() {
+        return this.origin;
+    }
+
+    private int toIndex(final @NotNull Vector3ic pos) {
+        final @NotNull Vector3ic size = getSize();
+        return (pos.y() * size.z() * size.x()) + (pos.z() * size.x()) + pos.x();
+    }
+
+    @Override
+    public @NotNull Block<?> getBlockType(final @NotNull Vector3ic pos) {
+        final int index = toIndex(pos);
+        if (index < 0 || index >= this.blocks.length) {
+            return Blocks.AIR;
+        }
+        return Blocks.getBlock(this.blocks[index]);
+    }
+
+    public void setBlockType(final @NotNull Vector3ic pos, final @NotNull Block<?> block) {
+        final int index = toIndex(pos);
+        if (index < 0 || index >= this.blocks.length) {
+            return;
+        }
+        this.blocks[index] = (short) block.id();
+        setBlockData(pos, 0);
+    }
+
+    @Override
+    public int getBlockData(final @NotNull Vector3ic pos) {
+        return this.data.get(pos.x(), pos.y(), pos.z());
+    }
+
+    public void setBlockData(final @NotNull Vector3ic pos, final int data) {
+        this.data.set(pos.x(), pos.y(), pos.z(), data);
+    }
+
+    public void setBlockTypeData(final @NotNull Vector3ic pos, final @NotNull Block<?> block, final int data) {
+        setBlockType(pos, block);
+        setBlockData(pos, data);
+    }
+
+    public static boolean save(
+        final @NotNull World world,
+        final @NotNull String id,
+        final @NotNull TilePosc pos,
+        final @NotNull Vector3ic size,
+        final @NotNull Vector3ic origin
+    ) {
+        final @NotNull SavedStructure structure = new SavedStructure(size, origin);
+        final @NotNull Vector3i sPos = new Vector3i();
+        final @NotNull TilePos queryPos = new TilePos();
+        for (int x = 0; x < size.x(); x++) {
+            for (int y = 0; y < size.y(); y++) {
+                for (int z = 0; z < size.z(); z++) {
+                    sPos.set(x, y, z);
+                    queryPos.set(pos).add(sPos);
+                    structure.setBlockType(sPos, world.getBlockType(queryPos));
+                    structure.setBlockData(sPos, world.getBlockData(queryPos));
+                }
+            }
+        }
+        STRUCTURES.put(id, structure);
+        return writeTag(toNbt(structure), id);
+    }
+
+    private static @NotNull CompoundTag toNbt(final @NotNull SavedStructure structure) {
+        final @NotNull CompoundTag tag = new CompoundTag();
+        tag.putInt("formatVersion", 1);
+
+        final @NotNull CompoundTag structureTag = new CompoundTag();
+        structureTag.putInt("sizeX", structure.size.x);
+        structureTag.putInt("sizeY", structure.size.y);
+        structureTag.putInt("sizeZ", structure.size.z);
+        structureTag.putInt("originX", structure.origin.x);
+        structureTag.putInt("originY", structure.origin.y);
+        structureTag.putInt("originZ", structure.origin.z);
+        structureTag.putShortArray("blocks", structure.blocks);
+        structureTag.putByteArray("data", structure.data.data);
+
+        tag.put("structure", structureTag);
+
+        return tag;
+    }
+
+    private static boolean writeTag(final @NotNull CompoundTag tag, final @NotNull String id) {
+        try {
+            final @NotNull File directory = new File(Global.accessor.getMinecraftDir(), "structures");
+            if (!directory.exists()) {
+                if (!directory.mkdirs()) {
+                    return false;
+                }
+            }
+            final @NotNull File file = new File(directory, id + ".nbt");
+            if (!file.exists()) {
+                if (!file.createNewFile()) {
+                    return false;
+                }
+            }
+            try (final @NotNull FileOutputStream stream = new FileOutputStream(file)) {
+                NbtIo.writeCompressed(tag, stream);
+            }
+            return true;
+        } catch (final @NotNull IOException e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    public static @Nullable SavedStructure load(final @NotNull String id) {
+        if (STRUCTURES.containsKey(id)) {
+            return STRUCTURES.get(id);
+        }
+        @Nullable InputStream stream = Structure.class.getResourceAsStream("/structures/" + id + ".nbt");
+        if (stream == null) {
+            try {
+                final @NotNull File file = new File(Global.accessor.getMinecraftDir(), "structures/" + id + ".nbt");
+                if (file.exists()) {
+                    stream = new FileInputStream(file);
+                }
+            } catch (final @NotNull IOException e) {
+                e.printStackTrace();
+            }
+        }
+        try {
+            final @NotNull CompoundTag tag = NbtIo.readCompressed(stream);
+            final @NotNull SavedStructure structure = fromNbt(tag);
+            STRUCTURES.put(id, structure);
+            return structure;
+        } catch (final @NotNull IOException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    private static @NotNull SavedStructure fromNbt(final @NotNull CompoundTag tag) {
+        final @NotNull CompoundTag structureTag = tag.getCompound("structure");
+        int sizeX = structureTag.getInteger("sizeX");
+        int sizeY = structureTag.getInteger("sizeY");
+        int sizeZ = structureTag.getInteger("sizeZ");
+        int originX = structureTag.getInteger("originX");
+        int originY = structureTag.getInteger("originY");
+        int originZ = structureTag.getInteger("originZ");
+        short[] blocks = structureTag.getShortArray("blocks");
+        byte[] data = structureTag.getByteArray("data");
+        return new SavedStructure(new Vector3i(sizeX, sizeY, sizeZ), new Vector3i(originX, originY, originZ), blocks, new ChunkNibbleArray(sizeX, sizeY, sizeZ, data));
+    }
+}
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/world/structure/Structure.java b/game/core/src/main/java/net/minecraft/core/world/structure/Structure.java
new file mode 100644
index 000000000..ceb2fd878
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/structure/Structure.java
@@ -0,0 +1,12 @@
+package net.minecraft.core.world.structure;
+
+import net.minecraft.core.block.Block;
+import org.jetbrains.annotations.NotNull;
+import org.joml.Vector3ic;
+
+public interface Structure {
+    @NotNull Vector3ic getSize();
+    @NotNull Vector3ic getOrigin();
+    @NotNull Block<?> getBlockType(final @NotNull Vector3ic pos);
+    int getBlockData(final @NotNull Vector3ic pos);
+}
\ No newline at end of file
diff --git a/game/core/src/main/resources/lang/en_US/tile.lang b/game/core/src/main/resources/lang/en_US/tile.lang
index 5d0c333b1..163bee0e0 100644
--- a/game/core/src/main/resources/lang/en_US/tile.lang
+++ b/game/core/src/main/resources/lang/en_US/tile.lang
@@ -1466,3 +1466,6 @@ tile.matcher.name=Matcher
 tile.matcher.desc=Matches the block in front to the block behind.
 tile.matcher.active.name=Matcher
 tile.matcher.desc=Matches the block in front to the block behind.
+
+tile.flowstone.name=Flowstone
+tile.flowstone.desc=A rocky growth formed over millennia.
\ No newline at end of file
diff --git a/game/server/src/main/java/net/minecraft/server/MinecraftServer.java b/game/server/src/main/java/net/minecraft/server/MinecraftServer.java
index df7242fef..713a12eb7 100644
--- a/game/server/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/game/server/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -25,35 +25,7 @@ import net.minecraft.core.net.NetworkManager;
 import net.minecraft.core.net.PropertyManager;
 import net.minecraft.core.net.ServerCommandEntry;
 import net.minecraft.core.net.command.CommandManager;
-import net.minecraft.core.net.command.commands.CommandAchievement;
-import net.minecraft.core.net.command.commands.CommandBiome;
-import net.minecraft.core.net.command.commands.CommandChunk;
-import net.minecraft.core.net.command.commands.CommandClear;
-import net.minecraft.core.net.command.commands.CommandClone;
-import net.minecraft.core.net.command.commands.CommandDamage;
-import net.minecraft.core.net.command.commands.CommandFill;
-import net.minecraft.core.net.command.commands.CommandGameMode;
-import net.minecraft.core.net.command.commands.CommandGameRule;
-import net.minecraft.core.net.command.commands.CommandGive;
-import net.minecraft.core.net.command.commands.CommandHeal;
-import net.minecraft.core.net.command.commands.CommandHelp;
-import net.minecraft.core.net.command.commands.CommandKill;
-import net.minecraft.core.net.command.commands.CommandMessage;
-import net.minecraft.core.net.command.commands.CommandMobSpawning;
-import net.minecraft.core.net.command.commands.CommandPlace;
-import net.minecraft.core.net.command.commands.CommandPlaySound;
-import net.minecraft.core.net.command.commands.CommandSay;
-import net.minecraft.core.net.command.commands.CommandSeed;
-import net.minecraft.core.net.command.commands.CommandSetBlock;
-import net.minecraft.core.net.command.commands.CommandSetSpawn;
-import net.minecraft.core.net.command.commands.CommandSpawn;
-import net.minecraft.core.net.command.commands.CommandParticle;
-import net.minecraft.core.net.command.commands.CommandSummon;
-import net.minecraft.core.net.command.commands.CommandTeleport;
-import net.minecraft.core.net.command.commands.CommandTellRaw;
-import net.minecraft.core.net.command.commands.CommandTestFor;
-import net.minecraft.core.net.command.commands.CommandTime;
-import net.minecraft.core.net.command.commands.CommandWeather;
+import net.minecraft.core.net.command.commands.*;
 import net.minecraft.core.net.command.util.CommandHelper;
 import net.minecraft.core.player.gamemode.Gamemodes;
 import net.minecraft.core.util.pool.ObjectPool;
@@ -449,6 +421,7 @@ public class MinecraftServer
         CommandManager.registerCommand(new CommandMobSpawning());
         CommandManager.registerCommand(new CommandPlaySound());
         CommandManager.registerCommand(new CommandParticle());
+        CommandManager.registerCommand(new CommandStructure());
 
         CommandManager.registerServerCommand(new CommandStop());
         CommandManager.registerServerCommand(new CommandOp());
