Merge remote-tracking branch 'origin/main' into 1.18

This commit is contained in:
embeddedt 2023-04-10 21:50:58 -04:00
commit 2a327ef4c9
No known key found for this signature in database
GPG Key ID: A69433EC199B5613

View File

@ -6,10 +6,7 @@ import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalNotification; import com.google.common.cache.RemovalNotification;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.gson.JsonElement; import com.google.gson.*;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.util.Pair;
import com.mojang.math.Transformation; import com.mojang.math.Transformation;
import net.minecraft.Util; import net.minecraft.Util;
@ -25,6 +22,7 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.Resource; import net.minecraft.server.packs.resources.Resource;
import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.StateDefinition;
@ -33,6 +31,8 @@ import net.minecraftforge.client.model.ForgeModelBakery;
import net.minecraftforge.client.model.ModelLoaderRegistry; import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.ModLoader; import net.minecraftforge.fml.ModLoader;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.ForgeRegistryEntry;
import org.apache.commons.lang3.tuple.Triple; import org.apache.commons.lang3.tuple.Triple;
import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider; import org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider;
@ -56,6 +56,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
@Mixin(ModelBakery.class) @Mixin(ModelBakery.class)
public abstract class ModelBakeryMixin { public abstract class ModelBakeryMixin {
@ -88,6 +89,7 @@ public abstract class ModelBakeryMixin {
@Shadow @Final public static BlockModel BLOCK_ENTITY_MARKER; @Shadow @Final public static BlockModel BLOCK_ENTITY_MARKER;
@Shadow @Final private static Logger LOGGER; @Shadow @Final private static Logger LOGGER;
private Cache<Triple<ResourceLocation, Transformation, Boolean>, BakedModel> loadedBakedModels; private Cache<Triple<ResourceLocation, Transformation, Boolean>, BakedModel> loadedBakedModels;
private Cache<ResourceLocation, UnbakedModel> loadedModels; private Cache<ResourceLocation, UnbakedModel> loadedModels;
@ -134,78 +136,145 @@ public abstract class ModelBakeryMixin {
private UnbakedModel missingModel; private UnbakedModel missingModel;
/** private Set<ResourceLocation> blockStateFiles;
* @author embeddedt private Set<ResourceLocation> modelFiles;
* @reason don't load any models initially, just set up initial data structures
*/ @Redirect(method = "processLoading", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelBakery;loadBlockModel(Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/renderer/block/model/BlockModel;", ordinal = 0))
@Overwrite(remap = false) private BlockModel captureMissingModel(ModelBakery bakery, ResourceLocation location) throws IOException {
protected void processLoading(ProfilerFiller arg, int maxMipLevels) { this.missingModel = this.loadBlockModel(location);
ModelLoaderRegistry.onModelLoadingStart(); this.blockStateFiles = new HashSet<>();
try { this.modelFiles = new HashSet<>();
this.missingModel = this.loadBlockModel(MISSING_MODEL_LOCATION); return (BlockModel)this.missingModel;
} catch (IOException var10) {
ModernFix.LOGGER.error("Error loading missing model, should never happen :(", var10);
throw new RuntimeException(var10);
}
// Gather model materials
Set<Material> initialMaterials = new HashSet<>(UNREFERENCED_TEXTURES);
gatherModelMaterials(initialMaterials);
ForgeHooksClient.gatherFluidTextures(initialMaterials);
Map<ResourceLocation, List<Material>> map = initialMaterials.stream().collect(Collectors.groupingBy(Material::atlasLocation));
this.atlasPreparations = Maps.newHashMap();
for(Map.Entry<ResourceLocation, List<Material>> entry : map.entrySet()) {
TextureAtlas atlas = new TextureAtlas(entry.getKey());
TextureAtlas.Preparations atlastexture$sheetdata = atlas.prepareToStitch(this.resourceManager, entry.getValue().stream().map(Material::texture), arg, maxMipLevels);
this.atlasPreparations.put(entry.getKey(), Pair.of(atlas, atlastexture$sheetdata));
}
} }
/** /**
* Scan the models folder and try to load, parse, and get materials from as many models as possible. * @author embeddedt
* @reason don't actually load the model. instead, keep track of if we need to load a blockstate or a model,
* and save the info into the two lists
*/
@Overwrite
private void loadTopLevel(ModelResourceLocation location) {
if(Objects.equals(location.getVariant(), "inventory")) {
modelFiles.add(new ResourceLocation(location.getNamespace(), "item/" + location.getPath()));
} else {
blockStateFiles.add(new ResourceLocation(location.getNamespace(), location.getPath()));
}
}
@Redirect(method = "processLoading", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/client/ForgeHooksClient;gatherFluidTextures(Ljava/util/Set;)V", remap = false), remap = false)
private void gatherModelTextures(Set<Material> materialSet) {
ForgeHooksClient.gatherFluidTextures(materialSet);
gatherModelMaterials(materialSet);
}
/**
* Load all blockstate JSONs and model files, collect textures.
*/ */
private void gatherModelMaterials(Set<Material> materialSet) { private void gatherModelMaterials(Set<Material> materialSet) {
Stopwatch stopwatch = Stopwatch.createStarted(); Stopwatch stopwatch = Stopwatch.createStarted();
List<ResourceLocation> allModels = new ArrayList<>(this.resourceManager.listResources("models", path -> path.endsWith(".json"))); List<CompletableFuture<Pair<ResourceLocation, JsonElement>>> blockStateData = new ArrayList<>();
// for KubeJS, etc. for(ResourceLocation blockstate : blockStateFiles) {
allModels.addAll(ResourcePackHandler.getExtraResources(this.resourceManager, "models", path -> path.endsWith(".json"))); blockStateData.add(CompletableFuture.supplyAsync(() -> {
List<CompletableFuture<Pair<ResourceLocation, JsonElement>>> modelBytes = new ArrayList<>(); ResourceLocation fileLocation = new ResourceLocation(blockstate.getNamespace(), "blockstates/" + blockstate.getPath() + ".json");
for(ResourceLocation fileLocation : allModels) {
modelBytes.add(CompletableFuture.supplyAsync(() -> {
try(Resource resource = this.resourceManager.getResource(fileLocation)) { try(Resource resource = this.resourceManager.getResource(fileLocation)) {
JsonParser parser = new JsonParser(); JsonParser parser = new JsonParser();
// strip models/ and .json from the name return Pair.of(blockstate, parser.parse(new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8)));
ResourceLocation model = new ResourceLocation(fileLocation.getNamespace(), fileLocation.getPath().substring(7, fileLocation.getPath().length()-5));
return Pair.of(model, parser.parse(new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8)));
} catch(IOException | JsonParseException e) { } catch(IOException | JsonParseException e) {
ModernFix.LOGGER.error("Error reading model {}: {}", fileLocation, e); ModernFix.LOGGER.error("Error reading blockstate {}: {}", blockstate, e);
return Pair.of(fileLocation, null);
} }
return Pair.of(blockstate, null);
}, Util.backgroundExecutor())); }, Util.backgroundExecutor()));
} }
allModels.clear(); blockStateFiles = null;
CompletableFuture.allOf(modelBytes.toArray(new CompletableFuture[0])).join(); CompletableFuture.allOf(blockStateData.toArray(new CompletableFuture[0])).join();
Set<Pair<String, String>> errorSet = Sets.newLinkedHashSet(); for(CompletableFuture<Pair<ResourceLocation, JsonElement>> result : blockStateData) {
Map<ResourceLocation, BlockModel> basicModels = new HashMap<>(); Pair<ResourceLocation, JsonElement> pair = result.join();
try { if(pair.getSecond() != null) {
basicModels.put(MISSING_MODEL_LOCATION, this.loadBlockModel(MISSING_MODEL_LOCATION)); try {
basicModels.put(new ResourceLocation("builtin/generated"), GENERATION_MARKER); JsonObject obj = pair.getSecond().getAsJsonObject();
basicModels.put(new ResourceLocation("builtin/entity"), BLOCK_ENTITY_MARKER); if(obj.has("variants")) {
} catch(IOException e) { JsonObject eachVariant = obj.getAsJsonObject("variants");
throw new RuntimeException("Exception when populating built-in models", e); for(Map.Entry<String, JsonElement> entry : eachVariant.entrySet()) {
} JsonElement variantData = entry.getValue();
for(CompletableFuture<Pair<ResourceLocation, JsonElement>> future : modelBytes) { List<JsonObject> variantModels;
Pair<ResourceLocation, JsonElement> pair = future.join(); if(variantData.isJsonArray()) {
try { variantModels = new ArrayList<>();
if(pair.getSecond() != null) { for(JsonElement model : variantData.getAsJsonArray()) {
BlockModel model = ModelLoaderRegistry.ExpandedBlockModelDeserializer.INSTANCE.fromJson(pair.getSecond(), BlockModel.class); variantModels.add(model.getAsJsonObject());
model.name = pair.getFirst().toString(); }
basicModels.put(pair.getFirst(), model); } else
variantModels = Collections.singletonList(variantData.getAsJsonObject());
for(JsonObject variant : variantModels) {
modelFiles.add(new ResourceLocation(variant.get("model").getAsString()));
}
}
} else {
JsonArray multipartData = obj.get("multipart").getAsJsonArray();
for(JsonElement element : multipartData) {
JsonObject self = element.getAsJsonObject();
JsonElement apply = self.get("apply");
List<JsonObject> applyObjects;
if(apply.isJsonArray()) {
applyObjects = new ArrayList<>();
for(JsonElement e : apply.getAsJsonArray()) {
applyObjects.add(e.getAsJsonObject());
}
} else
applyObjects = Collections.singletonList(apply.getAsJsonObject());
for(JsonObject applyEntry : applyObjects) {
modelFiles.add(new ResourceLocation(applyEntry.get("model").getAsString()));
}
}
}
} catch(RuntimeException e) {
ModernFix.LOGGER.error("Error with blockstate {}: {}", pair.getFirst(), e);
} }
} catch(Throwable e) {
ModernFix.LOGGER.warn("Unable to parse {}: {}", pair.getFirst(), e);
} }
} }
modelBytes.clear(); blockStateData = null;
Map<ResourceLocation, BlockModel> basicModels = new HashMap<>();
basicModels.put(MISSING_MODEL_LOCATION, (BlockModel)missingModel);
basicModels.put(new ResourceLocation("builtin/generated"), GENERATION_MARKER);
basicModels.put(new ResourceLocation("builtin/entity"), BLOCK_ENTITY_MARKER);
Set<Pair<String, String>> errorSet = Sets.newLinkedHashSet();
while(modelFiles.size() > 0) {
List<CompletableFuture<Pair<ResourceLocation, JsonElement>>> modelBytes = new ArrayList<>();
for(ResourceLocation model : modelFiles) {
if(basicModels.containsKey(model))
continue;
ResourceLocation fileLocation = new ResourceLocation(model.getNamespace(), "models/" + model.getPath() + ".json");
modelBytes.add(CompletableFuture.supplyAsync(() -> {
try(Resource resource = this.resourceManager.getResource(fileLocation)) {
JsonParser parser = new JsonParser();
return Pair.of(model, parser.parse(new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8)));
} catch(IOException | JsonParseException e) {
ModernFix.LOGGER.error("Error reading model {}: {}", fileLocation, e);
return Pair.of(fileLocation, null);
}
}, Util.backgroundExecutor()));
}
modelFiles.clear();
CompletableFuture.allOf(modelBytes.toArray(new CompletableFuture[0])).join();
for(CompletableFuture<Pair<ResourceLocation, JsonElement>> future : modelBytes) {
Pair<ResourceLocation, JsonElement> pair = future.join();
try {
if(pair.getSecond() != null) {
BlockModel model = ModelLoaderRegistry.ExpandedBlockModelDeserializer.INSTANCE.fromJson(pair.getSecond(), BlockModel.class);
model.name = pair.getFirst().toString();
modelFiles.addAll(model.getDependencies());
basicModels.put(pair.getFirst(), model);
continue;
}
} catch(Throwable e) {
ModernFix.LOGGER.warn("Unable to parse {}: {}", pair.getFirst(), e);
}
basicModels.put(pair.getFirst(), (BlockModel)missingModel);
}
}
modelFiles = null;
Function<ResourceLocation, UnbakedModel> modelGetter = loc -> basicModels.getOrDefault(loc, (BlockModel)this.missingModel); Function<ResourceLocation, UnbakedModel> modelGetter = loc -> basicModels.getOrDefault(loc, (BlockModel)this.missingModel);
for(BlockModel model : basicModels.values()) { for(BlockModel model : basicModels.values()) {
materialSet.addAll(model.getMaterials(modelGetter, errorSet)); materialSet.addAll(model.getMaterials(modelGetter, errorSet));