Merge remote-tracking branch 'origin/1.18' into 1.19.2
This commit is contained in:
commit
a8e92871c2
11
build.gradle
11
build.gradle
|
|
@ -1,5 +1,5 @@
|
|||
plugins {
|
||||
id "dev.architectury.loom" version "1.0.312"
|
||||
id "dev.architectury.loom" version "1.1-SNAPSHOT"
|
||||
id "maven-publish"
|
||||
id 'com.matthewprenger.cursegradle' version '1.4.0'
|
||||
id 'com.palantir.git-version' version '1.0.0'
|
||||
|
|
@ -89,10 +89,12 @@ dependencies {
|
|||
|
||||
// compile against the JEI API but do not include it at runtime
|
||||
modCompileOnly("mezz.jei:jei-${minecraft_version}-forge:${jei_version}")
|
||||
modRuntimeOnly("curse.maven:jei-238222:4405346")
|
||||
|
||||
modCompileOnly("curse.maven:jeresources-240630:3951643")
|
||||
modCompileOnly "me.shedaniel:RoughlyEnoughItems-forge:${rei_version}"
|
||||
modCompileOnly("dev.latvian.mods:kubejs-forge:${kubejs_version}")
|
||||
modRuntimeOnly("curse.maven:ferritecore-429235:4117906")
|
||||
modCompileOnly("team.chisel.ctm:CTM:${ctm_version}")
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
|
|
@ -135,10 +137,7 @@ jar {
|
|||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
// add all the jars that should be included when publishing to maven
|
||||
artifact(remapJar) {
|
||||
builtBy remapJar
|
||||
}
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,3 +14,5 @@ parchment_version=2022.11.27
|
|||
refined_storage_version=4392788
|
||||
jei_version=11.6.0.1011
|
||||
rei_version=9.1.591
|
||||
ctm_version=1.19.2-1.1.7+11
|
||||
kubejs_version=1902.6.0-build.142
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package org.embeddedt.modernfix;
|
|||
import com.mojang.datafixers.util.Pair;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.components.DebugScreenOverlay;
|
||||
import net.minecraft.client.gui.screens.ConnectScreen;
|
||||
import net.minecraft.client.gui.screens.TitleScreen;
|
||||
import net.minecraftforge.client.event.CustomizeGuiOverlayEvent;
|
||||
|
|
@ -10,12 +11,15 @@ import net.minecraftforge.client.event.ScreenEvent;
|
|||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraftforge.client.gui.overlay.ForgeGui;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.event.level.LevelEvent;
|
||||
import net.minecraftforge.eventbus.api.EventPriority;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.ModContainer;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||
import org.embeddedt.modernfix.load.LoadEvents;
|
||||
|
|
@ -37,6 +41,9 @@ public class ModernFixClient {
|
|||
private String brandingString = null;
|
||||
|
||||
public ModernFixClient() {
|
||||
// clear reserve as it's not needed
|
||||
// TODO: port 1.18+
|
||||
// ObfuscationReflectionHelper.setPrivateValue(Minecraft.class, null, new byte[0], "field_71444_a");
|
||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("perf.faster_singleplayer_load.ClientEvents")) {
|
||||
MinecraftForge.EVENT_BUS.register(new LoadEvents());
|
||||
}
|
||||
|
|
@ -82,6 +89,16 @@ public class ModernFixClient {
|
|||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onDisconnect(LevelEvent.Unload event) {
|
||||
if(event.getLevel().isClientSide()) {
|
||||
DebugScreenOverlay overlay = ObfuscationReflectionHelper.getPrivateValue(ForgeGui.class, (ForgeGui)Minecraft.getInstance().gui, "debugOverlay");
|
||||
if(overlay != null) {
|
||||
Minecraft.getInstance().tell(overlay::clearChunkCache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the IDs match and remap them if not.
|
||||
* @return true if ID remap was needed
|
||||
|
|
|
|||
|
|
@ -0,0 +1,181 @@
|
|||
package org.embeddedt.modernfix.blockstate;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Fake "map" implementation used to hold the states.
|
||||
*
|
||||
* Intentionally throws on methods that would be inefficient so that we know
|
||||
* if an incompatible mod is present.
|
||||
*/
|
||||
public class FakeStateMap<S> implements Map<Map<Property<?>, Comparable<?>>, S> {
|
||||
private final Map<Property<?>, Comparable<?>>[] keys;
|
||||
private final Object[] values;
|
||||
private int usedSlots;
|
||||
public FakeStateMap(int numStates) {
|
||||
this.keys = new Map[numStates];
|
||||
this.values = new Object[numStates];
|
||||
this.usedSlots = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return usedSlots;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public S get(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public S put(Map<Property<?>, Comparable<?>> propertyComparableMap, S s) {
|
||||
keys[usedSlots] = propertyComparableMap;
|
||||
values[usedSlots] = s;
|
||||
usedSlots++;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public S remove(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(@NotNull Map<? extends Map<Property<?>, Comparable<?>>, ? extends S> map) {
|
||||
for(Entry<? extends Map<Property<?>, Comparable<?>>, ? extends S> entry : map.entrySet()) {
|
||||
this.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
for(int i = 0; i < this.keys.length; i++) {
|
||||
this.keys[i] = null;
|
||||
this.values[i] = null;
|
||||
}
|
||||
this.usedSlots = 0;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Map<Property<?>, Comparable<?>>> keySet() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<S> values() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Entry<Map<Property<?>, Comparable<?>>, S>> entrySet() {
|
||||
return new Set<Entry<Map<Property<?>, Comparable<?>>, S>>() {
|
||||
@Override
|
||||
public int size() {
|
||||
return usedSlots;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return FakeStateMap.this.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<Entry<Map<Property<?>, Comparable<?>>, S>> iterator() {
|
||||
return new Iterator<Entry<Map<Property<?>, Comparable<?>>, S>>() {
|
||||
int currentIdx = 0;
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return currentIdx < usedSlots;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<Map<Property<?>, Comparable<?>>, S> next() {
|
||||
if(currentIdx >= usedSlots)
|
||||
throw new IndexOutOfBoundsException();
|
||||
Entry<Map<Property<?>, Comparable<?>>, S> entry = new AbstractMap.SimpleImmutableEntry<>(keys[currentIdx], (S)values[currentIdx]);
|
||||
currentIdx++;
|
||||
return entry;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <T> T[] toArray(@NotNull T[] ts) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Entry<Map<Property<?>, Comparable<?>>, S> mapSEntry) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(@NotNull Collection<?> collection) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(@NotNull Collection<? extends Entry<Map<Property<?>, Comparable<?>>, S>> collection) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(@NotNull Collection<?> collection) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(@NotNull Collection<?> collection) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,24 @@
|
|||
package org.embeddedt.modernfix.core;
|
||||
|
||||
import cpw.mods.modlauncher.api.INameMappingService;
|
||||
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.embeddedt.modernfix.core.config.ModernFixEarlyConfig;
|
||||
import org.embeddedt.modernfix.core.config.Option;
|
||||
import org.embeddedt.modernfix.util.DummyList;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.tree.*;
|
||||
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
|
||||
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
||||
import org.spongepowered.asm.mixin.injection.struct.InjectionInfo;
|
||||
import org.spongepowered.asm.mixin.injection.struct.InjectorGroupInfo;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ModernFixMixinPlugin implements IMixinConfigPlugin {
|
||||
private static final String MIXIN_PACKAGE_ROOT = "org.embeddedt.modernfix.mixin.";
|
||||
|
|
@ -28,6 +37,18 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin {
|
|||
|
||||
this.logger.info("Loaded configuration file for ModernFix: {} options available, {} override(s) found",
|
||||
config.getOptionCount(), config.getOptionOverrideCount());
|
||||
|
||||
/* https://github.com/FabricMC/Mixin/pull/99 */
|
||||
try {
|
||||
Field groupMembersField = InjectorGroupInfo.class.getDeclaredField("members");
|
||||
groupMembersField.setAccessible(true);
|
||||
Field noGroupField = InjectorGroupInfo.Map.class.getDeclaredField("NO_GROUP");
|
||||
noGroupField.setAccessible(true);
|
||||
InjectorGroupInfo noGroup = (InjectorGroupInfo)noGroupField.get(null);
|
||||
groupMembersField.set(noGroup, new DummyList<>());
|
||||
} catch(RuntimeException | ReflectiveOperationException e) {
|
||||
logger.error("Failed to patch mixin memory leak", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -99,6 +120,40 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin {
|
|||
|
||||
@Override
|
||||
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
|
||||
|
||||
if(mixinClassName.equals("org.embeddedt.modernfix.mixin.perf.compress_blockstate.BlockStateBaseMixin")) {
|
||||
// Delete unused fields off BlockStateBase
|
||||
Set<String> fieldsToDelete = Stream.of(
|
||||
"field_235702_f_", // isAir
|
||||
"field_235703_g_", // material
|
||||
"field_235705_i_", // destroySpeed
|
||||
"field_235706_j_", // requiresCorrectToolForDrops
|
||||
"field_235707_k_", // canOcclude
|
||||
"field_235708_l_", // isRedstoneConductor
|
||||
"field_235709_m_", // isSuffocating
|
||||
"field_235710_n_", // isViewBlocking
|
||||
"field_235711_o_", // hasPostProcess
|
||||
"field_235712_p_" // emissiveRendering
|
||||
).map(name -> ObfuscationReflectionHelper.remapName(INameMappingService.Domain.FIELD, name)).collect(Collectors.toSet());
|
||||
targetClass.fields.removeIf(field -> {
|
||||
if(fieldsToDelete.contains(field.name)) {
|
||||
logger.info("Removing " + field.name);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
for(MethodNode m : targetClass.methods) {
|
||||
if(m.name.equals("<init>")) {
|
||||
ListIterator<AbstractInsnNode> iter = m.instructions.iterator();
|
||||
while(iter.hasNext()) {
|
||||
AbstractInsnNode node = iter.next();
|
||||
if(node.getOpcode() == Opcodes.PUTFIELD) {
|
||||
if(fieldsToDelete.contains(((FieldInsnNode)node).name)) {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package org.embeddedt.modernfix.core.config;
|
||||
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ExplodedDirectoryLocator;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
|
@ -14,6 +15,10 @@ public class ModernFixEarlyConfig {
|
|||
|
||||
private final Map<String, Option> options = new HashMap<>();
|
||||
|
||||
private static boolean modPresent(String modId) {
|
||||
return FMLLoader.getLoadingModList().getModFileById(modId) != null;
|
||||
}
|
||||
|
||||
private ModernFixEarlyConfig() {
|
||||
// Defines the default rules which can be configured by the user or other mods.
|
||||
// You must manually add a rule for any new mixins not covered by an existing package rule.
|
||||
|
|
@ -25,24 +30,23 @@ public class ModernFixEarlyConfig {
|
|||
this.addMixinRule("feature.direct_stack_trace", false);
|
||||
this.addMixinRule("perf.fast_registry_validation", true);
|
||||
this.addMixinRule("perf.skip_first_datapack_reload", true);
|
||||
this.addMixinRule("perf.use_integrated_resources", true);
|
||||
this.addMixinRule("perf.jeresources_startup", true);
|
||||
this.addMixinRule("perf.remove_biome_temperature_cache", true);
|
||||
this.addMixinRule("perf.reduce_blockstate_cache_rebuilds", true);
|
||||
this.addMixinRule("perf.parallelize_model_loading", true);
|
||||
this.addMixinRule("perf.parallelize_model_loading.multipart", false);
|
||||
this.addMixinRule("perf.model_optimizations", true);
|
||||
this.addMixinRule("perf.dynamic_resources", false);
|
||||
/* Use a simpler ArrayMap if FerriteCore is using the map intelligently anyway */
|
||||
this.addMixinRule("perf.state_definition_construct", modPresent("ferritecore"));
|
||||
this.addMixinRule("perf.cache_strongholds", true);
|
||||
this.addMixinRule("perf.cache_upgraded_structures", true);
|
||||
this.addMixinRule("perf.compress_blockstate", false);
|
||||
this.addMixinRule("bugfix.concurrency", true);
|
||||
this.addMixinRule("bugfix.edge_chunk_not_saved", true);
|
||||
this.addMixinRule("perf.async_jei", true);
|
||||
this.addMixinRule("perf.thread_priorities", true);
|
||||
this.addMixinRule("perf.sync_executor_sleep", true);
|
||||
this.addMixinRule("perf.scan_cache", true);
|
||||
this.addMixinRule("perf.flatten_model_predicates", true);
|
||||
this.addMixinRule("perf.deduplicate_location", false);
|
||||
this.addMixinRule("perf.cache_blockstate_cache_arrays", true);
|
||||
this.addMixinRule("perf.faster_baking", true);
|
||||
this.addMixinRule("perf.cache_model_materials", true);
|
||||
this.addMixinRule("perf.datapack_reload_exceptions", true);
|
||||
this.addMixinRule("perf.faster_texture_stitching", true);
|
||||
|
|
@ -53,6 +57,8 @@ public class ModernFixEarlyConfig {
|
|||
this.addMixinRule("safety", true);
|
||||
this.addMixinRule("launch.transformer_cache", false);
|
||||
this.addMixinRule("launch.class_search_cache", true);
|
||||
boolean isDevEnv = !FMLLoader.isProduction() && FMLLoader.getLoadingModList().getModFileById("modernfix").getFile().getLocator() instanceof ExplodedDirectoryLocator;
|
||||
this.addMixinRule("devenv", isDevEnv);
|
||||
|
||||
/* Mod compat */
|
||||
disableIfModPresent("mixin.perf.thread_priorities", "smoothboot");
|
||||
|
|
@ -60,6 +66,7 @@ public class ModernFixEarlyConfig {
|
|||
disableIfModPresent("mixin.perf.compress_biome_container", "chocolate", "betterendforge");
|
||||
disableIfModPresent("mixin.bugfix.mc218112", "performant");
|
||||
disableIfModPresent("mixin.perf.faster_baking", "touhou_little_maid");
|
||||
disableIfModPresent("mixin.perf.reuse_datapacks", "tac");
|
||||
}
|
||||
|
||||
private void disableIfModPresent(String configName, String... ids) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
package org.embeddedt.modernfix.duck.rs;
|
||||
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||
|
||||
public interface IFluidExternalStorageCache {
|
||||
boolean initCache(IFluidHandler handler);
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package org.embeddedt.modernfix.duck.rs;
|
||||
|
||||
import net.minecraftforge.items.IItemHandler;
|
||||
|
||||
public interface IItemExternalStorageCache {
|
||||
boolean initCache(IItemHandler handler);
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
package org.embeddedt.modernfix.dynamicresources;
|
||||
|
||||
import com.mojang.math.Transformation;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.BlockModelRotation;
|
||||
import net.minecraft.client.resources.model.ModelBakery;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class DynamicBakedModelProvider implements Map<ResourceLocation, BakedModel> {
|
||||
private final ModelBakery bakery;
|
||||
private final Map<Triple<ResourceLocation, Transformation, Boolean>, BakedModel> bakedCache;
|
||||
private final Map<ResourceLocation, BakedModel> permanentOverrides;
|
||||
|
||||
public DynamicBakedModelProvider(ModelBakery bakery, Map<Triple<ResourceLocation, Transformation, Boolean>, BakedModel> cache) {
|
||||
this.bakery = bakery;
|
||||
this.bakedCache = cache;
|
||||
this.permanentOverrides = new Object2ObjectOpenHashMap<>();
|
||||
}
|
||||
private static Triple<ResourceLocation, Transformation, Boolean> vanillaKey(Object o) {
|
||||
return Triple.of((ResourceLocation)o, BlockModelRotation.X0_Y0.getRotation(), false);
|
||||
}
|
||||
@Override
|
||||
public int size() {
|
||||
return bakedCache.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return bakedCache.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
return permanentOverrides.containsKey(o) || bakedCache.containsKey(vanillaKey(o));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object o) {
|
||||
return permanentOverrides.containsValue(o) || bakedCache.containsValue(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BakedModel get(Object o) {
|
||||
BakedModel model = permanentOverrides.get(o);
|
||||
return model != null ? model : bakery.bake((ResourceLocation)o, BlockModelRotation.X0_Y0);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BakedModel put(ResourceLocation resourceLocation, BakedModel bakedModel) {
|
||||
BakedModel m = permanentOverrides.put(resourceLocation, bakedModel);
|
||||
if(m != null)
|
||||
return m;
|
||||
else
|
||||
return bakedCache.get(vanillaKey(resourceLocation));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BakedModel remove(Object o) {
|
||||
BakedModel m = permanentOverrides.remove(o);
|
||||
if(m != null)
|
||||
return m;
|
||||
return bakedCache.remove(vanillaKey(o));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(@NotNull Map<? extends ResourceLocation, ? extends BakedModel> map) {
|
||||
permanentOverrides.putAll(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<ResourceLocation> keySet() {
|
||||
return bakedCache.keySet().stream().map(Triple::getLeft).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<BakedModel> values() {
|
||||
return bakedCache.values();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Entry<ResourceLocation, BakedModel>> entrySet() {
|
||||
return bakedCache.entrySet().stream().map(entry -> new AbstractMap.SimpleImmutableEntry<>(entry.getKey().getLeft(), entry.getValue())).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceAll(BiFunction<? super ResourceLocation, ? super BakedModel, ? extends BakedModel> function) {
|
||||
Set<ResourceLocation> overridenLocations = permanentOverrides.keySet();
|
||||
permanentOverrides.replaceAll(function);
|
||||
boolean uvLock = BlockModelRotation.X0_Y0.isUvLocked();
|
||||
Transformation rotation = BlockModelRotation.X0_Y0.getRotation();
|
||||
bakedCache.replaceAll((loc, oldModel) -> {
|
||||
if(loc.getMiddle() != rotation || loc.getRight() != uvLock || overridenLocations.contains(loc.getLeft()))
|
||||
return oldModel;
|
||||
else
|
||||
return function.apply(loc.getLeft(), oldModel);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package org.embeddedt.modernfix.dynamicresources;
|
||||
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelManager;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.client.model.ForgeModelBakery;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
|
||||
/**
|
||||
* Fired when a model is baked dynamically. Intended to be used as a replacement for ModelBakeEvent
|
||||
* if mods want to replace a model.
|
||||
* <p></p>
|
||||
* Note that this event can fire many times for the same resource location, as models are unloaded
|
||||
* if unused/under memory pressure.
|
||||
*/
|
||||
public class DynamicModelBakeEvent extends Event implements IModBusEvent {
|
||||
private final ResourceLocation location;
|
||||
private BakedModel model;
|
||||
private final UnbakedModel unbakedModel;
|
||||
private final ForgeModelBakery modelLoader;
|
||||
public DynamicModelBakeEvent(ResourceLocation location, UnbakedModel unbakedModel, BakedModel model, ForgeModelBakery loader) {
|
||||
this.location = location;
|
||||
this.model = model;
|
||||
this.unbakedModel = unbakedModel;
|
||||
this.modelLoader = loader;
|
||||
}
|
||||
|
||||
public ResourceLocation getLocation() {
|
||||
return this.location;
|
||||
}
|
||||
|
||||
public BakedModel getModel() {
|
||||
return this.model;
|
||||
}
|
||||
|
||||
public UnbakedModel getUnbakedModel() {
|
||||
return this.unbakedModel;
|
||||
}
|
||||
|
||||
public ForgeModelBakery getModelLoader() {
|
||||
return this.modelLoader;
|
||||
}
|
||||
|
||||
public void setModel(BakedModel model) {
|
||||
this.model = model;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package org.embeddedt.modernfix.dynamicresources;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Handles loading models dynamically, rather than at startup time.
|
||||
*/
|
||||
public class DynamicModelProvider {
|
||||
private final Map<ResourceLocation, UnbakedModel> internalModels;
|
||||
private final Cache<ResourceLocation, Optional<UnbakedModel>> loadedModels =
|
||||
CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(3, TimeUnit.MINUTES)
|
||||
.maximumSize(1000)
|
||||
.concurrencyLevel(8)
|
||||
.softValues()
|
||||
.build();
|
||||
|
||||
public DynamicModelProvider(Map<ResourceLocation, UnbakedModel> initialModels) {
|
||||
this.internalModels = initialModels;
|
||||
}
|
||||
|
||||
public UnbakedModel getModel(ResourceLocation location) {
|
||||
try {
|
||||
return loadedModels.get(location, () -> Optional.ofNullable(loadModel(location))).orElse(null);
|
||||
} catch(ExecutionException e) {
|
||||
throw new RuntimeException(e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
private UnbakedModel loadModel(ResourceLocation location) {
|
||||
return null; /* TODO :) */
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package org.embeddedt.modernfix.dynamicresources;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.renderer.block.BlockModelShaper;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class ModelLocationCache {
|
||||
private static final LoadingCache<BlockState, ModelResourceLocation> locationCache = CacheBuilder.newBuilder()
|
||||
.maximumSize(10000)
|
||||
.build(new CacheLoader<BlockState, ModelResourceLocation>() {
|
||||
@Override
|
||||
public ModelResourceLocation load(BlockState key) throws Exception {
|
||||
return BlockModelShaper.stateToModelLocation(key);
|
||||
}
|
||||
});
|
||||
|
||||
public static ModelResourceLocation get(BlockState state) {
|
||||
try {
|
||||
return locationCache.get(state);
|
||||
} catch(ExecutionException e) {
|
||||
throw new RuntimeException(e.getCause());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package org.embeddedt.modernfix.dynamicresources;
|
||||
|
||||
import dev.latvian.mods.kubejs.script.data.KubeJSResourcePack;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.PackResources;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import org.embeddedt.modernfix.util.FileUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ResourcePackHandler {
|
||||
private static final List<PackHandler> packHandlers = new ArrayList<>();
|
||||
public static Collection<ResourceLocation> getExtraResources(ResourceManager manager, String path, Predicate<String> matchPredicate) {
|
||||
final String normalizedPath = FileUtil.normalize(path);
|
||||
return manager.listPacks().flatMap(pack -> packHandlers.stream().flatMap(handler -> {
|
||||
if(handler.shouldHandle(pack)) {
|
||||
return handler.getExtraResources(pack, normalizedPath, matchPredicate);
|
||||
} else
|
||||
return Stream.of();
|
||||
})).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
interface PackHandler {
|
||||
Stream<ResourceLocation> getExtraResources(PackResources pack, String path, Predicate<String> matchPredicate);
|
||||
boolean shouldHandle(PackResources pack);
|
||||
}
|
||||
|
||||
static class KubeJSPackHandler implements PackHandler {
|
||||
|
||||
@Override
|
||||
public Stream<ResourceLocation> getExtraResources(PackResources pack, String path, Predicate<String> matchPredicate) {
|
||||
KubeJSResourcePack p = (KubeJSResourcePack)pack;
|
||||
return p.getCachedResources().keySet().stream()
|
||||
.filter(l -> l.getPath().startsWith(path))
|
||||
.filter(l -> matchPredicate.test(l.getPath()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldHandle(PackResources pack) {
|
||||
return pack instanceof KubeJSResourcePack;
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
if(ModList.get().isLoaded("kubejs")) {
|
||||
packHandlers.add(new KubeJSPackHandler());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package org.embeddedt.modernfix.mixin.devenv;
|
||||
|
||||
import com.mojang.authlib.minecraft.UserApiService;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.main.GameConfig;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
|
||||
@Mixin(Minecraft.class)
|
||||
public class MinecraftMixin {
|
||||
@Overwrite
|
||||
private UserApiService createUserApiService(YggdrasilAuthenticationService yggdrasilAuthenticationService, GameConfig arg) {
|
||||
return UserApiService.OFFLINE;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package org.embeddedt.modernfix.mixin.devenv;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
/* Disable waiting for spawn chunk load */
|
||||
@Mixin(MinecraftServer.class)
|
||||
public class MinecraftServerMixin {
|
||||
@Redirect(method = "prepareLevels", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;getTickingGenerated()I"))
|
||||
private int noSpawnChunkWait(ServerChunkCache cache) {
|
||||
return 441;
|
||||
}
|
||||
|
||||
@Redirect(method = "prepareLevels", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;waitUntilNextTick()V"))
|
||||
private void noWaitTick(MinecraftServer server) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package org.embeddedt.modernfix.mixin.devenv;
|
||||
|
||||
import com.mojang.text2speech.Narrator;
|
||||
import com.mojang.text2speech.NarratorDummy;
|
||||
import net.minecraft.client.gui.chat.NarratorChatListener;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(NarratorChatListener.class)
|
||||
public class NarratorMixin {
|
||||
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lcom/mojang/text2speech/Narrator;getNarrator()Lcom/mojang/text2speech/Narrator;", remap = false))
|
||||
private Narrator useDummyNarrator() {
|
||||
return new NarratorDummy();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.async_jei;
|
||||
|
||||
import com.mojang.blaze3d.platform.InputConstants;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(InputConstants.class)
|
||||
public class InputConstantsMixin {
|
||||
@Redirect(method = "isKeyDown", at = @At(value = "INVOKE", target = "Lorg/lwjgl/glfw/GLFW;glfwGetKey(JI)I", remap = false))
|
||||
private static int offThreadKeyFetch(long win, int k) {
|
||||
if(RenderSystem.isOnRenderThreadOrInit())
|
||||
return GLFW.glfwGetKey(win, k);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.compress_blockstate;
|
||||
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
|
||||
@Mixin(BlockBehaviour.class)
|
||||
public class BlockBehaviourMixin {
|
||||
@Overwrite(remap = false)
|
||||
protected boolean isAir(BlockState state) {
|
||||
return state.getBlock().properties.isAir;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.compress_blockstate;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateHolder;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import net.minecraft.world.level.material.Material;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(BlockBehaviour.BlockStateBase.class)
|
||||
public abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> {
|
||||
protected BlockStateBaseMixin(Block object, ImmutableMap<Property<?>, Comparable<?>> immutableMap, MapCodec<BlockState> mapCodec) {
|
||||
super(object, immutableMap, mapCodec);
|
||||
}
|
||||
|
||||
@Redirect(method = "*", at = @At(
|
||||
value = "FIELD",
|
||||
opcode = Opcodes.GETFIELD,
|
||||
target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;material:Lnet/minecraft/world/level/material/Material;"
|
||||
))
|
||||
private Material getMaterial(BlockBehaviour.BlockStateBase base) {
|
||||
return this.owner.properties.material;
|
||||
}
|
||||
|
||||
@Redirect(method = "*", at = @At(
|
||||
value = "FIELD",
|
||||
opcode = Opcodes.GETFIELD,
|
||||
target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;destroySpeed:F"
|
||||
))
|
||||
private float getDestroyTime(BlockBehaviour.BlockStateBase base) {
|
||||
return this.owner.properties.destroyTime;
|
||||
}
|
||||
|
||||
@Redirect(method = "*", at = @At(
|
||||
value = "FIELD",
|
||||
opcode = Opcodes.GETFIELD,
|
||||
target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;requiresCorrectToolForDrops:Z"
|
||||
))
|
||||
private boolean getRequiresTool(BlockBehaviour.BlockStateBase base) {
|
||||
return this.owner.properties.requiresCorrectToolForDrops;
|
||||
}
|
||||
|
||||
@Redirect(method = "*", at = @At(
|
||||
value = "FIELD",
|
||||
opcode = Opcodes.GETFIELD,
|
||||
target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;canOcclude:Z"
|
||||
))
|
||||
private boolean getCanOcclude(BlockBehaviour.BlockStateBase base) {
|
||||
return this.owner.properties.canOcclude;
|
||||
}
|
||||
|
||||
@Redirect(method = "*", at = @At(
|
||||
value = "FIELD",
|
||||
opcode = Opcodes.GETFIELD,
|
||||
target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;isRedstoneConductor:Lnet/minecraft/world/level/block/state/BlockBehaviour$StatePredicate;"
|
||||
))
|
||||
private BlockBehaviour.StatePredicate getRedstoneConductor(BlockBehaviour.BlockStateBase base) {
|
||||
return this.owner.properties.isRedstoneConductor;
|
||||
}
|
||||
|
||||
@Redirect(method = "*", at = @At(
|
||||
value = "FIELD",
|
||||
opcode = Opcodes.GETFIELD,
|
||||
target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;isSuffocating:Lnet/minecraft/world/level/block/state/BlockBehaviour$StatePredicate;"
|
||||
))
|
||||
private BlockBehaviour.StatePredicate getSuffocating(BlockBehaviour.BlockStateBase base) {
|
||||
return this.owner.properties.isSuffocating;
|
||||
}
|
||||
|
||||
@Redirect(method = "*", at = @At(
|
||||
value = "FIELD",
|
||||
opcode = Opcodes.GETFIELD,
|
||||
target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;isViewBlocking:Lnet/minecraft/world/level/block/state/BlockBehaviour$StatePredicate;"
|
||||
))
|
||||
private BlockBehaviour.StatePredicate getViewBlocking(BlockBehaviour.BlockStateBase base) {
|
||||
return this.owner.properties.isViewBlocking;
|
||||
}
|
||||
|
||||
@Redirect(method = "*", at = @At(
|
||||
value = "FIELD",
|
||||
opcode = Opcodes.GETFIELD,
|
||||
target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;hasPostProcess:Lnet/minecraft/world/level/block/state/BlockBehaviour$StatePredicate;"
|
||||
))
|
||||
private BlockBehaviour.StatePredicate getPostProcess(BlockBehaviour.BlockStateBase base) {
|
||||
return this.owner.properties.hasPostProcess;
|
||||
}
|
||||
|
||||
@Redirect(method = "*", at = @At(
|
||||
value = "FIELD",
|
||||
opcode = Opcodes.GETFIELD,
|
||||
target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;emissiveRendering:Lnet/minecraft/world/level/block/state/BlockBehaviour$StatePredicate;"
|
||||
))
|
||||
private BlockBehaviour.StatePredicate getEmissiveRendering(BlockBehaviour.BlockStateBase base) {
|
||||
return this.owner.properties.emissiveRendering;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.dynamic_resources;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.renderer.block.BlockModelShaper;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelManager;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Mixin(BlockModelShaper.class)
|
||||
public class BlockModelShaperMixin {
|
||||
@Shadow @Final private ModelManager modelManager;
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason no need to rebuild model cache, and location cache is done elsewhere
|
||||
*/
|
||||
@Overwrite
|
||||
public void rebuildCache() {
|
||||
}
|
||||
|
||||
@Overwrite
|
||||
public BakedModel getBlockModel(BlockState state) {
|
||||
BakedModel model = modelManager.getModel(ModelLocationCache.get(state));
|
||||
if (model == null) {
|
||||
model = modelManager.getMissingModel();
|
||||
}
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.dynamic_resources;
|
||||
|
||||
import net.minecraft.client.renderer.ItemModelShaper;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelManager;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraftforge.client.ItemModelMesherForge;
|
||||
import net.minecraftforge.registries.IRegistryDelegate;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(ItemModelMesherForge.class)
|
||||
public abstract class ItemModelShaperMixin extends ItemModelShaper {
|
||||
@Shadow @Final private Map<IRegistryDelegate<Item>, ModelResourceLocation> locations;
|
||||
|
||||
public ItemModelShaperMixin(ModelManager arg) {
|
||||
super(arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Get the stored location for that item and meta, and get the model
|
||||
* from that location from the model manager.
|
||||
**/
|
||||
@Overwrite
|
||||
@Override
|
||||
public BakedModel getItemModel(Item item) {
|
||||
ModelResourceLocation map = locations.get(item.delegate);
|
||||
return map == null ? null : getModelManager().getModel(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Don't get all models during init (with dynamic loading, that would
|
||||
* generate them all). Just store location instead.
|
||||
**/
|
||||
@Overwrite
|
||||
@Override
|
||||
public void register(Item item, ModelResourceLocation location) {
|
||||
locations.put(item.delegate, location);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Disable cache rebuilding (with dynamic loading, that would generate
|
||||
* all models).
|
||||
**/
|
||||
@Overwrite
|
||||
@Override
|
||||
public void rebuildCache() {}
|
||||
}
|
||||
|
|
@ -0,0 +1,311 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.dynamic_resources;
|
||||
|
||||
import com.google.common.base.Stopwatch;
|
||||
import com.google.common.cache.Cache;
|
||||
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.mojang.datafixers.util.Pair;
|
||||
import com.mojang.math.Transformation;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.color.block.BlockColors;
|
||||
import net.minecraft.client.renderer.block.model.BlockModel;
|
||||
import net.minecraft.client.renderer.block.model.ItemModelGenerator;
|
||||
import net.minecraft.client.renderer.texture.AtlasSet;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlas;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.client.resources.model.*;
|
||||
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.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraftforge.client.ForgeHooksClient;
|
||||
import net.minecraftforge.client.model.ForgeModelBakery;
|
||||
import net.minecraftforge.client.model.ModelLoaderRegistry;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.fml.ModLoader;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider;
|
||||
import org.embeddedt.modernfix.dynamicresources.DynamicModelBakeEvent;
|
||||
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
|
||||
import org.embeddedt.modernfix.dynamicresources.ResourcePackHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mixin(ModelBakery.class)
|
||||
public abstract class ModelBakeryMixin {
|
||||
|
||||
private static final boolean debugDynamicModelLoading = Boolean.getBoolean("modernfix.debugDynamicModelLoading");
|
||||
|
||||
@Shadow @Final @Mutable public Map<ResourceLocation, UnbakedModel> unbakedCache;
|
||||
|
||||
@Shadow @Final public static ModelResourceLocation MISSING_MODEL_LOCATION;
|
||||
|
||||
@Shadow protected abstract BlockModel loadBlockModel(ResourceLocation location) throws IOException;
|
||||
|
||||
@Shadow @Final protected static Set<Material> UNREFERENCED_TEXTURES;
|
||||
@Shadow private Map<ResourceLocation, Pair<TextureAtlas, TextureAtlas.Preparations>> atlasPreparations;
|
||||
@Shadow @Final protected ResourceManager resourceManager;
|
||||
@Shadow @Nullable private AtlasSet atlasSet;
|
||||
@Shadow @Final private Set<ResourceLocation> loadingStack;
|
||||
|
||||
@Shadow protected abstract void loadModel(ResourceLocation blockstateLocation) throws Exception;
|
||||
|
||||
@Shadow @Final @Mutable
|
||||
private Map<ResourceLocation, BakedModel> bakedTopLevelModels;
|
||||
|
||||
@Shadow @Final @Mutable private Map<Triple<ResourceLocation, Transformation, Boolean>, BakedModel> bakedCache;
|
||||
|
||||
@Shadow @Final public static BlockModel GENERATION_MARKER;
|
||||
|
||||
@Shadow @Final private static ItemModelGenerator ITEM_MODEL_GENERATOR;
|
||||
|
||||
@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;
|
||||
|
||||
|
||||
@Inject(method = "<init>(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/client/color/block/BlockColors;Z)V", at = @At("RETURN"))
|
||||
private void replaceTopLevelBakedModels(ResourceManager manager, BlockColors colors, boolean vanillaBakery, CallbackInfo ci) {
|
||||
this.loadedBakedModels = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(3, TimeUnit.MINUTES)
|
||||
.maximumSize(1000)
|
||||
.concurrencyLevel(8)
|
||||
.removalListener(this::onModelRemoved)
|
||||
.softValues()
|
||||
.build();
|
||||
this.loadedModels = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(3, TimeUnit.MINUTES)
|
||||
.maximumSize(1000)
|
||||
.concurrencyLevel(8)
|
||||
.removalListener(this::onModelRemoved)
|
||||
.softValues()
|
||||
.build();
|
||||
this.bakedCache = loadedBakedModels.asMap();
|
||||
this.unbakedCache = loadedModels.asMap();
|
||||
this.bakedTopLevelModels = new DynamicBakedModelProvider((ModelBakery)(Object)this, bakedCache);
|
||||
}
|
||||
|
||||
private <K, V> void onModelRemoved(RemovalNotification<K, V> notification) {
|
||||
if(!debugDynamicModelLoading)
|
||||
return;
|
||||
Object k = notification.getKey();
|
||||
if(k == null)
|
||||
return;
|
||||
ResourceLocation rl;
|
||||
boolean baked = false;
|
||||
if(k instanceof ResourceLocation) {
|
||||
rl = (ResourceLocation)k;
|
||||
} else {
|
||||
rl = ((Triple<ResourceLocation, Transformation, Boolean>)k).getLeft();
|
||||
baked = true;
|
||||
}
|
||||
ModernFix.LOGGER.warn("Evicted {} model {}", baked ? "baked" : "unbaked", rl);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the models folder and try to load, parse, and get materials from as many models as possible.
|
||||
*/
|
||||
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(() -> {
|
||||
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)));
|
||||
} catch(IOException | JsonParseException e) {
|
||||
ModernFix.LOGGER.error("Error reading model {}: {}", fileLocation, e);
|
||||
return Pair.of(fileLocation, 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);
|
||||
}
|
||||
} catch(Throwable e) {
|
||||
ModernFix.LOGGER.warn("Unable to parse {}: {}", pair.getFirst(), e);
|
||||
}
|
||||
}
|
||||
modelBytes.clear();
|
||||
Function<ResourceLocation, UnbakedModel> modelGetter = loc -> basicModels.getOrDefault(loc, (BlockModel)this.missingModel);
|
||||
for(BlockModel model : basicModels.values()) {
|
||||
materialSet.addAll(model.getMaterials(modelGetter, errorSet));
|
||||
}
|
||||
//errorSet.stream().filter(pair -> !pair.getSecond().equals(MISSING_MODEL_LOCATION_STRING)).forEach(pair -> LOGGER.warn("Unable to resolve texture reference: {} in {}", pair.getFirst(), pair.getSecond()));
|
||||
stopwatch.stop();
|
||||
ModernFix.LOGGER.info("Resolving model textures took " + stopwatch);
|
||||
}
|
||||
|
||||
@Inject(method = "uploadTextures", at = @At(value = "FIELD", target = "Lnet/minecraft/client/resources/model/ModelBakery;topLevelModels:Ljava/util/Map;", ordinal = 0), cancellable = true)
|
||||
private void skipBake(TextureManager resourceManager, ProfilerFiller profiler, CallbackInfoReturnable<AtlasSet> cir) {
|
||||
profiler.pop();
|
||||
cir.setReturnValue(atlasSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the already loaded missing model instead of the cache entry (which will probably get evicted).
|
||||
*/
|
||||
@Redirect(method = "loadModel", at = @At(value = "INVOKE", target = "Ljava/util/Map;get(Ljava/lang/Object;)Ljava/lang/Object;", ordinal = 1))
|
||||
private Object getMissingModel(Map map, Object rl) {
|
||||
if(rl == MISSING_MODEL_LOCATION && map == unbakedCache)
|
||||
return missingModel;
|
||||
return unbakedCache.get(rl);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason synchronize
|
||||
*/
|
||||
@Overwrite
|
||||
public UnbakedModel getModel(ResourceLocation modelLocation) {
|
||||
if(modelLocation.equals(MISSING_MODEL_LOCATION))
|
||||
return missingModel;
|
||||
UnbakedModel existing = this.unbakedCache.get(modelLocation);
|
||||
if (existing != null) {
|
||||
return existing;
|
||||
} else {
|
||||
synchronized(this) {
|
||||
if (this.loadingStack.contains(modelLocation)) {
|
||||
throw new IllegalStateException("Circular reference while loading " + modelLocation);
|
||||
} else {
|
||||
this.loadingStack.add(modelLocation);
|
||||
UnbakedModel iunbakedmodel = missingModel;
|
||||
|
||||
while(!this.loadingStack.isEmpty()) {
|
||||
ResourceLocation resourcelocation = this.loadingStack.iterator().next();
|
||||
|
||||
try {
|
||||
if (!this.unbakedCache.containsKey(resourcelocation)) {
|
||||
if(debugDynamicModelLoading)
|
||||
LOGGER.info("Loading {}", resourcelocation);
|
||||
this.loadModel(resourcelocation);
|
||||
// TODO: in theory the cache can get evicted right here and we lose the model
|
||||
// very unlikely to occur though
|
||||
}
|
||||
} catch (ModelBakery.BlockStateDefinitionException var9) {
|
||||
LOGGER.warn(var9.getMessage());
|
||||
this.unbakedCache.put(resourcelocation, iunbakedmodel);
|
||||
} catch (Exception var10) {
|
||||
LOGGER.warn("Unable to load model: '{}' referenced from: {}: {}", resourcelocation, modelLocation, var10);
|
||||
this.unbakedCache.put(resourcelocation, iunbakedmodel);
|
||||
} finally {
|
||||
this.loadingStack.remove(resourcelocation);
|
||||
}
|
||||
}
|
||||
|
||||
return this.unbakedCache.getOrDefault(modelLocation, iunbakedmodel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Overwrite(remap = false)
|
||||
public BakedModel bake(ResourceLocation arg, ModelState arg2, Function<Material, TextureAtlasSprite> textureGetter) {
|
||||
Triple<ResourceLocation, Transformation, Boolean> triple = Triple.of(arg, arg2.getRotation(), arg2.isUvLocked());
|
||||
BakedModel existing = this.bakedCache.get(triple);
|
||||
if (existing != null) {
|
||||
return existing;
|
||||
} else if (this.atlasSet == null) {
|
||||
throw new IllegalStateException("bake called too early");
|
||||
} else {
|
||||
synchronized (this) {
|
||||
if(debugDynamicModelLoading)
|
||||
LOGGER.info("Baking {}", arg);
|
||||
UnbakedModel iunbakedmodel = this.getModel(arg);
|
||||
iunbakedmodel.getMaterials(this::getModel, new HashSet<>());
|
||||
BakedModel ibakedmodel = null;
|
||||
if (iunbakedmodel instanceof BlockModel) {
|
||||
BlockModel blockmodel = (BlockModel)iunbakedmodel;
|
||||
if (blockmodel.getRootModel() == GENERATION_MARKER) {
|
||||
ibakedmodel = ITEM_MODEL_GENERATOR.generateBlockModel(textureGetter, blockmodel).bake((ModelBakery)(Object)this, blockmodel, this.atlasSet::getSprite, arg2, arg, false);
|
||||
}
|
||||
}
|
||||
if(ibakedmodel == null) {
|
||||
ibakedmodel = iunbakedmodel.bake((ModelBakery) (Object) this, textureGetter, arg2, arg);
|
||||
}
|
||||
DynamicModelBakeEvent event = new DynamicModelBakeEvent(arg, iunbakedmodel, ibakedmodel, (ForgeModelBakery)(Object)this);
|
||||
ModLoader.get().postEvent(event);
|
||||
this.bakedCache.put(triple, event.getModel());
|
||||
return event.getModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.dynamic_resources.ctm;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.minecraft.client.renderer.ItemBlockRenderTypes;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.block.BlockModelShaper;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.client.resources.model.MultiPartBakedModel;
|
||||
import net.minecraft.client.resources.model.WeightedBakedModel;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.eventbus.api.EventPriority;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import net.minecraftforge.registries.IRegistryDelegate;
|
||||
import org.embeddedt.modernfix.dynamicresources.DynamicModelBakeEvent;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import team.chisel.ctm.client.model.AbstractCTMBakedModel;
|
||||
import team.chisel.ctm.client.util.CTMPackReloadListener;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Mixin(CTMPackReloadListener.class)
|
||||
public abstract class CTMPackReloadListenerMixin {
|
||||
@Shadow @Final private static Map<IRegistryDelegate<Block>, Predicate<RenderType>> blockRenderChecks;
|
||||
|
||||
@Shadow protected abstract Predicate<RenderType> getLayerCheck(BlockState state, BakedModel model);
|
||||
|
||||
@Shadow protected abstract Predicate<RenderType> getExistingRenderCheck(Block block);
|
||||
|
||||
private Map<ModelResourceLocation, BlockState> locationToState = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void onInit(CallbackInfo ci) {
|
||||
FMLJavaModLoadingContext.get().getModEventBus().addListener(EventPriority.LOW, this::onModelBake);
|
||||
}
|
||||
|
||||
@Overwrite(remap = false)
|
||||
private void refreshLayerHacks() {
|
||||
blockRenderChecks.forEach((b, p) -> ItemBlockRenderTypes.setRenderLayer((Block) b.get(), p));
|
||||
blockRenderChecks.clear();
|
||||
if(locationToState.isEmpty()) {
|
||||
for(Block block : ForgeRegistries.BLOCKS.getValues()) {
|
||||
for(BlockState state : block.getStateDefinition().getPossibleStates()) {
|
||||
locationToState.put(BlockModelShaper.stateToModelLocation(state), state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onModelBake(DynamicModelBakeEvent event) {
|
||||
if(!(event.getModel() instanceof AbstractCTMBakedModel || event.getModel() instanceof WeightedBakedModel || event.getModel() instanceof MultiPartBakedModel))
|
||||
return;
|
||||
BlockState state = locationToState.get(event.getLocation());
|
||||
if(state == null)
|
||||
return;
|
||||
Block block = state.getBlock();
|
||||
if(blockRenderChecks.containsKey(block.delegate))
|
||||
return;
|
||||
Predicate<RenderType> newPredicate = this.getLayerCheck(state, event.getModel());
|
||||
if(newPredicate != null) {
|
||||
blockRenderChecks.put(block.delegate, this.getExistingRenderCheck(block));
|
||||
ItemBlockRenderTypes.setRenderLayer(block, newPredicate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.dynamic_resources.ctm;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.Material;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.client.model.ForgeModelBakery;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import org.embeddedt.modernfix.dynamicresources.DynamicModelBakeEvent;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import team.chisel.ctm.CTM;
|
||||
import team.chisel.ctm.client.model.AbstractCTMBakedModel;
|
||||
import team.chisel.ctm.client.texture.IMetadataSectionCTM;
|
||||
import team.chisel.ctm.client.util.ResourceUtil;
|
||||
import team.chisel.ctm.client.util.TextureMetadataHandler;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
@Mixin(TextureMetadataHandler.class)
|
||||
public abstract class TextureMetadataHandlerMixin {
|
||||
|
||||
@Shadow @Nonnull protected abstract BakedModel wrap(ResourceLocation loc, UnbakedModel model, BakedModel object, ForgeModelBakery loader) throws IOException;
|
||||
|
||||
@SubscribeEvent
|
||||
public void onDynamicModelBake(DynamicModelBakeEvent event) {
|
||||
UnbakedModel rootModel = event.getUnbakedModel();
|
||||
BakedModel baked = event.getModel();
|
||||
ResourceLocation rl = event.getLocation();
|
||||
if (!(baked instanceof AbstractCTMBakedModel) && !baked.isCustomRenderer()) {
|
||||
Deque<ResourceLocation> dependencies = new ArrayDeque<>();
|
||||
Set<ResourceLocation> seenModels = new HashSet<>();
|
||||
dependencies.push(rl);
|
||||
seenModels.add(rl);
|
||||
boolean shouldWrap = false;
|
||||
Set<Pair<String, String>> errors = new HashSet<>();
|
||||
// Breadth-first loop through dependencies, exiting as soon as a CTM texture is found, and skipping duplicates/cycles
|
||||
while (!shouldWrap && !dependencies.isEmpty()) {
|
||||
ResourceLocation dep = dependencies.pop();
|
||||
UnbakedModel model;
|
||||
try {
|
||||
model = dep == rl ? rootModel : event.getModelLoader().getModel(dep);
|
||||
} catch (Exception e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Collection<Material> textures = model.getMaterials(event.getModelLoader()::getModel, errors);
|
||||
Collection<ResourceLocation> newDependencies = model.getDependencies();
|
||||
for (Material tex : textures) {
|
||||
IMetadataSectionCTM meta = null;
|
||||
// Cache all dependent texture metadata
|
||||
try {
|
||||
meta = ResourceUtil.getMetadata(ResourceUtil.spriteToAbsolute(tex.texture()));
|
||||
} catch (IOException e) {} // Fallthrough
|
||||
if (meta != null) {
|
||||
// At least one texture has CTM metadata, so we should wrap this model
|
||||
shouldWrap = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (ResourceLocation newDep : newDependencies) {
|
||||
if (seenModels.add(newDep)) {
|
||||
dependencies.push(newDep);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shouldWrap) {
|
||||
try {
|
||||
event.setModel(wrap(rl, rootModel, baked, event.getModelLoader()));
|
||||
dependencies.clear();
|
||||
} catch (IOException e) {
|
||||
CTM.logger.error("Could not wrap model " + rl + ". Aborting...", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.faster_baking;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.client.renderer.block.BlockModelShaper;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelManager;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.Registry;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Mixin(BlockModelShaper.class)
|
||||
public abstract class BlockModelShapesMixin {
|
||||
@Shadow @Final private ModelManager modelManager;
|
||||
|
||||
@Shadow @Final private Map<BlockState, BakedModel> modelByStateCache;
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason parallelize cache rebuild
|
||||
*/
|
||||
@Overwrite
|
||||
public void rebuildCache() {
|
||||
this.modelByStateCache.clear();
|
||||
ArrayList<CompletableFuture<Pair<BlockState, BakedModel>>> futures = new ArrayList<>();
|
||||
for(Block block : Registry.BLOCK) {
|
||||
block.getStateDefinition().getPossibleStates().forEach((state) -> {
|
||||
futures.add(CompletableFuture.supplyAsync(() -> {
|
||||
return Pair.of(state, this.modelManager.getModel(BlockModelShaper.stateToModelLocation(state)));
|
||||
}, Util.backgroundExecutor()));
|
||||
});
|
||||
}
|
||||
for(CompletableFuture<Pair<BlockState, BakedModel>> future : futures) {
|
||||
Pair<BlockState, BakedModel> pair = future.join();
|
||||
this.modelByStateCache.put(pair.getFirst(), pair.getSecond());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,162 +0,0 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.faster_baking;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.color.block.BlockColors;
|
||||
import net.minecraft.client.renderer.block.model.MultiVariant;
|
||||
import net.minecraft.client.renderer.block.model.multipart.MultiPart;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlas;
|
||||
import net.minecraft.client.renderer.texture.AtlasSet;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import com.mojang.math.Transformation;
|
||||
import net.minecraftforge.client.event.ModelEvent;
|
||||
import net.minecraftforge.fml.loading.progress.StartupMessageManager;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
import org.embeddedt.modernfix.core.config.ModernFixConfig;
|
||||
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
|
||||
import org.embeddedt.modernfix.models.LazyBakedModel;
|
||||
import org.embeddedt.modernfix.util.ModUtil;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.embeddedt.modernfix.ModernFix.LOGGER;
|
||||
|
||||
import net.minecraft.client.renderer.block.model.BlockModel;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.BlockModelRotation;
|
||||
import net.minecraft.client.resources.model.Material;
|
||||
import net.minecraft.client.resources.model.ModelBakery;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.client.resources.model.ModelState;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
|
||||
@Mixin(ModelBakery.class)
|
||||
public abstract class ModelBakeryMixin implements IExtendedModelBakery {
|
||||
@Shadow @Final private Map<ResourceLocation, UnbakedModel> topLevelModels;
|
||||
|
||||
@Shadow @Final private Map<ResourceLocation, BakedModel> bakedTopLevelModels;
|
||||
|
||||
@Shadow @Deprecated @Nullable public abstract BakedModel bake(ResourceLocation pLocation, ModelState pTransform);
|
||||
|
||||
@Shadow private Map<ResourceLocation, Pair<TextureAtlas, TextureAtlas.Preparations>> atlasPreparations;
|
||||
|
||||
@Shadow @Nullable private AtlasSet atlasSet;
|
||||
|
||||
@Shadow @Final public static ModelResourceLocation MISSING_MODEL_LOCATION;
|
||||
|
||||
@Shadow @Final private Map<Triple<ResourceLocation, Transformation, Boolean>, BakedModel> bakedCache;
|
||||
|
||||
@Shadow @Final private Map<ResourceLocation, UnbakedModel> unbakedCache;
|
||||
|
||||
private BakedModel bakeIfPossible(ResourceLocation p_229350_1_) {
|
||||
BakedModel ibakedmodel = null;
|
||||
|
||||
try {
|
||||
ibakedmodel = this.bake(p_229350_1_, BlockModelRotation.X0_Y0);
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
LOGGER.warn("Unable to bake model: '{}': {}", p_229350_1_, exception);
|
||||
}
|
||||
|
||||
return ibakedmodel;
|
||||
}
|
||||
|
||||
private boolean requiresBake(UnbakedModel model) {
|
||||
if(model instanceof BlockModel && ((BlockModel)model).customData.hasCustomGeometry())
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
@Inject(method = "<init>", at = @At("TAIL"))
|
||||
private void bakeModels(ResourceManager arg, BlockColors arg2, ProfilerFiller pProfiler, int i, CallbackInfo ci) {
|
||||
pProfiler.push("atlas");
|
||||
Minecraft.getInstance().executeBlocking(() -> {
|
||||
for(Pair<TextureAtlas, TextureAtlas.Preparations> pair : this.atlasPreparations.values()) {
|
||||
TextureAtlas atlastexture = pair.getFirst();
|
||||
TextureAtlas.Preparations atlastexture$sheetdata = pair.getSecond();
|
||||
atlastexture.reload(atlastexture$sheetdata);
|
||||
}
|
||||
});
|
||||
pProfiler.popPush("baking");
|
||||
StartupMessageManager.mcLoaderConsumer().ifPresent(c -> c.accept("Baking models"));
|
||||
this.atlasSet = new AtlasSet(this.atlasPreparations.values().stream().map(Pair::getFirst).collect(Collectors.toList()));
|
||||
BakedModel missingModel = this.bake(MISSING_MODEL_LOCATION, BlockModelRotation.X0_Y0);
|
||||
this.bakedTopLevelModels.put(MISSING_MODEL_LOCATION, missingModel);
|
||||
Collection<String> modsListening = ModUtil.findAllModsListeningToEvent(ModelEvent.BakingCompleted.class);
|
||||
LOGGER.debug("Found ModelBakeEvent listeners: [" + String.join(", ", modsListening) + "]");
|
||||
Set<String> incompatibleLazyBakedModels = ImmutableSet.<String>builder()
|
||||
.addAll(modsListening)
|
||||
.build();
|
||||
/* First, bake any incompatible models ahead of time (for mods that have custom models) */
|
||||
new ArrayList<>(this.unbakedCache.keySet()).forEach(location -> {
|
||||
if(incompatibleLazyBakedModels.contains(location.getNamespace())) {
|
||||
this.bakeIfPossible(location);
|
||||
}
|
||||
});
|
||||
List<ResourceLocation> multiparts = new ArrayList<>();
|
||||
/* Then store them as top-level models if needed, and set up the lazy models */
|
||||
this.topLevelModels.forEach((location, value) -> {
|
||||
if (requiresBake(value) || incompatibleLazyBakedModels.contains(location.getNamespace())) {
|
||||
BakedModel model = this.bakeIfPossible(location);
|
||||
if (model != null)
|
||||
this.bakedTopLevelModels.put(location, model);
|
||||
} else {
|
||||
if(value instanceof MultiPart || value instanceof MultiVariant) {
|
||||
multiparts.add(location);
|
||||
} else {
|
||||
this.bakedTopLevelModels.put(location, new LazyBakedModel(() -> {
|
||||
synchronized (this.bakedCache) {
|
||||
BakedModel ibakedmodel = this.bakeIfPossible(location);
|
||||
|
||||
return ibakedmodel != null ? ibakedmodel : missingModel;
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
});
|
||||
multiparts.forEach(location -> {
|
||||
BakedModel model = this.bakeIfPossible(location);
|
||||
if (model != null)
|
||||
this.bakedTopLevelModels.put(location, model);
|
||||
});
|
||||
pProfiler.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason texture loading and baking are moved earlier in the launch process, only render thread stuff is done here
|
||||
*/
|
||||
@Overwrite
|
||||
public AtlasSet uploadTextures(TextureManager pResourceManager, ProfilerFiller pProfiler) {
|
||||
pProfiler.push("atlas_upload");
|
||||
for(Pair<TextureAtlas, TextureAtlas.Preparations> pair : this.atlasPreparations.values()) {
|
||||
TextureAtlas atlastexture = pair.getFirst();
|
||||
TextureAtlas.Preparations atlastexture$sheetdata = pair.getSecond();
|
||||
pResourceManager.register(atlastexture.location(), atlastexture);
|
||||
pResourceManager.bindForSetup(atlastexture.location());
|
||||
atlastexture.updateFilter(atlastexture$sheetdata);
|
||||
}
|
||||
pProfiler.pop();
|
||||
return this.atlasSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AtlasSet getUnfinishedAtlasSet() {
|
||||
return this.atlasSet;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.faster_baking;
|
||||
|
||||
import net.minecraft.client.resources.model.ModelBakery;
|
||||
import net.minecraft.client.resources.model.ModelManager;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import org.embeddedt.modernfix.models.LazyBakedModel;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ModelManager.class)
|
||||
public class ModelManagerMixin {
|
||||
@Inject(method = "apply(Lnet/minecraft/client/resources/model/ModelBakery;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V",
|
||||
at = @At(value = "RETURN"))
|
||||
private void allowBake(ModelBakery pObject, ResourceManager pResourceManager, ProfilerFiller pProfiler, CallbackInfo ci) {
|
||||
LazyBakedModel.allowBakeForFlags = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading;
|
||||
package org.embeddedt.modernfix.mixin.perf.model_optimizations;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading;
|
||||
package org.embeddedt.modernfix.mixin.perf.model_optimizations;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.client.model.obj.ObjLoader;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading;
|
||||
package org.embeddedt.modernfix.mixin.perf.model_optimizations;
|
||||
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.embeddedt.modernfix.dedup.IdentifierCaches;
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading;
|
||||
package org.embeddedt.modernfix.mixin.perf.model_optimizations;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.client.renderer.block.model.multipart.Selector;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading;
|
||||
package org.embeddedt.modernfix.mixin.perf.model_optimizations;
|
||||
|
||||
import com.mojang.math.Matrix4f;
|
||||
import com.mojang.math.Transformation;
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading;
|
||||
|
||||
import com.google.common.collect.Interner;
|
||||
import com.google.common.collect.Interners;
|
||||
import net.minecraft.client.renderer.block.BlockModelShaper;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Mixin(BlockModelShaper.class)
|
||||
public class BlockModelShaperMixin {
|
||||
private static Map<BlockState, String> stateToPropertiesCache = new ConcurrentHashMap<>();
|
||||
|
||||
@Redirect(method = "stateToModelLocation(Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/world/level/block/state/BlockState;)Lnet/minecraft/client/resources/model/ModelResourceLocation;",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/block/BlockModelShaper;statePropertiesToString(Ljava/util/Map;)Ljava/lang/String;"))
|
||||
private static String getCachedProperty(Map<Property<?>, Comparable<?>> values, ResourceLocation location, BlockState state) {
|
||||
/* We intentionally don't use the values parameter inside the lambda to avoid an allocation */
|
||||
return stateToPropertiesCache.computeIfAbsent(state, s -> BlockModelShaper.statePropertiesToString(s.getValues()));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.client.color.block.BlockColors;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraft.server.packs.resources.Resource;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraftforge.fml.loading.progress.StartupMessageManager;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.util.AsyncStopwatch;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import net.minecraft.client.renderer.block.model.BlockModelDefinition;
|
||||
import net.minecraft.client.resources.model.ModelBakery;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
|
||||
@Mixin(ModelBakery.class)
|
||||
public abstract class ModelBakeryMixin {
|
||||
@Shadow @Final protected ResourceManager resourceManager;
|
||||
|
||||
private Map<ResourceLocation, List<Pair<String, BlockModelDefinition>>> deserializedBlockstateCache = null;
|
||||
|
||||
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiling/ProfilerFiller;popPush(Ljava/lang/String;)V", ordinal = 0))
|
||||
private void preloadJsonModels(ProfilerFiller profilerIn, String str) {
|
||||
StartupMessageManager.mcLoaderConsumer().ifPresent(c -> c.accept("Loading models"));
|
||||
profilerIn.popPush("loadblockstates");
|
||||
AsyncStopwatch.measureAndLogSerialRunningTime("Parallel blockstate loading", () -> {
|
||||
ThreadLocal<BlockModelDefinition.Context> containerHolder = ThreadLocal.withInitial(BlockModelDefinition.Context::new);
|
||||
this.deserializedBlockstateCache = new ConcurrentHashMap<>();
|
||||
ArrayList<CompletableFuture<Void>> futures = new ArrayList<>();
|
||||
for(Block block : Registry.BLOCK) {
|
||||
ResourceLocation blockLocation = Registry.BLOCK.getKey(block);
|
||||
futures.add(CompletableFuture.runAsync(() -> {
|
||||
ResourceLocation blockStateJSON = new ResourceLocation(blockLocation.getNamespace(), "blockstates/" + blockLocation.getPath() + ".json");
|
||||
List<Resource> blockStates;
|
||||
/* Some mods' custom resource pack implementations don't seem to like concurrency here */
|
||||
synchronized(this.resourceManager) {
|
||||
blockStates = this.resourceManager.getResourceStack(blockStateJSON);
|
||||
}
|
||||
List<Pair<String, BlockModelDefinition>> definitions = new ArrayList<>();
|
||||
StateDefinition<Block, BlockState> stateContainer = block.getStateDefinition();
|
||||
BlockModelDefinition.Context context = containerHolder.get();
|
||||
context.setDefinition(stateContainer);
|
||||
for(Resource resource : blockStates) {
|
||||
try (InputStream inputstream = resource.open()) {
|
||||
BlockModelDefinition definition = BlockModelDefinition.fromStream(context, new InputStreamReader(inputstream, StandardCharsets.UTF_8));
|
||||
definitions.add(Pair.of(resource.sourcePackId(), definition));
|
||||
} catch (Exception exception1) {
|
||||
ModernFix.LOGGER.warn(String.format("Exception loading blockstate definition: '%s' in resourcepack: '%s': %s", blockStateJSON, resource.sourcePackId(), exception1.getMessage()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.deserializedBlockstateCache.put(blockLocation, definitions);
|
||||
}, Util.backgroundExecutor()));
|
||||
}
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
|
||||
});
|
||||
profilerIn.popPush(str);
|
||||
}
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void clearModelCache(ResourceManager arg, BlockColors arg2, ProfilerFiller arg3, int i, CallbackInfo ci) {
|
||||
deserializedBlockstateCache.clear();
|
||||
}
|
||||
|
||||
private List<?> replacementList = null;
|
||||
|
||||
@Redirect(method = "loadModel", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/packs/resources/ResourceManager;getResourceStack(Lnet/minecraft/resources/ResourceLocation;)Ljava/util/List;", ordinal = 0))
|
||||
private List<?> getResourceList(ResourceManager instance, ResourceLocation jsonLocation, ResourceLocation originalBlockLocation) throws IOException {
|
||||
replacementList = null;
|
||||
if(this.deserializedBlockstateCache != null) {
|
||||
if(!(originalBlockLocation instanceof ModelResourceLocation)) {
|
||||
throw new AssertionError("Injector in unexpected spot?");
|
||||
}
|
||||
ModelResourceLocation mrl = (ModelResourceLocation)originalBlockLocation;
|
||||
ResourceLocation location = new ResourceLocation(mrl.getNamespace(), mrl.getPath());
|
||||
List<?> theList = this.deserializedBlockstateCache.get(location);
|
||||
if(theList != null && theList.size() > 0) {
|
||||
replacementList = theList;
|
||||
return theList;
|
||||
}
|
||||
}
|
||||
return instance.getResourceStack(jsonLocation);
|
||||
}
|
||||
|
||||
@Redirect(method = "loadModel", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;map(Ljava/util/function/Function;)Ljava/util/stream/Stream;", ordinal = 0))
|
||||
private Stream<?> fakeResourceList(Stream<?> instance, Function function) {
|
||||
return replacementList != null ? instance : instance.map(function);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading.multipart;
|
||||
|
||||
import net.minecraft.client.renderer.block.model.multipart.MultiPart;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Mixin(MultiPart.class)
|
||||
public class MultipartMixin {
|
||||
@Redirect(method = "getMaterials", at = @At(value = "INVOKE", target = "Ljava/util/List;stream()Ljava/util/stream/Stream;", ordinal = 0))
|
||||
private Stream makeStreamParallel(List instance) {
|
||||
return instance.parallelStream();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading.multipart;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.client.resources.model.Material;
|
||||
import net.minecraft.client.renderer.block.model.Variant;
|
||||
import net.minecraft.client.renderer.block.model.MultiVariant;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mixin(MultiVariant.class)
|
||||
public abstract class VariantListMixin {
|
||||
@Shadow public abstract List<Variant> getVariants();
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason Parallelize calls to getMaterials
|
||||
*/
|
||||
@Overwrite
|
||||
public Collection<Material> getMaterials(Function<ResourceLocation, UnbakedModel> pModelGetter, Set<Pair<String, String>> pMissingTextureErrors) {
|
||||
List<UnbakedModel> models = this.getVariants().stream().map(Variant::getModelLocation).distinct().map(pModelGetter).collect(Collectors.toList());
|
||||
return models.parallelStream().flatMap(model -> model.getMaterials(pModelGetter, pMissingTextureErrors).stream()).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.state_definition_construct;
|
||||
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.StateHolder;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.embeddedt.modernfix.blockstate.FakeStateMap;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(StateDefinition.class)
|
||||
public class StateDefinitionMixin<O, S extends StateHolder<O, S>> {
|
||||
@Shadow @Final private ImmutableSortedMap<String, Property<?>> propertiesByName;
|
||||
|
||||
@ModifyVariable(method = "<init>", at = @At(value = "STORE", ordinal = 0), ordinal = 1, index = 8)
|
||||
private Map<Map<Property<?>, Comparable<?>>, S> useArrayMap(Map<Map<Property<?>, Comparable<?>>, S> in) {
|
||||
int numStates = 1;
|
||||
for(Property<?> prop : this.propertiesByName.values()) {
|
||||
numStates *= prop.getPossibleValues().size();
|
||||
}
|
||||
return new FakeStateMap<>(numStates);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.use_integrated_resources;
|
||||
|
||||
import jeresources.util.LootTableHelper;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraftforge.server.ServerLifecycleHooks;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(LootTableHelper.class)
|
||||
public class LootTableHelperMixin {
|
||||
@Redirect(method = "getLootTables", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;getServer()Lnet/minecraft/server/MinecraftServer;"))
|
||||
private static MinecraftServer useIntegrated(Level level) {
|
||||
MinecraftServer server = level.getServer();
|
||||
if(server != null)
|
||||
return server;
|
||||
return ServerLifecycleHooks.getCurrentServer();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,140 +0,0 @@
|
|||
package org.embeddedt.modernfix.models;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.renderer.block.model.ItemTransforms;
|
||||
import net.minecraft.client.renderer.block.model.ItemOverrides;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraftforge.client.ChunkRenderTypeSet;
|
||||
import net.minecraftforge.client.model.data.ModelData;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class LazyBakedModel implements BakedModel {
|
||||
private BakedModel delegate = null;
|
||||
private Supplier<BakedModel> delegateSupplier;
|
||||
|
||||
/**
|
||||
* This flag is changed to true when we should bake instead of returning reasonable defaults for certain
|
||||
* method calls.
|
||||
*/
|
||||
public static boolean allowBakeForFlags = false;
|
||||
|
||||
public LazyBakedModel(Supplier<BakedModel> delegateSupplier) {
|
||||
this.delegateSupplier = delegateSupplier;
|
||||
}
|
||||
|
||||
public BakedModel computeDelegate() {
|
||||
if(this.delegate == null) {
|
||||
this.delegate = this.delegateSupplier.get();
|
||||
this.delegateSupplier = null;
|
||||
}
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BakedQuad> getQuads(@Nullable BlockState pState, @Nullable Direction pSide, RandomSource pRand) {
|
||||
return computeDelegate().getQuads(pState, pSide, pRand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useAmbientOcclusion() {
|
||||
return computeDelegate().useAmbientOcclusion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGui3d() {
|
||||
return computeDelegate().isGui3d();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean usesBlockLight() {
|
||||
return computeDelegate().usesBlockLight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCustomRenderer() {
|
||||
if(this.delegate != null)
|
||||
return this.delegate.isCustomRenderer();
|
||||
if(!LazyBakedModel.allowBakeForFlags)
|
||||
return false;
|
||||
return computeDelegate().isCustomRenderer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureAtlasSprite getParticleIcon() {
|
||||
return computeDelegate().getParticleIcon();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemTransforms getTransforms() {
|
||||
return computeDelegate().getTransforms();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemOverrides getOverrides() {
|
||||
return computeDelegate().getOverrides();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, @Nonnull RandomSource rand, @Nonnull ModelData extraData, @org.jetbrains.annotations.Nullable RenderType renderType) {
|
||||
return computeDelegate().getQuads(state, side, rand, extraData, renderType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useAmbientOcclusion(BlockState state) {
|
||||
return computeDelegate().useAmbientOcclusion(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useAmbientOcclusion(BlockState state, RenderType renderType) {
|
||||
return computeDelegate().useAmbientOcclusion(state, renderType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BakedModel applyTransform(ItemTransforms.TransformType transformType, PoseStack poseStack, boolean applyLeftHandTransform) {
|
||||
return computeDelegate().applyTransform(transformType, poseStack, applyLeftHandTransform);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ModelData getModelData(@Nonnull BlockAndTintGetter world, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nonnull ModelData tileData) {
|
||||
return computeDelegate().getModelData(world, pos, state, tileData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureAtlasSprite getParticleIcon(@Nonnull ModelData data) {
|
||||
return computeDelegate().getParticleIcon(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkRenderTypeSet getRenderTypes(@NotNull BlockState state, @NotNull RandomSource rand, @NotNull ModelData data) {
|
||||
return computeDelegate().getRenderTypes(state, rand, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BakedModel> getRenderPasses(ItemStack itemStack, boolean fabulous) {
|
||||
return computeDelegate().getRenderPasses(itemStack, fabulous);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RenderType> getRenderTypes(ItemStack itemStack, boolean fabulous) {
|
||||
return computeDelegate().getRenderTypes(itemStack, fabulous);
|
||||
}
|
||||
}
|
||||
|
|
@ -92,18 +92,18 @@ public class StbStitcher {
|
|||
}
|
||||
}
|
||||
|
||||
private static STBRPRect setWrapper(STBRPRect rect, int id, int width, int height, int x, int y, boolean was_packed) {
|
||||
public static STBRPRect setWrapper(STBRPRect rect, int id, int width, int height, int x, int y, boolean was_packed) {
|
||||
try {
|
||||
if(MH_rect_shortSet != null)
|
||||
return (STBRPRect)MH_rect_shortSet.invokeExact(rect, id, (short)width, (short)height, (short)0, (short)0, false);
|
||||
return (STBRPRect)MH_rect_shortSet.invokeExact(rect, id, (short)width, (short)height, (short)x, (short)y, was_packed);
|
||||
else
|
||||
return (STBRPRect)MH_rect_intSet.invokeExact(rect, id, width, height, 0, 0, false);
|
||||
return (STBRPRect)MH_rect_intSet.invokeExact(rect, id, width, height, x, y, was_packed);
|
||||
} catch(Throwable e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static int getX(STBRPRect rect) {
|
||||
public static int getX(STBRPRect rect) {
|
||||
try {
|
||||
if(MH_rect_shortX != null)
|
||||
return (short)MH_rect_shortX.invokeExact(rect);
|
||||
|
|
@ -114,7 +114,7 @@ public class StbStitcher {
|
|||
}
|
||||
}
|
||||
|
||||
private static int getY(STBRPRect rect) {
|
||||
public static int getY(STBRPRect rect) {
|
||||
try {
|
||||
if(MH_rect_shortX != null)
|
||||
return (short)MH_rect_shortY.invokeExact(rect);
|
||||
|
|
|
|||
131
src/main/java/org/embeddedt/modernfix/util/DummyList.java
Normal file
131
src/main/java/org/embeddedt/modernfix/util/DummyList.java
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* List with no-op methods.
|
||||
*/
|
||||
public class DummyList<T> implements List<T> {
|
||||
@Override
|
||||
public int size() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return Collections.emptyIterator();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return new Object[0];
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <T1> T1[] toArray(@NotNull T1[] t1s) {
|
||||
return Arrays.copyOf(t1s, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(T t) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(@NotNull Collection<?> collection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(@NotNull Collection<? extends T> collection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int i, @NotNull Collection<? extends T> collection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(@NotNull Collection<?> collection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(@NotNull Collection<?> collection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(int i) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T set(int i, T t) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int i, T t) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public T remove(int i) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Object o) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastIndexOf(Object o) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ListIterator<T> listIterator() {
|
||||
return Collections.emptyListIterator();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ListIterator<T> listIterator(int i) {
|
||||
return Collections.emptyListIterator();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<T> subList(int i, int i1) {
|
||||
return new DummyList<>();
|
||||
}
|
||||
}
|
||||
|
|
@ -9,14 +9,34 @@ public class FileUtil {
|
|||
return file;
|
||||
}
|
||||
|
||||
private static final Pattern SLASH_PATTERN = Pattern.compile("(?:\\\\+|\\/+)");
|
||||
|
||||
/**
|
||||
* Normalize a path by removing double slashes, etc.
|
||||
* <p></p>
|
||||
* This implementation avoids creating a new string unless there are actually double slashes present
|
||||
* in the input path.
|
||||
* @param path input path
|
||||
* @return a normalized version of the path
|
||||
*/
|
||||
public static String normalize(String path) {
|
||||
return SLASH_PATTERN.matcher(path).replaceAll("/");
|
||||
char prevChar = 0;
|
||||
StringBuilder sb = null;
|
||||
for(int i = 0; i < path.length(); i++) {
|
||||
char thisChar = path.charAt(i);
|
||||
if(prevChar != '/' || thisChar != prevChar) {
|
||||
/* This character should end up in the final string. If we are using the builder, add it there. */
|
||||
if(sb != null)
|
||||
sb.append(thisChar);
|
||||
} else {
|
||||
/* This character should not end up in the final string. We need to make a buidler if we haven't
|
||||
* done so yet.
|
||||
*/
|
||||
if(sb == null) {
|
||||
sb = new StringBuilder(path.length());
|
||||
sb.append(path, 0, i);
|
||||
}
|
||||
}
|
||||
prevChar = thisChar;
|
||||
}
|
||||
return sb == null ? path : sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,4 +12,15 @@ public net.minecraft.client.Minecraft f_90999_ # progressListener
|
|||
public-f net.minecraft.network.syncher.EntityDataAccessor f_135010_ # id
|
||||
public-f net.minecraft.network.syncher.SynchedEntityData f_135345_ # itemsById
|
||||
public-f net.minecraft.network.syncher.SynchedEntityData f_135346_ # lock
|
||||
public net.minecraft.client.gui.screens.worldselection.WorldGenSettingsComponent m_233042_(Lnet/minecraft/client/gui/screens/worldselection/WorldCreationContext;)V # updateSettings
|
||||
public net.minecraft.client.gui.screens.worldselection.WorldGenSettingsComponent m_233042_(Lnet/minecraft/client/gui/screens/worldselection/WorldCreationContext;)V # updateSettings
|
||||
public net.minecraft.world.level.block.state.BlockBehaviour f_60439_ # properties
|
||||
public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60882_ # material
|
||||
public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60895_ # canOcclude
|
||||
public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60896_ # isAir
|
||||
public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60898_ # isRedstoneConductor
|
||||
public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60899_ # isSuffocating
|
||||
public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60900_ # isViewBlocking
|
||||
public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60901_ # hasPostProcess
|
||||
public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60902_ # emissiveRendering
|
||||
public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60889_ # requiresCorrectToolForDrops
|
||||
public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60888_ # destroyTime
|
||||
|
|
|
|||
|
|
@ -28,7 +28,11 @@
|
|||
"perf.fast_registry_validation.ForgeRegistryMixin",
|
||||
"perf.cache_strongholds.ChunkGeneratorMixin",
|
||||
"perf.cache_upgraded_structures.StructureManagerMixin",
|
||||
"perf.cache_strongholds.ServerLevelMixin"
|
||||
"perf.cache_strongholds.ServerLevelMixin",
|
||||
"perf.state_definition_construct.StateDefinitionMixin",
|
||||
"perf.compress_blockstate.BlockStateBaseMixin",
|
||||
"perf.compress_blockstate.BlockBehaviourMixin",
|
||||
"devenv.MinecraftServerMixin"
|
||||
],
|
||||
"client": [
|
||||
"core.MinecraftMixin",
|
||||
|
|
@ -36,15 +40,16 @@
|
|||
"feature.measure_time.MinecraftMixin",
|
||||
"feature.reduce_loading_screen_freezes.ModelBakeryMixin",
|
||||
"bugfix.concurrency.MinecraftMixin",
|
||||
"perf.parallelize_model_loading.BlockModelShaperMixin",
|
||||
"perf.parallelize_model_loading.ModelBakeryMixin",
|
||||
"perf.parallelize_model_loading.OBJLoaderMixin",
|
||||
"perf.parallelize_model_loading.multipart.MultipartMixin",
|
||||
"perf.parallelize_model_loading.multipart.VariantListMixin",
|
||||
"perf.parallelize_model_loading.SelectorMixin",
|
||||
"perf.parallelize_model_loading.TransformationMatrixMixin",
|
||||
"perf.parallelize_model_loading.BooleanPropertyMixin",
|
||||
"perf.parallelize_model_loading.PropertyMixin",
|
||||
"perf.dynamic_resources.BlockModelShaperMixin",
|
||||
"perf.dynamic_resources.ItemModelShaperMixin",
|
||||
"perf.dynamic_resources.ModelBakeryMixin",
|
||||
"perf.dynamic_resources.ctm.TextureMetadataHandlerMixin",
|
||||
"perf.dynamic_resources.ctm.CTMPackReloadListenerMixin",
|
||||
"perf.model_optimizations.OBJLoaderMixin",
|
||||
"perf.model_optimizations.SelectorMixin",
|
||||
"perf.model_optimizations.TransformationMatrixMixin",
|
||||
"perf.model_optimizations.BooleanPropertyMixin",
|
||||
"perf.model_optimizations.PropertyMixin",
|
||||
"perf.thread_priorities.IntegratedServerMixin",
|
||||
"safety.BlockColorsMixin",
|
||||
"perf.flatten_model_predicates.AndConditionMixin",
|
||||
|
|
@ -52,15 +57,14 @@
|
|||
"perf.flatten_model_predicates.PropertyValueConditionMixin",
|
||||
"perf.blast_search_trees.MinecraftMixin",
|
||||
"perf.blast_search_trees.IngredientFilterInvoker",
|
||||
"perf.faster_baking.ModelBakeryMixin",
|
||||
"perf.faster_baking.BlockModelShapesMixin",
|
||||
"perf.faster_baking.ModelManagerMixin",
|
||||
"perf.cache_model_materials.VanillaModelMixin",
|
||||
"perf.cache_model_materials.MultipartMixin",
|
||||
"perf.faster_texture_stitching.StitcherMixin",
|
||||
"perf.use_integrated_resources.LootTableHelperMixin",
|
||||
"perf.skip_first_datapack_reload.CreateWorldScreenMixin",
|
||||
"perf.faster_singleplayer_load.MinecraftServerMixin"
|
||||
"perf.faster_singleplayer_load.MinecraftServerMixin",
|
||||
"devenv.MinecraftMixin",
|
||||
"devenv.NarratorMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user