Skip to content

Commit 97bb943

Browse files
authored
NBT Predicate Ingredient (#4175)
1 parent 8373cb0 commit 97bb943

File tree

22 files changed

+1356
-46
lines changed

22 files changed

+1356
-46
lines changed

docs/content/Modpacks/Changes/v7.3.0.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@
22
title: "Version 7.3.0"
33
---
44

5-
65
# Updating from `7.2.1` to `7.3.0`
76

87
## Diluted Sulfuric and Hydrochloric Acid
9-
108
Previously, Distilling Diluted H2SO4 and HCl back into usable acid was done on Circuit 1 for H2SO4 but on Circuit 2 for
119
HCl. These have been unified to both use Circuit 1. This will (very mildly) break existing bases.
1210

1311
## Chance Logics
1412

1513
- `first` ChanceLogic has been Deprecated as its behavior is highly unreliable, and it will be fully removed in 8.0.0.
16-
We recommend using `xor` instead.
14+
We recommend using `xor` instead.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
title: "Version 7.4.0"
3+
---
4+
5+
6+
# Updating from `7.3.0` to `7.4.0`
7+
8+
## Added NBTPredicateIngredients
9+
Added a type of ingredient to do more careful pattern matching on ItemStack NBT data, see [NBT Predicate Ingredient](../Recipes/Ingredients/NBT-Predicate-Ingredients.md).

docs/content/Modpacks/Recipes/Adding-and-Removing-Recipes.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,6 @@ ServerEvents.recipes(event => {
195195
.EUt(30)
196196
})
197197
```
198+
199+
### More custom ingredients
200+
For more custom ingredients, see the [Ingredients list in the sidebar](Ingredients/index.md)
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
---
2+
title: "NBT Predicate Ingredients"
3+
---
4+
5+
For some use-cases, using Partial or Strict NBT Ingredients does not give enough control. For this, we have NBT Predicate Ingredients.
6+
This system allows you to query NBT contents during recipe matching to validate more advanced queries on ItemStacks.
7+
8+
!!! note
9+
To test your items in-game, you can use the give and ftblibrary commands, e.g. `/give @p dirt{"attributes": {"strength":16, "sound":"crunch.wav" } }` to give yourself an item with custom NBT or `/ftblibrary nbtedit hand` for a graphical editor
10+
## Usage
11+
### Equals
12+
For JavaScript, custom overloads were made:
13+
14+
- `.eqString(key, value)`
15+
- `.eqInt(key, value)`
16+
- `.eqFloat(key, value)`
17+
- `.eqByte(key, value)`
18+
- `.eqDouble(key, value)`
19+
- `.eqTag(key, value)`
20+
21+
All of these also have an `.neq[...](key, value)` function.
22+
In Java, these are also available, as well as simpler `.[n]eq(key, [type] value)` overloads.
23+
24+
25+
=== "JavaScript"
26+
```js title="gt_recipes.js"
27+
28+
ServerEvents.recipes(event => {
29+
event.recipes.gtceu.assembler('test_nbt')
30+
.inputItemNbtPredicate('minecraft:dirt', NBTPredicates.eqString("charge", "23"))
31+
.itemOutputs('minecraft:stick')
32+
.duration(100)
33+
.EUt(30)
34+
})
35+
36+
```
37+
38+
=== "Java"
39+
```java title="GTRecipes.java"
40+
41+
public static void init(Consumer<FinishedRecipe> provider) {
42+
ASSEMBLER_RECIPES.recipeBuilder("test_nbt")
43+
.inputItemNbtPredicate(new ItemStack(Items.dirt, 1), NBTPredicates.eq("charge", "23"))
44+
.outputItems(new ItemStack(Items.STICK))
45+
.duration(100)
46+
.EUt(30)
47+
.save(provider);
48+
}
49+
50+
```
51+
52+
### Number Comparison
53+
The following number comparison operators exist:
54+
55+
- `.lte(key, number)`: Less Than or Equal to
56+
- `.lt(key, number)`: Less Than
57+
- `.gte(key, number)`: Greater Than or Equal to
58+
- `.gt(key, number)`: Greater Than
59+
60+
=== "JavaScript"
61+
```js title="gt_recipes.js"
62+
63+
ServerEvents.recipes(event => {
64+
event.recipes.gtceu.assembler('test_nbt')
65+
.inputItemNbtPredicate('minecraft:dirt', NBTPredicates.lt("charge", 23))
66+
.itemOutputs('minecraft:stick')
67+
.duration(100)
68+
.EUt(30)
69+
})
70+
71+
```
72+
73+
=== "Java"
74+
```java title="GTRecipes.java"
75+
76+
public static void init(Consumer<FinishedRecipe> provider) {
77+
ASSEMBLER_RECIPES.recipeBuilder("test_nbt")
78+
.inputItemNbtPredicate(new ItemStack(Items.dirt, 1), NBTPredicates.lt("charge", 23))
79+
.outputItems(new ItemStack(Items.STICK))
80+
.duration(100)
81+
.EUt(30)
82+
.save(provider);
83+
}
84+
85+
```
86+
87+
88+
### Any/All
89+
The following list operators exist:
90+
91+
- `.all(NBTPredicate...)`
92+
- `.any(NBTPredicate...)`
93+
94+
=== "JavaScript"
95+
```js title="gt_recipes.js"
96+
97+
ServerEvents.recipes(event => {
98+
event.recipes.gtceu.assembler('test_nbt')
99+
.inputItemNbtPredicate('minecraft:dirt',
100+
NBTPredicates.all([
101+
NBTPredicates.lt("charge", 23),
102+
NBTPredicates.eqString("color", "blue")
103+
]))
104+
.itemOutputs('minecraft:stick')
105+
.duration(100)
106+
.EUt(30)
107+
})
108+
109+
```
110+
111+
=== "Java"
112+
```java title="GTRecipes.java"
113+
114+
public static void init(Consumer<FinishedRecipe> provider) {
115+
ASSEMBLER_RECIPES.recipeBuilder("test_nbt")
116+
.inputItemNbtPredicate(new ItemStack(Items.dirt, 1),
117+
NBTPredicates.all([
118+
NBTPredicates.lt("charge", 23),
119+
NBTPredicates.eqString("color", "blue")
120+
]))
121+
.outputItems(new ItemStack(Items.STICK))
122+
.duration(100)
123+
.EUt(30)
124+
.save(provider);
125+
}
126+
127+
```
128+
129+
130+
### Not
131+
The negation operators exists:
132+
133+
- `.not(NBTPredicate)`
134+
135+
136+
=== "JavaScript"
137+
```js title="gt_recipes.js"
138+
139+
ServerEvents.recipes(event => {
140+
event.recipes.gtceu.assembler('test_nbt')
141+
.inputItemNbtPredicate(new ItemStack(Items.dirt, 1),
142+
NBTPredicates.not(
143+
NBTPredicates.all([
144+
NBTPredicates.lt("charge", 23),
145+
NBTPredicates.eqString("color", "blue")
146+
])
147+
)
148+
)
149+
.itemOutputs('minecraft:stick')
150+
.duration(100)
151+
.EUt(30)
152+
})
153+
154+
```
155+
156+
=== "Java"
157+
```java title="GTRecipes.java"
158+
159+
public static void init(Consumer<FinishedRecipe> provider) {
160+
ASSEMBLER_RECIPES.recipeBuilder("test_nbt")
161+
.inputItemNbtPredicate(new ItemStack(Items.dirt, 1),
162+
NBTPredicates.not(
163+
NBTPredicates.all([
164+
NBTPredicates.lt("charge", 23),
165+
NBTPredicates.eqString("color", "blue")
166+
])
167+
)
168+
)
169+
.outputItems(new ItemStack(Items.STICK))
170+
.duration(100)
171+
.EUt(30)
172+
.save(provider);
173+
}
174+
175+
```
176+
177+
178+
### Key Navigation
179+
You can use `.` to navigate nested tags, and `[i]` to index into lists. so
180+
```
181+
{ "machine":
182+
{ "states" :
183+
[
184+
{"color": "green"},
185+
{"color": "red"},
186+
]
187+
}
188+
}
189+
```
190+
would match
191+
`.eq("machine.states[0].color", "green")`
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
title: "Ingredients"
3+
---
4+
5+
These pages contain information about custom ingredients to be used in your recipes.

src/main/java/com/gregtechceu/gtceu/api/capability/recipe/ItemRecipeCapability.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public List<Object> compressIngredients(Collection<Object> ingredients) {
119119
break;
120120
}
121121
} else if (obj instanceof ItemStack stack1) {
122-
if (ItemStack.isSameItem(stack, stack1)) {
122+
if (ItemStack.isSameItemSameTags(stack, stack1)) {
123123
isEqual = true;
124124
break;
125125
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package com.gregtechceu.gtceu.api.recipe.ingredient;
2+
3+
import com.gregtechceu.gtceu.GTCEu;
4+
import com.gregtechceu.gtceu.api.recipe.ingredient.nbtpredicate.NBTPredicate;
5+
import com.gregtechceu.gtceu.api.recipe.ingredient.nbtpredicate.NBTPredicates;
6+
import com.gregtechceu.gtceu.api.recipe.ingredient.nbtpredicate.TrueNBTPredicate;
7+
8+
import net.minecraft.network.FriendlyByteBuf;
9+
import net.minecraft.resources.ResourceLocation;
10+
import net.minecraft.util.GsonHelper;
11+
import net.minecraft.world.item.ItemStack;
12+
import net.minecraft.world.item.crafting.Ingredient;
13+
import net.minecraftforge.common.crafting.AbstractIngredient;
14+
import net.minecraftforge.common.crafting.CraftingHelper;
15+
import net.minecraftforge.common.crafting.IIngredientSerializer;
16+
import net.minecraftforge.registries.ForgeRegistries;
17+
18+
import com.google.gson.JsonElement;
19+
import com.google.gson.JsonObject;
20+
import org.jetbrains.annotations.NotNull;
21+
import org.jetbrains.annotations.Nullable;
22+
23+
import java.util.stream.Stream;
24+
25+
public class NBTPredicateIngredient extends AbstractIngredient {
26+
27+
public static final ResourceLocation TYPE = GTCEu.id("nbt_predicate");
28+
public static final NBTPredicate ALWAYS_TRUE = new TrueNBTPredicate();
29+
private final NBTPredicate predicate;
30+
private final ItemStack stack;
31+
32+
public NBTPredicateIngredient(ItemStack stack, NBTPredicate predicate) {
33+
super(Stream.of(new Ingredient.ItemValue(stack)));
34+
this.stack = stack;
35+
this.predicate = predicate;
36+
}
37+
38+
protected NBTPredicateIngredient(ItemStack stack) {
39+
this(stack, ALWAYS_TRUE);
40+
}
41+
42+
public static NBTPredicateIngredient of(ItemStack stack, NBTPredicate predicate) {
43+
return new NBTPredicateIngredient(stack, predicate);
44+
}
45+
46+
public static NBTPredicateIngredient of(ItemStack stack) {
47+
return NBTPredicateIngredient.of(stack, ALWAYS_TRUE);
48+
}
49+
50+
public boolean test(@Nullable ItemStack input) {
51+
if (input == null) {
52+
return false;
53+
} else {
54+
return this.stack.getItem() == input.getItem() &&
55+
predicate.test(input.getOrCreateTag());
56+
}
57+
}
58+
59+
public boolean isSimple() {
60+
return false;
61+
}
62+
63+
public @NotNull IIngredientSerializer<? extends Ingredient> getSerializer() {
64+
return NBTPredicateIngredient.Serializer.INSTANCE;
65+
}
66+
67+
public JsonElement toJson() {
68+
JsonObject json = new JsonObject();
69+
json.addProperty("type", TYPE.toString());
70+
json.addProperty("item", ForgeRegistries.ITEMS.getKey(this.stack.getItem()).toString());
71+
json.addProperty("count", this.stack.getCount());
72+
if (this.stack.hasTag()) {
73+
json.addProperty("nbt", this.stack.getTag().toString());
74+
}
75+
json.add("predicate", predicate.toJson());
76+
return json;
77+
}
78+
79+
public static class Serializer implements IIngredientSerializer<NBTPredicateIngredient> {
80+
81+
public static final NBTPredicateIngredient.Serializer INSTANCE = new NBTPredicateIngredient.Serializer();
82+
83+
public @NotNull NBTPredicateIngredient parse(FriendlyByteBuf buffer) {
84+
var stack = buffer.readItem();
85+
var json = buffer.readUtf();
86+
var predicate = NBTPredicates.fromJson(GsonHelper.parse(json));
87+
return new NBTPredicateIngredient(stack, predicate);
88+
}
89+
90+
public @NotNull NBTPredicateIngredient parse(@NotNull JsonObject json) {
91+
var stack = CraftingHelper.getItemStack(json, true);
92+
var predicate = NBTPredicates.fromJson(GsonHelper.getAsJsonObject(json, "predicate"));
93+
94+
return new NBTPredicateIngredient(stack, predicate);
95+
}
96+
97+
public void write(FriendlyByteBuf buffer, NBTPredicateIngredient ingredient) {
98+
buffer.writeItem(ingredient.stack);
99+
buffer.writeUtf(ingredient.toJson().toString());
100+
}
101+
}
102+
}

0 commit comments

Comments
 (0)