Merge remote-tracking branch 'origin/1.20' into 1.21.1

This commit is contained in:
embeddedt 2025-05-03 14:43:31 -04:00 committed by DerCommander323
parent 7ac2c5fa68
commit 69f08abe8c
3 changed files with 72 additions and 4 deletions

View File

@ -8,6 +8,8 @@ import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.ItemStackLinkedSet;
import net.minecraft.world.item.crafting.Ingredient;
import net.neoforged.neoforge.common.crafting.ICustomIngredient;
import org.embeddedt.modernfix.neoforge.recipe.ExtendedIngredient;
import org.embeddedt.modernfix.neoforge.recipe.IngredientItemStacksSoftReference;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
@ -22,7 +24,7 @@ import java.util.ArrayList;
import java.util.stream.Collectors;
@Mixin(value = Ingredient.class, priority = 700)
public abstract class IngredientMixin {
public abstract class IngredientMixin implements ExtendedIngredient {
@Shadow @Final
private Ingredient.Value[] values;
@ -39,6 +41,8 @@ public abstract class IngredientMixin {
return !this.isCustom();
}
private volatile IngredientItemStacksSoftReference mfix$cachedItemStacks;
/**
* @author embeddedt
* @reason tag ingredients can be tested without iterating over all items
@ -109,9 +113,8 @@ public abstract class IngredientMixin {
/**
* @author embeddedt
* @reason remove caching of the item stacks, it won't deduplicate anything with tags (since each Ingredient
* instance would make new item stacks anyway, and storing them permanently takes up a lot of memory).
* We implement an optimized version of some functions that avoids needing to call this entirely.
* @reason Change caching of item stacks to use a soft reference, which allows the GC to evict the array under
* memory pressure/when it hasn't been used.
*/
@Overwrite
public ItemStack[] getItems() {
@ -124,6 +127,19 @@ public abstract class IngredientMixin {
this.itemStacks = this.customIngredient.getItems().collect(Collectors.toCollection(ItemStackLinkedSet::createTypeAndComponentsSet)).toArray(ItemStack[]::new);
return this.itemStacks;
}
var cache = this.mfix$cachedItemStacks;
if (cache != null) {
var stacks = cache.get();
if (stacks != null) {
return stacks;
}
}
ItemStack[] result = computeItemsArray();
this.mfix$cachedItemStacks = new IngredientItemStacksSoftReference((Ingredient)(Object)this, result);
return result;
}
private ItemStack[] computeItemsArray() {
// Fast path for case with one item
if (this.values.length == 1) {
if (this.values[0] instanceof Ingredient.ItemValue itemValue) {
@ -150,4 +166,9 @@ public abstract class IngredientMixin {
}
return itemList.toArray(ItemStack[]::new);
}
@Override
public void mfix$clearReference() {
this.mfix$cachedItemStacks = null;
}
}

View File

@ -0,0 +1,5 @@
package org.embeddedt.modernfix.neoforge.recipe;
public interface ExtendedIngredient {
void mfix$clearReference();
}

View File

@ -0,0 +1,42 @@
package org.embeddedt.modernfix.neoforge.recipe;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
public class IngredientItemStacksSoftReference extends SoftReference<ItemStack[]> {
private final Ingredient ingredient;
private static final ReferenceQueue<ItemStack[]> QUEUE = new ReferenceQueue<>();
private static final Thread DISCARD_THREAD = new Thread(IngredientItemStacksSoftReference::clearReferences, "Ingredient reference clearing thread");
static {
DISCARD_THREAD.setPriority(Thread.NORM_PRIORITY + 2);
DISCARD_THREAD.setDaemon(true);
DISCARD_THREAD.start();
}
public IngredientItemStacksSoftReference(Ingredient ingredient, ItemStack[] stacks) {
super(stacks, QUEUE);
this.ingredient = ingredient;
}
private static void clearReferences() {
while (true) {
Reference<? extends ItemStack[]> ref;
try {
ref = QUEUE.remove();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
if (ref instanceof IngredientItemStacksSoftReference ingRef && (Object)ingRef.ingredient instanceof ExtendedIngredient extIng) {
// Null out the reference to the SoftReference object, to allow the SoftReference itself to be garbage collected.
extIng.mfix$clearReference();
}
}
}
}