Skip to content

Commit 5bc8d99

Browse files
committed
fix(compat): render ArchitectureCraft blocks via the vanilla dispatcher
ArchitectureCraft (TridenMC/Spocel, issue #101) replaces Minecraft.blockRenderDispatcher with CustomBlockDispatcher during postInit and emits its shape geometry through that entry point; its baked-model slots stay empty (blockstates only publish a normal variant, so facing=* states resolve to Forge's FancyMissingModel). The fast chunk path reads BlockModelShapes.getModelForState directly, hits the missing model and skipped the block, so every ArchitectureCraft block was invisible (0.0.6 crashed on the fancy missing model's lazy font rendering before #90 made it a skip). Force blocks registered under the architecturecraft namespace down the existing vanilla dispatcher fallback (the SnowRealMagicCompat route); the replaced dispatcher then supplies the real geometry. Its RenderTargetWorld target writes into a BufferBuilder without touching GL, so it stays safe on mesh worker threads. The routing decision is extracted into ArchitectureCraftRenderRouting so it can be tested without an FML bootstrap, and the compat-bridge renderer is untouched, keeping the third-party mixin binding contract intact.
1 parent 0e246ee commit 5bc8d99

5 files changed

Lines changed: 105 additions & 2 deletions

File tree

gradle/scripts/dependencies.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,4 +145,8 @@ dependencies {
145145
// Botania CEU (issue: held items render flat white because its RenderWorldLastEvent handlers leave
146146
// GL_TEXTURE_2D disabled in the GLSM cache; the conditional botania mixin restores it)
147147
modImplementation 'curse.maven:botania-ceu-1179448:8706250'
148+
149+
// ArchitectureCraft 1.12-3.108 TridenMC (issue #101: its blocks are invisible / sawbench hits
150+
// FancyMissingModel inside the fast chunk meshing path; dev-verifies the shape rendering compat)
151+
modImplementation 'curse.maven:architecturecraft-tridev-277631:4344128'
148152
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.dhj.actinium.compat.architecturecraft;
2+
3+
import net.minecraft.block.Block;
4+
import net.minecraft.util.ResourceLocation;
5+
import net.minecraftforge.fml.common.Loader;
6+
7+
/**
8+
* Compatibility for ArchitectureCraft (mod id {@code architecturecraft}; the TridentMC and Spocel
9+
* releases share it).
10+
*
11+
* <p>ArchitectureCraft never relies on baked models for its world rendering. Its blockstates only
12+
* publish a {@code normal} variant (the runtime definition comes from its generated resource pack),
13+
* so every {@code facing=*} state resolves to Forge's {@code FancyMissingModel} in the bake
14+
* registry, and the real geometry is emitted by a {@code BlockRendererDispatcher} replacement that
15+
* {@code CustomBlockDispatcher.inject()} installs into {@code Minecraft.blockRenderDispatcher}
16+
* during postInit. The vanilla chunk rebuild calls that entry point and the blocks stay visible;
17+
* Actinium's fast chunk path reads {@code BlockModelShapes.getModelForState} directly, hits the
18+
* missing model and skips the block (#101). The dispatcher replacement must therefore not be
19+
* bypassed: the compat forces ArchitectureCraft blocks down the vanilla dispatcher fallback,
20+
* whose {@code RenderTargetWorld} target writes into a {@code BufferBuilder} without touching GL,
21+
* which keeps it safe on mesh worker threads.</p>
22+
*/
23+
public final class ArchitectureCraftCompat {
24+
/**
25+
* The mod ID used by both maintained ArchitectureCraft releases.
26+
*/
27+
public static final String MODID = "architecturecraft";
28+
public static final boolean IS_LOADED = Loader.isModLoaded(MODID);
29+
30+
private ArchitectureCraftCompat() {
31+
}
32+
33+
/**
34+
* Returns whether {@code block} belongs to ArchitectureCraft and must be rendered through the
35+
* vanilla {@code BlockRendererDispatcher}, which ArchitectureCraft replaces with its own
36+
* dispatcher providing the real shape geometry.
37+
*/
38+
public static boolean shouldForceVanillaRender(Block block) {
39+
if (!IS_LOADED) {
40+
return false;
41+
}
42+
return ArchitectureCraftRenderRouting.isArchitectureCraftNamespace(block.getRegistryName());
43+
}
44+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.dhj.actinium.compat.architecturecraft;
2+
3+
import net.minecraft.util.ResourceLocation;
4+
5+
import javax.annotation.Nullable;
6+
7+
/**
8+
* Render-path routing decisions for ArchitectureCraft blocks, kept free of live-client
9+
* dependencies so they can be exercised without an FML bootstrap.
10+
*/
11+
public final class ArchitectureCraftRenderRouting {
12+
private ArchitectureCraftRenderRouting() {
13+
}
14+
15+
/**
16+
* Returns whether {@code registryName} belongs to the ArchitectureCraft namespace and
17+
* therefore must stay on the vanilla dispatcher path. Identified by the registry namespace,
18+
* which covers every block of both maintained releases without referencing any
19+
* ArchitectureCraft class.
20+
*/
21+
public static boolean isArchitectureCraftNamespace(@Nullable ResourceLocation registryName) {
22+
return registryName != null && ArchitectureCraftCompat.MODID.equals(registryName.getNamespace());
23+
}
24+
}

src/main/java/com/dhj/actinium/render/terrain/compile/task/ChunkBuilderMeshingTask.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.embeddedt.embeddium.api.shader.BlockRenderLayer;
3434
import org.embeddedt.embeddium.api.shader.ShaderProvider;
3535
import org.embeddedt.embeddium.api.shader.ShaderProviderHolder;
36+
import com.dhj.actinium.compat.architecturecraft.ArchitectureCraftCompat;
3637
import com.dhj.actinium.compat.fluidlogged.FluidloggedCompat;
3738
import com.dhj.actinium.compat.snowrealmagic.SnowRealMagicCompat;
3839
import com.dhj.actinium.runtime.ActiniumRuntime;
@@ -124,7 +125,9 @@ public ChunkBuildOutput execute(ChunkBuildContext context, CancellationToken can
124125
net.minecraft.util.BlockRenderLayer layer = shaderLayerOverride.toVanillaLayer();
125126
ForgeHooksClient.setRenderLayer(layer);
126127
block.canRenderInLayer(blockState, layer);
127-
if (blockState.getRenderType() == EnumBlockRenderType.MODEL && ActiniumRuntime.options().performance.useFastBlockRenderer && !SnowRealMagicCompat.shouldForceVanillaRender(block)) {
128+
if (blockState.getRenderType() == EnumBlockRenderType.MODEL && ActiniumRuntime.options().performance.useFastBlockRenderer
129+
&& !SnowRealMagicCompat.shouldForceVanillaRender(block)
130+
&& !ArchitectureCraftCompat.shouldForceVanillaRender(block)) {
128131
buildContext.getBlockRenderer().renderBlock(blockState, blockPos, slice, layer, false);
129132
} else {
130133
var buffer = buildContext.getBufferForLayer(layer);
@@ -139,7 +142,9 @@ public ChunkBuildOutput execute(ChunkBuildContext context, CancellationToken can
139142
for (net.minecraft.util.BlockRenderLayer layer : VintageChunkBuildContext.LAYERS) {
140143
if (block.canRenderInLayer(blockState, layer)) {
141144
ForgeHooksClient.setRenderLayer(layer);
142-
if (blockState.getRenderType() == EnumBlockRenderType.MODEL && ActiniumRuntime.options().performance.useFastBlockRenderer && !SnowRealMagicCompat.shouldForceVanillaRender(block)) {
145+
if (blockState.getRenderType() == EnumBlockRenderType.MODEL && ActiniumRuntime.options().performance.useFastBlockRenderer
146+
&& !SnowRealMagicCompat.shouldForceVanillaRender(block)
147+
&& !ArchitectureCraftCompat.shouldForceVanillaRender(block)) {
143148
buildContext.getBlockRenderer().renderBlock(blockState, blockPos, slice, layer);
144149
} else {
145150
var buffer = buildContext.getBufferForLayer(layer);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.dhj.actinium.compat.architecturecraft;
2+
3+
import net.minecraft.util.ResourceLocation;
4+
import org.junit.jupiter.api.Test;
5+
6+
import static org.junit.jupiter.api.Assertions.assertFalse;
7+
import static org.junit.jupiter.api.Assertions.assertTrue;
8+
9+
class ArchitectureCraftRenderRoutingTest {
10+
@Test
11+
void architectureCraftBlocksAreForcedOntoTheVanillaDispatcher() {
12+
assertTrue(ArchitectureCraftRenderRouting.isArchitectureCraftNamespace(
13+
new ResourceLocation("architecturecraft", "sawbench")));
14+
assertTrue(ArchitectureCraftRenderRouting.isArchitectureCraftNamespace(
15+
new ResourceLocation("architecturecraft", "shape")));
16+
}
17+
18+
@Test
19+
void foreignAndMissingRegistryNamesStayOnTheFastPath() {
20+
assertFalse(ArchitectureCraftRenderRouting.isArchitectureCraftNamespace(
21+
new ResourceLocation("minecraft", "stone")));
22+
assertFalse(ArchitectureCraftRenderRouting.isArchitectureCraftNamespace(
23+
new ResourceLocation("chisel", "marble")));
24+
assertFalse(ArchitectureCraftRenderRouting.isArchitectureCraftNamespace(null));
25+
}
26+
}

0 commit comments

Comments
 (0)