Don't crash when old models are loaded (#443)

This commit is contained in:
ah-OOG-ah authored and GitHub committed 2026-08-16 17:58:48 +07:00
1 parent 075273b8b1
commit 2e2b469b40
3 files changed
+124 -5

No files matched your search

@@ -19,16 +19,20 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.IIcon;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.IItemRenderer;
import net.minecraftforge.common.util.ForgeDirection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector3f;
import org.lwjgl.opengl.GL11;
import com.github.bsideup.jabel.Desugar;
import com.gtnewhorizon.gtnhlib.api.BlockModelInfo;
import com.gtnewhorizon.gtnhlib.blockstate.registry.BlockPropertyRegistry;
import com.gtnewhorizon.gtnhlib.client.model.baked.BakedModel;
@@ -69,14 +73,13 @@ public class ModelISBRH implements ISimpleBlockRenderingHandler, IItemRenderer {
@Override
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId,
RenderBlocks renderer) {
// Setup for rendering
final Tessellator tesselator = TessellatorManager.get();
final var wrappedContext = new WorldWrapper(world, block, x, y, z);
worldContext.set(wrappedContext, x, y, z, RAND);
final int meta = wrappedContext.getBlockMetadata(x, y, z);
// Get the model!
final int meta = world.getBlockMetadata(x, y, z);
worldContext.set(world, x, y, z, RAND);
final BakedModel model = ModelRegistry.getBakedModel(worldContext);
int color = model.getColor(world, x, y, z, block, meta, RAND);
var rendered = false;
@@ -503,4 +506,65 @@ public class ModelISBRH implements ISimpleBlockRenderingHandler, IItemRenderer {
public @NotNull IIcon getMissingIcon() {
return Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite("missingno");
}
/// Some blocks, like LittleTiles, rely on recursively rendering other blocks in the same location. Unfortunately,
/// this means we have to actually respect the block passed into [ModelISBRH#renderWorldBlock]. In order to read a
/// blockstate with that, we wrap the original IBA with one that just returns a slightly different block.
@Desugar
private record WorldWrapper(IBlockAccess wrapped, Block replace, int x, int y, int z) implements IBlockAccess {
@Override
public Block getBlock(int x, int y, int z) {
if (x == this.x && y == this.y && z == this.z) return replace;
return wrapped.getBlock(x, y, z);
}
@Override
public TileEntity getTileEntity(int x, int y, int z) {
return wrapped.getTileEntity(x, y, z);
}
@Override
public int getLightBrightnessForSkyBlocks(int x, int y, int z, int defauld) {
return wrapped.getLightBrightnessForSkyBlocks(x, y, z, defauld);
}
@Override
public int getBlockMetadata(int x, int y, int z) {
return wrapped.getBlockMetadata(x, y, z);
}
/// WARNING: This may improperly ignore the replacing block!
@Override
public int isBlockProvidingPowerTo(int x, int y, int z, int directionIn) {
return wrapped.isBlockProvidingPowerTo(x, y, z, directionIn);
}
/// WARNING: This may improperly ignore the replacing block!
@Override
public boolean isAirBlock(int x, int y, int z) {
return wrapped.isAirBlock(x, y, z);
}
@Override
public BiomeGenBase getBiomeGenForCoords(int x, int z) {
return wrapped.getBiomeGenForCoords(x, y);
}
@Override
public int getHeight() {
return wrapped.getHeight();
}
@Override
public boolean extendedLevelsInChunkCache() {
return wrapped.extendedLevelsInChunkCache();
}
/// WARNING: may improperly ignore replaced block!
@Override
public boolean isSideSolid(int x, int y, int z, ForgeDirection side, boolean defauld) {
return wrapped.isSideSolid(x, y, z, side, defauld);
}
}
}
@@ -8,17 +8,27 @@ import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.AbstractResourcePack;
import net.minecraft.client.resources.FileResourcePack;
import net.minecraft.client.resources.data.IMetadataSection;
import net.minecraft.client.resources.data.PackMetadataSection;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import com.gtnewhorizon.gtnhlib.client.model.loading.ModelResourcePack;
import com.gtnewhorizon.gtnhlib.client.model.loading.RPInfo;
import com.gtnewhorizon.gtnhlib.client.model.unbaked.JSONModel;
import com.llamalad7.mixinextras.expression.Definition;
import com.llamalad7.mixinextras.expression.Expression;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.sugar.Local;
import it.unimi.dsi.fastutil.objects.ObjectLists;
@@ -70,4 +80,43 @@ public abstract class MixinFileResourcePack extends AbstractResourcePack impleme
return new RPInfo(textures, models);
}
@Unique
private static final int PACK_FORMAT_MC_13_X = 4;
@Unique
private static final String METADATA_FILE = "pack.mcmeta";
/// Block old resourcepacks from loading blockstates or models.
/// @return True if the resource SHOULDN'T load, false if it SHOULD.
@Definition(id = "zipentry", local = @Local(type = ZipEntry.class))
@Expression("zipentry == null")
@ModifyExpressionValue(method = "getInputStreamByName", at = @At(value = "MIXINEXTRAS:EXPRESSION"))
private boolean nhlib$blockOldModels(boolean original, @Local(argsOnly = true) String resource) {
// Return the default for the metadata, or getting it below will infinitely recurse.
if (original || METADATA_FILE.equals(resource)) return original;
// This song and dance is needed because some calls to getInputStreamByName expect it to never fail,
// since they're referring to resources bundled in jars. So while the original has a checked exception, we can't
// rely on everyone checking it.
final int packFormat;
IMetadataSection packFormatSection = null;
try {
packFormatSection = getPackMetadata(
Minecraft.getMinecraft().getResourcePackRepository().rprMetadataSerializer,
"pack");
} catch (IOException e) {
// If the above fails, pack metadata is null and thus format is assumed to be 1 (i.e. old).
}
if (!(packFormatSection instanceof PackMetadataSection metadata)) packFormat = 1;
else packFormat = metadata.getPackFormat();
if (!resource.endsWith(".json")) return false; // not a JSON model/blockstate
var pathParts = resource.split("/");
if (pathParts.length < 4) return false; // too short to be a blockstate or model
if (!"assets".equals(pathParts[0])) return false;
if (!"blockstates".equals(pathParts[2]) && !"models".equals(pathParts[2])) return false;
// This is a blockstate or model, reject it if it's too old.
return packFormat < PACK_FORMAT_MC_13_X;
}
}
+6
View File
@@ -0,0 +1,6 @@
{
"pack": {
"description": "GTNHLib resources",
"pack_format": 64
}
}