diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/model/GemEntityModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/model/GemEntityModel.java
index cbefae22..ca23f131 100644
--- a/src/main/java/com/minelittlepony/unicopia/client/render/model/GemEntityModel.java
+++ b/src/main/java/com/minelittlepony/unicopia/client/render/model/GemEntityModel.java
@@ -1,7 +1,8 @@
 package com.minelittlepony.unicopia.client.render.model;
 
 import com.minelittlepony.unicopia.entity.SpellcastEntity;
-import com.minelittlepony.unicopia.util.Color;
+import com.minelittlepony.util.Color;
+
 import net.minecraft.client.model.ModelPart;
 import net.minecraft.client.render.VertexConsumer;
 import net.minecraft.client.render.entity.model.EntityModel;
diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCamera.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCamera.java
index 643e448b..2c40682f 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCamera.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCamera.java
@@ -1,6 +1,6 @@
 package com.minelittlepony.unicopia.entity.player;
 
-import com.minelittlepony.unicopia.util.MotionCompositor;
+import com.minelittlepony.util.MotionCompositor;
 
 import net.minecraft.util.math.Vec3d;
 
diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerImpl.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerImpl.java
index f0f96235..1e7e3cd1 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerImpl.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerImpl.java
@@ -18,8 +18,8 @@ import com.minelittlepony.unicopia.magic.spell.SpellRegistry;
 import com.minelittlepony.unicopia.network.Channel;
 import com.minelittlepony.unicopia.network.EffectSync;
 import com.minelittlepony.unicopia.network.MsgPlayerCapabilities;
-import com.minelittlepony.unicopia.util.BasicEasingInterpolator;
-import com.minelittlepony.unicopia.util.IInterpolator;
+import com.minelittlepony.util.BasicEasingInterpolator;
+import com.minelittlepony.util.IInterpolator;
 import com.mojang.datafixers.util.Either;
 
 import net.minecraft.client.network.packet.EntityPassengersSetS2CPacket;
diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java
index 64329bb2..5add4417 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java
@@ -11,7 +11,7 @@ import com.minelittlepony.unicopia.entity.RaceContainer;
 import com.minelittlepony.unicopia.magic.Caster;
 import com.minelittlepony.unicopia.magic.HeldMagicEffect;
 import com.minelittlepony.unicopia.network.Transmittable;
-import com.minelittlepony.unicopia.util.IInterpolator;
+import com.minelittlepony.util.IInterpolator;
 import com.mojang.authlib.GameProfile;
 import com.mojang.datafixers.util.Either;
 
diff --git a/src/main/java/com/minelittlepony/unicopia/magic/CastResult.java b/src/main/java/com/minelittlepony/unicopia/magic/CastResult.java
index 257e91b3..3990f1a1 100644
--- a/src/main/java/com/minelittlepony/unicopia/magic/CastResult.java
+++ b/src/main/java/com/minelittlepony/unicopia/magic/CastResult.java
@@ -18,4 +18,8 @@ public enum CastResult {
      * When right clicking a block the itemstack will be decremented.
      */
     DEFAULT;
+
+    public static CastResult cancelled(boolean cancelled) {
+        return cancelled ? NONE : DEFAULT;
+    }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/magic/spell/DarknessSpell.java b/src/main/java/com/minelittlepony/unicopia/magic/spell/DarknessSpell.java
index fbcde2d1..cf7d8eb3 100644
--- a/src/main/java/com/minelittlepony/unicopia/magic/spell/DarknessSpell.java
+++ b/src/main/java/com/minelittlepony/unicopia/magic/spell/DarknessSpell.java
@@ -101,7 +101,7 @@ public class DarknessSpell extends AbstractAttachableSpell {
     }
 
     private void applyBlocks(Caster<?> source, int radius) {
-        for (BlockPos pos : PosHelper.getAllInRegionMutable(source.getOrigin(), new Sphere(false, radius))) {
+        PosHelper.getAllInRegionMutable(source.getOrigin(), new Sphere(false, radius)).filter(pos -> {
             if (source.getWorld().random.nextInt(500) == 0) {
                 BlockState state = source.getWorld().getBlockState(pos);
 
@@ -112,11 +112,13 @@ public class DarknessSpell extends AbstractAttachableSpell {
                         if (source.getWorld() instanceof ServerWorld) {
                             growable.grow((ServerWorld)source.getWorld(), source.getWorld().random, pos, state);
                         }
-                        return;
+                        return true;
                     }
                 }
             }
-        }
+
+            return false;
+        }).findFirst();
     }
 
     private void applyEntities(Caster<?> source, int radius, Consumer<LivingEntity> consumer) {
diff --git a/src/main/java/com/minelittlepony/unicopia/magic/spell/FireSpell.java b/src/main/java/com/minelittlepony/unicopia/magic/spell/FireSpell.java
index 619268f2..63442902 100644
--- a/src/main/java/com/minelittlepony/unicopia/magic/spell/FireSpell.java
+++ b/src/main/java/com/minelittlepony/unicopia/magic/spell/FireSpell.java
@@ -85,10 +85,9 @@ public class FireSpell extends AbstractSpell.RangedAreaSpell implements Useable,
         if (player == null || player.isSneaking()) {
             result = applyBlocks(context.getWorld(), pos);
         } else {
-
-            for (BlockPos i : PosHelper.getAllInRegionMutable(pos, effect_range)) {
-                result |= applyBlocks(context.getWorld(), i);
-            }
+            result = PosHelper.getAllInRegionMutable(pos, effect_range).reduce(result,
+                    (r, i) -> applyBlocks(context.getWorld(), i),
+                    (a, b) -> a || b);
         }
 
         if (!result) {
@@ -111,17 +110,10 @@ public class FireSpell extends AbstractSpell.RangedAreaSpell implements Useable,
     public CastResult onDispenced(BlockPos pos, Direction facing, BlockPointer source, Affinity affinity) {
         pos = pos.offset(facing, 4);
 
-        boolean result = false;
-
-        for (BlockPos i : PosHelper.getAllInRegionMutable(pos, effect_range)) {
-            result |= applyBlocks(source.getWorld(), i);
-        }
-
-        if (!result) {
-            result = applyEntities(null, source.getWorld(), pos);
-        }
-
-        return result ? CastResult.NONE : CastResult.DEFAULT;
+        return CastResult.cancelled(PosHelper.getAllInRegionMutable(pos, effect_range).reduce(false,
+                (r, i) -> applyBlocks(source.getWorld(), i),
+                (a, b) -> a || b)
+                || applyEntities(null, source.getWorld(), pos));
     }
 
     protected boolean applyBlocks(World world, BlockPos pos) {
diff --git a/src/main/java/com/minelittlepony/unicopia/magic/spell/IceSpell.java b/src/main/java/com/minelittlepony/unicopia/magic/spell/IceSpell.java
index d2702d10..1a72791c 100644
--- a/src/main/java/com/minelittlepony/unicopia/magic/spell/IceSpell.java
+++ b/src/main/java/com/minelittlepony/unicopia/magic/spell/IceSpell.java
@@ -88,9 +88,7 @@ public class IceSpell extends AbstractSpell.RangedAreaSpell implements Useable,
 
     private boolean applyBlocks(PlayerEntity owner, World world, BlockPos pos) {
 
-        for (BlockPos i : PosHelper.getAllInRegionMutable(pos, effect_range)) {
-            applyBlockSingle(owner, world, i);
-        }
+        PosHelper.getAllInRegionMutable(pos, effect_range).forEach(i -> applyBlockSingle(owner, world, i));
 
         return applyEntities(owner, world, pos);
     }
@@ -136,7 +134,7 @@ public class IceSpell extends AbstractSpell.RangedAreaSpell implements Useable,
     }
 
     public static boolean isSurroundedByIce(World w, BlockPos pos) {
-        return !PosHelper.adjacentNeighbours(pos).stream().anyMatch(i ->
+        return !PosHelper.adjacentNeighbours(pos).anyMatch(i ->
             w.getBlockState(i).getMaterial() == Material.ICE
         );
     }
diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java
index f39e4442..55cdf239 100644
--- a/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java
+++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java
@@ -69,6 +69,7 @@ public abstract class MixinLivingEntity extends Entity implements PonyContainer<
     }
 
     // ---------- temporary
+    @SuppressWarnings("deprecation")
     @Inject(method = "isClimbing()Z", at = @At("HEAD"), cancellable = true)
     public void onIsClimbing(CallbackInfoReturnable<Boolean> info) {
         LivingEntity self = (LivingEntity)(Object)this;
diff --git a/src/main/java/com/minelittlepony/unicopia/particles/MagicParticleEffect.java b/src/main/java/com/minelittlepony/unicopia/particles/MagicParticleEffect.java
index d83db22d..f80cec40 100644
--- a/src/main/java/com/minelittlepony/unicopia/particles/MagicParticleEffect.java
+++ b/src/main/java/com/minelittlepony/unicopia/particles/MagicParticleEffect.java
@@ -2,7 +2,7 @@ package com.minelittlepony.unicopia.particles;
 
 import java.util.Locale;
 
-import com.minelittlepony.unicopia.util.Color;
+import com.minelittlepony.util.Color;
 import com.mojang.brigadier.StringReader;
 import com.mojang.brigadier.exceptions.CommandSyntaxException;
 
diff --git a/src/main/java/com/minelittlepony/unicopia/particles/ParticleSource.java b/src/main/java/com/minelittlepony/unicopia/particles/ParticleSource.java
index 99b43b9f..fc7c8765 100644
--- a/src/main/java/com/minelittlepony/unicopia/particles/ParticleSource.java
+++ b/src/main/java/com/minelittlepony/unicopia/particles/ParticleSource.java
@@ -30,7 +30,7 @@ public interface ParticleSource {
     default void spawnParticles(Shape area, int count, Consumer<Vec3d> particleSpawner) {
         Vec3d pos = getOriginVector();
 
-        area.randomPoints(count, getWorld().random).stream()
+        area.randomPoints(count, getWorld().random)
             .map(point -> point.add(pos))
             .forEach(particleSpawner);
     }
diff --git a/src/main/java/com/minelittlepony/unicopia/util/BasicEasingInterpolator.java b/src/main/java/com/minelittlepony/unicopia/util/BasicEasingInterpolator.java
deleted file mode 100644
index 6c21b440..00000000
--- a/src/main/java/com/minelittlepony/unicopia/util/BasicEasingInterpolator.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.minelittlepony.unicopia.util;
-
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
-
-public class BasicEasingInterpolator implements IInterpolator {
-
-    private static LoadingCache<UUID, BasicEasingInterpolator> instanceCache = CacheBuilder.newBuilder()
-        .expireAfterAccess(30, TimeUnit.SECONDS)
-        .build(CacheLoader.from(BasicEasingInterpolator::new));
-
-    /**
-     * Gets or creates a new basic, linear interpolator for the provided id.
-     */
-    public static IInterpolator getInstance(UUID id) {
-        return instanceCache.getUnchecked(id);
-    }
-
-    private final Map<String, Float> properties = new HashMap<>();
-
-    private float getLast(String key, float to) {
-        if (properties.containsKey(key)) {
-            return properties.get(key);
-        }
-
-        return to;
-    }
-
-    @Override
-    public float interpolate(String key, float to, float scalingFactor) {
-        float from = getLast(key, to);
-
-        from += (to - from) / scalingFactor;
-
-        if (Float.isNaN(from) || Float.isInfinite(from)) {
-            System.err.println("Error: Animation frame for " + key + " is NaN or Infinite.");
-            from = to;
-        }
-
-        properties.put(key, from);
-
-        return from;
-
-    }
-
-}
diff --git a/src/main/java/com/minelittlepony/unicopia/util/Color.java b/src/main/java/com/minelittlepony/unicopia/util/Color.java
deleted file mode 100644
index bbc5ea1e..00000000
--- a/src/main/java/com/minelittlepony/unicopia/util/Color.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.minelittlepony.unicopia.util;
-
-public interface Color {
-    static float r(int hex) {
-        return (hex >> 16 & 255) / 255F;
-    }
-
-    static float g(int hex) {
-        return (hex >> 8 & 255) / 255F;
-    }
-
-    static float b(int hex) {
-        return (hex & 255) / 255F;
-    }
-}
diff --git a/src/main/java/com/minelittlepony/unicopia/util/IInterpolator.java b/src/main/java/com/minelittlepony/unicopia/util/IInterpolator.java
deleted file mode 100644
index e38629f9..00000000
--- a/src/main/java/com/minelittlepony/unicopia/util/IInterpolator.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.minelittlepony.unicopia.util;
-
-/**
- * Interpolator function for handling transitions between animation states.
- */
-@FunctionalInterface
-public interface IInterpolator {
-    /**
-     * Interpolates a value between the requested final destination and what it was last.
-     *
-     * @param key           Identifier to track previous values
-     * @param to            The new values
-     * @param scalingFactor Scaling factor to control how quickly values change
-     */
-    float interpolate(String key, float to, float scalingFactor);
-}
diff --git a/src/main/java/com/minelittlepony/unicopia/util/Iterators.java b/src/main/java/com/minelittlepony/unicopia/util/Iterators.java
deleted file mode 100644
index 1c3f2859..00000000
--- a/src/main/java/com/minelittlepony/unicopia/util/Iterators.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package com.minelittlepony.unicopia.util;
-
-import java.util.function.Supplier;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-
-import com.google.common.collect.AbstractIterator;
-
-public interface Iterators<T> extends Iterable<T> {
-
-    static <T> Iterators<T> iterate(Supplier<T> supplier) {
-        return () -> new AbstractIterator<T>() {
-            @Override
-            protected T computeNext() {
-                T next = supplier.get();
-
-                if (next == null) {
-                    endOfData();
-                }
-
-                return next;
-            }
-        };
-    }
-
-    /**
-     * Returns a sequential {@code Stream} with this collection as its source.
-     *
-     * <p>This method should be overridden when the {@link #spliterator()}
-     * method cannot return a spliterator that is {@code IMMUTABLE},
-     * {@code CONCURRENT}, or <em>late-binding</em>. (See {@link #spliterator()}
-     * for details.)
-     *
-     * @implSpec
-     * The default implementation creates a sequential {@code Stream} from the
-     * collection's {@code Spliterator}.
-     *
-     * @return a sequential {@code Stream} over the elements in this collection
-     * @since 1.8
-     */
-    default Stream<T> stream() {
-        return StreamSupport.stream(spliterator(), false);
-    }
-
-    /**
-     * Returns a possibly parallel {@code Stream} with this collection as its
-     * source.  It is allowable for this method to return a sequential stream.
-     *
-     * <p>This method should be overridden when the {@link #spliterator()}
-     * method cannot return a spliterator that is {@code IMMUTABLE},
-     * {@code CONCURRENT}, or <em>late-binding</em>. (See {@link #spliterator()}
-     * for details.)
-     *
-     * @implSpec
-     * The default implementation creates a parallel {@code Stream} from the
-     * collection's {@code Spliterator}.
-     *
-     * @return a possibly parallel {@code Stream} over the elements in this
-     * collection
-     * @since 1.8
-     */
-    default Stream<T> parallelStream() {
-        return StreamSupport.stream(spliterator(), true);
-    }
-}
diff --git a/src/main/java/com/minelittlepony/unicopia/util/MotionCompositor.java b/src/main/java/com/minelittlepony/unicopia/util/MotionCompositor.java
deleted file mode 100644
index e75daf91..00000000
--- a/src/main/java/com/minelittlepony/unicopia/util/MotionCompositor.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package com.minelittlepony.unicopia.util;
-
-import net.minecraft.entity.player.PlayerEntity;
-import net.minecraft.util.math.MathHelper;
-
-/**
- * Calculates roll and incline for a player based on their motion vectors.
- *
- * @author Polyakovich - Big thank you to this dude and his meta-physics friend for working this all out.
- */
-public abstract class MotionCompositor {
-
-    static double clampLimit(double num, double limit) {
-        return MathHelper.clamp(num, -limit, limit);
-    }
-
-    static float sensibleAngle(float angle) {
-        angle %= 360;
-
-        if (angle > 180) angle -= 360;
-        if (angle < -180) angle += 360;
-
-        return angle;
-    }
-    /**
-     * Gets the angle of horizontal roll in degrees based on the player's vertical and horizontal motion.
-     */
-    protected double calculateRoll(PlayerEntity player, double motionX, double motionY, double motionZ) {
-
-        // since model roll should probably be calculated from model rotation rather than entity rotation...
-        double roll = sensibleAngle(player.prevBodyYaw - player.bodyYaw);
-        double horMotion = Math.sqrt(motionX * motionX + motionZ * motionZ);
-        float modelYaw = sensibleAngle(player.bodyYaw);
-
-        // detecting that we're flying backwards and roll must be inverted
-        if (Math.abs(sensibleAngle((float) Math.toDegrees(Math.atan2(motionX, motionZ)) + modelYaw)) > 90) {
-            roll *= -1;
-        }
-
-        // ayyy magic numbers (after 5 - an approximation of nice looking coefficients calculated by hand)
-
-        // roll might be zero, in which case Math.pow produces +Infinity. Anything x Infinity = NaN.
-        double pow = roll != 0 ? Math.pow(Math.abs(roll), -0.191) : 0;
-
-        roll *= horMotion * 5 * (3.6884f * pow);
-
-        assert !Float.isNaN((float)roll);
-
-        return MathHelper.clamp(roll, -54, 54);
-    }
-
-    /**
-     * Gets the angle of inclination in degrees based on the player's vertical and horizontal motion.
-     */
-    protected double calculateIncline(PlayerEntity player, double motionX, double motionY, double motionZ) {
-        double dist = Math.sqrt(motionX * motionX + motionZ * motionZ);
-        double angle = Math.atan2(motionY, dist);
-
-        if (!player.abilities.allowFlying) {
-            angle /= 2;
-        }
-
-        angle = clampLimit(angle, Math.PI / 3);
-
-        return Math.toDegrees(angle);
-    }
-
-}
diff --git a/src/main/java/com/minelittlepony/unicopia/util/PosHelper.java b/src/main/java/com/minelittlepony/unicopia/util/PosHelper.java
index 3d096b57..07ea4ecc 100644
--- a/src/main/java/com/minelittlepony/unicopia/util/PosHelper.java
+++ b/src/main/java/com/minelittlepony/unicopia/util/PosHelper.java
@@ -1,9 +1,13 @@
 package com.minelittlepony.unicopia.util;
 
 import java.util.Iterator;
+import java.util.List;
+import java.util.Spliterator;
+import java.util.Spliterators.AbstractSpliterator;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
 
 import com.google.common.collect.Lists;
 import com.minelittlepony.unicopia.util.shape.Shape;
@@ -44,46 +48,29 @@ public interface PosHelper {
         return false;
     }
 
-    static Iterators<BlockPos> adjacentNeighbours(BlockPos origin) {
+    static Stream<BlockPos> adjacentNeighbours(BlockPos origin) {
         BlockPos.Mutable pos = new BlockPos.Mutable(origin);
-        Iterator<Direction> directions = Lists.newArrayList(Direction.values()).iterator();
+        List<Direction> directions = Lists.newArrayList(Direction.values());
+        Iterator<Direction> iter = directions.iterator();
+        return StreamSupport.stream(new AbstractSpliterator<BlockPos>(directions.size(), Spliterator.SIZED) {
+            @Override
+            public boolean tryAdvance(Consumer<? super BlockPos> consumer) {
+                if (iter.hasNext()) {
+                    Direction next = iter.next();
 
-        return Iterators.iterate(() -> {
-            if (directions.hasNext()) {
-                Direction next = directions.next();
-
-                pos.set(origin.getX() + next.getOffsetX(), origin.getY() + next.getOffsetY(), origin.getZ() + next.getOffsetZ());
-                return pos;
-            }
-
-            return null;
-        });
-    }
-
-    static Iterators<BlockPos> getAllInRegionMutable(BlockPos origin, Shape shape) {
-        Iterator<BlockPos> iter = BlockPos.iterate(
-                origin.add(new BlockPos(shape.getLowerBound())),
-                origin.add(new BlockPos(shape.getUpperBound()))
-            ).iterator();
-
-        return Iterators.iterate(() -> {
-            while (iter.hasNext()) {
-                BlockPos pos = iter.next();
-
-                if (shape.isPointInside(new Vec3d(pos.subtract(origin)))) {
-                    return pos;
+                    pos.set(origin.getX() + next.getOffsetX(), origin.getY() + next.getOffsetY(), origin.getZ() + next.getOffsetZ());
+                    consumer.accept(pos);
+                    return true;
                 }
+                return false;
             }
-
-            return null;
-        });
+        }, false);
     }
 
-    /**
-     * Creates a stream of mutable block positions ranging from the beginning position to end.
-     */
-    @Deprecated
-    static Stream<BlockPos> inRegion(BlockPos from, BlockPos to) {
-        return BlockPos.stream(from, to);
+    static Stream<BlockPos> getAllInRegionMutable(BlockPos origin, Shape shape) {
+        return BlockPos.stream(
+            origin.add(new BlockPos(shape.getLowerBound())),
+            origin.add(new BlockPos(shape.getUpperBound()))
+        ).filter(pos -> shape.isPointInside(new Vec3d(pos.subtract(origin))));
     }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/util/WorldEvent.java b/src/main/java/com/minelittlepony/unicopia/util/WorldEvent.java
index 1b959f0e..5dbef533 100644
--- a/src/main/java/com/minelittlepony/unicopia/util/WorldEvent.java
+++ b/src/main/java/com/minelittlepony/unicopia/util/WorldEvent.java
@@ -6,7 +6,7 @@ import net.minecraft.util.math.BlockPos;
 import net.minecraft.world.World;
 
 /**
- * All of the Auxiliary effects used in minecraft for World.spawnEvent
+ * All of the Auxiliary effects used in minecraft for World.playGlobalEvent
  */
 public enum WorldEvent {
     DISPENSER_DISPENSE_BLOCK(1000),
@@ -82,16 +82,7 @@ public enum WorldEvent {
         play(world, pos, 0);
     }
 
-    public void play(World world, BlockPos pos, int data) {
+    private void play(World world, BlockPos pos, int data) {
         world.playGlobalEvent(getId(), pos, data);
     }
-
-    public static WorldEvent fromId(int id) {
-        for (WorldEvent i : values()) {
-            if (i.id == id) {
-                return i;
-            }
-        }
-        return UNKNOWN;
-    }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/util/shape/Shape.java b/src/main/java/com/minelittlepony/unicopia/util/shape/Shape.java
index b0cc7e9c..4b56e5f9 100644
--- a/src/main/java/com/minelittlepony/unicopia/util/shape/Shape.java
+++ b/src/main/java/com/minelittlepony/unicopia/util/shape/Shape.java
@@ -1,9 +1,12 @@
 package com.minelittlepony.unicopia.util.shape;
 
 import java.util.Random;
+import java.util.Spliterator;
+import java.util.Spliterators.AbstractSpliterator;
 import java.util.concurrent.atomic.AtomicInteger;
-
-import com.minelittlepony.unicopia.util.Iterators;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
 
 import net.minecraft.util.math.Vec3d;
 
@@ -75,16 +78,19 @@ public interface Shape {
     /**
      * Returns a sequence of N random points.
      */
-    default Iterators<Vec3d> randomPoints(int n, Random rand) {
+    default Stream<Vec3d> randomPoints(int n, Random rand) {
         AtomicInteger atom = new AtomicInteger(n);
-        return Iterators.iterate(() -> {
-            if (atom.get() <= 0) {
-                return null;
+        return StreamSupport.stream(new AbstractSpliterator<Vec3d>(n, Spliterator.SIZED) {
+            @Override
+            public boolean tryAdvance(Consumer<? super Vec3d> consumer) {
+
+                if (atom.decrementAndGet() >= 0) {
+                    consumer.accept(computePoint(rand));
+                    return true;
+                }
+
+                return false;
             }
-
-            atom.decrementAndGet();
-
-            return computePoint(rand);
-        });
+        }, false);
     }
 }