Merge remote-tracking branch 'origin/1.20' into 1.21.1
This commit is contained in:
commit
a25e37b968
2
.github/workflows/gradle.yml
vendored
2
.github/workflows/gradle.yml
vendored
|
|
@ -44,6 +44,8 @@ jobs:
|
||||||
run: ./scripts/tagcleaner.sh
|
run: ./scripts/tagcleaner.sh
|
||||||
- name: Build ModernFix using Gradle
|
- name: Build ModernFix using Gradle
|
||||||
run: ./gradlew build
|
run: ./gradlew build
|
||||||
|
- name: Run mixin audit
|
||||||
|
run: timeout 60 xvfb-run ./gradlew runAuditClient
|
||||||
- name: Publish mod to CurseForge & Modrinth
|
- name: Publish mod to CurseForge & Modrinth
|
||||||
if: steps.check_branch.outputs.is_release == 'true'
|
if: steps.check_branch.outputs.is_release == 'true'
|
||||||
run: ./gradlew publishMods copyJarToBin
|
run: ./gradlew publishMods copyJarToBin
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,10 @@ neoForge {
|
||||||
create("server") {
|
create("server") {
|
||||||
server()
|
server()
|
||||||
}
|
}
|
||||||
|
create("auditClient") {
|
||||||
|
client()
|
||||||
|
jvmArguments.addAll("-Dmodernfix.auditAndExit=true", "-Djava.awt.headless=true")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mods {
|
mods {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||||
import org.embeddedt.modernfix.resources.ReloadExecutor;
|
import org.embeddedt.modernfix.resources.ReloadExecutor;
|
||||||
import org.embeddedt.modernfix.util.ClassInfoManager;
|
import org.embeddedt.modernfix.util.ClassInfoManager;
|
||||||
|
import org.spongepowered.asm.mixin.MixinEnvironment;
|
||||||
|
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
@ -45,6 +46,15 @@ public class ModernFix {
|
||||||
return resourceReloadService;
|
return resourceReloadService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void runAuditIfRequested() {
|
||||||
|
boolean auditAndExit = Boolean.getBoolean("modernfix.auditAndExit");
|
||||||
|
if (auditAndExit || Boolean.getBoolean("modernfix.auditMixinsAtStart")) {
|
||||||
|
MixinEnvironment.getCurrentEnvironment().audit();
|
||||||
|
if (auditAndExit) {
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ModernFix() {
|
public ModernFix() {
|
||||||
INSTANCE = this;
|
INSTANCE = this;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ package org.embeddedt.modernfix.common.mixin.perf.dynamic_languages;
|
||||||
|
|
||||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||||
import com.llamalad7.mixinextras.sugar.Local;
|
import com.llamalad7.mixinextras.sugar.Share;
|
||||||
|
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
|
||||||
import net.minecraft.client.resources.language.ClientLanguage;
|
import net.minecraft.client.resources.language.ClientLanguage;
|
||||||
import net.minecraft.server.packs.resources.Resource;
|
import net.minecraft.server.packs.resources.Resource;
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
|
|
@ -11,39 +12,33 @@ import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modifies the language system to load/unload the contents of language entries based on GC pressure.
|
* Modifies the language system to load/unload the contents of language entries based on GC pressure.
|
||||||
*/
|
*/
|
||||||
@Mixin(ClientLanguage.class)
|
@Mixin(value = ClientLanguage.class, priority = 2000)
|
||||||
@ClientOnlyMixin
|
@ClientOnlyMixin
|
||||||
public class ClientLanguageMixin {
|
public class ClientLanguageMixin {
|
||||||
private static final ThreadLocal<Boolean> MFIX_MODIFY_APPEND_SEMANTICS = ThreadLocal.withInitial(() -> Boolean.FALSE);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author embeddedt
|
* @author embeddedt
|
||||||
* @reason modify the semantics of appendFrom so that it's used to do a prepass
|
* @reason collect the list of all known language resources
|
||||||
*/
|
*/
|
||||||
@ModifyArg(method = "appendFrom", at = @At(value = "INVOKE", target = "Lnet/minecraft/locale/Language;loadFromJson(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V"), index = 1)
|
@WrapOperation(method = "loadFrom", at = @At(value = "INVOKE",
|
||||||
private static BiConsumer<String, ?> changeSemanticsOfConsumer(BiConsumer<String, ?> consumer, @Local(ordinal = 0, argsOnly = true) Map<String, Object> destinationMap, @Local(ordinal = 0) Resource resource) {
|
target = "Lnet/minecraft/client/resources/language/ClientLanguage;appendFrom(Ljava/lang/String;Ljava/util/List;Ljava/util/Map;)V"))
|
||||||
return MFIX_MODIFY_APPEND_SEMANTICS.get() ? ((k, v) -> destinationMap.put(k, resource)) : consumer;
|
private static void collectResources(String languageName, List<Resource> resources,
|
||||||
}
|
Map<String, String> destinationMap, Operation<Void> original,
|
||||||
|
@Share("usedResources") LocalRef<List<Resource>> usedResources) {
|
||||||
/**
|
List<Resource> collected = usedResources.get();
|
||||||
* @author embeddedt
|
if (collected == null) {
|
||||||
* @reason collect resources that own keys with a prepass
|
collected = new ArrayList<>();
|
||||||
*/
|
usedResources.set(collected);
|
||||||
@WrapOperation(method = "loadFrom", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/language/ClientLanguage;appendFrom(Ljava/lang/String;Ljava/util/List;Ljava/util/Map;)V"))
|
|
||||||
private static void trackEntrySource(String languageName, List<Resource> resources, Map<String, String> destinationMap, Operation<Void> original) {
|
|
||||||
MFIX_MODIFY_APPEND_SEMANTICS.set(true);
|
|
||||||
try {
|
|
||||||
original.call(languageName, resources, destinationMap);
|
|
||||||
} finally {
|
|
||||||
MFIX_MODIFY_APPEND_SEMANTICS.remove();
|
|
||||||
}
|
}
|
||||||
|
collected.addAll(resources);
|
||||||
|
original.call(languageName, resources, destinationMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -51,7 +46,8 @@ public class ClientLanguageMixin {
|
||||||
* @reason figure out which keys are dynamically loaded and which are injected by mixins
|
* @reason figure out which keys are dynamically loaded and which are injected by mixins
|
||||||
*/
|
*/
|
||||||
@ModifyArg(method = "loadFrom", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/language/ClientLanguage;<init>(Ljava/util/Map;Z)V"), index = 0)
|
@ModifyArg(method = "loadFrom", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/language/ClientLanguage;<init>(Ljava/util/Map;Z)V"), index = 0)
|
||||||
private static Map<String, String> modifyLanguageMap(Map<String, ?> storage) {
|
private static Map<String, String> modifyLanguageMap(Map<String, String> storage, @Share("usedResources") LocalRef<List<Resource>> usedResources) {
|
||||||
return DynamicLanguageMap.forStorage(Map.copyOf(storage));
|
List<Resource> collected = Objects.requireNonNullElse(usedResources.get(), List.of());
|
||||||
|
return DynamicLanguageMap.forVanillaData(storage, collected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
@Mixin(SurfaceSystem.class)
|
@Mixin(value = SurfaceSystem.class, priority = 2000)
|
||||||
public class SurfaceSystemMixin {
|
public class SurfaceSystemMixin {
|
||||||
private static final ThreadLocal<ChunkBiomeLookup> MFIX_LOOKUP_CACHE = ThreadLocal.withInitial(ChunkBiomeLookup::new);
|
private static final ThreadLocal<ChunkBiomeLookup> MFIX_LOOKUP_CACHE = ThreadLocal.withInitial(ChunkBiomeLookup::new);
|
||||||
private static final ThreadLocal<PrefetchingBlockColumn> MFIX_BLOCK_COLUMN = new ThreadLocal<>();
|
private static final ThreadLocal<PrefetchingBlockColumn> MFIX_BLOCK_COLUMN = new ThreadLocal<>();
|
||||||
|
|
@ -43,7 +43,7 @@ public class SurfaceSystemMixin {
|
||||||
return lookup;
|
return lookup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "buildSurface", at = @At("RETURN"))
|
@Inject(method = "buildSurface", at = @At("TAIL"))
|
||||||
private void finishAndDisposeLookups(RandomState randomState, BiomeManager biomeManager, Registry<Biome> biomes, boolean p_224652_, WorldGenerationContext context, ChunkAccess chunk, NoiseChunk noiseChunk, SurfaceRules.RuleSource ruleSource, CallbackInfo ci) {
|
private void finishAndDisposeLookups(RandomState randomState, BiomeManager biomeManager, Registry<Biome> biomes, boolean p_224652_, WorldGenerationContext context, ChunkAccess chunk, NoiseChunk noiseChunk, SurfaceRules.RuleSource ruleSource, CallbackInfo ci) {
|
||||||
MFIX_LOOKUP_CACHE.get().dispose();
|
MFIX_LOOKUP_CACHE.get().dispose();
|
||||||
var column = MFIX_BLOCK_COLUMN.get();
|
var column = MFIX_BLOCK_COLUMN.get();
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ import org.embeddedt.modernfix.duck.suspend_integrated_server_during_load.IDefer
|
||||||
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.callback.CallbackInfo;
|
||||||
|
|
||||||
import java.net.Proxy;
|
import java.net.Proxy;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
@ -28,6 +30,7 @@ public abstract class IntegratedServerMixin extends MinecraftServer implements I
|
||||||
@Shadow
|
@Shadow
|
||||||
private boolean paused;
|
private boolean paused;
|
||||||
|
|
||||||
|
private int mfix$numTickServerCalls = 0;
|
||||||
private final AtomicBoolean mfix$hasPrimaryClientJoined = new AtomicBoolean(false);
|
private final AtomicBoolean mfix$hasPrimaryClientJoined = new AtomicBoolean(false);
|
||||||
|
|
||||||
public IntegratedServerMixin(Thread serverThread, LevelStorageSource.LevelStorageAccess storageSource, PackRepository packRepository, WorldStem worldStem, Proxy proxy, DataFixer fixerUpper, Services services, ChunkProgressListenerFactory progressListenerFactory) {
|
public IntegratedServerMixin(Thread serverThread, LevelStorageSource.LevelStorageAccess storageSource, PackRepository packRepository, WorldStem worldStem, Proxy proxy, DataFixer fixerUpper, Services services, ChunkProgressListenerFactory progressListenerFactory) {
|
||||||
|
|
@ -44,14 +47,26 @@ public abstract class IntegratedServerMixin extends MinecraftServer implements I
|
||||||
return !mfix$hasPrimaryClientJoined.get() || original.call(instance);
|
return !mfix$hasPrimaryClientJoined.get() || original.call(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author embeddedt
|
||||||
|
* @reason Keep our own tick count for the integrated server specifically, rather than relying on super
|
||||||
|
* to increment.
|
||||||
|
*/
|
||||||
|
@Inject(method = "tickServer", at = @At("HEAD"))
|
||||||
|
private void mfix$countTicks(CallbackInfo ci) {
|
||||||
|
this.mfix$numTickServerCalls++;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author embeddedt
|
* @author embeddedt
|
||||||
* @reason If waiting for a client connection to exist, we only need to tick the server connection,
|
* @reason If waiting for a client connection to exist, we only need to tick the server connection,
|
||||||
* not the whole server as vanilla does.
|
* not the whole server as vanilla does. However, we must tick the whole server once to accommodate mods
|
||||||
|
* that rely on the first tick to initialize state as a side effect. Not doing this causes issues like
|
||||||
|
* <a href="https://github.com/embeddedt/ModernFix/issues/639">#639</a>.
|
||||||
*/
|
*/
|
||||||
@WrapWithCondition(method = "tickServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;tickServer(Ljava/util/function/BooleanSupplier;)V", ordinal = 0))
|
@WrapWithCondition(method = "tickServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;tickServer(Ljava/util/function/BooleanSupplier;)V", ordinal = 0))
|
||||||
private boolean preventRunningFullServerTick(MinecraftServer server, BooleanSupplier hasTimeLeft) {
|
private boolean preventRunningFullServerTick(MinecraftServer server, BooleanSupplier hasTimeLeft) {
|
||||||
if (this.paused && !mfix$hasPrimaryClientJoined.get()) {
|
if (this.mfix$numTickServerCalls >= 2 && this.paused && !mfix$hasPrimaryClientJoined.get()) {
|
||||||
var conn = this.getConnection();
|
var conn = this.getConnection();
|
||||||
if (conn != null) {
|
if (conn != null) {
|
||||||
conn.tick();
|
conn.tick();
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,28 @@ import net.minecraft.server.packs.resources.Resource;
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class DynamicLanguageMap {
|
public class DynamicLanguageMap {
|
||||||
public static Map<String, String> forStorage(Map<String, ?> storage) {
|
private static Map<String, Object> createStorage(Map<String, String> rawLanguageContents, List<Resource> languageResources) {
|
||||||
|
Map<String, Object> storage = new HashMap<>(rawLanguageContents);
|
||||||
|
for (var resource : languageResources) {
|
||||||
|
try (var stream = resource.open()) {
|
||||||
|
Language.loadFromJson(stream, (key, value) -> {
|
||||||
|
if (value != null && value.equals(storage.get(key))) {
|
||||||
|
storage.put(key, resource);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Map.copyOf(storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, String> forVanillaData(Map<String, String> rawLanguageContents, List<Resource> languageResources) {
|
||||||
|
Map<String, Object> storage = createStorage(rawLanguageContents, languageResources);
|
||||||
LoadingCache<Resource, Map<String, String>> languageFileContents = CacheBuilder.newBuilder()
|
LoadingCache<Resource, Map<String, String>> languageFileContents = CacheBuilder.newBuilder()
|
||||||
.softValues()
|
.softValues()
|
||||||
.build(new CacheLoader<>() {
|
.build(new CacheLoader<>() {
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ public class ModernFixForge {
|
||||||
ModLoader.addLoadingIssue(ModLoadingIssue.warning("modernfix.perf_mod_warning"));
|
ModLoader.addLoadingIssue(ModLoadingIssue.warning("modernfix.perf_mod_warning"));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
event.enqueueWork(ModernFix::runAuditIfRequested);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerNetworkChannel(final RegisterPayloadHandlersEvent event) {
|
private void registerNetworkChannel(final RegisterPayloadHandlersEvent event) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user