Sync tag ingredients directly if ModernFix is installed on both sides

This commit is contained in:
embeddedt 2025-05-01 16:32:21 -04:00
parent 536eb03b50
commit 01d2582c44
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
5 changed files with 160 additions and 9 deletions

View File

@ -66,5 +66,6 @@ accessible field net/minecraft/server/packs/resources/ProfiledReloadInstance$Sta
accessible field net/minecraft/server/packs/resources/ProfiledReloadInstance$State reloadNanos Ljava/util/concurrent/atomic/AtomicLong;
accessible class net/minecraft/world/item/crafting/Ingredient$Value
accessible field net/minecraft/world/item/crafting/Ingredient$TagValue tag Lnet/minecraft/tags/TagKey;
accessible class net/minecraft/world/item/crafting/Ingredient$ItemValue
accessible class net/minecraft/client/searchtree/SearchRegistry$TreeEntry

View File

@ -0,0 +1,33 @@
package org.embeddedt.modernfix.forge.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 org.embeddedt.modernfix.forge.packet.PacketHandler;
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, @Nullable PacketSendListener sendListener, ConnectionProtocol newProtocol, ConnectionProtocol currentProtocol, Operation<Void> original) {
if (packet instanceof ClientboundUpdateRecipesPacket && PacketHandler.INGREDIENT_SYNC.isRemotePresent((Connection)(Object)this)) {
PacketHandler.CLIENT_HAS_SMART_INGREDIENT_SYNC.set(true);
try {
original.call(packet, sendListener, newProtocol, currentProtocol);
} finally {
PacketHandler.CLIENT_HAS_SMART_INGREDIENT_SYNC.set(false);
}
} else {
original.call(packet, sendListener, newProtocol, currentProtocol);
}
}
}

View File

@ -0,0 +1,63 @@
package org.embeddedt.modernfix.forge.mixin.perf.smart_ingredient_sync;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.crafting.Ingredient;
import org.embeddedt.modernfix.forge.packet.PacketHandler;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
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.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(Ingredient.class)
public abstract class IngredientMixin {
@Shadow public abstract boolean isVanilla();
@Shadow @Final private Ingredient.Value[] values;
@Unique
private static final ResourceLocation MODERNFIX_TAG_VALUE = new ResourceLocation("modernfix", "tag_value");
@Inject(method = "toNetwork",
at = @At("HEAD"),
cancellable = true)
private void checkForVanillaTagIngredient(FriendlyByteBuf buf, CallbackInfo ci) {
if (!PacketHandler.CLIENT_HAS_SMART_INGREDIENT_SYNC.get() || !this.isVanilla()) {
return;
}
Ingredient.Value[] values = this.values;
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(-1);
buf.writeResourceLocation(MODERNFIX_TAG_VALUE);
buf.writeResourceLocation(tagValue.tag.location());
}
}
@Inject(method = "fromNetwork", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/FriendlyByteBuf;readResourceLocation()Lnet/minecraft/resources/ResourceLocation;", ordinal = 0), cancellable = true)
private static void deserializeModernFixTagValue(FriendlyByteBuf buffer, CallbackInfoReturnable<Ingredient> cir) {
int readerIndex = buffer.readerIndex();
var type = buffer.readResourceLocation();
if (!type.equals(MODERNFIX_TAG_VALUE)) {
// Allow Forge to read the original packet
buffer.readerIndex(readerIndex);
return;
}
var tag = buffer.readResourceLocation();
cir.setReturnValue(Ingredient.of(TagKey.create(Registries.ITEM, tag)));
}
}

View File

@ -0,0 +1,50 @@
package org.embeddedt.modernfix.forge.mixin.perf.smart_ingredient_sync;
import com.llamalad7.mixinextras.sugar.Local;
import com.llamalad7.mixinextras.sugar.Share;
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
import net.minecraft.network.Connection;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundUpdateRecipesPacket;
import net.minecraft.network.protocol.game.ClientboundUpdateTagsPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.server.players.PlayerList;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.forge.packet.PacketHandler;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.Slice;
@Mixin(PlayerList.class)
public class PlayerListMixin {
/**
* @author embeddedt
* @reason Ensure the tag packet is always sent before the recipe packet (like it is after /reload).
*/
@Redirect(method = "placeNewPlayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;send(Lnet/minecraft/network/protocol/Packet;)V"),
slice = @Slice(
from = @At(value = "NEW", target = "(Lnet/minecraft/server/players/PlayerList;Lnet/minecraft/server/level/ServerPlayer;)Lnet/minecraftforge/event/OnDatapackSyncEvent;"),
to = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/PlayerList;sendPlayerPermissionLevel(Lnet/minecraft/server/level/ServerPlayer;)V")
), expect = 2, allow = 2, require = 2)
private void modernfix$switchTagAndRecipeOrder(ServerGamePacketListenerImpl instance, Packet<?> packet, @Local(ordinal = 0, argsOnly = true) Connection netManager, @Local(ordinal = 0, argsOnly = true) ServerPlayer player, @Share("deferred") LocalRef<Packet<?>> pktRef) {
if (!(packet instanceof ClientboundUpdateRecipesPacket) && !(packet instanceof ClientboundUpdateTagsPacket)) {
throw new AssertionError("Mixin injected in wrong place");
}
if (!PacketHandler.INGREDIENT_SYNC.isRemotePresent(netManager)) {
instance.send(packet);
return;
}
if (packet instanceof ClientboundUpdateRecipesPacket) {
pktRef.set(packet);
} else {
ModernFix.LOGGER.info("Using enhanced recipe sync for player {}", player.getName().getString());
// send tags
instance.send(packet);
// send recipes
instance.send(pktRef.get());
}
}
}

View File

@ -13,17 +13,21 @@ import org.embeddedt.modernfix.packet.EntityIDSyncPacket;
import java.util.function.Supplier;
public class PacketHandler {
private static final String PROTOCOL_VERSION = "1";
public static final SimpleChannel INSTANCE = NetworkRegistry.newSimpleChannel(
new ResourceLocation(ModernFix.MODID, "main"),
() -> PROTOCOL_VERSION,
NetworkRegistry.acceptMissingOr(PROTOCOL_VERSION),
NetworkRegistry.acceptMissingOr(PROTOCOL_VERSION)
);
public static final SimpleChannel INSTANCE = buildChannel("main", "1");
public static final SimpleChannel INGREDIENT_SYNC = buildChannel("ingredient_sync", "1");
public static final ThreadLocal<Boolean> CLIENT_HAS_SMART_INGREDIENT_SYNC = ThreadLocal.withInitial(() -> false);
private static SimpleChannel buildChannel(String name, String version) {
return NetworkRegistry.newSimpleChannel(
new ResourceLocation(ModernFix.MODID, name),
() -> version,
NetworkRegistry.acceptMissingOr(version),
NetworkRegistry.acceptMissingOr(version)
);
}
public static void register() {
int id = 1;
INSTANCE.registerMessage(id++, EntityIDSyncPacket.class, EntityIDSyncPacket::serialize, EntityIDSyncPacket::deserialize, PacketHandler::handleSyncPacket);
INSTANCE.registerMessage(1, EntityIDSyncPacket.class, EntityIDSyncPacket::serialize, EntityIDSyncPacket::deserialize, PacketHandler::handleSyncPacket);
}
private static void handleSyncPacket(EntityIDSyncPacket packet, Supplier<NetworkEvent.Context> contextSupplier) {