diff --git a/.gitignore b/.gitignore
index a3951e7f..c4d1b6c5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,9 @@ libs
media
classes/
+# Changelog
+CHANGELOG.md
+
# Created by https://www.gitignore.io/api/gradle,intellij,eclipse,windows,osx,linux
### Gradle ###
diff --git a/build.gradle b/build.gradle
index 69b647a7..93ee9737 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,6 +3,8 @@ plugins {
id "maven-publish"
id 'com.matthewprenger.cursegradle' version '1.4.0'
id 'com.palantir.git-version' version '1.0.0'
+ id 'se.bjurr.gitchangelog.git-changelog-gradle-plugin' version '1.79.0'
+ id "com.modrinth.minotaur" version "2.+"
}
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_17
@@ -63,6 +65,7 @@ repositories {
url "https://maven.tterrag.com/"
}
maven { url "https://maven.shedaniel.me" }
+ maven { url 'https://maven.blamejared.com' }
}
dependencies {
@@ -98,6 +101,8 @@ dependencies {
modRuntimeOnly("curse.maven:ferritecore-429235:4441949")
modCompileOnly("team.chisel.ctm:CTM:${ctm_version}")
modCompileOnly("curse.maven:supermartijncore-454372:4484241")
+ modCompileOnly("appeng:appliedenergistics2-forge:12.9.4")
+ modCompileOnly("vazkii.patchouli:Patchouli:1.19.2-77")
}
tasks.withType(JavaCompile) {
@@ -115,6 +120,22 @@ tasks.withType(JavaCompile) {
*/
}
+task generateChangelog(type: se.bjurr.gitchangelog.plugin.gradle.GitChangelogTask) {
+ def details = versionDetails();
+ if(details.commitDistance > 0) {
+ fromRef = details.lastTag;
+ } else {
+ def secondLastTagCmd = "git describe --abbrev=0 " + details.lastTag + "^"
+ def secondLastTag = secondLastTagCmd.execute().text.trim()
+ fromRef = secondLastTag;
+ }
+
+ file = new File("CHANGELOG.md");
+ def otherTemplateContent = new File('gradle/changelog.mustache').getText('UTF-8');
+ templateContent = "## Changes since " + fromRef + "\n" + otherTemplateContent;
+ toCommit = "HEAD";
+}
+
java {
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
@@ -160,12 +181,41 @@ curseforge {
apiKey = System.getenv("CURSEFORGE_TOKEN")
project {
id = "790626"
- changelog = '[Changelog is not currently available]'
+ changelog = file('./CHANGELOG.md')
changelogType = "markdown"
- releaseType = "beta"
+ releaseType = "release"
addGameVersion "Forge"
addGameVersion minecraft_version
mainArtifact remapJar
}
}
}
+
+modrinth {
+ token = System.getenv("MODRINTH_TOKEN")
+ projectId = "modernfix" // This can be the project ID or the slug. Either will work!
+ versionType = "release" // This is the default -- can also be `beta` or `alpha`
+ uploadFile = remapJar
+ gameVersions = [minecraft_version]
+ loaders = ["forge"]
+ changelog.set(provider { file("./CHANGELOG.md").getText('UTF-8') })
+}
+
+tasks.register('checkCleanTag') {
+ doLast {
+ def details = versionDetails()
+ if (!details.isCleanTag || versionDetails().commitDistance != 0) {
+ throw new GradleException('Not a clean tree.')
+ }
+ }
+}
+
+tasks.curseforge.dependsOn(":checkCleanTag")
+tasks.curseforge.dependsOn(":generateChangelog")
+tasks.modrinth.dependsOn(":checkCleanTag")
+tasks.modrinth.dependsOn(":generateChangelog")
+
+tasks.register('publishToModSites') {
+ publishToModSites.dependsOn(tasks.modrinth)
+ publishToModSites.dependsOn(tasks.curseforge)
+}
diff --git a/gradle/changelog.mustache b/gradle/changelog.mustache
new file mode 100644
index 00000000..b3175640
--- /dev/null
+++ b/gradle/changelog.mustache
@@ -0,0 +1,5 @@
+{{#commits}}
+{{#ifMatches messageTitle "^(?!Merge).*"}}
+ * [{{{messageTitle}}}](https://github.com/embeddedt/ModernFix/commit/{{hashFull}}) - {{{authorName}}}
+{{/ifMatches}}
+{{/commits}}
\ No newline at end of file
diff --git a/release.sh b/release.sh
new file mode 100755
index 00000000..092d47c8
--- /dev/null
+++ b/release.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+echo -n "Currently on: "
+git describe
+echo -n "New version: "
+read newtag
+git tag -a $newtag -m "$newtag"
+git push
+git push --tags
+./gradlew publishToModSites
diff --git a/src/main/java/org/embeddedt/modernfix/ModernFixClient.java b/src/main/java/org/embeddedt/modernfix/ModernFixClient.java
index 4270f094..06fc2635 100644
--- a/src/main/java/org/embeddedt/modernfix/ModernFixClient.java
+++ b/src/main/java/org/embeddedt/modernfix/ModernFixClient.java
@@ -27,14 +27,11 @@ import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
import net.minecraftforge.network.NetworkEvent;
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
import org.embeddedt.modernfix.core.config.ModernFixConfig;
-import org.embeddedt.modernfix.load.LoadEvents;
import org.embeddedt.modernfix.packet.EntityIDSyncPacket;
-import org.embeddedt.modernfix.screen.DeferredLevelLoadingScreen;
import org.embeddedt.modernfix.world.IntegratedWatchdog;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Field;
-import java.sql.Ref;
import java.util.*;
import java.util.function.Supplier;
@@ -51,9 +48,6 @@ public class ModernFixClient {
public ModernFixClient() {
// clear reserve as it's not needed
MemoryReserve.release();
- if(ModernFixMixinPlugin.instance.isOptionEnabled("perf.faster_singleplayer_load.ClientEvents")) {
- MinecraftForge.EVENT_BUS.register(new LoadEvents());
- }
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.branding.F3Screen")) {
Optional extends ModContainer> mfContainer = ModList.get().getModContainerById("modernfix");
if(mfContainer.isPresent())
@@ -93,7 +87,6 @@ public class ModernFixClient {
if(event.phase == TickEvent.Phase.END
&& recipesUpdated
&& tagsUpdated
- && !(Minecraft.getInstance().screen instanceof DeferredLevelLoadingScreen)
&& worldLoadStartTime != -1
&& Minecraft.getInstance().player != null
&& numRenderTicks++ >= 10) {
diff --git a/src/main/java/org/embeddedt/modernfix/agent/Agent.java b/src/main/java/org/embeddedt/modernfix/agent/Agent.java
deleted file mode 100644
index 5cf5298d..00000000
--- a/src/main/java/org/embeddedt/modernfix/agent/Agent.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package org.embeddedt.modernfix.agent;
-
-import com.google.common.collect.ImmutableMap;
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.tree.*;
-
-import java.lang.instrument.ClassFileTransformer;
-import java.lang.instrument.IllegalClassFormatException;
-import java.lang.instrument.Instrumentation;
-import java.security.ProtectionDomain;
-import java.util.function.Function;
-
-public class Agent {
- public static void agentmain(String args, Instrumentation instrumentation) {
- instrumentation.addTransformer(new EarlyTransformer());
- }
-
- private static class EarlyTransformer implements ClassFileTransformer {
-
- private static final ImmutableMap> TRANSFORMERS = ImmutableMap.>builder()
- .put("net/minecraftforge/fml/loading/moddiscovery/Scanner", EarlyTransformer::transformScanner)
- .build();
-
- private static ClassNode transformScanner(ClassNode input) {
- for(MethodNode method : input.methods) {
- if(method.name.equals("fileVisitor")) {
- for(int i = 0; i < method.instructions.size(); i++) {
- AbstractInsnNode ainsn = method.instructions.get(i);
- if(ainsn.getOpcode() == Opcodes.INVOKEVIRTUAL) {
- MethodInsnNode minsn = (MethodInsnNode)ainsn;
- if(minsn.name.equals("accept") && minsn.owner.equals("org/objectweb/asm/ClassReader")) {
- method.instructions.set(minsn.getPrevious(), new LdcInsnNode(ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES));
- return input;
- }
- }
- }
- }
- }
- return input;
- }
-
- @Override
- public byte[] transform(ClassLoader classLoader, String s, Class> aClass, ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException {
- Function func = TRANSFORMERS.get(s);
- if(func != null) {
- ClassReader reader = new ClassReader(bytes);
- ClassNode node = new ClassNode(Opcodes.ASM9);
- reader.accept(node, 0);
- node = func.apply(node);
- ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
- node.accept(writer);
- return writer.toByteArray();
- } else
- return bytes;
- }
- }
-}
diff --git a/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java b/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java
index f9df7610..df2538ac 100644
--- a/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java
+++ b/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java
@@ -41,6 +41,9 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin {
this.logger.info("Loaded configuration file for ModernFix: {} options available, {} override(s) found",
config.getOptionCount(), config.getOptionOverrideCount());
+ if(ModernFixEarlyConfig.OPTIFINE_PRESENT)
+ this.logger.fatal("OptiFine detected. Use of ModernFix with OptiFine is not supported due to its impact on launch time and breakage of Forge features.");
+
FastAccessTransformerList.attemptReplace();
DFUBlaster.blastMaps();
diff --git a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java
index 7afb9339..30d63335 100644
--- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java
+++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java
@@ -15,8 +15,23 @@ public class ModernFixEarlyConfig {
private final Map options = new HashMap<>();
+ public static final boolean OPTIFINE_PRESENT;
+
+ static {
+ boolean hasOfClass = false;
+ try {
+ Class.forName("optifine.OptiFineTransformationService");
+ hasOfClass = true;
+ } catch(Throwable e) {
+ }
+ OPTIFINE_PRESENT = hasOfClass;
+ }
+
private static boolean modPresent(String modId) {
- return FMLLoader.getLoadingModList().getModFileById(modId) != null;
+ if(modId.equals("optifine"))
+ return OPTIFINE_PRESENT;
+ else
+ return FMLLoader.getLoadingModList().getModFileById(modId) != null;
}
private ModernFixEarlyConfig() {
@@ -44,14 +59,19 @@ public class ModernFixEarlyConfig {
this.addMixinRule("perf.compress_blockstate", false);
this.addMixinRule("bugfix.concurrency", true);
this.addMixinRule("bugfix.edge_chunk_not_saved", true);
+ this.addMixinRule("perf.fast_forge_dummies", true);
this.addMixinRule("perf.dynamic_structure_manager", true);
this.addMixinRule("bugfix.chunk_deadlock", true);
this.addMixinRule("perf.thread_priorities", true);
this.addMixinRule("perf.scan_cache", true);
+ this.addMixinRule("perf.kubejs", modPresent("kubejs"));
this.addMixinRule("perf.flatten_model_predicates", true);
+ this.addMixinRule("perf.tag_id_caching", true);
this.addMixinRule("perf.deduplicate_location", false);
this.addMixinRule("perf.cache_blockstate_cache_arrays", true);
this.addMixinRule("perf.cache_model_materials", true);
+ this.addMixinRule("perf.nbt_memory_usage", true);
+ this.addMixinRule("perf.patchouli_deduplicate_books", modPresent("patchouli"));
this.addMixinRule("perf.datapack_reload_exceptions", true);
this.addMixinRule("perf.faster_texture_stitching", true);
/* off by default in 1.18 because it doesn't work as well */
@@ -59,7 +79,6 @@ public class ModernFixEarlyConfig {
/* Keep this off if JEI/REI isn't installed to prevent breaking vanilla gameplay */
this.addMixinRule("perf.blast_search_trees", FMLLoader.getLoadingModList().getModFileById("jei") != null || FMLLoader.getLoadingModList().getModFileById("roughlyenoughitems") != null);
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().getProvider() instanceof ExplodedDirectoryLocator;
this.addMixinRule("devenv", isDevEnv);
@@ -71,11 +90,12 @@ public class ModernFixEarlyConfig {
disableIfModPresent("mixin.perf.compress_biome_container", "chocolate", "betterendforge");
disableIfModPresent("mixin.bugfix.mc218112", "performant");
disableIfModPresent("mixin.perf.reuse_datapacks", "tac");
+ disableIfModPresent("mixin.launch.class_search_cache", "optifine");
}
private void disableIfModPresent(String configName, String... ids) {
for(String id : ids) {
- if(FMLLoader.getLoadingModList().getModFileById(id) != null) {
+ if(modPresent(id)) {
Option option = this.options.get(configName);
if(option != null)
option.addModOverride(false, id);
diff --git a/src/main/java/org/embeddedt/modernfix/duck/IExtendedModelBakery.java b/src/main/java/org/embeddedt/modernfix/duck/IExtendedModelBakery.java
index ffe5a8f8..50a325be 100644
--- a/src/main/java/org/embeddedt/modernfix/duck/IExtendedModelBakery.java
+++ b/src/main/java/org/embeddedt/modernfix/duck/IExtendedModelBakery.java
@@ -3,6 +3,7 @@ package org.embeddedt.modernfix.duck;
import com.google.common.collect.ImmutableList;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.ModelResourceLocation;
+import net.minecraft.client.resources.model.UnbakedModel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
@@ -11,4 +12,7 @@ import net.minecraft.world.level.block.state.StateDefinition;
public interface IExtendedModelBakery {
ImmutableList getBlockStatesForMRL(StateDefinition stateDefinition, ModelResourceLocation location);
BakedModel bakeDefault(ResourceLocation modelLocation);
+ BakedModel getBakedMissingModel();
+ void setBakedMissingModel(BakedModel m);
+ UnbakedModel mfix$getUnbakedMissingModel();
}
diff --git a/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java b/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java
index d7db7461..d9bc92b4 100644
--- a/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java
+++ b/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java
@@ -4,6 +4,7 @@ import com.mojang.math.Transformation;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.BlockModelRotation;
+import net.minecraft.client.resources.model.BuiltInModel;
import net.minecraft.client.resources.model.ModelBakery;
import net.minecraft.resources.ResourceLocation;
import org.apache.commons.lang3.tuple.Triple;
@@ -22,12 +23,19 @@ public class DynamicBakedModelProvider implements Map bakedCache;
private final Map permanentOverrides;
+ private BakedModel missingModel;
+ private static final BakedModel SENTINEL = new BuiltInModel(null, null, null, false);
public DynamicBakedModelProvider(ModelBakery bakery, Map cache) {
this.bakery = bakery;
this.bakedCache = cache;
this.permanentOverrides = new Object2ObjectOpenHashMap<>();
}
+
+ public void setMissingModel(BakedModel model) {
+ this.missingModel = model;
+ }
+
private static Triple vanillaKey(Object o) {
return Triple.of((ResourceLocation)o, BlockModelRotation.X0_Y0.getRotation(), false);
}
@@ -43,7 +51,7 @@ public class DynamicBakedModelProvider implements Map
- * TODO: The vanilla check that at least 441 chunks have been loaded does not check whether they are spawn chunks
- * or chunks loaded by the player. Consequently it is possible for loading to finish before every spawn chunk has
- * been loaded. However the chunk system has at least been warmed up by this point so the remaining chunks load
- * reasonably quickly.
- */
-public class LoadEvents {
- private boolean hasFirstPlayerJoined = false;
-
- @SubscribeEvent
- public void serverWillStart(ServerAboutToStartEvent event) {
- hasFirstPlayerJoined = false;
- }
-
- @SubscribeEvent(priority = EventPriority.LOWEST)
- public void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) {
- if(!hasFirstPlayerJoined && ModernFixMixinPlugin.instance.isOptionEnabled("perf.faster_singleplayer_load.ClientEvents")) {
- hasFirstPlayerJoined = true;
- MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
- if(server instanceof IntegratedServer) {
- handleInitialChunkLoad();
- }
- }
- }
-
- @SubscribeEvent(priority = EventPriority.LOWEST)
- public void onWorldShow(ScreenEvent.Opening event) {
- if(ServerLifecycleHooks.getCurrentServer() instanceof IntegratedServer) {
- if(event.getNewScreen() == null && Minecraft.getInstance().level != null && integratedWorldLoadListener != null) {
- /* this means the world is about to be displayed, check if 441 initialized */
- ServerChunkCache provider = ServerLifecycleHooks.getCurrentServer().overworld().getChunkSource();
- BooleanSupplier worldLoadDone = () -> provider.getTickingGenerated() >= 441;
- if(!worldLoadDone.getAsBoolean()) {
- DeferredLevelLoadingScreen newScreen = new DeferredLevelLoadingScreen(Minecraft.getInstance().progressListener.get(), worldLoadDone);
- event.setNewScreen(newScreen);
- }
- } else if(event.getNewScreen() instanceof LevelLoadingScreen && Minecraft.getInstance().level == null && ModernFixMixinPlugin.instance.isOptionEnabled("perf.faster_singleplayer_load.ClientEvents")) {
- ProgressScreen loadscreen = new ProgressScreen(false);
- loadscreen.progressStartNoAbort(Component.translatable("connect.joining"));
- event.setNewScreen(loadscreen);
- }
- }
- }
-
- public static ChunkProgressListener integratedWorldLoadListener;
-
- private void handleInitialChunkLoad() {
- MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
- ServerLevel overworld = server.overworld();
- ServerChunkCache provider = overworld.getChunkSource();
- provider.getLightEngine().setTaskPerBatch(500);
- provider.addRegionTicket(TicketType.START, new ChunkPos(overworld.getSharedSpawnPos()), 11, Unit.INSTANCE);
- while(provider.getTickingGenerated() < 441) {
- server.runAllTasks();
- Thread.yield();
- LockSupport.parkNanos("waiting for world load", 100000L);
- server.nextTickTime = Util.getMillis() + 10;
- }
- for(ServerLevel serverworld1 : server.getAllLevels()) {
- ForcedChunksSavedData forcedchunkssavedata = serverworld1.getDataStorage().get(ForcedChunksSavedData::load, "chunks");
- if (forcedchunkssavedata != null) {
- LongIterator longiterator = forcedchunkssavedata.getChunks().iterator();
-
- while(longiterator.hasNext()) {
- long i = longiterator.nextLong();
- ChunkPos chunkpos = new ChunkPos(i);
- serverworld1.getChunkSource().updateChunkForced(chunkpos, true);
- }
-
- ForgeChunkManager.reinstatePersistentChunks(serverworld1, forcedchunkssavedata);
- }
- }
- server.runAllTasks();
- server.nextTickTime = Util.getMillis() + 10;
- provider.getLightEngine().setTaskPerBatch(5);
- if(integratedWorldLoadListener != null) {
- integratedWorldLoadListener.stop();
- integratedWorldLoadListener = null;
- }
- }
-}
diff --git a/src/main/java/org/embeddedt/modernfix/mixin/devenv/NarratorMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/devenv/NarratorMixin.java
index 06e63d15..a4871a5b 100644
--- a/src/main/java/org/embeddedt/modernfix/mixin/devenv/NarratorMixin.java
+++ b/src/main/java/org/embeddedt/modernfix/mixin/devenv/NarratorMixin.java
@@ -1,7 +1,6 @@
package org.embeddedt.modernfix.mixin.devenv;
import com.mojang.text2speech.Narrator;
-import com.mojang.text2speech.NarratorDummy;
import net.minecraft.client.GameNarrator;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -11,6 +10,6 @@ import org.spongepowered.asm.mixin.injection.Redirect;
public class NarratorMixin {
@Redirect(method = "", at = @At(value = "INVOKE", target = "Lcom/mojang/text2speech/Narrator;getNarrator()Lcom/mojang/text2speech/Narrator;", remap = false))
private Narrator useDummyNarrator() {
- return new NarratorDummy();
+ return Narrator.EMPTY;
}
}
diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ModelBakerImplMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ModelBakerImplMixin.java
index 305f3ba2..994b2f2d 100644
--- a/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ModelBakerImplMixin.java
+++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ModelBakerImplMixin.java
@@ -8,6 +8,8 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.common.MinecraftForge;
import org.apache.commons.lang3.tuple.Triple;
import org.embeddedt.modernfix.ModernFix;
+import org.embeddedt.modernfix.duck.IExtendedModelBakery;
+import org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider;
import org.embeddedt.modernfix.dynamicresources.DynamicModelBakeEvent;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
@@ -46,9 +48,18 @@ public abstract class ModelBakerImplMixin {
}
}
if(ibakedmodel == null) {
- ibakedmodel = iunbakedmodel.bake((ModelBaker)this, textureGetter, arg2, arg);
+ IExtendedModelBakery extendedBakery = (IExtendedModelBakery)this.field_40571;
+ if(iunbakedmodel == extendedBakery.mfix$getUnbakedMissingModel()) {
+ // use a shared baked missing model
+ if(extendedBakery.getBakedMissingModel() == null) {
+ extendedBakery.setBakedMissingModel(iunbakedmodel.bake((ModelBaker)this, textureGetter, arg2, arg));
+ ((DynamicBakedModelProvider)this.field_40571.getBakedTopLevelModels()).setMissingModel(extendedBakery.getBakedMissingModel());
+ }
+ ibakedmodel = extendedBakery.getBakedMissingModel();
+ } else
+ ibakedmodel = iunbakedmodel.bake((ModelBaker)this, textureGetter, arg2, arg);
}
- DynamicModelBakeEvent event = new DynamicModelBakeEvent(arg, iunbakedmodel, ibakedmodel, (ModelBaker)this);
+ DynamicModelBakeEvent event = new DynamicModelBakeEvent(arg, iunbakedmodel, ibakedmodel, (ModelBaker)this, this.field_40571);
MinecraftForge.EVENT_BUS.post(event);
this.field_40571.bakedCache.put(key, event.getModel());
cir.setReturnValue(event.getModel());
diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ModelBakeryMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ModelBakeryMixin.java
index b8138a75..5b20d4f1 100644
--- a/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ModelBakeryMixin.java
+++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ModelBakeryMixin.java
@@ -6,6 +6,7 @@ import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalNotification;
import com.google.common.collect.ImmutableList;
import com.mojang.math.Transformation;
+import net.minecraft.client.color.block.BlockColors;
import net.minecraft.client.renderer.block.model.BlockModel;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.*;
@@ -19,6 +20,7 @@ import org.apache.commons.lang3.tuple.Triple;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider;
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
+import org.objectweb.asm.Opcodes;
import org.slf4j.Logger;
import org.spongepowered.asm.mixin.*;
import org.spongepowered.asm.mixin.injection.At;
@@ -63,14 +65,17 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
@Shadow @Final @Mutable private Map bakedCache;
+ @Shadow @Final @Mutable private BlockColors blockColors;
private Cache loadedBakedModels;
+
private Cache loadedModels;
private HashMap smallLoadingCache = new HashMap<>();
- @Redirect(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiling/ProfilerFiller;push(Ljava/lang/String;)V", ordinal = 0))
- private void replaceTopLevelBakedModels(ProfilerFiller profiler, String arg) {
+ @Redirect(method = "", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/resources/model/ModelBakery;blockColors:Lnet/minecraft/client/color/block/BlockColors;"))
+ private void replaceTopLevelBakedModels(ModelBakery bakery, BlockColors val) {
+ this.blockColors = val;
this.loadedBakedModels = CacheBuilder.newBuilder()
.expireAfterAccess(3, TimeUnit.MINUTES)
.maximumSize(1000)
@@ -88,7 +93,6 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
this.bakedCache = loadedBakedModels.asMap();
this.unbakedCache = loadedModels.asMap();
this.bakedTopLevelModels = new DynamicBakedModelProvider((ModelBakery)(Object)this, bakedCache);
- profiler.push(arg);
}
private void onModelRemoved(RemovalNotification notification) {
@@ -276,4 +280,18 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
public ImmutableList getBlockStatesForMRL(StateDefinition stateDefinition, ModelResourceLocation location) {
return loadOnlyRelevantBlockState(stateDefinition, location);
}
+
+ private BakedModel bakedMissingModel = null;
+
+ public void setBakedMissingModel(BakedModel m) {
+ bakedMissingModel = m;
+ }
+
+ public BakedModel getBakedMissingModel() {
+ return bakedMissingModel;
+ }
+
+ public UnbakedModel mfix$getUnbakedMissingModel() {
+ return missingModel;
+ }
}
diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ae2/RegistrationMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ae2/RegistrationMixin.java
new file mode 100644
index 00000000..7583b1c8
--- /dev/null
+++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ae2/RegistrationMixin.java
@@ -0,0 +1,38 @@
+package org.embeddedt.modernfix.mixin.perf.dynamic_resources.ae2;
+
+import appeng.core.AppEng;
+import appeng.init.client.InitAutoRotatingModel;
+import net.minecraft.client.resources.model.BakedModel;
+import net.minecraft.client.resources.model.ModelBakery;
+import net.minecraftforge.common.MinecraftForge;
+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 org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import java.util.Map;
+import java.util.function.Function;
+
+@Mixin(InitAutoRotatingModel.class)
+public class RegistrationMixin {
+ @Shadow @Final private static Map> CUSTOMIZERS;
+ @Inject(method = "init", at = @At("TAIL"), remap = false)
+ private static void doRegisterDynBake(CallbackInfo ci) {
+ MinecraftForge.EVENT_BUS.addListener(RegistrationMixin::onDynamicModelBake);
+ }
+
+ private static void onDynamicModelBake(DynamicModelBakeEvent event) {
+ if (!event.getLocation().getNamespace().equals(AppEng.MOD_ID)) {
+ return;
+ }
+ BakedModel missing = event.getModelBakery().getBakedTopLevelModels().get(ModelBakery.MISSING_MODEL_LOCATION);
+ if(event.getModel() == missing)
+ return;
+ Function customizerFn = CUSTOMIZERS.get(event.getLocation().getPath());
+ if(customizerFn != null)
+ event.setModel(customizerFn.apply(event.getModel()));
+ }
+}
diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/fast_forge_dummies/NamespacedHolderHelperMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/fast_forge_dummies/NamespacedHolderHelperMixin.java
new file mode 100644
index 00000000..49159495
--- /dev/null
+++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/fast_forge_dummies/NamespacedHolderHelperMixin.java
@@ -0,0 +1,46 @@
+package org.embeddedt.modernfix.mixin.perf.fast_forge_dummies;
+
+import com.mojang.serialization.Lifecycle;
+import net.minecraft.core.Holder;
+import net.minecraft.core.MappedRegistry;
+import net.minecraft.core.Registry;
+import net.minecraft.resources.ResourceKey;
+import net.minecraft.resources.ResourceLocation;
+import org.jetbrains.annotations.Nullable;
+import org.objectweb.asm.Opcodes;
+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.callback.CallbackInfoReturnable;
+
+import java.util.Map;
+import java.util.function.Function;
+
+@Mixin(targets = { "net/minecraftforge/registries/NamespacedWrapper" })
+public abstract class NamespacedHolderHelperMixin extends MappedRegistry {
+ @Shadow private Map> holdersByName;
+
+ public NamespacedHolderHelperMixin(ResourceKey extends Registry> arg, Lifecycle lifecycle) {
+ super(arg, lifecycle);
+ }
+
+ @Inject(method = "freeze", at = @At(value = "FIELD", opcode = Opcodes.GETFIELD, target = "Lnet/minecraftforge/registries/NamespacedWrapper;holdersByName:Ljava/util/Map;"), cancellable = true, remap = false)
+ private void fastDummyCheck(CallbackInfoReturnable> cir) {
+ // Quickly iterate without making any streams, etc. to see if everything is fine
+ // Use the slow path (by returning without cancelling) when there is an error
+ for(Holder.Reference ref : this.holdersByName.values()) {
+ if(!ref.isBound())
+ return;
+ }
+ if (this.unregisteredIntrusiveHolders != null) {
+ for(Holder.Reference ref : this.unregisteredIntrusiveHolders.values()) {
+ if(ref.getType() == Holder.Reference.Type.INTRUSIVE && !ref.isBound())
+ return;
+ }
+ }
+ // Skip the creation of streams
+ cir.setReturnValue(this);
+ }
+}
diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_singleplayer_load/MinecraftServerMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_singleplayer_load/MinecraftServerMixin.java
deleted file mode 100644
index ad0b11a0..00000000
--- a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_singleplayer_load/MinecraftServerMixin.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.embeddedt.modernfix.mixin.perf.faster_singleplayer_load;
-
-import net.minecraft.Util;
-import net.minecraft.client.server.IntegratedServer;
-import net.minecraft.server.MinecraftServer;
-import net.minecraft.server.level.ServerChunkCache;
-import net.minecraft.server.level.ServerLevel;
-import net.minecraft.server.level.progress.ChunkProgressListener;
-import org.embeddedt.modernfix.ModernFixClient;
-import org.embeddedt.modernfix.load.LoadEvents;
-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;
-
-@Mixin(MinecraftServer.class)
-public abstract class MinecraftServerMixin {
- @Shadow protected long nextTickTime;
-
- @Shadow public abstract ServerLevel overworld();
-
- @Shadow protected abstract void updateMobSpawningFlags();
-
- /**
- * @author embeddedt
- * @reason defer the 441 chunk load until *after* join game packets are sent to the client, in order to allow
- * mods that process advancements, etc. to work on that at the same time
- */
- @Inject(method = "prepareLevels", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;getChunkSource()Lnet/minecraft/server/level/ServerChunkCache;", ordinal = 0), cancellable = true)
- private void skipInitialChunkLoad(ChunkProgressListener arg, CallbackInfo ci) {
- if(((Object)this) instanceof IntegratedServer) {
- ci.cancel();
- LoadEvents.integratedWorldLoadListener = arg;
- this.nextTickTime = Util.getMillis();
- this.overworld().getChunkSource().getLightEngine().setTaskPerBatch(5);
- this.updateMobSpawningFlags();
- }
- }
-}
diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/AndConditionMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/AndConditionMixin.java
deleted file mode 100644
index 94283f0a..00000000
--- a/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/AndConditionMixin.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.embeddedt.modernfix.mixin.perf.flatten_model_predicates;
-
-import com.google.common.collect.Streams;
-import net.minecraft.world.level.block.Block;
-import net.minecraft.world.level.block.state.BlockState;
-import net.minecraft.client.renderer.block.model.multipart.AndCondition;
-import net.minecraft.client.renderer.block.model.multipart.Condition;
-import net.minecraft.world.level.block.state.StateDefinition;
-import org.embeddedt.modernfix.predicate.StatePropertyPredicateHelper;
-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.function.Predicate;
-import java.util.stream.Collectors;
-
-@Mixin(AndCondition.class)
-public class AndConditionMixin {
- @Shadow @Final private Iterable extends Condition> conditions;
-
- /**
- * @author JellySquid
- * @reason Flatten predicates
- */
- @Overwrite
- public Predicate getPredicate(StateDefinition stateManager) {
- return StatePropertyPredicateHelper.allMatch(Streams.stream(this.conditions).map((multipartModelSelector) -> {
- return multipartModelSelector.getPredicate(stateManager);
- }).collect(Collectors.toList()));
- }
-}
diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/OrConditionMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/OrConditionMixin.java
deleted file mode 100644
index f336a997..00000000
--- a/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/OrConditionMixin.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.embeddedt.modernfix.mixin.perf.flatten_model_predicates;
-
-import com.google.common.collect.Streams;
-import net.minecraft.world.level.block.Block;
-import net.minecraft.world.level.block.state.BlockState;
-import net.minecraft.client.renderer.block.model.multipart.Condition;
-import net.minecraft.client.renderer.block.model.multipart.OrCondition;
-import net.minecraft.world.level.block.state.StateDefinition;
-import org.embeddedt.modernfix.predicate.StatePropertyPredicateHelper;
-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.function.Predicate;
-import java.util.stream.Collectors;
-
-@Mixin(OrCondition.class)
-public class OrConditionMixin {
- @Shadow @Final private Iterable extends Condition> conditions;
-
- /**
- * @author JellySquid
- * @reason Flatten predicates
- */
- @Overwrite
- public Predicate getPredicate(StateDefinition stateManager) {
- return StatePropertyPredicateHelper.anyMatch(Streams.stream(this.conditions).map((multipartModelSelector) -> {
- return multipartModelSelector.getPredicate(stateManager);
- }).collect(Collectors.toList()));
- }
-}
diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/PropertyValueConditionMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/PropertyValueConditionMixin.java
deleted file mode 100644
index 80c8991d..00000000
--- a/src/main/java/org/embeddedt/modernfix/mixin/perf/flatten_model_predicates/PropertyValueConditionMixin.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package org.embeddedt.modernfix.mixin.perf.flatten_model_predicates;
-
-import com.google.common.base.Splitter;
-import net.minecraft.world.level.block.Block;
-import net.minecraft.world.level.block.state.BlockState;
-import net.minecraft.client.renderer.block.model.multipart.KeyValueCondition;
-import net.minecraft.world.level.block.state.properties.Property;
-import net.minecraft.world.level.block.state.StateDefinition;
-import org.embeddedt.modernfix.predicate.single.SingleMatchAny;
-import org.embeddedt.modernfix.predicate.single.SingleMatchOne;
-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.List;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-
-@Mixin(KeyValueCondition.class)
-public class PropertyValueConditionMixin {
- @Shadow @Final private String key;
-
- @Shadow @Final private String value;
-
- @Shadow @Final private static Splitter PIPE_SPLITTER;
-
- /**
- * @author JellySquid
- * @reason De-duplication
- */
- @Overwrite
- public Predicate getPredicate(StateDefinition stateManager) {
- Property> property = stateManager.getProperty(this.key);
-
- if (property == null) {
- throw new RuntimeException(String.format("Unknown property '%s' on '%s'", this.key, stateManager.getOwner().toString()));
- }
-
- String valueString = this.value;
- boolean negate = !valueString.isEmpty() && valueString.charAt(0) == '!';
-
- if (negate) {
- valueString = valueString.substring(1);
- }
-
- List split = PIPE_SPLITTER.splitToList(valueString);
-
- if (split.isEmpty()) {
- throw new RuntimeException(String.format("Empty value '%s' for property '%s' on '%s'", this.value, this.key, stateManager.getOwner().toString()));
- }
-
- Predicate predicate;
-
- if (split.size() == 1) {
- predicate = new SingleMatchOne(property, this.getPropertyValue(stateManager, property, valueString));
- } else {
- predicate = SingleMatchAny.create(property, split.stream()
- .map(str -> this.getPropertyValue(stateManager, property, str))
- .collect(Collectors.toList()));
- }
-
- return negate ? predicate.negate() : predicate;
- }
-
- private Object getPropertyValue(StateDefinition stateFactory, Property> property, String valueString) {
- Object value = property.getValue(valueString)
- .orElse(null);
-
- if (value == null) {
- throw new RuntimeException(String.format("Unknown value '%s' for property '%s' on '%s' in '%s'",
- valueString, this.key, stateFactory.getOwner().toString(), this.value));
- }
-
- return value;
- }
-}
diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/kubejs/RecipeEventJSMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/kubejs/RecipeEventJSMixin.java
new file mode 100644
index 00000000..ffbd0e26
--- /dev/null
+++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/kubejs/RecipeEventJSMixin.java
@@ -0,0 +1,50 @@
+package org.embeddedt.modernfix.mixin.perf.kubejs;
+
+import dev.latvian.mods.kubejs.recipe.RecipesEventJS;
+import org.embeddedt.modernfix.ModernFix;
+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;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.Map;
+
+@Mixin(RecipesEventJS.class)
+public class RecipeEventJSMixin {
+
+ /**
+ * The recipe event object can be leaked in scripts and this wastes 40MB of memory.
+ */
+ @Inject(method = "post", at = @At("RETURN"), remap = false)
+ private void clearRecipeLists(CallbackInfo ci) {
+ ModernFix.LOGGER.info("Clearing KubeJS recipe lists...");
+ // Even though we are a mixin class, use reflection so this works across a variety of versions
+ Field[] fields = RecipesEventJS.class.getDeclaredFields();
+ for(Field f : fields) {
+ try {
+ if(!Modifier.isStatic(f.getModifiers())
+ && (Collection.class.isAssignableFrom(f.getType())
+ || Map.class.isAssignableFrom(f.getType()))
+ ) {
+ f.setAccessible(true);
+ Object collection = f.get(this);
+ int size;
+ if(collection instanceof Map) {
+ size = ((Map, ?>)collection).size();
+ ((Map, ?>)collection).clear();
+ } else if(collection instanceof Collection) {
+ size = ((Collection>)collection).size();
+ ((Collection>)collection).clear();
+ } else
+ throw new IllegalStateException();
+ ModernFix.LOGGER.debug("Cleared {} with {} entries", f.getName(), size);
+ }
+ } catch(RuntimeException | ReflectiveOperationException e) {
+ ModernFix.LOGGER.debug("Uh oh, couldn't clear field", e);
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/modern_resourcepacks/VanillaPackResourcesMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/modern_resourcepacks/VanillaPackResourcesMixin.java
index 68f0a216..8de18d38 100644
--- a/src/main/java/org/embeddedt/modernfix/mixin/perf/modern_resourcepacks/VanillaPackResourcesMixin.java
+++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/modern_resourcepacks/VanillaPackResourcesMixin.java
@@ -1,22 +1,84 @@
package org.embeddedt.modernfix.mixin.perf.modern_resourcepacks;
-import net.minecraft.resources.ResourceLocation;
+import com.google.common.base.Joiner;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.LoadingCache;
import net.minecraft.server.packs.PackType;
+import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.VanillaPackResources;
+import net.minecraft.server.packs.metadata.pack.PackMetadataSection;
+import org.apache.commons.lang3.tuple.Pair;
+import org.embeddedt.modernfix.FileWalker;
+import org.embeddedt.modernfix.util.FileUtil;
+import org.embeddedt.modernfix.util.PackTypeHelper;
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.Redirect;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Map;
+import java.nio.file.*;
+import java.util.*;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Stream;
@Mixin(VanillaPackResources.class)
public class VanillaPackResourcesMixin {
@Shadow @Final private static Map ROOT_DIR_BY_TYPE;
+ private static LoadingCache, List> pathStreamLoadingCache = CacheBuilder.newBuilder()
+ .build(FileWalker.INSTANCE);
+
+ private static Set containedPaths = null;
+
+ //@Inject(method = "", at = @At("TAIL"))
+ private void cacheContainedPaths(PackMetadataSection arg, String[] p_i47912_1_, CallbackInfo ci) {
+ if(containedPaths != null)
+ return;
+ containedPaths = new HashSet<>();
+ Joiner slashJoiner = Joiner.on('/');
+ for(PackType type : PackType.values()) {
+ if(!PackTypeHelper.isVanillaPackType(type))
+ continue;
+ Path root = ROOT_DIR_BY_TYPE.get(type);
+ if(root == null)
+ throw new IllegalStateException("No filesystem for vanilla " + type.name() + " assets");
+ try {
+ try(Stream stream = Files.walk(root)) {
+ stream
+ .map(path -> root.relativize(path.toAbsolutePath()))
+ .forEach(path -> containedPaths.add(slashJoiner.join(type.getDirectory(), path)));
+ }
+ } catch(IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ //@Redirect(method = "getResources(Ljava/util/Collection;Ljava/lang/String;Ljava/nio/file/Path;Ljava/lang/String;Ljava/util/function/Predicate;)V", at = @At(value = "INVOKE", target = "Ljava/nio/file/Files;walk(Ljava/nio/file/Path;[Ljava/nio/file/FileVisitOption;)Ljava/util/stream/Stream;"))
+ private static Stream useCacheForLoading(Path path, FileVisitOption[] fileVisitOptions) throws IOException {
+ try {
+ return pathStreamLoadingCache.get(Pair.of(path, Integer.MAX_VALUE)).stream();
+ } catch (ExecutionException e) {
+ if(e.getCause() instanceof IOException) /* generally always should be */
+ throw (IOException)e.getCause();
+ else
+ throw new IOException(e);
+ }
+ }
+
+ //@Inject(method = "hasResource", at = @At(value = "INVOKE", target = "Ljava/lang/Class;getResource(Ljava/lang/String;)Ljava/net/URL;"), cancellable = true)
+ private void useCacheForExistence(PackType type, ResourceLocation location, CallbackInfoReturnable cir) {
+ if(!PackTypeHelper.isVanillaPackType(type))
+ return;
+ cir.setReturnValue(containedPaths.contains(type.getDirectory() + "/" + location.getNamespace() + "/" + FileUtil.normalize(location.getPath())));
+ }
/**
* @author embeddedt
diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/nbt_memory_usage/CompoundTagMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/nbt_memory_usage/CompoundTagMixin.java
new file mode 100644
index 00000000..348a8f4b
--- /dev/null
+++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/nbt_memory_usage/CompoundTagMixin.java
@@ -0,0 +1,45 @@
+package org.embeddedt.modernfix.mixin.perf.nbt_memory_usage;
+
+import com.google.common.collect.Maps;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.Tag;
+import org.embeddedt.modernfix.util.CanonizingStringMap;
+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.ModifyArg;
+import org.spongepowered.asm.mixin.injection.Redirect;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+import java.util.Map;
+
+@Mixin(CompoundTag.class)
+public class CompoundTagMixin {
+ @Shadow @Final @Mutable
+ private Map tags;
+
+ /**
+ * Ensure that the backing map is always a CanonizingStringMap.
+ */
+ @Redirect(method = "(Ljava/util/Map;)V", at = @At(value = "FIELD", target = "Lnet/minecraft/nbt/CompoundTag;tags:Ljava/util/Map;", ordinal = 0))
+ private void replaceTagMap(CompoundTag tag, Map incomingMap) {
+ if(incomingMap instanceof CanonizingStringMap)
+ this.tags = incomingMap;
+ else {
+ this.tags = new CanonizingStringMap<>();
+ this.tags.putAll(incomingMap);
+ }
+ }
+
+ /**
+ * @author embeddedt
+ * @reason use more efficient method when copying canonizing string map
+ */
+ @Inject(method = "copy()Lnet/minecraft/nbt/CompoundTag;", at = @At("HEAD"), cancellable = true)
+ public void copyEfficient(CallbackInfoReturnable cir) {
+ if(this.tags instanceof CanonizingStringMap) {
+ cir.setReturnValue(new CompoundTag(CanonizingStringMap.deepCopy((CanonizingStringMap)this.tags, Tag::copy)));
+ }
+ }
+}
diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/patchouli_deduplicate_books/ClientBookRegistryMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/patchouli_deduplicate_books/ClientBookRegistryMixin.java
new file mode 100644
index 00000000..971bf43e
--- /dev/null
+++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/patchouli_deduplicate_books/ClientBookRegistryMixin.java
@@ -0,0 +1,70 @@
+package org.embeddedt.modernfix.mixin.perf.patchouli_deduplicate_books;
+
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.Items;
+import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
+import org.embeddedt.modernfix.ModernFix;
+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;
+import vazkii.patchouli.client.book.BookContents;
+import vazkii.patchouli.client.book.BookEntry;
+import vazkii.patchouli.client.book.BookPage;
+import vazkii.patchouli.client.book.ClientBookRegistry;
+import vazkii.patchouli.client.book.page.PageTemplate;
+import vazkii.patchouli.client.book.template.BookTemplate;
+import vazkii.patchouli.client.book.template.TemplateComponent;
+import vazkii.patchouli.client.book.template.component.ComponentItemStack;
+import vazkii.patchouli.common.book.Book;
+import vazkii.patchouli.common.book.BookRegistry;
+
+import java.lang.reflect.Field;
+import java.util.List;
+
+@Mixin(ClientBookRegistry.class)
+public class ClientBookRegistryMixin {
+ @Inject(method = "reload", at = @At("RETURN"), remap = false)
+ private void performDeduplication(CallbackInfo ci) {
+ Field templateField = ObfuscationReflectionHelper.findField(PageTemplate.class, "template");
+ Field contentsField = ObfuscationReflectionHelper.findField(Book.class, "contents");
+ Field componentsField = ObfuscationReflectionHelper.findField(BookTemplate.class, "components");
+ Field itemsField = ObfuscationReflectionHelper.findField(ComponentItemStack.class, "items");
+ int numItemsCleared = 0;
+ for(Book book : BookRegistry.INSTANCE.books.values()) {
+ try {
+ BookContents contents = (BookContents)contentsField.get(book);
+ if(contents == null || contents.entries == null)
+ continue;
+ for(BookEntry entry : contents.entries.values()) {
+ for(BookPage page : entry.getPages()) {
+ if(page instanceof PageTemplate) {
+ List components;
+ BookTemplate template = (BookTemplate) templateField.get(page);
+ if(template == null)
+ continue;
+ components = (List) componentsField.get(template);
+ if(components == null)
+ continue;
+ for (TemplateComponent component : components) {
+ if (component instanceof ComponentItemStack) {
+ ItemStack[] items = (ItemStack[]) itemsField.get(component);
+ if(items == null)
+ continue;
+ for (int i = 0; i < items.length; i++) {
+ if (items[i] != null && items[i].getItem() == Items.AIR) {
+ numItemsCleared++;
+ items[i] = ItemStack.EMPTY;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } catch(ReflectiveOperationException ignored) {
+ }
+ }
+ ModernFix.LOGGER.info("Cleared {} unneeded book NBT tags", numItemsCleared);
+ }
+}
diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagEntryMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagEntryMixin.java
new file mode 100644
index 00000000..82ffcda6
--- /dev/null
+++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagEntryMixin.java
@@ -0,0 +1,30 @@
+package org.embeddedt.modernfix.mixin.perf.tag_id_caching;
+
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.tags.TagEntry;
+import net.minecraft.util.ExtraCodecs;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Overwrite;
+import org.spongepowered.asm.mixin.Shadow;
+
+@Mixin(TagEntry.class)
+public class TagEntryMixin {
+ @Shadow @Final private boolean tag;
+ @Shadow @Final private ResourceLocation id;
+ private ExtraCodecs.TagOrElementLocation cachedLoc;
+
+ /**
+ * @author embeddedt
+ * @reason use cached location, overwrite rather than inject to avoid allocs
+ */
+ @Overwrite
+ public ExtraCodecs.TagOrElementLocation elementOrTag() {
+ ExtraCodecs.TagOrElementLocation loc = cachedLoc;
+ if(loc == null) {
+ loc = new ExtraCodecs.TagOrElementLocation(this.id, this.tag);
+ cachedLoc = loc;
+ }
+ return loc;
+ }
+}
diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagOrElementLocationMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagOrElementLocationMixin.java
new file mode 100644
index 00000000..6b3e1d55
--- /dev/null
+++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/tag_id_caching/TagOrElementLocationMixin.java
@@ -0,0 +1,29 @@
+package org.embeddedt.modernfix.mixin.perf.tag_id_caching;
+
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.util.ExtraCodecs;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Overwrite;
+import org.spongepowered.asm.mixin.Shadow;
+
+@Mixin(ExtraCodecs.TagOrElementLocation.class)
+public class TagOrElementLocationMixin {
+ @Shadow @Final private boolean tag;
+ @Shadow @Final private ResourceLocation id;
+ private String cachedDecoratedId;
+
+ /**
+ * @author embeddedt
+ * @reason use cached ID, overwrite rather than inject to avoid allocs
+ */
+ @Overwrite
+ public String decoratedId() {
+ String id = cachedDecoratedId;
+ if(id == null) {
+ id = this.tag ? "#" + this.id : this.id.toString();
+ cachedDecoratedId = id;
+ }
+ return id;
+ }
+}
diff --git a/src/main/java/org/embeddedt/modernfix/predicate/AllPredicate.java b/src/main/java/org/embeddedt/modernfix/predicate/AllPredicate.java
deleted file mode 100644
index e741e58a..00000000
--- a/src/main/java/org/embeddedt/modernfix/predicate/AllPredicate.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.embeddedt.modernfix.predicate;
-
-import java.util.function.Predicate;
-
-public class AllPredicate implements Predicate {
- private final Predicate[] predicates;
-
- public AllPredicate(Predicate[] predicates) {
- this.predicates = predicates;
- }
-
- @Override
- public boolean test(T t) {
- for (Predicate predicate : this.predicates) {
- if (!predicate.test(t)) {
- return false;
- }
- }
-
- return true;
- }
-}
diff --git a/src/main/java/org/embeddedt/modernfix/predicate/AnyPredicate.java b/src/main/java/org/embeddedt/modernfix/predicate/AnyPredicate.java
deleted file mode 100644
index 7c04f5fb..00000000
--- a/src/main/java/org/embeddedt/modernfix/predicate/AnyPredicate.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.embeddedt.modernfix.predicate;
-
-import java.util.function.Predicate;
-
-public class AnyPredicate implements Predicate {
- private final Predicate[] predicates;
-
- public AnyPredicate(Predicate[] predicates) {
- this.predicates = predicates;
- }
-
- @Override
- public boolean test(T t) {
- for (Predicate predicate : this.predicates) {
- if (predicate.test(t)) {
- return true;
- }
- }
-
- return false;
- }
-}
diff --git a/src/main/java/org/embeddedt/modernfix/predicate/CachedModelPredicate.java b/src/main/java/org/embeddedt/modernfix/predicate/CachedModelPredicate.java
deleted file mode 100644
index 8c5a4566..00000000
--- a/src/main/java/org/embeddedt/modernfix/predicate/CachedModelPredicate.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.embeddedt.modernfix.predicate;
-
-/**
- * Calculates the
- */
-public class CachedModelPredicate {
-}
diff --git a/src/main/java/org/embeddedt/modernfix/predicate/StatePropertyPredicateHelper.java b/src/main/java/org/embeddedt/modernfix/predicate/StatePropertyPredicateHelper.java
deleted file mode 100644
index 8125e307..00000000
--- a/src/main/java/org/embeddedt/modernfix/predicate/StatePropertyPredicateHelper.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.embeddedt.modernfix.predicate;
-
-import net.minecraft.world.level.block.state.BlockState;
-import org.embeddedt.modernfix.predicate.all.AllMatchOneBoolean;
-import org.embeddedt.modernfix.predicate.all.AllMatchOneObject;
-import org.embeddedt.modernfix.predicate.any.AllMatchAnyObject;
-import org.embeddedt.modernfix.predicate.single.SingleMatchAny;
-import org.embeddedt.modernfix.predicate.single.SingleMatchOne;
-
-import java.util.List;
-import java.util.function.Predicate;
-
-public class StatePropertyPredicateHelper {
- @SuppressWarnings("unchecked")
- public static Predicate allMatch(List> predicates) {
- if (SingleMatchOne.areOfType(predicates)) {
- if (SingleMatchOne.valuesMatchType(predicates, Boolean.class)) {
- return new AllMatchOneBoolean(predicates);
- }
-
- return new AllMatchOneObject(predicates);
- } else if (SingleMatchAny.areOfType(predicates)) {
- return new AllMatchAnyObject(predicates);
- }
-
- return new AllPredicate<>(predicates.toArray(new Predicate[0]));
- }
-
- @SuppressWarnings("unchecked")
- public static Predicate anyMatch(List> predicates) {
- return new AnyPredicate<>(predicates.toArray(new Predicate[0]));
- }
-}
diff --git a/src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneBoolean.java b/src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneBoolean.java
deleted file mode 100644
index 06984eb1..00000000
--- a/src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneBoolean.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package org.embeddedt.modernfix.predicate.all;
-
-import net.minecraft.world.level.block.state.BlockState;
-import net.minecraft.world.level.block.state.properties.Property;
-import org.embeddedt.modernfix.predicate.single.SingleMatchOne;
-
-import java.util.List;
-import java.util.function.Predicate;
-
-public class AllMatchOneBoolean implements Predicate {
- private final Property>[] properties;
- private final boolean[] values;
-
- public AllMatchOneBoolean(List> list) {
- int size = list.size();
-
- this.properties = new Property[size];
- this.values = new boolean[size];
-
- for (int i = 0; i < size; i++) {
- SingleMatchOne predicate = (SingleMatchOne) list.get(i);
-
- this.properties[i] = predicate.property;
- this.values[i] = (boolean) predicate.value;
- }
- }
-
- public static boolean canReplace(List> list) {
- return list.stream()
- .allMatch(p -> {
- return p instanceof SingleMatchOne && ((SingleMatchOne) p).value instanceof Boolean;
- });
- }
-
- @Override
- public boolean test(BlockState blockState) {
- for (int i = 0; i < this.properties.length; i++) {
- Boolean value = (Boolean) blockState.getValue(this.properties[i]);
-
- if (value != this.values[i]) {
- return false;
- }
- }
-
- return true;
- }
-}
diff --git a/src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneObject.java b/src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneObject.java
deleted file mode 100644
index 5a90591b..00000000
--- a/src/main/java/org/embeddedt/modernfix/predicate/all/AllMatchOneObject.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package org.embeddedt.modernfix.predicate.all;
-
-import net.minecraft.world.level.block.state.BlockState;
-import net.minecraft.world.level.block.state.properties.Property;
-import org.embeddedt.modernfix.predicate.single.SingleMatchOne;
-
-import java.util.List;
-import java.util.function.Predicate;
-
-public class AllMatchOneObject implements Predicate {
- private final Property>[] properties;
- private final Object[] values;
-
- public AllMatchOneObject(List> list) {
- int size = list.size();
-
- this.properties = new Property[size];
- this.values = new Object[size];
-
- for (int i = 0; i < size; i++) {
- SingleMatchOne predicate = (SingleMatchOne) list.get(i);
-
- this.properties[i] = predicate.property;
- this.values[i] = predicate.value;
- }
- }
-
- @Override
- public boolean test(BlockState blockState) {
- for (int i = 0; i < this.properties.length; i++) {
- if (blockState.getValue(this.properties[i]) != this.values[i]) {
- return false;
- }
- }
-
- return true;
- }
-}
diff --git a/src/main/java/org/embeddedt/modernfix/predicate/any/AllMatchAnyObject.java b/src/main/java/org/embeddedt/modernfix/predicate/any/AllMatchAnyObject.java
deleted file mode 100644
index f9585376..00000000
--- a/src/main/java/org/embeddedt/modernfix/predicate/any/AllMatchAnyObject.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package org.embeddedt.modernfix.predicate.any;
-
-import net.minecraft.world.level.block.state.BlockState;
-import net.minecraft.world.level.block.state.properties.Property;
-import org.apache.commons.lang3.ArrayUtils;
-import org.embeddedt.modernfix.predicate.single.SingleMatchAny;
-
-import java.util.List;
-import java.util.function.Predicate;
-
-public class AllMatchAnyObject implements Predicate {
- private final Property>[] properties;
- private final Object[][] values;
-
- public AllMatchAnyObject(List> list) {
- int size = list.size();
-
- this.properties = new Property[size];
- this.values = new Object[size][];
-
- for (int i = 0; i < size; i++) {
- SingleMatchAny predicate = (SingleMatchAny) list.get(i);
-
- this.properties[i] = predicate.property;
- this.values[i] = predicate.values;
- }
- }
-
- @Override
- public boolean test(BlockState blockState) {
- for (int i = 0; i < this.properties.length; i++) {
- if (!ArrayUtils.contains(this.values[i], blockState.getValue(this.properties[i]))) {
- return false;
- }
- }
-
- return true;
- }
-}
diff --git a/src/main/java/org/embeddedt/modernfix/predicate/single/SingleMatchAny.java b/src/main/java/org/embeddedt/modernfix/predicate/single/SingleMatchAny.java
deleted file mode 100644
index 1c7906e9..00000000
--- a/src/main/java/org/embeddedt/modernfix/predicate/single/SingleMatchAny.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package org.embeddedt.modernfix.predicate.single;
-
-import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
-import net.minecraft.world.level.block.state.BlockState;
-import net.minecraft.world.level.block.state.properties.Property;
-import org.apache.commons.lang3.ArrayUtils;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-import java.util.function.Predicate;
-
-public class SingleMatchAny implements Predicate {
- public static final ObjectOpenHashSet PREDICATES = new ObjectOpenHashSet<>();
-
- public final Property> property;
- public final Object[] values;
-
- private SingleMatchAny(Property> property, List