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.collect.Maps;
import com.google.common.collect.Sets;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import com.google.gson.*;
import com.mojang.datafixers.util.Pair;
import com.mojang.math.Transformation;
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.ResourceManager;
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.state.BlockState;
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.common.MinecraftForge;
import net.minecraftforge.fml.ModLoader;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.ForgeRegistryEntry;
import org.apache.commons.lang3.tuple.Triple;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider;
@ -56,6 +56,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Mixin(ModelBakery.class)
public abstract class ModelBakeryMixin {
@ -88,6 +89,7 @@ public abstract class ModelBakeryMixin {
@Shadow @Final public static BlockModel BLOCK_ENTITY_MARKER;
@Shadow @Final private static Logger LOGGER;
private Cache<Triple<ResourceLocation, Transformation, Boolean>, BakedModel> loadedBakedModels;
private Cache<ResourceLocation, UnbakedModel> loadedModels;
@ -134,78 +136,145 @@ public abstract class ModelBakeryMixin {
private UnbakedModel missingModel;
/**
* @author embeddedt
* @reason don't load any models initially, just set up initial data structures
*/
@Overwrite(remap = false)
protected void processLoading(ProfilerFiller arg, int maxMipLevels) {
ModelLoaderRegistry.onModelLoadingStart();
try {
this.missingModel = this.loadBlockModel(MISSING_MODEL_LOCATION);
} 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));
}
private Set<ResourceLocation> blockStateFiles;
private Set<ResourceLocation> modelFiles;
@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))
private BlockModel captureMissingModel(ModelBakery bakery, ResourceLocation location) throws IOException {
this.missingModel = this.loadBlockModel(location);
this.blockStateFiles = new HashSet<>();
this.modelFiles = new HashSet<>();
return (BlockModel)this.missingModel;
}
/**
* 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) {
Stopwatch stopwatch = Stopwatch.createStarted();
List<ResourceLocation> allModels = new ArrayList<>(this.resourceManager.listResources("models", path -> path.endsWith(".json")));
// for KubeJS, etc.
allModels.addAll(ResourcePackHandler.getExtraResources(this.resourceManager, "models", path -> path.endsWith(".json")));
List<CompletableFuture<Pair<ResourceLocation, JsonElement>>> modelBytes = new ArrayList<>();
for(ResourceLocation fileLocation : allModels) {
modelBytes.add(CompletableFuture.supplyAsync(() -> {
List<CompletableFuture<Pair<ResourceLocation, JsonElement>>> blockStateData = new ArrayList<>();
for(ResourceLocation blockstate : blockStateFiles) {
blockStateData.add(CompletableFuture.supplyAsync(() -> {
ResourceLocation fileLocation = new ResourceLocation(blockstate.getNamespace(), "blockstates/" + blockstate.getPath() + ".json");
try(Resource resource = this.resourceManager.getResource(fileLocation)) {
JsonParser parser = new JsonParser();
// strip models/ and .json from the name
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)));
return Pair.of(blockstate, 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);
ModernFix.LOGGER.error("Error reading blockstate {}: {}", blockstate, e);
}
return Pair.of(blockstate, null);
}, Util.backgroundExecutor()));
}
allModels.clear();
CompletableFuture.allOf(modelBytes.toArray(new CompletableFuture[0])).join();
Set<Pair<String, String>> errorSet = Sets.newLinkedHashSet();
Map<ResourceLocation, BlockModel> basicModels = new HashMap<>();
try {
basicModels.put(MISSING_MODEL_LOCATION, this.loadBlockModel(MISSING_MODEL_LOCATION));
basicModels.put(new ResourceLocation("builtin/generated"), GENERATION_MARKER);
basicModels.put(new ResourceLocation("builtin/entity"), BLOCK_ENTITY_MARKER);
} catch(IOException e) {
throw new RuntimeException("Exception when populating built-in models", e);
}
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();
basicModels.put(pair.getFirst(), model);
blockStateFiles = null;
CompletableFuture.allOf(blockStateData.toArray(new CompletableFuture[0])).join();
for(CompletableFuture<Pair<ResourceLocation, JsonElement>> result : blockStateData) {
Pair<ResourceLocation, JsonElement> pair = result.join();
if(pair.getSecond() != null) {
try {
JsonObject obj = pair.getSecond().getAsJsonObject();
if(obj.has("variants")) {
JsonObject eachVariant = obj.getAsJsonObject("variants");
for(Map.Entry<String, JsonElement> entry : eachVariant.entrySet()) {
JsonElement variantData = entry.getValue();
List<JsonObject> variantModels;
if(variantData.isJsonArray()) {
variantModels = new ArrayList<>();
for(JsonElement model : variantData.getAsJsonArray()) {
variantModels.add(model.getAsJsonObject());
}
} 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);
for(BlockModel model : basicModels.values()) {
materialSet.addAll(model.getMaterials(modelGetter, errorSet));