Merge remote-tracking branch 'origin/1.19.2' into 1.19.4
This commit is contained in:
commit
6c7cd80bb3
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -4,6 +4,9 @@ libs
|
|||
media
|
||||
classes/
|
||||
|
||||
# Changelog
|
||||
CHANGELOG.md
|
||||
|
||||
# Created by https://www.gitignore.io/api/gradle,intellij,eclipse,windows,osx,linux
|
||||
|
||||
### Gradle ###
|
||||
|
|
|
|||
54
build.gradle
54
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)
|
||||
}
|
||||
|
|
|
|||
5
gradle/changelog.mustache
Normal file
5
gradle/changelog.mustache
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{{#commits}}
|
||||
{{#ifMatches messageTitle "^(?!Merge).*"}}
|
||||
* [{{{messageTitle}}}](https://github.com/embeddedt/ModernFix/commit/{{hashFull}}) - {{{authorName}}}
|
||||
{{/ifMatches}}
|
||||
{{/commits}}
|
||||
9
release.sh
Executable file
9
release.sh
Executable file
|
|
@ -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
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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<String, Function<ClassNode, ClassNode>> TRANSFORMERS = ImmutableMap.<String, Function<ClassNode, ClassNode>>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<ClassNode, ClassNode> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,23 @@ public class ModernFixEarlyConfig {
|
|||
|
||||
private final Map<String, Option> 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);
|
||||
|
|
|
|||
|
|
@ -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<BlockState> getBlockStatesForMRL(StateDefinition<Block, BlockState> stateDefinition, ModelResourceLocation location);
|
||||
BakedModel bakeDefault(ResourceLocation modelLocation);
|
||||
BakedModel getBakedMissingModel();
|
||||
void setBakedMissingModel(BakedModel m);
|
||||
UnbakedModel mfix$getUnbakedMissingModel();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ResourceLocation, BakedMod
|
|||
private final ModelBakery bakery;
|
||||
private final Map<ModelBakery.BakedCacheKey, BakedModel> bakedCache;
|
||||
private final Map<ResourceLocation, BakedModel> permanentOverrides;
|
||||
private BakedModel missingModel;
|
||||
private static final BakedModel SENTINEL = new BuiltInModel(null, null, null, false);
|
||||
|
||||
public DynamicBakedModelProvider(ModelBakery bakery, Map<ModelBakery.BakedCacheKey, BakedModel> cache) {
|
||||
this.bakery = bakery;
|
||||
this.bakedCache = cache;
|
||||
this.permanentOverrides = new Object2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
public void setMissingModel(BakedModel model) {
|
||||
this.missingModel = model;
|
||||
}
|
||||
|
||||
private static Triple<ResourceLocation, Transformation, Boolean> vanillaKey(Object o) {
|
||||
return Triple.of((ResourceLocation)o, BlockModelRotation.X0_Y0.getRotation(), false);
|
||||
}
|
||||
|
|
@ -43,7 +51,7 @@ public class DynamicBakedModelProvider implements Map<ResourceLocation, BakedMod
|
|||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
return true; //permanentOverrides.containsKey(o) || bakedCache.containsKey(vanillaKey(o));
|
||||
return permanentOverrides.getOrDefault(o, SENTINEL) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -53,8 +61,18 @@ public class DynamicBakedModelProvider implements Map<ResourceLocation, BakedMod
|
|||
|
||||
@Override
|
||||
public BakedModel get(Object o) {
|
||||
BakedModel model = permanentOverrides.get(o);
|
||||
return model != null ? model : ((IExtendedModelBakery)bakery).bakeDefault((ResourceLocation)o);
|
||||
BakedModel model = permanentOverrides.getOrDefault(o, SENTINEL);
|
||||
if(model != SENTINEL)
|
||||
return model;
|
||||
else {
|
||||
model = ((IExtendedModelBakery)bakery).bakeDefault((ResourceLocation)o);
|
||||
if(model == missingModel) {
|
||||
// to correctly emulate the original map, we return null for missing models
|
||||
permanentOverrides.put((ResourceLocation) o, null);
|
||||
return null;
|
||||
} else
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
|
|||
|
|
@ -17,11 +17,13 @@ public class DynamicModelBakeEvent extends Event {
|
|||
private BakedModel model;
|
||||
private final UnbakedModel unbakedModel;
|
||||
private final ModelBaker modelLoader;
|
||||
public DynamicModelBakeEvent(ResourceLocation location, UnbakedModel unbakedModel, BakedModel model, ModelBaker loader) {
|
||||
private final ModelBakery modelBakery;
|
||||
public DynamicModelBakeEvent(ResourceLocation location, UnbakedModel unbakedModel, BakedModel model, ModelBaker loader, ModelBakery bakery) {
|
||||
this.location = location;
|
||||
this.model = model;
|
||||
this.unbakedModel = unbakedModel;
|
||||
this.modelLoader = loader;
|
||||
this.modelBakery = bakery;
|
||||
}
|
||||
|
||||
public ResourceLocation getLocation() {
|
||||
|
|
@ -40,6 +42,10 @@ public class DynamicModelBakeEvent extends Event {
|
|||
return this.modelLoader;
|
||||
}
|
||||
|
||||
public ModelBakery getModelBakery() {
|
||||
return this.modelBakery;
|
||||
}
|
||||
|
||||
public void setModel(BakedModel model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,113 +0,0 @@
|
|||
package org.embeddedt.modernfix.load;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screens.LevelLoadingScreen;
|
||||
import net.minecraft.client.gui.screens.ProgressScreen;
|
||||
import net.minecraft.client.server.IntegratedServer;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.TicketType;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.util.Unit;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.ForcedChunksSavedData;
|
||||
import net.minecraftforge.client.event.ScreenEvent;
|
||||
import net.minecraftforge.common.world.ForgeChunkManager;
|
||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||
import net.minecraftforge.event.server.ServerAboutToStartEvent;
|
||||
import net.minecraftforge.eventbus.api.EventPriority;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.server.ServerLifecycleHooks;
|
||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||
import org.embeddedt.modernfix.screen.DeferredLevelLoadingScreen;
|
||||
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
/**
|
||||
* Handles deferring the world load screen.
|
||||
* <p></p>
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 = "<init>", 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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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<ModelBakery.BakedCacheKey, BakedModel> bakedCache;
|
||||
|
||||
@Shadow @Final @Mutable private BlockColors blockColors;
|
||||
private Cache<ModelBakery.BakedCacheKey, BakedModel> loadedBakedModels;
|
||||
|
||||
private Cache<ResourceLocation, UnbakedModel> loadedModels;
|
||||
|
||||
private HashMap<ResourceLocation, UnbakedModel> smallLoadingCache = new HashMap<>();
|
||||
|
||||
|
||||
@Redirect(method = "<init>", 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 = "<init>", 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 <K, V> void onModelRemoved(RemovalNotification<K, V> notification) {
|
||||
|
|
@ -276,4 +280,18 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
|
|||
public ImmutableList<BlockState> getBlockStatesForMRL(StateDefinition<Block, BlockState> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<String, Function<BakedModel, BakedModel>> 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<BakedModel, BakedModel> customizerFn = CUSTOMIZERS.get(event.getLocation().getPath());
|
||||
if(customizerFn != null)
|
||||
event.setModel(customizerFn.apply(event.getModel()));
|
||||
}
|
||||
}
|
||||
|
|
@ -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<T> extends MappedRegistry<T> {
|
||||
@Shadow private Map<ResourceLocation, Holder.Reference<T>> holdersByName;
|
||||
|
||||
public NamespacedHolderHelperMixin(ResourceKey<? extends Registry<T>> 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<Registry<T>> 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<T> ref : this.holdersByName.values()) {
|
||||
if(!ref.isBound())
|
||||
return;
|
||||
}
|
||||
if (this.unregisteredIntrusiveHolders != null) {
|
||||
for(Holder.Reference<T> ref : this.unregisteredIntrusiveHolders.values()) {
|
||||
if(ref.getType() == Holder.Reference.Type.INTRUSIVE && !ref.isBound())
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Skip the creation of streams
|
||||
cir.setReturnValue(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<BlockState> getPredicate(StateDefinition<Block, BlockState> stateManager) {
|
||||
return StatePropertyPredicateHelper.allMatch(Streams.stream(this.conditions).map((multipartModelSelector) -> {
|
||||
return multipartModelSelector.getPredicate(stateManager);
|
||||
}).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
|
|
@ -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<BlockState> getPredicate(StateDefinition<Block, BlockState> stateManager) {
|
||||
return StatePropertyPredicateHelper.anyMatch(Streams.stream(this.conditions).map((multipartModelSelector) -> {
|
||||
return multipartModelSelector.getPredicate(stateManager);
|
||||
}).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
|
|
@ -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<BlockState> getPredicate(StateDefinition<Block, BlockState> 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<String> 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<BlockState> 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<Block, BlockState> 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<PackType, Path> ROOT_DIR_BY_TYPE;
|
||||
private static LoadingCache<Pair<Path, Integer>, List<Path>> pathStreamLoadingCache = CacheBuilder.newBuilder()
|
||||
.build(FileWalker.INSTANCE);
|
||||
|
||||
private static Set<String> containedPaths = null;
|
||||
|
||||
//@Inject(method = "<init>", 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<Path> 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<Path> 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<Boolean> cir) {
|
||||
if(!PackTypeHelper.isVanillaPackType(type))
|
||||
return;
|
||||
cir.setReturnValue(containedPaths.contains(type.getDirectory() + "/" + location.getNamespace() + "/" + FileUtil.normalize(location.getPath())));
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
|
|
|
|||
|
|
@ -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<String, Tag> tags;
|
||||
|
||||
/**
|
||||
* Ensure that the backing map is always a CanonizingStringMap.
|
||||
*/
|
||||
@Redirect(method = "<init>(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<String, Tag> 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<Tag> cir) {
|
||||
if(this.tags instanceof CanonizingStringMap) {
|
||||
cir.setReturnValue(new CompoundTag(CanonizingStringMap.deepCopy((CanonizingStringMap<Tag>)this.tags, Tag::copy)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<TemplateComponent> components;
|
||||
BookTemplate template = (BookTemplate) templateField.get(page);
|
||||
if(template == null)
|
||||
continue;
|
||||
components = (List<TemplateComponent>) 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
package org.embeddedt.modernfix.predicate;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class AllPredicate<T> implements Predicate<T> {
|
||||
private final Predicate<T>[] predicates;
|
||||
|
||||
public AllPredicate(Predicate<T>[] predicates) {
|
||||
this.predicates = predicates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(T t) {
|
||||
for (Predicate<T> predicate : this.predicates) {
|
||||
if (!predicate.test(t)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
package org.embeddedt.modernfix.predicate;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class AnyPredicate<T> implements Predicate<T> {
|
||||
private final Predicate<T>[] predicates;
|
||||
|
||||
public AnyPredicate(Predicate<T>[] predicates) {
|
||||
this.predicates = predicates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(T t) {
|
||||
for (Predicate<T> predicate : this.predicates) {
|
||||
if (predicate.test(t)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package org.embeddedt.modernfix.predicate;
|
||||
|
||||
/**
|
||||
* Calculates the
|
||||
*/
|
||||
public class CachedModelPredicate {
|
||||
}
|
||||
|
|
@ -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<BlockState> allMatch(List<Predicate<BlockState>> 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<BlockState> anyMatch(List<Predicate<BlockState>> predicates) {
|
||||
return new AnyPredicate<>(predicates.toArray(new Predicate[0]));
|
||||
}
|
||||
}
|
||||
|
|
@ -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<BlockState> {
|
||||
private final Property<?>[] properties;
|
||||
private final boolean[] values;
|
||||
|
||||
public AllMatchOneBoolean(List<Predicate<BlockState>> 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<Predicate<BlockState>> 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<BlockState> {
|
||||
private final Property<?>[] properties;
|
||||
private final Object[] values;
|
||||
|
||||
public AllMatchOneObject(List<Predicate<BlockState>> 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<BlockState> {
|
||||
private final Property<?>[] properties;
|
||||
private final Object[][] values;
|
||||
|
||||
public AllMatchAnyObject(List<Predicate<BlockState>> 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<BlockState> {
|
||||
public static final ObjectOpenHashSet<SingleMatchAny> PREDICATES = new ObjectOpenHashSet<>();
|
||||
|
||||
public final Property<?> property;
|
||||
public final Object[] values;
|
||||
|
||||
private SingleMatchAny(Property<?> property, List<Object> values) {
|
||||
this.property = property;
|
||||
this.values = values.toArray();
|
||||
}
|
||||
|
||||
public static SingleMatchAny create(Property<?> property, List<Object> values) {
|
||||
return PREDICATES.addOrGet(new SingleMatchAny(property, values));
|
||||
}
|
||||
|
||||
public static boolean areOfType(List<Predicate<BlockState>> predicates) {
|
||||
return predicates.stream()
|
||||
.allMatch(p -> {
|
||||
return p instanceof SingleMatchAny;
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean valuesMatchType(List<Predicate<BlockState>> predicates, Class<?> type) {
|
||||
return predicates.stream()
|
||||
.allMatch(p -> {
|
||||
return p instanceof SingleMatchAny &&
|
||||
Arrays.stream(((SingleMatchAny) p).values).allMatch(t -> type.isInstance(p));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(BlockState blockState) {
|
||||
return ArrayUtils.contains(this.values, blockState.getValue(this.property));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
SingleMatchAny that = (SingleMatchAny) o;
|
||||
return Objects.equals(property, that.property) &&
|
||||
Arrays.equals(values, that.values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Objects.hash(property);
|
||||
result = 31 * result + Arrays.hashCode(values);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
package org.embeddedt.modernfix.predicate.single;
|
||||
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class SingleMatchOne implements Predicate<BlockState> {
|
||||
public final Property<?> property;
|
||||
public final Object value;
|
||||
|
||||
public SingleMatchOne(Property<?> property, Object value) {
|
||||
this.property = property;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static boolean areOfType(List<Predicate<BlockState>> predicates) {
|
||||
return predicates.stream()
|
||||
.allMatch(p -> {
|
||||
return p instanceof SingleMatchOne;
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean valuesMatchType(List<Predicate<BlockState>> predicates, Class<?> type) {
|
||||
return predicates.stream()
|
||||
.allMatch(p -> {
|
||||
return p instanceof SingleMatchOne && type.isInstance(((SingleMatchOne) p).value);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(BlockState blockState) {
|
||||
return blockState.getValue(this.property) == this.value;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
package org.embeddedt.modernfix.registry;
|
||||
|
||||
import com.google.common.base.Stopwatch;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.fml.ModWorkManager;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.util.AsyncStopwatch;
|
||||
import org.embeddedt.modernfix.util.CachedSupplier;
|
||||
import org.embeddedt.modernfix.util.OrderedParallelModDispatcher;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class DeferredRegisterBaker {
|
||||
private static final HashMap<ResourceLocation, HashMap<String, List<CachedSupplier<?>>>> supplierMap = new HashMap<>();
|
||||
public static <T> Supplier<T> cacheForComputationLater(ResourceLocation registry, String modid, Supplier<T> supplier) {
|
||||
synchronized (supplierMap) {
|
||||
HashMap<String, List<CachedSupplier<?>>> registrySupplierMap = supplierMap.computeIfAbsent(registry, reg -> new HashMap<>());
|
||||
List<CachedSupplier<?>> modSupplierList = registrySupplierMap.computeIfAbsent(modid, id -> new ArrayList<>());
|
||||
CachedSupplier<T> cacher = new CachedSupplier<>(supplier);
|
||||
modSupplierList.add(cacher);
|
||||
return cacher;
|
||||
}
|
||||
}
|
||||
|
||||
public static void bakeSuppliers(ResourceLocation registry) {
|
||||
synchronized (supplierMap) {
|
||||
Set<String> modErrors = Collections.synchronizedSet(new HashSet<>());
|
||||
HashMap<String, List<CachedSupplier<?>>> registrySupplierMap = supplierMap.get(registry);
|
||||
if(registrySupplierMap == null)
|
||||
return;
|
||||
ModernFix.LOGGER.info("Caching suppliers for " + registry);
|
||||
Stopwatch realtimeStopwatch = Stopwatch.createStarted();
|
||||
AsyncStopwatch cpuStopwatch = new AsyncStopwatch();
|
||||
OrderedParallelModDispatcher.dispatchBlocking(ModWorkManager.parallelExecutor(), modId -> {
|
||||
List<CachedSupplier<?>> suppliersToCompute = registrySupplierMap.get(modId);
|
||||
if (suppliersToCompute == null || suppliersToCompute.size() == 0) {
|
||||
return;
|
||||
}
|
||||
cpuStopwatch.startMeasuringAsync();
|
||||
for (CachedSupplier<?> supplier : suppliersToCompute) {
|
||||
try {
|
||||
supplier.compute();
|
||||
} catch(RuntimeException e) {
|
||||
ModernFix.LOGGER.debug("Exception encountered while caching supplier", e);
|
||||
modErrors.add(modId);
|
||||
}
|
||||
}
|
||||
cpuStopwatch.stopMeasuringAsync();
|
||||
});
|
||||
realtimeStopwatch.stop();
|
||||
if(modErrors.size() > 0)
|
||||
ModernFix.LOGGER.warn("The following mods had errors while caching " + registry + " suppliers (this is likely safe): [" + String.join(", ", modErrors) + "]");
|
||||
ModernFix.LOGGER.info("CPU time spent constructing " + registry + " suppliers: " + cpuStopwatch.getCpuTime()/1000f + " seconds");
|
||||
ModernFix.LOGGER.info("Real time spent constructing " + registry + " suppliers: " + realtimeStopwatch.elapsed(TimeUnit.MILLISECONDS)/1000f + " seconds");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
package org.embeddedt.modernfix.screen;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.gui.screens.LevelLoadingScreen;
|
||||
import net.minecraft.server.level.progress.StoringChunkProgressListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
public class DeferredLevelLoadingScreen extends LevelLoadingScreen {
|
||||
private final BooleanSupplier worldLoadFinished;
|
||||
public DeferredLevelLoadingScreen(StoringChunkProgressListener arg, BooleanSupplier worldLoadFinished) {
|
||||
super(arg);
|
||||
this.worldLoadFinished = worldLoadFinished;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
if(this.worldLoadFinished.getAsBoolean())
|
||||
this.onClose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderBackground(@NotNull PoseStack arg) {
|
||||
renderDirtBackground(arg);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import com.google.common.base.Stopwatch;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class AsyncStopwatch {
|
||||
private final AtomicLong cpuTimeMs = new AtomicLong(0);
|
||||
private final ThreadLocal<Stopwatch> threadStopwatch = ThreadLocal.withInitial(Stopwatch::createUnstarted);
|
||||
|
||||
public void startMeasuringAsync() {
|
||||
threadStopwatch.get().start();
|
||||
}
|
||||
|
||||
public void stopMeasuringAsync() {
|
||||
Stopwatch watch = threadStopwatch.get();
|
||||
watch.stop();
|
||||
long elapsed = watch.elapsed(TimeUnit.MILLISECONDS);
|
||||
cpuTimeMs.addAndGet(elapsed);
|
||||
watch.reset();
|
||||
}
|
||||
|
||||
public void ensureStoppedAsync() {
|
||||
Stopwatch watch = threadStopwatch.get();
|
||||
if(watch.isRunning())
|
||||
stopMeasuringAsync();
|
||||
}
|
||||
|
||||
public long getCpuTime() {
|
||||
return cpuTimeMs.get();
|
||||
}
|
||||
|
||||
public static void measureAndLogSerialRunningTime(String label, Runnable runnable) {
|
||||
ModernFix.LOGGER.info(label + "...");
|
||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||
try {
|
||||
runnable.run();
|
||||
ModernFix.LOGGER.info(label + " took " + stopwatch.elapsed(TimeUnit.MILLISECONDS)/1000f + " seconds");
|
||||
} finally {
|
||||
stopwatch.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* An implementation of Supplier that allows separating the time at which the value is computed from when it is
|
||||
* retrieved.
|
||||
*/
|
||||
public class CachedSupplier<T> implements Supplier<T> {
|
||||
private T value = null;
|
||||
|
||||
private boolean hasBeenComputed;
|
||||
private final Supplier<T> delegate;
|
||||
|
||||
public CachedSupplier(Supplier<T> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
public synchronized void compute() {
|
||||
this.value = this.delegate.get();
|
||||
this.hasBeenComputed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized T get() {
|
||||
if(this.hasBeenComputed) {
|
||||
this.hasBeenComputed = false;
|
||||
return this.value;
|
||||
} else {
|
||||
return this.delegate.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import com.google.common.collect.Interner;
|
||||
import com.google.common.collect.Interners;
|
||||
import it.unimi.dsi.fastutil.objects.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Replacement backing map for CompoundTags. Uses an array map for tags with 4 or less entries,
|
||||
* and a hash map for larger tags.
|
||||
*/
|
||||
public class CanonizingStringMap<T> implements Map<String, T> {
|
||||
private Object2ObjectMap<String, T> backingMap;
|
||||
|
||||
private static final int GROWTH_THRESHOLD = 4;
|
||||
private static final Interner<String> KEY_INTERNER = Interners.newStrongInterner();
|
||||
|
||||
public CanonizingStringMap() {
|
||||
this(new Object2ObjectArrayMap<>());
|
||||
}
|
||||
|
||||
protected CanonizingStringMap(Object2ObjectMap<String, T> newMap) {
|
||||
this.backingMap = newMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return backingMap.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return backingMap.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
return backingMap.containsKey(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object o) {
|
||||
return backingMap.containsValue(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(Object o) {
|
||||
return backingMap.get(o);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public T put(String s, T t) {
|
||||
if(backingMap.size() >= GROWTH_THRESHOLD && !(backingMap instanceof Object2ObjectOpenHashMap) && !backingMap.containsKey(s)) {
|
||||
// map will grow to GROWTH_THRESHOLD + 1 entries, change to hashmap
|
||||
backingMap = new Object2ObjectOpenHashMap<>(backingMap);
|
||||
}
|
||||
s = KEY_INTERNER.intern(s);
|
||||
return backingMap.put(s, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T remove(Object o) {
|
||||
T value = backingMap.remove(o);
|
||||
// need to shrink to be consistent with new maps
|
||||
if(backingMap.size() <= GROWTH_THRESHOLD && backingMap instanceof Object2ObjectOpenHashMap) {
|
||||
backingMap = new Object2ObjectArrayMap<>(backingMap);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(@NotNull Map<? extends String, ? extends T> map) {
|
||||
if(map.size() == 0)
|
||||
return;
|
||||
map.forEach((String key, T val) -> {
|
||||
key = KEY_INTERNER.intern(key);
|
||||
backingMap.put(key, val);
|
||||
});
|
||||
// if it's too big to be an array, grow it
|
||||
if(backingMap.size() > GROWTH_THRESHOLD && !(backingMap instanceof Object2ObjectOpenHashMap)) {
|
||||
backingMap = new Object2ObjectOpenHashMap<>(backingMap);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
if(!(this.backingMap instanceof Object2ObjectArrayMap))
|
||||
this.backingMap = new Object2ObjectArrayMap<>();
|
||||
else
|
||||
this.backingMap.clear();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
return Collections.unmodifiableSet(this.backingMap.keySet());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<T> values() {
|
||||
return Collections.unmodifiableCollection(this.backingMap.values());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Entry<String, T>> entrySet() {
|
||||
return Collections.unmodifiableSet(this.backingMap.entrySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
CanonizingStringMap<?> that = (CanonizingStringMap<?>)o;
|
||||
if(that.backingMap.size() != backingMap.size())
|
||||
return false;
|
||||
return backingMap.object2ObjectEntrySet().containsAll(that.backingMap.object2ObjectEntrySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* We deliberately use a hashcode that will be consistent regardless of underlying map type.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final ObjectIterator<Object2ObjectMap.Entry<String, T>> i = Object2ObjectMaps.fastIterator(backingMap);
|
||||
int h = 0, n = backingMap.size();
|
||||
while (n-- != 0)
|
||||
h += i.next().hashCode();
|
||||
return h;
|
||||
}
|
||||
|
||||
public static <T> CanonizingStringMap<T> deepCopy(CanonizingStringMap<T> inputMap, Function<T, T> deepCopier) {
|
||||
Object2ObjectMap<String, T> copiedBackingMap;
|
||||
int size = inputMap.backingMap.size();
|
||||
if(size > GROWTH_THRESHOLD) {
|
||||
copiedBackingMap = new Object2ObjectOpenHashMap<>(size);
|
||||
} else
|
||||
copiedBackingMap = new Object2ObjectArrayMap<>(size);
|
||||
inputMap.backingMap.object2ObjectEntrySet().forEach(entry -> {
|
||||
copiedBackingMap.put(entry.getKey(), deepCopier.apply(entry.getValue()));
|
||||
});
|
||||
return new CanonizingStringMap<>(copiedBackingMap);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import net.minecraftforge.fml.ModContainer;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.fml.ModLoadingContext;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
|
||||
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
|
||||
import net.minecraftforge.forgespi.language.IModInfo;
|
||||
import org.apache.logging.log4j.Marker;
|
||||
import org.apache.logging.log4j.MarkerManager;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Iterates over all mods in the game, parallelizing where possible while preserving dependency ordering.
|
||||
*
|
||||
* Can also be given a list of mods to skip.
|
||||
*/
|
||||
public class OrderedParallelModDispatcher {
|
||||
private static final Marker DISPATCHER = MarkerManager.getMarker("OrderedParallelModDispatcher");
|
||||
public static void dispatchBlocking(Executor executor, Consumer<String> task, Collection<String> modIDsToFilter) {
|
||||
Set<String> finishedMods = Collections.synchronizedSet(new HashSet<>(modIDsToFilter));
|
||||
HashMap<String, CompletableFuture<?>> submittedFutures = new HashMap<>();
|
||||
Semaphore jobWaitingSemaphore = new Semaphore(0);
|
||||
ArrayList<IModInfo> remainingModList = new ArrayList<>(ModList.get().getMods());
|
||||
while(remainingModList.size() > 0) {
|
||||
remainingModList.removeIf(modInfo -> {
|
||||
if(finishedMods.contains(modInfo.getModId()))
|
||||
return true;
|
||||
List<String> missingDependencies = modInfo.getDependencies().stream()
|
||||
.filter(IModInfo.ModVersion::isMandatory)
|
||||
.map(IModInfo.ModVersion::getModId)
|
||||
.filter(modId -> !finishedMods.contains(modId))
|
||||
.collect(Collectors.toList());
|
||||
if(missingDependencies.size() > 0) {
|
||||
ModernFix.LOGGER.debug(DISPATCHER, "Cannot process " + modInfo.getModId() + ", as it is waiting on mods: [" + String.join(", ", missingDependencies) + "]");
|
||||
return false;
|
||||
}
|
||||
Optional<? extends ModContainer> modContainerOpt = ModList.get().getModContainerById(modInfo.getModId());
|
||||
if(!modContainerOpt.isPresent())
|
||||
throw new IllegalStateException("Can't find mod container");
|
||||
ModContainer container = modContainerOpt.get();
|
||||
ModernFix.LOGGER.debug(DISPATCHER, "Submitting job for " + modInfo.getModId());
|
||||
submittedFutures.put(modInfo.getModId(), CompletableFuture.runAsync(() -> {
|
||||
ModLoadingContext.get().setActiveContainer(container);
|
||||
try {
|
||||
task.accept(modInfo.getModId());
|
||||
} catch(RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
/*
|
||||
* We cannot rely on the main thread to correctly mark us as done, as it might start running
|
||||
* before the future is marked as complete. So we add the mod to the finished set ourselves.
|
||||
*/
|
||||
finishedMods.add(modInfo.getModId());
|
||||
jobWaitingSemaphore.release();
|
||||
//ModLoadingContext.get().setActiveContainer(null, null);
|
||||
}, executor));
|
||||
return true;
|
||||
});
|
||||
Preconditions.checkState(submittedFutures.size() > 0, "The semaphore will block forever!");
|
||||
ModernFix.LOGGER.debug(DISPATCHER, "Waiting for one of [" + String.join(", ", submittedFutures.keySet()) + "] to finish...");
|
||||
try {
|
||||
jobWaitingSemaphore.acquire();
|
||||
} catch(InterruptedException e) {
|
||||
throw new RuntimeException("Unexpected interruption", e);
|
||||
}
|
||||
submittedFutures.entrySet().removeIf(entry -> {
|
||||
if(entry.getValue().isDone()) {
|
||||
ModernFix.LOGGER.debug(DISPATCHER, "Job finished for " + entry.getKey());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
submittedFutures.values().forEach(CompletableFuture::join);
|
||||
}
|
||||
|
||||
public static void dispatchBlocking(Executor executor, Consumer<String> task) {
|
||||
dispatchBlocking(executor, task, Collections.emptyList());
|
||||
}
|
||||
}
|
||||
|
|
@ -30,4 +30,6 @@ public net.minecraft.client.resources.model.ModelBakery$BakedCacheKey
|
|||
public net.minecraft.client.resources.model.ModelBakery$BakedCacheKey <init>(Lnet/minecraft/resources/ResourceLocation;Lcom/mojang/math/Transformation;Z)V # <init>
|
||||
public net.minecraft.client.resources.model.ModelBakery f_119241_ # ITEM_MODEL_GENERATOR
|
||||
public net.minecraft.client.resources.model.ModelBakery$ModelBakerImpl
|
||||
public net.minecraft.client.resources.model.ModelBakery$ModelBakerImpl <init>(Lnet/minecraft/client/resources/model/ModelBakery;Ljava/util/function/BiFunction;Lnet/minecraft/resources/ResourceLocation;)V # <init>
|
||||
public net.minecraft.client.resources.model.ModelBakery$ModelBakerImpl <init>(Lnet/minecraft/client/resources/model/ModelBakery;Ljava/util/function/BiFunction;Lnet/minecraft/resources/ResourceLocation;)V # <init>
|
||||
public net.minecraft.nbt.CompoundTag <init>(Ljava/util/Map;)V # <init>
|
||||
public net.minecraft.client.renderer.texture.TextureAtlasSprite <init>(Lnet/minecraft/client/renderer/texture/TextureAtlas;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite$Info;IIIIILcom/mojang/blaze3d/platform/NativeImage;)V # <init>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
"perf.dynamic_structure_manager.StructureManagerMixin",
|
||||
"bugfix.chunk_deadlock.ServerChunkCacheMixin",
|
||||
"perf.dedicated_reload_executor.MinecraftServerMixin",
|
||||
"perf.fast_forge_dummies.NamespacedHolderHelperMixin",
|
||||
"perf.remove_biome_temperature_cache.BiomeMixin",
|
||||
"perf.reduce_blockstate_cache_rebuilds.GameDataMixin",
|
||||
"perf.reduce_blockstate_cache_rebuilds.BlockCallbacksMixin",
|
||||
|
|
@ -27,7 +28,11 @@
|
|||
"feature.measure_time.ReloadableServerResourcesMixin",
|
||||
"feature.branding.BrandingControlMixin",
|
||||
"feature.direct_stack_trace.CrashReportMixin",
|
||||
"perf.nbt_memory_usage.CompoundTagMixin",
|
||||
"perf.fast_registry_validation.ForgeRegistryMixin",
|
||||
"perf.kubejs.RecipeEventJSMixin",
|
||||
"perf.tag_id_caching.TagEntryMixin",
|
||||
"perf.tag_id_caching.TagOrElementLocationMixin",
|
||||
"perf.cache_strongholds.ChunkGeneratorMixin",
|
||||
"perf.cache_upgraded_structures.StructureManagerMixin",
|
||||
"perf.cache_strongholds.ServerLevelMixin",
|
||||
|
|
@ -52,6 +57,7 @@
|
|||
"perf.dynamic_resources.ModelBakerImplMixin",
|
||||
"perf.dynamic_resources.ModelBakeryMixin",
|
||||
"perf.dynamic_resources.ModelManagerMixin",
|
||||
"perf.dynamic_resources.ae2.RegistrationMixin",
|
||||
"perf.dynamic_resources.ctm.TextureMetadataHandlerMixin",
|
||||
"perf.dynamic_resources.ctm.CTMPackReloadListenerMixin",
|
||||
"perf.dynamic_resources.supermartijncore.ClientRegistrationHandlerMixin",
|
||||
|
|
@ -60,18 +66,15 @@
|
|||
"perf.model_optimizations.TransformationMatrixMixin",
|
||||
"perf.model_optimizations.BooleanPropertyMixin",
|
||||
"perf.model_optimizations.PropertyMixin",
|
||||
"perf.patchouli_deduplicate_books.ClientBookRegistryMixin",
|
||||
"perf.thread_priorities.IntegratedServerMixin",
|
||||
"safety.BlockColorsMixin",
|
||||
"safety.ItemColorsMixin",
|
||||
"perf.flatten_model_predicates.AndConditionMixin",
|
||||
"perf.flatten_model_predicates.OrConditionMixin",
|
||||
"perf.flatten_model_predicates.PropertyValueConditionMixin",
|
||||
"perf.blast_search_trees.MinecraftMixin",
|
||||
"perf.blast_search_trees.IngredientFilterInvoker",
|
||||
"perf.cache_model_materials.MultipartMixin",
|
||||
"perf.faster_texture_stitching.StitcherMixin",
|
||||
"perf.skip_first_datapack_reload.CreateWorldScreenMixin",
|
||||
"perf.faster_singleplayer_load.MinecraftServerMixin",
|
||||
"devenv.MinecraftMixin",
|
||||
"devenv.NarratorMixin"
|
||||
],
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user