Merge 1.19.2 into 1.19.4

This commit is contained in:
embeddedt 2023-06-20 14:34:53 -04:00
commit 9cdeeb4c94
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
8 changed files with 91 additions and 134 deletions

View File

@ -2,6 +2,7 @@ package org.embeddedt.modernfix.common.mixin.perf.dedicated_reload_executor;
import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
@ -9,6 +10,7 @@ import org.spongepowered.asm.mixin.injection.ModifyArg;
import java.util.concurrent.Executor;
@Mixin(CreateWorldScreen.class)
@ClientOnlyMixin
public class CreateWorldScreenMixin {
@ModifyArg(method = "applyNewPackConfig", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/WorldLoader;load(Lnet/minecraft/server/WorldLoader$InitConfig;Lnet/minecraft/server/WorldLoader$WorldDataSupplier;Lnet/minecraft/server/WorldLoader$ResultFactory;Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"), index = 3)
private Executor getReloadExecutorService(Executor e) {

View File

@ -2,6 +2,7 @@ package org.embeddedt.modernfix.common.mixin.perf.dedicated_reload_executor;
import net.minecraft.client.gui.screens.worldselection.WorldOpenFlows;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
@ -9,6 +10,7 @@ import org.spongepowered.asm.mixin.injection.ModifyArg;
import java.util.concurrent.Executor;
@Mixin(WorldOpenFlows.class)
@ClientOnlyMixin
public class WorldOpenFlowsMixin {
@ModifyArg(method = "loadWorldDataBlocking", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/WorldLoader;load(Lnet/minecraft/server/WorldLoader$InitConfig;Lnet/minecraft/server/WorldLoader$WorldDataSupplier;Lnet/minecraft/server/WorldLoader$ResultFactory;Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"), index = 3)
private Executor getResourceReloadExecutor(Executor service) {

View File

@ -158,6 +158,9 @@ public class ModernFixEarlyConfig {
this.scanForAndBuildMixinOptions();
mixinOptions.addAll(DEFAULT_SETTING_OVERRIDES.keySet());
if(!isFabric) {
mixinOptions.add("mixin.bugfix.fix_config_crashes");
}
for(String optionName : mixinOptions) {
boolean defaultEnabled = DEFAULT_SETTING_OVERRIDES.getOrDefault(optionName, true);
this.options.putIfAbsent(optionName, new Option(optionName, defaultEnabled, false));

View File

@ -1,157 +1,46 @@
package org.embeddedt.modernfix.util;
import com.google.common.base.Function;
import com.google.common.collect.Interner;
import com.google.common.collect.Interners;
import it.unimi.dsi.fastutil.objects.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.google.common.collect.Maps;
import java.util.*;
import java.util.function.Function;
import java.util.HashMap;
import java.util.Map;
/**
* Replacement backing map for CompoundTags. Uses an array map for tags with 4 or less entries,
* and a hash map for larger tags.
* Replacement backing map for CompoundTags that interns keys.
*/
public class CanonizingStringMap<T> implements Map<String, T> {
private Object2ObjectMap<String, T> backingMap;
private static final int GROWTH_THRESHOLD = 4;
public class CanonizingStringMap<T> extends HashMap<String, T> {
private static final Interner<String> KEY_INTERNER = Interners.newStrongInterner();
private static String intern(String key) {
return key != null ? KEY_INTERNER.intern(key) : null;
}
public CanonizingStringMap() {
this(new Object2ObjectArrayMap<>());
}
protected CanonizingStringMap(Object2ObjectMap<String, T> newMap) {
this.backingMap = newMap;
super();
}
@Override
public int size() {
return backingMap.size();
public T put(String key, T value) {
return super.put(intern(key), value);
}
@Override
public boolean isEmpty() {
return backingMap.isEmpty();
public void putAll(Map<? extends String, ? extends T> m) {
HashMap<String, T> tmp = new HashMap<>();
m.forEach((k, v) -> tmp.put(intern(k), v));
super.putAll(tmp);
}
@Override
public boolean containsKey(Object o) {
return backingMap.containsKey(o);
private void putAllWithoutInterning(Map<? extends String, ? extends T> m) {
super.putAll(m);
}
@Override
public boolean containsValue(Object o) {
return backingMap.containsValue(o);
}
@Override
public T get(Object o) {
return backingMap.get(o);
}
@Nullable
@Override
public T put(String s, T t) {
if(backingMap.size() >= GROWTH_THRESHOLD && !(backingMap instanceof Object2ObjectOpenHashMap) && !backingMap.containsKey(s)) {
// map will grow to GROWTH_THRESHOLD + 1 entries, change to hashmap
backingMap = new Object2ObjectOpenHashMap<>(backingMap);
}
s = KEY_INTERNER.intern(s);
return backingMap.put(s, t);
}
@Override
public T remove(Object o) {
T value = backingMap.remove(o);
// need to shrink to be consistent with new maps
if(backingMap.size() <= GROWTH_THRESHOLD && backingMap instanceof Object2ObjectOpenHashMap) {
backingMap = new Object2ObjectArrayMap<>(backingMap);
}
return value;
}
@Override
public void putAll(@NotNull Map<? extends String, ? extends T> map) {
if(map.size() == 0)
return;
// grow early if we know there are enough non-overlapping keys
if((map.size() - backingMap.size()) > GROWTH_THRESHOLD && !(backingMap instanceof Object2ObjectOpenHashMap)) {
backingMap = new Object2ObjectOpenHashMap<>(backingMap);
}
map.forEach((String key, T val) -> {
key = KEY_INTERNER.intern(key);
backingMap.put(key, val);
});
// if it's still an array, and now too big, grow it
if(backingMap.size() > GROWTH_THRESHOLD && !(backingMap instanceof Object2ObjectOpenHashMap)) {
backingMap = new Object2ObjectOpenHashMap<>(backingMap);
}
}
@Override
public void clear() {
if(!(this.backingMap instanceof Object2ObjectArrayMap))
this.backingMap = new Object2ObjectArrayMap<>();
else
this.backingMap.clear();
}
@NotNull
@Override
public Set<String> keySet() {
// has to be modifiable because mods (cough, Tinkers) use it to clear the tag
return this.backingMap.keySet();
}
@NotNull
@Override
public Collection<T> values() {
return Collections.unmodifiableCollection(this.backingMap.values());
}
@NotNull
@Override
public Set<Entry<String, T>> entrySet() {
return Collections.unmodifiableSet(this.backingMap.entrySet());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CanonizingStringMap<?> that = (CanonizingStringMap<?>)o;
if(that.backingMap.size() != backingMap.size())
return false;
return backingMap.object2ObjectEntrySet().containsAll(that.backingMap.object2ObjectEntrySet());
}
/**
* We deliberately use a hashcode that will be consistent regardless of underlying map type.
*/
@Override
public int hashCode() {
final ObjectIterator<Object2ObjectMap.Entry<String, T>> i = Object2ObjectMaps.fastIterator(backingMap);
int h = 0, n = backingMap.size();
while (n-- != 0)
h += i.next().hashCode();
return h;
}
public static <T> CanonizingStringMap<T> deepCopy(CanonizingStringMap<T> inputMap, Function<T, T> deepCopier) {
Objects.requireNonNull(deepCopier);
Object2ObjectMap<String, T> copiedBackingMap;
int size = inputMap.backingMap.size();
if(size > GROWTH_THRESHOLD) {
copiedBackingMap = new Object2ObjectOpenHashMap<>(size);
} else
copiedBackingMap = new Object2ObjectArrayMap<>(size);
inputMap.backingMap.object2ObjectEntrySet().forEach(entry -> {
if(entry.getKey() != null && entry.getValue() != null)
copiedBackingMap.put(entry.getKey(), deepCopier.apply(entry.getValue()));
});
return new CanonizingStringMap<>(copiedBackingMap);
public static <T> CanonizingStringMap<T> deepCopy(CanonizingStringMap<T> incomingMap, Function<T, T> deepCopier) {
CanonizingStringMap<T> newMap = new CanonizingStringMap<>();
newMap.putAllWithoutInterning(Maps.transformValues(incomingMap, deepCopier));
return newMap;
}
}

View File

@ -0,0 +1,56 @@
package org.embeddedt.modernfix.forge.config;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import net.minecraftforge.fml.config.ModConfig;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
import java.util.Optional;
import java.util.function.Consumer;
public class ConfigFixer {
/**
* To prevent mods from crashing if their ModConfigEvent is invoked by Night Config's watch thread and Forge
* at the same time, wrap their config handler so that it only executes the event in serial for that mod.
*
* Should have no noticeable performance impact as config handlers are virtually instant.
*/
public static void replaceConfigHandlers() {
if(!ModernFixMixinPlugin.instance.isOptionEnabled("bugfix.fix_config_crashes.ConfigFixerMixin"))
return;
ModList.get().forEachModContainer((id, container) -> {
try {
Optional<Consumer<ModConfig.ModConfigEvent>> configOpt = ObfuscationReflectionHelper.getPrivateValue(ModContainer.class, container, "configHandler");
if(configOpt.isPresent()) {
ObfuscationReflectionHelper.setPrivateValue(ModContainer.class, container, Optional.of(new LockingConfigHandler(id, configOpt.get())), "configHandler");
}
} catch(RuntimeException e) {
ModernFix.LOGGER.error("Error replacing config handler", e);
}
});
}
private static class LockingConfigHandler implements Consumer<ModConfig.ModConfigEvent> {
private final Consumer<ModConfig.ModConfigEvent> actualHandler;
private final String modId;
LockingConfigHandler(String id, Consumer<ModConfig.ModConfigEvent> actualHandler) {
this.modId = id;
this.actualHandler = actualHandler;
}
@Override
public void accept(ModConfig.ModConfigEvent modConfigEvent) {
synchronized (this) {
this.actualHandler.accept(modConfigEvent);
}
}
@Override
public String toString() {
return "LockingConfigHandler{id=" + modId + "}";
}
}
}

View File

@ -26,6 +26,7 @@ import org.embeddedt.modernfix.forge.classloading.ClassLoadHack;
import org.embeddedt.modernfix.forge.classloading.ModFileScanDataDeduplicator;
import org.embeddedt.modernfix.forge.ModernFixConfig;
import org.embeddedt.modernfix.entity.EntityDataIDSyncHandler;
import org.embeddedt.modernfix.forge.config.ConfigFixer;
import org.embeddedt.modernfix.forge.packet.PacketHandler;
import org.embeddedt.modernfix.forge.registry.ObjectHolderClearer;
@ -46,6 +47,7 @@ public class ModernFixForge {
PacketHandler.register();
ModFileScanDataDeduplicator.deduplicate();
ClassLoadHack.loadModClasses();
ConfigFixer.replaceConfigHandlers();
}
@SubscribeEvent

View File

@ -27,6 +27,8 @@ do_release() {
echo "we think the current tag is $(git describe --tags --abbrev=0)"
echo "the current commit head is $(git rev-parse HEAD)"
read -p "new tag name: " tag_name
git tag -a $tag_name -m "$tag_name"
git push --tags
gh release create $tag_name --target $1 --title "$tag_name" --notes ""
}

View File

@ -47,6 +47,7 @@ for version in "${all_versions[@]}"; do
read -rs -n1
fi
if (git add . && git commit -m "Merge $our_version into $version" &>/dev/null); then
echo
git push -u origin propagations/$version:$version &>/dev/null
else
echo -e "\b\b\b\bnothing to merge"