Skip to content

Commit 904da0d

Browse files
committed
Rewrite the state-of-the-art fluid API in Java
Bugs fixed: - FluidAmountPredicate.atMost being an equality check - FluidReference.increment: units swapped
1 parent d094ea1 commit 904da0d

32 files changed

+833
-581
lines changed

common/src/main/java/juuxel/adorn/compat/jei/BrewerCategory.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ public void setRecipe(IRecipeLayoutBuilder layoutBuilder, BrewingRecipe recipe,
8080

8181
var ingredient = r.fluid();
8282
var amount = FluidUnit.convert(ingredient.getAmount(), ingredient.getUnit(), FluidBridge.get().getFluidUnit());
83-
for (Fluid fluid : ingredient.getFluid().getFluids()) {
84-
tank.addFluidStack(fluid, amount, ingredient.getNbt());
83+
for (Fluid fluid : ingredient.fluid().getFluids()) {
84+
tank.addFluidStack(fluid, amount, ingredient.nbt());
8585
}
8686
}
8787
}

common/src/main/java/juuxel/adorn/compat/rei/BrewerDisplay.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ public BrewerDisplay(FluidBrewingRecipe recipe) {
4848

4949
private static EntryIngredient entryIngredientOf(FluidIngredient fluidIngredient) {
5050
var amount = FluidUnit.convert(fluidIngredient.getAmount(), fluidIngredient.getUnit(), FluidBridge.get().getFluidUnit());
51-
var stacks = fluidIngredient.getFluid()
51+
var stacks = fluidIngredient.fluid()
5252
.getFluids()
5353
.stream()
54-
.map(fluid -> EntryStacks.of(FluidStack.create(fluid, amount, fluidIngredient.getNbt())))
54+
.map(fluid -> EntryStacks.of(FluidStack.create(fluid, amount, fluidIngredient.nbt())))
5555
.toList();
5656
return EntryIngredient.of(stacks);
5757
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package juuxel.adorn.fluid;
2+
3+
import net.minecraft.fluid.Fluids;
4+
5+
public interface FluidAmountPredicate {
6+
HasFluidAmount getUpperBound();
7+
8+
boolean test(long amount, FluidUnit unit);
9+
10+
static FluidAmountPredicate exactly(long amount, FluidUnit unit) {
11+
return new FluidAmountPredicate() {
12+
private final FluidVolume upperBound = new FluidVolume(Fluids.EMPTY, amount, null, unit);
13+
14+
@Override
15+
public HasFluidAmount getUpperBound() {
16+
return upperBound;
17+
}
18+
19+
@Override
20+
public boolean test(long amount, FluidUnit unit) {
21+
return FluidUnit.compareVolumes(amount, unit, upperBound.getAmount(), upperBound.getUnit()) == 0;
22+
}
23+
};
24+
}
25+
26+
static FluidAmountPredicate atMost(long max, FluidUnit unit) {
27+
return new FluidAmountPredicate() {
28+
private final FluidVolume upperBound = new FluidVolume(Fluids.EMPTY, max, null, unit);
29+
30+
@Override
31+
public HasFluidAmount getUpperBound() {
32+
return upperBound;
33+
}
34+
35+
@Override
36+
public boolean test(long amount, FluidUnit unit) {
37+
return FluidUnit.compareVolumes(amount, unit, upperBound.getAmount(), upperBound.getUnit()) <= 0;
38+
}
39+
};
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package juuxel.adorn.fluid;
2+
3+
import com.mojang.serialization.Codec;
4+
import com.mojang.serialization.codecs.RecordCodecBuilder;
5+
import net.minecraft.nbt.NbtCompound;
6+
import net.minecraft.network.PacketByteBuf;
7+
import org.jetbrains.annotations.NotNull;
8+
import org.jetbrains.annotations.Nullable;
9+
10+
import java.util.Optional;
11+
12+
/**
13+
* A fluid ingredient for crafting.
14+
*/
15+
public record FluidIngredient(FluidKey fluid, long amount, @Nullable NbtCompound nbt, FluidUnit unit) implements HasFluidAmount {
16+
public static final Codec<FluidIngredient> CODEC = RecordCodecBuilder.create(instance -> instance.group(
17+
FluidKey.CODEC.fieldOf("fluid").forGetter(FluidIngredient::fluid),
18+
Codec.LONG.fieldOf("amount").forGetter(FluidIngredient::amount),
19+
NbtCompound.CODEC.optionalFieldOf("nbt").forGetter(ingredient -> Optional.ofNullable(ingredient.nbt)),
20+
FluidUnit.CODEC.optionalFieldOf("unit", FluidUnit.LITRE).forGetter(FluidIngredient::unit)
21+
).apply(instance, FluidIngredient::new));
22+
23+
// for DFU
24+
private FluidIngredient(FluidKey fluid, long amount, Optional<NbtCompound> nbt, FluidUnit unit) {
25+
this(fluid, amount, nbt.orElse(null), unit);
26+
}
27+
28+
public static FluidIngredient load(PacketByteBuf buf) {
29+
var fluid = FluidKey.load(buf);
30+
var amount = buf.readVarLong();
31+
var nbt = buf.readNbt();
32+
var unit = buf.readEnumConstant(FluidUnit.class);
33+
return new FluidIngredient(fluid, amount, nbt, unit);
34+
}
35+
36+
public void write(PacketByteBuf buf) {
37+
fluid.write(buf);
38+
buf.writeVarLong(amount);
39+
buf.writeNbt(nbt);
40+
buf.writeEnumConstant(unit);
41+
}
42+
43+
@Override
44+
public long getAmount() {
45+
return amount;
46+
}
47+
48+
@NotNull
49+
@Override
50+
public FluidUnit getUnit() {
51+
return unit;
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package juuxel.adorn.fluid;
2+
3+
import com.mojang.serialization.Codec;
4+
import net.minecraft.fluid.Fluid;
5+
import net.minecraft.network.PacketByteBuf;
6+
import net.minecraft.registry.Registries;
7+
8+
import java.util.ArrayList;
9+
import java.util.List;
10+
import java.util.Set;
11+
12+
/// A "key" that identifies a fluid or a group of fluids.
13+
///
14+
/// Could be a single fluid, a tag or a list of the former.
15+
///
16+
/// ## JSON format
17+
///
18+
/// A fluid key is one of:
19+
///
20+
/// - a string; if prefixed with `#`, a tag, otherwise a fluid ID
21+
/// - an array of strings as described above
22+
///
23+
/// Examples: `"minecraft:water"`, `"#c:milk"`, `["minecraft:water", "minecraft:lava"]`
24+
public sealed interface FluidKey permits FluidKeyImpl.Simple, FluidKeyImpl.OfArray {
25+
Codec<FluidKey> CODEC = FluidKeyImpl.CODEC;
26+
27+
/**
28+
* Returns the set of all fluids matching this key.
29+
*/
30+
Set<Fluid> getFluids();
31+
32+
/**
33+
* Tests whether the fluid matches this key.
34+
*/
35+
boolean matches(Fluid fluid);
36+
37+
/**
38+
* Writes this key to a packet buffer.
39+
* @see #load
40+
*/
41+
default void write(PacketByteBuf buf) {
42+
var fluids = getFluids();
43+
buf.writeVarInt(fluids.size());
44+
45+
for (Fluid fluid : fluids) {
46+
buf.writeVarInt(Registries.FLUID.getRawId(fluid));
47+
}
48+
}
49+
50+
/**
51+
* Reads a key from a packet buffer.
52+
* @see #write
53+
*/
54+
static FluidKey load(PacketByteBuf buf) {
55+
var size = buf.readVarInt();
56+
57+
if (size == 1) {
58+
return new FluidKeyImpl.OfFluid(Registries.FLUID.get(buf.readVarInt()));
59+
} else {
60+
List<FluidKeyImpl.Simple> children = new ArrayList<>(size);
61+
for (int i = 0; i < size; i++) {
62+
children.add(new FluidKeyImpl.OfFluid(Registries.FLUID.get(buf.readVarInt())));
63+
}
64+
return new FluidKeyImpl.OfArray(children);
65+
}
66+
}
67+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package juuxel.adorn.fluid;
2+
3+
import com.mojang.datafixers.util.Either;
4+
import com.mojang.datafixers.util.Pair;
5+
import com.mojang.serialization.Codec;
6+
import com.mojang.serialization.DataResult;
7+
import com.mojang.serialization.DynamicOps;
8+
import net.minecraft.fluid.Fluid;
9+
import net.minecraft.registry.Registries;
10+
import net.minecraft.registry.RegistryKeys;
11+
import net.minecraft.registry.entry.RegistryEntry;
12+
import net.minecraft.registry.tag.TagKey;
13+
import net.minecraft.util.Identifier;
14+
import net.minecraft.util.dynamic.Codecs;
15+
16+
import java.util.List;
17+
import java.util.Set;
18+
import java.util.function.Function;
19+
import java.util.stream.Collectors;
20+
21+
final class FluidKeyImpl {
22+
private static final Codec<Simple> SIMPLE_CODEC = new Codec<>() {
23+
@Override
24+
public <T> DataResult<Pair<FluidKeyImpl.Simple, T>> decode(DynamicOps<T> ops, T input) {
25+
return ops.getStringValue(input)
26+
.map(s -> {
27+
if (s.startsWith("#")) {
28+
return new OfTag(TagKey.of(RegistryKeys.FLUID, new Identifier(s.substring(1))));
29+
} else {
30+
return new OfFluid(Registries.FLUID.get(new Identifier(s)));
31+
}
32+
})
33+
.map(key -> Pair.of(key, ops.empty()));
34+
}
35+
36+
@Override
37+
public <T> DataResult<T> encode(FluidKeyImpl.Simple input, DynamicOps<T> ops, T prefix) {
38+
return ops.mergeToPrimitive(prefix, ops.createString(input.getId()));
39+
}
40+
41+
@Override
42+
public String toString() {
43+
return "SimpleFluidKey";
44+
}
45+
};
46+
47+
// TODO: Switch to either instead of xor
48+
public static final Codec<FluidKey> CODEC = Codecs.xor(
49+
SIMPLE_CODEC,
50+
SIMPLE_CODEC.listOf().xmap(OfArray::new, OfArray::children)
51+
).xmap(
52+
// TODO (Java 21): Use pattern matching
53+
either -> either.map(Function.identity(), Function.identity()),
54+
key -> {
55+
if (key instanceof Simple simple) {
56+
return Either.left(simple);
57+
} else if (key instanceof OfArray ofArray) {
58+
return Either.right(ofArray);
59+
} else {
60+
throw new IllegalArgumentException();
61+
}
62+
}
63+
);
64+
65+
sealed interface Simple extends FluidKey {
66+
String getId();
67+
}
68+
69+
record OfFluid(Fluid fluid) implements Simple {
70+
@Override
71+
public String getId() {
72+
return Registries.FLUID.getId(fluid).toString();
73+
}
74+
75+
@Override
76+
public Set<Fluid> getFluids() {
77+
return Set.of(fluid);
78+
}
79+
80+
@Override
81+
public boolean matches(Fluid fluid) {
82+
return fluid == this.fluid;
83+
}
84+
}
85+
86+
record OfTag(TagKey<Fluid> tag) implements Simple {
87+
@Override
88+
public String getId() {
89+
return "#" + tag.id();
90+
}
91+
92+
@Override
93+
public Set<Fluid> getFluids() {
94+
return Registries.FLUID.getOrCreateEntryList(tag)
95+
.stream()
96+
.map(RegistryEntry::value)
97+
.collect(Collectors.toSet());
98+
}
99+
100+
@Override
101+
public boolean matches(Fluid fluid) {
102+
return fluid.isIn(tag);
103+
}
104+
}
105+
106+
record OfArray(List<Simple> children) implements FluidKey {
107+
@Override
108+
public Set<Fluid> getFluids() {
109+
return children.stream()
110+
.flatMap(child -> child.getFluids().stream())
111+
.collect(Collectors.toSet());
112+
}
113+
114+
@Override
115+
public boolean matches(Fluid fluid) {
116+
for (var child : children) {
117+
if (child.matches(fluid)) return true;
118+
}
119+
120+
return false;
121+
}
122+
}
123+
}

0 commit comments

Comments
 (0)