From 237250c08d805400105adb1ba342e4690a1d68ff Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Wed, 10 Apr 2019 20:36:31 -0700 Subject: [PATCH] Add Heightmap API (#1724) Resolves #1672 This API is intended to expose useful heightmaps found in the server to API. This exposes all of the live world heightmaps currently in the server. If a heightmap becomes impossible to implement, api spec allows the implementation to throw UnsupportedOperationException (far better than returning some weird unexpected value). Tested via: https://gist.github.com/Spottedleaf/5d47f67c55a9fb870251ff344bfeb6b3 --- .../0182-Add-Heightmap-API.patch | 161 ++++++++++++++++++ .../0437-Add-Heightmap-API.patch | 54 ++++++ 2 files changed, 215 insertions(+) create mode 100644 Spigot-API-Patches/0182-Add-Heightmap-API.patch create mode 100644 Spigot-Server-Patches/0437-Add-Heightmap-API.patch diff --git a/Spigot-API-Patches/0182-Add-Heightmap-API.patch b/Spigot-API-Patches/0182-Add-Heightmap-API.patch new file mode 100644 index 0000000000..ec46f777e9 --- /dev/null +++ b/Spigot-API-Patches/0182-Add-Heightmap-API.patch @@ -0,0 +1,161 @@ +From 61d8f2edbf9a488e035f6308d126794e3fda2d9c Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sat, 1 Dec 2018 19:00:36 -0800 +Subject: [PATCH] Add Heightmap API + + +diff --git a/src/main/java/com/destroystokyo/paper/HeightmapType.java b/src/main/java/com/destroystokyo/paper/HeightmapType.java +new file mode 100644 +index 00000000..4cd9b5ed +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/HeightmapType.java +@@ -0,0 +1,35 @@ ++package com.destroystokyo.paper; ++ ++import org.bukkit.World; ++ ++/** ++ * Enumeration of different heightmap types maintained by the server. Generally using these maps is much faster ++ * than using an iterative search for a block in a given x, z coordinate. ++ */ ++public enum HeightmapType { ++ ++ /** ++ * The highest block used for lighting in the world. Also the block returned by {@link World#getHighestBlockYAt(int, int)}} ++ */ ++ LIGHT_BLOCKING, ++ ++ /** ++ * References the highest block in the world. ++ */ ++ ANY, ++ ++ /** ++ * References the highest solid block in a world. ++ */ ++ SOLID, ++ ++ /** ++ * References the highest solid or liquid block in a world. ++ */ ++ SOLID_OR_LIQUID, ++ ++ /** ++ * References the highest solid or liquid block in a world, excluding leaves. ++ */ ++ SOLID_OR_LIQUID_NO_LEAVES; ++} +diff --git a/src/main/java/org/bukkit/Location.java b/src/main/java/org/bukkit/Location.java +index b4f3e8ce..b78b76f3 100644 +--- a/src/main/java/org/bukkit/Location.java ++++ b/src/main/java/org/bukkit/Location.java +@@ -612,6 +612,33 @@ public class Location implements Cloneable, ConfigurationSerializable { + return centerLoc; + } + ++ // Paper start - Add heightmap api ++ ++ /** ++ * Returns a copy of this location except with y = getWorld().getHighestBlockYAt(this.getBlockX(), this.getBlockZ()) ++ * @return A copy of this location except with y = getWorld().getHighestBlockYAt(this.getBlockX(), this.getBlockZ()) ++ * @throws NullPointerException if {{@link #getWorld()}} is {@code null} ++ */ ++ @NotNull ++ public Location toHighestLocation() { ++ return this.toHighestLocation(com.destroystokyo.paper.HeightmapType.LIGHT_BLOCKING); ++ } ++ ++ /** ++ * Returns a copy of this location except with y = getWorld().getHighestBlockYAt(this.getBlockX(), this.getBlockZ(), heightmap) ++ * @param heightmap The heightmap to use for finding the highest y location. ++ * @return A copy of this location except with y = getWorld().getHighestBlockYAt(this.getBlockX(), this.getBlockZ(), heightmap) ++ * @throws NullPointerException if {{@link #getWorld()}} is {@code null} ++ * @throws UnsupportedOperationException if {@link World#getHighestBlockYAt(int, int, com.destroystokyo.paper.HeightmapType)} does not support the specified heightmap ++ */ ++ @NotNull ++ public Location toHighestLocation(@NotNull final com.destroystokyo.paper.HeightmapType heightmap) { ++ final Location ret = this.clone(); ++ ret.setY(this.getWorld().getHighestBlockYAt(this, heightmap)); ++ return ret; ++ } ++ // Paper end ++ + /** + * Creates explosion at this location with given power + * +diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java +index 3d8ff98a..4dc813f3 100644 +--- a/src/main/java/org/bukkit/World.java ++++ b/src/main/java/org/bukkit/World.java +@@ -154,6 +154,68 @@ public interface World extends PluginMessageRecipient, Metadatable { + @NotNull + public Block getHighestBlockAt(@NotNull Location location); + ++ // Paper start - Add heightmap API ++ /** ++ * Returns the highest block's y-coordinate at the specified block coordinates that match the specified heightmap's conditions. ++ * @param x The block's x-coordinate. ++ * @param z The block's z-coordinate. ++ * @param heightmap The specified heightmap to use. See {@link com.destroystokyo.paper.HeightmapType} ++ * @return The highest block's y-coordinate at (x, z) that matches the specified heightmap's conditions. ++ * @throws UnsupportedOperationException If the heightmap type is not supported. ++ * @implNote Implementations are recommended to use an iterative search as a fallback before resorting to ++ * throwing an {@code UnsupportedOperationException}. ++ * @see com.destroystokyo.paper.HeightmapType ++ */ ++ public int getHighestBlockYAt(int x, int z, @NotNull com.destroystokyo.paper.HeightmapType heightmap) throws UnsupportedOperationException; ++ ++ /** ++ * Returns the highest block's y-coordinate at the specified block coordinates that match the specified heightmap's conditions. ++ * Note that the y-coordinate of the specified location is ignored. ++ * @param location The specified block coordinates. ++ * @param heightmap The specified heightmap to use. See {@link com.destroystokyo.paper.HeightmapType} ++ * @return The highest block's y-coordinate at {@code location} that matches the specified heightmap's conditions. ++ * @throws UnsupportedOperationException If the heightmap type is not supported. ++ * @implNote Implementations are recommended to use an iterative search as a fallback before resorting to ++ * throwing an {@code UnsupportedOperationException}. ++ * @see com.destroystokyo.paper.HeightmapType ++ */ ++ default int getHighestBlockYAt(@NotNull Location location, @NotNull com.destroystokyo.paper.HeightmapType heightmap) throws UnsupportedOperationException { ++ return this.getHighestBlockYAt(location.getBlockX(), location.getBlockZ(), heightmap); ++ } ++ ++ /** ++ * Returns the highest {@link Block} at the specified block coordinates that match the specified heightmap's conditions. ++ * @param x The block's x-coordinate. ++ * @param z The block's z-coordinate. ++ * @param heightmap The specified heightmap to use. See {@link com.destroystokyo.paper.HeightmapType} ++ * @return The highest {@link Block} at (x, z) that matches the specified heightmap's conditions. ++ * @throws UnsupportedOperationException If the heightmap type is not supported. ++ * @implNote Implementations are recommended to use an iterative search as a fallback before resorting to ++ * throwing an {@code UnsupportedOperationException}. ++ * @see com.destroystokyo.paper.HeightmapType ++ */ ++ @NotNull ++ default Block getHighestBlockAt(int x, int z, @NotNull com.destroystokyo.paper.HeightmapType heightmap) throws UnsupportedOperationException { ++ return this.getBlockAt(x, this.getHighestBlockYAt(x, z, heightmap), z); ++ } ++ ++ /** ++ * Returns the highest {@link Block} at the specified block coordinates that match the specified heightmap's conditions. ++ * Note that the y-coordinate of the specified location is ignored. ++ * @param location The specified block coordinates. ++ * @param heightmap The specified heightmap to use. See {@link com.destroystokyo.paper.HeightmapType} ++ * @return The highest {@link Block} at {@code location} that matches the specified heightmap's conditions. ++ * @throws UnsupportedOperationException If the heightmap type is not supported. ++ * @implNote Implementations are recommended to use an iterative search as a fallback before resorting to ++ * throwing an {@code UnsupportedOperationException}. ++ * @see com.destroystokyo.paper.HeightmapType ++ */ ++ @NotNull ++ default Block getHighestBlockAt(@NotNull Location location, @NotNull com.destroystokyo.paper.HeightmapType heightmap) throws UnsupportedOperationException { ++ return this.getHighestBlockAt(location.getBlockX(), location.getBlockZ(), heightmap); ++ } ++ // Paper end ++ + /** + * Gets the {@link Chunk} at the given coordinates + * +-- +2.21.0 + diff --git a/Spigot-Server-Patches/0437-Add-Heightmap-API.patch b/Spigot-Server-Patches/0437-Add-Heightmap-API.patch new file mode 100644 index 0000000000..8b06d16537 --- /dev/null +++ b/Spigot-Server-Patches/0437-Add-Heightmap-API.patch @@ -0,0 +1,54 @@ +From aae0b5f4b74ab34bbd6d0f3bcd5c34e78bcffabb Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Tue, 1 Jan 2019 02:22:01 -0800 +Subject: [PATCH] Add Heightmap API + + +diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java +index b940f95bd..d20f6ac7e 100644 +--- a/src/main/java/net/minecraft/server/World.java ++++ b/src/main/java/net/minecraft/server/World.java +@@ -720,6 +720,7 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc + } + } + ++ public final int getHighestBlockY(final HeightMap.Type heightmap, final int x, final int z) { return this.a(heightmap, x, z); } // Paper - OBFHELPER + public int a(HeightMap.Type heightmap_type, int i, int j) { + int k; + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index 40ee34675..457aa5a3f 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -136,6 +136,28 @@ public class CraftWorld implements World { + return world.getHighestBlockYAt(HeightMap.Type.LIGHT_BLOCKING, new BlockPosition(x, 0, z)).getY(); + } + ++ // Paper start - Implement heightmap api ++ @Override ++ public int getHighestBlockYAt(final int x, final int z, final com.destroystokyo.paper.HeightmapType heightmap) throws UnsupportedOperationException { ++ this.loadChunk(x >> 4, z >> 4); // heightmap will ret 0 on unloaded areas ++ ++ switch (heightmap) { ++ case LIGHT_BLOCKING: ++ return this.world.getHighestBlockY(HeightMap.Type.LIGHT_BLOCKING, x, z); ++ case ANY: ++ return this.world.getHighestBlockY(HeightMap.Type.WORLD_SURFACE, x, z); ++ case SOLID: ++ return this.world.getHighestBlockY(HeightMap.Type.OCEAN_FLOOR, x, z); ++ case SOLID_OR_LIQUID: ++ return this.world.getHighestBlockY(HeightMap.Type.MOTION_BLOCKING, x, z); ++ case SOLID_OR_LIQUID_NO_LEAVES: ++ return this.world.getHighestBlockY(HeightMap.Type.MOTION_BLOCKING_NO_LEAVES, x, z); ++ default: ++ throw new UnsupportedOperationException(); ++ } ++ } ++ // Paper end ++ + public Location getSpawnLocation() { + BlockPosition spawn = world.getSpawn(); + return new Location(this, spawn.getX(), spawn.getY(), spawn.getZ()); +-- +2.21.0 +