diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/CustomIngredientImpl.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/CustomIngredientImpl.java index e88a593b7f..53ed87ad08 100644 --- a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/CustomIngredientImpl.java +++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/CustomIngredientImpl.java @@ -85,7 +85,7 @@ public CustomIngredientImpl(CustomIngredient customIngredient) { this.customIngredient = customIngredient; } - private List> getCustomMatchingItems() { + public List> getCustomMatchingItems() { if (customMatchingItems == null) { customMatchingItems = customIngredient.getMatchingItems().toList(); } diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/CustomIngredientPacketCodec.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/CustomIngredientPacketCodec.java index 7003d71b13..c4b4f81564 100644 --- a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/CustomIngredientPacketCodec.java +++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/CustomIngredientPacketCodec.java @@ -18,6 +18,8 @@ import java.util.Set; +import org.jetbrains.annotations.Nullable; + import net.minecraft.network.RegistryByteBuf; import net.minecraft.network.codec.PacketCodec; import net.minecraft.recipe.Ingredient; @@ -27,7 +29,7 @@ import net.fabricmc.fabric.api.recipe.v1.ingredient.CustomIngredientSerializer; public class CustomIngredientPacketCodec implements PacketCodec { - private static final int PACKET_MARKER = -1; + static final int PACKET_MARKER = -1; private final PacketCodec fallback; public CustomIngredientPacketCodec(PacketCodec fallback) { @@ -72,7 +74,7 @@ public void encode(RegistryByteBuf buf, Ingredient value) { packetCodec.encode(buf, customIngredient); } - private static boolean shouldEncodeFallback(CustomIngredient customIngredient) { + static boolean shouldEncodeFallback(@Nullable CustomIngredient customIngredient) { if (customIngredient == null) { return true; } diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/OptionalCustomIngredientPacketCodec.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/OptionalCustomIngredientPacketCodec.java new file mode 100644 index 0000000000..f23da4a065 --- /dev/null +++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/OptionalCustomIngredientPacketCodec.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.impl.recipe.ingredient; + +import java.util.Optional; + +import net.minecraft.network.RegistryByteBuf; +import net.minecraft.network.codec.PacketCodec; +import net.minecraft.recipe.Ingredient; +import net.minecraft.util.Identifier; + +import net.fabricmc.fabric.api.recipe.v1.ingredient.CustomIngredient; +import net.fabricmc.fabric.api.recipe.v1.ingredient.CustomIngredientSerializer; + +public class OptionalCustomIngredientPacketCodec implements PacketCodec> { + private final PacketCodec> fallback; + + public OptionalCustomIngredientPacketCodec(PacketCodec> fallback) { + this.fallback = fallback; + } + + @Override + public Optional decode(RegistryByteBuf buf) { + int index = buf.readerIndex(); + + if (buf.readVarInt() != CustomIngredientPacketCodec.PACKET_MARKER) { + // Reset index for vanilla's normal deserialization logic. + buf.readerIndex(index); + return this.fallback.decode(buf); + } + + Identifier type = buf.readIdentifier(); + CustomIngredientSerializer serializer = CustomIngredientSerializer.get(type); + + if (serializer == null) { + throw new IllegalArgumentException("Cannot deserialize custom ingredient of unknown type " + type); + } + + return Optional.of(serializer.getPacketCodec().decode(buf).toVanilla()); + } + + @Override + @SuppressWarnings("unchecked") + public void encode(RegistryByteBuf buf, Optional value) { + if (value.isEmpty()) { + this.fallback.encode(buf, value); + return; + } + + CustomIngredient customIngredient = value.get().getCustomIngredient(); + + if (CustomIngredientPacketCodec.shouldEncodeFallback(customIngredient)) { + // The client doesn't support this custom ingredient, so we send the matching stacks as a regular ingredient. + this.fallback.encode(buf, value); + return; + } + + // The client supports this custom ingredient, so we send it as a custom ingredient. + buf.writeVarInt(CustomIngredientPacketCodec.PACKET_MARKER); + buf.writeIdentifier(customIngredient.getSerializer().getIdentifier()); + PacketCodec packetCodec = (PacketCodec) customIngredient.getSerializer().getPacketCodec(); + packetCodec.encode(buf, customIngredient); + } +} diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/mixin/recipe/ingredient/IngredientMixin.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/mixin/recipe/ingredient/IngredientMixin.java index f71996ab00..30df0bc8f4 100644 --- a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/mixin/recipe/ingredient/IngredientMixin.java +++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/mixin/recipe/ingredient/IngredientMixin.java @@ -16,6 +16,8 @@ package net.fabricmc.fabric.mixin.recipe.ingredient; +import java.util.Optional; + import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; @@ -39,6 +41,7 @@ import net.fabricmc.fabric.api.recipe.v1.ingredient.FabricIngredient; import net.fabricmc.fabric.impl.recipe.ingredient.CustomIngredientImpl; import net.fabricmc.fabric.impl.recipe.ingredient.CustomIngredientPacketCodec; +import net.fabricmc.fabric.impl.recipe.ingredient.OptionalCustomIngredientPacketCodec; @Mixin(Ingredient.class) public class IngredientMixin implements FabricIngredient { @@ -51,6 +54,30 @@ public class IngredientMixin implements FabricIngredient { @Final private RegistryEntryList entries; + @ModifyExpressionValue( + method = "", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/codec/PacketCodec;xmap(Ljava/util/function/Function;Ljava/util/function/Function;)Lnet/minecraft/network/codec/PacketCodec;", + ordinal = 0 + ) + ) + private static PacketCodec useCustomIngredientPacketCodec(PacketCodec original) { + return new CustomIngredientPacketCodec(original); + } + + @ModifyExpressionValue( + method = "", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/codec/PacketCodec;xmap(Ljava/util/function/Function;Ljava/util/function/Function;)Lnet/minecraft/network/codec/PacketCodec;", + ordinal = 1 + ) + ) + private static PacketCodec> useOptionalCustomIngredientPacketCodec(PacketCodec> original) { + return new OptionalCustomIngredientPacketCodec(original); + } + @Inject(method = "", at = @At("TAIL"), cancellable = true) private static void injectCodec(CallbackInfo ci) { Codec customIngredientCodec = CustomIngredientImpl.CODEC.dispatch( @@ -67,16 +94,14 @@ private static void injectCodec(CallbackInfo ci) { ); } - @ModifyExpressionValue( - method = "", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/network/codec/PacketCodec;xmap(Ljava/util/function/Function;Ljava/util/function/Function;)Lnet/minecraft/network/codec/PacketCodec;", - ordinal = 0 - ) - ) - private static PacketCodec useCustomIngredientPacketCodec(PacketCodec original) { - return new CustomIngredientPacketCodec(original); + // Targets the lambdas in the codecs which extract the entries from an ingredient. + // For custom ingredients, these lambdas will only be invoked when the client does not support this ingredient. + // In this case, use CustomIngredientImpl#getCustomMatchingItems, which as close as we can get. + @Inject(method = { "method_61673", "method_61677", "method_61680" }, at = @At("HEAD"), cancellable = true) + private static void onGetEntries(Ingredient ingredient, CallbackInfoReturnable> cir) { + if (ingredient instanceof CustomIngredientImpl customIngredient) { + cir.setReturnValue(RegistryEntryList.of(customIngredient.getCustomMatchingItems())); + } } @Inject(method = "equals(Ljava/lang/Object;)Z", at = @At("HEAD")) diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/mixin/recipe/ingredient/ShapelessRecipeMixin.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/mixin/recipe/ingredient/ShapelessRecipeMixin.java index 2b652936e7..fd5bbe9d0c 100644 --- a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/mixin/recipe/ingredient/ShapelessRecipeMixin.java +++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/mixin/recipe/ingredient/ShapelessRecipeMixin.java @@ -60,7 +60,7 @@ public void customIngredientMatch(CraftingRecipeInput recipeInput, World world, if (fabric_requiresTesting) { List nonEmptyStacks = new ArrayList<>(recipeInput.getStackCount()); - for (int i = 0; i < recipeInput.getStackCount(); ++i) { + for (int i = 0; i < recipeInput.size(); ++i) { ItemStack stack = recipeInput.getStackInSlot(i); if (!stack.isEmpty()) {