-
Notifications
You must be signed in to change notification settings - Fork 110
/
FluidIngredient.java
436 lines (380 loc) · 12.7 KB
/
FluidIngredient.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
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
package slimeknights.mantle.recipe;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.Fluids;
import net.minecraft.network.PacketBuffer;
import net.minecraft.tags.ITag;
import net.minecraft.tags.TagCollectionManager;
import net.minecraft.util.JSONUtils;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.registries.ForgeRegistries;
import slimeknights.mantle.util.JsonHelper;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
// TODO: move to ingredient package in 1.17
@SuppressWarnings("unused")
public abstract class FluidIngredient {
/** Empty fluid ingredient, matches nothing */
public static final FluidIngredient EMPTY = new Empty();
/** Cached list of display fluids */
private List<FluidStack> displayFluids;
/**
* Checks if the given fluid matches this ingredient
* @param fluid Fluid to check
* @return True if the fluid matches
*/
public abstract boolean test(Fluid fluid);
/**
* Gets the amount of the given fluid needed for the recipe
* @param fluid Fluid to check
* @return Amount of the fluid needed
*/
public abstract int getAmount(Fluid fluid);
/**
* Checks if the given fluid stack argument matches this ingredient
* @param stack Fluid stack to check
* @return True if the fluid matches this ingredient and the amount is equal or greater than this
*/
public boolean test(FluidStack stack) {
Fluid fluid = stack.getFluid();
return stack.getAmount() >= getAmount(fluid) && test(stack.getFluid());
}
/**
* Gets a list of fluid stacks contained in this ingredient for display
* @return List of fluid stacks for this ingredient
*/
public List<FluidStack> getFluids() {
if (displayFluids == null) {
displayFluids = getAllFluids().stream().filter(stack -> {
Fluid fluid = stack.getFluid();
return fluid.isSource(fluid.getDefaultState());
}).collect(Collectors.toList());
}
return displayFluids;
}
/**
* Gets a list of fluid stacks contained in this ingredient for display, may include flowing fluids
* @return List of fluid stacks for this ingredient
*/
protected abstract List<FluidStack> getAllFluids();
/**
* Serializes the Fluid Ingredient into JSON
* @return FluidIngredient JSON
*/
public abstract JsonElement serialize();
/**
* Writes the ingredient into the packet buffer
* @param buffer Packet buffer instance
*/
public void write(PacketBuffer buffer) {
Collection<FluidStack> fluids = getAllFluids();
buffer.writeInt(fluids.size());
for (FluidStack stack : fluids) {
buffer.writeString(Objects.requireNonNull(stack.getFluid().getRegistryName()).toString());
buffer.writeInt(stack.getAmount());
}
}
/*
* Instance creation
*/
/**
* Creates a new ingredient using the given fluid and amount
* @param fluid Fluid to check
* @param amount Minimum fluid amount
* @return Fluid ingredient for this fluid
*/
public static FluidIngredient of(Fluid fluid, int amount) {
return new FluidIngredient.FluidMatch(fluid, amount);
}
/**
* Creates a new ingredient using the given fluidstack
* @param stack Fluid stack
* @return Fluid ingredient for this fluid stack
*/
public static FluidIngredient of(FluidStack stack) {
return of(stack.getFluid(), stack.getAmount());
}
/**
* Creates a new fluid ingredient from the given tag
* @param fluid Fluid tag
* @param amount Minimum fluid amount
* @return Fluid ingredient from a tag
*/
public static FluidIngredient of(ITag<Fluid> fluid, int amount) {
return new FluidIngredient.TagMatch(fluid, amount);
}
/**
* Creates a new compound ingredient from the given list of ingredients
* @param ingredients Ingredient list
* @return Compound ingredient
*/
public static FluidIngredient of(FluidIngredient... ingredients) {
return new FluidIngredient.Compound(ingredients);
}
/*
* JSON deserializing
*/
/**
* Deserializes the fluid ingredient from JSON
* @param parent Parent containing the fluid JSON
* @param name Name of the key to fetch from the parent object
* @return Fluid ingredient instance
* @throws JsonSyntaxException if syntax is invalid
*/
public static FluidIngredient deserialize(JsonObject parent, String name) {
return deserialize(JsonHelper.getElement(parent, name), name);
}
/**
* Deserializes the fluid ingredient from JSON
* @param json Json element instance
* @param name Name of the object for error messages
* @return Fluid ingredient instance
* @throws JsonSyntaxException if syntax is invalid
*/
public static FluidIngredient deserialize(JsonElement json, String name) {
// single ingredient object
if (json.isJsonObject()) {
return deserializeObject(json.getAsJsonObject());
}
// array
if (json.isJsonArray()) {
return Compound.deserialize(json.getAsJsonArray(), name);
}
throw new JsonSyntaxException("Fluid ingredient " + name + " must be either an object or array");
}
/**
* Deserializes the fluid ingredient from JSON
* @param json JSON object
* @return Fluid Ingredient
* @throws JsonSyntaxException if syntax is invalid
*/
private static FluidIngredient deserializeObject(JsonObject json) {
if (json.entrySet().isEmpty()) {
return EMPTY;
}
// fluid match
if (json.has("name")) {
// don't set both, obviously an error
if (json.has("tag")) {
throw new JsonSyntaxException("An ingredient entry is either a tag or an fluid, not both");
}
// parse a fluid
return FluidMatch.deserialize(json);
}
// tag match
if (json.has("tag")) {
return TagMatch.deserialize(json);
}
throw new JsonSyntaxException("An ingredient entry needs either a tag or an fluid");
}
/*
* Packet buffers
*/
/**
* Reads a fluid ingredient from the packet buffer
* @param buffer Buffer instance
* @return Fluid ingredient instance
*/
public static FluidIngredient read(PacketBuffer buffer) {
int count = buffer.readInt();
FluidIngredient[] ingredients = new FluidIngredient[count];
for (int i = 0; i < count; i++) {
Fluid fluid = ForgeRegistries.FLUIDS.getValue(new ResourceLocation(buffer.readString(32767)));
if (fluid == null) {
fluid = Fluids.EMPTY;
}
int amount = buffer.readInt();
ingredients[i] = of(fluid, amount);
}
// if a single ingredient, do not wrap in compound
if (count == 1) {
return ingredients[0];
}
// compound for anything else
return of(ingredients);
}
/**
* Empty fluid ingredient, matches only empty fluid stacks
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
private static class Empty extends FluidIngredient {
@Override
public boolean test(Fluid fluid) {
return fluid == Fluids.EMPTY;
}
@Override
public boolean test(FluidStack fluid) {
return fluid.isEmpty();
}
@Override
public int getAmount(Fluid fluid) {
return 0;
}
@Override
public List<FluidStack> getAllFluids() {
return Collections.emptyList();
}
@Override
public JsonElement serialize() {
return new JsonObject();
}
}
/**
* Fluid ingredient that matches a single fluid
*/
@AllArgsConstructor(access=AccessLevel.PRIVATE)
private static class FluidMatch extends FluidIngredient {
private final Fluid fluid;
private final int amount;
@Override
public boolean test(Fluid fluid) {
return fluid == this.fluid;
}
@Override
public int getAmount(Fluid fluid) {
return amount;
}
@Override
public List<FluidStack> getAllFluids() {
return Collections.singletonList(new FluidStack(fluid, amount));
}
@Override
public JsonElement serialize() {
JsonObject object = new JsonObject();
object.addProperty("name", Objects.requireNonNull(fluid.getRegistryName()).toString());
object.addProperty("amount", amount);
return object;
}
@Override
public void write(PacketBuffer buffer) {
// count
buffer.writeInt(1);
// single fluid
buffer.writeString(Objects.requireNonNull(fluid.getRegistryName()).toString());
buffer.writeInt(amount);
}
/**
* Deserailizes the ingredient from JSON
* @param json JSON object
* @return Fluid ingredient instance
*/
private static FluidMatch deserialize(JsonObject json) {
String fluidName = JSONUtils.getString(json, "name");
Fluid fluid = ForgeRegistries.FLUIDS.getValue(new ResourceLocation(fluidName));
if (fluid == null || fluid == Fluids.EMPTY) {
throw new JsonSyntaxException("Unknown fluid '" + fluidName + "'");
}
int amount = JSONUtils.getInt(json, "amount");
return new FluidMatch(fluid, amount);
}
}
/**
* Fluid ingredient that matches a tag
*/
@AllArgsConstructor(access=AccessLevel.PRIVATE)
private static class TagMatch extends FluidIngredient {
private final ITag<Fluid> tag;
private final int amount;
@Override
public boolean test(Fluid fluid) {
return tag.contains(fluid);
}
@Override
public int getAmount(Fluid fluid) {
return amount;
}
@Override
public List<FluidStack> getAllFluids() {
return tag.getAllElements().stream().map((fluid) -> new FluidStack(fluid, amount)).collect(Collectors.toList());
}
@Override
public JsonElement serialize() {
JsonObject object = new JsonObject();
object.addProperty("tag", TagCollectionManager.getManager().getFluidTags().getValidatedIdFromTag(this.tag).toString());
object.addProperty("amount", amount);
return object;
}
/**
* Deseralizes the ingredient from JSON
* @param json JSON object
* @return Fluid ingredient instance
*/
private static TagMatch deserialize(JsonObject json) {
String tagName = JSONUtils.getString(json, "tag");
ITag<Fluid> tag = TagCollectionManager.getManager().getFluidTags().get(new ResourceLocation(tagName));
if (tag == null) {
throw new JsonSyntaxException("Unknown fluid tag '" + tagName + "'");
}
int amount = JSONUtils.getInt(json, "amount");
return new TagMatch(tag, amount);
}
}
/**
* Fluid ingredient that matches a list of ingredients
*/
private static class Compound extends FluidIngredient {
private final List<FluidIngredient> ingredients;
private Compound(FluidIngredient[] ingredients) {
this.ingredients = Arrays.asList(ingredients);
}
@Override
public boolean test(Fluid fluid) {
return ingredients.stream().anyMatch(ingredient -> ingredient.test(fluid));
}
@Override
public boolean test(FluidStack stack) {
return ingredients.stream().anyMatch(ingredient -> ingredient.test(stack));
}
@Override
public int getAmount(Fluid fluid) {
return ingredients.stream()
.filter(ingredient -> ingredient.test(fluid))
.mapToInt(ingredient -> ingredient.getAmount(fluid))
.findFirst()
.orElse(0);
}
@Override
public List<FluidStack> getAllFluids() {
return ingredients.stream()
.flatMap(ingredient -> ingredient.getFluids().stream())
.collect(Collectors.toList());
}
@Override
public JsonElement serialize() {
return ingredients.stream()
.map(FluidIngredient::serialize)
.collect(JsonArray::new, JsonArray::add, JsonArray::addAll);
}
/**
* Deserializes a compound ingredient from JSON
* @param array JSON array
* @param name Array key
* @return Compound fluid ingredient instance
*/
private static Compound deserialize(JsonArray array, String name) {
// size must be valid
int size = array.size();
if (size == 0) {
throw new JsonSyntaxException("Fluid array cannot be empty, at least one fluid must be defined");
}
// parse all ingredients
FluidIngredient[] ingredients = new FluidIngredient[size];
for (int i = 0; i < size; i++) {
// no reason to an array in an array
ingredients[i] = deserializeObject(JSONUtils.getJsonObject(array.get(i), name + "[" + i + "]"));
}
return new Compound(ingredients);
}
}
}