diff --git a/src/api/java/baritone/api/schematic/AbstractSchematic.java b/src/api/java/baritone/api/schematic/AbstractSchematic.java
new file mode 100644
index 00000000..3b6bb41c
--- /dev/null
+++ b/src/api/java/baritone/api/schematic/AbstractSchematic.java
@@ -0,0 +1,30 @@
+package baritone.api.schematic;
+
+import baritone.api.utils.ISchematic;
+
+public abstract class AbstractSchematic implements ISchematic {
+    protected int x;
+    protected int y;
+    protected int z;
+
+    public AbstractSchematic(int x, int y, int z) {
+        this.x = x;
+        this.y = y;
+        this.z = z;
+    }
+
+    @Override
+    public int widthX() {
+        return x;
+    }
+
+    @Override
+    public int heightY() {
+        return y;
+    }
+
+    @Override
+    public int lengthZ() {
+        return z;
+    }
+}
diff --git a/src/api/java/baritone/api/schematic/CompositeSchematic.java b/src/api/java/baritone/api/schematic/CompositeSchematic.java
new file mode 100644
index 00000000..56c49b80
--- /dev/null
+++ b/src/api/java/baritone/api/schematic/CompositeSchematic.java
@@ -0,0 +1,61 @@
+package baritone.api.schematic;
+
+import baritone.api.utils.ISchematic;
+import net.minecraft.block.state.IBlockState;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CompositeSchematic extends AbstractSchematic {
+    private final List<CompositeSchematicEntry> schematics;
+    private CompositeSchematicEntry[] schematicArr;
+
+    private void recalcArr() {
+        schematicArr = schematics.toArray(new CompositeSchematicEntry[0]);
+
+        for (CompositeSchematicEntry entry : schematicArr) {
+            this.x = Math.max(x, entry.x + entry.schematic.widthX());
+            this.y = Math.max(y, entry.y + entry.schematic.heightY());
+            this.z = Math.max(z, entry.z + entry.schematic.lengthZ());
+        }
+    }
+
+    public CompositeSchematic(int x, int y, int z) {
+        super(x, y, z);
+        schematics = new ArrayList<>();
+        recalcArr();
+    }
+
+    public void put(ISchematic extra, int x, int y, int z) {
+        schematics.add(new CompositeSchematicEntry(extra, x, y, z));
+        recalcArr();
+    }
+
+    private CompositeSchematicEntry getSchematic(int x, int y, int z, IBlockState currentState) {
+        for (CompositeSchematicEntry entry : schematicArr) {
+            if (x >= entry.x && y >= entry.y && z >= entry.z &&
+                entry.schematic.inSchematic(x - entry.x, y - entry.y, z - entry.z, currentState)) {
+                return entry;
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public boolean inSchematic(int x, int y, int z, IBlockState currentState) {
+        CompositeSchematicEntry entry = getSchematic(x, y, z, currentState);
+        return entry != null && entry.schematic.inSchematic(x - entry.x, y - entry.y, z - entry.z, currentState);
+    }
+
+    @Override
+    public IBlockState desiredState(int x, int y, int z, IBlockState current) {
+        CompositeSchematicEntry entry = getSchematic(x, y, z, current);
+
+        if (entry == null) {
+            throw new IllegalStateException("couldn't find schematic for this position");
+        }
+
+        return entry.schematic.desiredState(x - entry.x, y - entry.y, z - entry.z, current);
+    }
+}
diff --git a/src/api/java/baritone/api/schematic/CompositeSchematicEntry.java b/src/api/java/baritone/api/schematic/CompositeSchematicEntry.java
new file mode 100644
index 00000000..c34cd112
--- /dev/null
+++ b/src/api/java/baritone/api/schematic/CompositeSchematicEntry.java
@@ -0,0 +1,17 @@
+package baritone.api.schematic;
+
+import baritone.api.utils.ISchematic;
+
+public class CompositeSchematicEntry {
+    public final ISchematic schematic;
+    public final int x;
+    public final int y;
+    public final int z;
+
+    public CompositeSchematicEntry(ISchematic schematic, int x, int y, int z) {
+        this.schematic = schematic;
+        this.x = x;
+        this.y = y;
+        this.z = z;
+    }
+}
diff --git a/src/api/java/baritone/api/schematic/FillBomSchematic.java b/src/api/java/baritone/api/schematic/FillBomSchematic.java
new file mode 100644
index 00000000..b7536e81
--- /dev/null
+++ b/src/api/java/baritone/api/schematic/FillBomSchematic.java
@@ -0,0 +1,26 @@
+package baritone.api.schematic;
+
+import baritone.api.utils.BlockOptionalMeta;
+import net.minecraft.block.state.IBlockState;
+
+public class FillBomSchematic extends AbstractSchematic {
+    private final BlockOptionalMeta bom;
+
+    public FillBomSchematic(int x, int y, int z, BlockOptionalMeta bom) {
+        super(x, y, z);
+        this.bom = bom;
+    }
+
+    public BlockOptionalMeta getBom() {
+        return bom;
+    }
+
+    @Override
+    public IBlockState desiredState(int x, int y, int z, IBlockState current) {
+        if (bom.matches(current)) {
+            return current;
+        }
+
+        return bom.getAnyBlockState();
+    }
+}
diff --git a/src/api/java/baritone/api/schematic/MaskSchematic.java b/src/api/java/baritone/api/schematic/MaskSchematic.java
new file mode 100644
index 00000000..033e390c
--- /dev/null
+++ b/src/api/java/baritone/api/schematic/MaskSchematic.java
@@ -0,0 +1,25 @@
+package baritone.api.schematic;
+
+import baritone.api.utils.ISchematic;
+import net.minecraft.block.state.IBlockState;
+
+public abstract class MaskSchematic extends AbstractSchematic {
+    private final ISchematic schematic;
+
+    public MaskSchematic(ISchematic schematic) {
+        super(schematic.widthX(), schematic.heightY(), schematic.lengthZ());
+        this.schematic = schematic;
+    }
+
+    protected abstract boolean partOfMask(int x, int y, int z, IBlockState currentState);
+
+    @Override
+    public boolean inSchematic(int x, int y, int z, IBlockState currentState) {
+        return schematic.inSchematic(x, y, z, currentState) && partOfMask(x, y, z, currentState);
+    }
+
+    @Override
+    public IBlockState desiredState(int x, int y, int z, IBlockState current) {
+        return schematic.desiredState(x, y, z, current);
+    }
+}
diff --git a/src/api/java/baritone/api/schematic/ShellSchematic.java b/src/api/java/baritone/api/schematic/ShellSchematic.java
new file mode 100644
index 00000000..2c94fa9f
--- /dev/null
+++ b/src/api/java/baritone/api/schematic/ShellSchematic.java
@@ -0,0 +1,14 @@
+package baritone.api.schematic;
+
+import baritone.api.utils.ISchematic;
+import net.minecraft.block.state.IBlockState;
+
+public class ShellSchematic extends MaskSchematic {
+    public ShellSchematic(ISchematic schematic) {
+        super(schematic);
+    }
+
+    protected boolean partOfMask(int x, int y, int z, IBlockState currentState) {
+        return x == 0 || y == 0 || z == 0 || x == widthX() - 1 || y == heightY() - 1 || z == lengthZ() - 1;
+    }
+}
diff --git a/src/api/java/baritone/api/schematic/WallsSchematic.java b/src/api/java/baritone/api/schematic/WallsSchematic.java
new file mode 100644
index 00000000..8091944e
--- /dev/null
+++ b/src/api/java/baritone/api/schematic/WallsSchematic.java
@@ -0,0 +1,14 @@
+package baritone.api.schematic;
+
+import baritone.api.utils.ISchematic;
+import net.minecraft.block.state.IBlockState;
+
+public class WallsSchematic extends MaskSchematic {
+    public WallsSchematic(ISchematic schematic) {
+        super(schematic);
+    }
+
+    protected boolean partOfMask(int x, int y, int z, IBlockState currentState) {
+        return x == 0 || z == 0 || x == widthX() - 1 || z == lengthZ() - 1;
+    }
+}
diff --git a/src/api/java/baritone/api/utils/BlockOptionalMeta.java b/src/api/java/baritone/api/utils/BlockOptionalMeta.java
index 90d163d8..bd17307c 100644
--- a/src/api/java/baritone/api/utils/BlockOptionalMeta.java
+++ b/src/api/java/baritone/api/utils/BlockOptionalMeta.java
@@ -31,7 +31,6 @@ import net.minecraft.block.BlockDoublePlant;
 import net.minecraft.block.BlockFence;
 import net.minecraft.block.BlockFire;
 import net.minecraft.block.BlockGrass;
-import net.minecraft.block.BlockHorizontal;
 import net.minecraft.block.BlockLeaves;
 import net.minecraft.block.BlockLever;
 import net.minecraft.block.BlockLog;
@@ -323,4 +322,12 @@ public final class BlockOptionalMeta {
         //noinspection deprecation
         return Block.getBlockFromItem(stack.getItem()).getStateFromMeta(stack.getMetadata());
     }
+
+    public IBlockState getAnyBlockState() {
+        if (blockstates.size() > 0) {
+            return blockstates.iterator().next();
+        }
+
+        return null;
+    }
 }
diff --git a/src/api/java/baritone/api/utils/command/defaults/SelCommand.java b/src/api/java/baritone/api/utils/command/defaults/SelCommand.java
index ef66be5e..c696bd72 100644
--- a/src/api/java/baritone/api/utils/command/defaults/SelCommand.java
+++ b/src/api/java/baritone/api/utils/command/defaults/SelCommand.java
@@ -19,15 +19,25 @@ package baritone.api.utils.command.defaults;
 
 import baritone.api.Settings;
 import baritone.api.event.events.RenderEvent;
+import baritone.api.schematic.CompositeSchematic;
+import baritone.api.schematic.FillBomSchematic;
+import baritone.api.schematic.ShellSchematic;
+import baritone.api.schematic.WallsSchematic;
+import baritone.api.selection.ISelection;
 import baritone.api.utils.BetterBlockPos;
+import baritone.api.utils.BlockOptionalMeta;
 import baritone.api.utils.IRenderer;
+import baritone.api.utils.ISchematic;
 import baritone.api.utils.command.Command;
+import baritone.api.utils.command.datatypes.ForBlockOptionalMeta;
 import baritone.api.utils.command.datatypes.RelativeBlockPos;
 import baritone.api.utils.command.exception.CommandInvalidStateException;
 import baritone.api.utils.command.exception.CommandInvalidTypeException;
 import baritone.api.utils.command.helpers.arguments.ArgConsumer;
 import baritone.api.utils.command.helpers.tabcomplete.TabCompleteHelper;
+import net.minecraft.init.Blocks;
 import net.minecraft.util.math.AxisAlignedBB;
+import net.minecraft.util.math.Vec3i;
 
 import java.awt.Color;
 import java.util.HashSet;
@@ -75,6 +85,46 @@ public class SelCommand extends Command {
                 "Removed %d selections",
                 baritone.getSelectionManager().removeAllSelections().length
             ));
+        } else {
+            BlockOptionalMeta type = action == Action.CLEARAREA
+                ? new BlockOptionalMeta(Blocks.AIR)
+                : args.getDatatypeFor(ForBlockOptionalMeta.class);
+            args.requireMax(0);
+            ISelection[] selections = baritone.getSelectionManager().getSelections();
+
+            if (selections.length == 0) {
+                throw new CommandInvalidStateException("No selections");
+            }
+
+            BetterBlockPos origin = selections[0].min();
+            CompositeSchematic composite = new CompositeSchematic(0, 0, 0);
+
+            for (ISelection selection : selections) {
+                BetterBlockPos min = selection.min();
+                origin = new BetterBlockPos(
+                    Math.min(origin.x, min.x),
+                    Math.min(origin.y, min.y),
+                    Math.min(origin.z, min.z)
+                );
+            }
+
+            for (ISelection selection : selections) {
+                Vec3i size = selection.size();
+                BetterBlockPos min = selection.min();
+
+                ISchematic schematic = new FillBomSchematic(size.getX(), size.getY(), size.getZ(), type);
+
+                if (action == Action.WALLS) {
+                    schematic = new WallsSchematic(schematic);
+                } else if (action == Action.SHELL) {
+                    schematic = new ShellSchematic(schematic);
+                }
+
+                composite.put(schematic, min.x - origin.x, min.y - origin.y, min.z - origin.z);
+            }
+
+            baritone.getBuilderProcess().build("Fill", composite, origin);
+            logDirect("Filling now");
         }
     }
 
@@ -112,7 +162,11 @@ public class SelCommand extends Command {
     enum Action {
         POS1("pos1", "p1"),
         POS2("pos2", "p2"),
-        CLEAR("clear", "c");
+        CLEAR("clear", "c"),
+        SET("set", "fill", "s", "f"),
+        WALLS("walls", "w"),
+        SHELL("shell", "sh"),
+        CLEARAREA("cleararea", "ca");
 
         private final String[] names;