Skip to content

Commit dce5013

Browse files
Port FIX_BLOCK_CRACK from FalseTweaks (#999)
1 parent 6eef636 commit dce5013

File tree

3 files changed

+256
-1
lines changed

3 files changed

+256
-1
lines changed

src/main/java/com/gtnewhorizons/angelica/config/AngelicaConfig.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,4 +166,26 @@ public class AngelicaConfig {
166166
@Config.Comment("Allows unicode languages to use an odd gui scale")
167167
@Config.DefaultBoolean(true)
168168
public static boolean removeUnicodeEvenScaling;
169+
170+
@Config.Comment({
171+
"Block corners and edges between chunks might have \"cracks\" in them. This option fixes it"
172+
})
173+
@Config.DefaultBoolean(true)
174+
public static boolean fixBlockCrack;
175+
176+
@Config.Comment({
177+
"The \"epsilon\" value for the block crack fix inside chunks. Set this a bit higher if you can",
178+
"still see light leaking between solid blocks in dark areas"
179+
})
180+
@Config.RangeDouble(min = 0,
181+
max = 0.005)
182+
@Config.DefaultDouble(0.001)
183+
public static double blockCrackFixEpsilon;
184+
185+
@Config.Comment("Block classes that have bugs when rendering with the crack fix can be put here to avoid manipulating them")
186+
@Config.DefaultStringList({
187+
"net.minecraft.block.BlockCauldron",
188+
"net.minecraft.block.BlockStairs"
189+
})
190+
public static String[] blockCrackFixBlacklist;
169191
}

src/main/java/com/gtnewhorizons/angelica/mixins/Mixins.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,13 @@ public enum Mixins implements IMixins {
7676
, "angelica.dynamiclights.MixinItemRenderer"
7777
)
7878
),
79-
79+
80+
ANGELICA_FIX_BLOCK_CRACK(
81+
new MixinBuilder("Block corners and edges between chunks might have \"cracks\" in them. This option fixes it")
82+
.setPhase(Phase.EARLY)
83+
.addClientMixins("angelica.bugfixes.MixinRenderBlocks_CrackFix")
84+
.setApplyIf(() -> AngelicaConfig.fixBlockCrack)),
85+
8086
ANGELICA_FIX_FLUID_RENDERER_CHECKING_BLOCK_AGAIN(
8187
new MixinBuilder("Fix RenderBlockFluid reading the block type from the world access multiple times")
8288
.setPhase(Phase.EARLY)
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
/*
2+
* This file is part of FalseTweaks.
3+
*
4+
* Copyright (C) 2022-2025 FalsePattern
5+
* All Rights Reserved
6+
*
7+
* Modifications by Angelica in accordance with LGPL v3.0
8+
*
9+
* The above copyright notice and this permission notice shall be included
10+
* in all copies or substantial portions of the Software.
11+
*
12+
* FalseTweaks is free software: you can redistribute it and/or modify
13+
* it under the terms of the GNU Lesser General Public License as published by
14+
* the Free Software Foundation, only version 3 of the License.
15+
*
16+
* FalseTweaks is distributed in the hope that it will be useful,
17+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
* GNU Lesser General Public License for more details.
20+
*
21+
* You should have received a copy of the GNU Lesser General Public License
22+
* along with FalseTweaks. If not, see <https://www.gnu.org/licenses/>.
23+
*/
24+
25+
package com.gtnewhorizons.angelica.mixins.early.angelica.bugfixes;
26+
27+
import com.gtnewhorizons.angelica.config.AngelicaConfig;
28+
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
29+
import me.jellysquid.mods.sodium.client.render.chunk.ChunkRenderManager;
30+
import net.minecraft.block.Block;
31+
import net.minecraft.client.renderer.RenderBlocks;
32+
import net.minecraft.util.IIcon;
33+
import net.minecraftforge.client.ForgeHooksClient;
34+
import net.minecraftforge.client.MinecraftForgeClient;
35+
import net.minecraftforge.common.util.ForgeDirection;
36+
import org.spongepowered.asm.mixin.Mixin;
37+
import org.spongepowered.asm.mixin.Shadow;
38+
import org.spongepowered.asm.mixin.Unique;
39+
import org.spongepowered.asm.mixin.injection.At;
40+
import org.spongepowered.asm.mixin.injection.Inject;
41+
import org.spongepowered.asm.mixin.injection.Redirect;
42+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
43+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
44+
45+
import java.util.Arrays;
46+
import java.util.Objects;
47+
@Mixin(RenderBlocks.class)
48+
public class MixinRenderBlocks_CrackFix {
49+
@Unique
50+
private static String[] angelica$currentCrackFixBlacklistArr;
51+
@Unique
52+
private static Class<?>[] angelica$currentCrackFixBlacklistClasses;
53+
@Shadow
54+
public double renderMinX;
55+
@Shadow
56+
public double renderMinY;
57+
@Shadow
58+
public double renderMinZ;
59+
@Shadow
60+
public double renderMaxX;
61+
@Shadow
62+
public double renderMaxY;
63+
@Shadow
64+
public double renderMaxZ;
65+
@Unique
66+
private double[] angelica$bounds;
67+
@Unique
68+
private boolean angelica$disableCrackFix;
69+
70+
@Redirect(method = "renderFaceXNeg",
71+
at = @At(value = "FIELD",
72+
target = "Lnet/minecraft/client/renderer/RenderBlocks;renderMinX:D",
73+
ordinal = 0),
74+
require = 1)
75+
private double xNegBounds(RenderBlocks instance) {
76+
angelica$preBounds(ForgeDirection.WEST);
77+
return instance.renderMinX;
78+
}
79+
80+
@Redirect(method = "renderFaceXPos",
81+
at = @At(value = "FIELD",
82+
target = "Lnet/minecraft/client/renderer/RenderBlocks;renderMaxX:D",
83+
ordinal = 0),
84+
require = 1)
85+
private double xPosBounds(RenderBlocks instance) {
86+
angelica$preBounds(ForgeDirection.EAST);
87+
return instance.renderMaxX;
88+
}
89+
@SuppressWarnings("SuspiciousNameCombination")
90+
@Redirect(method = "renderFaceYNeg",
91+
at = @At(value = "FIELD",
92+
target = "Lnet/minecraft/client/renderer/RenderBlocks;renderMinX:D",
93+
ordinal = 5),
94+
require = 1)
95+
private double yNegBounds(RenderBlocks instance) {
96+
angelica$preBounds(ForgeDirection.DOWN);
97+
return instance.renderMinX;
98+
}
99+
@SuppressWarnings("SuspiciousNameCombination")
100+
@Redirect(method = "renderFaceYPos",
101+
at = @At(value = "FIELD",
102+
target = "Lnet/minecraft/client/renderer/RenderBlocks;renderMinX:D",
103+
ordinal = 5),
104+
require = 1)
105+
private double yPosBounds(RenderBlocks instance) {
106+
angelica$preBounds(ForgeDirection.UP);
107+
return instance.renderMinX;
108+
}
109+
110+
@Redirect(method = "renderFaceZNeg",
111+
at = @At(value = "FIELD",
112+
target = "Lnet/minecraft/client/renderer/RenderBlocks;renderMinX:D",
113+
ordinal = 6),
114+
require = 1)
115+
private double zNegBounds(RenderBlocks instance) {
116+
angelica$preBounds(ForgeDirection.NORTH);
117+
return instance.renderMinX;
118+
}
119+
@Redirect(method = "renderFaceZPos",
120+
at = @At(value = "FIELD",
121+
target = "Lnet/minecraft/client/renderer/RenderBlocks;renderMinX:D",
122+
ordinal = 5),
123+
require = 1)
124+
private double zPosBounds(RenderBlocks instance) {
125+
angelica$preBounds(ForgeDirection.SOUTH);
126+
return instance.renderMinX;
127+
}
128+
@Unique
129+
private void angelica$preBounds(ForgeDirection skipDir) {
130+
if (angelica$crackFixOff()) {
131+
return;
132+
}
133+
if (angelica$bounds == null) {
134+
angelica$bounds = new double[6];
135+
}
136+
angelica$bounds[0] = renderMinX;
137+
angelica$bounds[1] = renderMinY;
138+
angelica$bounds[2] = renderMinZ;
139+
angelica$bounds[3] = renderMaxX;
140+
angelica$bounds[4] = renderMaxY;
141+
angelica$bounds[5] = renderMaxZ;
142+
143+
if (ForgeHooksClient.getWorldRenderPass() != 0) {
144+
return;
145+
}
146+
147+
if (renderMinX != 0 || renderMinY != 0 || renderMinZ != 0 || renderMaxX != 1 || renderMaxY != 1 || renderMaxZ != 1) {
148+
return;
149+
}
150+
double EPSILON = AngelicaConfig.blockCrackFixEpsilon;
151+
renderMinX -= EPSILON;
152+
renderMinY -= EPSILON;
153+
renderMinZ -= EPSILON;
154+
renderMaxX += EPSILON;
155+
renderMaxY += EPSILON;
156+
renderMaxZ += EPSILON;
157+
switch (skipDir) {
158+
case WEST: renderMinX = angelica$bounds[0]; break;
159+
case DOWN: renderMinY = angelica$bounds[1]; break;
160+
case NORTH: renderMinZ = angelica$bounds[2]; break;
161+
case EAST: renderMaxX = angelica$bounds[3]; break;
162+
case UP: renderMaxY = angelica$bounds[4]; break;
163+
case SOUTH: renderMaxZ = angelica$bounds[5]; break;
164+
}
165+
}
166+
@Inject(method = {"renderFaceXNeg", "renderFaceXPos", "renderFaceYNeg", "renderFaceYPos", "renderFaceZNeg", "renderFaceZPos"},
167+
at = @At(value = "RETURN"),
168+
require = 6)
169+
private void postBounds(Block p_147798_1_, double p_147798_2_, double p_147798_4_, double p_147798_6_, IIcon p_147798_8_, CallbackInfo ci) {
170+
if (angelica$crackFixOff() || angelica$bounds == null) {
171+
return;
172+
}
173+
renderMinX = angelica$bounds[0];
174+
renderMinY = angelica$bounds[1];
175+
renderMinZ = angelica$bounds[2];
176+
renderMaxX = angelica$bounds[3];
177+
renderMaxY = angelica$bounds[4];
178+
renderMaxZ = angelica$bounds[5];
179+
}
180+
@Inject(method = "renderBlockByRenderType",
181+
at = @At("HEAD"),
182+
require = 1)
183+
private void exclusion(Block block, int x, int y, int z, CallbackInfoReturnable<Boolean> cir) {
184+
angelica$disableCrackFix = angelica$isBlacklisted(block.getClass());
185+
}
186+
187+
@Inject(method = "renderBlockByRenderType",
188+
at = @At("RETURN"),
189+
require = 1)
190+
private void endExclusion(Block p_147805_1_, int p_147805_2_, int p_147805_3_, int p_147805_4_, CallbackInfoReturnable<Boolean> cir) {
191+
angelica$disableCrackFix = false;
192+
}
193+
194+
@Unique
195+
private static boolean angelica$isBlacklisted(Class<?> clazz) {
196+
Class<?>[] blacklist = angelica$getCrackFixBlacklist();
197+
if (blacklist == null) {
198+
return false;
199+
}
200+
for (Class<?> element : blacklist) {
201+
if (element.isAssignableFrom(clazz)) {
202+
return true;
203+
}
204+
}
205+
return false;
206+
}
207+
208+
@Unique
209+
private static Class<?>[] angelica$getCrackFixBlacklist() {
210+
if (angelica$currentCrackFixBlacklistArr != AngelicaConfig.blockCrackFixBlacklist) {
211+
angelica$currentCrackFixBlacklistArr = AngelicaConfig.blockCrackFixBlacklist;
212+
angelica$currentCrackFixBlacklistClasses = Arrays.stream(angelica$currentCrackFixBlacklistArr).map((name) -> {
213+
try {
214+
return Class.forName(name);
215+
} catch (ClassNotFoundException e) {
216+
AngelicaTweaker.LOGGER.info("Could not find class " + name + " for crack fix blacklist!");
217+
return null;
218+
}
219+
}).filter(Objects::nonNull).toArray(Class<?>[]::new);
220+
}
221+
return angelica$currentCrackFixBlacklistClasses;
222+
}
223+
@Unique
224+
private boolean angelica$crackFixOff() {
225+
return !AngelicaConfig.fixBlockCrack || angelica$disableCrackFix;
226+
}
227+
}

0 commit comments

Comments
 (0)