diff --git a/build.gradle b/build.gradle index b52f1ef9..e9d3f8dd 100644 --- a/build.gradle +++ b/build.gradle @@ -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") } } } diff --git a/common/build.gradle b/common/build.gradle index 86fd3cb4..33cd3594 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -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) { diff --git a/common/src/main/java/org/embeddedt/modernfix/ModernFixClient.java b/common/src/main/java/org/embeddedt/modernfix/ModernFixClient.java index ef2ed28e..b8a1f42a 100644 --- a/common/src/main/java/org/embeddedt/modernfix/ModernFixClient.java +++ b/common/src/main/java/org/embeddedt/modernfix/ModernFixClient.java @@ -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; diff --git a/common/src/main/java/org/embeddedt/modernfix/chunk/SafeBlockGetter.java b/common/src/main/java/org/embeddedt/modernfix/chunk/SafeBlockGetter.java new file mode 100644 index 00000000..3d4efe52 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/chunk/SafeBlockGetter.java @@ -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); + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/chunk_deadlock/BlockStateBaseMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/chunk_deadlock/BlockStateBaseMixin.java new file mode 100644 index 00000000..464130c8 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/chunk_deadlock/BlockStateBaseMixin.java @@ -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; + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/chunk_deadlock/ServerLevelMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/chunk_deadlock/ServerLevelMixin.java new file mode 100644 index 00000000..e7c3b137 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/chunk_deadlock/ServerLevelMixin.java @@ -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; + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/world_leaks/MinecraftMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/world_leaks/MinecraftMixin.java new file mode 100644 index 00000000..e4f0fa57 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/world_leaks/MinecraftMixin.java @@ -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 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); + } + } + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/world_screen_skipped/WorldSelectionListMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/world_screen_skipped/WorldSelectionListMixin.java new file mode 100644 index 00000000..27f67f7a --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/bugfix/world_screen_skipped/WorldSelectionListMixin.java @@ -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(); + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/devenv/MinecraftMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/devenv/MinecraftMixin.java index b5e9cfec..082b60a9 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/devenv/MinecraftMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/devenv/MinecraftMixin.java @@ -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; diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/compact_mojang_registries/DirectObjectMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/compact_mojang_registries/DirectObjectMixin.java new file mode 100644 index 00000000..02d346d2 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/compact_mojang_registries/DirectObjectMixin.java @@ -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; + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/compact_mojang_registries/MappedRegistryMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/compact_mojang_registries/MappedRegistryMixin.java new file mode 100644 index 00000000..f6e86477 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/compact_mojang_registries/MappedRegistryMixin.java @@ -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 { + @Shadow + @Final + @Mutable + private Map lifecycles; + + private static final ImmutableSet MFIX$NEW_STORAGE_KEYS = ImmutableSet.of(new ResourceLocation("block"), new ResourceLocation("item")); + + @Inject(method = "(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) RegistryStorage.createStorage(); + this.keyStorage = (BiMap, T>)RegistryStorage.createKeyStorage(this.key(), (BiMap)this.storage); + } + */ + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_block_codecs/StateHolderMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_block_codecs/StateHolderMixin.java index 7455fcbb..540c7495 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_block_codecs/StateHolderMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_block_codecs/StateHolderMixin.java @@ -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 > Codec obtainCodec(Codec codec, String typeKey, Function type, Function> codecFn, Codec codecMethodArg, Function stateSupplier) { return codec.dispatch(typeKey, type, block -> { if(block instanceof Block) { diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/BlockElementFaceDeserializerMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/BlockElementFaceDeserializerMixin.java index 2cd4a853..17404372 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/BlockElementFaceDeserializerMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/BlockElementFaceDeserializerMixin.java @@ -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); } diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/ItemModelShaperMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/ItemModelShaperMixin.java index 8739d0e3..398ac679 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/ItemModelShaperMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/ItemModelShaperMixin.java @@ -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). **/ diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/mojang_registry_size/MappedRegistryMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/mojang_registry_size/MappedRegistryMixin.java index d29fc4fa..ae4faf59 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/mojang_registry_size/MappedRegistryMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/mojang_registry_size/MappedRegistryMixin.java @@ -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()) { diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/mojang_registry_size/StateHolderMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/mojang_registry_size/StateHolderMixin.java new file mode 100644 index 00000000..9e67dd7b --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/mojang_registry_size/StateHolderMixin.java @@ -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, 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(); + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/reduce_blockstate_cache_rebuilds/BlockStateBaseMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/reduce_blockstate_cache_rebuilds/BlockStateBaseMixin.java index 3d3b3acc..2c209d88 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/reduce_blockstate_cache_rebuilds/BlockStateBaseMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/reduce_blockstate_cache_rebuilds/BlockStateBaseMixin.java @@ -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 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 } } - return this.cache; } @Redirect(method = "*", at = @At( @@ -77,7 +73,8 @@ public abstract class BlockStateBaseMixin extends StateHolder 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 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); - } } diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/remove_biome_temperature_cache/BiomeMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/remove_biome_temperature_cache/BiomeMixin.java index 03b45602..0091ac98 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/remove_biome_temperature_cache/BiomeMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/remove_biome_temperature_cache/BiomeMixin.java @@ -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); } } diff --git a/common/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java b/common/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java index 6c902036..c61492a3 100644 --- a/common/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java +++ b/common/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java @@ -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 initCacheMethodNames = ImmutableSet.of("m_60611_", "func_215692_c", "method_26200", "initCache"); + Set whitelistedInjections = ImmutableSet.of( + "getFluidState", "method_26227", "m_60819_", "func_204520_s" + ); + Map injectorMethodNames = new HashMap<>(); + Map injectorMixinSource = new HashMap<>(); + String descriptor = Type.getDescriptor(MixinMerged.class); + for(MethodNode m : targetClass.methods) { + if((m.access & Opcodes.ACC_STATIC) != 0) + continue; + Set 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 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 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); + } + }); + } } \ No newline at end of file diff --git a/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java b/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java index bb97dc39..6377955f 100644 --- a/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -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; diff --git a/common/src/main/java/org/embeddedt/modernfix/duck/ISafeBlockGetter.java b/common/src/main/java/org/embeddedt/modernfix/duck/ISafeBlockGetter.java new file mode 100644 index 00000000..3fb462ef --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/duck/ISafeBlockGetter.java @@ -0,0 +1,7 @@ +package org.embeddedt.modernfix.duck; + +import org.embeddedt.modernfix.chunk.SafeBlockGetter; + +public interface ISafeBlockGetter { + SafeBlockGetter mfix$getSafeBlockGetter(); +} diff --git a/common/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java b/common/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java index 06b6a697..d35ba62e 100644 --- a/common/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java +++ b/common/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java @@ -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 { + /** + * 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 BAKE_SKIPPED_TOPLEVEL = ImmutableSet.builder() + .add(new ResourceLocation("custommachinery", "block/custom_machine_block")) + .build(); public static DynamicBakedModelProvider currentInstance = null; private final ModelBakery bakery; private final Map bakedCache; @@ -138,7 +148,10 @@ public class DynamicBakedModelProvider implements Map implements BiMap { + private final Function keyGetter; + private final BiConsumer keySetter; + private final Map forwardMap; + + public DirectStorageBiMap(Function keyGetter, BiConsumer 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 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 keySet() { + return this.forwardMap.keySet(); + } + + @Override + public Set values() { + return new HashSet<>(this.forwardMap.values()); + } + + @NotNull + @Override + public Set> entrySet() { + return this.forwardMap.entrySet(); + } + + @Override + public BiMap inverse() { + return new Reverse(); + } + + class Reverse implements BiMap { + @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 map) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public Set keySet() { + return DirectStorageBiMap.this.values(); + } + + @Override + public Set values() { + return DirectStorageBiMap.this.keySet(); + } + + @NotNull + @Override + public Set> entrySet() { + return DirectStorageBiMap.this.entrySet().stream() + .map(entry -> new AbstractMap.SimpleImmutableEntry<>(entry.getValue(), entry.getKey())) + .collect(Collectors.toSet()); + } + + @Override + public BiMap inverse() { + return DirectStorageBiMap.this; + } + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/registry/DirectStorageRegistryObject.java b/common/src/main/java/org/embeddedt/modernfix/registry/DirectStorageRegistryObject.java new file mode 100644 index 00000000..c18ac699 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/registry/DirectStorageRegistryObject.java @@ -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); +} diff --git a/common/src/main/java/org/embeddedt/modernfix/registry/LifecycleMap.java b/common/src/main/java/org/embeddedt/modernfix/registry/LifecycleMap.java new file mode 100644 index 00000000..76285abd --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/registry/LifecycleMap.java @@ -0,0 +1,20 @@ +package org.embeddedt.modernfix.registry; + +import com.mojang.serialization.Lifecycle; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; + +public class LifecycleMap extends Reference2ReferenceOpenHashMap { + 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; + } + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/registry/RegistryStorage.java b/common/src/main/java/org/embeddedt/modernfix/registry/RegistryStorage.java new file mode 100644 index 00000000..252676d2 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/registry/RegistryStorage.java @@ -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 createStorage() { + return new DirectStorageBiMap<>(DirectStorageRegistryObject::mfix$getResourceKey, DirectStorageRegistryObject::mfix$setResourceKey); + } + + public static BiMap, DirectStorageRegistryObject> createKeyStorage(ResourceKey> registryKey, BiMap storage) { + if(storage instanceof DirectStorageBiMap) { + DirectStorageBiMap directStorageBiMap = (DirectStorageBiMap)storage; + // silently ignore put/putAll calls on this map + return new TransformingBiMap, DirectStorageRegistryObject>(directStorageBiMap, loc -> ResourceKey.create(registryKey, loc), ResourceKey::location, Function.identity(), Function.identity()) { + @Override + public DirectStorageRegistryObject put(ResourceKey key, DirectStorageRegistryObject value) { + return null; + } + + @Override + public void putAll(Map, ? extends DirectStorageRegistryObject> map) { + + } + }; + } else + throw new UnsupportedOperationException(); + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/registry/TransformingBiMap.java b/common/src/main/java/org/embeddedt/modernfix/registry/TransformingBiMap.java new file mode 100644 index 00000000..2fb1beb1 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/registry/TransformingBiMap.java @@ -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 implements BiMap { + private final BiMap delegate; + private final Function keyFwd; + private final Function keyBack; + private final Function valueFwd; + private final Function valueBack; + + public TransformingBiMap(BiMap map, Function keyFwd, Function keyBack, Function valueFwd, Function 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 map) { + map.forEach((key, value) -> { + this.delegate.put(keyBack(key), valueBack(value)); + }); + } + + @Override + public void clear() { + this.delegate.clear(); + } + + @NotNull + @Override + public Set keySet() { + return new TransformingSet<>(this.delegate.keySet(), this.keyFwd, this.keyBack); + } + + @Override + public Set values() { + return new TransformingSet<>(this.delegate.values(), this.valueFwd, this.valueBack); + } + + @NotNull + @Override + public Set> 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 inverse() { + return new TransformingBiMap<>(this.delegate.inverse(), this.valueFwd, this.valueBack, this.keyFwd, this.keyBack); + } + + static class TransformingSet implements Set { + private final Set delegate; + private final Function forward; + private final Function reverse; + + public TransformingSet(Set set, Function forward, Function 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 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[] 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 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 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(); + } + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/util/CanonizingStringMap.java b/common/src/main/java/org/embeddedt/modernfix/util/CanonizingStringMap.java index 845fbb1e..015e74a4 100644 --- a/common/src/main/java/org/embeddedt/modernfix/util/CanonizingStringMap.java +++ b/common/src/main/java/org/embeddedt/modernfix/util/CanonizingStringMap.java @@ -12,7 +12,7 @@ import java.util.Map; * Replacement backing map for CompoundTags that interns keys. */ public class CanonizingStringMap extends HashMap { - private static final Interner KEY_INTERNER = Interners.newStrongInterner(); + private static final Interner KEY_INTERNER = Interners.newWeakInterner(); private static String intern(String key) { return key != null ? KEY_INTERNER.intern(key) : null; diff --git a/common/src/main/java/org/embeddedt/modernfix/world/ThreadDumper.java b/common/src/main/java/org/embeddedt/modernfix/world/ThreadDumper.java index bd57cb0e..a412d009 100644 --- a/common/src/main/java/org/embeddedt/modernfix/world/ThreadDumper.java +++ b/common/src/main/java/org/embeddedt/modernfix/world/ThreadDumper.java @@ -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]); diff --git a/common/src/main/resources/assets/modernfix/lang/en_us.json b/common/src/main/resources/assets/modernfix/lang/en_us.json index 5f32b072..3afbc788 100644 --- a/common/src/main/resources/assets/modernfix/lang/en_us.json +++ b/common/src/main/resources/assets/modernfix/lang/en_us.json @@ -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", diff --git a/common/src/main/resources/assets/modernfix/lang/ja_jp.json b/common/src/main/resources/assets/modernfix/lang/ja_jp.json new file mode 100644 index 00000000..81103546 --- /dev/null +++ b/common/src/main/resources/assets/modernfix/lang/ja_jp.json @@ -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を保存しますが、特殊文字がレンダリングされなくなります" +} diff --git a/common/src/main/resources/assets/modernfix/lang/zh_cn.json b/common/src/main/resources/assets/modernfix/lang/zh_cn.json index 4d3b776e..f0686d62 100644 --- a/common/src/main/resources/assets/modernfix/lang/zh_cn.json +++ b/common/src/main/resources/assets/modernfix/lang/zh_cn.json @@ -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": "已禁用", diff --git a/common/src/main/resources/modernfix-common.mixins.json b/common/src/main/resources/modernfix-common.mixins.json index 912d7b5c..437491fb 100644 --- a/common/src/main/resources/modernfix-common.mixins.json +++ b/common/src/main/resources/modernfix-common.mixins.json @@ -9,5 +9,8 @@ ${mixin_classes} ], "injectors": { "defaultRequire": 1 + }, + "overwrites": { + "conformVisibility": true } } diff --git a/common/src/main/resources/modernfix.accesswidener b/common/src/main/resources/modernfix.accesswidener index 35f1affc..3d6d358a 100644 --- a/common/src/main/resources/modernfix.accesswidener +++ b/common/src/main/resources/modernfix.accesswidener @@ -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 (Ljava/util/Map;)V diff --git a/fabric/build.gradle b/fabric/build.gradle index b2bd2ab9..b772ea75 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -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}" diff --git a/fabric/src/main/java/org/embeddedt/modernfix/ModernFixPreLaunchFabric.java b/fabric/src/main/java/org/embeddedt/modernfix/ModernFixPreLaunchFabric.java index 92352b52..596237c3 100644 --- a/fabric/src/main/java/org/embeddedt/modernfix/ModernFixPreLaunchFabric.java +++ b/fabric/src/main/java/org/embeddedt/modernfix/ModernFixPreLaunchFabric.java @@ -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"); + } } } diff --git a/fabric/src/main/java/org/embeddedt/modernfix/fabric/api/dynresources/ModelScanController.java b/fabric/src/main/java/org/embeddedt/modernfix/fabric/api/dynresources/ModelScanController.java new file mode 100644 index 00000000..770efcf8 --- /dev/null +++ b/fabric/src/main/java/org/embeddedt/modernfix/fabric/api/dynresources/ModelScanController.java @@ -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> SCAN_PREDICATES = new ArrayList<>(); + public static boolean shouldScanAndTestWrapping(ResourceLocation location) { + if(SCAN_PREDICATES.size() > 0) { + for(Predicate predicate : SCAN_PREDICATES) { + if(!predicate.test(location)) + return false; + } + } + return true; + } +} diff --git a/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/faster_command_suggestions/SuggestionsBuilderMixin.java b/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/faster_command_suggestions/SuggestionsBuilderMixin.java new file mode 100644 index 00000000..435508d2 --- /dev/null +++ b/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/faster_command_suggestions/SuggestionsBuilderMixin.java @@ -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 result; + + @Redirect(method = "*", at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z"), require = 0) + private boolean addIfFits(List list, T entry) { + if(list != result || list.size() < MAX_SUGGESTIONS) { + return list.add(entry); + } + return false; + } +} diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 58fc2335..af11380f 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -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": [ diff --git a/fabric/src/main/resources/modernfix-fabric.mixins.json b/fabric/src/main/resources/modernfix-fabric.mixins.json index c6246634..3d94ea0d 100644 --- a/fabric/src/main/resources/modernfix-fabric.mixins.json +++ b/fabric/src/main/resources/modernfix-fabric.mixins.json @@ -9,5 +9,8 @@ ${mixin_classes} ], "injectors": { "defaultRequire": 1 + }, + "overwrites": { + "conformVisibility": true } } \ No newline at end of file diff --git a/fabric/testmod/build.gradle b/fabric/testmod/build.gradle new file mode 100644 index 00000000..228b8a61 --- /dev/null +++ b/fabric/testmod/build.gradle @@ -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 + } +} + diff --git a/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/TestBlock.java b/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/TestBlock.java new file mode 100644 index 00000000..c10b8c09 --- /dev/null +++ b/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/TestBlock.java @@ -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); + } +} diff --git a/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/TestBlockItem.java b/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/TestBlockItem.java new file mode 100644 index 00000000..f5cbe047 --- /dev/null +++ b/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/TestBlockItem.java @@ -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); + } +} diff --git a/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/TestMod.java b/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/TestMod.java new file mode 100644 index 00000000..81ef4c3d --- /dev/null +++ b/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/TestMod.java @@ -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 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; + } +} diff --git a/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/client/TestModBlockModel.java b/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/client/TestModBlockModel.java new file mode 100644 index 00000000..43ccf4d6 --- /dev/null +++ b/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/client/TestModBlockModel.java @@ -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 randomSupplier, RenderContext context) { + context.meshConsumer().accept(mesh); + } + + @Override + public void emitItemQuads(ItemStack stack, Supplier randomSupplier, RenderContext context) { + context.meshConsumer().accept(mesh); + } + + @Override + public List 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 getDependencies() { + return Collections.emptyList(); + } + + @Override + public void resolveParents(Function function) { + + } + + private static int scaleColor(int c) { + return c * 255 / TestMod.MAX_COLOR; + } + + @Nullable + @Override + public BakedModel bake(ModelBaker baker, Function 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; + } +} diff --git a/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/client/TestModClient.java b/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/client/TestModClient.java new file mode 100644 index 00000000..7f82a508 --- /dev/null +++ b/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/client/TestModClient.java @@ -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; + }); + } +} diff --git a/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/mixin/ChunkMixin.java b/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/mixin/ChunkMixin.java new file mode 100644 index 00000000..142faf51 --- /dev/null +++ b/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/mixin/ChunkMixin.java @@ -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 cir) { + BlockState overrideState = TestMod.getColorCubeStateFor(pos.getX(), pos.getY(), pos.getZ()); + if(overrideState != null) { + cir.setReturnValue(overrideState); + } + } +} diff --git a/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/mixin/DebugLevelSourceMixin.java b/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/mixin/DebugLevelSourceMixin.java new file mode 100644 index 00000000..46f52d31 --- /dev/null +++ b/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/mixin/DebugLevelSourceMixin.java @@ -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); + } + } + } + } + } +} diff --git a/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/mixin/RenderChunkMixin.java b/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/mixin/RenderChunkMixin.java new file mode 100644 index 00000000..eaa6d1d2 --- /dev/null +++ b/fabric/testmod/src/main/java/org/embeddedt/modernfix/testmod/mixin/RenderChunkMixin.java @@ -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 cir) { + if(this.debug) { + cir.setReturnValue(TestMod.getColorCubeStateFor(pos.getX(), pos.getY(), pos.getZ())); + } + } +} diff --git a/fabric/testmod/src/main/resources/assets/mfix_testmod/textures/block/base_wool.png b/fabric/testmod/src/main/resources/assets/mfix_testmod/textures/block/base_wool.png new file mode 100644 index 00000000..39d6ec76 Binary files /dev/null and b/fabric/testmod/src/main/resources/assets/mfix_testmod/textures/block/base_wool.png differ diff --git a/fabric/testmod/src/main/resources/fabric.mod.json b/fabric/testmod/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..6ebb4d54 --- /dev/null +++ b/fabric/testmod/src/main/resources/fabric.mod.json @@ -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" + ] + } +} \ No newline at end of file diff --git a/fabric/testmod/src/main/resources/pack.mcmeta b/fabric/testmod/src/main/resources/pack.mcmeta new file mode 100644 index 00000000..68df7ebf --- /dev/null +++ b/fabric/testmod/src/main/resources/pack.mcmeta @@ -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." + } +} diff --git a/fabric/testmod/src/main/resources/testmod.mixins.json b/fabric/testmod/src/main/resources/testmod.mixins.json new file mode 100644 index 00000000..46222aa3 --- /dev/null +++ b/fabric/testmod/src/main/resources/testmod.mixins.json @@ -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 + } +} \ No newline at end of file diff --git a/forge/build.gradle b/forge/build.gradle index dffacfb1..dcf23917 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -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 } } diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java b/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java index f412dae2..9a9d5dd5 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/config/NightConfigFixer.java @@ -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 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 { @@ -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); } } diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixClientForge.java b/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixClientForge.java index af34719a..1695d588 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixClientForge.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/init/ModernFixClientForge.java @@ -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()) { diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/blueprint_modif_memory_leak/ObjectModificationManagerMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/blueprint_modif_memory_leak/ObjectModificationManagerMixin.java index 1c5f1961..2be15e95 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/blueprint_modif_memory_leak/ObjectModificationManagerMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/blueprint_modif_memory_leak/ObjectModificationManagerMixin.java @@ -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); diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/chunk_deadlock/ChunkMapLoadMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/chunk_deadlock/ChunkMapLoadMixin.java new file mode 100644 index 00000000..cfed9788 --- /dev/null +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/chunk_deadlock/ChunkMapLoadMixin.java @@ -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 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); + } + } +} diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/ctm_resourceutil_cme/ResourceUtilMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/ctm_resourceutil_cme/ResourceUtilMixin.java index 7e5e41b2..26844d70 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/ctm_resourceutil_cme/ResourceUtilMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/ctm_resourceutil_cme/ResourceUtilMixin.java @@ -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; /** diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/extra_experimental_screen/CreateWorldScreenMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/extra_experimental_screen/CreateWorldScreenMixin.java new file mode 100644 index 00000000..f83a8e11 --- /dev/null +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/bugfix/extra_experimental_screen/CreateWorldScreenMixin.java @@ -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; + } +} diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/devenv/GameDataMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/devenv/GameDataMixin.java index c9ab0be9..5602da08 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/devenv/GameDataMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/devenv/GameDataMixin.java @@ -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) { } } diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ItemModelMesherForgeMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ItemModelMesherForgeMixin.java index c8ebb87a..55a4e3e2 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ItemModelMesherForgeMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ItemModelMesherForgeMixin.java @@ -23,7 +23,7 @@ import java.util.Map; @Mixin(ForgeItemModelShaper.class) @ClientOnlyMixin public abstract class ItemModelMesherForgeMixin extends ItemModelShaper { - @Shadow @Final @Mutable private Map, ModelResourceLocation> locations; + @Shadow(remap = false) @Final @Mutable private Map, ModelResourceLocation> locations; private Map, 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). **/ diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ModelBakeryMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ModelBakeryMixin.java index adf3c186..b12fbefa 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ModelBakeryMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ModelBakeryMixin.java @@ -63,6 +63,7 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery { @Shadow @Final @Mutable private Map bakedCache; @Shadow @Final @Mutable private BlockColors blockColors; + @Shadow @Final private static Logger LOGGER; @Shadow public abstract UnbakedModel getModel(ResourceLocation resourceLocation); diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ctm/CTMPackReloadListenerMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ctm/CTMPackReloadListenerMixin.java index 02928406..478ce77f 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ctm/CTMPackReloadListenerMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ctm/CTMPackReloadListenerMixin.java @@ -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, Predicate> blockRenderChecks; + @Shadow(remap = false) @Final private static Map, Predicate> blockRenderChecks; private static Map, Predicate> renderCheckOverrides = new ConcurrentHashMap<>(); private static ChunkRenderTypeSet DEFAULT_TYPE_SET = ChunkRenderTypeSet.of(RenderType.solid()); - @Shadow protected abstract Predicate getLayerCheck(BlockState state, BakedModel model); + @Shadow(remap = false) protected abstract Predicate getLayerCheck(BlockState state, BakedModel model); - @Shadow protected abstract ChunkRenderTypeSet getExistingRenderCheck(Block block); + @Shadow(remap = false) protected abstract ChunkRenderTypeSet getExistingRenderCheck(Block block); @Inject(method = "", 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()) { diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ctm/TextureMetadataHandlerMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ctm/TextureMetadataHandlerMixin.java index 34e740a5..4af90c43 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ctm/TextureMetadataHandlerMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ctm/TextureMetadataHandlerMixin.java @@ -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 TEXTURES_SCRAPED; + @Shadow(remap = false) @Final public static Multimap TEXTURES_SCRAPED; @Inject(method = "", at = @At("RETURN")) private void subscribeDynamic(CallbackInfo ci) { diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/rs/ClientSetupMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/rs/ClientSetupMixin.java index f7674d06..873c0ec7 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/rs/ClientSetupMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/rs/ClientSetupMixin.java @@ -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 = "", at = @At("RETURN")) private void addDynamicListener(CallbackInfo ci) { diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/supermartijncore/ClientRegistrationHandlerMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/supermartijncore/ClientRegistrationHandlerMixin.java index e4cc3781..12e235a9 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/supermartijncore/ClientRegistrationHandlerMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/supermartijncore/ClientRegistrationHandlerMixin.java @@ -31,7 +31,7 @@ import java.util.stream.Stream; @RequiresMod("supermartijn642corelib") @ClientOnlyMixin public class ClientRegistrationHandlerMixin { - @Shadow @Final private List>, Function>> modelOverwrites; + @Shadow(remap = false) @Final private List>, Function>> modelOverwrites; private Map> modelOverwritesByLocation = new Object2ObjectOpenHashMap<>(); diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/fast_forge_dummies/NamespacedHolderHelperMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/fast_forge_dummies/NamespacedHolderHelperMixin.java index 9b0b9e31..ef19734d 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/fast_forge_dummies/NamespacedHolderHelperMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/fast_forge_dummies/NamespacedHolderHelperMixin.java @@ -17,13 +17,13 @@ import java.util.Map; @Mixin(targets = { "net/minecraftforge/registries/NamespacedWrapper" }) public abstract class NamespacedHolderHelperMixin extends MappedRegistry { - @Shadow private Map> holdersByName; + @Shadow(remap = false) private Map> holdersByName; public NamespacedHolderHelperMixin(ResourceKey> 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> 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 diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/resourcepacks/ForgePathPackResourcesMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/resourcepacks/ForgePathPackResourcesMixin.java index 5f4c368f..c79ca8d5 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/resourcepacks/ForgePathPackResourcesMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/resourcepacks/ForgePathPackResourcesMixin.java @@ -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 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(); } diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/rewrite_registry/ForgeRegistrySnapshotMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/rewrite_registry/ForgeRegistrySnapshotMixin.java index a538a112..2412cbf0 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/rewrite_registry/ForgeRegistrySnapshotMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/rewrite_registry/ForgeRegistrySnapshotMixin.java @@ -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 ids; - - @Shadow @Final @Mutable public Set dummied; + @Shadow(remap = false) @Final @Mutable public Map 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 = "", at = @At("RETURN")) private void replaceSnapshotMaps(CallbackInfo ci) { this.ids = new Object2ObjectOpenHashMap<>(); - this.dummied = new ObjectOpenHashSet<>(); } } diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/tag_id_caching/TagEntryMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/tag_id_caching/TagEntryMixin.java index ac21c7a0..8bdc74b8 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/tag_id_caching/TagEntryMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/tag_id_caching/TagEntryMixin.java @@ -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); diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/tag_id_caching/TagOrElementLocationMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/tag_id_caching/TagOrElementLocationMixin.java index 5d91aa4c..43575e93 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/tag_id_caching/TagOrElementLocationMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/tag_id_caching/TagOrElementLocationMixin.java @@ -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(); diff --git a/forge/src/main/resources/modernfix-forge.mixins.json b/forge/src/main/resources/modernfix-forge.mixins.json index cd76ed5b..f63f1dfc 100644 --- a/forge/src/main/resources/modernfix-forge.mixins.json +++ b/forge/src/main/resources/modernfix-forge.mixins.json @@ -9,5 +9,8 @@ ${mixin_classes} ], "injectors": { "defaultRequire": 1 + }, + "overwrites": { + "conformVisibility": true } } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index cbb1b8ff..8d90fa48 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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 diff --git a/settings.gradle b/settings.gradle index 739e778f..c5b39d7c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -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'