diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicCrystal.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicCrystal.java
deleted file mode 100644
index e69de29bb..000000000
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicThermalVent.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicThermalVent.java
index 2f1fcc15b..fa438a8f0 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicThermalVent.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicThermalVent.java
@@ -1,8 +1,10 @@
 package net.minecraft.core.block;
 
 import net.minecraft.core.block.material.Materials;
+import net.minecraft.core.block.tag.BlockTags;
 import net.minecraft.core.entity.Entity;
 import net.minecraft.core.sound.SoundCategory;
+import net.minecraft.core.util.helper.Direction;
 import net.minecraft.core.world.World;
 import net.minecraft.core.world.pos.TilePos;
 import net.minecraft.core.world.pos.TilePosc;
@@ -13,29 +15,87 @@ import java.util.List;
 import java.util.Random;
 
 public class BlockLogicThermalVent
-    extends BlockLogic {
+        extends BlockLogic {
+    public static final int META_MASK = 0b0000_0111;
+    public static int DORMANT_STATE = 0;
+    public static int VENTING_STATE = 4;
 
     public BlockLogicThermalVent(final @NotNull Block<?> block) {
         super(block, Materials.STONE);
-
+        block.setTicking(true);
     }
 
     @Override
     public void animationTick(final @NotNull World world, final @NotNull TilePosc tilePos, final @NotNull Random rand) {
+        final int state = getVentState(world.getBlockData(tilePos));
 
         final double xPos = (double) tilePos.x() + 0.5D;
         final double yPos = (double) tilePos.y() + 1.125D;
         final double zPos = (double) tilePos.z() + 0.5D;
 
-        TilePos queryPos = new TilePos();
-        tilePos.up(queryPos);
+        if(world.getBlockType(tilePos.up(new TilePos())).solid()){
+            return;
+        }
+
+        world.spawnParticle("ventsmoke", xPos, yPos, zPos, 0.0D, 0.005D, 0.0D, 1, 32);
 
-        if(world.isAirBlock(queryPos)){
-            world.spawnParticle("ventsmoke", xPos, yPos, zPos, 0.0D, 0.005D, 0.0D, 1, 32);
+        if (state >= VENTING_STATE - 1) {
+            for (int i = 0; i < 4; i++) {
+                world.spawnParticle("ventsmoke", xPos, yPos, zPos, 0.0D, 0.005D, 0.0D, 0, 32);
+                world.spawnParticle("smoke", xPos + rand.nextFloat() - 0.5D, yPos, zPos + rand.nextFloat() - 0.5D, 0.0D, 0.005D, 0.0D, 1, 32);
+            }
         }
+    }
 
+    private static int getBuildupRate(final @NotNull World world, final @NotNull TilePosc tilePos, final @NotNull Random rand) {
+        if (world.getBlockType(tilePos.down(new TilePos())) == Blocks.MAGMA) {
+            return 2;
+        }
+        return 1;
+    }
 
+    @Override
+    public void updateTick(final @NotNull World world, final @NotNull TilePosc tilePos, final @NotNull Random rand, final boolean isRandomTick) {
+        if (world.getBlockType(tilePos.up(new TilePos())).solid()) {
+            return;
+        }
+
+        int state = getVentState(world.getBlockData(tilePos));
+        if (state < VENTING_STATE) {
+            final int buildUpRate = getBuildupRate(world, tilePos, rand);
+            state += buildUpRate;
+            world.setBlockDataNotify(tilePos, state);
+
+        } else {
+            vent(world, tilePos, rand);
+        }
     }
 
+    private static int getVentState(int data){
+        return META_MASK & data;
+    }
+
+
+    private static void vent(final @NotNull World world, final @NotNull TilePosc tilePos, final @NotNull Random rand) {
+        for (int i = 0; i < 40; i++) {
+            final double xPos = tilePos.x() + 0.5D;
+            final double yPos = tilePos.y() + 1.125D;
+            final double zPos = tilePos.z() + 0.5D;
+
+            final double xd = (rand.nextFloat() - 0.5d) * 0.5d;
+            final double zd = (rand.nextFloat() - 0.5d) * 0.5d;
+
+            world.spawnParticle("ventsmoke", xPos, yPos, zPos, 0.0D, 0.25D + rand.nextFloat(), 0.0D, 1, 32);
+            world.spawnParticle("flame", xPos, yPos, zPos, xd, 0.25D + rand.nextFloat(), zd, 0, 32);
+        }
+
+        world.playSoundEffect(null, SoundCategory.WORLD_SOUNDS, tilePos.x(), tilePos.y(), tilePos.z(), "random.explode", 2.0F, 0.5F + rand.nextFloat() * 0.2F);
+
+        final List<Entity> entities = world.getEntitiesWithinAABB(Entity.class, new AABBd(tilePos.x() - 1, tilePos.y(), tilePos.z() - 1, tilePos.x() + 1, tilePos.y() + 6, tilePos.z() + 1));
+        for (final @NotNull Entity entity : entities) {
+            entity.fling(0, 2, 0, 1f);
+        }
+        world.setBlockDataNotify(tilePos, DORMANT_STATE);
+    }
 
 }
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 aa74514bc..6fec006f3 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
@@ -120,11 +120,18 @@ public class ChunkDecoratorNether
                         .withPlacementMethod(PlacementMethods.OncePerChunk)
         );
 
+        this.register("minecraft:decoration/nether/default/sulfur_pool_floor_vent",
+                new ChunkDecorationBuilder(new WorldFeatureFloorVent())
+                        .withBiomeMask(new Biome[]{ Biomes.NETHER_SULFUR_POOLS })
+                        .withPositionSelector(PositionSelectors.HeightRangeUniformFromOcean)
+                        .withPlacementMethod(new PlacementMethod.TriesPerChunk(12))
+        );
+
         this.register("minecraft:decoration/nether/default/sulfur_pool_spire",
                 new ChunkDecorationBuilder(new WorldFeatureThermalSpire())
                         .withBiomeMask(new Biome[]{ Biomes.NETHER_SULFUR_POOLS })
                         .withPositionSelector(PositionSelectors.HeightRangeUniformFromOcean)
-                        .withPlacementMethod(new PlacementMethod.TriesPerChunk(12))
+                        .withPlacementMethod(new PlacementMethod.TriesPerChunk(8))
         );
 
         this.register("minecraft:decoration/nether/default/nether_coal_ore",
diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/feature/WorldFeatureFloorVent.java b/game/core/src/main/java/net/minecraft/core/world/generate/feature/WorldFeatureFloorVent.java
new file mode 100644
index 000000000..e9efe2239
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/generate/feature/WorldFeatureFloorVent.java
@@ -0,0 +1,91 @@
+package net.minecraft.core.world.generate.feature;
+
+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 java.util.Random;
+
+public class WorldFeatureFloorVent implements WorldFeatureInterface
+{
+    private final int minRadius;
+    private final int radiusMod;
+
+
+    public WorldFeatureFloorVent(int minRadius, int radiusMod)
+    {
+        this.minRadius = minRadius;
+        this.radiusMod = radiusMod;
+    }
+
+    public WorldFeatureFloorVent()
+    {
+        this.minRadius = 1;
+        this.radiusMod = 2;
+    }
+
+    private boolean isAirOrLiquid(World world, TilePos tilePos){
+        return world.isAirBlock(tilePos) ||
+                world.getBlockType(tilePos).hasTag(BlockTags.IS_LAVA) ||
+                world.getBlockType(tilePos).hasTag(BlockTags.IS_WATER);
+    }
+
+    @Override
+    public boolean place(@NotNull World world, @NotNull Random random, @NotNull TilePosc tilePos) {
+        TilePos pos = (TilePos) tilePos;
+        while(isAirOrLiquid(world, pos) && pos.y - 1 > 0) {pos.down(); }
+
+        int radius = minRadius + random.nextInt(radiusMod);
+
+        TilePos centerPos = new TilePos(pos.x() + random.nextInt(8), pos.y(), pos.z() + random.nextInt(8));
+        TilePos cursor = new TilePos();
+
+        if(!canPlace(centerPos, world) && !isAirOrLiquid(world, centerPos.up(new TilePos()))){
+            return false;
+        }
+
+        for(cursor.x = centerPos.x() - radius; cursor.x <= centerPos.x() + radius; cursor.x++) {
+            for (cursor.z = centerPos.z() - radius; cursor.z <= centerPos.z() + radius; cursor.z++) {
+                int xd = cursor.x() - centerPos.x();
+                int zd = cursor.z() - centerPos.z();
+
+                float dist = xd * xd + zd * zd;
+
+                for(cursor.y = centerPos.y(); cursor.y > pos.y - 4; cursor.y--) {
+
+                    if(cursor.y <= 0)
+                        continue;
+
+                    // Skip placement if cursor hasn't hit ground yet
+
+                    if(dist <= (radius * radius) && canPlace(cursor, world)) {
+                        if(cursor.equals(centerPos)){
+                            world.setBlockTypeNotify(cursor, Blocks.THERMAL_VENT);
+                        } else {
+                            if(random.nextInt(4) != 0) {
+                                world.setBlockTypeNotify(cursor, Blocks.BASALT);
+                            } else if(random.nextInt(20) == 0) {
+                                    world.setBlockTypeNotify(cursor, Blocks.THERMAL_VENT);
+                            }
+
+                        }
+
+                    }
+                }
+            }
+        }
+        return true;
+    }
+
+    private boolean canPlace(TilePos cursor, World world) {
+
+        if (isAirOrLiquid(world, cursor)) {
+            return false;
+        }
+
+        return world.getBlockType(cursor).hasTag(BlockTags.NETHER_SURFACE_BLOCK);
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/feature/WorldFeatureMagmaBoulder.java b/game/core/src/main/java/net/minecraft/core/world/generate/feature/WorldFeatureMagmaBoulder.java
index d2912096f..6146fcf30 100644
--- a/game/core/src/main/java/net/minecraft/core/world/generate/feature/WorldFeatureMagmaBoulder.java
+++ b/game/core/src/main/java/net/minecraft/core/world/generate/feature/WorldFeatureMagmaBoulder.java
@@ -36,9 +36,7 @@ public class WorldFeatureMagmaBoulder extends WorldFeature{
                     for (circlePos.y = pos.y() - 4; circlePos.y < pos.y + 4; circlePos.y++) {
 
                         if (!world.isAirBlock(circlePos)) {
-                            if (random.nextInt(16) == 0)
-                                world.setBlockType(circlePos, Blocks.MAGMA);
-                            else if (random.nextInt(4) == 0)
+                            if (random.nextInt(8) == 0)
                                 world.setBlockType(circlePos, Blocks.COBBLE_BASALT);
                         }
                     }
@@ -51,7 +49,7 @@ public class WorldFeatureMagmaBoulder extends WorldFeature{
     }
 
     private Block<?> getBlockAt(Vector2i center, int x, int z){
-        return Blocks.COBBLE_BASALT;
+        return Blocks.MAGMA;
     }
 
     private void plotBlocks(World world, Vector2i center, TilePos queryPos, int x, int y, int z){
diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/feature/WorldFeatureThermalVents.java b/game/core/src/main/java/net/minecraft/core/world/generate/feature/WorldFeatureThermalVents.java
deleted file mode 100644
index 1597cd77b..000000000
--- a/game/core/src/main/java/net/minecraft/core/world/generate/feature/WorldFeatureThermalVents.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package net.minecraft.core.world.generate.feature;
-
-import net.minecraft.core.block.Blocks;
-import net.minecraft.core.block.tag.BlockTags;
-import net.minecraft.core.util.helper.Direction;
-import net.minecraft.core.world.World;
-import net.minecraft.core.world.pos.TilePos;
-
-import java.util.Random;
-
-public class WorldFeatureThermalVents extends WorldFeature
-{
-
-    private final int minRadius;
-    private final int radiusMod;
-
-
-    public WorldFeatureThermalVents(int minRadius, int radiusMod)
-    {
-        this.minRadius = minRadius;
-        this.radiusMod = radiusMod;
-    }
-
-    public WorldFeatureThermalVents()
-    {
-        this.minRadius = 4;
-        this.radiusMod = 6;
-    }
-
-    @Override
-	public boolean place(World world, Random random, int x, int y, int z)
-    {
-        int radius = minRadius + random.nextInt(radiusMod);
-
-        TilePos tilePos = new TilePos(x, y, z);
-        while(world.isAirBlock(tilePos) && tilePos.y - 1 > 0) {tilePos.down(); }
-
-        TilePos queryPos = new TilePos();
-        TilePos blockAbove = new TilePos();
-
-        for (int r = 0; r < 10; r++) {
-            int radModX = random.nextInt(radius) - random.nextInt(radius);
-            int radModY = random.nextInt(radius) - random.nextInt(radius);
-            int radModZ = random.nextInt(radius) - random.nextInt(radius);
-
-            int height = random.nextInt(5);
-
-            tilePos.add(radModX, radModY, radModZ, queryPos);
-            queryPos.add(0, 1, 0, blockAbove);
-            boolean canBlockSpawn = world.getBlockType(queryPos).hasTag(BlockTags.NETHER_SURFACE_BLOCK);
-
-            if (canBlockSpawn) {
-                for(int i = 1; i < height; i++){
-                    queryPos.add(0, i, 0, blockAbove);
-
-                    if(!world.isAirBlock(blockAbove)){
-                        break;
-                    }
-
-                    world.setBlockTypeNotify(blockAbove, Blocks.THERMAL_VENT);
-                }
-            }
-
-        }
-
-        return true;
-    }
-}
