-
-
Notifications
You must be signed in to change notification settings - Fork 120
/
IRecipeManager.java
371 lines (313 loc) · 13.2 KB
/
IRecipeManager.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
package com.blamejared.crafttweaker.api.managers;
import com.blamejared.crafttweaker.CraftTweaker;
import com.blamejared.crafttweaker.api.CraftTweakerAPI;
import com.blamejared.crafttweaker.api.annotations.ZenRegister;
import com.blamejared.crafttweaker.api.brackets.CommandStringDisplayable;
import com.blamejared.crafttweaker.api.data.IData;
import com.blamejared.crafttweaker.api.item.IIngredient;
import com.blamejared.crafttweaker.api.item.IItemStack;
import com.blamejared.crafttweaker.api.zencode.impl.util.PositionUtil;
import com.blamejared.crafttweaker.impl.actions.recipes.ActionAddRecipe;
import com.blamejared.crafttweaker.impl.actions.recipes.ActionRemoveAll;
import com.blamejared.crafttweaker.impl.actions.recipes.ActionRemoveRecipe;
import com.blamejared.crafttweaker.impl.actions.recipes.ActionRemoveRecipeByModid;
import com.blamejared.crafttweaker.impl.actions.recipes.ActionRemoveRecipeByName;
import com.blamejared.crafttweaker.impl.actions.recipes.ActionRemoveRecipeByOutput;
import com.blamejared.crafttweaker.impl.actions.recipes.ActionRemoveRecipeByRegex;
import com.blamejared.crafttweaker.impl.data.MapData;
import com.blamejared.crafttweaker.impl.item.MCItemStackMutable;
import com.blamejared.crafttweaker.impl.managers.CTCraftingTableManager;
import com.blamejared.crafttweaker.impl.recipes.wrappers.WrapperRecipe;
import com.blamejared.crafttweaker.impl.util.NameUtils;
import com.blamejared.crafttweaker_annotations.annotations.Document;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.item.crafting.RecipeManager;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.ResourceLocationException;
import net.minecraft.util.registry.Registry;
import net.minecraftforge.registries.ForgeRegistries;
import org.openzen.zencode.java.ZenCodeType;
import org.openzen.zencode.shared.CodePosition;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Default interface for Registry based handlers as they can all remove recipes by ResourceLocation.
*
* @docParam this craftingTable
*/
@ZenRegister
@ZenCodeType.Name("crafttweaker.api.registries.IRecipeManager")
@Document("vanilla/api/managers/IRecipeManager")
public interface IRecipeManager extends CommandStringDisplayable {
Gson JSON_RECIPE_GSON = new GsonBuilder().create();
/**
* Adds a recipe based on a provided IData. The provided IData should represent a DataPack JSON, this effectively allows you to register recipes for any DataPack supporting IRecipeType systems.
*
* @param name name of the recipe
* @param data data representing the json file
*
* @docParam name "recipe_name"
* @docParam data {ingredient:{item:<item:minecraft:gold_ore>.registryName},result:<item:minecraft:cooked_porkchop>.registryName,experience:0.35 as float, cookingtime:100}
*/
@ZenCodeType.Method
default void addJSONRecipe(String name, IData data) {
name = validateRecipeName(name);
if(!(data instanceof MapData)) {
throw new IllegalArgumentException("Json recipe's IData should be a MapData!");
}
MapData mapData = (MapData) data;
JsonObject recipeObject = JSON_RECIPE_GSON.fromJson(mapData.toJsonString(), JsonObject.class);
ResourceLocation recipeTypeKey = getBracketResourceLocation();
if(recipeObject.has("type")) {
ResourceLocation recipeSerializerKey;
try {
recipeSerializerKey = new ResourceLocation(recipeObject.get("type").getAsString());
} catch(ClassCastException | IllegalStateException | ResourceLocationException ex) {
throw new IllegalArgumentException("Expected \"type\" field to be a valid resource location.", ex);
}
if(!ForgeRegistries.RECIPE_SERIALIZERS.containsKey(recipeSerializerKey)) {
throw new IllegalArgumentException("Recipe Serializer \"" + recipeSerializerKey + "\" does not exist.");
}
} else {
if(ForgeRegistries.RECIPE_SERIALIZERS.containsKey(recipeTypeKey)) {
recipeObject.addProperty("type", recipeTypeKey.toString());
} else {
throw new IllegalArgumentException("Recipe Type \"" + recipeTypeKey + "\" does not have a Recipe Serializer of the same ID."
+ " Please specify a serializer manually using the \"type\" field in the JSON object.");
}
}
IRecipe<?> iRecipe = RecipeManager.deserializeRecipe(new ResourceLocation(CraftTweaker.MODID, name), recipeObject);
IRecipeType<?> recipeType = iRecipe.getType();
if(recipeType != getRecipeType()) {
throw new IllegalArgumentException("Recipe Serializer \"" + iRecipe.getSerializer().getRegistryName()
+ "\" resulted in Recipe Type \"" + Registry.RECIPE_TYPE.getKey(recipeType)
+ "\" but expected Recipe Type \"" + recipeTypeKey + "\".");
}
CraftTweakerAPI.apply(new ActionAddRecipe(this, iRecipe, ""));
}
@ZenCodeType.Method
default WrapperRecipe getRecipeByName(String name) {
IRecipe<?> recipe = getRecipes().get(new ResourceLocation(name));
if(recipe == null) {
throw new IllegalArgumentException("No recipe found with name: \"" + name + "\" in type: \"" + getRecipeType().toString() + "\"");
}
return new WrapperRecipe(recipe);
}
@ZenCodeType.Method
default List<WrapperRecipe> getRecipesByOutput(IIngredient output) {
return getRecipes().values()
.stream()
.filter(iRecipe -> output.matches(new MCItemStackMutable(iRecipe.getRecipeOutput())))
.map(WrapperRecipe::new)
.collect(Collectors.toList());
}
@ZenCodeType.Method
@ZenCodeType.Getter("allRecipes")
default List<WrapperRecipe> getAllRecipes() {
return getRecipes().values().stream().map(WrapperRecipe::new).collect(Collectors.toList());
}
/**
* Returns a map of all known recipes.
*
* @return A Map of recipe name to recipe of all known recipes.
*/
@ZenCodeType.Method
@ZenCodeType.Getter("recipeMap")
default Map<ResourceLocation, WrapperRecipe> getRecipeMap() {
return getRecipes().entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, entry -> new WrapperRecipe(entry
.getValue())));
}
/**
* Remove a recipe based on it's output.
*
* @param output output of the recipe
*
* @docParam output <tag:items:minecraft:wool>
*/
@ZenCodeType.Method
default void removeRecipe(IIngredient output) {
CraftTweakerAPI.apply(new ActionRemoveRecipeByOutput(this, output));
}
// This is only here for backwards compat, should be removed next breaking change
/**
* Removes a recipe based on it's output.
*
* @param output output of the recipe
*
* @docParam output <item:minecraft:glass>
*/
@ZenCodeType.Method
default void removeRecipe(IItemStack output) {
removeRecipe((IIngredient) output);
}
/**
* Removes all recipes who's input contains the given IItemStack.
*
* @param input The input IItemStack.
*
* @docParam input <item:minecraft:iron_ingot>
*/
@ZenCodeType.Method
default void removeRecipeByInput(IItemStack input) {
CraftTweakerAPI.apply(new ActionRemoveRecipe(this, iRecipe -> iRecipe.getIngredients()
.stream()
.anyMatch(ingredient -> ingredient.test(input.getInternal()))));
}
/**
* Remove recipe based on Registry name
*
* @param name registry name of recipe to remove
*
* @docParam name "minecraft:furnace"
*/
@ZenCodeType.Method
default void removeByName(String name) {
CraftTweakerAPI.apply(new ActionRemoveRecipeByName(this, new ResourceLocation(name)));
}
/**
* Remove recipe based on Registry name modid
*
* @param modid modid of the recipes to remove
*
* @docParam modid "minecraft"
*/
@ZenCodeType.Method
default void removeByModid(String modid) {
CraftTweakerAPI.apply(new ActionRemoveRecipeByModid(this, modid));
}
/**
* Remove recipe based on Registry name modid with an added exclusion check, so you can remove the whole mod besides a few specified.
*
* @param modid modid of the recipes to remove
* @param exclude recipes to exlude from being removed.
*
* @docParam modid "minecraft"
* @docParam exclude (name as string) => {return name == "orange_wool";}
*/
@ZenCodeType.Method
default void removeByModid(String modid, RecipeFilter exclude) {
CraftTweakerAPI.apply(new ActionRemoveRecipeByModid(this, modid, exclude));
}
/**
* Remove recipe based on regex.
*
* @param regex regex to match against
*
* @docParam regex "\\d_\\d"
*/
@ZenCodeType.Method
default void removeByRegex(String regex) {
CraftTweakerAPI.apply(new ActionRemoveRecipeByRegex(this, regex));
}
/**
* Remove recipe based on regex with an added exclusion check, so you can remove the whole mod besides a few specified.
*
* @param regex regex to match against
*
* @docParam regex "\\d_\\d"
* @docParam exclude (name as string) => {return name == "orange_wool";}
*/
@ZenCodeType.Method
default void removeByRegex(String regex, RecipeFilter exclude) {
CraftTweakerAPI.apply(new ActionRemoveRecipeByRegex(this, regex, exclude));
}
/**
* Remove all recipes in this registry
*/
@ZenCodeType.Method
default void removeAll() {
CraftTweakerAPI.apply(new ActionRemoveAll(this));
}
/**
* Gets the recipe type for the registry to remove from.
*
* @return IRecipeType of this registry.
*/
IRecipeType getRecipeType();
/**
* Gets all the vanilla IRecipes for this recipe type.
*
* @return Map of ResourceLocation to IRecipe for this recipe type.
*/
default Map<ResourceLocation, IRecipe<?>> getRecipes() {
return CTCraftingTableManager.recipeManager.recipes.computeIfAbsent(getRecipeType(), iRecipeType -> new HashMap<>());
}
/**
* Checks if the given name is a valid ResourceLocation path, used to ensure recipe names are correct
*
* @param name name to check
*/
default String validateRecipeName(String name) {
return fixRecipeName(name);
}
/**
* Fixes and logs some common errors that people run into with recipe names
*
* @param name name to check
*
* @return fixed name
*/
default String fixRecipeName(String name) {
CodePosition position = PositionUtil.getZCScriptPositionFromStackTrace();
return NameUtils.fixing(
name,
(fixed, mistakes) -> CraftTweakerAPI.logWarning(
"%sInvalid recipe name '%s', mistakes:\n%s\nNew recipe name: %s",
position == CodePosition.UNKNOWN ? "" : position + ": ",
name,
String.join("\n", mistakes),
fixed
)
);
}
/**
* Gets the resource location to get this Recipe handler
* Default just looks up the Recipe Type key from the registry
*/
default ResourceLocation getBracketResourceLocation() {
return Registry.RECIPE_TYPE.getKey(getRecipeType());
}
@FunctionalInterface
@ZenRegister
@ZenCodeType.Name("crafttweaker.api.recipe.RecipeFilter")
@Document("vanilla/api/recipe/RecipeFilter")
interface RecipeFilter {
@ZenCodeType.Method
boolean test(String name);
}
@FunctionalInterface
@ZenRegister
@ZenCodeType.Name("crafttweaker.api.recipe.RecipeFunctionSingle")
@Document("vanilla/api/recipe/RecipeFunctionSingle")
interface RecipeFunctionSingle {
@ZenCodeType.Method
IItemStack process(IItemStack usualOut, IItemStack inputs);
}
@FunctionalInterface
@ZenRegister
@ZenCodeType.Name("crafttweaker.api.recipe.RecipeFunctionArray")
@Document("vanilla/api/recipe/RecipeFunctionArray")
interface RecipeFunctionArray {
@ZenCodeType.Method
IItemStack process(IItemStack usualOut, IItemStack[] inputs);
}
@FunctionalInterface
@ZenRegister
@ZenCodeType.Name("crafttweaker.api.recipe.RecipeFunctionMatrix")
@Document("vanilla/api/recipe/RecipeFunctionMatrix")
interface RecipeFunctionMatrix {
@ZenCodeType.Method
IItemStack process(IItemStack usualOut, IItemStack[][] inputs);
}
@Override
default String getCommandString() {
return "<recipetype:" + getBracketResourceLocation() + ">";
}
}