Merge branch 'embeddedt:1.16' into 1.16

This commit is contained in:
羊羽ちゃん 2023-08-19 10:28:22 +09:00 committed by GitHub
commit 62fbab2d15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
75 changed files with 1814 additions and 111 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
ko_fi: embeddedt

View File

@ -94,6 +94,7 @@ allprojects {
maven {
url 'https://maven.terraformersmc.com/releases'
}
maven { url = "https://jitpack.io" }
}
}
@ -146,7 +147,7 @@ configure(subprojects.findAll {it.name == "common" || it.name == "forge" || it.n
}
}
tasks.withType(JavaCompile) {
tasks.withType(JavaCompile).configureEach {
// ensure that the encoding is set to UTF-8, no matter what the system default is
// this fixes some edge cases with special characters not displaying correctly
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
@ -164,10 +165,10 @@ tasks.withType(JavaCompile) {
*/
}
task generateChangelog(type: se.bjurr.gitchangelog.plugin.gradle.GitChangelogTask) {
tasks.register('generateChangelog', se.bjurr.gitchangelog.plugin.gradle.GitChangelogTask) {
def details = versionDetails();
def theVersionRef
if(details.commitDistance > 0) {
if (details.commitDistance > 0) {
theVersionRef = details.lastTag;
} else {
def secondLastTagCmd = "git describe --abbrev=0 " + details.lastTag + "^"
@ -204,8 +205,9 @@ configure(subprojects.findAll {it.name == "forge" || it.name == "fabric"}) {
}
runs {
client {
vmArgs "-Xmx512m"
vmArgs "-Xms512m"
vmArgs "-Xmx1G"
vmArgs "-Xms1G"
property("mixin.debug.export", "true")
}
}
}

View File

@ -10,14 +10,18 @@ dependencies {
// We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies
// Do NOT use other classes from fabric loader
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
implementation(annotationProcessor("com.github.llamalad7.mixinextras:mixinextras-common:${rootProject.mixinextras_version}"))
modApi("dev.latvian.mods:kubejs:${kubejs_version}") {
modCompileOnly("dev.latvian.mods:kubejs:${kubejs_version}") {
transitive = false
}
// Remove the next line if you don't want to depend on the API
// modApi "me.shedaniel:architectury:${rootProject.architectury_version}"
}
// don't need remapped common jar
tasks.named('remapJar') { enabled = false }
publishing {
publications {
mavenCommon(MavenPublication) {

View File

@ -27,7 +27,7 @@ public class ModernFixClient {
public static float gameStartTimeSeconds = -1;
private static boolean recipesUpdated, tagsUpdated = false;
public static boolean recipesUpdated, tagsUpdated = false;
public String brandingString = null;

View File

@ -0,0 +1,68 @@
package org.embeddedt.modernfix.chunk;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import org.jetbrains.annotations.Nullable;
public class SafeBlockGetter implements BlockGetter {
private final ServerLevel wrapped;
private final Thread mainThread;
public SafeBlockGetter(ServerLevel wrapped) {
this.wrapped = wrapped;
this.mainThread = Thread.currentThread();
}
public boolean shouldUse() {
return Thread.currentThread() != this.mainThread;
}
@Nullable
private BlockGetter getChunkSafe(BlockPos pos) {
// can safely call getChunkForLighting off-thread
BlockGetter access = this.wrapped.getChunkSource().getChunkForLighting(pos.getX() >> 4, pos.getZ() >> 4);
if(!(access instanceof ChunkAccess))
return null;
ChunkAccess chunk = (ChunkAccess)access;
if(!chunk.getStatus().isOrAfter(ChunkStatus.FULL))
return null;
return chunk;
}
@Override
public int getMaxBuildHeight() {
return this.wrapped.getMaxBuildHeight();
}
@Override
public int getMaxLightLevel() {
return this.wrapped.getMaxLightLevel();
}
@Nullable
@Override
public BlockEntity getBlockEntity(BlockPos pos) {
BlockGetter g = getChunkSafe(pos);
return g == null ? null : g.getBlockEntity(pos);
}
@Override
public BlockState getBlockState(BlockPos pos) {
BlockGetter g = getChunkSafe(pos);
return g == null ? Blocks.AIR.defaultBlockState() : g.getBlockState(pos);
}
@Override
public FluidState getFluidState(BlockPos pos) {
BlockGetter g = getChunkSafe(pos);
return g == null ? Fluids.EMPTY.defaultFluidState() : g.getFluidState(pos);
}
}

View File

@ -0,0 +1,22 @@
package org.embeddedt.modernfix.common.mixin.bugfix.chunk_deadlock;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.state.BlockBehaviour;
import org.embeddedt.modernfix.chunk.SafeBlockGetter;
import org.embeddedt.modernfix.duck.ISafeBlockGetter;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
@Mixin(value = BlockBehaviour.BlockStateBase.class, priority = 100)
public class BlockStateBaseMixin {
@ModifyVariable(method = "getOffset", at = @At("HEAD"), argsOnly = true, index = 1)
private BlockGetter useSafeGetter(BlockGetter g) {
if(g instanceof ISafeBlockGetter) {
SafeBlockGetter replacement = ((ISafeBlockGetter) g).mfix$getSafeBlockGetter();
if(replacement.shouldUse())
return replacement;
}
return g;
}
}

View File

@ -0,0 +1,18 @@
package org.embeddedt.modernfix.common.mixin.bugfix.chunk_deadlock;
import net.minecraft.server.level.ServerLevel;
import org.embeddedt.modernfix.chunk.SafeBlockGetter;
import org.embeddedt.modernfix.duck.ISafeBlockGetter;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@Mixin(ServerLevel.class)
public class ServerLevelMixin implements ISafeBlockGetter {
@Unique
private final SafeBlockGetter mfix$safeBlockGetter = new SafeBlockGetter((ServerLevel)(Object)this);
@Override
public SafeBlockGetter mfix$getSafeBlockGetter() {
return mfix$safeBlockGetter;
}
}

View File

@ -0,0 +1,41 @@
package org.embeddedt.modernfix.common.mixin.bugfix.world_leaks;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.lighting.LevelLightEngine;
import org.embeddedt.modernfix.ModernFix;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.Opcodes;
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.concurrent.atomic.AtomicReferenceArray;
@Mixin(Minecraft.class)
public class MinecraftMixin {
@Shadow @Nullable public ClientLevel level;
/**
* To mitigate the effect of leaked client worlds, clear most of the data structures that waste memory.
*/
@Inject(method = "clearLevel(Lnet/minecraft/client/gui/screens/Screen;)V", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/Minecraft;level:Lnet/minecraft/client/multiplayer/ClientLevel;"))
private void clearLevelDataForLeaks(CallbackInfo ci) {
if(this.level != null) {
try {
AtomicReferenceArray<LevelChunk> chunks = this.level.getChunkSource().storage.chunks;
for(int i = 0; i < chunks.length(); i++) {
chunks.set(i, null);
}
this.level.getChunkSource().lightEngine = new LevelLightEngine(this.level.getChunkSource(), false, false);
// clear BE list otherwise they will hold chunks
this.level.blockEntityList.clear();
} catch(RuntimeException e) {
ModernFix.LOGGER.error("Exception clearing level data", e);
}
}
}
}

View File

@ -0,0 +1,24 @@
package org.embeddedt.modernfix.common.mixin.perf.compact_mojang_registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import org.embeddedt.modernfix.annotation.IgnoreOutsideDev;
import org.embeddedt.modernfix.registry.DirectStorageRegistryObject;
import org.spongepowered.asm.mixin.Mixin;
@Mixin({ Block.class, Item.class })
@IgnoreOutsideDev
public class DirectObjectMixin implements DirectStorageRegistryObject {
private ResourceLocation mfix$resourceKey;
@Override
public ResourceLocation mfix$getResourceKey() {
return mfix$resourceKey;
}
@Override
public void mfix$setResourceKey(ResourceLocation key) {
mfix$resourceKey = key;
}
}

View File

@ -0,0 +1,52 @@
package org.embeddedt.modernfix.common.mixin.perf.compact_mojang_registries;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableSet;
import com.mojang.serialization.Lifecycle;
import net.minecraft.core.MappedRegistry;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import org.embeddedt.modernfix.annotation.IgnoreOutsideDev;
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
import org.embeddedt.modernfix.registry.DirectStorageRegistryObject;
import org.embeddedt.modernfix.registry.LifecycleMap;
import org.embeddedt.modernfix.registry.RegistryStorage;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
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;
@Mixin(MappedRegistry.class)
@IgnoreOutsideDev
public abstract class MappedRegistryMixin<T> extends Registry<T> {
@Shadow
@Final
@Mutable
private Map<T, Lifecycle> lifecycles;
@Shadow @Final @Mutable
private BiMap<ResourceLocation, T> storage;
@Shadow @Final @Mutable
private BiMap<ResourceKey<T>, T> keyStorage;
private static final ImmutableSet<ResourceLocation> MFIX$NEW_STORAGE_KEYS = ImmutableSet.of(new ResourceLocation("block"), new ResourceLocation("item"));
protected MappedRegistryMixin(ResourceKey<? extends Registry<T>> resourceKey, Lifecycle lifecycle) {
super(resourceKey, lifecycle);
}
@Inject(method = "<init>", at = @At("RETURN"))
private void replaceStorage(CallbackInfo ci) {
this.lifecycles = new LifecycleMap<>();
if(MFIX$NEW_STORAGE_KEYS.contains(this.key().location())) {
ModernFixMixinPlugin.instance.logger.info("Using experimental registry storage for {}", this.key());
this.storage = (BiMap<ResourceLocation, T>) RegistryStorage.createStorage();
this.keyStorage = (BiMap<ResourceKey<T>, T>)RegistryStorage.createKeyStorage(this.key(), (BiMap<ResourceLocation, DirectStorageRegistryObject>)this.storage);
}
}
}

View File

@ -36,7 +36,7 @@ public class StateHolderMixin {
}
});
@Redirect(method = "codec", at = @At(value = "INVOKE", target = "Lcom/mojang/serialization/Codec;dispatch(Ljava/lang/String;Ljava/util/function/Function;Ljava/util/function/Function;)Lcom/mojang/serialization/Codec;"))
@Redirect(method = "codec", at = @At(value = "INVOKE", target = "Lcom/mojang/serialization/Codec;dispatch(Ljava/lang/String;Ljava/util/function/Function;Ljava/util/function/Function;)Lcom/mojang/serialization/Codec;", remap = false))
private static <O, S extends StateHolder<O, S>> Codec<S> obtainCodec(Codec<O> codec, String typeKey, Function<S, O> type, Function<O, ? extends Codec<S>> codecFn, Codec<O> codecMethodArg, Function<O, S> stateSupplier) {
return codec.dispatch(typeKey, type, block -> {
if(block instanceof Block) {

View File

@ -0,0 +1,19 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_dfu;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.types.Type;
import net.minecraft.world.level.block.entity.BlockEntityType;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
/**
* Prevent fetchChoiceType calls from loading DFU early. Vanilla doesn't need the return values here.
*/
@Mixin(BlockEntityType.class)
public class BlockEntityTypeMixin {
@Redirect(method = "register", at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;fetchChoiceType(Lcom/mojang/datafixers/DSL$TypeReference;Ljava/lang/String;)Lcom/mojang/datafixers/types/Type;"))
private static Type<?> skipSchemaCheck(DSL.TypeReference ref, String s) {
return null;
}
}

View File

@ -0,0 +1,19 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_dfu;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.types.Type;
import net.minecraft.world.entity.EntityType;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
/**
* Prevent fetchChoiceType calls from loading DFU early. Vanilla doesn't need the return values here.
*/
@Mixin(EntityType.Builder.class)
public class EntityTypeBuilderMixin {
@Redirect(method = "build", at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;fetchChoiceType(Lcom/mojang/datafixers/DSL$TypeReference;Ljava/lang/String;)Lcom/mojang/datafixers/types/Type;"))
private Type<?> skipSchemaCheck(DSL.TypeReference ref, String s) {
return null;
}
}

View File

@ -1,15 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_dfu;
import net.minecraft.SharedConstants;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(SharedConstants.class)
public class SharedConstantsMixin {
@Inject(method = "<clinit>", at = @At("RETURN"))
private static void skipSchemaCheck(CallbackInfo ci) {
SharedConstants.CHECK_DATA_FIXER_SCHEMA = false;
}
}

View File

@ -16,7 +16,7 @@ import java.lang.reflect.Type;
public class BlockElementFaceDeserializerMixin {
@Redirect(method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/client/renderer/block/model/BlockElementFace;",
at = @At(value = "INVOKE", target = "Lcom/google/gson/JsonDeserializationContext;deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;)Ljava/lang/Object;", ordinal = 0))
at = @At(value = "INVOKE", target = "Lcom/google/gson/JsonDeserializationContext;deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;)Ljava/lang/Object;", ordinal = 0, remap = false))
private Object skipUvsForInitialLoad(JsonDeserializationContext context, JsonElement element, Type type) {
return UVController.useDummyUv.get() ? UVController.dummyUv : context.deserialize(element, type);
}

View File

@ -48,6 +48,7 @@ public abstract class ItemModelShaperMixin {
}
/**
* @author embeddedt
* @reason Get the stored location for that item and meta, and get the model
* from that location from the model manager.
**/
@ -58,6 +59,7 @@ public abstract class ItemModelShaperMixin {
}
/**
* @author embeddedt
* @reason Don't get all models during init (with dynamic loading, that would
* generate them all). Just store location instead.
**/
@ -67,6 +69,7 @@ public abstract class ItemModelShaperMixin {
}
/**
* @author embeddedt
* @reason Disable cache rebuilding (with dynamic loading, that would generate
* all models).
**/

View File

@ -15,7 +15,7 @@ public class MappedRegistryMixin {
*/
@Redirect(
method = "registerMapping(ILnet/minecraft/resources/ResourceKey;Ljava/lang/Object;Lcom/mojang/serialization/Lifecycle;Z)Ljava/lang/Object;",
at = @At(value = "INVOKE", target = "Lit/unimi/dsi/fastutil/objects/ObjectList;size(I)V")
at = @At(value = "INVOKE", target = "Lit/unimi/dsi/fastutil/objects/ObjectList;size(I)V", remap = false)
)
private void setSizeSmart(ObjectList<?> list, int size) {
if(list instanceof ObjectArrayList && size > list.size()) {

View File

@ -0,0 +1,31 @@
package org.embeddedt.modernfix.common.mixin.perf.mojang_registry_size;
import com.google.common.collect.ArrayTable;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Table;
import net.minecraft.world.level.block.state.StateHolder;
import net.minecraft.world.level.block.state.properties.Property;
import org.embeddedt.modernfix.annotation.RequiresMod;
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;
/**
* Minor mixin to avoid duplicate empty neighbor tables, used when FerriteCore is not present. Won't be enabled in 99% of
* modded environments but is useful for testing in dev without dragging in Fabric API.
*/
@Mixin(StateHolder.class)
@RequiresMod("!ferritecore")
public class StateHolderMixin {
@Shadow private Table<Property<?>, Comparable<?>, ?> neighbours;
/* optimize the case where block has no properties */
@Inject(method = "populateNeighbours", at = @At("RETURN"), require = 0)
private void replaceEmptyTable(CallbackInfo ci) {
if((this.neighbours instanceof ArrayTable || this.neighbours instanceof HashBasedTable) && this.neighbours.isEmpty())
this.neighbours = ImmutableTable.of();
}
}

View File

@ -3,13 +3,10 @@ package org.embeddedt.modernfix.common.mixin.perf.reduce_blockstate_cache_rebuil
import net.minecraft.world.level.block.state.BlockBehaviour;
import org.embeddedt.modernfix.duck.IBlockState;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Dynamic;
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.CallbackInfoReturnable;
@Mixin(BlockBehaviour.BlockStateBase.class)
@ -30,7 +27,7 @@ public abstract class BlockStateBaseMixin implements IBlockState {
return cacheInvalid;
}
private BlockBehaviour.BlockStateBase.Cache generateCache(BlockBehaviour.BlockStateBase base) {
private void mfix$generateCache() {
if(cacheInvalid) {
// Ensure that only one block's cache is built at a time
synchronized (BlockBehaviour.BlockStateBase.class) {
@ -49,7 +46,6 @@ public abstract class BlockStateBaseMixin implements IBlockState {
}
}
return this.cache;
}
@Redirect(method = "*", at = @At(
@ -59,24 +55,7 @@ public abstract class BlockStateBaseMixin implements IBlockState {
ordinal = 0
))
private BlockBehaviour.BlockStateBase.Cache dynamicCacheGen(BlockBehaviour.BlockStateBase base) {
return generateCache(base);
}
@Dynamic
@Inject(method = "getPathNodeType", at = @At("HEAD"), require = 0, remap = false)
private void generateCacheLithium(CallbackInfoReturnable<?> cir) {
generateCache((BlockBehaviour.BlockStateBase)(Object)this);
}
@Dynamic
@Inject(method = "getNeighborPathNodeType", at = @At("HEAD"), require = 0, remap = false)
private void generateCacheLithium2(CallbackInfoReturnable<?> cir) {
generateCache((BlockBehaviour.BlockStateBase)(Object)this);
}
@Dynamic
@Inject(method = "getAllFlags", at = @At("HEAD"), require = 0, remap = false)
private void generateCacheLithium3(CallbackInfoReturnable<?> cir) {
generateCache((BlockBehaviour.BlockStateBase)(Object)this);
mfix$generateCache();
return this.cache;
}
}

View File

@ -1,14 +1,18 @@
package org.embeddedt.modernfix.core;
import com.google.common.collect.ImmutableSet;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.embeddedt.modernfix.core.config.ModernFixEarlyConfig;
import org.embeddedt.modernfix.core.config.Option;
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
import org.embeddedt.modernfix.world.ThreadDumper;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import org.spongepowered.asm.mixin.transformer.meta.MixinMerged;
import java.io.File;
import java.util.*;
@ -146,6 +150,111 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin {
@Override
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
if(mixinClassName.equals("org.embeddedt.modernfix.common.mixin.perf.reduce_blockstate_cache_rebuilds.BlockStateBaseMixin")) {
try {
applyBlockStateCacheScan(targetClass);
} catch(RuntimeException e) {
ModernFixMixinPlugin.instance.logger.error("Applying blockstate cache ASM patch failed", e);
}
}
ModernFixPlatformHooks.INSTANCE.applyASMTransformers(mixinClassName, targetClass);
}
private void applyBlockStateCacheScan(ClassNode targetClass) {
Set<String> initCacheMethodNames = ImmutableSet.of("m_60611_", "func_215692_c", "method_26200", "initCache");
Set<String> whitelistedInjections = ImmutableSet.of(
"getFluidState", "method_26227", "m_60819_", "func_204520_s"
);
Map<String, MethodNode> injectorMethodNames = new HashMap<>();
Map<String, String> injectorMixinSource = new HashMap<>();
String descriptor = Type.getDescriptor(MixinMerged.class);
for(MethodNode m : targetClass.methods) {
if((m.access & Opcodes.ACC_STATIC) != 0)
continue;
Set<AnnotationNode> seenNodes = new HashSet<>();
if(m.invisibleAnnotations != null) {
for(AnnotationNode ann : m.invisibleAnnotations) {
if(ann.desc.equals(descriptor)) {
seenNodes.add(ann);
}
}
}
if(m.visibleAnnotations != null) {
for(AnnotationNode ann : m.visibleAnnotations) {
if(ann.desc.equals(descriptor)) {
seenNodes.add(ann);
}
}
}
if(seenNodes.size() > 0) {
injectorMethodNames.put(m.name, m);
for(AnnotationNode node : seenNodes) {
for(int i = 0; i < node.values.size(); i += 2) {
if(Objects.equals(node.values.get(i), "mixin")) {
injectorMixinSource.put(m.name, (String)node.values.get(i + 1));
break;
}
}
}
}
}
Set<String> cacheCalledInjectors = new HashSet<>();
// Search for initCache in the class
for(MethodNode m : targetClass.methods) {
if((m.access & Opcodes.ACC_STATIC) != 0)
continue;
if(initCacheMethodNames.contains(m.name)) {
// This is it. Check for any injectors it calls
for(AbstractInsnNode n : m.instructions) {
if(n instanceof MethodInsnNode) {
MethodInsnNode invoke = (MethodInsnNode)n;
if(((MethodInsnNode)n).owner.equals(targetClass.name) && injectorMethodNames.containsKey(((MethodInsnNode)n).name)) {
cacheCalledInjectors.add(invoke.name);
}
}
}
break;
}
}
Set<String> accessedFieldNames = new HashSet<>();
// We now know all methods that have been injected into initCache. See what fields they write to
injectorMethodNames.forEach((name, method) -> {
if(cacheCalledInjectors.contains(name)) {
for(AbstractInsnNode n : method.instructions) {
if(n instanceof FieldInsnNode) {
FieldInsnNode fieldAcc = (FieldInsnNode)n;
if(fieldAcc.getOpcode() == Opcodes.PUTFIELD && fieldAcc.owner.equals(targetClass.name)) {
accessedFieldNames.add(fieldAcc.name);
}
}
}
}
});
// Lastly, scan all injected methods and see if they retrieve from the field. If so, inject a generateCache
// call at the start.
injectorMethodNames.forEach((name, method) -> {
// skip whitelisted injectors, and injectors called by initCache itself (to prevent recursion)
if(whitelistedInjections.contains(name) || cacheCalledInjectors.contains(name))
return;
boolean needInjection = false;
for(AbstractInsnNode n : method.instructions) {
if(n instanceof FieldInsnNode) {
FieldInsnNode fieldAcc = (FieldInsnNode)n;
if(fieldAcc.getOpcode() == Opcodes.GETFIELD && accessedFieldNames.contains(fieldAcc.name)) {
needInjection = true;
break;
}
}
}
if(needInjection) {
ModernFixMixinPlugin.instance.logger.info("Injecting BlockStateBase cache population hook into {} from {}",
name, injectorMixinSource.getOrDefault(name, "[unknown mixin]"));
// inject this.mfix$generateCache() at method head
InsnList injection = new InsnList();
injection.add(new VarInsnNode(Opcodes.ALOAD, 0));
injection.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, targetClass.name, "mfix$generateCache", "()V"));
method.instructions.insert(injection);
}
});
}
}

View File

@ -11,6 +11,7 @@ import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.annotation.IgnoreOutsideDev;
import org.embeddedt.modernfix.annotation.RequiresMod;
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Type;
@ -112,7 +113,7 @@ public class ModernFixEarlyConfig {
if(annotation.values.get(i).equals("value")) {
String modId = (String)annotation.values.get(i + 1);
if(modId != null) {
requiredModPresent = modPresent(modId);
requiredModPresent = modId.startsWith("!") ? !modPresent(modId.substring(1)) : modPresent(modId);
requiredModId = modId;
}
break;
@ -253,6 +254,17 @@ public class ModernFixEarlyConfig {
}
}
private void readJVMProperties() {
for(String optionKey : this.options.keySet()) {
String value = System.getProperty("modernfix.config." + optionKey);
if(value == null || value.length() == 0)
continue;
boolean isEnabled = Boolean.valueOf(value);
ModernFixMixinPlugin.instance.logger.info("Configured {} to '{}' via JVM property.", optionKey, isEnabled);
this.options.get(optionKey).setEnabled(isEnabled, true);
}
}
private void readProperties(Properties props) {
if(ALLOW_OVERRIDE_OVERRIDES)
LOGGER.fatal("JVM argument given to override mod overrides. Issues opened with this option present will be ignored unless they can be reproduced without.");
@ -341,6 +353,8 @@ public class ModernFixEarlyConfig {
} catch (IOException e) {
LOGGER.warn("Could not write configuration file", e);
}
config.readJVMProperties();
}
return config;

View File

@ -0,0 +1,7 @@
package org.embeddedt.modernfix.duck;
import org.embeddedt.modernfix.chunk.SafeBlockGetter;
public interface ISafeBlockGetter {
SafeBlockGetter mfix$getSafeBlockGetter();
}

View File

@ -1,5 +1,6 @@
package org.embeddedt.modernfix.dynamicresources;
import com.google.common.collect.ImmutableSet;
import com.mojang.math.Transformation;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.client.renderer.block.model.BakedQuad;
@ -25,6 +26,15 @@ import java.util.function.BiFunction;
import java.util.stream.Collectors;
public class DynamicBakedModelProvider implements Map<ResourceLocation, BakedModel> {
/**
* The list of blacklisted resource locations that are never baked as top-level models.
*
* This is a hack to get around the fact that we don't really know exactly what models were supposed to end up
* in the baked registry ahead of time.
*/
private static final ImmutableSet<ResourceLocation> BAKE_SKIPPED_TOPLEVEL = ImmutableSet.<ResourceLocation>builder()
.add(new ResourceLocation("custommachinery", "block/custom_machine_block"))
.build();
public static DynamicBakedModelProvider currentInstance = null;
private final ModelBakery bakery;
private final Map<Triple<ResourceLocation, Transformation, Boolean>, BakedModel> bakedCache;
@ -135,7 +145,10 @@ public class DynamicBakedModelProvider implements Map<ResourceLocation, BakedMod
return model;
else {
try {
model = bakery.bake((ResourceLocation)o, BlockModelRotation.X0_Y0);
if(BAKE_SKIPPED_TOPLEVEL.contains((ResourceLocation)o))
model = missingModel;
else
model = bakery.bake((ResourceLocation)o, BlockModelRotation.X0_Y0);
} catch(RuntimeException e) {
ModernFix.LOGGER.error("Exception baking {}: {}", o, e);
model = missingModel;

View File

@ -57,6 +57,32 @@ public class ModelBakeryHelpers {
*/
public static final int MAX_MODEL_LIFETIME_SECS = 300;
/**
* These folders will have all textures stitched onto the atlas when dynamic resources is enabled.
*/
public static String[] getExtraTextureFolders() {
return new String[] {
"attachment",
"bettergrass",
"block",
"blocks",
"cape",
"entity/bed",
"entity/chest",
"item",
"items",
"model",
"models",
"part",
"pipe",
"ropebridge",
"runes",
"solid_block",
"spell_effect",
"spell_projectile"
};
}
private static JsonElement parseStream(InputStream stream) {
JsonParser parser = new JsonParser();
JsonReader jsonReader = new JsonReader(new InputStreamReader(stream, StandardCharsets.UTF_8));

View File

@ -0,0 +1,184 @@
package org.embeddedt.modernfix.registry;
import com.google.common.collect.BiMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
@SuppressWarnings("unchecked")
public class DirectStorageBiMap<K, V> implements BiMap<K, V> {
private final Function<V, K> keyGetter;
private final BiConsumer<V, K> keySetter;
private final Map<K, V> forwardMap;
public DirectStorageBiMap(Function<V, K> keyGetter, BiConsumer<V, K> keySetter) {
Objects.requireNonNull(keyGetter);
Objects.requireNonNull(keySetter);
this.keyGetter = keyGetter;
this.keySetter = keySetter;
this.forwardMap = new Object2ObjectOpenHashMap<>();
}
@Override
public int size() {
return this.forwardMap.size();
}
@Override
public boolean isEmpty() {
return this.forwardMap.isEmpty();
}
@Override
public boolean containsKey(Object o) {
return this.forwardMap.containsKey(o);
}
@Override
public boolean containsValue(Object o) {
return o != null && keyGetter.apply((V)o) != null;
}
@Override
public V get(Object o) {
return this.forwardMap.get(o);
}
@Override
public V put(K key, V value) {
if(this.forwardMap.containsKey(key) || (value != null && keyGetter.apply(value) != null))
throw new IllegalArgumentException("Already have mapping for " + key);
return forcePut(key, value);
}
@Override
public V remove(Object o) {
return put((K)o, null);
}
@Override
public V forcePut(K key, V value) {
V previousValue = this.forwardMap.put(key, value);
if(previousValue != null)
keySetter.accept(previousValue, null);
if(value != null)
keySetter.accept(value, key);
return previousValue;
}
@Override
public void putAll(Map<? extends K, ? extends V> map) {
map.forEach(this::put);
}
@Override
public void clear() {
for(V value : this.forwardMap.values()) {
if(value != null)
keySetter.accept(value, null);
}
this.forwardMap.clear();
}
@NotNull
@Override
public Set<K> keySet() {
return this.forwardMap.keySet();
}
@Override
public Set<V> values() {
return new HashSet<>(this.forwardMap.values());
}
@NotNull
@Override
public Set<Entry<K, V>> entrySet() {
return this.forwardMap.entrySet();
}
@Override
public BiMap<V, K> inverse() {
return new Reverse();
}
class Reverse implements BiMap<V, K> {
@Override
public int size() {
return DirectStorageBiMap.this.size();
}
@Override
public boolean isEmpty() {
return DirectStorageBiMap.this.isEmpty();
}
@Override
public boolean containsKey(Object o) {
return DirectStorageBiMap.this.containsValue(o);
}
@Override
public boolean containsValue(Object o) {
return DirectStorageBiMap.this.containsKey(o);
}
@Override
public K get(Object o) {
return o == null ? null : keyGetter.apply((V)o);
}
@Override
public K put(V key, K value) {
throw new UnsupportedOperationException();
}
@Override
public K remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
public K forcePut(V key, K value) {
throw new UnsupportedOperationException();
}
@Override
public void putAll(Map<? extends V, ? extends K> map) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@NotNull
@Override
public Set<V> keySet() {
return DirectStorageBiMap.this.values();
}
@Override
public Set<K> values() {
return DirectStorageBiMap.this.keySet();
}
@NotNull
@Override
public Set<Entry<V, K>> entrySet() {
return DirectStorageBiMap.this.entrySet().stream()
.map(entry -> new AbstractMap.SimpleImmutableEntry<>(entry.getValue(), entry.getKey()))
.collect(Collectors.toSet());
}
@Override
public BiMap<K, V> inverse() {
return DirectStorageBiMap.this;
}
}
}

View File

@ -0,0 +1,8 @@
package org.embeddedt.modernfix.registry;
import net.minecraft.resources.ResourceLocation;
public interface DirectStorageRegistryObject {
ResourceLocation mfix$getResourceKey();
void mfix$setResourceKey(ResourceLocation key);
}

View File

@ -0,0 +1,20 @@
package org.embeddedt.modernfix.registry;
import com.mojang.serialization.Lifecycle;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
public class LifecycleMap<T> extends Reference2ReferenceOpenHashMap<T, Lifecycle> {
public LifecycleMap() {
this.defaultReturnValue(Lifecycle.stable());
}
@Override
public Lifecycle put(T t, Lifecycle lifecycle) {
if(lifecycle != defRetValue)
return super.put(t, lifecycle);
else {
// need the duplicate containsKey/get logic here to override the default return value
return super.containsKey(t) ? super.get(t) : null;
}
}
}

View File

@ -0,0 +1,34 @@
package org.embeddedt.modernfix.registry;
import com.google.common.collect.BiMap;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import java.util.Map;
import java.util.function.Function;
public class RegistryStorage {
public static BiMap<ResourceLocation, DirectStorageRegistryObject> createStorage() {
return new DirectStorageBiMap<>(DirectStorageRegistryObject::mfix$getResourceKey, DirectStorageRegistryObject::mfix$setResourceKey);
}
public static <T> BiMap<ResourceKey<T>, DirectStorageRegistryObject> createKeyStorage(ResourceKey<? extends Registry<T>> registryKey, BiMap<ResourceLocation, DirectStorageRegistryObject> storage) {
if(storage instanceof DirectStorageBiMap) {
DirectStorageBiMap<ResourceLocation, DirectStorageRegistryObject> directStorageBiMap = (DirectStorageBiMap<ResourceLocation, DirectStorageRegistryObject>)storage;
// silently ignore put/putAll calls on this map
return new TransformingBiMap<ResourceLocation, DirectStorageRegistryObject, ResourceKey<T>, DirectStorageRegistryObject>(directStorageBiMap, loc -> ResourceKey.create(registryKey, loc), ResourceKey::location, Function.identity(), Function.identity()) {
@Override
public DirectStorageRegistryObject put(ResourceKey<T> key, DirectStorageRegistryObject value) {
return null;
}
@Override
public void putAll(Map<? extends ResourceKey<T>, ? extends DirectStorageRegistryObject> map) {
}
};
} else
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,224 @@
package org.embeddedt.modernfix.registry;
import com.google.common.collect.BiMap;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterators;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.function.Function;
public class TransformingBiMap<KFrom, VFrom, KTo, VTo> implements BiMap<KTo, VTo> {
private final BiMap<KFrom, VFrom> delegate;
private final Function<KFrom, KTo> keyFwd;
private final Function<KTo, KFrom> keyBack;
private final Function<VFrom, VTo> valueFwd;
private final Function<VTo, VFrom> valueBack;
public TransformingBiMap(BiMap<KFrom, VFrom> map, Function<KFrom, KTo> keyFwd, Function<KTo, KFrom> keyBack, Function<VFrom, VTo> valueFwd, Function<VTo, VFrom> valueBack) {
this.delegate = map;
this.keyFwd = keyFwd;
this.keyBack = keyBack;
this.valueFwd = valueFwd;
this.valueBack = valueBack;
}
private KFrom keyBack(KTo key) {
return key == null ? null : this.keyBack.apply(key);
}
private KTo keyFwd(KFrom key) {
return key == null ? null : this.keyFwd.apply(key);
}
private VFrom valueBack(VTo value) {
return value == null ? null : this.valueBack.apply(value);
}
private VTo valueFwd(VFrom value) {
return value == null ? null : this.valueFwd.apply(value);
}
@Override
public int size() {
return this.delegate.size();
}
@Override
public boolean isEmpty() {
return this.delegate.isEmpty();
}
@Override
public boolean containsKey(Object o) {
return this.delegate.containsKey(keyBack((KTo)o));
}
@Override
public boolean containsValue(Object o) {
return false;
}
@Override
public VTo get(Object o) {
return valueFwd(this.delegate.get(keyBack((KTo)o)));
}
@Override
public VTo put(KTo key, VTo value) {
return valueFwd(this.delegate.put(keyBack(key), valueBack(value)));
}
@Override
public VTo remove(Object o) {
return valueFwd(this.delegate.remove(keyBack((KTo)o)));
}
@Override
public VTo forcePut(KTo key, VTo value) {
return valueFwd(this.delegate.forcePut(keyBack(key), valueBack(value)));
}
@Override
public void putAll(Map<? extends KTo, ? extends VTo> map) {
map.forEach((key, value) -> {
this.delegate.put(keyBack(key), valueBack(value));
});
}
@Override
public void clear() {
this.delegate.clear();
}
@NotNull
@Override
public Set<KTo> keySet() {
return new TransformingSet<>(this.delegate.keySet(), this.keyFwd, this.keyBack);
}
@Override
public Set<VTo> values() {
return new TransformingSet<>(this.delegate.values(), this.valueFwd, this.valueBack);
}
@NotNull
@Override
public Set<Entry<KTo, VTo>> entrySet() {
return new TransformingSet<>(this.delegate.entrySet(), entry -> {
return new AbstractMap.SimpleImmutableEntry<>(keyFwd(entry.getKey()), valueFwd(entry.getValue()));
}, entry -> {
return new AbstractMap.SimpleImmutableEntry<>(keyBack(entry.getKey()), valueBack(entry.getValue()));
});
}
@Override
public BiMap<VTo, KTo> inverse() {
return new TransformingBiMap<>(this.delegate.inverse(), this.valueFwd, this.valueBack, this.keyFwd, this.keyBack);
}
static class TransformingSet<TypeFrom, TypeTo> implements Set<TypeTo> {
private final Set<TypeFrom> delegate;
private final Function<TypeFrom, TypeTo> forward;
private final Function<TypeTo, TypeFrom> reverse;
public TransformingSet(Set<TypeFrom> set, Function<TypeFrom, TypeTo> forward, Function<TypeTo, TypeFrom> reverse) {
this.delegate = set;
this.forward = forward;
this.reverse = reverse;
}
private TypeTo forward(TypeFrom t) {
return t == null ? null : this.forward.apply(t);
}
private TypeFrom reverse(TypeTo t) {
return t == null ? null : this.reverse.apply(t);
}
@Override
public int size() {
return this.delegate.size();
}
@Override
public boolean isEmpty() {
return this.delegate.isEmpty();
}
@Override
public boolean contains(Object o) {
return this.delegate.contains(reverse((TypeTo)o));
}
@NotNull
@Override
public Iterator<TypeTo> iterator() {
return Iterators.transform(this.delegate.iterator(), this::forward);
}
@NotNull
@Override
public Object[] toArray() {
Object[] array = this.delegate.toArray();
for(int i = 0; i < array.length; i++) {
array[i] = this.forward((TypeFrom)array[i]);
}
return array;
}
@NotNull
@Override
public <T> T[] toArray(@NotNull T[] ts) {
if(ts.length >= this.delegate.size()) {
Object[] setContents = toArray();
System.arraycopy(setContents, 0, ts, 0, Math.min(setContents.length, ts.length));
if(ts.length > setContents.length)
ts[setContents.length] = null;
return ts;
} else {
T[] realArray = Arrays.copyOf(ts, this.delegate.size());
Iterator<TypeTo> iterator = this.iterator();
int i = 0;
while(iterator.hasNext())
realArray[i++] = (T)iterator.next();
return realArray;
}
}
@Override
public boolean add(TypeTo typeFrom) {
return this.delegate.add(reverse(typeFrom));
}
@Override
public boolean remove(Object o) {
return this.delegate.remove(reverse((TypeTo)o));
}
@Override
public boolean containsAll(@NotNull Collection<?> collection) {
return this.delegate.containsAll(Collections2.transform(collection, obj -> reverse((TypeTo)obj)));
}
@Override
public boolean addAll(@NotNull Collection<? extends TypeTo> collection) {
return this.delegate.addAll(Collections2.transform(collection, this::reverse));
}
@Override
public boolean retainAll(@NotNull Collection<?> collection) {
return this.delegate.retainAll(Collections2.transform(collection, obj -> reverse((TypeTo)obj)));
}
@Override
public boolean removeAll(@NotNull Collection<?> collection) {
return this.delegate.removeAll(Collections2.transform(collection, obj -> reverse((TypeTo)obj)));
}
@Override
public void clear() {
this.delegate.clear();
}
}
}

View File

@ -1,11 +1,10 @@
package org.embeddedt.modernfix.screen;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.Util;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Style;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.network.chat.*;
import org.jetbrains.annotations.Nullable;
public class ModernFixConfigScreen extends Screen {
@ -13,7 +12,7 @@ public class ModernFixConfigScreen extends Screen {
private Screen lastScreen;
public boolean madeChanges = false;
private Button doneButton;
private Button doneButton, wikiButton;
public ModernFixConfigScreen(Screen lastScreen) {
super(new TranslatableComponent("modernfix.config"));
this.lastScreen = lastScreen;
@ -23,9 +22,13 @@ public class ModernFixConfigScreen extends Screen {
protected void init() {
this.optionList = new OptionList(this, this.minecraft);
this.children.add(this.optionList);
this.doneButton = new Button(this.width / 2 - 100, this.height - 29, 200, 20, CommonComponents.GUI_DONE, (arg) -> {
this.wikiButton = new Button(this.width / 2 - 155, this.height - 29, 150, 20, new TranslatableComponent("modernfix.config.wiki"), (arg) -> {
Util.getPlatform().openUri("https://github.com/embeddedt/ModernFix/wiki/Summary-of-Patches");
});
this.doneButton = new Button(this.width / 2 - 155 + 160, this.height - 29, 150, 20, CommonComponents.GUI_DONE, (arg) -> {
this.onClose();
});
this.addButton(this.wikiButton);
this.addButton(this.doneButton);
}

View File

@ -12,7 +12,7 @@ import java.util.Map;
* Replacement backing map for CompoundTags that interns keys.
*/
public class CanonizingStringMap<T> extends HashMap<String, T> {
private static final Interner<String> KEY_INTERNER = Interners.newStrongInterner();
private static final Interner<String> KEY_INTERNER = Interners.newWeakInterner();
private static String intern(String key) {
return key != null ? KEY_INTERNER.intern(key) : null;

View File

@ -0,0 +1,67 @@
package org.embeddedt.modernfix.util;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import static java.util.concurrent.TimeUnit.*;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
/**
* All code here is derived from Guava's Stopwatch and Platform classes.
* Too bad it's not a public method call indeed...
*/
public class TimeFormatter {
static String formatCompact4Digits(double value) {
return String.format(Locale.ROOT, "%.4g", value);
}
public static String formatNanos(long nanos) {
TimeUnit unit = chooseUnit(nanos);
double value = (double) nanos / NANOSECONDS.convert(1, unit);
return formatCompact4Digits(value) + " " + abbreviate(unit);
}
private static TimeUnit chooseUnit(long nanos) {
if (DAYS.convert(nanos, NANOSECONDS) > 0) {
return DAYS;
}
if (HOURS.convert(nanos, NANOSECONDS) > 0) {
return HOURS;
}
if (MINUTES.convert(nanos, NANOSECONDS) > 0) {
return MINUTES;
}
if (SECONDS.convert(nanos, NANOSECONDS) > 0) {
return SECONDS;
}
if (MILLISECONDS.convert(nanos, NANOSECONDS) > 0) {
return MILLISECONDS;
}
if (MICROSECONDS.convert(nanos, NANOSECONDS) > 0) {
return MICROSECONDS;
}
return NANOSECONDS;
}
private static String abbreviate(TimeUnit unit) {
switch (unit) {
case NANOSECONDS:
return "ns";
case MICROSECONDS:
return "\u03bcs"; // μs
case MILLISECONDS:
return "ms";
case SECONDS:
return "s";
case MINUTES:
return "min";
case HOURS:
return "h";
case DAYS:
return "d";
default:
throw new AssertionError();
}
}
}

View File

@ -5,16 +5,23 @@ import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
public class ThreadDumper {
private static final String STACKTRACE_TAIL = "\t...\n\n";
public static String obtainThreadDump() {
ThreadMXBean threadmxbean = ManagementFactory.getThreadMXBean();
ThreadInfo[] athreadinfo = threadmxbean.dumpAllThreads(true, true);
StringBuilder sb = new StringBuilder();
sb.append("Thread Dump:\n");
for(ThreadInfo threadinfo : athreadinfo) {
sb.append(threadinfo);
String tInfo = threadinfo.toString();
StackTraceElement[] elements = threadinfo.getStackTrace();
if(elements.length > 8) {
sb.append("extended trace:\n");
if(tInfo.endsWith(STACKTRACE_TAIL))
tInfo = tInfo.substring(0, tInfo.length() - STACKTRACE_TAIL.length());
else
tInfo = tInfo + "extended trace:\n";
}
sb.append(tInfo);
if(elements.length > 8) {
for(int i = 8; i < elements.length; i++) {
sb.append("\tat ");
sb.append(elements[i]);

View File

@ -4,10 +4,12 @@
"modernfix.jei_load": "Loading JEI, this may take a while",
"modernfix.no_lazydfu": "LazyDFU is not installed. If Minecraft needs to update game data from an older version, there may be noticeable lag.",
"modernfix.no_ferritecore": "FerriteCore is not installed. Memory usage will be very high.",
"modernfix.connectedness_dynresoruces": "Connectedness and ModernFix's dynamic resources option are not compatible. Remove Connectedness or disable dynamic resources in the ModernFix config.",
"modernfix.perf_mod_warning": "It is recommended to install the mods, but the warning(s) can be disabled in the ModernFix config.",
"modernfix.config": "ModernFix mixin config",
"modernfix.config.done_restart": "Done (restart required)",
"modernfix.message.reload_config": "Run /mfrc after changing configs on disk for them to take effect.",
"modernfix.config.wiki": "Open wiki",
"modernfix.message.reload_config": "A mod config file change was detected. To prevent loading files that aren't done saving, reloading must be triggered by running /mfrc.",
"modernfix.option.on": "on",
"modernfix.option.off": "off",
"modernfix.option.disabled": "disabled",

View File

@ -0,0 +1,117 @@
{
"key.modernfix": "ModernFix",
"key.modernfix.config": "Apri la schermata configurazione",
"modernfix.jei_load": "Caricamento JEI in corso, potrebbe richiedere un po' di tempo",
"modernfix.no_lazydfu": "LazyDFU non è installato. Se Minecraft deve aggiornare i dati di gioco da una versione precedente, potrebbe esserci un notevole lag.",
"modernfix.no_ferritecore": "FerriteCore non è installato. L'uso della memoria sarà molto elevato.",
"modernfix.connectedness_dynresoruces": "Connectedness e l'opzione risorse dinamiche di ModernFix non sono compatibili. Rimuovi Connectedness o disabilita le risorse dinamiche nella configurazione di ModernFix.",
"modernfix.perf_mod_warning": "Si consiglia di installare le mod, ma gli avvertimenti possono essere disabilitati nella configurazione di ModernFix.",
"modernfix.config": "Configurazione mixin ModernFix",
"modernfix.config.done_restart": "Fatto (riavvio richiesto)",
"modernfix.message.reload_config": "È stata rilevata una modifica al file di configurazione. Per evitare di caricare file che non sono ancora stati salvati, è necessario avviare nuovamente il caricamento eseguendo /mfrc.",
"modernfix.option.on": "attivo",
"modernfix.option.off": "disattivo",
"modernfix.option.disabled": "disabilitato",
"modernfix.option.enabled": "abilitato",
"modernfix.option.mod_override": " da mod [%s]",
"modernfix.config.not_default": " (modificato)",
"asynclocator.map.locating": "Mappa (Ricerca in corso...)",
"asynclocator.map.none": "Mappa (Nessuna caratteristica vicina trovata)",
"modernfix.option.category.performance": "Prestazioni",
"modernfix.option.category.performance.description": "Funzionalità che aiutano a migliorare le prestazioni di gioco/avvio",
"modernfix.option.category.bugfixes": "Risoluzione errori",
"modernfix.option.category.bugfixes.description": "Correzioni di bug di base per migliorare la stabilità del gioco",
"modernfix.option.category.troubleshooting": "Risoluzione di problemi/Strumenti",
"modernfix.option.category.troubleshooting.description": "Funzionalità pensate per assistere nella diagnosi dei problemi",
"modernfix.option.category.expert_only": "Solo per esperti",
"modernfix.option.category.expert_only.description": "Non modificare a meno che tu sappia cosa stai facendo",
"modernfix.option.name.mixin.perf.async_jei": "Caricamento JEI in background",
"modernfix.option.mixin.perf.async_jei": "Solo per la versione 1.16. **Un'ottimizzazione fondamentale.** Parcheggia JEI per eseguire il ricaricamento su un thread in background, eliminando completamente il lungo ritardo che aggiunge al caricamento del mondo.",
"modernfix.option.mixin.perf.async_locator": "Solo per la versione 1.16. Riporta le patch di Async Locator mod per eliminare i blocchi del server associati a `/locate`, generazione di tabelle del bottino, ecc.",
"modernfix.option.mixin.perf.biome_zoomer": "Solo per la versione 1.16. Ottimizzazione minore per migliorare le prestazioni del zoom sulla biome usando la logica dalla versione 1.18.",
"modernfix.option.mixin.perf.blast_search_trees": "Tutte le versioni. Se sono installate REI o JEI, la costruzione delle tabelle di ricerca vanilla per la ricerca creativa viene disabilitata e la ricerca viene invece effettuata utilizzando le implementazioni di ricerca di queste mod. Questo risparmia alcuni secondi durante il caricamento del mondo e probabilmente risparmia anche un po' di RAM (sebbene non sia stato misurato).",
"modernfix.option.mixin.perf.boost_worker_count": "Solo per la versione 1.16. Rimuove il limite codificato sul conteggio dei thread dei lavoratori, simile a quanto fatto da Mojang nella versione 1.18.",
"modernfix.option.mixin.perf.cache_blockstate_cache_arrays": "Tutte le versioni. Evita di creare copie nuove di array enumerativi ogni volta che viene inizializzata una cache di blockstate. Ottimizzazione minore, ma semplice da fare.",
"modernfix.option.mixin.perf.cache_model_materials": "Tutte le versioni. Memorizza la collezione `RenderMaterial` (texture) e la lista delle dipendenze che i modelli restituiscono invece di richiederne il calcolo ad ogni richiesta. Aiuta ad accelerare il processo di caricamento/modellazione dei modelli.",
"modernfix.option.mixin.perf.cache_strongholds": "Tutte le versioni. Salva l'elenco generato delle posizioni delle fortezze con il mondo, invece di rigenerarlo ad ogni caricamento del mondo. Risparmia un po' di tempo nella versione 1.16, e molto di più nelle versioni 1.18 e 1.19.",
"modernfix.option.mixin.perf.cache_upgraded_structures": "Tutte le versioni. Molti mod includono file di strutture obsoleti, il che richiede al gioco di aggiornarli utilizzando DFU ogni volta che vengono caricati. Questo può essere piuttosto lento. Questa patch aggiunge una logica per invece salvare la versione aggiornata della struttura e riutilizzarla al caricamento successivo. Per gestire il caso in cui il mod cambia un file di struttura ma non il nome, l'hash del file originale viene confrontato con la versione in cache e se non corrispondono la struttura verrà nuovamente aggiornata.",
"modernfix.option.mixin.perf.compress_biome_container": "Solo per la versione 1.16. Ottimizzazione minore presa in prestito da Hydrogen, che cerca di risparmiare spazio nel contenitore della biome quando possibile. Questo viene disabilitato automaticamente se sono installati mod in conflitto come BetterEnd o Chocolate.",
"modernfix.option.mixin.perf.datapack_reload_exceptions": "Tutte le versioni. Riduce lo spam nei log e potrebbe migliorare leggermente la velocità di caricamento evitando di stampare le tracce dello stack per alcune eccezioni comunemente generate durante il ricaricamento dei datapack (ad esempio, oggetti mancanti nelle tabelle di bottino/ricette). Il messaggio verrà comunque stampato.",
"modernfix.option.mixin.perf.dedicated_reload_executor": "Tutte le versioni. Sposta il ricaricamento dei resource pack e dei datapack in un pool di thread dedicato anziché utilizzare i thread predefiniti `Worker-Main`. Questo consente ai mod Smooth Boot di potenzialmente migliorare le prestazioni in modalità singleplayer durante l'esecuzione, senza rallentare l'avvio a causa di un conteggio limitato di thread.",
"modernfix.option.mixin.perf.deduplicate_location": "Tutte le versioni, ma disabilitato per impostazione predefinita a causa dell'impatto sui tempi di caricamento. Duplica i namespace e i percorsi delle risorse. Questo risparmia memoria, ma aumenta anche il costo di costruzione di una nuova `ResourceLocation` in modo significativo.",
"modernfix.option.mixin.perf.dynamic_dfu": "Tutte le versioni. Modifica l'inizializzazione di DFU in modo che avvenga la prima volta che è necessario eseguire un aggiornamento. Questo è simile a LazyDFU, ma si differenzia in quanto evita di caricare *qualsiasi* classe/struttura dati DFU, mentre LazyDFU disabilita solo l'ottimizzazione delle regole. Fondamentalmente, questa opzione è una versione più sicura di DataFixerSlayer, in quanto caricherà comunque DFU quando necessario.\n\nIn genere dovresti continuare a utilizzare LazyDFU anche con questa opzione abilitata, in quanto l'ottimizzazione delle regole DFU altrimenti causerà lag.",
"modernfix.option.mixin.perf.dynamic_resources": "Tutte le versioni. Vedi https://github.com/embeddedt/ModernFix/wiki/Dynamic-Resources-FAQ.",
"modernfix.option.mixin.perf.dynamic_structure_manager": "Tutte le versioni. Consente al gioco di scaricare i file delle strutture dopo la generazione, anziché mantenerli caricati per sempre.",
"modernfix.option.mixin.perf.fast_registry_validation": "Tutte le versioni. Forge cerca inutilmente un metodo tramite riflessione ogni volta che viene convalidato un registro. Questa patch semplicemente memorizza nella cache il valore restituito poiché sarà lo stesso ogni volta.",
"modernfix.option.mixin.perf.faster_font_loading": "Tutte le versioni. Ottimizza il renderer dei font per caricare i font più velocemente, velocizzando il ricaricamento delle risorse.",
"modernfix.option.mixin.perf.faster_item_rendering": "Tutte le versioni. Evita di renderizzare i lati degli oggetti nelle interfacce utente (GUI). (Sì, sembra che anche il gioco di base lo faccia.)\n\nQuesto può triplicare il frame rate (FPS) con un mod come REI/JEI installato su GPU meno potenti, se sono visibili abbastanza oggetti. Disabilitato per impostazione predefinita poiché è una funzionalità nuova e non testata molto, ma dovrebbe essere sicuro. Il problema più probabile è che gli oggetti siano completamente invisibili nelle interfacce utente (GUI) o che appaiano piatti nel mondo.",
"modernfix.option.mixin.perf.faster_texture_loading": "Tutte le versioni precedenti alla 1.19.4. Evita di leggere le texture due volte (la prima volta utilizzando un percorso di codice molto lento) e invece ne effettua un caricamento più veloce (simile a 1.19.3+).",
"modernfix.option.mixin.perf.faster_texture_stitching": "Tutte le versioni. Consente al gioco di utilizzare un sistema di cucitura delle texture più veloce, originariamente sviluppato da SuperCoder79 per lwjgl3ify su 1.7.10, che può risparmiare tempo durante il caricamento. Raramente si è riscontrato che causi artefatti strani su blocchi o interfacce utente (GUI), potrebbe essere un errore di Sodium.",
"modernfix.option.mixin.perf.jeresources_startup": "Solo 1.16. Ottimizza Just Enough Resources in modo da non ricreare inutilmente le entità dei villaggi molte volte per la stessa professione, risparmiando tempo durante l'avvio di JEI.",
"modernfix.option.mixin.perf.kubejs": "Solo 1.16. Ottimizzazioni per KubeJS per evitare copie inutili di `ItemStack`, ecc., riducendo il tempo richiesto per caricare i datapack.",
"modernfix.option.mixin.perf.model_optimizations": "Tutte le versioni. Implementa ottimizzazioni per velocizzare il processo di caricamento dei modelli.",
"modernfix.option.mixin.perf.nbt_memory_usage": "Tutte le versioni. Utilizza una mappa di supporto più efficiente per i tag NBT composti che deduplica i nomi chiave e utilizza anche una mappa di array per i composti molto piccoli. Ciò riduce l'onere di memorizzare molti tag composti in memoria.",
"modernfix.option.mixin.perf.nuke_empty_chunk_sections": "Solo 1.16, ispirato a Hydrogen. Evita di memorizzare in memoria le sezioni di chunk piene di aria, segnalandole invece come vuote.",
"modernfix.option.mixin.perf.remove_biome_temperature_cache": "Tutte le versioni. Rimuove la cache delle temperature dei biome come fa Lithium nelle versioni moderne.",
"modernfix.option.mixin.perf.resourcepacks": "Tutte le versioni. **Un'ottimizzazione fondamentale.** I lanci nelle versioni moderne sono fortemente rallentati dall'accesso al filesystem. Molte richieste vengono fatte frequentemente ai pacchetti di risorse per elencare le risorse o verificare se esiste una risorsa specifica, e ognuna di queste richieste produce una chiamata API al file molto lenta.\n\nModernFix elimina completamente la maggior parte del collo di bottiglia semplicemente memorizzando in cache un elenco di tutte le risorse presenti nei pacchetti di risorse forniti dai mod e in quelli vanilla. La cache viene ricostruita alla ricarica delle risorse (ad eccezione delle risorse vanilla, poiché non dovrebbero mai cambiare mentre il gioco è in esecuzione).\n\nNon ci sono problemi di compatibilità noti con questa patch, ad eccezione di OptiFine (le sue risorse CTM non vengono caricate correttamente). Tuttavia, non consiglio di utilizzare OptiFine in nessuno scenario, poiché aggiunge diversi minuti all'avvio da solo e non è testato con ModernFix.",
"modernfix.option.mixin.perf.reuse_datapacks": "Solo versione 1.16. Tenta di velocizzare il passaggio tra mondi in giocatore singolo saltando il riavvio dei datapack quando possibile. Potrebbe causare problemi di compatibilità con alcuni mod, ma è abilitato per impostazione predefinita.",
"modernfix.option.mixin.perf.rewrite_registry": "Tutte le versioni. **Attualmente semi-rotto.** Sostituisce aggressivamente alcune parti interne del sistema di registro di Forge con versioni più veloci, tuttavia attualmente causa blocchi durante il caricamento di alcuni modpack. Disabilitato per impostazione predefinita per ovvi motivi.",
"modernfix.option.mixin.perf.skip_first_datapack_reload": "Solo versioni 1.16 e 1.19. **Un'ottimizzazione fondamentale.**\n\nNel mezzo del ciclo di sviluppo 1.16, Forge ha patchato il gioco per ricaricare i datapack due volte quando si caricava un mondo esistente, al fine di risolvere un problema di spostamento dell'ID dei biome. Purtroppo, le ricariche dei datapack richiedono spesso più di 30 secondi e quindi questo influisce molto sui tempi di caricamento del mondo.\n\nModernFix apporta le modifiche necessarie per evitare questa ricarica, basandosi sulla pull request incompiuta di Forge #8163.\n\nQuesta modifica è stata rimossa da Forge nella versione 1.18, ma poi una patch simile è stata aggiunta *nuovamente* nella versione 1.19 per risolvere il problema dei datapack dei mod che non venivano caricati durante la creazione di nuovi mondi in giocatore singolo. Fortunatamente, il problema è limitato alla schermata di creazione del mondo nella versione 1.19, e i mondi esistenti richiedono solo una ricarica. Tuttavia, questo raddoppia comunque la durata della lag spike quando si fa clic su \"Crea nuovo mondo\" nella versione 1.19, quindi ModernFix apporta nuovamente modifiche per evitare una ricarica ridondante.",
"modernfix.option.mixin.perf.state_definition_construct": "Tutte le versioni. Abilitato solo se è installato FerriteCore. Sfrutta la gestione dei blockstate di FerriteCore per accelerarne la creazione. Questo può aiutare ad accelerare l'avvio con mod che aggiungono molti blockstate, come ad esempio le mod di mobili.",
"modernfix.option.mixin.perf.sync_executor_sleep": "Tutte le versioni. Evita che il thread principale giri inutilmente consumando un core della CPU mentre aspetta che i lavoratori di caricamento dei mod finiscano.",
"modernfix.option.mixin.perf.thread_priorities": "Tutte le versioni. Regola le priorità dei thread dei lavoratori e del server in modo che siano inferiori al thread del client. Questo aiuta a migliorare la stabilità degli FPS su macchine con pochi core della CPU, purché l'implementazione Java in uso rispetti le priorità.",
"modernfix.option.mixin.perf.use_integrated_resources": "Principalmente per la versione 1.16. Corregge JEResources affinché utilizzi i dati delle tabelle del bottino del server integrato se si gioca in giocatore singolo, anziché ricaricare inutilmente le tabelle del bottino. Risparmia alcuni secondi in più durante l'avvio di JEI.",
"modernfix.option.mixin.bugfix.concurrency": "Le patch in questo gruppo risolvono problemi legati alla concorrenza in Minecraft e/o Forge. La maggior parte di esse provoca crash rari e difficili da diagnosticare durante il caricamento.",
"modernfix.option.mixin.bugfix.edge_chunk_not_saved": "Questa opzione è un porting della mod Chunk Saving Fix di SuperCoder (perché non mi sono reso conto che era già disponibile per Forge all'epoca).",
"modernfix.option.mixin.bugfix.mc218112": "Questa opzione risolve un deadlock che può verificarsi se viene lanciata un'eccezione durante l'elaborazione dei dati dell'entità. Vanilla non sblocca correttamente il gestore dei dati quando dovrebbe farlo. Questo è tracciato come MC-218112 nel bug tracker ed è stato corretto da Mojang nella versione 1.17.",
"modernfix.option.mixin.bugfix.packet_leak": "**Sperimentale**, non abilitato per impostazione predefinita. Un tentativo di correzione per il problema di perdita di memoria che si verifica dopo aver giocato abbastanza a lungo nella versione 1.16.",
"modernfix.option.mixin.bugfix.paper_chunk_patches": "Versioni 1.18 e successive. **Un'ottimizzazione fondamentale.** Porta una patch da Paper che risolve i problemi nella versione 1.17 con il caricamento dei chunk che richiede enormi quantità di memoria e genera molte istanze di `CompletableFuture`. Le versioni 1.18+ ora possono caricare mondi con soli 400MB di memoria come poteva fare la 1.16.",
"modernfix.option.mixin.bugfix.tf_cme_on_load": "Modifica Twilight Forest per eseguire la configurazione client non thread-safe utilizzando il thread principale, come dovrebbe fare, anziché il thread worker FML.",
"modernfix.option.mixin.feature.branding": "Aggiunge ModernFix all'elenco del marchio nella schermata dei titoli e anche alla schermata F3.",
"modernfix.option.mixin.feature.direct_stack_trace": "Di solito disabilitato, può essere abilitato per forzare la traccia dello stack grezzo da registrare quando si verifica un crash. Occasionalmente, il sistema di report dei crash di Vanilla non funziona correttamente e fornisce una traccia/report dello stack completamente irrilevante.",
"modernfix.option.mixin.feature.measure_time": "Utilizza alcune iniezioni per misurare il tempo di caricamento del mondo, il tempo di ricarica dei datapack, il tempo di ricarica delle risorse, il tempo di avvio e aggiunge i ganci necessari per abilitare la logica del profiler inutilizzata di Vanilla per la ricarica delle risorse, se configurato.",
"modernfix.option.mixin.feature.spam_thread_dump": "**Da utilizzare solo per scopi di debug.** Fa sì che venga registrato un thread dump nel registro ogni 60 secondi. Questo può aiutare a diagnosticare i blocchi inspiegabili durante il caricamento/gioco.",
"modernfix.option.mixin.bugfix.chunk_deadlock": "Tenta di prevenire i blocchi del sistema di chunk o fornisce informazioni di debug aggiuntive nel registro quando si verificano. Questi blocchi di solito si manifestano come il server che si blocca indefinitamente (ad esempio, le entità non si muovono), mentre il client continua a funzionare normalmente.",
"modernfix.option.mixin.bugfix.chunk_deadlock.valhesia": "Modifica Valhesia Structures per risolvere un problema nel suo codice che causa blocchi frequenti del worldgen/caricamento dei chunk.",
"modernfix.option.mixin.bugfix.cofh_core_crash": "Corregge un problema di multithreading in CoFH Core che può causare crash rari durante l'avvio.",
"modernfix.option.mixin.bugfix.ctm_resourceutil_cme": "Corregge un problema di multithreading in ConnectedTexturesMod che può causare crash rari durante l'avvio.",
"modernfix.option.mixin.bugfix.ender_dragon_leak": "Corregge una perdita di memoria in Vanilla causata dal drago dell'End che mantiene un riferimento al mondo client precedente.",
"modernfix.option.mixin.bugfix.entity_load_deadlock": "Corregge molti problemi in cui EntityJoinWorldEvent/EntityJoinLevelEvent causano un blocco del worldgen, ritardando leggermente il caricamento dell'entità. Non dovrebbe, tuttavia, causare cambiamenti di comportamento visibili in gioco.",
"modernfix.option.mixin.bugfix.fix_config_crashes": "Corregge il problema di Forge delle configurazioni che occasionalmente diventano corrotte durante il lancio del gioco.",
"modernfix.option.mixin.bugfix.item_cache_flag": "Corregge MC-258939",
"modernfix.option.mixin.bugfix.preserve_early_window_pos": "Fa sì che la finestra di gioco mantenga le sue dimensioni esistenti quando il controllo passa dal caricamento anticipato di Forge al codice di Minecraft. Corregge il problema della finestra che torna al centro dello schermo dopo essere stata trascinata, ecc.",
"modernfix.option.mixin.bugfix.refinedstorage.te_bug": "Corregge i blocchi di archiviazione esterna di Refined Storage che occasionalmente non mostrano i contenuti dei cassetti, ecc. quando vengono caricati. Backport di Refined Storage PR #3435, che è stato applicato solo alla versione 1.18 e successive.",
"modernfix.option.mixin.bugfix.remove_block_chunkloading": "Corregge il fatto che i maiali zombie tengano perpetuamente caricato il chunk 0, 0 su Forge. Backport di Forge PR #8583.",
"modernfix.option.mixin.bugfix.starlight_emptiness": "Corregge un crash occasionale di Starlight dovuto all'inizializzazione non corretta delle mappe di vuoto. Backport della stessa correzione in Starlight per la versione 1.18.x.",
"modernfix.option.mixin.core": "Patch di base necessarie per far funzionare ModernFix",
"modernfix.option.mixin.perf.reduce_blockstate_cache_rebuilds": "Tutte le versioni. **Un'ottimizzazione fondamentale.** Le versioni più recenti di Minecraft (dopo la 1.12) hanno implementato un sistema di cache dei blockstate che memorizza le informazioni frequentemente utilizzate su un blockstate, come ad esempio se è solido, la sua forma di collisione, ecc. Ricostruire questa cache è abbastanza veloce nella versione standard (richiede solo uno o due secondi), ma è molto lento con molti mod installati, poiché sono presenti molti più blockstate nel gioco che devono tutti avere le loro cache ricostruite.\n\nQuesto problema viene esacerbato da Forge poiché la cache viene ricostruita in molti punti quando i dati sarebbero quasi certamente inutilizzati prima della successiva ricostruzione. Esempi includono subito prima di raggiungere il menu principale (durante la fase \"Freezing data\"), nonché più volte (!) quando viene caricato un mondo.\n\nModernFix risolve questo collo di bottiglia delle prestazioni rendendo invece le ricostruzioni della cache pigre. Ogni blockstate ricostruisce la sua cache la prima volta che i dati vengono acceduti. In qualsiasi momento in cui Vanilla o Forge tenterebbero di ricostruire le cache per tutti i blockstate, questo viene ridirezionato per invalidare semplicemente la cache su ciascun blockstate.\n\nQuesto non dovrebbe avere alcun impatto sul TPS dopo che l'avvio è concluso.",
"modernfix.option.mixin.devenv": "Patch utilizzate durante l'esecuzione in un ambiente di sviluppo, per miglioramenti di velocità e/o test",
"modernfix.option.mixin.safety": "Patch di concorrenza per prevenire crash durante il lancio",
"modernfix.option.mixin.feature.integrated_server_watchdog": "Aggiunge il watchdog vanilla anche ai mondi in giocatore singolo, ma stampa solo le tracce dello stack anziché terminare forzatamente il mondo. Questa versione include la funzionalità di Fullstack Watchdog, ma quest'ultimo è comunque necessario per il multiplayer.",
"modernfix.option.mixin.feature.snapshot_easter_egg": "Aggiunge funzionalità easter egg (non influisce su alcuna visualizzazione o comportamento vanilla) quando si esegue una versione snapshot.",
"modernfix.option.mixin.feature.spark_profile_launch": "Se abilitato e installata una versione compatibile di Spark, l'intera sequenza di avvio verrà profilata fino al menu principale.",
"modernfix.option.mixin.feature.warn_missing_perf_mods": "Mostra un avviso all'avvio se altri mod per le prestazioni considerati essenziali e altamente compatibili non sono presenti",
"modernfix.option.mixin.launch.class_search_cache": "Sostituisce il risolutore delle risorse di Forge (usato per trovare il codice del gioco e di un mod) con una versione significativamente più veloce, accelerando l'avvio",
"modernfix.option.mixin.perf.clear_fabric_mapping_tables": "Riduce l'utilizzo della memoria cancellando le strutture di dati di mappatura in Fabric Loader che sono ridondanti o raramente utilizzate dai mod. Disabilitato per impostazione predefinita per motivi di compatibilità.",
"modernfix.option.mixin.perf.clear_mixin_classinfo": "Carica forzatamente tutti i mixin quando il lancio termina e quindi cancella le strutture di dati di mixin per rimuovere gran parte della memoria di Mixin. Disabilitato per impostazione predefinita per motivi di compatibilità.",
"modernfix.option.mixin.perf.deduplicate_wall_shapes": "Rende la maggior parte dei blocchi murari condividere lo stesso oggetto forma anziché avere ognuno la propria copia. Può ridurre notevolmente l'utilizzo della memoria quando vengono aggiunti molti blocchi murari dai mod.",
"modernfix.option.mixin.perf.dynamic_resources.ae2": "Patch di compatibilità AE2 per le risorse dinamiche",
"modernfix.option.mixin.perf.dynamic_resources.ctm": "Patch di compatibilità CTM per le risorse dinamiche",
"modernfix.option.mixin.perf.dynamic_resources.rs": "Patch di compatibilità Refined Storage per le risorse dinamiche",
"modernfix.option.mixin.perf.dynamic_resources.supermartijncore": "Patch di compatibilità SuperMartijn642CoreLib per le risorse dinamiche",
"modernfix.option.mixin.perf.dynamic_resources.diagonalfences": "Patch di compatibilità Diagonal Fences per le risorse dinamiche",
"modernfix.option.mixin.perf.faster_advancements": "Riscrive la logica di controllo degli avanzamenti per renderla più veloce e per evitare StackOverflowError in pacchetti grandi. Porting di Advancements Debug da Fabric.",
"modernfix.option.mixin.perf.patchouli_deduplicate_books": "Risolve il problema dei libri di Patchouli che memorizzano molti oggetti vuoti con tag NBT, riducendo l'utilizzo della memoria.",
"modernfix.option.mixin.perf.remove_spawn_chunks": "Rimuove completamente i chunk di spawn dal gioco. Non vengono più caricati affatto, a differenza di Ksyxis.",
"modernfix.option.mixin.perf.use_integrated_resources.jepb": "",
"modernfix.option.mixin.perf.use_integrated_resources.jeresources": "",
"modernfix.option.mixin.bugfix.blueprint_modif_memory_leak": "Risolve la perdita di risorse vaniglia di ObjectModificationManager in Blueprint, riducendo l'utilizzo della memoria. Nonostante la correzione sia stata contribuita in PR #195, non è ancora stata rilasciata.",
"modernfix.option.mixin.bugfix.removed_dimensions": "Risolve il problema del gioco che non riesce a caricare i mondi se vengono rimossi i mod delle dimensioni. Backport di Forge PR #8959.",
"modernfix.option.mixin.perf.compact_bit_storage": "Corregge lo spreco di memoria causato da alcuni server legacy (ad esempio Hypixel) che inviano chunk vuoti come se contenessero blocchi. Riduce notevolmente l'utilizzo della memoria su questi server.",
"modernfix.option.mixin.perf.deduplicate_climate_parameters": "Deduplica gli oggetti parametro del clima utilizzati dal nuovo sistema di biome, può risparmiare ~2MB ma rallenta leggermente la ricarica dei datapack.",
"modernfix.option.mixin.perf.dynamic_entity_renderers": "Costruisce i modelli delle entità la prima volta che vengono visti anziché durante l'avvio. Alcuni mod non sono compatibili con questa opzione e causeranno crash di EntityRenderer.",
"modernfix.option.mixin.perf.twilightforest.structure_spawn_fix": "Risolve il lag causato dal worldgen di Twilight Forest che controlla le strutture in modo molto inefficiente",
"modernfix.option.mixin.perf.fast_forge_dummies": "Velocizza il congelamento del registro di Forge durante l'avvio utilizzando un percorso del codice più veloce",
"modernfix.option.mixin.perf.tag_id_caching": "Velocizza l'uso delle voci dei tag memorizzando nella cache l'oggetto di posizione anziché ricrearlo ogni volta",
"modernfix.option.mixin.feature.disable_unihex_font": "Rimuove il font Unicode, risparmiando 10MB ma causando la mancata visualizzazione dei caratteri speciali"
}

View File

@ -4,9 +4,11 @@
"modernfix.jei_load": "正在加载JEI这可能会花费一段时间。",
"modernfix.no_lazydfu": "未安装DFU载入优化。如果Minecraft需要从旧版本更新游戏数据可能会出现极大的延迟。",
"modernfix.no_ferritecore": "未安装铁氧体磁芯。内存占用将会非常高。",
"modernfix.connectedness_dynresoruces": "Connectedness模组用于提供连接纹理和现代化修复的动态资源dynamic resources功能不兼容。请删除Connectedness模组或在现代化修复配置中禁用动态资源功能。",
"modernfix.perf_mod_warning": "推荐安装这些模组,但你也可以在现代化修复的配置中禁用此警告。",
"modernfix.config": "现代化修复Mixin配置",
"modernfix.config.done_restart": "完成(生效需重启)",
"modernfix.message.reload_config": "检测到模组配置文件的更改。为了避免加载尚未保存完毕的文件重载过程必须通过使用§b/mfrc§r命令来触发。",
"modernfix.option.on": "开启",
"modernfix.option.off": "关闭",
"modernfix.option.disabled": "已禁用",

View File

@ -9,5 +9,8 @@ ${mixin_classes}
],
"injectors": {
"defaultRequire": 1
},
"overwrites": {
"conformVisibility": true
}
}

View File

@ -1,5 +1,11 @@
accessWidener v2 named
accessible field net/minecraft/client/multiplayer/ClientChunkCache storage Lnet/minecraft/client/multiplayer/ClientChunkCache$Storage;
accessible field net/minecraft/client/multiplayer/ClientChunkCache lightEngine Lnet/minecraft/world/level/lighting/LevelLightEngine;
mutable field net/minecraft/client/multiplayer/ClientChunkCache lightEngine Lnet/minecraft/world/level/lighting/LevelLightEngine;
accessible class net/minecraft/client/multiplayer/ClientChunkCache$Storage
accessible field net/minecraft/client/multiplayer/ClientChunkCache$Storage chunks Ljava/util/concurrent/atomic/AtomicReferenceArray;
accessible class net/minecraft/client/renderer/RenderType$CompositeRenderType
accessible method net/minecraft/client/renderer/RenderType$CompositeRenderType <init> (Ljava/lang/String;Lcom/mojang/blaze3d/vertex/VertexFormat;IIZZLnet/minecraft/client/renderer/RenderType$CompositeState;)V
accessible method net/minecraft/nbt/CompoundTag <init> (Ljava/util/Map;)V

View File

@ -31,15 +31,22 @@ configurations {
dependencies {
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
testImplementation "net.fabricmc:fabric-loader-junit:${rootProject.fabric_loader_version}"
include(implementation(annotationProcessor("com.github.llamalad7.mixinextras:mixinextras-fabric:${rootProject.mixinextras_version}")))
modCompileOnly(fabricApi.module("fabric-api-base", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modCompileOnly(fabricApi.module("fabric-screen-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modCompileOnly(fabricApi.module("fabric-command-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modCompileOnly(fabricApi.module("fabric-models-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modCompileOnly(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
if(project.use_fabric_api_at_runtime.toBoolean()) {
modImplementation("com.terraformersmc:modmenu:${rootProject.modmenu_version}") { transitive false }
modImplementation "curse.maven:spark-361579:${rootProject.spark_fabric_version}"
modRuntimeOnly("net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}") { exclude group: 'net.fabricmc', module: 'fabric-loader' }
} else {
modCompileOnly("com.terraformersmc:modmenu:${rootProject.modmenu_version}") { transitive false }
modCompileOnly "curse.maven:spark-361579:${rootProject.spark_fabric_version}"
}
modImplementation(fabricApi.module("fabric-api-base", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modImplementation(fabricApi.module("fabric-screen-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modImplementation(fabricApi.module("fabric-command-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modImplementation(fabricApi.module("fabric-models-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modImplementation(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modImplementation("com.terraformersmc:modmenu:${rootProject.modmenu_version}") { transitive false }
modImplementation "curse.maven:spark-361579:${rootProject.spark_fabric_version}"
modRuntimeOnly("net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}") { exclude group: 'net.fabricmc', module: 'fabric-loader' }
// Remove the next line if you don't want to depend on the API
// modApi "me.shedaniel:architectury-fabric:${rootProject.architectury_version}"

View File

@ -1,6 +1,9 @@
package org.embeddedt.modernfix;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint;
import net.fabricmc.loader.impl.gui.FabricGuiEntry;
import net.fabricmc.loader.impl.gui.FabricStatusTree;
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
import org.embeddedt.modernfix.fabric.mappings.MappingsClearer;
import org.embeddedt.modernfix.fabric.spark.SparkLaunchProfiler;
@ -19,5 +22,18 @@ public class ModernFixPreLaunchFabric implements PreLaunchEntrypoint {
if(ModernFixMixinPlugin.instance.isOptionEnabled("perf.clear_fabric_mapping_tables.MappingsClearer")) {
MappingsClearer.clear();
}
// Prevent launching with Continuity when dynamic resources is on
if(ModernFixMixinPlugin.instance.isOptionEnabled("perf.dynamic_resources.ContinuityCheck")
&& FabricLoader.getInstance().isModLoaded("continuity")) {
CommonModUtil.runWithoutCrash(() -> {
FabricGuiEntry.displayError("Compatibility warning", null, tree -> {
FabricStatusTree.FabricStatusTab crashTab = tree.addTab("Warning");
crashTab.node.addMessage("Continuity and ModernFix's dynamic resources option are not compatible before Minecraft 1.19.4.", FabricStatusTree.FabricTreeWarningLevel.ERROR);
crashTab.node.addMessage("Remove Continuity or disable dynamic resources in the ModernFix config.", FabricStatusTree.FabricTreeWarningLevel.ERROR);
tree.tabs.removeIf(tab -> tab != crashTab);
}, true);
}, "display Continuity warning");
}
}
}

View File

@ -0,0 +1,20 @@
package org.embeddedt.modernfix.fabric.api.dynresources;
import net.minecraft.resources.ResourceLocation;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class ModelScanController {
public static final List<Predicate<ResourceLocation>> SCAN_PREDICATES = new ArrayList<>();
public static boolean shouldScanAndTestWrapping(ResourceLocation location) {
if(SCAN_PREDICATES.size() > 0) {
for(Predicate<ResourceLocation> predicate : SCAN_PREDICATES) {
if(!predicate.test(location))
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,39 @@
package org.embeddedt.modernfix.fabric.mixin.perf.blast_search_trees;
import net.minecraft.client.Minecraft;
import net.minecraft.client.searchtree.SearchRegistry;
import net.minecraft.core.NonNullList;
import net.minecraft.core.Registry;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.searchtree.DummySearchTree;
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;
@Mixin(Minecraft.class)
@ClientOnlyMixin
public class MinecraftMixin {
@Shadow @Final private SearchRegistry searchRegistry;
@Inject(method = "createSearchTrees", at = @At("HEAD"), cancellable = true)
private void replaceSearchTrees(CallbackInfo ci) {
ci.cancel();
ModernFix.LOGGER.warn("Disabling creative search");
NonNullList<ItemStack> stacks = NonNullList.create();
for(Item item : Registry.ITEM) {
stacks.clear();
item.fillItemCategory(CreativeModeTab.TAB_SEARCH, stacks);
}
this.searchRegistry.register(SearchRegistry.CREATIVE_NAMES, new DummySearchTree<>());
this.searchRegistry.register(SearchRegistry.CREATIVE_TAGS, new DummySearchTree<>());
this.searchRegistry.register(SearchRegistry.RECIPE_COLLECTIONS, new DummySearchTree<>());
}
}

View File

@ -39,6 +39,7 @@ import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
import org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider;
import org.embeddedt.modernfix.dynamicresources.ModelBakeryHelpers;
import org.embeddedt.modernfix.fabric.api.dynresources.ModelScanController;
import org.embeddedt.modernfix.fabric.bridge.ModelV0Bridge;
import org.embeddedt.modernfix.util.LayeredForwardingMap;
import org.jetbrains.annotations.Nullable;
@ -174,6 +175,13 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
private boolean forceLoadModel = false;
@Inject(method = "loadTopLevel", at = @At("HEAD"), cancellable = true)
private void ignoreRejectedModel(ModelResourceLocation location, CallbackInfo ci) {
if(this.inTextureGatheringPass && !this.forceLoadModel && !ModelScanController.shouldScanAndTestWrapping(location)) {
ci.cancel();
}
}
@Inject(method = "loadModel", at = @At(value = "HEAD"), cancellable = true)
private void ignoreNonFabricModel(ResourceLocation modelLocation, CallbackInfo ci) throws Exception {
if(this.inTextureGatheringPass && !this.forceLoadModel && !this.injectedModels.contains(modelLocation)) {
@ -246,25 +254,7 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
blockStateFiles, modelFiles, this.missingModel, json -> BlockModel.GSON.fromJson(json, BlockModel.class),
this::getModel);
/* take every texture from these folders (1.19.3+ emulation) */
String[] extraFolders = new String[] {
"attachment",
"bettergrass",
"block",
"blocks",
"cape",
"entity/bed",
"entity/chest",
"item",
"items",
"model",
"models",
"part",
"pipe",
"ropebridge",
"solid_block",
"spell_effect",
"spell_projectile"
};
String[] extraFolders = ModelBakeryHelpers.getExtraTextureFolders();
for(String folder : extraFolders) {
Collection<ResourceLocation> textureLocations = this.resourceManager.listResources("textures/" + folder, p -> p.endsWith(".png"));
for(ResourceLocation rl : textureLocations) {

View File

@ -0,0 +1,30 @@
package org.embeddedt.modernfix.fabric.mixin.perf.faster_command_suggestions;
import com.mojang.brigadier.suggestion.Suggestion;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import org.spongepowered.asm.mixin.*;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.List;
/**
* Simple hack-fix to limit the number of suggestions being processed. Not a perfect fix but mitigates lag decently
* on an i3-4150.
*/
@Mixin(SuggestionsBuilder.class)
public class SuggestionsBuilderMixin {
@Unique
private static final int MAX_SUGGESTIONS = 10000;
@Shadow(remap = false) @Final @Mutable
private List<Suggestion> result;
@Redirect(method = "*", at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z"), require = 0)
private <T> boolean addIfFits(List<T> list, T entry) {
if(list != result || list.size() < MAX_SUGGESTIONS) {
return list.add(entry);
}
return false;
}
}

View File

@ -23,10 +23,10 @@ import java.util.Set;
public abstract class ModNioResourcePackMixin implements ICachingResourcePack {
@Shadow public abstract Set<String> getNamespaces(PackType type);
@Shadow @Final private Path basePath;
@Shadow(remap = false) @Final private Path basePath;
private PackResourcesCacheEngine cacheEngine;
@Inject(method = "<init>", at = @At("RETURN"))
@Inject(method = "<init>", at = @At("RETURN"), remap = false)
private void cacheResources(CallbackInfo ci) {
invalidateCache();
PackResourcesCacheEngine.track(this);

View File

@ -14,6 +14,15 @@
},
"license": "LGPL-3.0",
"icon": "icon.png",
"custom": {
"modmenu": {
"links": {
"modmenu.kofi": "https://ko-fi.com/embeddedt",
"modmenu.github_releases": "https://github.com/embeddedt/ModernFix/releases",
"modmenu.curseforge": "https://www.curseforge.com/minecraft/mc-mods/modernfix"
}
}
},
"environment": "*",
"entrypoints": {
"main": [

View File

@ -9,5 +9,8 @@ ${mixin_classes}
],
"injectors": {
"defaultRequire": 1
},
"overwrites": {
"conformVisibility": true
}
}

View File

@ -0,0 +1,43 @@
apply plugin: "dev.architectury.loom"
loom {
accessWidenerPath = project(":common").loom.accessWidenerPath
runs {
client {
vmArgs "-Xmx8G"
property("modernfix.config.mixin.perf.blast_search_trees", "true")
property("modernfix.config.mixin.perf.dynamic_resources", "true")
property("modernfix.config.mixin.perf.dynamic_block_codecs", "true")
}
}
}
dependencies {
minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
mappings loom.layered() {
officialMojangMappings()
if(rootProject.hasProperty("parchment_version")) {
parchment("org.parchmentmc.data:parchment-${minecraft_version}:${parchment_version}@zip")
}
}
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
modImplementation(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modImplementation(fabricApi.module("fabric-models-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modImplementation(fabricApi.module("fabric-renderer-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modImplementation(fabricApi.module("fabric-rendering-data-attachment-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modImplementation(fabricApi.module("fabric-rendering-fluids-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modRuntimeOnly(fabricApi.module("fabric-renderer-indigo", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
implementation project(path: ":common", configuration: "namedElements")
implementation project(path: ":fabric", configuration: "namedElements")
}
processResources {
inputs.property "version", project.version
filesMatching("fabric.mod.json") {
expand "version": project.version
}
}

View File

@ -0,0 +1,13 @@
package org.embeddedt.modernfix.testmod;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockBehaviour;
public class TestBlock extends Block {
private static final BlockBehaviour.Properties PROPERTIES = BlockBehaviour.Properties.copy(Blocks.STONE);
public TestBlock() {
super(PROPERTIES);
}
}

View File

@ -0,0 +1,13 @@
package org.embeddedt.modernfix.testmod;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
public class TestBlockItem extends BlockItem {
private static final Item.Properties PROPERTIES = new Item.Properties().tab(CreativeModeTab.TAB_BUILDING_BLOCKS);
public TestBlockItem(TestBlock block) {
super(block, PROPERTIES);
}
}

View File

@ -0,0 +1,67 @@
package org.embeddedt.modernfix.testmod;
import com.google.common.base.Stopwatch;
import net.fabricmc.api.ModInitializer;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.List;
public class TestMod implements ModInitializer {
public static final String ID = "mfix_testmod";
public static final Logger LOGGER = LogManager.getLogger("ModernFix TestMod");
public static final int NUM_COLORS = 256;
public static final int MAX_COLOR = NUM_COLORS - 1;
public static final List<BlockState> WOOL_STATES = new ArrayList<>();
@Override
public void onInitialize() {
// Register 1 million blocks & items
Stopwatch watch = Stopwatch.createStarted();
int totalToRegister = NUM_COLORS * NUM_COLORS * NUM_COLORS;
int progressReport = totalToRegister / 20;
int numRegistered = 0;
for(int r = 0; r < NUM_COLORS; r++) {
for(int g = 0; g < NUM_COLORS; g++) {
for(int b = 0; b < NUM_COLORS; b++) {
ResourceLocation name = new ResourceLocation(ID, "wool_" + r + "_" + g + "_" + b);
TestBlock block = Registry.register(Registry.BLOCK, name, new TestBlock());
WOOL_STATES.add(block.defaultBlockState());
//Registry.register(Registry.ITEM, name, new TestBlockItem(block));
numRegistered++;
if((numRegistered % progressReport) == 0) {
LOGGER.info(String.format("Registering... %.02f%%", ((float)numRegistered)/totalToRegister * 100));
}
}
}
}
watch.stop();
LOGGER.info("Registered {} registry entries in {}", totalToRegister, watch);
}
private static final BlockState AIR = Blocks.AIR.defaultBlockState();
public static BlockState getColorCubeStateFor(int chunkX, int chunkY, int chunkZ) {
BlockState blockState = null;
if (chunkX >= 0 && chunkY >= 0 && chunkZ >= 0) { // && chunkX % 2 == 0 && chunkY % 2 == 0 && chunkZ % 2 == 0) {
/*
chunkX /= 2;
chunkY /= 2;
chunkZ /= 2;
*/
if(chunkX <= TestMod.MAX_COLOR && chunkY <= TestMod.MAX_COLOR && chunkZ <= TestMod.MAX_COLOR) {
blockState = TestMod.WOOL_STATES.get((chunkX * TestMod.NUM_COLORS * TestMod.NUM_COLORS) + (chunkY * TestMod.NUM_COLORS) + chunkZ);
}
}
return blockState;
}
}

View File

@ -0,0 +1,142 @@
package org.embeddedt.modernfix.testmod.client;
import com.google.common.collect.ImmutableList;
import com.mojang.datafixers.util.Pair;
import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.ItemOverrides;
import net.minecraft.client.renderer.block.model.ItemTransforms;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.*;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.state.BlockState;
import org.embeddedt.modernfix.testmod.TestMod;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
public class TestModBlockModel implements UnbakedModel, BakedModel, FabricBakedModel {
private static final Material BASE_WOOL = new Material(TextureAtlas.LOCATION_BLOCKS, new ResourceLocation(TestMod.ID, "block/base_wool"));
private Mesh mesh;
private TextureAtlasSprite texture;
private final int r, g, b;
public TestModBlockModel(int r, int g, int b) {
this.r = r;
this.g = g;
this.b = b;
}
@Override
public boolean isVanillaAdapter() {
return false;
}
@Override
public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
context.meshConsumer().accept(mesh);
}
@Override
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
context.meshConsumer().accept(mesh);
}
@Override
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, Random rand) {
return Collections.emptyList();
}
@Override
public boolean useAmbientOcclusion() {
return true;
}
@Override
public boolean isGui3d() {
return true;
}
@Override
public boolean usesBlockLight() {
return true;
}
@Override
public boolean isCustomRenderer() {
return false;
}
@Override
public TextureAtlasSprite getParticleIcon() {
return texture;
}
@Override
public ItemTransforms getTransforms() {
return ModelHelper.MODEL_TRANSFORM_BLOCK;
}
@Override
public ItemOverrides getOverrides() {
return ItemOverrides.EMPTY;
}
@Override
public Collection<ResourceLocation> getDependencies() {
return Collections.emptyList();
}
@Override
public Collection<Material> getMaterials(Function<ResourceLocation, UnbakedModel> modelGetter, Set<Pair<String, String>> missingTextureErrors) {
return ImmutableList.of(BASE_WOOL);
}
private static int scaleColor(int c) {
return c * 255 / TestMod.MAX_COLOR;
}
@Nullable
@Override
public BakedModel bake(ModelBakery modelBakery, Function<Material, TextureAtlasSprite> spriteGetter, ModelState transform, ResourceLocation location) {
// Build the mesh using the Renderer API
Renderer renderer = RendererAccess.INSTANCE.getRenderer();
MeshBuilder builder = renderer.meshBuilder();
QuadEmitter emitter = builder.getEmitter();
texture = spriteGetter.apply(BASE_WOOL);
for(Direction direction : Direction.values()) {
// Add a new face to the mesh
emitter.square(direction, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f);
// Set the sprite of the face, must be called after .square()
// We haven't specified any UV coordinates, so we want to use the whole texture. BAKE_LOCK_UV does exactly that.
emitter.spriteBake(0, texture, MutableQuadView.BAKE_LOCK_UV);
int color = (255 << 24) | (scaleColor(r) << 16) | (scaleColor(g) << 8) | scaleColor(b);
// Enable texture usage
emitter.spriteColor(0, color, color, color, color);
// Add the quad to the mesh
emitter.emit();
}
mesh = builder.build();
return this;
}
}

View File

@ -0,0 +1,29 @@
package org.embeddedt.modernfix.testmod.client;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry;
import org.embeddedt.modernfix.fabric.api.dynresources.ModelScanController;
import org.embeddedt.modernfix.testmod.TestMod;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TestModClient implements ClientModInitializer {
private static final Pattern RGB_PATTERN = Pattern.compile("^wool_([0-9]+)_([0-9]+)_([0-9]+)$");
@Override
public void onInitializeClient() {
ModelScanController.SCAN_PREDICATES.add(rl -> !rl.getNamespace().equals(TestMod.ID));
ModelLoadingRegistry.INSTANCE.registerVariantProvider(resourceManager -> (modelId, context) -> {
if(modelId.getNamespace().equals(TestMod.ID)) {
Matcher matcher = RGB_PATTERN.matcher(modelId.getPath());
if(matcher.matches()) {
int r = Integer.parseInt(matcher.group(1));
int g = Integer.parseInt(matcher.group(2));
int b = Integer.parseInt(matcher.group(3));
return new TestModBlockModel(r, g, b);
}
}
return null;
});
}
}

View File

@ -0,0 +1,21 @@
package org.embeddedt.modernfix.testmod.mixin;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.LevelChunk;
import org.embeddedt.modernfix.testmod.TestMod;
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.CallbackInfoReturnable;
@Mixin(LevelChunk.class)
public class ChunkMixin {
@Inject(method = "getBlockState", at = @At("HEAD"), cancellable = true)
private void redirectDebugWorld(BlockPos pos, CallbackInfoReturnable<BlockState> cir) {
BlockState overrideState = TestMod.getColorCubeStateFor(pos.getX(), pos.getY(), pos.getZ());
if(overrideState != null) {
cir.setReturnValue(overrideState);
}
}
}

View File

@ -0,0 +1,39 @@
package org.embeddedt.modernfix.testmod.mixin;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.StructureFeatureManager;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.StructureSettings;
import org.embeddedt.modernfix.testmod.TestMod;
import org.spongepowered.asm.mixin.Mixin;
@Mixin(FlatLevelSource.class)
public abstract class DebugLevelSourceMixin extends ChunkGenerator {
public DebugLevelSourceMixin(BiomeSource biomeSource, StructureSettings structureSettings) {
super(biomeSource, structureSettings);
}
@Override
public void applyBiomeDecoration(WorldGenRegion region, StructureFeatureManager structureManager) {
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
int i = region.getCenterX();
int j = region.getCenterZ();
for(int k = 0; k < 16; ++k) {
for(int l = 0; l < 16; ++l) {
int m = (i << 4) + k;
int n = (j << 4) + l;
for(int y = 0; y < 255; y++) {
BlockState blockState = TestMod.getColorCubeStateFor(m, y, n);
if (blockState != null) {
region.setBlock(mutableBlockPos.set(m, y, n), blockState, 2);
}
}
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

View File

@ -0,0 +1,26 @@
{
"schemaVersion": 1,
"id": "mfix_testmod",
"version": "${version}",
"name": "ModernFix test mod",
"description": "Test mod used to validate features and behaviors of ModernFix, the essential Minecraft performance mod",
"authors": [
"embeddedt"
],
"contact": {
"sources": "https://github.com/embeddedt/ModernFix",
"homepage": "https://modrinth.com/mod/modernfix",
"issues": "https://github.com/embeddedt/ModernFix/issues"
},
"license": "LGPL-3.0",
"environment": "*",
"mixins": [ "testmod.mixins.json" ],
"entrypoints": {
"main": [
"org.embeddedt.modernfix.testmod.TestMod"
],
"client": [
"org.embeddedt.modernfix.testmod.client.TestModClient"
]
}
}

View File

@ -0,0 +1,7 @@
{
"pack": {
"description": "testmod resources",
"pack_format": 6,
"_comment": "A pack_format of 6 requires json lang files and some texture changes from 1.16.2. Note: we require v6 pack meta for all mods."
}
}

View File

@ -0,0 +1,13 @@
{
"required": true,
"package": "org.embeddedt.modernfix.testmod.mixin",
"compatibilityLevel": "JAVA_8",
"minVersion": "0.8",
"mixins": [
"ChunkMixin",
"DebugLevelSourceMixin"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@ -27,8 +27,26 @@ configurations {
runtimeClasspath.extendsFrom common
}
def extraModsDir = "extra-mods"
repositories {
exclusiveContent {
forRepository {
flatDir {
name "extra-mods"
dir file(extraModsDir)
}
}
filter {
includeGroup "extra-mods"
}
}
}
dependencies {
forge "net.minecraftforge:forge:${rootProject.forge_version}"
shadow(annotationProcessor("com.github.llamalad7.mixinextras:mixinextras-common:${rootProject.mixinextras_version}"))
runtimeOnly("com.github.llamalad7.mixinextras:mixinextras-common:${rootProject.mixinextras_version}")
// Remove the next line if you don't want to depend on the API
// modApi "me.shedaniel:architectury-forge:${rootProject.architectury_version}"
@ -51,6 +69,16 @@ dependencies {
modCompileOnly("vazkii.patchouli:Patchouli:1.16.4-53.3")
modImplementation "curse.maven:spark-361579:${rootProject.spark_forge_version}"
// runtime remapping at home
for (extraModJar in fileTree(dir: extraModsDir, include: '*.jar')) {
def basename = extraModJar.name.substring(0, extraModJar.name.length() - ".jar".length())
def versionSep = basename.lastIndexOf('-')
assert versionSep != -1
def artifactId = basename.substring(0, versionSep)
def version = basename.substring(versionSep + 1)
modRuntimeOnly("extra-mods:$artifactId:$version")
}
common(project(path: ":common", configuration: "namedElements")) { transitive false }
shadowCommon(project(path: ":common", configuration: "transformProductionForge")) { transitive = false }
}
@ -67,8 +95,10 @@ shadowJar {
exclude "fabric.mod.json"
exclude "architectury.common.json"
configurations = [project.configurations.shadowCommon]
configurations = [project.configurations.shadowCommon, project.configurations.shadow]
relocate("com.llamalad7.mixinextras", "org.embeddedt.modernfix.forge.shadow.mixinextras")
archiveClassifier.set("dev-shadow")
mergeServiceFiles()
}
remapJar {

View File

@ -7,6 +7,7 @@ import net.minecraft.network.chat.TranslatableComponent;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import net.minecraftforge.fml.loading.FMLLoader;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.ModernFixClient;
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
import org.embeddedt.modernfix.util.CommonModUtil;
@ -16,11 +17,11 @@ import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
public class NightConfigFixer {
public static final LinkedHashSet<Runnable> configsToReload = new LinkedHashSet<>();
public static void monitorFileWatcher() {
if(!ModernFixMixinPlugin.instance.isOptionEnabled("bugfix.fix_config_crashes.NightConfigFixerMixin"))
return;
@ -49,6 +50,7 @@ public class NightConfigFixer {
}
}
ModernFix.LOGGER.info("Processed {} config reloads", runnablesToRun.size());
couldShowMessage = true;
}
static class MonitoringMap extends ConcurrentHashMap<Path, Object> {
@ -74,13 +76,13 @@ public class NightConfigFixer {
}
}
private static long lastConfigTrigger = System.nanoTime();
private static boolean couldShowMessage = true;
private static void triggerConfigMessage() {
if((System.nanoTime() - lastConfigTrigger) >= TimeUnit.SECONDS.toNanos(5)) {
lastConfigTrigger = System.nanoTime();
if(false && couldShowMessage && Minecraft.getInstance().level != null && ModernFixClient.recipesUpdated && ModernFixClient.tagsUpdated) {
Minecraft.getInstance().execute(() -> {
if(Minecraft.getInstance().level != null) {
couldShowMessage = false;
Minecraft.getInstance().gui.getChat().addMessage(new TranslatableComponent("modernfix.message.reload_config"));
}
});
@ -102,8 +104,9 @@ public class NightConfigFixer {
synchronized(configsToReload) {
if(FMLLoader.getDist().isClient())
triggerConfigMessage();
if(configsToReload.size() == 0)
if(configsToReload.size() == 0) {
ModernFixMixinPlugin.instance.logger.info("Please use /{} to reload any changed mod config files", FMLLoader.getDist().isDedicatedServer() ? "mfsrc" : "mfrc");
}
configsToReload.add(configTracker);
}
}

View File

@ -14,14 +14,14 @@ import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.ExtensionPoint;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.*;
import net.minecraftforge.fml.client.registry.ClientRegistry;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.server.FMLServerStartedEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import org.embeddedt.modernfix.ModernFixClient;
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
import org.embeddedt.modernfix.forge.config.NightConfigFixer;
import org.embeddedt.modernfix.screen.ModernFixConfigScreen;
@ -45,7 +45,12 @@ public class ModernFixClientForge {
private void clientSetup(FMLClientSetupEvent event) {
configKey = new KeyMapping("key.modernfix.config", KeyConflictContext.UNIVERSAL, InputConstants.UNKNOWN, "key.modernfix");
ClientRegistry.registerKeyBinding(configKey);
if(ModernFixMixinPlugin.instance.isOptionEnabled("perf.dynamic_resources.ConnectednessCheck")
&& ModList.get().isLoaded("connectedness")) {
event.enqueueWork(() -> {
ModLoader.get().addWarning(new ModLoadingWarning(ModLoadingContext.get().getActiveContainer().getModInfo(), ModLoadingStage.SIDED_SETUP, "modernfix.connectedness_dynresoruces"));
});
}
}
@SubscribeEvent

View File

@ -20,7 +20,7 @@ import java.util.concurrent.ConcurrentMap;
@ClientOnlyMixin
@SuppressWarnings({"rawtypes", "unchecked"})
public class ResourceUtilMixin {
@Shadow @Final @Mutable
@Shadow(remap = false) @Final @Mutable
private static Map metadataCache;
/**

View File

@ -60,7 +60,7 @@ public class WindowMixin {
* Grab the original width/height from the window and inject them into our state variables.
*/
@SuppressWarnings("unchecked")
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/loading/progress/EarlyProgressVisualization;handOffWindow(Ljava/util/function/IntSupplier;Ljava/util/function/IntSupplier;Ljava/util/function/Supplier;Ljava/util/function/LongSupplier;)J"), require = 0)
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/loading/progress/EarlyProgressVisualization;handOffWindow(Ljava/util/function/IntSupplier;Ljava/util/function/IntSupplier;Ljava/util/function/Supplier;Ljava/util/function/LongSupplier;)J", remap = false), require = 0)
private long performHandoff(EarlyProgressVisualization instance, IntSupplier width, IntSupplier height, Supplier<String> title, LongSupplier monitor, WindowEventHandler arg, ScreenManager arg2, DisplayData arg3) {
Object visualizer = getEarlyProgressVisualizer();
if(visualizer != null && defaultDisplayData(arg3)) {

View File

@ -17,11 +17,11 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
public abstract class StarLightEngineMixin {
@Shadow protected abstract LevelChunkSection getChunkSection(int chunkX, int chunkY, int chunkZ);
@Shadow @Final protected int minSection;
@Shadow(remap = false) @Final protected int minSection;
@Inject(method = "handleEmptySectionChanges(Lnet/minecraft/world/level/chunk/LightChunkGetter;Lnet/minecraft/world/level/chunk/ChunkAccess;[Ljava/lang/Boolean;Z)[Z",
at = @At(value = "INVOKE", target = "Lca/spottedleaf/starlight/common/light/StarLightEngine;setEmptinessMapCache(II[Z)V",
shift = At.Shift.AFTER))
shift = At.Shift.AFTER, remap = false))
private void lazyInitMapIfNeeded(LightChunkGetter lightAccess, ChunkAccess chunk, Boolean[] emptinessChanges, boolean unlit, CallbackInfoReturnable<int[]> cir) {
final int chunkX = chunk.getPos().x;
final int chunkZ = chunk.getPos().z;

View File

@ -3,6 +3,7 @@ package org.embeddedt.modernfix.forge.mixin.core;
import net.minecraft.server.Bootstrap;
import org.apache.logging.log4j.Logger;
import org.embeddedt.modernfix.forge.load.ModWorkManagerQueue;
import org.embeddedt.modernfix.util.TimeFormatter;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@ -10,6 +11,8 @@ 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.management.ManagementFactory;
@Mixin(Bootstrap.class)
public class BootstrapMixin {
@Shadow private static boolean isBootstrapped;
@ -19,7 +22,7 @@ public class BootstrapMixin {
@Inject(method = "bootStrap", at = @At("HEAD"))
private static void doModernFixBootstrap(CallbackInfo ci) {
if(!isBootstrapped) {
LOGGER.info("ModernFix bootstrap");
LOGGER.info("ModernFix reached bootstrap stage ({} after launch)", TimeFormatter.formatNanos(ManagementFactory.getRuntimeMXBean().getUptime() * 1000L * 1000L));
ModWorkManagerQueue.replace();
}
}

View File

@ -20,7 +20,7 @@ import java.util.Map;
@Mixin(net.minecraftforge.client.ItemModelMesherForge.class)
@ClientOnlyMixin
public abstract class ItemModelMesherForgeMixin extends ItemModelShaper {
@Shadow @Final @Mutable private Map<IRegistryDelegate<Item>, ModelResourceLocation> locations;
@Shadow(remap = false) @Final @Mutable private Map<IRegistryDelegate<Item>, ModelResourceLocation> locations;
private Map<IRegistryDelegate<Item>, ModelResourceLocation> overrideLocations;
@ -48,6 +48,7 @@ public abstract class ItemModelMesherForgeMixin extends ItemModelShaper {
}
/**
* @author embeddedt
* @reason Get the stored location for that item and meta, and get the model
* from that location from the model manager.
**/
@ -59,6 +60,7 @@ public abstract class ItemModelMesherForgeMixin extends ItemModelShaper {
}
/**
* @author embeddedt
* @reason Don't get all models during init (with dynamic loading, that would
* generate them all). Just store location instead.
**/
@ -69,6 +71,7 @@ public abstract class ItemModelMesherForgeMixin extends ItemModelShaper {
}
/**
* @author embeddedt
* @reason Disable cache rebuilding (with dynamic loading, that would generate
* all models).
**/

View File

@ -35,7 +35,7 @@ import java.util.function.Predicate;
@ClientOnlyMixin
public abstract class CTMPackReloadListenerMixin implements ModernFixClientIntegration {
/* caches the original render checks */
@Shadow @Final private static Map<IRegistryDelegate<Block>, Predicate<RenderType>> blockRenderChecks;
@Shadow(remap = false) @Final private static Map<IRegistryDelegate<Block>, Predicate<RenderType>> blockRenderChecks;
private static Map<IRegistryDelegate<Block>, Predicate<RenderType>> renderCheckOverrides = new ConcurrentHashMap<>();
@ -50,6 +50,10 @@ public abstract class CTMPackReloadListenerMixin implements ModernFixClientInteg
ModernFixClient.CLIENT_INTEGRATIONS.add(this);
}
/**
* @author embeddedt
* @reason handle layer changes dynamically
*/
@Overwrite(remap = false)
private void refreshLayerHacks() {
renderCheckOverrides.clear();

View File

@ -20,7 +20,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@RequiresMod("refinedstorage")
@ClientOnlyMixin
public class ClientSetupMixin {
@Shadow @Final private BakedModelOverrideRegistry bakedModelOverrideRegistry;
@Shadow(remap = false) @Final private BakedModelOverrideRegistry bakedModelOverrideRegistry;
@Inject(method = "<init>", at = @At("RETURN"))
private void addDynamicListener(CallbackInfo ci) {

View File

@ -31,7 +31,7 @@ import java.util.stream.Stream;
@RequiresMod("supermartijn642corelib")
@ClientOnlyMixin
public class ClientRegistrationHandlerMixin {
@Shadow @Final private List<Pair<Supplier<Stream<ResourceLocation>>, Function<BakedModel, BakedModel>>> modelOverwrites;
@Shadow(remap = false) @Final private List<Pair<Supplier<Stream<ResourceLocation>>, Function<BakedModel, BakedModel>>> modelOverwrites;
private Map<ResourceLocation, Function<BakedModel, BakedModel>> modelOverwritesByLocation = new Object2ObjectOpenHashMap<>();

View File

@ -19,9 +19,9 @@ import java.util.Set;
@Mixin(ForgeRegistry.Snapshot.class)
@IgnoreOutsideDev
public class ForgeRegistrySnapshotMixin {
@Shadow @Final @Mutable public Map<ResourceLocation, Integer> ids;
@Shadow(remap = false) @Final @Mutable public Map<ResourceLocation, Integer> ids;
@Shadow @Final @Mutable public Set<ResourceLocation> dummied;
@Shadow(remap = false) @Final @Mutable public Set<ResourceLocation> dummied;
/**
* The only good reason to use tree maps here is to keep the order the same. But we are tracking IDs

View File

@ -3,6 +3,7 @@ package org.embeddedt.modernfix.platform.forge;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.io.Resources;
import com.llamalad7.mixinextras.MixinExtrasBootstrap;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.brigadier.CommandDispatcher;
import cpw.mods.modlauncher.*;
@ -195,6 +196,7 @@ public class ModernFixPlatformHooksImpl implements ModernFixPlatformHooks {
}
NightConfigFixer.monitorFileWatcher();
MixinExtrasBootstrap.init();
}
private Method defineClassMethod = null;

View File

@ -9,5 +9,8 @@ ${mixin_classes}
],
"injectors": {
"defaultRequire": 1
},
"overwrites": {
"conformVisibility": true
}
}

View File

@ -2,6 +2,7 @@
org.gradle.jvmargs=-Xmx2G
junit_version=5.10.0-M1
mixinextras_version=0.2.0-beta.9
mod_id=modernfix
minecraft_version=1.16.5
@ -23,3 +24,5 @@ modmenu_version=1.16.23
spark_forge_version=3767277
spark_fabric_version=3337642
use_fabric_api_at_runtime=true

View File

@ -10,8 +10,14 @@ pluginManagement {
include("test_agent")
include("common")
getProperty("enabled_platforms").tokenize(',').each { it ->
include(it.trim())
def current_platforms = getProperty("enabled_platforms").tokenize(',')
current_platforms.each { it ->
def platform_name = it.trim()
include(platform_name)
def testmodFolder = new File(platform_name + "/" + "testmod")
if(testmodFolder.isDirectory()) {
include(platform_name + ":testmod")
}
}
rootProject.name = 'modernfix'