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

This commit is contained in:
embeddedt 2025-05-01 19:33:08 -04:00 committed by DerCommander323
parent 078d8284fe
commit 64a365a900
6 changed files with 264 additions and 41 deletions

View File

@ -17,11 +17,14 @@ import net.neoforged.fml.loading.FMLLoader;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.event.server.ServerStartedEvent;
import net.neoforged.neoforge.event.server.ServerStoppedEvent;
import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent;
import net.neoforged.neoforge.network.registration.PayloadRegistrar;
import net.neoforged.neoforge.registries.RegisterEvent;
import org.apache.commons.lang3.tuple.Pair;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
import org.embeddedt.modernfix.neoforge.ModernFixConfig;
import org.embeddedt.modernfix.neoforge.packet.SmartIngredientSyncPayload;
import java.util.List;
@ -36,6 +39,7 @@ public class ModernFixForge {
NeoForge.EVENT_BUS.register(this);
modBus.addListener(this::commonSetup);
modBus.addListener(this::registerItems);
modBus.addListener(this::registerNetworkChannel);
if(FMLEnvironment.dist == Dist.CLIENT) {
NeoForge.EVENT_BUS.register(new ModernFixClientForge(modContainer, modBus));
}
@ -74,6 +78,16 @@ public class ModernFixForge {
}
}
private void registerNetworkChannel(final RegisterPayloadHandlersEvent event) {
// Sets the current network version
final PayloadRegistrar registrar = event.registrar("1").optional();
registrar.playToClient(
SmartIngredientSyncPayload.TYPE,
SmartIngredientSyncPayload.STREAM_CODEC,
(payload, ctx) -> {}
);
}
@SubscribeEvent(priority = EventPriority.LOWEST)
public void onServerDead(ServerStoppedEvent event) {
commonMod.onServerDead(event.getServer());

View File

@ -0,0 +1,141 @@
package org.embeddedt.modernfix.neoforge.mixin.perf.faster_ingredients;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntComparators;
import it.unimi.dsi.fastutil.ints.IntList;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
import org.jetbrains.annotations.Nullable;
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 org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.ArrayList;
@Mixin(value = Ingredient.class, priority = 700)
public abstract class IngredientMixin {
@Shadow @Final
private Ingredient.Value[] values;
@Shadow private @Nullable IntList stackingIds;
@Shadow @Nullable private ItemStack[] itemStacks;
@Shadow public abstract boolean isCustom();
@Unique
private boolean isVanilla() {
return !this.isCustom();
}
/**
* @author embeddedt
* @reason tag ingredients can be tested without iterating over all items
*/
@Inject(method = "test(Lnet/minecraft/world/item/ItemStack;)Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/crafting/Ingredient;getItems()[Lnet/minecraft/world/item/ItemStack;"), cancellable = true)
private void modernfix$fasterTagIngredientTest(ItemStack stack, CallbackInfoReturnable<Boolean> cir) {
if (this.isCustom() && this.values.length == 1 && this.values[0] instanceof Ingredient.TagValue tagValue) {
cir.setReturnValue(stack.getItemHolder().is(tagValue.tag()));
}
}
/**
* @author embeddedt
* @reason exploding the stack list is unnecessary
*/
@Overwrite(remap = false)
public boolean hasNoItems() {
return !this.containsItems();
}
@Unique
private boolean isEmptyTagStack(ItemStack item) {
return item.getItem() == net.minecraft.world.item.Items.BARRIER && item.getHoverName() instanceof net.minecraft.network.chat.MutableComponent hoverName && hoverName.getString().startsWith("Empty Tag: ");
}
@Unique
private boolean containsItems() {
for (Ingredient.Value value : this.values) {
if (value instanceof Ingredient.ItemValue) {
return true;
} else if (value instanceof Ingredient.TagValue tagValue) {
var holderSetOpt = BuiltInRegistries.ITEM.getTag(tagValue.tag());
if (holderSetOpt.isPresent() && holderSetOpt.get().size() > 0) {
return true;
}
} else {
var items = value.getItems();
if (items.isEmpty() || isEmptyTagStack(items.iterator().next())) {
// Doesn't have items
continue;
}
return true;
}
}
return false;
}
/**
* @author embeddedt
* @reason tag ingredients can be converted to stacking IDs without expanding into stacks, since stacking only
* goes by item ID
*/
@Inject(method = "getStackingIds", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/crafting/Ingredient;getItems()[Lnet/minecraft/world/item/ItemStack;"), cancellable = true)
private void modernfix$fasterTagIngredientStacking(CallbackInfoReturnable<IntList> cir) {
if (this.isVanilla() && this.values.length == 1 && this.values[0] instanceof Ingredient.TagValue tagValue) {
var tag = BuiltInRegistries.ITEM.getTag(tagValue.tag());
if (!tag.isPresent() || tag.get().size() == 0) {
return;
}
var list = new IntArrayList(tag.get().stream().mapToInt(h -> BuiltInRegistries.ITEM.getId(h.value())).toArray());
list.sort(IntComparators.NATURAL_COMPARATOR);
this.stackingIds = list;
cir.setReturnValue(list);
}
}
/**
* @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.
*/
@Overwrite
public ItemStack[] getItems() {
// For compatibility if mods explicitly force a set of item stacks to be used
if (this.itemStacks != null) {
return this.itemStacks;
}
// Fast path for case with one item
if (this.values.length == 1) {
if (this.values[0] instanceof Ingredient.ItemValue itemValue) {
return new ItemStack[] { itemValue.item() };
} else if (this.values[0] instanceof Ingredient.TagValue tagValue) {
var tag = BuiltInRegistries.ITEM.getTag(tagValue.tag());
if (tag.isPresent() && tag.get().size() > 0) {
var holderSet = tag.get();
ItemStack[] result = new ItemStack[holderSet.size()];
for (int i = 0; i < result.length; i++) {
result[i] = new ItemStack(holderSet.get(i));
}
return result;
}
}
}
ArrayList<ItemStack> itemList = new ArrayList<>(2);
for (var value : this.values) {
var collection = value.getItems();
itemList.ensureCapacity(collection.size() + itemList.size());
for (var item : collection) {
itemList.add(item);
}
}
return itemList.toArray(ItemStack[]::new);
}
}

View File

@ -1,41 +0,0 @@
package org.embeddedt.modernfix.neoforge.mixin.perf.model_optimizations;
import net.minecraft.resources.ResourceLocation;
import net.neoforged.neoforge.client.model.obj.ObjLoader;
import net.neoforged.neoforge.client.model.obj.ObjMaterialLibrary;
import net.neoforged.neoforge.client.model.obj.ObjModel;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Mixin(ObjLoader.class)
@ClientOnlyMixin
public class OBJLoaderMixin {
/* TODO: Remove if unnecessary
@Final
@Mutable
@Shadow(remap = false) private Map<ResourceLocation, ObjMaterialLibrary> materialCache;
@Final
@Mutable
@Shadow(remap = false) private Map<ObjModel.ModelSettings, ObjModel> modelCache;
@Redirect(method = "<init>", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/neoforged/neoforge/client/model/obj/ObjLoader;materialCache:Ljava/util/Map;", remap = false))
private void useConcMap1(ObjLoader instance, Map<ResourceLocation, ObjMaterialLibrary> value) {
this.materialCache = new ConcurrentHashMap<>();
}
@Redirect(method = "<init>", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/neoforged/neoforge/client/model/obj/ObjLoader;modelCache:Ljava/util/Map;", remap = false))
private void useConcMap2(ObjLoader instance, Map<ResourceLocation, ObjMaterialLibrary> value) {
this.modelCache = new ConcurrentHashMap<>();
}
*/
}

View File

@ -0,0 +1,34 @@
package org.embeddedt.modernfix.neoforge.mixin.perf.smart_ingredient_sync;
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import net.minecraft.network.Connection;
import net.minecraft.network.ConnectionProtocol;
import net.minecraft.network.PacketSendListener;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundUpdateRecipesPacket;
import net.neoforged.neoforge.network.registration.NetworkRegistry;
import org.embeddedt.modernfix.neoforge.packet.SmartIngredientSyncPayload;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
@Mixin(Connection.class)
public class ConnectionMixin {
/**
* @author embeddedt
* @reason Provide context to the ingredient serializer about whether the enhanced sync protocol is supported.
*/
@WrapMethod(method = "doSendPacket")
private void modernfix$checkClientPresence(Packet<?> packet, PacketSendListener sendListener, boolean flush, Operation<Void> original) {
if (packet instanceof ClientboundUpdateRecipesPacket && NetworkRegistry.hasChannel((Connection)(Object)this, ConnectionProtocol.PLAY, SmartIngredientSyncPayload.TYPE.id())) {
SmartIngredientSyncPayload.CLIENT_HAS_SMART_INGREDIENT_SYNC.set(true);
try {
original.call(packet, sendListener, flush);
} finally {
SmartIngredientSyncPayload.CLIENT_HAS_SMART_INGREDIENT_SYNC.set(false);
}
} else {
original.call(packet, sendListener, flush);
}
}
}

View File

@ -0,0 +1,54 @@
package org.embeddedt.modernfix.neoforge.mixin.perf.smart_ingredient_sync;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.crafting.Ingredient;
import org.embeddedt.modernfix.neoforge.packet.SmartIngredientSyncPayload;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(targets = {"net/minecraft/world/item/crafting/Ingredient$1"})
public abstract class IngredientMixin {
@Inject(method = "encode(Lnet/minecraft/network/RegistryFriendlyByteBuf;Lnet/minecraft/world/item/crafting/Ingredient;)V",
at = @At(value = "FIELD", target = "Lnet/minecraft/world/item/ItemStack;LIST_STREAM_CODEC:Lnet/minecraft/network/codec/StreamCodec;"),
cancellable = true)
private void checkForVanillaTagIngredient(RegistryFriendlyByteBuf buf, Ingredient ingredient, CallbackInfo ci) {
if (!SmartIngredientSyncPayload.CLIENT_HAS_SMART_INGREDIENT_SYNC.get() || ingredient.isCustom()) {
return;
}
Ingredient.Value[] values = ingredient.getValues();
if (values.length == 1 && values[0] instanceof Ingredient.TagValue tagValue) {
var optionalHolderSet = BuiltInRegistries.ITEM.getTag(tagValue.tag());
if (optionalHolderSet.isEmpty()) {
// Use default serialization logic for tags that do not exist
return;
}
// Encode this as our tag ingredient type instead of using vanilla's flattening logic.
ci.cancel();
buf.writeVarInt(-2);
buf.writeResourceLocation(tagValue.tag().location());
}
}
@Inject(method = "decode(Lnet/minecraft/network/RegistryFriendlyByteBuf;)Lnet/minecraft/world/item/crafting/Ingredient;",
at = @At(value = "HEAD"),
cancellable = true, remap = false)
private void decodeSmartIngredient(RegistryFriendlyByteBuf buf, CallbackInfoReturnable<Ingredient> cir) {
int readerIndex = buf.readerIndex();
var sizeId = buf.readVarInt();
if (sizeId == -2) {
// Probably our ingredient
var tagKey = TagKey.create(Registries.ITEM, buf.readResourceLocation());
cir.setReturnValue(Ingredient.of(tagKey));
} else {
buf.readerIndex(readerIndex);
}
}
}

View File

@ -0,0 +1,21 @@
package org.embeddedt.modernfix.neoforge.packet;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
import org.embeddedt.modernfix.ModernFix;
public enum SmartIngredientSyncPayload implements CustomPacketPayload {
INSTANCE;
public static final ThreadLocal<Boolean> CLIENT_HAS_SMART_INGREDIENT_SYNC = ThreadLocal.withInitial(() -> false);
public static final CustomPacketPayload.Type<SmartIngredientSyncPayload> TYPE = new CustomPacketPayload.Type<>(ResourceLocation.fromNamespaceAndPath(ModernFix.MODID, "ingredient_sync"));
public static final StreamCodec<RegistryFriendlyByteBuf, SmartIngredientSyncPayload> STREAM_CODEC = StreamCodec.unit(INSTANCE);
@Override
public Type<? extends CustomPacketPayload> type() {
return TYPE;
}
}