Skip to content

Commit 562a059

Browse files
committed
rework SpongeShapelessRecipe#matches to use vanilla matching algorithm
1 parent bbfa92e commit 562a059

File tree

7 files changed

+171
-81
lines changed

7 files changed

+171
-81
lines changed

src/accessors/resources/mixins.sponge.accessors.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,6 @@
145145
"world.item.component.CustomDataAccessor",
146146
"world.item.context.UseOnContextAccessor",
147147
"world.item.crafting.RecipeMapAccessor",
148-
"world.item.crafting.ShapelessRecipeAccessor",
149148
"world.item.enchantment.ItemEnchantmentsAccessor",
150149
"world.item.trading.MerchantOfferAccessor",
151150
"world.level.BaseCommandBlockAccessor",
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* This file is part of Sponge, licensed under the MIT License (MIT).
3+
*
4+
* Copyright (c) SpongePowered <https://www.spongepowered.org>
5+
* Copyright (c) contributors
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
package org.spongepowered.common.bridge.world.item.crafting;
26+
27+
import net.minecraft.world.entity.player.StackedContents;
28+
import net.minecraft.world.item.ItemStack;
29+
30+
public interface CraftingInputBridge {
31+
32+
StackedContents<ItemStack> bridge$getItemStackStackedContents();
33+
}
Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,14 @@
2222
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2323
* THE SOFTWARE.
2424
*/
25-
package org.spongepowered.common.accessor.world.item.crafting;
25+
package org.spongepowered.common.bridge.world.item.crafting;
2626

27-
import net.minecraft.world.item.crafting.Ingredient;
28-
import net.minecraft.world.item.crafting.ShapelessRecipe;
29-
import org.spongepowered.asm.mixin.Mixin;
30-
import org.spongepowered.asm.mixin.gen.Accessor;
27+
import net.minecraft.world.entity.player.StackedContents;
28+
import net.minecraft.world.item.ItemStack;
3129

3230
import java.util.List;
3331

34-
@Mixin(ShapelessRecipe.class)
35-
public interface ShapelessRecipeAccessor {
36-
@Accessor("ingredients") List<Ingredient> accessor$ingredients();
32+
public interface PlacementInfoBridge {
33+
34+
List<StackedContents.IngredientInfo<ItemStack>> bridge$getStackIngredientInfos();
3735
}

src/main/java/org/spongepowered/common/item/recipe/crafting/shapeless/SpongeShapelessRecipe.java

Lines changed: 13 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,11 @@
3232
import net.minecraft.world.item.crafting.Ingredient;
3333
import net.minecraft.world.item.crafting.ShapelessRecipe;
3434
import net.minecraft.world.level.Level;
35-
import org.spongepowered.common.accessor.world.item.crafting.ShapelessRecipeAccessor;
35+
import org.spongepowered.common.bridge.world.item.crafting.CraftingInputBridge;
36+
import org.spongepowered.common.bridge.world.item.crafting.PlacementInfoBridge;
3637
import org.spongepowered.common.item.recipe.ingredient.SpongeIngredient;
3738

38-
import java.util.ArrayList;
39-
import java.util.Collection;
40-
import java.util.Comparator;
41-
import java.util.HashMap;
42-
import java.util.HashSet;
4339
import java.util.List;
44-
import java.util.Map;
45-
import java.util.Set;
4640
import java.util.function.Function;
4741

4842
/**
@@ -68,11 +62,19 @@ public SpongeShapelessRecipe(final String groupIn,
6862
}
6963

7064
@Override
71-
public boolean matches(final CraftingInput $$0, final Level $$1) {
65+
public boolean matches(final CraftingInput input, final Level $$1) {
7266
if (this.onlyVanillaIngredients) {
73-
return super.matches($$0, $$1);
67+
return super.matches(input, $$1);
7468
}
75-
return SpongeShapelessRecipe.matches($$0, ((ShapelessRecipeAccessor) this).accessor$ingredients());
69+
70+
// Quick check to avoid complex calculations if possible
71+
if (input.ingredientCount() != this.placementInfo().ingredients().size()) {
72+
// The amount of non-empty stacks doesn't match the amount of ingredients
73+
return false;
74+
}
75+
76+
return ((CraftingInputBridge) input).bridge$getItemStackStackedContents().tryPick(
77+
((PlacementInfoBridge) this.placementInfo()).bridge$getStackIngredientInfos(), 1, null);
7678
}
7779

7880
@Override
@@ -91,65 +93,4 @@ public ItemStack assemble(final CraftingInput $$0, final HolderLookup.Provider $
9193
}
9294
return super.assemble($$0, $$1);
9395
}
94-
95-
private static boolean matches(final CraftingInput input, final List<Ingredient> ingredients) {
96-
final int elements = ingredients.size();
97-
if (input.ingredientCount() != elements) {
98-
// The amount of non-empty stacks doesn't match the amount of ingredients
99-
return false;
100-
}
101-
102-
// This can contain empty stacks
103-
// Probably would be better to store non-empty stack list on CraftingInput
104-
final List<ItemStack> stacks = input.items();
105-
// find matched stack -> ingredient list
106-
final Map<Integer, List<Integer>> matchesMap = new HashMap<>();
107-
for (int i = 0; i < elements; i++) {
108-
final Ingredient ingredient = ingredients.get(i);
109-
boolean noMatch = true;
110-
for (int j = 0; j < stacks.size(); j++) {
111-
final ItemStack stack = stacks.get(j);
112-
if (!stack.isEmpty() && ingredient.test(stack)) {
113-
matchesMap.computeIfAbsent(j, k -> new ArrayList<>()).add(i);
114-
noMatch = false;
115-
}
116-
}
117-
if (noMatch) {
118-
// One ingredient had no matching stack
119-
return false;
120-
}
121-
}
122-
123-
if (matchesMap.size() != elements) {
124-
// At least one stack had no matching ingredient
125-
return false;
126-
}
127-
128-
// Every ingredient had at least one matching stack
129-
// Now check if each stack matches one ingredient
130-
final List<Collection<Integer>> stackList = new ArrayList<>(matchesMap.values());
131-
stackList.sort(Comparator.comparingInt(Collection::size));
132-
return SpongeShapelessRecipe.matchesRecursive(stackList, 0, new HashSet<>());
133-
}
134-
135-
private static boolean matchesRecursive(List<Collection<Integer>> stackList, int d, Set<Integer> used) {
136-
if (d == stackList.size()) {
137-
return true;
138-
}
139-
140-
final Collection<Integer> stacks = stackList.get(d);
141-
for (Integer stack : stacks) {
142-
if (used.contains(stack)) {
143-
// each stack is only used once
144-
continue;
145-
}
146-
final HashSet<Integer> copy = new HashSet<>(used);
147-
copy.add(stack);
148-
if (SpongeShapelessRecipe.matchesRecursive(stackList, d + 1, copy)) {
149-
return true;
150-
}
151-
}
152-
return false;
153-
}
154-
15596
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* This file is part of Sponge, licensed under the MIT License (MIT).
3+
*
4+
* Copyright (c) SpongePowered <https://www.spongepowered.org>
5+
* Copyright (c) contributors
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
package org.spongepowered.common.mixin.core.world.item.crafting;
26+
27+
import net.minecraft.world.entity.player.StackedContents;
28+
import net.minecraft.world.item.ItemStack;
29+
import net.minecraft.world.item.crafting.CraftingInput;
30+
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
31+
import org.spongepowered.asm.mixin.Final;
32+
import org.spongepowered.asm.mixin.Mixin;
33+
import org.spongepowered.asm.mixin.Shadow;
34+
import org.spongepowered.common.bridge.world.item.crafting.CraftingInputBridge;
35+
36+
import java.util.List;
37+
38+
@Mixin(CraftingInput.class)
39+
public abstract class CraftingInputMixin implements CraftingInputBridge {
40+
41+
@Shadow @Final private List<ItemStack> items;
42+
43+
private @MonotonicNonNull StackedContents<ItemStack> impl$itemStackStackedContents;
44+
45+
@Override
46+
public StackedContents<ItemStack> bridge$getItemStackStackedContents() {
47+
// This is only called by sponge recipes in some cases
48+
// so we don't need to calculate it for each CraftingInput
49+
if (this.impl$itemStackStackedContents == null) {
50+
this.impl$itemStackStackedContents = new StackedContents<>();
51+
for (final ItemStack stack : this.items) {
52+
if (!stack.isEmpty()) {
53+
this.impl$itemStackStackedContents.account(stack, 1);
54+
}
55+
}
56+
}
57+
return this.impl$itemStackStackedContents;
58+
}
59+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* This file is part of Sponge, licensed under the MIT License (MIT).
3+
*
4+
* Copyright (c) SpongePowered <https://www.spongepowered.org>
5+
* Copyright (c) contributors
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
package org.spongepowered.common.mixin.core.world.item.crafting;
26+
27+
import net.minecraft.world.entity.player.StackedContents;
28+
import net.minecraft.world.item.ItemStack;
29+
import net.minecraft.world.item.crafting.Ingredient;
30+
import net.minecraft.world.item.crafting.PlacementInfo;
31+
import org.spongepowered.asm.mixin.Final;
32+
import org.spongepowered.asm.mixin.Mixin;
33+
import org.spongepowered.asm.mixin.Shadow;
34+
import org.spongepowered.asm.mixin.injection.At;
35+
import org.spongepowered.asm.mixin.injection.Inject;
36+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
37+
import org.spongepowered.common.bridge.world.item.crafting.PlacementInfoBridge;
38+
39+
import java.util.List;
40+
41+
@Mixin(PlacementInfo.class)
42+
public abstract class PlacementInfoMixin implements PlacementInfoBridge {
43+
44+
@Shadow @Final private List<Ingredient> ingredients;
45+
46+
private List<StackedContents.IngredientInfo<ItemStack>> impl$stackIngredientInfos;
47+
48+
@Inject(method = "<init>", at = @At("RETURN"))
49+
private void impl$setIngredientInfos(final CallbackInfo ci) {
50+
this.impl$stackIngredientInfos = this.ingredients.stream()
51+
.<StackedContents.IngredientInfo<ItemStack>>map(ingredient -> ingredient::test)
52+
.toList();
53+
}
54+
55+
public List<StackedContents.IngredientInfo<ItemStack>> bridge$getStackIngredientInfos() {
56+
return this.impl$stackIngredientInfos;
57+
}
58+
}

src/mixins/resources/mixins.sponge.core.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,9 @@
210210
"world.item.ServerItemCooldownsMixin",
211211
"world.item.TeleportRandomlyConsumeEffectMixin",
212212
"world.item.crafting.AbstractCookingRecipeMixin",
213+
"world.item.crafting.CraftingInputMixin",
213214
"world.item.crafting.IngredientMixin",
215+
"world.item.crafting.PlacementInfoMixin",
214216
"world.item.crafting.RecipeManagerMixin",
215217
"world.item.crafting.ShapedRecipeMixin",
216218
"world.item.crafting.ShapelessRecipeMixin",

0 commit comments

Comments
 (0)