-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rewrite the state-of-the-art fluid API in Java
Bugs fixed: - FluidAmountPredicate.atMost being an equality check - FluidReference.increment: units swapped
- Loading branch information
Showing
32 changed files
with
833 additions
and
581 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
common/src/main/java/juuxel/adorn/fluid/FluidAmountPredicate.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package juuxel.adorn.fluid; | ||
|
||
import net.minecraft.fluid.Fluids; | ||
|
||
public interface FluidAmountPredicate { | ||
HasFluidAmount getUpperBound(); | ||
|
||
boolean test(long amount, FluidUnit unit); | ||
|
||
static FluidAmountPredicate exactly(long amount, FluidUnit unit) { | ||
return new FluidAmountPredicate() { | ||
private final FluidVolume upperBound = new FluidVolume(Fluids.EMPTY, amount, null, unit); | ||
|
||
@Override | ||
public HasFluidAmount getUpperBound() { | ||
return upperBound; | ||
} | ||
|
||
@Override | ||
public boolean test(long amount, FluidUnit unit) { | ||
return FluidUnit.compareVolumes(amount, unit, upperBound.getAmount(), upperBound.getUnit()) == 0; | ||
} | ||
}; | ||
} | ||
|
||
static FluidAmountPredicate atMost(long max, FluidUnit unit) { | ||
return new FluidAmountPredicate() { | ||
private final FluidVolume upperBound = new FluidVolume(Fluids.EMPTY, max, null, unit); | ||
|
||
@Override | ||
public HasFluidAmount getUpperBound() { | ||
return upperBound; | ||
} | ||
|
||
@Override | ||
public boolean test(long amount, FluidUnit unit) { | ||
return FluidUnit.compareVolumes(amount, unit, upperBound.getAmount(), upperBound.getUnit()) <= 0; | ||
} | ||
}; | ||
} | ||
} |
53 changes: 53 additions & 0 deletions
53
common/src/main/java/juuxel/adorn/fluid/FluidIngredient.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package juuxel.adorn.fluid; | ||
|
||
import com.mojang.serialization.Codec; | ||
import com.mojang.serialization.codecs.RecordCodecBuilder; | ||
import net.minecraft.nbt.NbtCompound; | ||
import net.minecraft.network.PacketByteBuf; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
import java.util.Optional; | ||
|
||
/** | ||
* A fluid ingredient for crafting. | ||
*/ | ||
public record FluidIngredient(FluidKey fluid, long amount, @Nullable NbtCompound nbt, FluidUnit unit) implements HasFluidAmount { | ||
public static final Codec<FluidIngredient> CODEC = RecordCodecBuilder.create(instance -> instance.group( | ||
FluidKey.CODEC.fieldOf("fluid").forGetter(FluidIngredient::fluid), | ||
Codec.LONG.fieldOf("amount").forGetter(FluidIngredient::amount), | ||
NbtCompound.CODEC.optionalFieldOf("nbt").forGetter(ingredient -> Optional.ofNullable(ingredient.nbt)), | ||
FluidUnit.CODEC.optionalFieldOf("unit", FluidUnit.LITRE).forGetter(FluidIngredient::unit) | ||
).apply(instance, FluidIngredient::new)); | ||
|
||
// for DFU | ||
private FluidIngredient(FluidKey fluid, long amount, Optional<NbtCompound> nbt, FluidUnit unit) { | ||
this(fluid, amount, nbt.orElse(null), unit); | ||
} | ||
|
||
public static FluidIngredient load(PacketByteBuf buf) { | ||
var fluid = FluidKey.load(buf); | ||
var amount = buf.readVarLong(); | ||
var nbt = buf.readNbt(); | ||
var unit = buf.readEnumConstant(FluidUnit.class); | ||
return new FluidIngredient(fluid, amount, nbt, unit); | ||
} | ||
|
||
public void write(PacketByteBuf buf) { | ||
fluid.write(buf); | ||
buf.writeVarLong(amount); | ||
buf.writeNbt(nbt); | ||
buf.writeEnumConstant(unit); | ||
} | ||
|
||
@Override | ||
public long getAmount() { | ||
return amount; | ||
} | ||
|
||
@NotNull | ||
@Override | ||
public FluidUnit getUnit() { | ||
return unit; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package juuxel.adorn.fluid; | ||
|
||
import com.mojang.serialization.Codec; | ||
import net.minecraft.fluid.Fluid; | ||
import net.minecraft.network.PacketByteBuf; | ||
import net.minecraft.registry.Registries; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Set; | ||
|
||
/// A "key" that identifies a fluid or a group of fluids. | ||
/// | ||
/// Could be a single fluid, a tag or a list of the former. | ||
/// | ||
/// ## JSON format | ||
/// | ||
/// A fluid key is one of: | ||
/// | ||
/// - a string; if prefixed with `#`, a tag, otherwise a fluid ID | ||
/// - an array of strings as described above | ||
/// | ||
/// Examples: `"minecraft:water"`, `"#c:milk"`, `["minecraft:water", "minecraft:lava"]` | ||
public sealed interface FluidKey permits FluidKeyImpl.Simple, FluidKeyImpl.OfArray { | ||
Codec<FluidKey> CODEC = FluidKeyImpl.CODEC; | ||
|
||
/** | ||
* Returns the set of all fluids matching this key. | ||
*/ | ||
Set<Fluid> getFluids(); | ||
|
||
/** | ||
* Tests whether the fluid matches this key. | ||
*/ | ||
boolean matches(Fluid fluid); | ||
|
||
/** | ||
* Writes this key to a packet buffer. | ||
* @see #load | ||
*/ | ||
default void write(PacketByteBuf buf) { | ||
var fluids = getFluids(); | ||
buf.writeVarInt(fluids.size()); | ||
|
||
for (Fluid fluid : fluids) { | ||
buf.writeVarInt(Registries.FLUID.getRawId(fluid)); | ||
} | ||
} | ||
|
||
/** | ||
* Reads a key from a packet buffer. | ||
* @see #write | ||
*/ | ||
static FluidKey load(PacketByteBuf buf) { | ||
var size = buf.readVarInt(); | ||
|
||
if (size == 1) { | ||
return new FluidKeyImpl.OfFluid(Registries.FLUID.get(buf.readVarInt())); | ||
} else { | ||
List<FluidKeyImpl.Simple> children = new ArrayList<>(size); | ||
for (int i = 0; i < size; i++) { | ||
children.add(new FluidKeyImpl.OfFluid(Registries.FLUID.get(buf.readVarInt()))); | ||
} | ||
return new FluidKeyImpl.OfArray(children); | ||
} | ||
} | ||
} |
123 changes: 123 additions & 0 deletions
123
common/src/main/java/juuxel/adorn/fluid/FluidKeyImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package juuxel.adorn.fluid; | ||
|
||
import com.mojang.datafixers.util.Either; | ||
import com.mojang.datafixers.util.Pair; | ||
import com.mojang.serialization.Codec; | ||
import com.mojang.serialization.DataResult; | ||
import com.mojang.serialization.DynamicOps; | ||
import net.minecraft.fluid.Fluid; | ||
import net.minecraft.registry.Registries; | ||
import net.minecraft.registry.RegistryKeys; | ||
import net.minecraft.registry.entry.RegistryEntry; | ||
import net.minecraft.registry.tag.TagKey; | ||
import net.minecraft.util.Identifier; | ||
import net.minecraft.util.dynamic.Codecs; | ||
|
||
import java.util.List; | ||
import java.util.Set; | ||
import java.util.function.Function; | ||
import java.util.stream.Collectors; | ||
|
||
final class FluidKeyImpl { | ||
private static final Codec<Simple> SIMPLE_CODEC = new Codec<>() { | ||
@Override | ||
public <T> DataResult<Pair<FluidKeyImpl.Simple, T>> decode(DynamicOps<T> ops, T input) { | ||
return ops.getStringValue(input) | ||
.map(s -> { | ||
if (s.startsWith("#")) { | ||
return new OfTag(TagKey.of(RegistryKeys.FLUID, new Identifier(s.substring(1)))); | ||
} else { | ||
return new OfFluid(Registries.FLUID.get(new Identifier(s))); | ||
} | ||
}) | ||
.map(key -> Pair.of(key, ops.empty())); | ||
} | ||
|
||
@Override | ||
public <T> DataResult<T> encode(FluidKeyImpl.Simple input, DynamicOps<T> ops, T prefix) { | ||
return ops.mergeToPrimitive(prefix, ops.createString(input.getId())); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "SimpleFluidKey"; | ||
} | ||
}; | ||
|
||
// TODO: Switch to either instead of xor | ||
public static final Codec<FluidKey> CODEC = Codecs.xor( | ||
SIMPLE_CODEC, | ||
SIMPLE_CODEC.listOf().xmap(OfArray::new, OfArray::children) | ||
).xmap( | ||
// TODO (Java 21): Use pattern matching | ||
either -> either.map(Function.identity(), Function.identity()), | ||
key -> { | ||
if (key instanceof Simple simple) { | ||
return Either.left(simple); | ||
} else if (key instanceof OfArray ofArray) { | ||
return Either.right(ofArray); | ||
} else { | ||
throw new IllegalArgumentException(); | ||
} | ||
} | ||
); | ||
|
||
sealed interface Simple extends FluidKey { | ||
String getId(); | ||
} | ||
|
||
record OfFluid(Fluid fluid) implements Simple { | ||
@Override | ||
public String getId() { | ||
return Registries.FLUID.getId(fluid).toString(); | ||
} | ||
|
||
@Override | ||
public Set<Fluid> getFluids() { | ||
return Set.of(fluid); | ||
} | ||
|
||
@Override | ||
public boolean matches(Fluid fluid) { | ||
return fluid == this.fluid; | ||
} | ||
} | ||
|
||
record OfTag(TagKey<Fluid> tag) implements Simple { | ||
@Override | ||
public String getId() { | ||
return "#" + tag.id(); | ||
} | ||
|
||
@Override | ||
public Set<Fluid> getFluids() { | ||
return Registries.FLUID.getOrCreateEntryList(tag) | ||
.stream() | ||
.map(RegistryEntry::value) | ||
.collect(Collectors.toSet()); | ||
} | ||
|
||
@Override | ||
public boolean matches(Fluid fluid) { | ||
return fluid.isIn(tag); | ||
} | ||
} | ||
|
||
record OfArray(List<Simple> children) implements FluidKey { | ||
@Override | ||
public Set<Fluid> getFluids() { | ||
return children.stream() | ||
.flatMap(child -> child.getFluids().stream()) | ||
.collect(Collectors.toSet()); | ||
} | ||
|
||
@Override | ||
public boolean matches(Fluid fluid) { | ||
for (var child : children) { | ||
if (child.matches(fluid)) return true; | ||
} | ||
|
||
return false; | ||
} | ||
} | ||
} |
Oops, something went wrong.