Merge remote-tracking branch 'origin/1.19.2' into 1.19.4
This commit is contained in:
commit
351e4d3fc6
|
|
@ -105,6 +105,7 @@ allprojects {
|
||||||
maven {
|
maven {
|
||||||
url 'https://maven.terraformersmc.com/releases'
|
url 'https://maven.terraformersmc.com/releases'
|
||||||
}
|
}
|
||||||
|
maven { url = "https://jitpack.io" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,6 +218,7 @@ configure(subprojects.findAll {it.name == "forge" || it.name == "fabric"}) {
|
||||||
client {
|
client {
|
||||||
vmArgs "-Xmx1G"
|
vmArgs "-Xmx1G"
|
||||||
vmArgs "-Xms1G"
|
vmArgs "-Xms1G"
|
||||||
|
property("mixin.debug.export", "true")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ dependencies {
|
||||||
// We depend on fabric loader here to use the fabric @Environment annotations and get the mixin 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
|
// Do NOT use other classes from fabric loader
|
||||||
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
||||||
|
implementation(annotationProcessor("com.github.llamalad7.mixinextras:mixinextras-common:${rootProject.mixinextras_version}"))
|
||||||
|
|
||||||
modCompileOnly("dev.latvian.mods:kubejs:${kubejs_version}") {
|
modCompileOnly("dev.latvian.mods:kubejs:${kubejs_version}") {
|
||||||
transitive = false
|
transitive = false
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,13 +11,10 @@ import net.minecraft.world.level.material.FluidState;
|
||||||
import net.minecraft.world.level.material.Fluids;
|
import net.minecraft.world.level.material.Fluids;
|
||||||
import org.embeddedt.modernfix.duck.IBlockState;
|
import org.embeddedt.modernfix.duck.IBlockState;
|
||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
import org.spongepowered.asm.mixin.Dynamic;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
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.Redirect;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
|
|
||||||
|
|
||||||
@Mixin(BlockBehaviour.BlockStateBase.class)
|
@Mixin(BlockBehaviour.BlockStateBase.class)
|
||||||
|
|
@ -48,7 +45,7 @@ public abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState>
|
||||||
return cacheInvalid;
|
return cacheInvalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BlockBehaviour.BlockStateBase.Cache generateCache(BlockBehaviour.BlockStateBase base) {
|
private void mfix$generateCache() {
|
||||||
if(cacheInvalid) {
|
if(cacheInvalid) {
|
||||||
// Ensure that only one block's cache is built at a time
|
// Ensure that only one block's cache is built at a time
|
||||||
synchronized (BlockBehaviour.BlockStateBase.class) {
|
synchronized (BlockBehaviour.BlockStateBase.class) {
|
||||||
|
|
@ -67,7 +64,6 @@ public abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState>
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.cache;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Redirect(method = "*", at = @At(
|
@Redirect(method = "*", at = @At(
|
||||||
|
|
@ -77,7 +73,8 @@ public abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState>
|
||||||
ordinal = 0
|
ordinal = 0
|
||||||
))
|
))
|
||||||
private BlockBehaviour.BlockStateBase.Cache dynamicCacheGen(BlockBehaviour.BlockStateBase base) {
|
private BlockBehaviour.BlockStateBase.Cache dynamicCacheGen(BlockBehaviour.BlockStateBase base) {
|
||||||
return generateCache(base);
|
mfix$generateCache();
|
||||||
|
return this.cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Redirect(method = "*", at = @At(
|
@Redirect(method = "*", at = @At(
|
||||||
|
|
@ -106,22 +103,4 @@ public abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState>
|
||||||
return this.owner.isRandomlyTicking(this.asState());
|
return this.owner.isRandomlyTicking(this.asState());
|
||||||
return this.isRandomlyTicking;
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,18 @@
|
||||||
package org.embeddedt.modernfix.core;
|
package org.embeddedt.modernfix.core;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.embeddedt.modernfix.core.config.ModernFixEarlyConfig;
|
import org.embeddedt.modernfix.core.config.ModernFixEarlyConfig;
|
||||||
import org.embeddedt.modernfix.core.config.Option;
|
import org.embeddedt.modernfix.core.config.Option;
|
||||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||||
import org.embeddedt.modernfix.world.ThreadDumper;
|
import org.embeddedt.modernfix.world.ThreadDumper;
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
import org.objectweb.asm.Type;
|
||||||
import org.objectweb.asm.tree.*;
|
import org.objectweb.asm.tree.*;
|
||||||
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
|
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
|
||||||
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
||||||
|
import org.spongepowered.asm.mixin.transformer.meta.MixinMerged;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
@ -146,6 +150,111 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
|
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);
|
ModernFixPlatformHooks.INSTANCE.applyASMTransformers(mixinClassName, targetClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applyBlockStateCacheScan(ClassNode targetClass) {
|
||||||
|
Set<String> initCacheMethodNames = ImmutableSet.of("m_60611_", "func_215692_c", "method_26200", "initCache");
|
||||||
|
Set<String> whitelistedInjections = ImmutableSet.of(
|
||||||
|
"getFluidState", "method_26227", "m_60819_", "func_204520_s"
|
||||||
|
);
|
||||||
|
Map<String, MethodNode> injectorMethodNames = new HashMap<>();
|
||||||
|
Map<String, String> injectorMixinSource = new HashMap<>();
|
||||||
|
String descriptor = Type.getDescriptor(MixinMerged.class);
|
||||||
|
for(MethodNode m : targetClass.methods) {
|
||||||
|
if((m.access & Opcodes.ACC_STATIC) != 0)
|
||||||
|
continue;
|
||||||
|
Set<AnnotationNode> seenNodes = new HashSet<>();
|
||||||
|
if(m.invisibleAnnotations != null) {
|
||||||
|
for(AnnotationNode ann : m.invisibleAnnotations) {
|
||||||
|
if(ann.desc.equals(descriptor)) {
|
||||||
|
seenNodes.add(ann);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(m.visibleAnnotations != null) {
|
||||||
|
for(AnnotationNode ann : m.visibleAnnotations) {
|
||||||
|
if(ann.desc.equals(descriptor)) {
|
||||||
|
seenNodes.add(ann);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(seenNodes.size() > 0) {
|
||||||
|
injectorMethodNames.put(m.name, m);
|
||||||
|
for(AnnotationNode node : seenNodes) {
|
||||||
|
for(int i = 0; i < node.values.size(); i += 2) {
|
||||||
|
if(Objects.equals(node.values.get(i), "mixin")) {
|
||||||
|
injectorMixinSource.put(m.name, (String)node.values.get(i + 1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Set<String> cacheCalledInjectors = new HashSet<>();
|
||||||
|
// Search for initCache in the class
|
||||||
|
for(MethodNode m : targetClass.methods) {
|
||||||
|
if((m.access & Opcodes.ACC_STATIC) != 0)
|
||||||
|
continue;
|
||||||
|
if(initCacheMethodNames.contains(m.name)) {
|
||||||
|
// This is it. Check for any injectors it calls
|
||||||
|
for(AbstractInsnNode n : m.instructions) {
|
||||||
|
if(n instanceof MethodInsnNode) {
|
||||||
|
MethodInsnNode invoke = (MethodInsnNode)n;
|
||||||
|
if(((MethodInsnNode)n).owner.equals(targetClass.name) && injectorMethodNames.containsKey(((MethodInsnNode)n).name)) {
|
||||||
|
cacheCalledInjectors.add(invoke.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Set<String> accessedFieldNames = new HashSet<>();
|
||||||
|
// We now know all methods that have been injected into initCache. See what fields they write to
|
||||||
|
injectorMethodNames.forEach((name, method) -> {
|
||||||
|
if(cacheCalledInjectors.contains(name)) {
|
||||||
|
for(AbstractInsnNode n : method.instructions) {
|
||||||
|
if(n instanceof FieldInsnNode) {
|
||||||
|
FieldInsnNode fieldAcc = (FieldInsnNode)n;
|
||||||
|
if(fieldAcc.getOpcode() == Opcodes.PUTFIELD && fieldAcc.owner.equals(targetClass.name)) {
|
||||||
|
accessedFieldNames.add(fieldAcc.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Lastly, scan all injected methods and see if they retrieve from the field. If so, inject a generateCache
|
||||||
|
// call at the start.
|
||||||
|
injectorMethodNames.forEach((name, method) -> {
|
||||||
|
// skip whitelisted injectors, and injectors called by initCache itself (to prevent recursion)
|
||||||
|
if(whitelistedInjections.contains(name) || cacheCalledInjectors.contains(name))
|
||||||
|
return;
|
||||||
|
boolean needInjection = false;
|
||||||
|
for(AbstractInsnNode n : method.instructions) {
|
||||||
|
if(n instanceof FieldInsnNode) {
|
||||||
|
FieldInsnNode fieldAcc = (FieldInsnNode)n;
|
||||||
|
if(fieldAcc.getOpcode() == Opcodes.GETFIELD && accessedFieldNames.contains(fieldAcc.name)) {
|
||||||
|
needInjection = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(needInjection) {
|
||||||
|
ModernFixMixinPlugin.instance.logger.info("Injecting BlockStateBase cache population hook into {} from {}",
|
||||||
|
name, injectorMixinSource.getOrDefault(name, "[unknown mixin]"));
|
||||||
|
// inject this.mfix$generateCache() at method head
|
||||||
|
InsnList injection = new InsnList();
|
||||||
|
injection.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||||
|
injection.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, targetClass.name, "mfix$generateCache", "()V"));
|
||||||
|
method.instructions.insert(injection);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package org.embeddedt.modernfix.duck;
|
||||||
|
|
||||||
|
import org.embeddedt.modernfix.chunk.SafeBlockGetter;
|
||||||
|
|
||||||
|
public interface ISafeBlockGetter {
|
||||||
|
SafeBlockGetter mfix$getSafeBlockGetter();
|
||||||
|
}
|
||||||
|
|
@ -4,10 +4,11 @@
|
||||||
"modernfix.jei_load": "正在加载JEI,这可能会花费一段时间。",
|
"modernfix.jei_load": "正在加载JEI,这可能会花费一段时间。",
|
||||||
"modernfix.no_lazydfu": "未安装DFU载入优化。如果Minecraft需要从旧版本更新游戏数据,可能会出现极大的延迟。",
|
"modernfix.no_lazydfu": "未安装DFU载入优化。如果Minecraft需要从旧版本更新游戏数据,可能会出现极大的延迟。",
|
||||||
"modernfix.no_ferritecore": "未安装铁氧体磁芯。内存占用将会非常高。",
|
"modernfix.no_ferritecore": "未安装铁氧体磁芯。内存占用将会非常高。",
|
||||||
|
"modernfix.connectedness_dynresoruces": "Connectedness模组(用于提供连接纹理)和现代化修复的动态资源(dynamic resources)功能不兼容。请删除Connectedness模组,或在现代化修复配置中禁用动态资源功能。",
|
||||||
"modernfix.perf_mod_warning": "推荐安装这些模组,但你也可以在现代化修复的配置中禁用此警告。",
|
"modernfix.perf_mod_warning": "推荐安装这些模组,但你也可以在现代化修复的配置中禁用此警告。",
|
||||||
"modernfix.config": "现代化修复Mixin配置",
|
"modernfix.config": "现代化修复Mixin配置",
|
||||||
"modernfix.config.done_restart": "完成(生效需重启)",
|
"modernfix.config.done_restart": "完成(生效需重启)",
|
||||||
"modernfix.message.reload_config": "在游戏外编辑完配置文件后,使用§b/mfrc§r命令使其生效。",
|
"modernfix.message.reload_config": "检测到模组配置文件的更改。为了避免加载尚未保存完毕的文件,重载过程必须通过使用§b/mfrc§r命令来触发。",
|
||||||
"modernfix.option.on": "开启",
|
"modernfix.option.on": "开启",
|
||||||
"modernfix.option.off": "关闭",
|
"modernfix.option.off": "关闭",
|
||||||
"modernfix.option.disabled": "已禁用",
|
"modernfix.option.disabled": "已禁用",
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ configurations {
|
||||||
dependencies {
|
dependencies {
|
||||||
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
||||||
testImplementation "net.fabricmc:fabric-loader-junit:${rootProject.fabric_loader_version}"
|
testImplementation "net.fabricmc:fabric-loader-junit:${rootProject.fabric_loader_version}"
|
||||||
|
include(implementation(annotationProcessor("com.github.llamalad7.mixinextras:mixinextras-fabric:${rootProject.mixinextras_version}")))
|
||||||
|
|
||||||
modCompileOnly(fabricApi.module("fabric-api-base", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
modCompileOnly(fabricApi.module("fabric-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-screen-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@ repositories {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
forge "net.minecraftforge:forge:${rootProject.forge_version}"
|
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
|
// Remove the next line if you don't want to depend on the API
|
||||||
// modApi "me.shedaniel:architectury-forge:${rootProject.architectury_version}"
|
// modApi "me.shedaniel:architectury-forge:${rootProject.architectury_version}"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
package org.embeddedt.modernfix.forge.mixin.bugfix.chunk_deadlock;
|
||||||
|
|
||||||
|
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||||
|
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||||
|
import net.minecraft.server.level.ChunkHolder;
|
||||||
|
import net.minecraft.server.level.ChunkMap;
|
||||||
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
|
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
|
||||||
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
@Mixin(ChunkMap.class)
|
||||||
|
public abstract class ChunkMapLoadMixin {
|
||||||
|
@Shadow @Nullable protected abstract ChunkHolder getVisibleChunkIfPresent(long l);
|
||||||
|
|
||||||
|
private static final Field currentlyLoadingField = ObfuscationReflectionHelper.findField(ChunkHolder.class, "currentlyLoading");
|
||||||
|
|
||||||
|
private static void setCurrentlyLoading(ChunkHolder holder, LevelChunk value) {
|
||||||
|
try {
|
||||||
|
currentlyLoadingField.set(holder, value);
|
||||||
|
} catch(ReflectiveOperationException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set currentlyLoading before calling runPostLoad and restore its old value afterwards. We track the old value
|
||||||
|
* to avoid conflicting with Forge if/when this feature is added.
|
||||||
|
*/
|
||||||
|
@WrapOperation(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/LevelChunk;runPostLoad()V"))
|
||||||
|
private void setCurrentLoadingThenPostLoad(LevelChunk chunk, Operation<Void> operation) {
|
||||||
|
ChunkHolder holder = this.getVisibleChunkIfPresent(chunk.getPos().toLong());
|
||||||
|
if(holder != null) {
|
||||||
|
LevelChunk prevLoading = null;
|
||||||
|
try {
|
||||||
|
prevLoading = (LevelChunk)currentlyLoadingField.get(holder);
|
||||||
|
} catch(ReflectiveOperationException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
setCurrentlyLoading(holder, chunk);
|
||||||
|
operation.call(chunk);
|
||||||
|
} finally {
|
||||||
|
setCurrentlyLoading(holder, prevLoading);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ModernFix.LOGGER.warn("Unable to find chunk holder for loading chunk");
|
||||||
|
operation.call(chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
org.gradle.jvmargs=-Xmx2G
|
org.gradle.jvmargs=-Xmx2G
|
||||||
|
|
||||||
junit_version=5.10.0-M1
|
junit_version=5.10.0-M1
|
||||||
|
mixinextras_version=0.2.0-beta.9
|
||||||
|
|
||||||
mod_id=modernfix
|
mod_id=modernfix
|
||||||
minecraft_version=1.19.4
|
minecraft_version=1.19.4
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user