Merge remote-tracking branch 'origin/1.20' into dev/1.20.2

This commit is contained in:
embeddedt 2023-08-17 11:30:19 -04:00
commit eb15718023
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
75 changed files with 1767 additions and 83 deletions

View File

@ -105,6 +105,7 @@ allprojects {
maven {
url 'https://maven.terraformersmc.com/releases'
}
maven { url = "https://jitpack.io" }
}
}
@ -157,7 +158,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
@ -175,10 +176,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 + "^"
@ -215,8 +216,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

@ -12,8 +12,9 @@ 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
}
modApi("dev.latvian.mods:rhino:${rhino_version}") {
@ -37,6 +38,9 @@ dependencies {
// modApi "me.shedaniel:architectury:${rootProject.architectury_version}"
}
// don't need remapped common jar
tasks.named('remapJar') { enabled = false }
publishing {
publications {
mavenCommon(MavenPublication) {

View File

@ -31,7 +31,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,78 @@
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();
}
@Override
public int getMinBuildHeight() {
return this.wrapped.getMinBuildHeight();
}
@Override
public int getHeight() {
return this.wrapped.getHeight();
}
@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.blockEntityTickers.clear();
} catch(RuntimeException e) {
ModernFix.LOGGER.error("Exception clearing level data", e);
}
}
}
}

View File

@ -0,0 +1,22 @@
package org.embeddedt.modernfix.common.mixin.bugfix.world_screen_skipped;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen;
import net.minecraft.client.gui.screens.worldselection.WorldSelectionList;
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(WorldSelectionList.WorldListEntry.class)
public class WorldSelectionListMixin {
@Shadow @Final private Minecraft minecraft;
@Inject(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/worldselection/WorldSelectionList$WorldListEntry;doDeleteWorld()V", ordinal = 0, shift = At.Shift.AFTER), cancellable = true)
private void preventClosingCreateScreenAfterDelete(CallbackInfo ci) {
if(minecraft.screen instanceof CreateWorldScreen)
ci.cancel();
}
}

View File

@ -11,6 +11,10 @@ import org.spongepowered.asm.mixin.Overwrite;
@Mixin(Minecraft.class)
@ClientOnlyMixin
public class MinecraftMixin {
/**
* @author embeddedt
* @reason avoid exception stacktrace being printed in dev
*/
@Overwrite
private UserApiService createUserApiService(YggdrasilAuthenticationService yggdrasilAuthenticationService, GameConfig arg) {
return UserApiService.OFFLINE;

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,40 @@
package org.embeddedt.modernfix.common.mixin.perf.compact_mojang_registries;
import com.google.common.collect.ImmutableSet;
import com.mojang.serialization.Lifecycle;
import net.minecraft.core.MappedRegistry;
import net.minecraft.resources.ResourceLocation;
import org.embeddedt.modernfix.annotation.IgnoreOutsideDev;
import org.embeddedt.modernfix.registry.LifecycleMap;
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> {
@Shadow
@Final
@Mutable
private Map<T, Lifecycle> lifecycles;
private static final ImmutableSet<ResourceLocation> MFIX$NEW_STORAGE_KEYS = ImmutableSet.of(new ResourceLocation("block"), new ResourceLocation("item"));
@Inject(method = "<init>(Lnet/minecraft/resources/ResourceKey;Lcom/mojang/serialization/Lifecycle;Z)V", 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

@ -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

@ -49,6 +49,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.
**/
@ -59,6 +60,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.
**/
@ -68,6 +70,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;)Lnet/minecraft/core/Holder$Reference;",
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

@ -11,13 +11,10 @@ import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
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)
@ -48,7 +45,7 @@ public abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState>
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) {
@ -67,7 +64,6 @@ public abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState>
}
}
return this.cache;
}
@Redirect(method = "*", at = @At(
@ -77,7 +73,8 @@ public abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState>
ordinal = 0
))
private BlockBehaviour.BlockStateBase.Cache dynamicCacheGen(BlockBehaviour.BlockStateBase base) {
return generateCache(base);
mfix$generateCache();
return this.cache;
}
@Redirect(method = "*", at = @At(
@ -106,22 +103,4 @@ public abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState>
return this.owner.isRandomlyTicking(this.asState());
return this.isRandomlyTicking;
}
@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);
}
}

View File

@ -18,7 +18,7 @@ public abstract class BiomeMixin {
* @return
*/
@Overwrite
public final float getTemperature(BlockPos pos) {
private float getTemperature(BlockPos pos) {
return this.getHeightAdjustedTemperature(pos);
}
}

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;
@ -251,6 +252,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.");
@ -339,6 +351,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;
@ -27,6 +28,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<ModelBakery.BakedCacheKey, BakedModel> bakedCache;
@ -138,7 +148,10 @@ public class DynamicBakedModelProvider implements Map<ResourceLocation, BakedMod
return model;
else {
try {
model = ((IExtendedModelBakery)bakery).bakeDefault((ResourceLocation)o, BlockModelRotation.X0_Y0);
if(BAKE_SKIPPED_TOPLEVEL.contains((ResourceLocation)o))
model = missingModel;
else
model = ((IExtendedModelBakery)bakery).bakeDefault((ResourceLocation)o, BlockModelRotation.X0_Y0);
} catch(RuntimeException e) {
ModernFix.LOGGER.error("Exception baking {}: {}", o, e);
model = missingModel;

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

@ -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

@ -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,11 @@
"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.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,115 @@
{
"key.modernfix": "ModernFix",
"key.modernfix.config": "設定ファイルを開く",
"modernfix.jei_load": "JEIを読み込み中... しばらく時間がかかる可能性があります。",
"modernfix.no_lazydfu": "LazyDFUがインストールされていません。Minecraftが古いバージョンのゲームデータを更新する必要がある場合、目立つラグが発生する可能性があります。",
"modernfix.no_ferritecore": "FerriteCoreがインストールされていません。メモリ使用量は非常に高くなります。",
"modernfix.perf_mod_warning": "Modをインストールすることをお勧めしますが、警告はModernFixの設定で無効にできます。",
"modernfix.config": "ModernFixによるmixinの設定",
"modernfix.config.done_restart": "完了 (再起動が必要)",
"modernfix.option.on": "オン",
"modernfix.option.off": "オフ",
"modernfix.option.disabled": "無効",
"modernfix.option.enabled": "有効",
"modernfix.option.mod_override": " 改造者 [%s]",
"modernfix.config.not_default": " [変更済]",
"asynclocator.map.locating": "マップ(ロケーティング)",
"asynclocator.map.none": "マップ (近くの機能が見つかりません)",
"modernfix.option.category.performance": "パフォーマンス",
"modernfix.option.category.performance.description": "ゲーム/起動パフォーマンスの向上に役立つ機能",
"modernfix.option.category.bugfixes": "バグ修正",
"modernfix.option.category.bugfixes.description": "ゲームの安定性を向上させるためのコアバグ修正",
"modernfix.option.category.troubleshooting": "トラブルシューティング/ユーティリティ",
"modernfix.option.category.troubleshooting.description": "問題の診断に役立つ機能",
"modernfix.option.category.expert_only": "上級者のみ",
"modernfix.option.category.expert_only.description": "あなたが何をしているかを知っていない限り、これを変更しないでください!",
"modernfix.option.name.mixin.perf.async_jei": "JEIのバックグラウンド読み込み",
"modernfix.option.mixin.perf.async_jei": "1.16のみです。**キーの最適化** JEIがバックグラウンドスレッドでリロードされるようにパッチを適用することで、世界の読み込みに追加される長い遅延が完全になくなります。",
"modernfix.option.mixin.perf.async_locator": "1.16 のみ。`/locate`に関連付けられているサーバーのフリーズや、戦利品テーブルの生成などを解消するために、Async Locator modのパッチをバックアップします。",
"modernfix.option.mixin.perf.biome_zoomer": "1.16のみ。1.18からのロジックを使用してバイオームズームのパフォーマンスを向上させるためのマイナーな最適化。",
"modernfix.option.mixin.perf.blast_search_trees": "すべてのバージョン。 REIまたはJEIがインストールされている場合、レシピ検索のためのバニラ検索ツリーの構築が無効になります。 代わりに、これらのmodsの検索実装を使用して検索を行います。 これにより、世界の読み込み中に数秒節約でき、おそらくいくつかのRAMも節約できます(私は測定していませんが)。",
"modernfix.option.mixin.perf.boost_worker_count": "1.16 のみ。Mojang が 1.18 と同様に、ワーカースレッド数のハードコードされたキャップを削除します。",
"modernfix.option.mixin.perf.cache_blockstate_cache_arrays": "すべてのバージョンです。ブロック状態キャッシュが初期化されるたびに列挙型配列の新規コピーを作成するのを避けます。マイナーな最適化ですが、簡単に行えます。",
"modernfix.option.mixin.perf.cache_model_materials": "すべてのバージョン。リクエストごとに再計算する代わりにモデルが返す`RenderMaterial` (テクスチャ) コレクションと依存関係リストをメモします。 モデルのロード/焼成プロセスを加速するのに役立ちます。",
"modernfix.option.mixin.perf.cache_strongholds": "すべてのバージョン。全てのワールドロード時に再生成するのではなく、ワールドで生成された要塞位置のリストを保存します。 1.16で少し時間を節約し、1.18と1.19ではかなり多くの時間を節約します。",
"modernfix.option.mixin.perf.cache_upgraded_structures": "すべてのバージョンです。多くのMODは古い構造ファイルを出荷しています。DFUを使用してゲームをアップグレードする必要があります。 これはかなり遅い可能性があります。このパッチは、アップグレードされたバージョンの構造を保存する代わりにロジックを追加し、次のロードで再利用します。 modが名前ではなく構造ファイルを変更する場合を処理する 元のファイルのハッシュはキャッシュされたバージョンと比較され、一致しない場合は再び構造がアップグレードされます。",
"modernfix.option.mixin.perf.compress_biome_container": "1.16 のみ。可能であればバイオームコンテナ内のスペースを節約しようとする、Hydrogenから借りたマイナーな最適化。 BetterEnd や Chocolate のような競合する Mod がインストールされている場合、自動的に無効になります。",
"modernfix.option.mixin.perf.datapack_reload_exceptions": "すべてのバージョン. ログスパムを削減し、データパックの再ロード中にいくつかの一般的にスローされる例外のスタックトレースを印刷しないことにより、ロード速度をわずかに向上させます (e. を選択します。戦利品のテーブル/レシピに欠落しています。メッセージは印刷されます。",
"modernfix.option.mixin.perf.dedicated_reload_executor": "すべてのバージョンです。デフォルトの `Worker-Main` スレッドを使用する代わりに、リソースパックとデータパックを専用のワーカープールにリロードします。 これにより、Modはスムーズブートを可能にし、限られたスレッド数で起動を遅らせずに実行時のシングルプレイヤーのパフォーマンスを向上させることができます。",
"modernfix.option.mixin.perf.deduplicate_location": "すべてのバージョンですが、時間の影響によりデフォルトでは無効になります。リソースロケーションの名前空間とパスをDeduplicatesします。 これによりRAMは節約されますが、新しい「ResourceLocation」を構築するコストもかなり増加します。",
"modernfix.option.mixin.perf.dynamic_dfu": "すべてのバージョンです。何かをアップグレードする必要があるときにDFU初期化を変更します。 これはLazyDFUに似ているように聞こえますが、LazyDFUはルールの最適化のみを無効にするので、明確に実装されています。 基本的に、このオプションはDataFixerSlayerの安全なバージョンであり、必要に応じてDFUをロードします。\n\nDFUルールの最適化が遅れるため、通常はこのオプションを有効にしてもLazyDFUを使用し続ける必要があります。",
"modernfix.option.mixin.perf.dynamic_resources": "すべてのバージョン。https://github.com/embeddedt/ModernFix/wiki/Dynamic-Resources-FAQを参照してください。",
"modernfix.option.mixin.perf.dynamic_structure_manager": "すべてのバージョンです。生成後、ゲームは永久に読み込まれるのではなく、構造ファイルをアンロードすることができます。",
"modernfix.option.mixin.perf.fast_registry_validation": "すべてのバージョン。Forge はレジストリが検証されるたびに反射を使ってメソッドを検索します。 このパッチは、毎回同じになるため、返された値をキャッシュするだけです。",
"modernfix.option.mixin.perf.faster_font_loading": "すべてのバージョンです。フォントレンダラーを最適化し、フォントの読み込みを高速化し、リソースのリロードを高速化します。",
"modernfix.option.mixin.perf.faster_item_rendering": "すべてのバージョンです。GUI内のアイテムの側面をレンダリングするのを避けます。(はい、バニラがそうするように見えます。\n\n十分なアイテムが表示されている場合、弱いGPUにREI/JEIのようなModがインストールされているとFPSを3倍にすることができます。 新しいものでテストされていないため、デフォルトで無効になっていますが、安全である必要があります。 おそらく、GUIで全く見えないアイテムや、世界中でフラットに見えるアイテムが問題です。",
"modernfix.option.mixin.perf.faster_texture_loading": "1.19.4より前のすべてのバージョン。 テクスチャを2回(非常に遅いコードパスを使用する最初の時) 読み取りを避け、代わりに1回高速負荷を行います(1.19.3以降と同様)。",
"modernfix.option.mixin.perf.faster_texture_stitching": "すべてのバージョンです。スーパーCoder79が1.7でlwjgl3ify用に作成した、より高速なテクスチャステッチシステムを使用できるようになります。 0は読み込み中に時間を節約できます。 まれに、ブロックやGUIに奇妙なアーティファクトが発生することが報告されています。これはナトリウムバグかもしれません。",
"modernfix.option.mixin.perf.jeresources_startup": "1.16のみ。村人を同じ職業に何度も再現しないように十分なリソースを最適化します。JEIのスタートアップ時の時間を節約できます。",
"modernfix.option.mixin.perf.kubejs": "1.16 のみです。不必要な `ItemStack` のコピーなどを避けるために KubeJS への最適化により、データパックの読み込みにかかる時間が短縮されます。",
"modernfix.option.mixin.perf.model_optimizations": "すべてのバージョンです。最適化を実装して、モデルの読み込みプロセスを高速化します。",
"modernfix.option.mixin.perf.nbt_memory_usage": "すべてのバージョン。 キー名をdeduplicateし、非常に小さな化合物の配列マップを使用する複合NBTタグに、より効率的なバッキングマップを使用します。 これにより、メモリに多くの複合タグを格納するオーバーヘッドが軽減されます。",
"modernfix.option.mixin.perf.nuke_empty_chunk_sections": "1.16 は Hydrogen に触発されています。空気がいっぱいのチャンクセクションをメモリに保存しないでください。",
"modernfix.option.mixin.perf.reduce_blockstate_cache_rebuilds": "すべてのバージョン。 **主要な最適化。** 新しい Minecraft バージョン (1.12 以降) では、ブロックステートについて頻繁に使用される情報 (ソリッドかどうか、衝突形状など) をキャッシュするブロックステート キャッシュ システムが実装されました。このキャッシュの再構築はバニラでは非常に高速です。 (わずか 1 2 秒しかかかりません) ただし、ゲーム内にはさらに多くのブロックステートが存在し、キャッシュを再構築する必要があるため、多くの MOD がインストールされていると非常に遅くなります。 Forge では、次の再構築までにデータがほぼ確実に未使用になる多くの時点でキャッシュが再構築されるため、この問題はさらに悪化します。 例には、メイン メニューに到達する直前 (「データの凍結」段階中) や、ワールドがロードされている複数回 (!) の時間が含まれます。 ModernFix は、代わりにキャッシュの再構築を遅延させることで、このパフォーマンスのボトルネックを解決します。 各ブロック状態は、データが初めてアクセスされるときにキャッシュを再構築します。 バニラまたは Forge がすべてのブロック状態のキャッシュを再構築しようとする時点で、これはリダイレクトされ、代わりに各ブロック状態のキャッシュを単に無効化します。 これは、起動完了後の TPS には影響しません。",
"modernfix.option.mixin.perf.remove_biome_temperature_cache": "すべてのバージョンです。Lithiumが最新バージョンで行うように、バイオーム温度キャッシュを削除します。",
"modernfix.option.mixin.perf.resourcepacks": "すべてのバージョン。**最近のバージョンでは、ファイルシステムへのアクセスが起動の大きなボトルネックとなっている。リソースをリストアップしたり、指定されたリソースが存在するかどうかをチェックするために、リソースパックに多くのリクエストが頻繁に行われ、これらのそれぞれが非常に遅いファイルAPI呼び出しになります。\n\nModernFixは、MODが提供するリソースパックとバニラリソースパック内に存在するすべてのリソースのリストをキャッシュするだけで、このボトルネックのほとんどを完全に排除します。キャッシュはリソースのリロード時に再構築されますバニラリソースを除く。\n\nこのパッチには、OptiFineそのCTMリソースが正しくロードされないを除いて、既知の互換性の問題はありません。しかし、OptiFineはそれだけで起動時間が数分長くなり、ModernFixとのテストが全く行われていないため、どのようなシナリオでもOptiFineを使用することはお勧めしません。",
"modernfix.option.mixin.perf.reuse_datapacks": "1.16 のみ。可能な場合、データパックのリロードをスキップしてシングルプレイヤー間の切り替えをスピードアップしようとします。 一部のMODとの互換性に問題が生じる可能性がありますが、現在デフォルトで有効になっています。",
"modernfix.option.mixin.perf.rewrite_registry": "すべてのバージョンです。**現在半分壊れています。 * 積極的に Forge レジストリシステムのいくつかの内部をより高速なバージョンに置き換えますが、Modpackをロードすると現在フリーズします。 明白な理由により、デフォルトでオフになります。",
"modernfix.option.mixin.perf.skip_first_datapack_reload": "1.16の開発サイクルの途中で、ForgeはバイオームIDシフトの問題を修正するために、既存のワールドをロードする際にデータパックを2回リロードするようにパッチを適用しました。残念なことに、データパックのリロードには30秒以上かかることが多いため、ワールドのロード時間に深刻な影響を及ぼしています。\n\nModernFixは、未完成のForge PR #8163に基づいて、このリロードを回避するために必要な変更を行います。\n\nこの変更は1.18でForgeによって削除されましたが、その後1.19で同様のパッチが*再び*追加され、新しいシングルプレイヤーワールドを作成する際にMODデータパックがロードされない問題が修正されました。幸いなことに、1.19ではこの問題はワールド作成画面に限定され、既存のワールドは1回のリロードで済むようになりました。しかし、それでも1.19の \"Create New World \"をクリックしたときのラグ・スパイクの長さは2倍になるため、ModernFixは冗長なリロードを行わないように再び変更を加えています。",
"modernfix.option.mixin.perf.state_definition_construct": "すべてのバージョン。FerriteCoreがインストールされている場合にのみ有効です。FerriteCoreのブロック状態の処理を利用して、作成をスピードアップします。 これは、家具モッドなどの多くのブロック状態を追加するModでの立ち上げを加速することができます。",
"modernfix.option.mixin.perf.sync_executor_sleep": "すべてのバージョン。Modloadingワーカーが終了するのを待っている間に、メインスレッドが一度だけCPUコアを消費することを回避します。",
"modernfix.option.mixin.perf.thread_priorities": "すべてのバージョン。ワーカーとサーバースレッドの優先度をクライアントスレッドよりも低く調整します。 これは、Java実装が優先順位を尊重していれば、CPUコアが少ないマシンでのFPSの安定性を向上させるのに役立ちます。",
"modernfix.option.mixin.perf.use_integrated_resources": "主に1.16の場合、JEResourcesがシングルプレイヤーをプレイする場合、戦利品テーブルを無意味にリロードするのではなく、統合サーバーの戦利品テーブルデータを使用するようにパッチします。 JEIの起動中にもう数秒節約します。",
"modernfix.option.mixin.bugfix.concurrency": "このグループのパッチは、Minecraft や Forge 内で同時に発生する問題を修正します。ほとんどのパッチは、読み込み中に稀で診断が難しいクラッシュを引き起こします。",
"modernfix.option.mixin.bugfix.edge_chunk_not_saved": "このオプションは、SuperCoder のチャンク節約修正モジュールのポートです(その時点で Forge ではすでに利用可能であることに気付いていませんでした)。",
"modernfix.option.mixin.bugfix.mc218112": "このオプションは、エンティティデータの処理中に例外が発生した場合に発生するデッドロックを修正します。 バニラは必要なときにデータマネージャのロックを正しく解除しません。 これはバグトラッカーでMC-218112として追跡され、1.17でMojangによって修正されました。",
"modernfix.option.mixin.bugfix.packet_leak": "**実験**はデフォルトで有効になっていません。1.16で十分な長時間再生した後に発生するメモリリーク問題の修正を試みました。",
"modernfix.option.mixin.bugfix.paper_chunk_patches": "1.18 以降。**鍵の最適化** 1の問題を修正する紙のパッチをポートします。 chunkloadは膨大なメモリを必要とし、多くの`ComplettableFuture`インスタンスを生成します。 1.18以降では、1.16のように400MBのメモリでワールドを読み込むことができます。",
"modernfix.option.mixin.bugfix.tf_cme_on_load": "パッチTwilight Forestは、FML ワーカースレッドではなく、メインスレッドを使用して非スレッドセーフなクライアント設定を実行します。",
"modernfix.option.mixin.feature.branding": "タイトル画面のブランドリストにModernFixを追加し、F3画面にも追加します。",
"modernfix.option.mixin.feature.direct_stack_trace": "通常は off にすることで、クラッシュ時にrawスタックトレースをログにダンプさせることができます。 場合によっては、バニラのクラッシュレポートシステムが正常に動作せず、スタックトレース/レポートを完全に無関係にすることがあります。",
"modernfix.option.mixin.feature.measure_time": "ワールドロード時間、データパックのリロード時間、リロード時間、ブートストラップ時間を測定するために、2つの注射を使用します。 そして、必要なフックを追加して、バニラの未使用のプロファイラロジックをリソースの再ロードに有効にします。",
"modernfix.option.mixin.feature.spam_thread_dump": "**デバッグ目的のみに使用するために使用します。** スレッドダンプを60秒ごとにログに出力します。 これにより、読み込み/ゲームプレイ中に解明されていないフリーズを診断するのに役立ちます。",
"modernfix.option.mixin.bugfix.chunk_deadlock": "チャンクシステムのデッドロックを防ぐか、ログに追加のデバッグ情報を提供しようとします。 これらのデッドロックは通常、サーバーが無期限にフリーズする(例えば、エンティティが移動しない)としてマニフェストされ、クライアントは正常に動作し続けます。",
"modernfix.option.mixin.bugfix.chunk_deadlock.valhesia": "パッチValhesia Structures to fix an problem in its code that cause frequent worldgen/chunkloading deadlocks.",
"modernfix.option.mixin.bugfix.cofh_core_crash": "CoFHコアで起動中にまれなクラッシュを引き起こすマルチスレッドの問題を修正しました。",
"modernfix.option.mixin.bugfix.ctm_resourceutil_cme": "ConnectedTexturesModで起動中にまれなクラッシュを引き起こすマルチスレッドの問題を修正しました。",
"modernfix.option.mixin.bugfix.ender_dragon_leak": "以前のクライアントの世界への参照を保持しているエンダードラゴンによって引き起こされるバニラのメモリリークを修正しました。",
"modernfix.option.mixin.bugfix.entity_load_deadlock": "EntityJoinWorldEvent/EntityJoinLevelEvent がエンティティの読み込みを少し遅らせることによって世界的なデッドロックを引き起こす多くの問題を修正します。ただし、ゲーム内で顕著な動作変更を引き起こす必要があります。",
"modernfix.option.mixin.bugfix.fix_config_crashes": "修正されたForgeはゲームを起動する際に時々壊れてしまうことがあります。",
"modernfix.option.mixin.bugfix.item_cache_flag": "MC-258939を修正",
"modernfix.option.mixin.bugfix.preserve_early_window_pos": "Forge の初期ロードから Minecraft コードにコントロールが渡されると、ゲームウィンドウが既存のサイズを保持します。 ドラッグされた後、画面の中心に戻ってテレポートするウィンドウを修正します。",
"modernfix.option.mixin.bugfix.refinedstorage.te_bug": "修理済みストレージ外部ストレージブロックに引き出しの内容などが表示されない場合があります。 ときにロードされます。Refined Storage PR #3435のバックポート。これは1.18以降にのみ適用されました。",
"modernfix.option.mixin.bugfix.remove_block_chunkloading": "Forgeで0チャンクを永久にロードしたゾンビピッグマンを修正。ForgeのPR#8583のバックポート。",
"modernfix.option.mixin.bugfix.starlight_emptiness": "初期化されていない空マップのために時折Starlightがクラッシュする問題を修正します。1.18.xのStarlightで同じ修正をバックポートします。",
"modernfix.option.mixin.core": "ModernFixが動作するために必要なコアパッチです",
"modernfix.option.mixin.devenv": "開発環境で実行するときに使用されるパッチ、速度向上および/またはテストのために使用されるパッチ。",
"modernfix.option.mixin.safety": "起動時にクラッシュを防ぐためにパッチを並行します。",
"modernfix.option.mixin.feature.integrated_server_watchdog": "バニラウォッチドッグをシングルプレイヤーの世界にも追加しますが、強制的に世界を終了するのではなくスタックトレースをプリントアウトします。 このバージョンには、Fullstack Watchdog の機能が含まれていますが、後者はマルチプレイにはまだ必要です。",
"modernfix.option.mixin.feature.snapshot_easter_egg": "スナップショット版で実行中にイースターエッグ機能を追加します (バニラビジュアルや動作には影響しません)。",
"modernfix.option.mixin.feature.spark_profile_launch": "有効にすると、互換性のあるバージョンの Spark がインストールされている場合、起動シーケンス全体がメインメニューまでプロファイルされます。",
"modernfix.option.mixin.feature.warn_missing_perf_mods": "本質的で互換性が高いと考えられる他のパフォーマンスMODが存在しない場合、起動時に警告を表示します",
"modernfix.option.mixin.launch.class_search_cache": "ForgeのリソースファインダーゲームとModコードの検索に使用を大幅に高速なバージョンに置き換え、起動を加速します。",
"modernfix.option.mixin.perf.clear_fabric_mapping_tables": "Modで冗長またはまれに使用されているFabric Loader のマッピングデータ構造をクリアすることで、メモリ使用量を削減します。 互換性の理由により、デフォルトではオフになります。",
"modernfix.option.mixin.perf.clear_mixin_classinfo": "起動時にすべてのミックスインを強制ロードし、ミックスインのデータ構造をクリアして、Mixinのメモリフットプリントの大部分を削除します。 互換性の理由によりデフォルトで無効になっています。",
"modernfix.option.mixin.perf.deduplicate_wall_shapes": "ほとんどの壁ブロックは、それぞれのブロックがそれぞれのブロックにコピーを持つのではなく、同じ形状のオブジェクトを共有するようにします。 Modで多くのウォールブロックを追加すると、メモリ使用量を大幅に削減できます。",
"modernfix.option.mixin.perf.dynamic_resources.ae2": "動的リソースのためのAE2互換性パッチ。",
"modernfix.option.mixin.perf.dynamic_resources.ctm": "動的リソース用のCTM互換性パッチ",
"modernfix.option.mixin.perf.dynamic_resources.rs": "動的リソースのためのAE2互換性パッチ。",
"modernfix.option.mixin.perf.dynamic_resources.supermartijncore": "動的リソースのためのAE2互換性パッチ。",
"modernfix.option.mixin.perf.dynamic_resources.diagonalfences": "動的リソースのためのAE2互換性パッチ。",
"modernfix.option.mixin.perf.faster_advancements": "前進チェックロジックを高速化し、大きなパックで StackOverflowError を引き起こさないように書き換えます。前進ポートは Fabric からデバッグします。",
"modernfix.option.mixin.perf.patchouli_deduplicate_books": "メモリ使用量を削減し、NBTタグで多くの空のアイテムを格納するパッチョリ本を修正しました。",
"modernfix.option.mixin.perf.remove_spawn_chunks": "ゲームからスポーンチャンクを完全に削除します。これらは、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": "解決法 設計図のObjectModificationManagerがバニラリソースをリークしているので、メモリ使用量を削減します。PR #195では修正が貢献されているにもかかわらず、リリースはまだ行われていません。",
"modernfix.option.mixin.bugfix.removed_dimensions": "ディメンションMODが削除された場合、ゲームがワールドの読み込みに失敗する問題を修正しました。Forge PR #899のバックポート。",
"modernfix.option.mixin.perf.compact_bit_storage": "ブロックが含まれているかのように空のチャンクを送信する旧式のサーバー (Hypixel など) によって引き起こされるメモリの浪費を修正します。これらのサーバーでメモリ使用量を大幅に削減します。",
"modernfix.option.mixin.perf.deduplicate_climate_parameters": "新しいバイオームシステムによって使用される気候パラメータオブジェクトをdeduplicates, 〜2MBを保存することができますが、また、データパックのリロードが少し遅くなります。",
"modernfix.option.mixin.perf.dynamic_entity_renderers": "起動時の代わりにエンティティモデルを初めて見るときに構築します。 一部の Mod はこのオプションと互換性がないため、EntityRenderer がクラッシュします。",
"modernfix.option.mixin.perf.twilightforest.structure_spawn_fix": "Twilight Forest worldgen のチェック構造によって引き起こされる遅延を修正しました。",
"modernfix.option.mixin.perf.fast_forge_dummies": "より高速なコードパスを使用することで、起動中に Forge レジストリの凍結を高速化する",
"modernfix.option.mixin.perf.tag_id_caching": "毎回再作成する代わりにlocationオブジェクトをキャッシュすることでタグエントリの使用を高速化します。",
"modernfix.option.mixin.feature.disable_unihex_font": "Unicodeフォントを削除し、10MBを保存しますが、特殊文字がレンダリングされなくなります"
}

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,13 @@
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 field net/minecraft/world/level/Level blockEntityTickers Ljava/util/List;
accessible class net/minecraft/client/renderer/RenderType$CompositeRenderType
accessible method net/minecraft/nbt/CompoundTag <init> (Ljava/util/Map;)V

View File

@ -43,18 +43,24 @@ repositories {
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}")))
modImplementation(fabricApi.module("fabric-api-base", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modImplementation(fabricApi.module("fabric-lifecycle-events-v1", 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-v2", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modImplementation(fabricApi.module("fabric-model-loading-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
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-v2", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modCompileOnly(fabricApi.module("fabric-model-loading-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modCompileOnly(fabricApi.module("fabric-models-v0", "0.84.0+1.20.1")) { 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(fabricApi.module("fabric-data-generation-api-v1", 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_version}"
modRuntimeOnly("net.fabricmc.fabric-api:fabric-api:${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' }
modCompileOnly(fabricApi.module("fabric-data-generation-api-v1", 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_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_version}"
}
// 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.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(false && 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,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

@ -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,44 @@
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", "0.84.0+1.20.1")) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
modImplementation(fabricApi.module("fabric-registry-sync-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,12 @@
package org.embeddedt.modernfix.testmod;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
public class TestBlockItem extends BlockItem {
private static final Item.Properties PROPERTIES = new Item.Properties();
public TestBlockItem(TestBlock block) {
super(block, PROPERTIES);
}
}

View File

@ -0,0 +1,68 @@
package org.embeddedt.modernfix.testmod;
import com.google.common.base.Stopwatch;
import net.fabricmc.api.ModInitializer;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
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 = 32;
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(BuiltInRegistries.BLOCK, name, new TestBlock());
WOOL_STATES.add(block.defaultBlockState());
//Registry.register(BuiltInRegistries.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,143 @@
package org.embeddedt.modernfix.testmod.client;
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.util.RandomSource;
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.Collection;
import java.util.Collections;
import java.util.List;
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<RandomSource> randomSupplier, RenderContext context) {
context.meshConsumer().accept(mesh);
}
@Override
public void emitItemQuads(ItemStack stack, Supplier<RandomSource> randomSupplier, RenderContext context) {
context.meshConsumer().accept(mesh);
}
@Override
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource 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 void resolveParents(Function<ResourceLocation, UnbakedModel> function) {
}
private static int scaleColor(int c) {
return c * 255 / TestMod.MAX_COLOR;
}
@Nullable
@Override
public BakedModel bake(ModelBaker baker, Function<Material, TextureAtlasSprite> spriteGetter, ModelState state, 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,42 @@
package org.embeddedt.modernfix.testmod.mixin;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.StructureManager;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.levelgen.FlatLevelSource;
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) {
super(biomeSource);
}
@Override
public void applyBiomeDecoration(WorldGenLevel level, ChunkAccess chunk, StructureManager structureFeatureManager) {
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
ChunkPos chunkPos = chunk.getPos();
int i = chunkPos.x;
int j = chunkPos.z;
for(int k = 0; k < 16; ++k) {
for(int l = 0; l < 16; ++l) {
int m = SectionPos.sectionToBlockCoord(i, k);
int n = SectionPos.sectionToBlockCoord(j, l);
for(int y = 0; y < 255; y++) {
BlockState blockState = TestMod.getColorCubeStateFor(m, y, n);
if (blockState != null) {
level.setBlock(mutableBlockPos.set(m, y, n), blockState, 2);
}
}
}
}
}
}

View File

@ -0,0 +1,23 @@
package org.embeddedt.modernfix.testmod.mixin;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.state.BlockState;
import org.embeddedt.modernfix.testmod.TestMod;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(targets = { "net/minecraft/client/renderer/chunk/RenderChunk" })
public class RenderChunkMixin {
@Shadow @Final private boolean debug;
@Inject(method = "getBlockState", at = @At("HEAD"), cancellable = true)
private void redirectDebugWorld(BlockPos pos, CallbackInfoReturnable<BlockState> cir) {
if(this.debug) {
cir.setReturnValue(TestMod.getColorCubeStateFor(pos.getX(), pos.getY(), pos.getZ()));
}
}
}

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,16 @@
{
"required": true,
"package": "org.embeddedt.modernfix.testmod.mixin",
"compatibilityLevel": "JAVA_8",
"minVersion": "0.8",
"mixins": [
"ChunkMixin",
"DebugLevelSourceMixin"
],
"client": [
"RenderChunkMixin"
],
"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}"
implementation(annotationProcessor("com.github.llamalad7.mixinextras:mixinextras-common:${rootProject.mixinextras_version}"))
implementation(include("com.github.llamalad7.mixinextras:mixinextras-forge:${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}"
@ -45,6 +63,16 @@ dependencies {
modCompileOnly("curse.maven:blueprint-382216:3991478")
// 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 }
}

View File

@ -7,6 +7,7 @@ import net.minecraft.network.chat.Component;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
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(couldShowMessage && Minecraft.getInstance().level != null && ModernFixClient.recipesUpdated && ModernFixClient.tagsUpdated) {
Minecraft.getInstance().execute(() -> {
if(Minecraft.getInstance().level != null) {
couldShowMessage = false;
Minecraft.getInstance().gui.getChat().addMessage(Component.translatable("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

@ -19,10 +19,12 @@ import net.minecraftforge.event.level.LevelEvent;
import net.minecraftforge.event.server.ServerStartedEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.*;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
import org.embeddedt.modernfix.ModernFixClient;
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
import org.embeddedt.modernfix.forge.config.NightConfigFixer;
import org.embeddedt.modernfix.screen.ModernFixConfigScreen;
@ -35,6 +37,7 @@ public class ModernFixClientForge {
public ModernFixClientForge() {
commonMod = new ModernFixClient();
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::keyBindRegister);
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onClientSetup);
ModLoadingContext.get().registerExtensionPoint(
ConfigScreenHandler.ConfigScreenFactory.class,
() -> new ConfigScreenHandler.ConfigScreenFactory((mc, screen) -> new ModernFixConfigScreen(screen))
@ -48,6 +51,15 @@ public class ModernFixClientForge {
event.register(configKey);
}
private void onClientSetup(FMLClientSetupEvent event) {
if(false && 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
public void onConfigKey(TickEvent.ClientTickEvent event) {
if(event.phase == TickEvent.Phase.START && configKey.consumeClick()) {

View File

@ -14,7 +14,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ObjectModificationManager.class)
@RequiresMod("blueprint")
public abstract class ObjectModificationManagerMixin extends SimpleJsonResourceReloadListener {
@Shadow protected SelectionSpace selectionSpace;
@Shadow(remap = false) protected SelectionSpace selectionSpace;
public ObjectModificationManagerMixin(Gson gson, String string) {
super(gson, string);

View File

@ -0,0 +1,56 @@
package org.embeddedt.modernfix.forge.mixin.bugfix.chunk_deadlock;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
import org.embeddedt.modernfix.ModernFix;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import java.lang.reflect.Field;
@Mixin(ChunkMap.class)
public abstract class ChunkMapLoadMixin {
@Shadow @Nullable protected abstract ChunkHolder getVisibleChunkIfPresent(long l);
private static final Field currentlyLoadingField = ObfuscationReflectionHelper.findField(ChunkHolder.class, "currentlyLoading");
private static void setCurrentlyLoading(ChunkHolder holder, LevelChunk value) {
try {
currentlyLoadingField.set(holder, value);
} catch(ReflectiveOperationException e) {
e.printStackTrace();
}
}
/**
* Set currentlyLoading before calling runPostLoad and restore its old value afterwards. We track the old value
* to avoid conflicting with Forge if/when this feature is added.
*/
@WrapOperation(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/LevelChunk;runPostLoad()V"))
private void setCurrentLoadingThenPostLoad(LevelChunk chunk, Operation<Void> operation) {
ChunkHolder holder = this.getVisibleChunkIfPresent(chunk.getPos().toLong());
if(holder != null) {
LevelChunk prevLoading = null;
try {
prevLoading = (LevelChunk)currentlyLoadingField.get(holder);
} catch(ReflectiveOperationException e) {
e.printStackTrace();
}
try {
setCurrentlyLoading(holder, chunk);
operation.call(chunk);
} finally {
setCurrentlyLoading(holder, prevLoading);
}
} else {
ModernFix.LOGGER.warn("Unable to find chunk holder for loading chunk");
operation.call(chunk);
}
}
}

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

@ -0,0 +1,24 @@
package org.embeddedt.modernfix.forge.mixin.bugfix.extra_experimental_screen;
import com.mojang.serialization.Lifecycle;
import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen;
import net.minecraft.world.level.storage.PrimaryLevelData;
import net.minecraft.world.level.storage.WorldData;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
@Mixin(CreateWorldScreen.class)
public class CreateWorldScreenMixin {
/**
* Fix experimental world dialog still being shown the first time you reopen a world that was created
* as experimental.
*/
@ModifyArg(method = "createNewWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/worldselection/WorldOpenFlows;createLevelFromExistingSettings(Lnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;Lnet/minecraft/server/ReloadableServerResources;Lnet/minecraft/core/LayeredRegistryAccess;Lnet/minecraft/world/level/storage/WorldData;)V"), index = 3)
private WorldData setExperimentalFlag(WorldData data) {
if(data instanceof PrimaryLevelData pld && data.worldGenSettingsLifecycle() != Lifecycle.stable()) {
pld.withConfirmedWarning(true);
}
return data;
}
}

View File

@ -10,7 +10,7 @@ import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(GameData.class)
public class GameDataMixin {
@Redirect(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/registries/ForgeRegistry;dump(Lnet/minecraft/resources/ResourceLocation;)V"))
@Redirect(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/registries/ForgeRegistry;dump(Lnet/minecraft/resources/ResourceLocation;)V", remap = false))
private static void noDump(ForgeRegistry<?> reg, ResourceLocation id) {
}
}

View File

@ -23,7 +23,7 @@ import java.util.Map;
@Mixin(ForgeItemModelShaper.class)
@ClientOnlyMixin
public abstract class ItemModelMesherForgeMixin extends ItemModelShaper {
@Shadow @Final @Mutable private Map<Holder.Reference<Item>, ModelResourceLocation> locations;
@Shadow(remap = false) @Final @Mutable private Map<Holder.Reference<Item>, ModelResourceLocation> locations;
private Map<Holder.Reference<Item>, ModelResourceLocation> overrideLocations;
@ -51,6 +51,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.
**/
@ -62,6 +63,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.
**/
@ -72,6 +74,7 @@ public abstract class ItemModelMesherForgeMixin extends ItemModelShaper {
}
/**
* @author embeddedt
* @reason Disable cache rebuilding (with dynamic loading, that would generate
* all models).
**/

View File

@ -63,6 +63,7 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
@Shadow @Final @Mutable private Map<ModelBakery.BakedCacheKey, BakedModel> bakedCache;
@Shadow @Final @Mutable private BlockColors blockColors;
@Shadow @Final private static Logger LOGGER;
@Shadow public abstract UnbakedModel getModel(ResourceLocation resourceLocation);

View File

@ -36,22 +36,27 @@ import java.util.function.Predicate;
@ClientOnlyMixin
public abstract class CTMPackReloadListenerMixin implements ModernFixClientIntegration {
/* caches the original render checks */
@Shadow @Final private static Map<Holder.Reference<Block>, Predicate<RenderType>> blockRenderChecks;
@Shadow(remap = false) @Final private static Map<Holder.Reference<Block>, Predicate<RenderType>> blockRenderChecks;
private static Map<Holder.Reference<Block>, Predicate<RenderType>> renderCheckOverrides = new ConcurrentHashMap<>();
private static ChunkRenderTypeSet DEFAULT_TYPE_SET = ChunkRenderTypeSet.of(RenderType.solid());
@Shadow protected abstract Predicate<RenderType> getLayerCheck(BlockState state, BakedModel model);
@Shadow(remap = false) protected abstract Predicate<RenderType> getLayerCheck(BlockState state, BakedModel model);
@Shadow protected abstract ChunkRenderTypeSet getExistingRenderCheck(Block block);
@Shadow(remap = false) protected abstract ChunkRenderTypeSet getExistingRenderCheck(Block block);
@Inject(method = "<init>", at = @At("RETURN"))
private void onInit(CallbackInfo ci) {
ModernFixClient.CLIENT_INTEGRATIONS.add(this);
}
/**
* @author embeddedt
* @reason handle layer changes dynamically
*/
@Overwrite(remap = false)
@SuppressWarnings("removal")
private void refreshLayerHacks() {
renderCheckOverrides.clear();
if(blockRenderChecks.isEmpty()) {

View File

@ -37,9 +37,9 @@ import java.util.function.Function;
@ClientOnlyMixin
public abstract class TextureMetadataHandlerMixin implements ModernFixClientIntegration {
@Shadow @Nonnull protected abstract BakedModel wrap(UnbakedModel model, BakedModel object) throws IOException;
@Shadow(remap = false) @Nonnull protected abstract BakedModel wrap(UnbakedModel model, BakedModel object) throws IOException;
@Shadow @Final public static Multimap<ResourceLocation, Material> TEXTURES_SCRAPED;
@Shadow(remap = false) @Final public static Multimap<ResourceLocation, Material> TEXTURES_SCRAPED;
@Inject(method = "<init>", at = @At("RETURN"))
private void subscribeDynamic(CallbackInfo ci) {

View File

@ -20,7 +20,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@RequiresMod("refinedstorage")
@ClientOnlyMixin
public class ClientSetupMixin {
@Shadow @Final private static BakedModelOverrideRegistry BAKED_MODEL_OVERRIDE_REGISTRY;
@Shadow(remap = false) @Final private static BakedModelOverrideRegistry BAKED_MODEL_OVERRIDE_REGISTRY;
@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

@ -17,13 +17,13 @@ import java.util.Map;
@Mixin(targets = { "net/minecraftforge/registries/NamespacedWrapper" })
public abstract class NamespacedHolderHelperMixin<T> extends MappedRegistry<T> {
@Shadow private Map<ResourceLocation, Holder.Reference<T>> holdersByName;
@Shadow(remap = false) private Map<ResourceLocation, Holder.Reference<T>> holdersByName;
public NamespacedHolderHelperMixin(ResourceKey<? extends Registry<T>> arg, Lifecycle lifecycle) {
super(arg, lifecycle);
}
@Inject(method = "freeze", at = @At(value = "FIELD", opcode = Opcodes.GETFIELD, target = "Lnet/minecraftforge/registries/NamespacedWrapper;holdersByName:Ljava/util/Map;"), cancellable = true)
@Inject(method = "freeze", at = @At(value = "FIELD", opcode = Opcodes.GETFIELD, target = "Lnet/minecraftforge/registries/NamespacedWrapper;holdersByName:Ljava/util/Map;", remap = false), cancellable = true)
private void fastDummyCheck(CallbackInfoReturnable<Registry<T>> cir) {
// Quickly iterate without making any streams, etc. to see if everything is fine
// Use the slow path (by returning without cancelling) when there is an error

View File

@ -28,12 +28,12 @@ import java.util.Set;
@Mixin(PathPackResources.class)
public abstract class ForgePathPackResourcesMixin implements ICachingResourcePack {
@Shadow protected abstract Path resolve(String... paths);
@Shadow(remap = false) protected abstract Path resolve(String... paths);
@Shadow @NotNull
@Shadow(remap = false) @NotNull
protected abstract Set<String> getNamespacesFromDisk(PackType type);
@Shadow private static String[] getPathFromLocation(PackType type, ResourceLocation location) {
@Shadow(remap = false) private static String[] getPathFromLocation(PackType type, ResourceLocation location) {
throw new AssertionError();
}

View File

@ -1,7 +1,6 @@
package org.embeddedt.modernfix.forge.mixin.perf.rewrite_registry;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.registries.ForgeRegistry;
import org.embeddedt.modernfix.annotation.IgnoreOutsideDev;
@ -14,14 +13,11 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Map;
import java.util.Set;
@Mixin(ForgeRegistry.Snapshot.class)
@IgnoreOutsideDev
public class ForgeRegistrySnapshotMixin {
@Shadow @Final @Mutable public Map<ResourceLocation, Integer> ids;
@Shadow @Final @Mutable public Set<ResourceLocation> dummied;
@Shadow(remap = false) @Final @Mutable public Map<ResourceLocation, Integer> ids;
/**
* The only good reason to use tree maps here is to keep the order the same. But we are tracking IDs
@ -30,6 +26,5 @@ public class ForgeRegistrySnapshotMixin {
@Inject(method = "<init>", at = @At("RETURN"))
private void replaceSnapshotMaps(CallbackInfo ci) {
this.ids = new Object2ObjectOpenHashMap<>();
this.dummied = new ObjectOpenHashSet<>();
}
}

View File

@ -19,7 +19,7 @@ public class TagEntryMixin {
* @reason use cached location, overwrite rather than inject to avoid allocs
*/
@Overwrite
public ExtraCodecs.TagOrElementLocation elementOrTag() {
private ExtraCodecs.TagOrElementLocation elementOrTag() {
ExtraCodecs.TagOrElementLocation loc = cachedLoc;
if(loc == null) {
loc = new ExtraCodecs.TagOrElementLocation(this.id, this.tag);

View File

@ -18,7 +18,7 @@ public class TagOrElementLocationMixin {
* @reason use cached ID, overwrite rather than inject to avoid allocs
*/
@Overwrite
public String decoratedId() {
private String decoratedId() {
String id = cachedDecoratedId;
if(id == null) {
id = this.tag ? "#" + this.id : this.id.toString();

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=23w32a
@ -25,3 +26,5 @@ modmenu_version=7.0.0-beta.2
diagonal_fences_version=4558828
spark_version=4587310
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'