gt: 			.filter(m -> m instanceof Updater)
context: package nova.microblock.micro;
  
  import nova.core.block.Block;
  import nova.core.block.Stateful;
  import nova.core.component.Component;
  import nova.core.component.Updater;
  import nova.core.component.transform.BlockTransform;
  import nova.core.network.NetworkException;
  import nova.core.network.Packet;
  import nova.core.network.Syncable;
  import nova.core.retention.Data;
  import nova.core.retention.Storable;
  import nova.core.util.Direction;
  import nova.core.util.math.MathUtil;
  import nova.core.util.math.Vector3DUtil;
  import nova.core.util.shape.Cuboid;
  import nova.microblock.NovaMicroblock;
  import nova.microblock.common.BlockComponent;
  import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
  
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Map;
  import java.util.Optional;
  import java.util.stream.Stream;
  
  /**
   * A component added to microblocks
   * @author Calclavia
   */
  public class MicroblockContainer extends BlockComponent implements Syncable, Storable, Updater {
  
  	/**
  	 * The amount of subdivisions of the microblock.
  	 * Must be 2^n
  	 */
  	//TOD: Make this variable, or configurable?
  	public static final int subdivision = 8;
  	private final String saveID = "microblockContainer";
  
  	/**
  	 * A sparse block map from (0,0) to (subdivision, subdivision) coordinates
  	 * of all the microblocks.
  	 */
  	private final Map<Vector3D, Microblock> blockMap = new HashMap<>();
  
  	public MicroblockContainer(Block block) {
  		super(block);
  	}
  
  	public static Vector3D centerPosition() {
  		return Vector3DUtil.ONE.scalarMultiply((subdivision - 1) / 2);
  	}
  
  	public static Vector3D sidePosition(Direction direction) {
  		return Vector3DUtil.floor(
  			direction.toVector()
  				.add(Vector3DUtil.ONE)
  				.scalarMultiply(0.5)
  				.scalarMultiply(subdivision - 1)
  		);
  	}
  
  	/**
  	 * Gets a collection of all microblocks.
  	 */
  	public Collection<Microblock> microblocks() {
  		return blockMap.values();
  	}
  
  	/**
  	 * Gets a collection of all components of the same type in all microblocks.
  	 * @param componentClass The component class
  	 * @param <C> The component type
  	 * @return Gets a stream of components in the microblocks
  	 */
  	public <C extends Component> Stream<C> microblocks(Class<C> componentClass) {
  		return stream()
  			.map(microblock -> microblock.block.components.getOp(componentClass))
  			.filter(Optional::isPresent)
  			.map(Optional::get);
  	}
  
  	public Stream<Microblock> stream() {
  		return new HashSet<>(blockMap.values()).stream();
  	}
  
  	/**
  	 * Puts a new microblock into this container.
  	 */
  	public boolean putNew(Vector3D localPos, Microblock microblock) {
  		if (put(localPos, microblock)) {
  			//Invoke load event
  			microblock.block.events.publish(new Stateful.LoadEvent());
  
  			//Invoke neighbor change event
  			microblocks().stream()
  				.filter(m -> m != microblock)
  				.forEach(m -> m.block.events.publish(new Microblock.MicroblockChangeEvent(Optional.of(localPos))));
  
  			block.world().markChange(block.position());
  
  			return true;
  		}
  
  		return false;
  	}
  
  	/**
  	 * Puts a microblock directly into this container.
  	 * No events will be invoked.
  	 */
  	public boolean put(Vector3D localPos, Microblock microblock) {
  		assert new Cuboid(0, 0, 0, subdivision, subdivision, subdivision).intersects(localPos);
  
  		if (!has(localPos)) {
  			//Place microblock
  			microblock.containers.add(this);
  			microblock.position = localPos;
  			blockMap.put(localPos, microblock);
  
  			//Inject components
  			NovaMicroblock.instance.componentInjection.injectToContained(microblock.block, block);
  			NovaMicroblock.instance.componentInjection.injectToContainer(microblock.block, block);
  
  			//If this is not the first microblock (first one already sent via default packet)
  			if (NovaMicroblock.instance.network.isServer() && microblocks().size() > 1) {
  				NovaMicroblock.instance.network.sync(block);
  			}
  
  			return true;
  		}
  
  		return false;
  	}
  
  	public boolean remove(Vector3D localPos) {
  		if (has(localPos)) {
  			get(localPos).get().block.events.publish(new Stateful.UnloadEvent());
  			blockMap.remove(localPos);
  
  			if (microblocks().size() > 0 && NovaMicroblock.instance.network.isServer()) {
  				NovaMicroblock.instance.network.sync(block);
  			}
  
  			//Invoke neighbor change event
  			microblocks().stream()
  				.forEach(m -> m.block.events.publish(new Microblock.MicroblockChangeEvent(Optional.of(localPos))));
  
  			block.world().markChange(block.position());
  			return true;
  		}
  
  		return false;
  	}
  
  	/**
  	 * Gets a microblock based on the slot.
  	 * @param side The side of the microblock
  	 * @return The microblock that is occupying this specific side.
  	 */
  	//TODO: Change to region
  	public Optional<Microblock> get(Direction side) {
  		return get(sidePosition(side));
  	}
  
  	public boolean has(Vector3D localPos) {
  		return blockMap.containsKey(localPos);
  	}
  
  	/**
  	 * Gets the microblock at a specific internal position.
  	 * @param localPos Te local position within the microblock space
  	 * @return The optional microblock.
  	 */
  	public Optional<Microblock> get(Vector3D localPos) {
  		return Optional.ofNullable(blockMap.get(localPos));
  	}
  
  	/**
  	 * Gets a single microblock that converts a specific region within the microblock space.
  	 * @param region A region in the microblock space
  	 * @return A single microblock if that microblock occupies the entire region.
  	 */
  	public Optional<Microblock> get(Cuboid region) {
  		//NO-OP. TODO: IMPLEMENT
  		return Optional.empty();
  	}
  
  	/**
  	 * @return A man of local positions to their microblocks.
  	 */
  	public Map<Vector3D, Microblock> map() {
  		return blockMap;
  	}
  
  	@Override
  	public void update(double deltaTime) {
  		stream()
  