diff --git a/build.gradle b/build.gradle
index 56d6fb380..94ae11c67 100644
--- a/build.gradle
+++ b/build.gradle
@@ -187,6 +187,40 @@ reobf {
     shadowJar {}
 }
 
+
+task sourcesJar(type: Jar) {
+    from sourceSets.main.allSource
+    archiveBaseName.set(project.archivesBaseName)
+    archiveVersion.set("${project.version}")
+    archiveClassifier.set('sources')
+}
+
+task javadocJar(type: Jar, dependsOn: javadoc) {
+    from javadoc.destinationDir
+    archiveClassifier.set('javadoc')
+}
+
+artifacts {
+    archives jar, shadowJar, sourcesJar, javadocJar
+}
+
+publishing {
+    tasks.publish.dependsOn 'build'
+    publications {
+        mavenJava(MavenPublication) {
+            artifact shadowJar
+            artifact sourcesJar
+            artifact javadocJar
+        }
+    }
+
+    repositories {
+        if (project.hasProperty('mavendir')) {
+            maven { url mavendir }
+        }
+    }
+}
+
 String getChangelogText() {
     def changelogFile = file('changelog.txt')
     String str = ''
diff --git a/gradle.properties b/gradle.properties
index 190398720..dff493265 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -4,7 +4,7 @@ org.gradle.jvmargs = -Xmx3G
 org.gradle.daemon = false
 
 # mod version info
-mod_version = 0.3.2c
+mod_version = 0.3.2d
 minecraft_version = 1.16.5
 forge_version = 36.2.0
 
@@ -16,7 +16,7 @@ cursegradle_version = 1.4.0
 
 # dependency versions
 registrate_version = 1.0.10
-flywheel_version = 1.16-0.2.0.34
+flywheel_version = 1.16-0.2.3.44
 jei_version = 7.7.1.116
 
 # curseforge information
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 000000000..ecc4e7182
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'Create'
diff --git a/src/generated/resources/data/create/tags/items/sandpaper.json b/src/generated/resources/data/create/tags/items/sandpaper.json
new file mode 100644
index 000000000..acab7913a
--- /dev/null
+++ b/src/generated/resources/data/create/tags/items/sandpaper.json
@@ -0,0 +1,7 @@
+{
+  "replace": false,
+  "values": [
+    "create:sand_paper",
+    "create:red_sand_paper"
+  ]
+}
\ No newline at end of file
diff --git a/src/main/java/com/simibubi/create/AllItems.java b/src/main/java/com/simibubi/create/AllItems.java
index 5103e0aa6..36885956c 100644
--- a/src/main/java/com/simibubi/create/AllItems.java
+++ b/src/main/java/com/simibubi/create/AllItems.java
@@ -228,10 +228,12 @@ public class AllItems {
 
 	public static final ItemEntry<SandPaperItem> SAND_PAPER = REGISTRATE.item("sand_paper", SandPaperItem::new)
 		.transform(CreateRegistrate.customRenderedItem(() -> SandPaperModel::new))
+		.tag(AllTags.AllItemTags.SANDPAPER.tag)
 		.register();
 
 	public static final ItemEntry<SandPaperItem> RED_SAND_PAPER = REGISTRATE.item("red_sand_paper", SandPaperItem::new)
 		.transform(CreateRegistrate.customRenderedItem(() -> SandPaperModel::new))
+		.tag(AllTags.AllItemTags.SANDPAPER.tag)
 		.onRegister(s -> TooltipHelper.referTo(s, SAND_PAPER))
 		.register();
 
diff --git a/src/main/java/com/simibubi/create/AllTags.java b/src/main/java/com/simibubi/create/AllTags.java
index a3542f906..f0328a5e6 100644
--- a/src/main/java/com/simibubi/create/AllTags.java
+++ b/src/main/java/com/simibubi/create/AllTags.java
@@ -79,6 +79,7 @@ public class AllTags {
 		SEATS(MOD),
 		VALVE_HANDLES(MOD),
 		UPRIGHT_ON_BELT(MOD),
+		SANDPAPER(MOD),
 		CREATE_INGOTS(MOD),
 		BEACON_PAYMENT(FORGE),
 		INGOTS(FORGE),
diff --git a/src/main/java/com/simibubi/create/Create.java b/src/main/java/com/simibubi/create/Create.java
index 5935dfb7e..6ab5657a9 100644
--- a/src/main/java/com/simibubi/create/Create.java
+++ b/src/main/java/com/simibubi/create/Create.java
@@ -58,7 +58,7 @@ public class Create {
 
 	public static final String ID = "create";
 	public static final String NAME = "Create";
-	public static final String VERSION = "0.3.2c";
+	public static final String VERSION = "0.3.2d";
 
 	public static final Logger LOGGER = LogManager.getLogger();
 
@@ -109,6 +109,7 @@ public class Create {
 		modEventBus.addListener(AllConfigs::onReload);
 		modEventBus.addListener(EventPriority.LOWEST, this::gatherData);
 		forgeEventBus.addListener(EventPriority.HIGH, Create::onBiomeLoad);
+		forgeEventBus.register(CHUNK_UTIL);
 
 		DistExecutor.unsafeRunWhenOn(Dist.CLIENT,
 			() -> () -> CreateClient.addClientListeners(forgeEventBus, modEventBus));
@@ -116,16 +117,14 @@ public class Create {
 
 	public static void init(final FMLCommonSetupEvent event) {
 		CapabilityMinecartController.register();
-		SchematicInstances.register();
-
-		CHUNK_UTIL.init();
-		MinecraftForge.EVENT_BUS.register(CHUNK_UTIL);
-
 		AllPackets.registerPackets();
-		AllTriggers.register();
+		SchematicInstances.register();
 		PotatoCannonProjectileTypes.register();
 
+		CHUNK_UTIL.init();
+
 		event.enqueueWork(() -> {
+			AllTriggers.register();
 			SchematicProcessor.register();
 			AllWorldFeatures.registerFeatures();
 		});
diff --git a/src/main/java/com/simibubi/create/CreateClient.java b/src/main/java/com/simibubi/create/CreateClient.java
index 29500e2c7..52bfb4fe8 100644
--- a/src/main/java/com/simibubi/create/CreateClient.java
+++ b/src/main/java/com/simibubi/create/CreateClient.java
@@ -107,12 +107,12 @@ public class CreateClient {
 
 		UIRenderHelper.init();
 
-		IResourceManager resourceManager = Minecraft.getInstance()
-			.getResourceManager();
-		if (resourceManager instanceof IReloadableResourceManager)
-			((IReloadableResourceManager) resourceManager).registerReloadListener(new ResourceReloadHandler());
-
 		event.enqueueWork(() -> {
+			IResourceManager resourceManager = Minecraft.getInstance()
+				.getResourceManager();
+			if (resourceManager instanceof IReloadableResourceManager)
+				((IReloadableResourceManager) resourceManager).registerReloadListener(new ResourceReloadHandler());
+
 			registerLayerRenderers(Minecraft.getInstance()
 				.getEntityRenderDispatcher());
 		});
diff --git a/src/main/java/com/simibubi/create/compat/jei/CreateJEI.java b/src/main/java/com/simibubi/create/compat/jei/CreateJEI.java
index da8730eaa..2cf7dcf90 100644
--- a/src/main/java/com/simibubi/create/compat/jei/CreateJEI.java
+++ b/src/main/java/com/simibubi/create/compat/jei/CreateJEI.java
@@ -123,7 +123,7 @@ public class CreateJEI implements IModPlugin {
 
 		autoShapeless = register("automatic_shapeless", MixingCategory::autoShapeless)
 			.recipes(r -> r.getSerializer() == IRecipeSerializer.SHAPELESS_RECIPE && r.getIngredients()
-				.size() > 1 && !MechanicalPressTileEntity.canCompress(r.getIngredients()),
+				.size() > 1 && !MechanicalPressTileEntity.canCompress(r),
 				BasinRecipe::convertShapeless)
 			.catalyst(AllBlocks.MECHANICAL_MIXER::get)
 			.catalyst(AllBlocks.BASIN::get)
@@ -160,7 +160,7 @@ public class CreateJEI implements IModPlugin {
 			.build(),
 
 		autoSquare = register("automatic_packing", PackingCategory::autoSquare)
-			.recipes(r -> (r instanceof ICraftingRecipe) && MechanicalPressTileEntity.canCompress(r.getIngredients()),
+			.recipes(r -> (r instanceof ICraftingRecipe) && MechanicalPressTileEntity.canCompress(r),
 				BasinRecipe::convertShapeless)
 			.catalyst(AllBlocks.MECHANICAL_PRESS::get)
 			.catalyst(AllBlocks.BASIN::get)
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/actors/DrillRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/actors/DrillRenderer.java
index 3a81d4509..7270d25ec 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/actors/DrillRenderer.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/actors/DrillRenderer.java
@@ -44,7 +44,7 @@ public class DrillRenderer extends KineticTileEntityRenderer {
 		float time = AnimationTickHolder.getRenderTime() / 20;
 		float angle = (float) (((time * speed) % 360));
 
-		MatrixStack m = matrices.contraptionStack;
+		MatrixStack m = matrices.getModel();
 		m.pushPose();
 		MatrixTransformStack.of(m)
 			.centre()
@@ -55,9 +55,9 @@ public class DrillRenderer extends KineticTileEntityRenderer {
 
 		superBuffer
 			.transform(m)
-			.light(matrices.entityMatrix,
+			.light(matrices.getWorld(),
 					ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld))
-			.renderInto(matrices.entityStack, buffer.getBuffer(RenderType.solid()));
+			.renderInto(matrices.getViewProjection(), buffer.getBuffer(RenderType.solid()));
 
 		m.popPose();
 	}
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/actors/HarvesterRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/actors/HarvesterRenderer.java
index 79eee6a0a..0aac5f333 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/actors/HarvesterRenderer.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/actors/HarvesterRenderer.java
@@ -52,13 +52,13 @@ public class HarvesterRenderer extends SafeTileEntityRenderer<HarvesterTileEntit
 		if (context.contraption.stalled)
 			speed = 0;
 
-		superBuffer.transform(matrices.contraptionStack);
+		superBuffer.transform(matrices.getModel());
 		transform(context.world, facing, superBuffer, speed);
 
 		superBuffer
-			.light(matrices.entityMatrix,
+			.light(matrices.getWorld(),
 					ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld))
-			.renderInto(matrices.entityStack, buffers.getBuffer(RenderType.cutoutMipped()));
+			.renderInto(matrices.getViewProjection(), buffers.getBuffer(RenderType.cutoutMipped()));
 	}
 
 	public static void transform(World world, Direction facing, SuperByteBuffer superBuffer, float speed) {
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/actors/PortableStorageInterfaceRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/actors/PortableStorageInterfaceRenderer.java
index edf462cb4..6bdabeae9 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/actors/PortableStorageInterfaceRenderer.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/actors/PortableStorageInterfaceRenderer.java
@@ -56,9 +56,9 @@ public class PortableStorageInterfaceRenderer extends SafeTileEntityRenderer<Por
 			lit = te.isConnected();
 		}
 
-		render(blockState, lit, progress, matrices.contraptionStack, sbb -> sbb.light(matrices.entityMatrix,
+		render(blockState, lit, progress, matrices.getModel(), sbb -> sbb.light(matrices.getWorld(),
 				ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld))
-			.renderInto(matrices.entityStack, vb));
+			.renderInto(matrices.getViewProjection(), vb));
 	}
 
 	private static void render(BlockState blockState, boolean lit, float progress,
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/clock/CuckooClockRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/clock/CuckooClockRenderer.java
index 443c00b11..935baf026 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/clock/CuckooClockRenderer.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/clock/CuckooClockRenderer.java
@@ -14,7 +14,6 @@ import com.simibubi.create.foundation.utility.AngleHelper;
 import net.minecraft.block.BlockState;
 import net.minecraft.client.renderer.IRenderTypeBuffer;
 import net.minecraft.client.renderer.RenderType;
-import net.minecraft.client.renderer.WorldRenderer;
 import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
 import net.minecraft.util.Direction;
 import net.minecraft.util.math.MathHelper;
@@ -34,7 +33,6 @@ public class CuckooClockRenderer extends KineticTileEntityRenderer {
 
 		CuckooClockTileEntity clock = (CuckooClockTileEntity) te;
 		BlockState blockState = te.getBlockState();
-		int packedLightmapCoords = WorldRenderer.getLightColor(te.getLevel(), blockState, te.getBlockPos());
 		Direction direction = blockState.getValue(CuckooClockBlock.HORIZONTAL_FACING);
 
 		IVertexBuilder vb = buffer.getBuffer(RenderType.solid());
@@ -44,9 +42,9 @@ public class CuckooClockRenderer extends KineticTileEntityRenderer {
 		SuperByteBuffer minuteHand = PartialBufferer.get(AllBlockPartials.CUCKOO_MINUTE_HAND, blockState);
 		float hourAngle = clock.hourHand.get(partialTicks);
 		float minuteAngle = clock.minuteHand.get(partialTicks);
-		rotateHand(hourHand, hourAngle, direction).light(packedLightmapCoords)
+		rotateHand(hourHand, hourAngle, direction).light(light)
 				.renderInto(ms, vb);
-		rotateHand(minuteHand, minuteAngle, direction).light(packedLightmapCoords)
+		rotateHand(minuteHand, minuteAngle, direction).light(light)
 				.renderInto(ms, vb);
 
 		// Doors
@@ -72,9 +70,9 @@ public class CuckooClockRenderer extends KineticTileEntityRenderer {
 			}
 		}
 
-		rotateDoor(leftDoor, angle, true, direction).light(packedLightmapCoords)
+		rotateDoor(leftDoor, angle, true, direction).light(light)
 			.renderInto(ms, vb);
-		rotateDoor(rightDoor, angle, false, direction).light(packedLightmapCoords)
+		rotateDoor(rightDoor, angle, false, direction).light(light)
 			.renderInto(ms, vb);
 
 		// Figure
@@ -85,7 +83,7 @@ public class CuckooClockRenderer extends KineticTileEntityRenderer {
 					PartialBufferer.get(partialModel, blockState);
 			figure.rotateCentered(Direction.UP, AngleHelper.rad(AngleHelper.horizontalAngle(direction.getCounterClockWise())));
 			figure.translate(offset, 0, 0);
-			figure.light(packedLightmapCoords)
+			figure.light(light)
 					.renderInto(ms, vb);
 		}
 
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/crafter/MechanicalCrafterRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/crafter/MechanicalCrafterRenderer.java
index 82858218e..d589cd999 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/crafter/MechanicalCrafterRenderer.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/crafter/MechanicalCrafterRenderer.java
@@ -22,7 +22,6 @@ import net.minecraft.block.BlockState;
 import net.minecraft.client.Minecraft;
 import net.minecraft.client.renderer.IRenderTypeBuffer;
 import net.minecraft.client.renderer.RenderType;
-import net.minecraft.client.renderer.WorldRenderer;
 import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType;
 import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
 import net.minecraft.item.ItemStack;
@@ -169,41 +168,39 @@ public class MechanicalCrafterRenderer extends SafeTileEntityRenderer<Mechanical
 
 		if ((te.covered || te.phase != Phase.IDLE) && te.phase != Phase.CRAFTING && te.phase != Phase.INSERTING) {
 			SuperByteBuffer lidBuffer =
-				renderAndTransform(te, AllBlockPartials.MECHANICAL_CRAFTER_LID, blockState, pos);
-			lidBuffer.renderInto(ms, vb);
+				renderAndTransform(AllBlockPartials.MECHANICAL_CRAFTER_LID, blockState);
+			lidBuffer.light(light).renderInto(ms, vb);
 		}
 
 		if (MechanicalCrafterBlock.isValidTarget(te.getLevel(), pos.relative(targetDirection), blockState)) {
 			SuperByteBuffer beltBuffer =
-				renderAndTransform(te, AllBlockPartials.MECHANICAL_CRAFTER_BELT, blockState, pos);
+				renderAndTransform(AllBlockPartials.MECHANICAL_CRAFTER_BELT, blockState);
 			SuperByteBuffer beltFrameBuffer =
-				renderAndTransform(te, AllBlockPartials.MECHANICAL_CRAFTER_BELT_FRAME, blockState, pos);
+				renderAndTransform(AllBlockPartials.MECHANICAL_CRAFTER_BELT_FRAME, blockState);
 
 			if (te.phase == Phase.EXPORTING) {
 				int textureIndex = (int) ((te.getCountDownSpeed() / 128f * AnimationTickHolder.getTicks()));
 				beltBuffer.shiftUVtoSheet(AllSpriteShifts.CRAFTER_THINGIES, (textureIndex % 4) / 4f, 0, 1);
 			}
 
-			beltBuffer.renderInto(ms, vb);
-			beltFrameBuffer.renderInto(ms, vb);
+			beltBuffer.light(light).renderInto(ms, vb);
+			beltFrameBuffer.light(light).renderInto(ms, vb);
 
 		} else {
 			SuperByteBuffer arrowBuffer =
-				renderAndTransform(te, AllBlockPartials.MECHANICAL_CRAFTER_ARROW, blockState, pos);
-			arrowBuffer.renderInto(ms, vb);
+				renderAndTransform(AllBlockPartials.MECHANICAL_CRAFTER_ARROW, blockState);
+			arrowBuffer.light(light).renderInto(ms, vb);
 		}
 
 	}
 
-	private SuperByteBuffer renderAndTransform(MechanicalCrafterTileEntity te, PartialModel renderBlock,
-		BlockState crafterState, BlockPos pos) {
+	private SuperByteBuffer renderAndTransform(PartialModel renderBlock, BlockState crafterState) {
 		SuperByteBuffer buffer = PartialBufferer.get(renderBlock, crafterState);
 		float xRot = crafterState.getValue(MechanicalCrafterBlock.POINTING)
 				.getXRotation();
 		float yRot = AngleHelper.horizontalAngle(crafterState.getValue(HORIZONTAL_FACING));
 		buffer.rotateCentered(Direction.UP, (float) ((yRot + 90) / 180 * Math.PI));
 		buffer.rotateCentered(Direction.EAST, (float) ((xRot) / 180 * Math.PI));
-		buffer.light(WorldRenderer.getLightColor(te.getLevel(), crafterState, pos));
 		return buffer;
 	}
 
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/crusher/CrushingWheelControllerBlock.java b/src/main/java/com/simibubi/create/content/contraptions/components/crusher/CrushingWheelControllerBlock.java
index 3184f7f3b..b136458b1 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/crusher/CrushingWheelControllerBlock.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/crusher/CrushingWheelControllerBlock.java
@@ -156,8 +156,10 @@ public class CrushingWheelControllerBlock extends DirectionalBlock
 					continue;
 				if (neighbour.getValue(BlockStateProperties.AXIS) == d.getAxis())
 					continue;
-				KineticTileEntity wheelTe = (KineticTileEntity) world.getBlockEntity(pos.relative(d));
-				te.crushingspeed = Math.abs(wheelTe.getSpeed() / 50f);
+				TileEntity adjTe = world.getBlockEntity(pos.relative(d));
+				if (!(adjTe instanceof KineticTileEntity))
+					continue;
+				te.crushingspeed = Math.abs(((KineticTileEntity) adjTe).getSpeed() / 50f);
 				te.sendData();
 				break;
 			}
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerApplicationRecipe.java b/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerApplicationRecipe.java
index c315bea50..3fb54e05f 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerApplicationRecipe.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerApplicationRecipe.java
@@ -6,9 +6,8 @@ import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
 import com.simibubi.create.AllBlocks;
-import com.simibubi.create.AllItems;
 import com.simibubi.create.AllRecipeTypes;
-import com.simibubi.create.Create;
+import com.simibubi.create.AllTags.AllItemTags;
 import com.simibubi.create.compat.jei.category.sequencedAssembly.SequencedAssemblySubCategory;
 import com.simibubi.create.content.contraptions.itemAssembly.IAssemblyRecipe;
 import com.simibubi.create.content.contraptions.processing.ProcessingRecipe;
@@ -20,6 +19,7 @@ import net.minecraft.item.ItemStack;
 import net.minecraft.item.crafting.IRecipe;
 import net.minecraft.item.crafting.Ingredient;
 import net.minecraft.util.IItemProvider;
+import net.minecraft.util.ResourceLocation;
 import net.minecraft.util.text.ITextComponent;
 import net.minecraft.util.text.StringTextComponent;
 import net.minecraft.util.text.TranslationTextComponent;
@@ -66,10 +66,10 @@ public class DeployerApplicationRecipe extends ProcessingRecipe<RecipeWrapper> i
 
 	public static List<DeployerApplicationRecipe> convert(List<IRecipe<?>> sandpaperRecipes) {
 		return sandpaperRecipes.stream()
-			.map(r -> new ProcessingRecipeBuilder<>(DeployerApplicationRecipe::new, Create.asResource(r.getId()
+			.map(r -> new ProcessingRecipeBuilder<>(DeployerApplicationRecipe::new, new ResourceLocation(r.getId().getNamespace(), r.getId()
 				.getPath() + "_using_deployer")).require(r.getIngredients()
 					.get(0))
-					.require(Ingredient.of(AllItems.SAND_PAPER.get(), AllItems.RED_SAND_PAPER.get()))
+					.require(AllItemTags.SANDPAPER.tag)
 					.output(r.getResultItem())
 					.build())
 			.collect(Collectors.toList());
@@ -79,7 +79,7 @@ public class DeployerApplicationRecipe extends ProcessingRecipe<RecipeWrapper> i
 	public void addAssemblyIngredients(List<Ingredient> list) {
 		list.add(ingredients.get(1));
 	}
-	
+
 	@Override
 	@OnlyIn(Dist.CLIENT)
 	public ITextComponent getDescriptionForAssembly() {
@@ -95,7 +95,7 @@ public class DeployerApplicationRecipe extends ProcessingRecipe<RecipeWrapper> i
 	public void addRequiredMachines(Set<IItemProvider> list) {
 		list.add(AllBlocks.DEPLOYER.get());
 	}
-	
+
 	@Override
 	public Supplier<Supplier<SequencedAssemblySubCategory>> getJEISubCategory() {
 		return () -> SequencedAssemblySubCategory.AssemblyDeploying::new;
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerRenderer.java
index 72b799cb3..03c0d1ffc 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerRenderer.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerRenderer.java
@@ -29,7 +29,6 @@ import net.minecraft.client.Minecraft;
 import net.minecraft.client.renderer.IRenderTypeBuffer;
 import net.minecraft.client.renderer.ItemRenderer;
 import net.minecraft.client.renderer.RenderType;
-import net.minecraft.client.renderer.WorldRenderer;
 import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType;
 import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
 import net.minecraft.item.BlockItem;
@@ -39,7 +38,6 @@ import net.minecraft.util.math.BlockPos;
 import net.minecraft.util.math.MathHelper;
 import net.minecraft.util.math.vector.Vector3d;
 import net.minecraft.util.math.vector.Vector3f;
-import net.minecraft.world.World;
 
 public class DeployerRenderer extends SafeTileEntityRenderer<DeployerTileEntity> {
 
@@ -117,16 +115,17 @@ public class DeployerRenderer extends SafeTileEntityRenderer<DeployerTileEntity>
 		}
 
 		BlockState blockState = te.getBlockState();
-		BlockPos pos = te.getBlockPos();
 		Vector3d offset = getHandOffset(te, partialTicks, blockState);
 
 		SuperByteBuffer pole = PartialBufferer.get(AllBlockPartials.DEPLOYER_POLE, blockState);
 		SuperByteBuffer hand = PartialBufferer.get(te.getHandPose(), blockState);
 
-		transform(te.getLevel(), pole.translate(offset.x, offset.y, offset.z), blockState, pos, true).renderInto(ms,
-				vb);
-		transform(te.getLevel(), hand.translate(offset.x, offset.y, offset.z), blockState, pos, false).renderInto(ms,
-				vb);
+		transform(pole.translate(offset.x, offset.y, offset.z), blockState, true)
+			.light(light)
+			.renderInto(ms, vb);
+		transform(hand.translate(offset.x, offset.y, offset.z), blockState, false)
+			.light(light)
+			.renderInto(ms, vb);
 	}
 
 	protected Vector3d getHandOffset(DeployerTileEntity te, float partialTicks, BlockState blockState) {
@@ -138,8 +137,7 @@ public class DeployerRenderer extends SafeTileEntityRenderer<DeployerTileEntity>
 		return KineticTileEntityRenderer.shaft(KineticTileEntityRenderer.getRotationAxisOf(te));
 	}
 
-	private static SuperByteBuffer transform(World world, SuperByteBuffer buffer, BlockState deployerState,
-		BlockPos pos, boolean axisDirectionMatters) {
+	private static SuperByteBuffer transform(SuperByteBuffer buffer, BlockState deployerState, boolean axisDirectionMatters) {
 		Direction facing = deployerState.getValue(FACING);
 
 		float zRotLast =
@@ -151,7 +149,6 @@ public class DeployerRenderer extends SafeTileEntityRenderer<DeployerTileEntity>
 		buffer.rotateCentered(Direction.SOUTH, (float) ((zRot) / 180 * Math.PI));
 		buffer.rotateCentered(Direction.UP, (float) ((yRot) / 180 * Math.PI));
 		buffer.rotateCentered(Direction.SOUTH, (float) ((zRotLast) / 180 * Math.PI));
-		buffer.light(WorldRenderer.getLightColor(world, deployerState, pos));
 		return buffer;
 	}
 
@@ -159,9 +156,7 @@ public class DeployerRenderer extends SafeTileEntityRenderer<DeployerTileEntity>
 		ContraptionMatrices matrices, IRenderTypeBuffer buffer) {
 		IVertexBuilder builder = buffer.getBuffer(RenderType.solid());
 		BlockState blockState = context.state;
-		BlockPos pos = BlockPos.ZERO;
 		Mode mode = NBTHelper.readEnum(context.tileData, "Mode", Mode.class);
-		World world = context.world;
 		PartialModel handPose = getHandPose(mode);
 
 		SuperByteBuffer pole = PartialBufferer.get(AllBlockPartials.DEPLOYER_POLE, blockState);
@@ -181,19 +176,19 @@ public class DeployerRenderer extends SafeTileEntityRenderer<DeployerTileEntity>
 		Vector3d offset = Vector3d.atLowerCornerOf(blockState.getValue(FACING)
 			.getNormal()).scale(factor);
 
-		MatrixStack m = matrices.contraptionStack;
+		MatrixStack m = matrices.getModel();
 		m.pushPose();
 		m.translate(offset.x, offset.y, offset.z);
 
 		pole.transform(m);
 		hand.transform(m);
-		pole = transform(world, pole, blockState, pos, true);
-		hand = transform(world, hand, blockState, pos, false);
+		pole = transform(pole, blockState, true);
+		hand = transform(hand, blockState, false);
 
-		pole.light(matrices.entityMatrix, ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld))
-			.renderInto(matrices.entityStack, builder);
-		hand.light(matrices.entityMatrix, ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld))
-			.renderInto(matrices.entityStack, builder);
+		pole.light(matrices.getWorld(), ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld))
+			.renderInto(matrices.getViewProjection(), builder);
+		hand.light(matrices.getWorld(), ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld))
+			.renderInto(matrices.getViewProjection(), builder);
 
 		m.popPose();
 	}
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/flywheel/engine/EngineRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/flywheel/engine/EngineRenderer.java
index 0d09715ae..a91ba53d3 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/flywheel/engine/EngineRenderer.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/flywheel/engine/EngineRenderer.java
@@ -10,7 +10,6 @@ import com.simibubi.create.foundation.utility.AngleHelper;
 import net.minecraft.block.Block;
 import net.minecraft.client.renderer.IRenderTypeBuffer;
 import net.minecraft.client.renderer.RenderType;
-import net.minecraft.client.renderer.WorldRenderer;
 import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
 import net.minecraft.util.Direction;
 
@@ -38,7 +37,7 @@ public class EngineRenderer<T extends EngineTileEntity> extends SafeTileEntityRe
 				PartialBufferer.get(frame, te.getBlockState())
 						.rotateCentered(Direction.UP, angle)
 						.translate(0, 0, -1)
-						.light(WorldRenderer.getLightColor(te.getLevel(), te.getBlockState(), te.getBlockPos()))
+						.light(light)
 						.renderInto(ms, buffer.getBuffer(RenderType.solid()));
 			}
 		}
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/mixer/MechanicalMixerRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/mixer/MechanicalMixerRenderer.java
index 6da31b2e5..61873ce38 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/mixer/MechanicalMixerRenderer.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/mixer/MechanicalMixerRenderer.java
@@ -13,10 +13,8 @@ import com.simibubi.create.foundation.utility.AnimationTickHolder;
 import net.minecraft.block.BlockState;
 import net.minecraft.client.renderer.IRenderTypeBuffer;
 import net.minecraft.client.renderer.RenderType;
-import net.minecraft.client.renderer.WorldRenderer;
 import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
 import net.minecraft.util.Direction;
-import net.minecraft.util.math.BlockPos;
 
 public class MechanicalMixerRenderer extends KineticTileEntityRenderer {
 
@@ -37,14 +35,12 @@ public class MechanicalMixerRenderer extends KineticTileEntityRenderer {
 
 		BlockState blockState = te.getBlockState();
 		MechanicalMixerTileEntity mixer = (MechanicalMixerTileEntity) te;
-		BlockPos pos = te.getBlockPos();
 
 		IVertexBuilder vb = buffer.getBuffer(RenderType.solid());
 
 		SuperByteBuffer superBuffer = PartialBufferer.get(AllBlockPartials.SHAFTLESS_COGWHEEL, blockState);
 		standardKineticRotationTransform(superBuffer, te, light).renderInto(ms, vb);
 
-		int packedLightmapCoords = WorldRenderer.getLightColor(te.getLevel(), blockState, pos);
 		float renderedHeadOffset = mixer.getRenderedHeadOffset(partialTicks);
 		float speed = mixer.getRenderedHeadRotationSpeed(partialTicks);
 		float time = AnimationTickHolder.getRenderTime(te.getLevel());
@@ -52,13 +48,13 @@ public class MechanicalMixerRenderer extends KineticTileEntityRenderer {
 
 		SuperByteBuffer poleRender = PartialBufferer.get(AllBlockPartials.MECHANICAL_MIXER_POLE, blockState);
 		poleRender.translate(0, -renderedHeadOffset, 0)
-				.light(packedLightmapCoords)
+				.light(light)
 				.renderInto(ms, vb);
 
 		SuperByteBuffer headRender = PartialBufferer.get(AllBlockPartials.MECHANICAL_MIXER_HEAD, blockState);
 		headRender.rotateCentered(Direction.UP, angle)
 				.translate(0, -renderedHeadOffset, 0)
-				.light(packedLightmapCoords)
+				.light(light)
 				.renderInto(ms, vb);
 	}
 
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/press/MechanicalPressRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/press/MechanicalPressRenderer.java
index 731294f2c..a310c7c1d 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/press/MechanicalPressRenderer.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/press/MechanicalPressRenderer.java
@@ -13,9 +13,7 @@ import com.simibubi.create.foundation.render.SuperByteBuffer;
 import net.minecraft.block.BlockState;
 import net.minecraft.client.renderer.IRenderTypeBuffer;
 import net.minecraft.client.renderer.RenderType;
-import net.minecraft.client.renderer.WorldRenderer;
 import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
-import net.minecraft.util.math.BlockPos;
 
 public class MechanicalPressRenderer extends KineticTileEntityRenderer {
 
@@ -35,14 +33,12 @@ public class MechanicalPressRenderer extends KineticTileEntityRenderer {
 
 		if (Backend.getInstance().canUseInstancing(te.getLevel())) return;
 
-		BlockPos pos = te.getBlockPos();
 		BlockState blockState = te.getBlockState();
-		int packedLightmapCoords = WorldRenderer.getLightColor(te.getLevel(), blockState, pos);
 		float renderedHeadOffset = ((MechanicalPressTileEntity) te).getRenderedHeadOffset(partialTicks);
 
 		SuperByteBuffer headRender = PartialBufferer.getFacing(AllBlockPartials.MECHANICAL_PRESS_HEAD, blockState, blockState.getValue(HORIZONTAL_FACING));
 		headRender.translate(0, -renderedHeadOffset, 0)
-				.light(packedLightmapCoords)
+				.light(light)
 				.renderInto(ms, buffer.getBuffer(RenderType.solid()));
 	}
 
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/press/MechanicalPressTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/press/MechanicalPressTileEntity.java
index 27ac4f416..059067cfd 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/press/MechanicalPressTileEntity.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/press/MechanicalPressTileEntity.java
@@ -4,6 +4,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
 
+import com.google.common.collect.ImmutableList;
 import com.simibubi.create.AllBlocks;
 import com.simibubi.create.AllRecipeTypes;
 import com.simibubi.create.AllSoundEvents;
@@ -31,12 +32,14 @@ import net.minecraft.inventory.IInventory;
 import net.minecraft.item.ItemStack;
 import net.minecraft.item.crafting.ICraftingRecipe;
 import net.minecraft.item.crafting.IRecipe;
+import net.minecraft.item.crafting.IRecipeSerializer;
 import net.minecraft.item.crafting.Ingredient;
 import net.minecraft.nbt.CompoundNBT;
 import net.minecraft.particles.ItemParticleData;
 import net.minecraft.particles.ParticleTypes;
 import net.minecraft.tileentity.TileEntityType;
 import net.minecraft.util.NonNullList;
+import net.minecraft.util.ResourceLocation;
 import net.minecraft.util.math.AxisAlignedBB;
 import net.minecraft.util.math.MathHelper;
 import net.minecraft.util.math.vector.Vector3d;
@@ -339,7 +342,19 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity {
 		return AllRecipeTypes.PRESSING.find(pressingInv, level);
 	}
 
-	public static boolean canCompress(NonNullList<Ingredient> ingredients) {
+	private static final List<ResourceLocation> RECIPE_DENY_LIST =
+		ImmutableList.of(new ResourceLocation("occultism", "spirit_trade"));
+
+	public static <C extends IInventory> boolean canCompress(IRecipe<C> recipe) {
+		NonNullList<Ingredient> ingredients = recipe.getIngredients();
+		if (!(recipe instanceof ICraftingRecipe))
+			return false;
+		
+		IRecipeSerializer<?> serializer = recipe.getSerializer();
+		for (ResourceLocation denied : RECIPE_DENY_LIST) 
+			if (serializer != null && denied.equals(serializer.getRegistryName()))
+				return false;
+		
 		return AllConfigs.SERVER.recipes.allowShapedSquareInPress.get()
 			&& (ingredients.size() == 4 || ingredients.size() == 9) && ItemHelper.condenseIngredients(ingredients)
 				.size() == 1;
@@ -347,7 +362,7 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity {
 
 	@Override
 	protected <C extends IInventory> boolean matchStaticFilters(IRecipe<C> recipe) {
-		return (recipe instanceof ICraftingRecipe && canCompress(recipe.getIngredients()))
+		return (recipe instanceof ICraftingRecipe && canCompress(recipe))
 			|| recipe.getType() == AllRecipeTypes.COMPACTING.getType();
 	}
 
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/saw/SawRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/saw/SawRenderer.java
index 91e468f99..4f52cd5d8 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/saw/SawRenderer.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/saw/SawRenderer.java
@@ -192,7 +192,7 @@ public class SawRenderer extends SafeTileEntityRenderer<SawTileEntity> {
 				superBuffer = PartialBufferer.get(AllBlockPartials.SAW_BLADE_VERTICAL_INACTIVE, state);
 		}
 
-		MatrixStack m = matrices.contraptionStack;
+		MatrixStack m = matrices.getModel();
 		m.pushPose();
 		MatrixTransformStack.of(m)
 			.centre()
@@ -205,8 +205,8 @@ public class SawRenderer extends SafeTileEntityRenderer<SawTileEntity> {
 			.unCentre();
 
 		superBuffer.transform(m)
-			.light(matrices.entityMatrix, ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld))
-			.renderInto(matrices.entityStack, buffer.getBuffer(RenderType.cutoutMipped()));
+			.light(matrices.getWorld(), ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld))
+			.renderInto(matrices.getViewProjection(), buffer.getBuffer(RenderType.cutoutMipped()));
 
 		m.popPose();
 	}
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntityRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntityRenderer.java
index 57e249dcf..8be7f7b41 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntityRenderer.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntityRenderer.java
@@ -1,7 +1,6 @@
 package com.simibubi.create.content.contraptions.components.structureMovement;
 
 import com.mojang.blaze3d.matrix.MatrixStack;
-import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionMatrices;
 import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionRenderDispatcher;
 
 import net.minecraft.client.renderer.IRenderTypeBuffer;
@@ -39,7 +38,7 @@ public class ContraptionEntityRenderer<C extends AbstractContraptionEntity> exte
 
 		Contraption contraption = entity.getContraption();
 		if (contraption != null) {
-			ContraptionRenderDispatcher.render(entity, contraption, buffers);
+			ContraptionRenderDispatcher.renderFromEntity(entity, contraption, buffers);
 		}
 	}
 
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/StabilizedBearingMovementBehaviour.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/StabilizedBearingMovementBehaviour.java
index 82ae9b925..2e03a363e 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/StabilizedBearingMovementBehaviour.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/StabilizedBearingMovementBehaviour.java
@@ -53,14 +53,14 @@ public class StabilizedBearingMovementBehaviour extends MovementBehaviour {
 
 		orientation = rotation;
 
-		superBuffer.transform(matrices.contraptionStack);
+		superBuffer.transform(matrices.getModel());
 		superBuffer.rotateCentered(orientation);
 
 		// render
 		superBuffer
-			.light(matrices.entityMatrix,
+			.light(matrices.getWorld(),
 				ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld))
-			.renderInto(matrices.entityStack, buffer.getBuffer(RenderType.solid()));
+			.renderInto(matrices.getViewProjection(), buffer.getBuffer(RenderType.solid()));
 	}
 
 	@Override
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionMatrices.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionMatrices.java
index 202d5dddf..733ff004d 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionMatrices.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionMatrices.java
@@ -6,64 +6,80 @@ import com.simibubi.create.foundation.utility.AnimationTickHolder;
 
 import net.minecraft.entity.Entity;
 import net.minecraft.util.math.MathHelper;
-import net.minecraft.util.math.vector.Matrix3f;
 import net.minecraft.util.math.vector.Matrix4f;
 
+/**
+ * <p>
+ *     ContraptionMatrices must be cleared and setup per-contraption per-frame
+ * </p>
+ */
 public class ContraptionMatrices {
 
-	/**
-	 * The results from using this are undefined.
-	 */
-	public static final ContraptionMatrices EMPTY = new ContraptionMatrices();
+	private final MatrixStack modelViewProjection = new MatrixStack();
+	private final MatrixStack viewProjection = new MatrixStack();
+	private final MatrixStack model = new MatrixStack();
+	private final Matrix4f world = new Matrix4f();
+	private final Matrix4f light = new Matrix4f();
 
-	public final MatrixStack entityStack;
-	public final MatrixStack contraptionStack;
-	public final MatrixStack finalStack;
-	public final Matrix4f entityMatrix;
-	public final Matrix4f lightMatrix;
+	private boolean ready;
 
-	private ContraptionMatrices() {
-		this.entityStack = this.contraptionStack = this.finalStack = new MatrixStack();
-		this.entityMatrix = new Matrix4f();
-		this.lightMatrix = new Matrix4f();
+	public ContraptionMatrices() {
+		world.setIdentity();
+		light.setIdentity();
 	}
 
-	public ContraptionMatrices(MatrixStack entityStack, AbstractContraptionEntity entity) {
-		this.entityStack = copyStack(entityStack);
-		this.contraptionStack = new MatrixStack();
+	public void setup(MatrixStack viewProjection, AbstractContraptionEntity entity) {
 		float partialTicks = AnimationTickHolder.getPartialTicks();
-		entity.doLocalTransforms(partialTicks, new MatrixStack[] { this.contraptionStack });
 
-		entityMatrix = translateTo(entity, partialTicks);
+		this.viewProjection.pushPose();
+		transform(this.viewProjection, viewProjection);
+		model.pushPose();
+		entity.doLocalTransforms(partialTicks, new MatrixStack[] { model });
 
-		lightMatrix = entityMatrix.copy();
-		lightMatrix.multiply(contraptionStack.last().pose());
+		modelViewProjection.pushPose();
+		transform(modelViewProjection, viewProjection);
+		transform(modelViewProjection, model);
 
-		finalStack = copyStack(entityStack);
-		transform(finalStack, contraptionStack);
+		translateToEntity(world, entity, partialTicks);
+
+		light.set(world);
+		light.multiply(model
+				.last().pose());
+
+		ready = true;
 	}
 
-	public MatrixStack getFinalStack() {
-		return finalStack;
+	public void clear() {
+		clearStack(modelViewProjection);
+		clearStack(viewProjection);
+		clearStack(model);
+		world.setIdentity();
+		light.setIdentity();
+		ready = false;
 	}
 
-	public Matrix4f getFinalModel() {
-		return finalStack.last().pose();
+	public MatrixStack getModelViewProjection() {
+		return modelViewProjection;
 	}
 
-	public Matrix3f getFinalNormal() {
-		return finalStack.last().normal();
+	public MatrixStack getViewProjection() {
+		return viewProjection;
 	}
 
-	public Matrix4f getFinalLight() {
-		return lightMatrix;
+	public MatrixStack getModel() {
+		return model;
 	}
 
-	public static Matrix4f translateTo(Entity entity, float partialTicks) {
-		double x = MathHelper.lerp(partialTicks, entity.xOld, entity.getX());
-		double y = MathHelper.lerp(partialTicks, entity.yOld, entity.getY());
-		double z = MathHelper.lerp(partialTicks, entity.zOld, entity.getZ());
-		return Matrix4f.createTranslateMatrix((float) x, (float) y, (float) z);
+	public Matrix4f getWorld() {
+		return world;
+	}
+
+	public Matrix4f getLight() {
+		return light;
+	}
+
+	public boolean isReady() {
+		return ready;
 	}
 
 	public static void transform(MatrixStack ms, MatrixStack transform) {
@@ -75,16 +91,17 @@ public class ContraptionMatrices {
 			.normal());
 	}
 
-	public static MatrixStack copyStack(MatrixStack ms) {
-		MatrixStack cms = new MatrixStack();
-
-		transform(cms, ms);
-
-		return cms;
+	public static void translateToEntity(Matrix4f matrix, Entity entity, float partialTicks) {
+		double x = MathHelper.lerp(partialTicks, entity.xOld, entity.getX());
+		double y = MathHelper.lerp(partialTicks, entity.yOld, entity.getY());
+		double z = MathHelper.lerp(partialTicks, entity.zOld, entity.getZ());
+		matrix.setTranslation((float) x, (float) y, (float) z);
 	}
 
-	public Matrix4f contraptionPose() {
-		return contraptionStack.last()
-				.pose();
+	public static void clearStack(MatrixStack ms) {
+		while (!ms.clear()) {
+			ms.popPose();
+		}
 	}
+
 }
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderDispatcher.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderDispatcher.java
index 2d98ca770..f74968039 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderDispatcher.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderDispatcher.java
@@ -71,13 +71,24 @@ public class ContraptionRenderDispatcher {
 		reset();
 	}
 
-	public static void render(AbstractContraptionEntity entity, Contraption contraption, IRenderTypeBuffer buffers) {
+	public static void renderFromEntity(AbstractContraptionEntity entity, Contraption contraption, IRenderTypeBuffer buffers) {
 		World world = entity.level;
 
 		ContraptionRenderInfo renderInfo = WORLDS.get(world)
 				.getRenderInfo(contraption);
+		ContraptionMatrices matrices = renderInfo.getMatrices();
 
-		renderDynamic(world, renderInfo.renderWorld, contraption, renderInfo.getMatrices(), buffers);
+		// something went wrong with the other rendering
+		if (!matrices.isReady()) return;
+
+		PlacementSimulationWorld renderWorld = renderInfo.renderWorld;
+
+		renderTileEntities(world, renderWorld, contraption, matrices, buffers);
+
+		if (buffers instanceof IRenderTypeBuffer.Impl)
+			((IRenderTypeBuffer.Impl) buffers).endBatch();
+
+		renderActors(world, renderWorld, contraption, matrices, buffers);
 	}
 
 	public static PlacementSimulationWorld setupRenderWorld(World world, Contraption c) {
@@ -96,18 +107,10 @@ public class ContraptionRenderDispatcher {
 		return renderWorld;
 	}
 
-	public static void renderDynamic(World world, PlacementSimulationWorld renderWorld, Contraption c,
-									 ContraptionMatrices matrices, IRenderTypeBuffer buffer) {
-		renderTileEntities(world, renderWorld, c, matrices, buffer);
-		if (buffer instanceof IRenderTypeBuffer.Impl)
-			((IRenderTypeBuffer.Impl) buffer).endBatch();
-		renderActors(world, renderWorld, c, matrices, buffer);
-	}
-
 	public static void renderTileEntities(World world, PlacementSimulationWorld renderWorld, Contraption c,
 										  ContraptionMatrices matrices, IRenderTypeBuffer buffer) {
 		TileEntityRenderHelper.renderTileEntities(world, renderWorld, c.specialRenderedTileEntities,
-				matrices.getFinalStack(), matrices.getFinalLight(), buffer);
+				matrices.getModelViewProjection(), matrices.getLight(), buffer);
 	}
 
 	protected static void renderActors(World world, PlacementSimulationWorld renderWorld, Contraption c,
@@ -120,7 +123,7 @@ public class ContraptionRenderDispatcher {
 				context.world = world;
 			Template.BlockInfo blockInfo = actor.getLeft();
 
-			MatrixStack m = matrices.contraptionStack;
+			MatrixStack m = matrices.getModel();
 			m.pushPose();
 			MatrixTransformStack.of(m)
 					.translate(blockInfo.pos);
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderInfo.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderInfo.java
index db8c3e764..2b628393e 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderInfo.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderInfo.java
@@ -1,19 +1,19 @@
 package com.simibubi.create.content.contraptions.components.structureMovement.render;
 
 import com.jozufozu.flywheel.event.BeginFrameEvent;
+import com.mojang.blaze3d.matrix.MatrixStack;
 import com.simibubi.create.content.contraptions.components.structureMovement.AbstractContraptionEntity;
 import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
 import com.simibubi.create.foundation.utility.AnimationTickHolder;
 import com.simibubi.create.foundation.utility.worldWrappers.PlacementSimulationWorld;
 
 import net.minecraft.util.math.MathHelper;
-import net.minecraft.util.math.vector.Vector3d;
 
 public class ContraptionRenderInfo {
 	public final Contraption contraption;
 	public final PlacementSimulationWorld renderWorld;
 
-	private ContraptionMatrices matrices = ContraptionMatrices.EMPTY;
+	private final ContraptionMatrices matrices = new ContraptionMatrices();
 	private boolean visible;
 
 	public ContraptionRenderInfo(Contraption contraption, PlacementSimulationWorld renderWorld) {
@@ -30,29 +30,41 @@ public class ContraptionRenderInfo {
     }
 
     public void beginFrame(BeginFrameEvent event) {
+		matrices.clear();
+
 		AbstractContraptionEntity entity = contraption.entity;
 
 		visible = event.getClippingHelper().isVisible(entity.getBoundingBoxForCulling().inflate(2));
-
-		event.getStack().pushPose();
-
-		Vector3d cameraPos = event.getInfo()
-				.getPosition();
-		double x = MathHelper.lerp(AnimationTickHolder.getPartialTicks(), entity.xOld, entity.getX()) - cameraPos.x;
-		double y = MathHelper.lerp(AnimationTickHolder.getPartialTicks(), entity.yOld, entity.getY()) - cameraPos.y;
-		double z = MathHelper.lerp(AnimationTickHolder.getPartialTicks(), entity.zOld, entity.getZ()) - cameraPos.z;
-
-		event.getStack().translate(x, y, z);
-
-		matrices = new ContraptionMatrices(event.getStack(), entity);
-
-		event.getStack().popPose();
 	}
 
 	public boolean isVisible() {
 		return visible && contraption.entity.isAlive();
 	}
 
+	/**
+	 * Need to call this during RenderLayerEvent.
+	 */
+	public void setupMatrices(MatrixStack viewProjection, double camX, double camY, double camZ) {
+		if (!matrices.isReady()) {
+			AbstractContraptionEntity entity = contraption.entity;
+
+			viewProjection.pushPose();
+
+			double x = MathHelper.lerp(AnimationTickHolder.getPartialTicks(), entity.xOld, entity.getX()) - camX;
+			double y = MathHelper.lerp(AnimationTickHolder.getPartialTicks(), entity.yOld, entity.getY()) - camY;
+			double z = MathHelper.lerp(AnimationTickHolder.getPartialTicks(), entity.zOld, entity.getZ()) - camZ;
+
+			viewProjection.translate(x, y, z);
+
+			matrices.setup(viewProjection, entity);
+
+			viewProjection.popPose();
+		}
+	}
+
+	/**
+	 * If #setupMatrices is called correctly, the returned matrices will be ready
+	 */
 	public ContraptionMatrices getMatrices() {
 		return matrices;
 	}
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderManager.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderManager.java
index 57d9fa089..735d569f1 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderManager.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderManager.java
@@ -29,7 +29,11 @@ public abstract class ContraptionRenderManager<C extends ContraptionRenderInfo>
 		this.world = (World) world;
 	}
 
-	public abstract void renderLayer(RenderLayerEvent event);
+	public void renderLayer(RenderLayerEvent event) {
+		for (C c : visible) {
+			c.setupMatrices(event.stack, event.camX, event.camY, event.camZ);
+		}
+	}
 
 	protected abstract C create(Contraption c);
 
@@ -50,13 +54,17 @@ public abstract class ContraptionRenderManager<C extends ContraptionRenderInfo>
 	}
 
 	public void beginFrame(BeginFrameEvent event) {
-		visible.clear();
 
 		renderInfos.int2ObjectEntrySet()
 				.stream()
 				.map(Map.Entry::getValue)
 				.forEach(renderInfo -> renderInfo.beginFrame(event));
 
+		collectVisible();
+	}
+
+	protected void collectVisible() {
+		visible.clear();
 		renderInfos.int2ObjectEntrySet()
 				.stream()
 				.map(Map.Entry::getValue)
@@ -80,6 +88,9 @@ public abstract class ContraptionRenderManager<C extends ContraptionRenderInfo>
 		renderInfos.clear();
 	}
 
+	/**
+	 * Remove all render infos associated with dead/removed contraptions.
+	 */
 	public void removeDeadRenderers() {
 		renderInfos.values().removeIf(ContraptionRenderInfo::isDead);
 	}
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/FlwContraptionManager.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/FlwContraptionManager.java
index 8bdb46d9f..efd42ad70 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/FlwContraptionManager.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/FlwContraptionManager.java
@@ -38,6 +38,8 @@ public class FlwContraptionManager extends ContraptionRenderManager<RenderedCont
 
 	@Override
 	public void renderLayer(RenderLayerEvent event) {
+		super.renderLayer(event);
+
 		if (visible.isEmpty()) return;
 
 		RenderType layer = event.getType();
@@ -80,13 +82,17 @@ public class FlwContraptionManager extends ContraptionRenderManager<RenderedCont
 
 	@Override
 	public void removeDeadRenderers() {
-		renderInfos.values().removeIf(renderer -> {
-			if (renderer.isDead()) {
-				renderer.invalidate();
-				return true;
-			}
-			return false;
-		});
+		boolean removed = renderInfos.values()
+				.removeIf(renderer -> {
+					if (renderer.isDead()) {
+						renderer.invalidate();
+						return true;
+					}
+					return false;
+				});
+
+		// we use visible in #tick() so we have to re-evaluate it if any were removed
+		if (removed) collectVisible();
 	}
 
 	@Override
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java
index 6dc366b95..56467c7dc 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java
@@ -15,6 +15,7 @@ import com.jozufozu.flywheel.core.model.IModel;
 import com.jozufozu.flywheel.core.model.WorldModel;
 import com.jozufozu.flywheel.event.BeginFrameEvent;
 import com.jozufozu.flywheel.light.GridAlignedBB;
+import com.mojang.blaze3d.matrix.MatrixStack;
 import com.simibubi.create.content.contraptions.components.structureMovement.AbstractContraptionEntity;
 import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
 import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionLighter;
@@ -40,7 +41,8 @@ public class RenderedContraption extends ContraptionRenderInfo {
 
 	private final Map<RenderType, ModelRenderer> renderLayers = new HashMap<>();
 
-	private Matrix4f model;
+	private final Matrix4f modelViewPartial = new Matrix4f();
+	private boolean modelViewPartialReady;
 	private AxisAlignedBB lightBox;
 
 	public RenderedContraption(Contraption contraption, PlacementSimulationWorld renderWorld) {
@@ -74,30 +76,32 @@ public class RenderedContraption extends ContraptionRenderInfo {
 	public void beginFrame(BeginFrameEvent event) {
 		super.beginFrame(event);
 
+		modelViewPartial.setIdentity();
+		modelViewPartialReady = false;
+
 		if (!isVisible()) return;
 
 		kinetics.beginFrame(event.getInfo());
 
-		AbstractContraptionEntity entity = contraption.entity;
-		float pt = AnimationTickHolder.getPartialTicks();
-		AxisAlignedBB lightBox = GridAlignedBB.toAABB(lighter.lightVolume.getTextureVolume());
+		Vector3d cameraPos = event.getCameraPos();
 
-		Vector3d cameraPos = event.getInfo()
-				.getPosition();
+		lightBox = GridAlignedBB.toAABB(lighter.lightVolume.getTextureVolume())
+				.move(-cameraPos.x, -cameraPos.y, -cameraPos.z);
+	}
 
-		float x = (float) (MathHelper.lerp(pt, entity.xOld, entity.getX()) - cameraPos.x);
-		float y = (float) (MathHelper.lerp(pt, entity.yOld, entity.getY()) - cameraPos.y);
-		float z = (float) (MathHelper.lerp(pt, entity.zOld, entity.getZ()) - cameraPos.z);
-		model = Matrix4f.createTranslateMatrix(x, y, z);
+	@Override
+	public void setupMatrices(MatrixStack viewProjection, double camX, double camY, double camZ) {
+		super.setupMatrices(viewProjection, camX, camY, camZ);
 
-		model.multiply(getMatrices().contraptionPose());
-
-		this.lightBox = lightBox.move(-cameraPos.x, -cameraPos.y, -cameraPos.z);
+		if (!modelViewPartialReady) {
+			setupModelViewPartial(modelViewPartial, getMatrices().getModel().last().pose(), contraption.entity, camX, camY, camZ, AnimationTickHolder.getPartialTicks());
+			modelViewPartialReady = true;
+		}
 	}
 
 	void setup(ContraptionProgram shader) {
-		if (model == null || lightBox == null) return;
-		shader.bind(model, lightBox);
+		if (!modelViewPartialReady || lightBox == null) return;
+		shader.bind(modelViewPartial, lightBox);
 		lighter.lightVolume.bind();
 	}
 
@@ -125,10 +129,13 @@ public class RenderedContraption extends ContraptionRenderInfo {
 		for (RenderType layer : blockLayers) {
 			Supplier<IModel> layerModel = () -> new WorldModel(renderWorld, layer, contraption.getBlocks().values());
 
+			ModelRenderer renderer;
 			if (Backend.getInstance().compat.vertexArrayObjectsSupported())
-				renderLayers.put(layer, new ArrayModelRenderer(layerModel));
+				renderer = new ArrayModelRenderer(layerModel);
 			else
-				renderLayers.put(layer, new ModelRenderer(layerModel));
+				renderer = new ModelRenderer(layerModel);
+
+			renderLayers.put(layer, renderer);
 		}
 	}
 
@@ -151,4 +158,13 @@ public class RenderedContraption extends ContraptionRenderInfo {
 	private void buildActors() {
 		contraption.getActors().forEach(kinetics::createActor);
 	}
+
+	public static void setupModelViewPartial(Matrix4f matrix, Matrix4f modelMatrix, AbstractContraptionEntity entity, double camX, double camY, double camZ, float pt) {
+		float x = (float) (MathHelper.lerp(pt, entity.xOld, entity.getX()) - camX);
+		float y = (float) (MathHelper.lerp(pt, entity.yOld, entity.getY()) - camY);
+		float z = (float) (MathHelper.lerp(pt, entity.zOld, entity.getZ()) - camZ);
+		matrix.setTranslation(x, y, z);
+		matrix.multiply(modelMatrix);
+	}
+
 }
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/SBBContraptionManager.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/SBBContraptionManager.java
index 94f0fec6d..4c68ea22c 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/SBBContraptionManager.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/SBBContraptionManager.java
@@ -21,6 +21,7 @@ public class SBBContraptionManager extends ContraptionRenderManager<ContraptionR
 
 	@Override
 	public void renderLayer(RenderLayerEvent event) {
+		super.renderLayer(event);
 		visible.forEach(info -> renderContraptionLayerSBB(event, info));
 	}
 
@@ -38,12 +39,12 @@ public class SBBContraptionManager extends ContraptionRenderManager<ContraptionR
 		SuperByteBuffer contraptionBuffer = CreateClient.BUFFER_CACHE.get(CONTRAPTION, Pair.of(renderInfo.contraption, layer), () -> buildStructureBuffer(renderInfo.renderWorld, renderInfo.contraption, layer));
 
 		if (!contraptionBuffer.isEmpty()) {
-
 			ContraptionMatrices matrices = renderInfo.getMatrices();
-			contraptionBuffer.transform(matrices.contraptionStack)
-					.light(matrices.entityMatrix)
+
+			contraptionBuffer.transform(matrices.getModel())
+					.light(matrices.getWorld())
 					.hybridLight()
-					.renderInto(matrices.entityStack, event.buffers.bufferSource()
+					.renderInto(matrices.getViewProjection(), event.buffers.bufferSource()
 							.getBuffer(layer));
 		}
 
diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/PipeConnection.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/PipeConnection.java
index f766f18f4..240883516 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/fluids/PipeConnection.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/PipeConnection.java
@@ -87,7 +87,7 @@ public class PipeConnection {
 		FlowSource flowSource = source.get();
 		flowSource.manageSource(world);
 	}
-		
+
 	public boolean manageFlows(World world, BlockPos pos, FluidStack internalFluid,
 		Predicate<FluidStack> extractionPredicate) {
 
@@ -193,7 +193,7 @@ public class PipeConnection {
 		if (world.isClientSide) {
 			if (!source.isPresent())
 				determineSource(world, pos);
-			
+
 			spawnParticles(world, pos, flow.fluid);
 			if (particleSplashNextTick)
 				spawnSplashOnRim(world, pos, flow.fluid);
@@ -282,8 +282,8 @@ public class PipeConnection {
 
 	/**
 	 * @return zero if outward == inbound <br>
-	 *         positive if outward > inbound <br>
-	 *         negative if outward < inbound
+	 *         positive if outward {@literal >} inbound <br>
+	 *         negative if outward {@literal <} inbound
 	 */
 	public float comparePressure() {
 		return getOutwardPressure() - getInboundPressure();
diff --git a/src/main/java/com/simibubi/create/content/contraptions/relays/gauge/GaugeRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/relays/gauge/GaugeRenderer.java
index 182c75728..ebd9bfad8 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/relays/gauge/GaugeRenderer.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/relays/gauge/GaugeRenderer.java
@@ -15,7 +15,6 @@ import com.simibubi.create.foundation.utility.Iterate;
 import net.minecraft.block.BlockState;
 import net.minecraft.client.renderer.IRenderTypeBuffer;
 import net.minecraft.client.renderer.RenderType;
-import net.minecraft.client.renderer.WorldRenderer;
 import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
 import net.minecraft.util.Direction;
 import net.minecraft.util.math.MathHelper;
@@ -43,9 +42,9 @@ public class GaugeRenderer extends KineticTileEntityRenderer {
 		if (Backend.getInstance().canUseInstancing(te.getLevel())) return;
 
 		super.renderSafe(te, partialTicks, ms, buffer, light, overlay);
+
 		BlockState gaugeState = te.getBlockState();
 		GaugeTileEntity gaugeTE = (GaugeTileEntity) te;
-		int lightCoords = WorldRenderer.getLightColor(te.getLevel(), gaugeState, te.getBlockPos());
 
 		PartialModel partialModel = (type == Type.SPEED ? AllBlockPartials.GAUGE_HEAD_SPEED : AllBlockPartials.GAUGE_HEAD_STRESS);
 		SuperByteBuffer headBuffer =
@@ -64,12 +63,11 @@ public class GaugeRenderer extends KineticTileEntityRenderer {
 			rotateBufferTowards(dialBuffer, facing).translate(0, dialPivot, dialPivot)
 				.rotate(Direction.EAST, (float) (Math.PI / 2 * -progress))
 				.translate(0, -dialPivot, -dialPivot)
-				.light(lightCoords)
+				.light(light)
 				.renderInto(ms, vb);
-			rotateBufferTowards(headBuffer, facing).light(lightCoords)
+			rotateBufferTowards(headBuffer, facing).light(light)
 				.renderInto(ms, vb);
 		}
-
 	}
 
 	@Override
diff --git a/src/main/java/com/simibubi/create/content/curiosities/bell/BellRenderer.java b/src/main/java/com/simibubi/create/content/curiosities/bell/BellRenderer.java
index 37d12a927..c32fce701 100644
--- a/src/main/java/com/simibubi/create/content/curiosities/bell/BellRenderer.java
+++ b/src/main/java/com/simibubi/create/content/curiosities/bell/BellRenderer.java
@@ -1,7 +1,6 @@
 package com.simibubi.create.content.curiosities.bell;
 
 import com.mojang.blaze3d.matrix.MatrixStack;
-import com.mojang.blaze3d.vertex.IVertexBuilder;
 import com.simibubi.create.foundation.render.PartialBufferer;
 import com.simibubi.create.foundation.render.SuperByteBuffer;
 import com.simibubi.create.foundation.tileEntity.renderer.SafeTileEntityRenderer;
@@ -11,7 +10,6 @@ import net.minecraft.block.BellBlock;
 import net.minecraft.block.BlockState;
 import net.minecraft.client.renderer.IRenderTypeBuffer;
 import net.minecraft.client.renderer.RenderType;
-import net.minecraft.client.renderer.WorldRenderer;
 import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
 import net.minecraft.state.properties.BellAttachment;
 import net.minecraft.util.Direction;
@@ -39,9 +37,8 @@ public class BellRenderer<TE extends AbstractBellTileEntity> extends SafeTileEnt
 			rY += 90;
 		bell.rotateCentered(Direction.UP, AngleHelper.rad(rY));
 
-		IVertexBuilder vb = buffer.getBuffer(RenderType.cutout());
-		int lightCoords = WorldRenderer.getLightColor(te.getLevel(), state, te.getBlockPos());
-		bell.light(lightCoords).renderInto(ms, vb);
+		bell.light(light)
+			.renderInto(ms, buffer.getBuffer(RenderType.cutout()));
 	}
 
 	public static float getSwingAngle(float time) {
diff --git a/src/main/java/com/simibubi/create/content/curiosities/weapons/PotatoCannonProjectileTypes.java b/src/main/java/com/simibubi/create/content/curiosities/weapons/PotatoCannonProjectileTypes.java
index 52c847bd7..cb1159fbe 100644
--- a/src/main/java/com/simibubi/create/content/curiosities/weapons/PotatoCannonProjectileTypes.java
+++ b/src/main/java/com/simibubi/create/content/curiosities/weapons/PotatoCannonProjectileTypes.java
@@ -77,7 +77,7 @@ public class PotatoCannonProjectileTypes {
 			.velocity(1.25f)
 			.knockback(0.5f)
 			.renderTumbling()
-			.onEntityHit(setFire(3))
+			.preEntityHit(setFire(3))
 			.registerAndAssign(Items.BAKED_POTATO),
 
 		CARROT = create("carrot").damage(4)
@@ -247,17 +247,21 @@ public class PotatoCannonProjectileTypes {
 			.velocity(1.1f)
 			.renderTumbling()
 			.sticky()
-			.onEntityHit(setFire(12))
+			.preEntityHit(setFire(12))
 			.soundPitch(1.0f)
 			.registerAndAssign(AllItems.BLAZE_CAKE.get())
 	;
 
 	public static void registerType(ResourceLocation resLoc, PotatoCannonProjectileTypes type) {
-		ALL.put(resLoc, type);
+		synchronized (ALL) {
+			ALL.put(resLoc, type);
+		}
 	}
 
 	public static void assignType(IRegistryDelegate<Item> item, PotatoCannonProjectileTypes type) {
-		ITEM_MAP.put(item, type);
+		synchronized (ITEM_MAP) {
+			ITEM_MAP.put(item, type);
+		}
 	}
 
 	public static Optional<PotatoCannonProjectileTypes> getProjectileTypeOf(ItemStack item) {
@@ -282,7 +286,8 @@ public class PotatoCannonProjectileTypes {
 	private float fwoompPitch = 1;
 	private boolean sticky = false;
 	private PotatoProjectileRenderMode renderMode = new PotatoProjectileRenderMode.Billboard();
-	private Predicate<EntityRayTraceResult> onEntityHit = e -> false;
+	private Predicate<EntityRayTraceResult> preEntityHit = e -> false; // True if hit should be canceled
+	private Predicate<EntityRayTraceResult> onEntityHit = e -> false; // True if shouldn't recover projectile
 	private BiPredicate<IWorld, BlockRayTraceResult> onBlockHit = (w, ray) -> false;
 
 	public float getGravityMultiplier() {
@@ -323,6 +328,10 @@ public class PotatoCannonProjectileTypes {
 
 	public boolean isSticky() { return sticky; }
 
+	public boolean preEntityHit(EntityRayTraceResult ray) {
+		return preEntityHit.test(ray);
+	}
+	
 	public boolean onEntityHit(EntityRayTraceResult ray) {
 		return onEntityHit.test(ray);
 	}
@@ -538,6 +547,11 @@ public class PotatoCannonProjectileTypes {
 			return this;
 		}
 
+		public Builder preEntityHit(Predicate<EntityRayTraceResult> callback) {
+			result.preEntityHit = callback;
+			return this;
+		}
+		
 		public Builder onEntityHit(Predicate<EntityRayTraceResult> callback) {
 			result.onEntityHit = callback;
 			return this;
diff --git a/src/main/java/com/simibubi/create/content/curiosities/weapons/PotatoProjectileEntity.java b/src/main/java/com/simibubi/create/content/curiosities/weapons/PotatoProjectileEntity.java
index 6d7fd45db..0cd481f15 100644
--- a/src/main/java/com/simibubi/create/content/curiosities/weapons/PotatoProjectileEntity.java
+++ b/src/main/java/com/simibubi/create/content/curiosities/weapons/PotatoProjectileEntity.java
@@ -190,6 +190,8 @@ public class PotatoProjectileEntity extends DamagingProjectileEntity implements
 
 		if (target instanceof WitherEntity && ((WitherEntity) target).isPowered())
 			return;
+		if (projectileType.preEntityHit(ray))
+			return;
 
 		boolean targetIsEnderman = target.getType() == EntityType.ENDERMAN;
 		int k = target.getRemainingFireTicks();
diff --git a/src/main/java/com/simibubi/create/content/logistics/block/redstone/AnalogLeverRenderer.java b/src/main/java/com/simibubi/create/content/logistics/block/redstone/AnalogLeverRenderer.java
index 79e604858..6475f2954 100644
--- a/src/main/java/com/simibubi/create/content/logistics/block/redstone/AnalogLeverRenderer.java
+++ b/src/main/java/com/simibubi/create/content/logistics/block/redstone/AnalogLeverRenderer.java
@@ -13,7 +13,6 @@ import com.simibubi.create.foundation.utility.Color;
 import net.minecraft.block.BlockState;
 import net.minecraft.client.renderer.IRenderTypeBuffer;
 import net.minecraft.client.renderer.RenderType;
-import net.minecraft.client.renderer.WorldRenderer;
 import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
 import net.minecraft.state.properties.AttachFace;
 import net.minecraft.util.Direction;
@@ -31,7 +30,6 @@ public class AnalogLeverRenderer extends SafeTileEntityRenderer<AnalogLeverTileE
 		if (Backend.getInstance().canUseInstancing(te.getLevel())) return;
 
 		BlockState leverState = te.getBlockState();
-		int lightCoords = WorldRenderer.getLightColor(te.getLevel(), leverState, te.getBlockPos());
 		float state = te.clientState.get(partialTicks);
 
 		IVertexBuilder vb = buffer.getBuffer(RenderType.solid());
@@ -42,13 +40,13 @@ public class AnalogLeverRenderer extends SafeTileEntityRenderer<AnalogLeverTileE
 		transform(handle, leverState).translate(1 / 2f, 1 / 16f, 1 / 2f)
 				.rotate(Direction.EAST, angle)
 				.translate(-1 / 2f, -1 / 16f, -1 / 2f);
-		handle.light(lightCoords)
+		handle.light(light)
 				.renderInto(ms, vb);
 
 		// Indicator
 		int color = Color.mixColors(0x2C0300, 0xCD0000, state / 15f);
 		SuperByteBuffer indicator = transform(PartialBufferer.get(AllBlockPartials.ANALOG_LEVER_INDICATOR, leverState), leverState);
-		indicator.light(lightCoords)
+		indicator.light(light)
 				.color(color)
 				.renderInto(ms, vb);
 	}
diff --git a/src/main/java/com/simibubi/create/content/logistics/item/LecternControllerRenderer.java b/src/main/java/com/simibubi/create/content/logistics/item/LecternControllerRenderer.java
index 3f725dff0..33e7d36d5 100644
--- a/src/main/java/com/simibubi/create/content/logistics/item/LecternControllerRenderer.java
+++ b/src/main/java/com/simibubi/create/content/logistics/item/LecternControllerRenderer.java
@@ -26,12 +26,12 @@ public class LecternControllerRenderer extends SafeTileEntityRenderer<LecternCon
 
 		ItemStack stack = AllItems.LINKED_CONTROLLER.asStack();
 		TransformType transformType = TransformType.NONE;
-		LinkedControllerModel mainModel = ((LinkedControllerModel) Minecraft.getInstance()
+		LinkedControllerModel mainModel = (LinkedControllerModel) Minecraft.getInstance()
 			.getItemRenderer()
-			.getModel(stack, null, null));
+			.getModel(stack, null, null);
 		PartialItemModelRenderer renderer = PartialItemModelRenderer.of(stack, transformType, ms, buffer, overlay);
 		boolean active = te.hasUser();
-		boolean usedByMe = te.isUsedBy(Minecraft.getInstance().player);
+		boolean renderDepression = te.isUsedBy(Minecraft.getInstance().player);
 
 		Direction facing = te.getBlockState().getValue(LecternControllerBlock.FACING);
 		MatrixTransformStack msr = MatrixTransformStack.of(ms);
@@ -41,7 +41,7 @@ public class LecternControllerRenderer extends SafeTileEntityRenderer<LecternCon
 		msr.rotateY(AngleHelper.horizontalAngle(facing) - 90);
 		msr.translate(0.28, 0, 0);
 		msr.rotateZ(-22.0);
-		LinkedControllerItemRenderer.renderLinkedController(stack, mainModel, renderer, transformType, ms, light, active, usedByMe);
+		LinkedControllerItemRenderer.renderInLectern(stack, mainModel, renderer, transformType, ms, light, active, renderDepression);
 		ms.popPose();
 	}
 
diff --git a/src/main/java/com/simibubi/create/content/logistics/item/LinkedControllerClientHandler.java b/src/main/java/com/simibubi/create/content/logistics/item/LinkedControllerClientHandler.java
index c5dd09860..18f1907d0 100644
--- a/src/main/java/com/simibubi/create/content/logistics/item/LinkedControllerClientHandler.java
+++ b/src/main/java/com/simibubi/create/content/logistics/item/LinkedControllerClientHandler.java
@@ -35,10 +35,6 @@ import net.minecraft.util.text.TextFormatting;
 
 public class LinkedControllerClientHandler {
 
-	enum Mode {
-		IDLE, ACTIVE, BIND
-	}
-
 	public static Mode MODE = Mode.IDLE;
 	public static int PACKET_RATE = 5;
 	public static Collection<Integer> currentlyPressed = new HashSet<>();
@@ -112,6 +108,8 @@ public class LinkedControllerClientHandler {
 		if (!currentlyPressed.isEmpty())
 			AllPackets.channel.sendToServer(new LinkedControllerInputPacket(currentlyPressed, false));
 		currentlyPressed.clear();
+
+		LinkedControllerItemRenderer.resetButtons();
 	}
 
 	protected static boolean isActuallyPressed(KeyBinding kb) {
@@ -124,6 +122,7 @@ public class LinkedControllerClientHandler {
 
 	public static void tick() {
 		LinkedControllerItemRenderer.tick();
+
 		if (MODE == Mode.IDLE)
 			return;
 		if (packetCooldown > 0)
@@ -269,4 +268,8 @@ public class LinkedControllerClientHandler {
 
 	}
 
+	public enum Mode {
+		IDLE, ACTIVE, BIND
+	}
+
 }
diff --git a/src/main/java/com/simibubi/create/content/logistics/item/LinkedControllerItemRenderer.java b/src/main/java/com/simibubi/create/content/logistics/item/LinkedControllerItemRenderer.java
index e82a57735..b67e280e5 100644
--- a/src/main/java/com/simibubi/create/content/logistics/item/LinkedControllerItemRenderer.java
+++ b/src/main/java/com/simibubi/create/content/logistics/item/LinkedControllerItemRenderer.java
@@ -50,35 +50,56 @@ public class LinkedControllerItemRenderer extends CustomRenderedItemModelRendere
 		}
 	}
 
+	static void resetButtons() {
+		for (int i = 0; i < buttons.size(); i++) {
+			buttons.get(i).startWithValue(0);
+		}
+	}
+
 	@Override
 	protected void render(ItemStack stack, LinkedControllerModel model, PartialItemModelRenderer renderer,
 		ItemCameraTransforms.TransformType transformType, MatrixStack ms, IRenderTypeBuffer buffer, int light,
 		int overlay) {
-
-		renderLinkedController(stack, model, renderer, transformType, ms, light, null, null);
+		renderNormal(stack, model, renderer, transformType, ms, light);
 	}
 
-	public static void renderLinkedController(ItemStack stack, LinkedControllerModel model,
+	protected static void renderNormal(ItemStack stack, LinkedControllerModel model,
 	  	PartialItemModelRenderer renderer, ItemCameraTransforms.TransformType transformType, MatrixStack ms,
-  		int light, Boolean active, Boolean usedByMe) {
+  		int light) {
+		render(stack, model, renderer, transformType, ms, light, RenderType.NORMAL, false, false);
+	}
 
+	public static void renderInLectern(ItemStack stack, LinkedControllerModel model,
+	  	PartialItemModelRenderer renderer, ItemCameraTransforms.TransformType transformType, MatrixStack ms,
+  		int light, boolean active, boolean renderDepression) {
+		render(stack, model, renderer, transformType, ms, light, RenderType.LECTERN, active, renderDepression);
+	}
+
+	protected static void render(ItemStack stack, LinkedControllerModel model,
+	  	PartialItemModelRenderer renderer, ItemCameraTransforms.TransformType transformType, MatrixStack ms,
+  		int light, RenderType renderType, boolean active, boolean renderDepression) {
 		float pt = AnimationTickHolder.getPartialTicks();
 		MatrixTransformStack msr = MatrixTransformStack.of(ms);
 
 		ms.pushPose();
 
-		Minecraft mc = Minecraft.getInstance();
-		boolean rightHanded = mc.options.mainHand == HandSide.RIGHT;
-		TransformType mainHand =
-				rightHanded ? TransformType.FIRST_PERSON_RIGHT_HAND : TransformType.FIRST_PERSON_LEFT_HAND;
-		TransformType offHand =
-				rightHanded ? TransformType.FIRST_PERSON_LEFT_HAND : TransformType.FIRST_PERSON_RIGHT_HAND;
+		if (renderType == RenderType.NORMAL) {
+			Minecraft mc = Minecraft.getInstance();
+			boolean rightHanded = mc.options.mainHand == HandSide.RIGHT;
+			TransformType mainHand =
+					rightHanded ? TransformType.FIRST_PERSON_RIGHT_HAND : TransformType.FIRST_PERSON_LEFT_HAND;
+			TransformType offHand =
+					rightHanded ? TransformType.FIRST_PERSON_LEFT_HAND : TransformType.FIRST_PERSON_RIGHT_HAND;
 
-		if (active == null) {
 			active = false;
-
 			boolean noControllerInMain = !AllItems.LINKED_CONTROLLER.isIn(mc.player.getMainHandItem());
+
 			if (transformType == mainHand || (transformType == offHand && noControllerInMain)) {
+				float equip = equipProgress.getValue(pt);
+				int handModifier = transformType == TransformType.FIRST_PERSON_LEFT_HAND ? -1 : 1;
+				msr.translate(0, equip / 4, equip / 4 * handModifier);
+				msr.rotateY(equip * -30 * handModifier);
+				msr.rotateZ(equip * -30);
 				active = true;
 			}
 
@@ -89,56 +110,62 @@ public class LinkedControllerItemRenderer extends CustomRenderedItemModelRendere
 					active = true;
 			}
 
-			active &= LinkedControllerClientHandler.MODE != Mode.IDLE && !LinkedControllerClientHandler.inLectern();
-			usedByMe = active;
+			active &= LinkedControllerClientHandler.MODE != Mode.IDLE;
 
-			if (active && (transformType == mainHand || transformType == offHand)) {
-				float equip = equipProgress.getValue(pt);
-				int handModifier = transformType == TransformType.FIRST_PERSON_LEFT_HAND ? -1 : 1;
-				msr.translate(0, equip / 4, equip / 4 * handModifier);
-				msr.rotateY(equip * -30 * handModifier);
-				msr.rotateZ(equip * -30);
-			}
+			renderDepression = true;
 		}
 
 		renderer.render(active ? model.getPartial("powered") : model.getOriginalModel(), light);
 
+		if (!active) {
+			ms.popPose();
+			return;
+		}
+
 		IBakedModel button = model.getPartial("button");
 		float s = 1 / 16f;
 		float b = s * -.75f;
 		int index = 0;
 
-		if (LinkedControllerClientHandler.MODE == Mode.BIND) {
-			int i = (int) MathHelper.lerp((MathHelper.sin(AnimationTickHolder.getRenderTime() / 4f) + 1) / 2, 5, 15);
-			light = i << 20;
+		if (renderType == RenderType.NORMAL) {
+			if (LinkedControllerClientHandler.MODE == Mode.BIND) {
+				int i = (int) MathHelper.lerp((MathHelper.sin(AnimationTickHolder.getRenderTime() / 4f) + 1) / 2, 5, 15);
+				light = i << 20;
+			}
 		}
 
 		ms.pushPose();
 		msr.translate(2 * s, 0, 8 * s);
-		button(renderer, ms, light, pt, button, b, index++, usedByMe);
+		renderButton(renderer, ms, light, pt, button, b, index++, renderDepression);
 		msr.translate(4 * s, 0, 0);
-		button(renderer, ms, light, pt, button, b, index++, usedByMe);
+		renderButton(renderer, ms, light, pt, button, b, index++, renderDepression);
 		msr.translate(-2 * s, 0, 2 * s);
-		button(renderer, ms, light, pt, button, b, index++, usedByMe);
+		renderButton(renderer, ms, light, pt, button, b, index++, renderDepression);
 		msr.translate(0, 0, -4 * s);
-		button(renderer, ms, light, pt, button, b, index++, usedByMe);
+		renderButton(renderer, ms, light, pt, button, b, index++, renderDepression);
 		ms.popPose();
 
 		msr.translate(3 * s, 0, 3 * s);
-		button(renderer, ms, light, pt, button, b, index++, usedByMe);
+		renderButton(renderer, ms, light, pt, button, b, index++, renderDepression);
 		msr.translate(2 * s, 0, 0);
-		button(renderer, ms, light, pt, button, b, index++, usedByMe);
+		renderButton(renderer, ms, light, pt, button, b, index++, renderDepression);
 
 		ms.popPose();
 	}
 
-	protected static void button(PartialItemModelRenderer renderer, MatrixStack ms, int light, float pt, IBakedModel button,
-		float b, int index, boolean usedByMe) {
+	protected static void renderButton(PartialItemModelRenderer renderer, MatrixStack ms, int light, float pt, IBakedModel button,
+		float b, int index, boolean renderDepression) {
 		ms.pushPose();
-		float depression = usedByMe ? b * buttons.get(index).getValue(pt) : 0;
-		ms.translate(0, depression, 0);
+		if (renderDepression) {
+			float depression = b * buttons.get(index).getValue(pt);
+			ms.translate(0, depression, 0);
+		}
 		renderer.renderSolid(button, light);
 		ms.popPose();
 	}
 
+	protected enum RenderType {
+		NORMAL, LECTERN;
+	}
+
 }
diff --git a/src/main/java/com/simibubi/create/content/logistics/item/filter/FilterItem.java b/src/main/java/com/simibubi/create/content/logistics/item/filter/FilterItem.java
index b6685de89..b971d7cda 100644
--- a/src/main/java/com/simibubi/create/content/logistics/item/filter/FilterItem.java
+++ b/src/main/java/com/simibubi/create/content/logistics/item/filter/FilterItem.java
@@ -220,6 +220,8 @@ public class FilterItem extends Item implements INamedContainerProvider {
 			for (INBT inbt : attributes) {
 				CompoundNBT compound = (CompoundNBT) inbt;
 				ItemAttribute attribute = ItemAttribute.fromNBT(compound);
+				if (attribute == null)
+					continue;
 				boolean matches = attribute.appliesTo(stack, world) != compound.getBoolean("Inverted");
 
 				if (matches) {
diff --git a/src/main/java/com/simibubi/create/content/schematics/SchematicPrinter.java b/src/main/java/com/simibubi/create/content/schematics/SchematicPrinter.java
index 99dd33f7a..906a907fe 100644
--- a/src/main/java/com/simibubi/create/content/schematics/SchematicPrinter.java
+++ b/src/main/java/com/simibubi/create/content/schematics/SchematicPrinter.java
@@ -51,7 +51,14 @@ public class SchematicPrinter {
 	public void fromTag(CompoundNBT compound, boolean clientPacket) {
 		if (compound.contains("CurrentPos"))
 			currentPos = NBTUtil.readBlockPos(compound.getCompound("CurrentPos"));
-
+		if (clientPacket) {
+			schematicLoaded = false;
+			if (compound.contains("Anchor")) {
+				schematicAnchor = NBTUtil.readBlockPos(compound.getCompound("Anchor"));
+				schematicLoaded = true;
+			}
+		}
+		
 		printingEntityIndex = compound.getInt("EntityProgress");
 		printStage = PrintStage.valueOf(compound.getString("PrintStage"));
 		compound.getList("DeferredBlocks", 10).stream()
@@ -62,7 +69,9 @@ public class SchematicPrinter {
 	public void write(CompoundNBT compound) {
 		if (currentPos != null)
 			compound.put("CurrentPos", NBTUtil.writeBlockPos(currentPos));
-
+		if (schematicAnchor != null)
+			compound.put("Anchor", NBTUtil.writeBlockPos(schematicAnchor));
+		
 		compound.putInt("EntityProgress", printingEntityIndex);
 		compound.putString("PrintStage", printStage.name());
 		ListNBT tagDeferredBlocks = new ListNBT();
@@ -78,15 +87,19 @@ public class SchematicPrinter {
 		Template activeTemplate = SchematicItem.loadSchematic(blueprint);
 		PlacementSettings settings = SchematicItem.getSettings(blueprint, processNBT);
 
-		schematicAnchor = NBTUtil.readBlockPos(blueprint.getTag().getCompound("Anchor"));
+		schematicAnchor = NBTUtil.readBlockPos(blueprint.getTag()
+			.getCompound("Anchor"));
 		blockReader = new SchematicWorld(schematicAnchor, originalWorld);
 		activeTemplate.placeInWorldChunk(blockReader, schematicAnchor, settings, blockReader.getRandom());
 
+		BlockPos extraBounds = Template.calculateRelativePosition(settings, activeTemplate.getSize()
+			.offset(-1, -1, -1));
+		blockReader.bounds.expand(new MutableBoundingBox(extraBounds, extraBounds));
+
 		StructureTransform transform = new StructureTransform(settings.getRotationPivot(), Direction.Axis.Y,
 			settings.getRotation(), settings.getMirror());
-		for (TileEntity te : blockReader.tileEntities.values()) {
+		for (TileEntity te : blockReader.tileEntities.values())
 			transform.apply(te);
-		}
 
 		printingEntityIndex = -1;
 		printStage = PrintStage.BLOCKS;
diff --git a/src/main/java/com/simibubi/create/content/schematics/packet/SchematicPlacePacket.java b/src/main/java/com/simibubi/create/content/schematics/packet/SchematicPlacePacket.java
index 3f6eccd0f..3331040c2 100644
--- a/src/main/java/com/simibubi/create/content/schematics/packet/SchematicPlacePacket.java
+++ b/src/main/java/com/simibubi/create/content/schematics/packet/SchematicPlacePacket.java
@@ -3,6 +3,7 @@ package com.simibubi.create.content.schematics.packet;
 import java.util.function.Supplier;
 
 import com.simibubi.create.content.schematics.SchematicPrinter;
+import com.simibubi.create.foundation.config.AllConfigs;
 import com.simibubi.create.foundation.networking.SimplePacketBase;
 import com.simibubi.create.foundation.utility.BlockHelper;
 
@@ -38,12 +39,20 @@ public class SchematicPlacePacket extends SimplePacketBase {
 			World world = player.getLevel();
 			SchematicPrinter printer = new SchematicPrinter();
 			printer.loadSchematic(stack, world, !player.canUseGameMasterBlocks());
+			if (!printer.isLoaded())
+				return;
+			
+			boolean includeAir = AllConfigs.SERVER.schematics.creativePrintIncludesAir.get();
 
 			while (printer.advanceCurrentPos()) {
 				if (!printer.shouldPlaceCurrent(world))
 					continue;
 
 				printer.handleCurrentTarget((pos, state, tile) -> {
+					boolean placingAir = state.getBlock().isAir(state, world, pos);
+					if (placingAir && !includeAir)
+						return;
+					
 					CompoundNBT tileData = tile != null ? tile.save(new CompoundNBT()) : null;
 					BlockHelper.placeSchematicBlock(world, state, pos, null, tileData);
 				}, (pos, entity) -> {
diff --git a/src/main/java/com/simibubi/create/foundation/config/CSchematics.java b/src/main/java/com/simibubi/create/foundation/config/CSchematics.java
index 21a38ef34..9745a8b84 100644
--- a/src/main/java/com/simibubi/create/foundation/config/CSchematics.java
+++ b/src/main/java/com/simibubi/create/foundation/config/CSchematics.java
@@ -2,6 +2,7 @@ package com.simibubi.create.foundation.config;
 
 public class CSchematics extends ConfigBase {
 
+	public ConfigBool creativePrintIncludesAir = b(false, "creativePrintIncludesAir", Comments.creativePrintIncludesAir);
 	public ConfigInt maxSchematics = i(10, 1, "maxSchematics", Comments.maxSchematics);
 	public ConfigInt maxTotalSchematicSize = i(256, 16, "maxSchematics", Comments.kb, Comments.maxSize);
 	public ConfigInt maxSchematicPacketSize =
@@ -33,6 +34,8 @@ public class CSchematics extends ConfigBase {
 		static String skips = "Amount of block positions per tick scanned by a running cannon. Higher => Faster";
 		static String gunpowderWorth = "% of Schematicannon's Fuel filled by 1 Gunpowder.";
 		static String fuelUsage = "% of Schematicannon's Fuel used for each fired block.";
+		static String creativePrintIncludesAir =
+			"Whether placing a Schematic directly in Creative Mode should replace world blocks with Air";
 	}
 
 }
diff --git a/src/main/java/com/simibubi/create/foundation/ponder/PonderRegistry.java b/src/main/java/com/simibubi/create/foundation/ponder/PonderRegistry.java
index ee420a6f4..ee876bd31 100644
--- a/src/main/java/com/simibubi/create/foundation/ponder/PonderRegistry.java
+++ b/src/main/java/com/simibubi/create/foundation/ponder/PonderRegistry.java
@@ -41,23 +41,32 @@ public class PonderRegistry {
 	// Map from item ids to all storyboards
 	public static final Map<ResourceLocation, List<PonderStoryBoardEntry>> ALL = new HashMap<>();
 
-	private static String currentNamespace;
+	private static final ThreadLocal<String> CURRENT_NAMESPACE = new ThreadLocal<>();
+
+	private static String getCurrentNamespace() {
+		return CURRENT_NAMESPACE.get();
+	}
+
+	private static void setCurrentNamespace(String namespace) {
+		CURRENT_NAMESPACE.set(namespace);
+	}
 
 	public static void startRegistration(String namespace) {
-		if (currentNamespace != null) {
+		if (getCurrentNamespace() != null) {
 			throw new IllegalStateException("Cannot start registration when already started!");
 		}
-		currentNamespace = namespace;
+		setCurrentNamespace(namespace);
 	}
 
 	public static void endRegistration() {
-		if (currentNamespace == null) {
+		if (getCurrentNamespace() == null) {
 			throw new IllegalStateException("Cannot end registration when not started!");
 		}
-		currentNamespace = null;
+		setCurrentNamespace(null);
 	}
 
 	private static String getNamespaceOrThrow() {
+		String currentNamespace = getCurrentNamespace();
 		if (currentNamespace == null) {
 			throw new IllegalStateException("Cannot register storyboard without starting registration!");
 		}
@@ -71,8 +80,10 @@ public class PonderRegistry {
 		PonderSceneBuilder builder = new PonderSceneBuilder(entry);
 		if (tags.length > 0)
 			builder.highlightTags(tags);
-		ALL.computeIfAbsent(id, _$ -> new ArrayList<>())
-			.add(entry);
+		synchronized (ALL) {
+			ALL.computeIfAbsent(id, _$ -> new ArrayList<>())
+				.add(entry);
+		}
 		return builder;
 	}
 
diff --git a/src/main/java/com/simibubi/create/foundation/ponder/SceneBuilder.java b/src/main/java/com/simibubi/create/foundation/ponder/SceneBuilder.java
index a18bc8c4a..63e7b06bf 100644
--- a/src/main/java/com/simibubi/create/foundation/ponder/SceneBuilder.java
+++ b/src/main/java/com/simibubi/create/foundation/ponder/SceneBuilder.java
@@ -165,7 +165,7 @@ public class SceneBuilder {
 	 * Use this in case you are not happy with the scale of the scene relative to
 	 * the overlay
 	 *
-	 * @param factor >1 will make the scene appear larger, smaller otherwise
+	 * @param factor {@literal >}1 will make the scene appear larger, smaller otherwise
 	 */
 	public void scaleSceneView(float factor) {
 		scene.scaleFactor = factor;
@@ -175,7 +175,7 @@ public class SceneBuilder {
 	 * Use this in case you are not happy with the vertical alignment of the scene
 	 * relative to the overlay
 	 *
-	 * @param yOffset >0 moves the scene up, down otherwise
+	 * @param yOffset {@literal >}0 moves the scene up, down otherwise
 	 */
 	public void setSceneOffsetY(float yOffset) {
 		scene.yOffset = yOffset;
diff --git a/src/main/java/com/simibubi/create/foundation/ponder/content/PonderChapterRegistry.java b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderChapterRegistry.java
index ec861ed1a..85d986fac 100644
--- a/src/main/java/com/simibubi/create/foundation/ponder/content/PonderChapterRegistry.java
+++ b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderChapterRegistry.java
@@ -22,11 +22,16 @@ public class PonderChapterRegistry {
 	}
 
 	public void addStoriesToChapter(@Nonnull PonderChapter chapter, PonderStoryBoardEntry... entries) {
-		chapters.get(chapter.getId()).getSecond().addAll(Arrays.asList(entries));
+		List<PonderStoryBoardEntry> entryList = chapters.get(chapter.getId()).getSecond();
+		synchronized (entryList) {
+			entryList.addAll(Arrays.asList(entries));
+		}
 	}
 
 	PonderChapter addChapter(@Nonnull PonderChapter chapter) {
-		chapters.put(chapter.getId(), Pair.of(chapter, new ArrayList<>()));
+		synchronized (chapters) {
+			chapters.put(chapter.getId(), Pair.of(chapter, new ArrayList<>()));
+		}
 		return chapter;
 	}
 
diff --git a/src/main/java/com/simibubi/create/foundation/ponder/content/PonderIndex.java b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderIndex.java
index 62241e7fc..9dc494031 100644
--- a/src/main/java/com/simibubi/create/foundation/ponder/content/PonderIndex.java
+++ b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderIndex.java
@@ -445,17 +445,17 @@ public class PonderIndex {
 			.add(Blocks.HONEY_BLOCK);
 
 		PonderRegistry.TAGS.forTag(PonderTag.CONTRAPTION_ACTOR)
-				.add(AllBlocks.MECHANICAL_HARVESTER)
-				.add(AllBlocks.MECHANICAL_PLOUGH)
-				.add(AllBlocks.MECHANICAL_DRILL)
-				.add(AllBlocks.MECHANICAL_SAW)
-				.add(AllBlocks.DEPLOYER)
-				.add(AllBlocks.PORTABLE_STORAGE_INTERFACE)
-				.add(AllBlocks.PORTABLE_FLUID_INTERFACE)
-				.add(AllBlocks.MECHANICAL_BEARING)
-				.add(AllBlocks.ANDESITE_FUNNEL)
-				.add(AllBlocks.BRASS_FUNNEL)
-				.add(AllBlocks.SEATS.get(DyeColor.WHITE))
+			.add(AllBlocks.MECHANICAL_HARVESTER)
+			.add(AllBlocks.MECHANICAL_PLOUGH)
+			.add(AllBlocks.MECHANICAL_DRILL)
+			.add(AllBlocks.MECHANICAL_SAW)
+			.add(AllBlocks.DEPLOYER)
+			.add(AllBlocks.PORTABLE_STORAGE_INTERFACE)
+			.add(AllBlocks.PORTABLE_FLUID_INTERFACE)
+			.add(AllBlocks.MECHANICAL_BEARING)
+			.add(AllBlocks.ANDESITE_FUNNEL)
+			.add(AllBlocks.BRASS_FUNNEL)
+			.add(AllBlocks.SEATS.get(DyeColor.WHITE))
 			.add(AllBlocks.REDSTONE_CONTACT)
 			.add(Blocks.BELL)
 			.add(Blocks.DISPENSER)
diff --git a/src/main/java/com/simibubi/create/foundation/ponder/content/PonderTagRegistry.java b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderTagRegistry.java
index f13fc6cdc..cdd5be6e6 100644
--- a/src/main/java/com/simibubi/create/foundation/ponder/content/PonderTagRegistry.java
+++ b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderTagRegistry.java
@@ -49,11 +49,15 @@ public class PonderTagRegistry {
 	}
 
 	public void add(PonderTag tag, ResourceLocation item) {
-		tags.put(item, tag);
+		synchronized (tags) {
+			tags.put(item, tag);
+		}
 	}
 
 	public void add(PonderTag tag, PonderChapter chapter) {
-		chapterTags.put(chapter, tag);
+		synchronized (chapterTags) {
+			chapterTags.put(chapter, tag);
+		}
 	}
 
 	public ItemBuilder forItems(ResourceLocation... items) {
diff --git a/src/main/java/com/simibubi/create/foundation/render/SuperByteBufferCache.java b/src/main/java/com/simibubi/create/foundation/render/SuperByteBufferCache.java
index 6b7131c3f..5027a0313 100644
--- a/src/main/java/com/simibubi/create/foundation/render/SuperByteBufferCache.java
+++ b/src/main/java/com/simibubi/create/foundation/render/SuperByteBufferCache.java
@@ -28,7 +28,7 @@ import net.minecraft.util.math.BlockPos;
 
 public class SuperByteBufferCache {
 
-	Map<Compartment<?>, Cache<Object, SuperByteBuffer>> cache;
+	private Map<Compartment<?>, Cache<Object, SuperByteBuffer>> cache;
 
 	public SuperByteBufferCache() {
 		cache = new HashMap<>();
@@ -87,14 +87,18 @@ public class SuperByteBufferCache {
 	}
 
 	public void registerCompartment(Compartment<?> instance) {
-		cache.put(instance, CacheBuilder.newBuilder()
-			.build());
+		synchronized (cache) {
+			cache.put(instance, CacheBuilder.newBuilder()
+				.build());
+		}
 	}
 
 	public void registerCompartment(Compartment<?> instance, long ticksUntilExpired) {
-		cache.put(instance, CacheBuilder.newBuilder()
-			.expireAfterAccess(ticksUntilExpired * 50, TimeUnit.MILLISECONDS)
-			.build());
+		synchronized (cache) {
+			cache.put(instance, CacheBuilder.newBuilder()
+				.expireAfterAccess(ticksUntilExpired * 50, TimeUnit.MILLISECONDS)
+				.build());
+		}
 	}
 
 	private SuperByteBuffer standardBlockRender(BlockState renderedState) {
diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml
index b3457e6a6..4f944f0a4 100644
--- a/src/main/resources/META-INF/mods.toml
+++ b/src/main/resources/META-INF/mods.toml
@@ -5,7 +5,7 @@ license="MIT"
 
 [[mods]]
 modId="create"
-version="v0.3.2c for 1.16.5"
+version="v0.3.2d"
 displayName="Create"
 #updateJSONURL=""
 displayURL="https://www.curseforge.com/minecraft/mc-mods/create"
@@ -32,6 +32,6 @@ Technology that empowers the player.'''
 [[dependencies.create]]
     modId="flywheel"
     mandatory=true
-    versionRange="[1.16-0.2,1.16-0.3)"
+    versionRange="[1.16-0.2.3,1.16-0.3)"
     ordering="AFTER"
     side="CLIENT"