Merge remote-tracking branch 'origin/1.20.4' into 1.20.5

This commit is contained in:
embeddedt 2024-03-14 20:01:31 -04:00
commit b65a63896b
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
21 changed files with 38 additions and 883 deletions

View File

@ -1,57 +0,0 @@
package org.embeddedt.modernfix.common.mixin.bugfix.buffer_builder_leak;
import com.mojang.blaze3d.vertex.BufferBuilder;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.render.UnsafeBufferHelper;
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.callback.CallbackInfo;
import java.nio.ByteBuffer;
@Mixin(value = BufferBuilder.class, priority = 1500)
@ClientOnlyMixin
public class BufferBuilderMixin {
@Shadow private ByteBuffer buffer;
@Shadow private boolean closed;
private static boolean leakReported = false;
private boolean mfix$shouldFree = true;
@Dynamic
@Inject(method = "flywheel$injectForRender", at = @At("RETURN"), remap = false, require = 0)
private void preventFree(CallbackInfo ci) {
mfix$shouldFree = false;
}
/**
* Ensure UnsafeBufferHelper is classloaded early, to avoid Forge's event transformer showing an error in the log.
*/
@Inject(method = "<clinit>", at = @At(value = "RETURN"))
private static void initUnsafeBufferHelper(CallbackInfo ci) {
UnsafeBufferHelper.init();
}
@Override
protected void finalize() throws Throwable {
try {
ByteBuffer buf = buffer;
// can be null if a mod already tried to free the buffer
if(!this.closed && buf != null && mfix$shouldFree) {
if(!leakReported) {
leakReported = true;
ModernFix.LOGGER.warn("One or more BufferBuilders have been leaked, ModernFix will attempt to correct this.");
}
UnsafeBufferHelper.free(buf);
buffer = null;
}
} finally {
super.finalize();
}
}
}

View File

@ -1,24 +0,0 @@
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;
}
}

View File

@ -1,10 +1,8 @@
package org.embeddedt.modernfix.common.mixin.perf.compact_mojang_registries;
import com.google.common.collect.ImmutableSet;
import net.minecraft.core.MappedRegistry;
import net.minecraft.core.RegistrationInfo;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import org.embeddedt.modernfix.annotation.IgnoreOutsideDev;
import org.embeddedt.modernfix.registry.LifecycleMap;
import org.spongepowered.asm.mixin.Final;
@ -25,17 +23,8 @@ public abstract class MappedRegistryMixin<T> {
@Mutable
private Map<ResourceKey<T>, RegistrationInfo> registrationInfos;
private static final ImmutableSet<ResourceLocation> MFIX$NEW_STORAGE_KEYS = ImmutableSet.of(new ResourceLocation("block"), new ResourceLocation("item"));
@Inject(method = "<init>(Lnet/minecraft/resources/ResourceKey;Lcom/mojang/serialization/Lifecycle;Z)V", at = @At("RETURN"))
@Inject(method = "<init>", at = @At("RETURN"))
private void replaceStorage(CallbackInfo ci) {
this.registrationInfos = 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<ResourceLocation, T>) RegistryStorage.createStorage();
this.keyStorage = (BiMap<ResourceKey<T>, T>)RegistryStorage.createKeyStorage(this.key(), (BiMap<ResourceLocation, DirectStorageRegistryObject>)this.storage);
}
*/
}
}

View File

@ -1,26 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_block_codecs;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.StateHolder;
import net.minecraft.world.level.block.state.properties.Property;
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.ModifyVariable;
import java.util.Map;
import java.util.function.Function;
@Mixin(StateDefinition.class)
public class StateDefinitionMixin<O, S extends StateHolder<O, S>> {
@Shadow @Final private O owner;
@ModifyVariable(method = "<init>", at = @At("HEAD"), ordinal = 0, argsOnly = true)
private static <O, S extends StateHolder<O, S>> StateDefinition.Factory<O, S> replaceMapCodec(StateDefinition.Factory<O, S> factory, Function<O, S> function, O object, StateDefinition.Factory<O, S> factory2, Map<String, Property<?>> map) {
if(object instanceof Block)
return (o, m, c) -> factory.create(o, m, null);
return factory;
}
}

View File

@ -1,58 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_block_codecs;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.mojang.serialization.Codec;
import com.mojang.serialization.Decoder;
import com.mojang.serialization.Encoder;
import com.mojang.serialization.MapCodec;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.StateHolder;
import net.minecraft.world.level.block.state.properties.Property;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import java.util.function.Supplier;
@Mixin(StateHolder.class)
public class StateHolderMixin {
private static final LoadingCache<Block, MapCodec<BlockState>> MODERNFIX_CODEC_CACHE = CacheBuilder.newBuilder()
.maximumSize(100000)
.build(new CacheLoader<Block, MapCodec<BlockState>>() {
@Override
public MapCodec<BlockState> load(Block block) throws Exception {
Supplier<BlockState> stateSupplier = block::defaultBlockState;
MapCodec<BlockState> mapCodec = MapCodec.of(Encoder.empty(), Decoder.unit(stateSupplier));
for(Property<?> property : block.getStateDefinition().getProperties()) {
mapCodec = StateDefinition.appendPropertyCodec(mapCodec, stateSupplier, property.getName(), property);
}
return mapCodec;
}
});
@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 <O, S extends StateHolder<O, S>> Codec<S> obtainCodec(Codec<O> codec, String typeKey, Function<S, O> type, Function<O, ? extends Codec<S>> codecFn, Codec<O> codecMethodArg, Function<O, S> stateSupplier) {
return codec.dispatch(typeKey, type, block -> {
if(block instanceof Block) {
S state = stateSupplier.apply(block);
if(state.getValues().isEmpty())
return Codec.unit(state);
MapCodec<S> mapCodec;
try {
mapCodec = (MapCodec<S>)MODERNFIX_CODEC_CACHE.get((Block)block);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
return mapCodec.fieldOf("Properties").codec();
} else {
return codecFn.apply(block);
}
});
}
}

View File

@ -1,12 +1,12 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceLinkedOpenHashMap;
import net.minecraft.client.renderer.block.BlockModelShaper;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.world.level.block.state.BlockState;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.dynamicresources.DynamicModelCache;
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
import org.embeddedt.modernfix.util.DynamicOverridableMap;
import org.spongepowered.asm.mixin.*;
@ -24,14 +24,13 @@ public class BlockModelShaperMixin {
@Shadow
private Map<BlockState, BakedModel> modelByStateCache;
private final DynamicModelCache<BlockState> mfix$modelCache = new DynamicModelCache<>(k -> this.cacheBlockModel((BlockState)k), false);
private ThreadLocal<Reference2ReferenceLinkedOpenHashMap<BlockState, BakedModel>> mfix$modelCache = ThreadLocal.withInitial(Reference2ReferenceLinkedOpenHashMap::new);
@Inject(method = { "<init>", "replaceCache" }, at = @At("RETURN"))
private void replaceModelMap(CallbackInfo ci) {
// replace the backing map for mods which will access it
this.modelByStateCache = new DynamicOverridableMap<>(state -> modelManager.getModel(ModelLocationCache.get(state)));
if(this.mfix$modelCache != null)
this.mfix$modelCache.clear();
this.mfix$modelCache = ThreadLocal.withInitial(Reference2ReferenceLinkedOpenHashMap::new);
}
private BakedModel cacheBlockModel(BlockState state) {
@ -51,6 +50,18 @@ public class BlockModelShaperMixin {
*/
@Overwrite
public BakedModel getBlockModel(BlockState state) {
return this.mfix$modelCache.get(state);
Reference2ReferenceLinkedOpenHashMap<BlockState, BakedModel> map = this.mfix$modelCache.get();
BakedModel model = map.get(state);
if(model != null) {
return model;
}
model = this.cacheBlockModel(state);
map.putAndMoveToFirst(state, model);
if(map.size() > 500) {
map.removeLast();
}
return model;
}
}

View File

@ -162,9 +162,7 @@ public class ModernFixEarlyConfig {
private static final ImmutableMap<String, Boolean> DEFAULT_SETTING_OVERRIDES = new DefaultSettingMapBuilder()
.put("mixin.perf.dynamic_resources", false)
.put("mixin.perf.dynamic_block_codecs", false)
.put("mixin.feature.direct_stack_trace", false)
.putConditionally(ModernFixPlatformHooks.INSTANCE::isDevEnv, "mixin.perf.rewrite_registry", false)
.put("mixin.perf.clear_mixin_classinfo", false)
.put("mixin.perf.deduplicate_climate_parameters", false)
.put("mixin.bugfix.packet_leak", false)
@ -228,6 +226,7 @@ public class ModernFixEarlyConfig {
disableIfModPresent("mixin.perf.reuse_datapacks", "tac");
disableIfModPresent("mixin.launch.class_search_cache", "optifine");
disableIfModPresent("mixin.perf.faster_texture_stitching", "optifine");
disableIfModPresent("mixin.bugfix.entity_pose_stack", "optifine");
disableIfModPresent("mixin.perf.datapack_reload_exceptions", "cyanide");
disableIfModPresent("mixin.bugfix.buffer_builder_leak", "isometric-renders");
disableIfModPresent("mixin.feature.remove_chat_signing", "nochatreports");

View File

@ -1,184 +0,0 @@
package org.embeddedt.modernfix.registry;
import com.google.common.collect.BiMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
@SuppressWarnings("unchecked")
public class DirectStorageBiMap<K, V> implements BiMap<K, V> {
private final Function<V, K> keyGetter;
private final BiConsumer<V, K> keySetter;
private final Map<K, V> forwardMap;
public DirectStorageBiMap(Function<V, K> keyGetter, BiConsumer<V, K> 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<? extends K, ? extends V> 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<K> keySet() {
return this.forwardMap.keySet();
}
@Override
public Set<V> values() {
return new HashSet<>(this.forwardMap.values());
}
@NotNull
@Override
public Set<Entry<K, V>> entrySet() {
return this.forwardMap.entrySet();
}
@Override
public BiMap<V, K> inverse() {
return new Reverse();
}
class Reverse implements BiMap<V, K> {
@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<? extends V, ? extends K> map) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@NotNull
@Override
public Set<V> keySet() {
return DirectStorageBiMap.this.values();
}
@Override
public Set<K> values() {
return DirectStorageBiMap.this.keySet();
}
@NotNull
@Override
public Set<Entry<V, K>> entrySet() {
return DirectStorageBiMap.this.entrySet().stream()
.map(entry -> new AbstractMap.SimpleImmutableEntry<>(entry.getValue(), entry.getKey()))
.collect(Collectors.toSet());
}
@Override
public BiMap<K, V> inverse() {
return DirectStorageBiMap.this;
}
}
}

View File

@ -1,8 +0,0 @@
package org.embeddedt.modernfix.registry;
import net.minecraft.resources.ResourceLocation;
public interface DirectStorageRegistryObject {
ResourceLocation mfix$getResourceKey();
void mfix$setResourceKey(ResourceLocation key);
}

View File

@ -1,33 +0,0 @@
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<ResourceLocation, DirectStorageRegistryObject> createStorage() {
return new DirectStorageBiMap<>(DirectStorageRegistryObject::mfix$getResourceKey, DirectStorageRegistryObject::mfix$setResourceKey);
}
public static <T> BiMap<ResourceKey<T>, DirectStorageRegistryObject> createKeyStorage(ResourceKey<? extends Registry<T>> registryKey, BiMap<ResourceLocation, DirectStorageRegistryObject> storage) {
if(storage instanceof DirectStorageBiMap) {
// silently ignore put/putAll calls on this map
return new TransformingBiMap<ResourceLocation, DirectStorageRegistryObject, ResourceKey<T>, DirectStorageRegistryObject>(storage, loc -> ResourceKey.create(registryKey, loc), ResourceKey::location, Function.identity(), Function.identity()) {
@Override
public DirectStorageRegistryObject put(ResourceKey<T> key, DirectStorageRegistryObject value) {
return null;
}
@Override
public void putAll(Map<? extends ResourceKey<T>, ? extends DirectStorageRegistryObject> map) {
}
};
} else
throw new UnsupportedOperationException();
}
}

View File

@ -1,224 +0,0 @@
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<KFrom, VFrom, KTo, VTo> implements BiMap<KTo, VTo> {
private final BiMap<KFrom, VFrom> delegate;
private final Function<KFrom, KTo> keyFwd;
private final Function<KTo, KFrom> keyBack;
private final Function<VFrom, VTo> valueFwd;
private final Function<VTo, VFrom> valueBack;
public TransformingBiMap(BiMap<KFrom, VFrom> map, Function<KFrom, KTo> keyFwd, Function<KTo, KFrom> keyBack, Function<VFrom, VTo> valueFwd, Function<VTo, VFrom> 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<? extends KTo, ? extends VTo> map) {
map.forEach((key, value) -> {
this.delegate.put(keyBack(key), valueBack(value));
});
}
@Override
public void clear() {
this.delegate.clear();
}
@NotNull
@Override
public Set<KTo> keySet() {
return new TransformingSet<>(this.delegate.keySet(), this.keyFwd, this.keyBack);
}
@Override
public Set<VTo> values() {
return new TransformingSet<>(this.delegate.values(), this.valueFwd, this.valueBack);
}
@NotNull
@Override
public Set<Entry<KTo, VTo>> 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<VTo, KTo> inverse() {
return new TransformingBiMap<>(this.delegate.inverse(), this.valueFwd, this.valueBack, this.keyFwd, this.keyBack);
}
static class TransformingSet<TypeFrom, TypeTo> implements Set<TypeTo> {
private final Set<TypeFrom> delegate;
private final Function<TypeFrom, TypeTo> forward;
private final Function<TypeTo, TypeFrom> reverse;
public TransformingSet(Set<TypeFrom> set, Function<TypeFrom, TypeTo> forward, Function<TypeTo, TypeFrom> 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<TypeTo> 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> 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<TypeTo> 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<? extends TypeTo> 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();
}
}
}

View File

@ -1,51 +0,0 @@
package org.embeddedt.modernfix.render;
import org.embeddedt.modernfix.ModernFix;
import org.lwjgl.system.MemoryUtil;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
/**
* Helper that frees ByteBuffers allocated by BufferBuilders, and nulls out the address pointer
* to prevent double frees.
*
* @author Moulberry
*/
public class UnsafeBufferHelper {
private static final MemoryUtil.MemoryAllocator ALLOCATOR = MemoryUtil.getAllocator(false);
private static sun.misc.Unsafe UNSAFE = null;
private static long ADDRESS = -1;
static {
try {
final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
UNSAFE = (Unsafe)theUnsafe.get(null);
final Field addressField = MemoryUtil.class.getDeclaredField("ADDRESS");
addressField.setAccessible(true);
ADDRESS = addressField.getLong(null);
} catch(Throwable t) {
ModernFix.LOGGER.error("Could load unsafe/buffer address", t);
}
}
public static void init() {
}
public static void free(ByteBuffer buf) {
if(UNSAFE != null && ADDRESS >= 0) {
// set the address to 0 to prevent double free
long address = UNSAFE.getAndSetLong(buf, ADDRESS, 0);
if(address != 0) {
ALLOCATOR.free(address);
}
} else {
ALLOCATOR.free(MemoryUtil.memAddress0(buf));
}
}
}

View File

@ -1,56 +0,0 @@
package org.embeddedt.modernfix.forge.mixin.bugfix.model_data_manager_cme;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.ChunkPos;
import net.minecraftforge.client.model.data.ModelDataManager;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
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.ModifyArg;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
/**
* Fix several concurrency issues in the default ModelDataManager.
*/
@Mixin(ModelDataManager.class)
@ClientOnlyMixin
public abstract class ModelDataManagerMixin {
@Shadow protected abstract void refreshAt(ChunkPos chunk);
@Shadow @Final private Map<ChunkPos, Set<BlockPos>> needModelDataRefresh;
/**
* Make the set of positions to refresh a real concurrent hash set rather than relying on synchronizedSet,
* because the returned iterator won't be thread-safe otherwise. See https://github.com/AppliedEnergistics/Applied-Energistics-2/issues/7511
*/
@ModifyArg(method = "requestRefresh", at = @At(value = "INVOKE", target = "Ljava/util/Map;computeIfAbsent(Ljava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object;", ordinal = 0), index = 1, remap = false)
private Function<ChunkPos, Set<BlockPos>> changeTypeOfSetUsed(Function<ChunkPos, Set<BlockPos>> mappingFunction) {
return pos -> Collections.newSetFromMap(new ConcurrentHashMap<>());
}
@Redirect(method = "getAt(Lnet/minecraft/world/level/ChunkPos;)Ljava/util/Map;", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/client/model/data/ModelDataManager;refreshAt(Lnet/minecraft/world/level/ChunkPos;)V"), remap = false)
private void onlyRefreshOnMainThread(ModelDataManager instance, ChunkPos pos) {
// Only refresh model data on the main thread. This prevents calling getBlockEntity from worker threads
// which could cause weird CMEs or other behavior.
// Avoid the loop if no model data needs to be refreshed, to prevent unnecessary allocation.
if(Minecraft.getInstance().isSameThread() && !needModelDataRefresh.isEmpty()) {
// Refresh the given chunk, and all its neighbors. This is less efficient than the default code
// but we have no choice since we need to not do refreshing on workers, and blocks might
// try to access model data in neighboring chunks.
for(int x = -1; x <= 1; x++) {
for(int z = -1; z <= 1; z++) {
refreshAt(new ChunkPos(pos.x + x, pos.z + z));
}
}
}
}
}

View File

@ -1,24 +0,0 @@
package org.embeddedt.modernfix.forge.mixin.perf.forge_cap_retrieval;
import net.minecraft.core.Direction;
import net.minecraft.world.entity.LivingEntity;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ForgeCapabilities;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import javax.annotation.Nullable;
@Mixin(LivingEntity.class)
public class LivingEntityMixin {
/**
* @author embeddedt (issue noted by XFactHD)
* @reason check capability equality before checking that entity is alive, the latter requires a lot more
* indirection
*/
@Redirect(method = "getCapability", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;isAlive()Z"))
private <T> boolean checkAliveAfterCap(LivingEntity entity, Capability<T> capability, @Nullable Direction facing) {
return capability == ForgeCapabilities.ITEM_HANDLER && entity.isAlive();
}
}

View File

@ -1,68 +0,0 @@
package org.embeddedt.modernfix.forge.mixin.perf.forge_registry_alloc;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.registries.ForgeRegistry;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import java.util.Locale;
import java.util.Map;
@Mixin(value = ForgeRegistry.class, remap = false)
public abstract class ForgeRegistryMixin<V> {
// Replace the backing maps with fastutil maps for a bit more speed, since value->holder lookups in particular
// are a bottleneck in many areas (e.g. render type lookup)
@Shadow @Final private Map<ResourceLocation, Holder.Reference<V>> delegatesByName = new Object2ObjectOpenHashMap<>();
@Shadow @Final private Map<V, Holder.Reference<V>> delegatesByValue = new Object2ObjectOpenHashMap<>();
/**
* @author embeddedt
* @reason stop allocating so many unneeded objects. stop.
*/
@Overwrite
public Holder.Reference<V> getDelegateOrThrow(ResourceLocation location) {
Holder.Reference<V> holder = delegatesByName.get(location);
if (holder == null) {
throw new IllegalArgumentException(String.format(Locale.ENGLISH, "No delegate exists for location %s", location));
}
return holder;
}
/**
* @author embeddedt
* @reason see above
*/
@Overwrite
public Holder.Reference<V> getDelegateOrThrow(ResourceKey<V> rkey) {
Holder.Reference<V> holder = delegatesByName.get(rkey.location());
if (holder == null) {
throw new IllegalArgumentException(String.format(Locale.ENGLISH, "No delegate exists for key %s", rkey));
}
return holder;
}
/**
* @author embeddedt
* @reason see above
*/
@Overwrite
public Holder.Reference<V> getDelegateOrThrow(V value) {
Holder.Reference<V> holder = delegatesByValue.get(value);
if (holder == null) {
throw new IllegalArgumentException(String.format(Locale.ENGLISH, "No delegate exists for value %s", value));
}
return holder;
}
}

View File

@ -7,7 +7,7 @@ mixinextras_version=0.3.2
mod_id=modernfix
minecraft_version=24w09a
enabled_platforms=fabric
forge_version=20.4.132-beta
forge_version=20.4.190
# parchment_version=2023.07.09
refined_storage_version=4392788
jei_version=16.0.0.28

View File

@ -50,8 +50,6 @@ dependencies {
modCompileOnly("curse.maven:supermartijncore-454372:4455391")
modCompileOnly("vazkii.patchouli:Patchouli:1.19.2-77")
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())

View File

@ -1,27 +0,0 @@
package org.embeddedt.modernfix.neoforge.mixin.bugfix.blueprint_modif_memory_leak;
import com.google.gson.Gson;
import com.teamabnormals.blueprint.core.util.modification.ObjectModificationManager;
import com.teamabnormals.blueprint.core.util.modification.selection.SelectionSpace;
import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener;
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;
@Mixin(ObjectModificationManager.class)
@RequiresMod("blueprint")
public abstract class ObjectModificationManagerMixin extends SimpleJsonResourceReloadListener {
@Shadow(remap = false) protected SelectionSpace selectionSpace;
public ObjectModificationManagerMixin(Gson gson, String string) {
super(gson, string);
}
@Inject(method = "apply(Ljava/util/Map;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V", at = @At("RETURN"), remap = false)
private void clearSelectionSpace(CallbackInfo ci) {
this.selectionSpace = consumer -> {};
}
}

View File

@ -1,10 +1,10 @@
package org.embeddedt.modernfix.forge.mixin.bugfix.entity_pose_stack;
package org.embeddedt.modernfix.neoforge.mixin.bugfix.entity_pose_stack;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraftforge.client.event.RenderLivingEvent;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.eventbus.api.IEventBus;
import net.neoforged.neoforge.client.event.RenderLivingEvent;
import net.neoforged.bus.api.Event;
import net.neoforged.bus.api.IEventBus;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@ -13,18 +13,17 @@ import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(LivingEntityRenderer.class)
@ClientOnlyMixin
public class LivingEntityRendererMixin {
@Redirect(method = "render(Lnet/minecraft/world/entity/LivingEntity;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/eventbus/api/IEventBus;post(Lnet/minecraftforge/eventbus/api/Event;)Z", ordinal = 0))
private boolean fireCheckingPoseStack(IEventBus instance, Event event) {
@Redirect(method = "render(Lnet/minecraft/world/entity/LivingEntity;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V", at = @At(value = "INVOKE", target = "Lnet/neoforged/bus/api/IEventBus;post(Lnet/neoforged/bus/api/Event;)Lnet/neoforged/bus/api/Event;", ordinal = 0))
private Event fireCheckingPoseStack(IEventBus instance, Event event) {
PoseStack stack = ((RenderLivingEvent)event).getPoseStack();
int size = ((PoseStackAccessor)stack).getPoseStack().size();
if (instance.post(event)) {
instance.post(event);
if (((RenderLivingEvent.Pre)event).isCanceled()) {
// Pop the stack if someone pushed it in the event
while (((PoseStackAccessor)stack).getPoseStack().size() > size) {
stack.popPose();
}
return true;
} else {
return false;
}
return event;
}
}

View File

@ -1,10 +1,10 @@
package org.embeddedt.modernfix.forge.mixin.bugfix.entity_pose_stack;
package org.embeddedt.modernfix.neoforge.mixin.bugfix.entity_pose_stack;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.renderer.entity.player.PlayerRenderer;
import net.minecraftforge.client.event.RenderPlayerEvent;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.eventbus.api.IEventBus;
import net.neoforged.bus.api.Event;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.client.event.RenderPlayerEvent;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@ -13,18 +13,17 @@ import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(PlayerRenderer.class)
@ClientOnlyMixin
public class PlayerRendererMixin {
@Redirect(method = "render(Lnet/minecraft/client/player/AbstractClientPlayer;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/eventbus/api/IEventBus;post(Lnet/minecraftforge/eventbus/api/Event;)Z", ordinal = 0))
private boolean fireCheckingPoseStack(IEventBus instance, Event event) {
@Redirect(method = "render(Lnet/minecraft/client/player/AbstractClientPlayer;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V", at = @At(value = "INVOKE", target = "Lnet/neoforged/bus/api/IEventBus;post(Lnet/neoforged/bus/api/Event;)Lnet/neoforged/bus/api/Event;", ordinal = 0))
private Event fireCheckingPoseStack(IEventBus instance, Event event) {
PoseStack stack = ((RenderPlayerEvent)event).getPoseStack();
int size = ((PoseStackAccessor)stack).getPoseStack().size();
if (instance.post(event)) {
instance.post(event);
if (((RenderPlayerEvent.Pre)event).isCanceled()) {
// Pop the stack if someone pushed it in the event
while (((PoseStackAccessor)stack).getPoseStack().size() > size) {
stack.popPose();
}
return true;
} else {
return false;
}
return event;
}
}

View File

@ -1,4 +1,4 @@
package org.embeddedt.modernfix.forge.mixin.bugfix.entity_pose_stack;
package org.embeddedt.modernfix.neoforge.mixin.bugfix.entity_pose_stack;
import com.mojang.blaze3d.vertex.PoseStack;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;