88import net .minecraft .world .item .ItemStackLinkedSet ;
99import net .minecraft .world .item .crafting .Ingredient ;
1010import net .neoforged .neoforge .common .crafting .ICustomIngredient ;
11+ import org .embeddedt .modernfix .neoforge .load .MinecraftServerReloadTracker ;
1112import org .embeddedt .modernfix .neoforge .recipe .ExtendedIngredient ;
1213import org .embeddedt .modernfix .neoforge .recipe .IngredientItemStacksSoftReference ;
1314import org .jetbrains .annotations .Nullable ;
@@ -43,13 +44,32 @@ private boolean isVanilla() {
4344
4445 private volatile IngredientItemStacksSoftReference mfix$cachedItemStacks ;
4546
47+ /**
48+ * Minecraft's server resource loading process has a design flaw in that tags are loaded, recipes are loaded,
49+ * then tags are bound to the server registries. This results in recipe modification mods like KubeJS/CraftTweaker
50+ * not being able to use Ingredient.test reliably as the TagValue will not find any contents for the given tag.
51+ * To work around this issue these mods track tag context themselves and then patch TagValue.getItems to use it
52+ * during the resource reload process. We often bypass Value.getItems, so we must disable that bypass
53+ * whenever a server reload is in progress.
54+ * <p>
55+ * An alternative fix would be to bind tags ourselves when the recipe manager reload begins, before control is
56+ * handed to these mods. However, it's unclear if there would be any negative side effects from binding tags early
57+ * like this. Moreover, this fix would only work if the mod-provided patches to getItems read exactly what the
58+ * registry would normally contain, rather than a modified version.
59+ * <p>
60+ * Note: this is a separate problem from the issue where clients may receive recipes before tags in 1.21.
61+ */
62+ private boolean mfix$areTagsAvailable () {
63+ return !MinecraftServerReloadTracker .isReloadActive ();
64+ }
65+
4666 /**
4767 * @author embeddedt
4868 * @reason tag ingredients can be tested without iterating over all items
4969 */
5070 @ 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 )
5171 private void modernfix$fasterTagIngredientTest (ItemStack stack , CallbackInfoReturnable <Boolean > cir ) {
52- if (this .isVanilla () && this .values .length == 1 && this .values [0 ] instanceof Ingredient .TagValue tagValue ) {
72+ if (this .isVanilla () && this .values .length == 1 && this .values [0 ] instanceof Ingredient .TagValue tagValue && mfix$areTagsAvailable () ) {
5373 cir .setReturnValue (stack .getItemHolder ().is (tagValue .tag ()));
5474 }
5575 }
@@ -75,7 +95,7 @@ private boolean containsItems() {
7595 for (Ingredient .Value value : this .values ) {
7696 if (value instanceof Ingredient .ItemValue ) {
7797 return true ;
78- } else if (value instanceof Ingredient .TagValue tagValue ) {
98+ } else if (value instanceof Ingredient .TagValue tagValue && mfix$areTagsAvailable () ) {
7999 var holderSetOpt = BuiltInRegistries .ITEM .getTag (tagValue .tag ());
80100 if (holderSetOpt .isPresent () && holderSetOpt .get ().size () > 0 ) {
81101 return true ;
@@ -99,7 +119,7 @@ private boolean containsItems() {
99119 */
100120 @ Inject (method = "getStackingIds" , at = @ At (value = "INVOKE" , target = "Lnet/minecraft/world/item/crafting/Ingredient;getItems()[Lnet/minecraft/world/item/ItemStack;" ), cancellable = true )
101121 private void modernfix$fasterTagIngredientStacking (CallbackInfoReturnable <IntList > cir ) {
102- if (this .isVanilla () && this .values .length == 1 && this .values [0 ] instanceof Ingredient .TagValue tagValue ) {
122+ if (this .isVanilla () && this .values .length == 1 && this .values [0 ] instanceof Ingredient .TagValue tagValue && mfix$areTagsAvailable () ) {
103123 var tag = BuiltInRegistries .ITEM .getTag (tagValue .tag ());
104124 if (!tag .isPresent () || tag .get ().size () == 0 ) {
105125 return ;
@@ -144,7 +164,7 @@ private ItemStack[] computeItemsArray() {
144164 if (this .values .length == 1 ) {
145165 if (this .values [0 ] instanceof Ingredient .ItemValue itemValue ) {
146166 return new ItemStack [] { itemValue .item () };
147- } else if (this .values [0 ] instanceof Ingredient .TagValue tagValue ) {
167+ } else if (this .values [0 ] instanceof Ingredient .TagValue tagValue && mfix$areTagsAvailable () ) {
148168 var tag = BuiltInRegistries .ITEM .getTag (tagValue .tag ());
149169 if (tag .isPresent () && tag .get ().size () > 0 ) {
150170 var holderSet = tag .get ();
0 commit comments