From 380cb52e43ca3670746a2ae6143f4ffef336df51 Mon Sep 17 00:00:00 2001
From: vicisacat <victor.branchu@gmail.com>
Date: Fri, 17 Nov 2023 20:22:43 +0100
Subject: [PATCH] Add FluidState API

---
 .../paper/block/fluid/PaperFluidData.java     | 110 ++++++++++++++++++
 .../paper/block/fluid/package-info.java       |   5 +
 .../fluid/type/PaperFallingFluidData.java     |  18 +++
 .../fluid/type/PaperFlowingFluidData.java     |  11 ++
 .../craftbukkit/CraftRegionAccessor.java      |   7 ++
 .../generator/CraftLimitedRegion.java         |   7 ++
 6 files changed, 158 insertions(+)
 create mode 100644 paper-server/src/main/java/io/papermc/paper/block/fluid/PaperFluidData.java
 create mode 100644 paper-server/src/main/java/io/papermc/paper/block/fluid/package-info.java
 create mode 100644 paper-server/src/main/java/io/papermc/paper/block/fluid/type/PaperFallingFluidData.java
 create mode 100644 paper-server/src/main/java/io/papermc/paper/block/fluid/type/PaperFlowingFluidData.java

diff --git a/paper-server/src/main/java/io/papermc/paper/block/fluid/PaperFluidData.java b/paper-server/src/main/java/io/papermc/paper/block/fluid/PaperFluidData.java
new file mode 100644
index 0000000000..479bc32241
--- /dev/null
+++ b/paper-server/src/main/java/io/papermc/paper/block/fluid/PaperFluidData.java
@@ -0,0 +1,110 @@
+package io.papermc.paper.block.fluid;
+
+import com.google.common.base.Preconditions;
+import io.papermc.paper.block.fluid.type.PaperFallingFluidData;
+import io.papermc.paper.block.fluid.type.PaperFlowingFluidData;
+import io.papermc.paper.util.MCUtil;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+import net.minecraft.world.level.material.FluidState;
+import net.minecraft.world.level.material.LavaFluid;
+import net.minecraft.world.level.material.WaterFluid;
+import org.bukkit.Fluid;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.CraftFluid;
+import org.bukkit.craftbukkit.CraftWorld;
+import org.bukkit.craftbukkit.util.CraftVector;
+import org.bukkit.util.Vector;
+import org.jetbrains.annotations.NotNull;
+
+public class PaperFluidData implements FluidData {
+
+    private final FluidState state;
+
+    protected PaperFluidData(final FluidState state) {
+        this.state = state;
+    }
+
+    /**
+     * Provides the internal server representation of this fluid data.
+     * @return the fluid state.
+     */
+    public FluidState getState() {
+        return this.state;
+    }
+
+    @Override
+    public final @NotNull Fluid getFluidType() {
+        return CraftFluid.minecraftToBukkit(this.state.getType());
+    }
+
+    @Override
+    public @NotNull PaperFluidData clone() {
+        try {
+            return (PaperFluidData) super.clone();
+        } catch (final CloneNotSupportedException ex) {
+            throw new AssertionError("Clone not supported", ex);
+        }
+    }
+
+    @Override
+    public @NotNull Vector computeFlowDirection(final Location location) {
+        Preconditions.checkArgument(location.getWorld() != null, "Cannot compute flow direction on world-less location");
+        return CraftVector.toBukkit(this.state.getFlow(
+            ((CraftWorld) location.getWorld()).getHandle(),
+            MCUtil.toBlockPosition(location)
+        ));
+    }
+
+    @Override
+    public int getLevel() {
+        return this.state.getAmount();
+    }
+
+    @Override
+    public float computeHeight(@NotNull final Location location) {
+        Preconditions.checkArgument(location.getWorld() != null, "Cannot compute height on world-less location");
+        return this.state.getHeight(((CraftWorld) location.getWorld()).getHandle(), MCUtil.toBlockPos(location));
+    }
+
+    @Override
+    public boolean isSource() {
+        return this.state.isSource();
+    }
+
+    @Override
+    public int hashCode() {
+        return this.state.hashCode();
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return obj instanceof final PaperFluidData paperFluidData && this.state.equals(paperFluidData.state);
+    }
+
+    @Override
+    public String toString() {
+        return "PaperFluidData{" + this.state + "}";
+    }
+
+    /* Registry */
+    private static final Map<Class<? extends net.minecraft.world.level.material.Fluid>, Function<FluidState, PaperFluidData>> MAP = new HashMap<>();
+    static {
+        //<editor-fold desc="PaperFluidData Registration" defaultstate="collapsed">
+        register(LavaFluid.Source.class, PaperFallingFluidData::new);
+        register(WaterFluid.Source.class, PaperFallingFluidData::new);
+        register(LavaFluid.Flowing.class, PaperFlowingFluidData::new);
+        register(WaterFluid.Flowing.class, PaperFlowingFluidData::new);
+        //</editor-fold>
+    }
+
+    static void register(final Class<? extends net.minecraft.world.level.material.Fluid> fluid, final Function<FluidState, PaperFluidData> creator) {
+        Preconditions.checkState(MAP.put(fluid, creator) == null, "Duplicate mapping %s->%s", fluid, creator);
+        MAP.put(fluid, creator);
+    }
+
+    public static PaperFluidData createData(final FluidState state) {
+        return MAP.getOrDefault(state.getType().getClass(), PaperFluidData::new).apply(state);
+    }
+}
diff --git a/paper-server/src/main/java/io/papermc/paper/block/fluid/package-info.java b/paper-server/src/main/java/io/papermc/paper/block/fluid/package-info.java
new file mode 100644
index 0000000000..cfabb814eb
--- /dev/null
+++ b/paper-server/src/main/java/io/papermc/paper/block/fluid/package-info.java
@@ -0,0 +1,5 @@
+@DefaultQualifier(NonNull.class)
+package io.papermc.paper.block.fluid;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
diff --git a/paper-server/src/main/java/io/papermc/paper/block/fluid/type/PaperFallingFluidData.java b/paper-server/src/main/java/io/papermc/paper/block/fluid/type/PaperFallingFluidData.java
new file mode 100644
index 0000000000..655dbd83ff
--- /dev/null
+++ b/paper-server/src/main/java/io/papermc/paper/block/fluid/type/PaperFallingFluidData.java
@@ -0,0 +1,18 @@
+
+package io.papermc.paper.block.fluid.type;
+
+import io.papermc.paper.block.fluid.PaperFluidData;
+import net.minecraft.world.level.material.FlowingFluid;
+import net.minecraft.world.level.material.FluidState;
+
+public class PaperFallingFluidData extends PaperFluidData implements FallingFluidData {
+
+    public PaperFallingFluidData(final FluidState state) {
+        super(state);
+    }
+
+    @Override
+    public boolean isFalling() {
+        return this.getState().getValue(FlowingFluid.FALLING);
+    }
+}
diff --git a/paper-server/src/main/java/io/papermc/paper/block/fluid/type/PaperFlowingFluidData.java b/paper-server/src/main/java/io/papermc/paper/block/fluid/type/PaperFlowingFluidData.java
new file mode 100644
index 0000000000..c0c2805cb0
--- /dev/null
+++ b/paper-server/src/main/java/io/papermc/paper/block/fluid/type/PaperFlowingFluidData.java
@@ -0,0 +1,11 @@
+package io.papermc.paper.block.fluid.type;
+
+import net.minecraft.world.level.material.FluidState;
+
+public class PaperFlowingFluidData extends PaperFallingFluidData implements FlowingFluidData {
+
+    public PaperFlowingFluidData(final FluidState state) {
+        super(state);
+    }
+
+}
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java
index 4c234e887c..f0bd7d01f5 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java
@@ -108,6 +108,13 @@ public abstract class CraftRegionAccessor implements RegionAccessor {
         return CraftBlock.at(this.getHandle(), new BlockPos(x, y, z)).getState();
     }
 
+    // Paper start - FluidState API
+    @Override
+    public io.papermc.paper.block.fluid.FluidData getFluidData(final int x, final int y, final int z) {
+        return io.papermc.paper.block.fluid.PaperFluidData.createData(getHandle().getFluidState(new BlockPos(x, y, z)));
+    }
+    // Paper end
+
     @Override
     public BlockData getBlockData(Location location) {
         return this.getBlockData(location.getBlockX(), location.getBlockY(), location.getBlockZ());
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java b/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java
index a23269e3bd..a57ac9dc8d 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java
@@ -304,4 +304,11 @@ public class CraftLimitedRegion extends CraftRegionAccessor implements LimitedRe
         return centerChunkZ;
     }
     // Paper end - Add more LimitedRegion API
+    // Paper start - Fluid API
+    @Override
+    public io.papermc.paper.block.fluid.FluidData getFluidData(int x, int y, int z) {
+        Preconditions.checkArgument(this.isInRegion(x, y, z), "Coordinates %s, %s, %s are not in the region", x, y, z);
+        return super.getFluidData(x, y, z);
+    }
+    // Paper end
 }