Merge 1.18 into 1.19.2
This commit is contained in:
commit
a84fa8ac0c
|
|
@ -158,6 +158,9 @@ public class ModernFixEarlyConfig {
|
||||||
|
|
||||||
this.scanForAndBuildMixinOptions();
|
this.scanForAndBuildMixinOptions();
|
||||||
mixinOptions.addAll(DEFAULT_SETTING_OVERRIDES.keySet());
|
mixinOptions.addAll(DEFAULT_SETTING_OVERRIDES.keySet());
|
||||||
|
if(!isFabric) {
|
||||||
|
mixinOptions.add("mixin.bugfix.fix_config_crashes");
|
||||||
|
}
|
||||||
for(String optionName : mixinOptions) {
|
for(String optionName : mixinOptions) {
|
||||||
boolean defaultEnabled = DEFAULT_SETTING_OVERRIDES.getOrDefault(optionName, true);
|
boolean defaultEnabled = DEFAULT_SETTING_OVERRIDES.getOrDefault(optionName, true);
|
||||||
this.options.putIfAbsent(optionName, new Option(optionName, defaultEnabled, false));
|
this.options.putIfAbsent(optionName, new Option(optionName, defaultEnabled, false));
|
||||||
|
|
|
||||||
|
|
@ -1,157 +1,46 @@
|
||||||
package org.embeddedt.modernfix.util;
|
package org.embeddedt.modernfix.util;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
import com.google.common.collect.Interner;
|
import com.google.common.collect.Interner;
|
||||||
import com.google.common.collect.Interners;
|
import com.google.common.collect.Interners;
|
||||||
import it.unimi.dsi.fastutil.objects.*;
|
import com.google.common.collect.Maps;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.HashMap;
|
||||||
import java.util.function.Function;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replacement backing map for CompoundTags. Uses an array map for tags with 4 or less entries,
|
* Replacement backing map for CompoundTags that interns keys.
|
||||||
* and a hash map for larger tags.
|
|
||||||
*/
|
*/
|
||||||
public class CanonizingStringMap<T> implements Map<String, T> {
|
public class CanonizingStringMap<T> extends HashMap<String, T> {
|
||||||
private Object2ObjectMap<String, T> backingMap;
|
|
||||||
|
|
||||||
private static final int GROWTH_THRESHOLD = 4;
|
|
||||||
private static final Interner<String> KEY_INTERNER = Interners.newStrongInterner();
|
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() {
|
public CanonizingStringMap() {
|
||||||
this(new Object2ObjectArrayMap<>());
|
super();
|
||||||
}
|
|
||||||
|
|
||||||
protected CanonizingStringMap(Object2ObjectMap<String, T> newMap) {
|
|
||||||
this.backingMap = newMap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public T put(String key, T value) {
|
||||||
return backingMap.size();
|
return super.put(intern(key), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() {
|
public void putAll(Map<? extends String, ? extends T> m) {
|
||||||
return backingMap.isEmpty();
|
HashMap<String, T> tmp = new HashMap<>();
|
||||||
|
m.forEach((k, v) -> tmp.put(intern(k), v));
|
||||||
|
super.putAll(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void putAllWithoutInterning(Map<? extends String, ? extends T> m) {
|
||||||
public boolean containsKey(Object o) {
|
super.putAll(m);
|
||||||
return backingMap.containsKey(o);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static <T> CanonizingStringMap<T> deepCopy(CanonizingStringMap<T> incomingMap, Function<T, T> deepCopier) {
|
||||||
public boolean containsValue(Object o) {
|
CanonizingStringMap<T> newMap = new CanonizingStringMap<>();
|
||||||
return backingMap.containsValue(o);
|
newMap.putAllWithoutInterning(Maps.transformValues(incomingMap, deepCopier));
|
||||||
}
|
return newMap;
|
||||||
|
|
||||||
@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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -26,6 +26,7 @@ import org.embeddedt.modernfix.forge.classloading.ClassLoadHack;
|
||||||
import org.embeddedt.modernfix.forge.classloading.ModFileScanDataDeduplicator;
|
import org.embeddedt.modernfix.forge.classloading.ModFileScanDataDeduplicator;
|
||||||
import org.embeddedt.modernfix.forge.ModernFixConfig;
|
import org.embeddedt.modernfix.forge.ModernFixConfig;
|
||||||
import org.embeddedt.modernfix.entity.EntityDataIDSyncHandler;
|
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.packet.PacketHandler;
|
||||||
import org.embeddedt.modernfix.forge.registry.ObjectHolderClearer;
|
import org.embeddedt.modernfix.forge.registry.ObjectHolderClearer;
|
||||||
|
|
||||||
|
|
@ -46,6 +47,7 @@ public class ModernFixForge {
|
||||||
PacketHandler.register();
|
PacketHandler.register();
|
||||||
ModFileScanDataDeduplicator.deduplicate();
|
ModFileScanDataDeduplicator.deduplicate();
|
||||||
ClassLoadHack.loadModClasses();
|
ClassLoadHack.loadModClasses();
|
||||||
|
ConfigFixer.replaceConfigHandlers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ do_release() {
|
||||||
echo "we think the current tag is $(git describe --tags --abbrev=0)"
|
echo "we think the current tag is $(git describe --tags --abbrev=0)"
|
||||||
echo "the current commit head is $(git rev-parse HEAD)"
|
echo "the current commit head is $(git rev-parse HEAD)"
|
||||||
read -p "new tag name: " tag_name
|
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 ""
|
gh release create $tag_name --target $1 --title "$tag_name" --notes ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ for version in "${all_versions[@]}"; do
|
||||||
read -rs -n1
|
read -rs -n1
|
||||||
fi
|
fi
|
||||||
if (git add . && git commit -m "Merge $our_version into $version" &>/dev/null); then
|
if (git add . && git commit -m "Merge $our_version into $version" &>/dev/null); then
|
||||||
|
echo
|
||||||
git push -u origin propagations/$version:$version &>/dev/null
|
git push -u origin propagations/$version:$version &>/dev/null
|
||||||
else
|
else
|
||||||
echo -e "\b\b\b\bnothing to merge"
|
echo -e "\b\b\b\bnothing to merge"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user