diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java index 7e92056c85..2d870eec3f 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java @@ -13,6 +13,8 @@ import org.bukkit.craftbukkit.block.CraftBlock; import org.bukkit.entity.Entity; import org.bukkit.craftbukkit.util.ConcurrentSoftMap; import org.bukkit.ChunkSnapshot; +import net.minecraft.server.BiomeBase; +import net.minecraft.server.WorldChunkManager; public class CraftChunk implements Chunk { private WeakReference weakChunk; @@ -111,13 +113,100 @@ public class CraftChunk implements Chunk { * @return ChunkSnapshot */ public ChunkSnapshot getChunkSnapshot() { + return getChunkSnapshot(true, false, false); + } + + /** + * Capture thread-safe read-only snapshot of chunk data + * @param includeMaxblocky - if true, snapshot includes per-coordinate maximum Y values + * @param includeBiome - if true, snapshot includes per-coordinate biome type + * @param includeBiomeTempRain - if true, snapshot includes per-coordinate raw biome temperature and rainfall + * @return ChunkSnapshot + */ + public ChunkSnapshot getChunkSnapshot(boolean includeMaxblocky, boolean includeBiome, boolean includeBiomeTempRain) { net.minecraft.server.Chunk chunk = getHandle(); byte[] buf = new byte[32768 + 16384 + 16384 + 16384]; // Get big enough buffer for whole chunk chunk.a(buf, 0, 0, 0, 16, 128, 16, 0); // Get whole chunk - byte[] hmap = new byte[256]; // Get copy of height map - System.arraycopy(chunk.h, 0, hmap, 0, 256); + byte[] hmap = null; + + if (includeMaxblocky) { + hmap = new byte[256]; // Get copy of height map + System.arraycopy(chunk.h, 0, hmap, 0, 256); + } + + BiomeBase[] biome = null; + double[] biomeTemp = null; + double[] biomeRain = null; + + if (includeBiome || includeBiomeTempRain) { + WorldChunkManager wcm = chunk.world.getWorldChunkManager(); + BiomeBase[] bb = wcm.a(getX()<<4, getZ()<<4, 16, 16); + + if (includeBiome) { + biome = new BiomeBase[256]; + System.arraycopy(bb, 0, biome, 0, biome.length); + } + + if (includeBiomeTempRain) { + biomeTemp = new double[256]; + biomeRain = new double[256]; + System.arraycopy(wcm.a, 0, biomeTemp, 0, biomeTemp.length); + System.arraycopy(wcm.b, 0, biomeRain, 0, biomeRain.length); + } + } World w = getWorld(); - return new CraftChunkSnapshot(getX(), getZ(), w.getName(), w.getFullTime(), buf, hmap); + return new CraftChunkSnapshot(getX(), getZ(), w.getName(), w.getFullTime(), buf, hmap, biome, biomeTemp, biomeRain); + } + /** + * Empty chunk snapshot - nothing but air blocks, but can include valid biome data + */ + private static class EmptyChunkSnapshot extends CraftChunkSnapshot { + EmptyChunkSnapshot(int x, int z, String w, long wtime, BiomeBase[] biome, double[] biomeTemp, double[] biomeRain) { + super(x, z, w, wtime, null, null, biome, biomeTemp, biomeRain); + } + + public final int getBlockTypeId(int x, int y, int z) { + return 0; + } + + public final int getBlockData(int x, int y, int z) { + return 0; + } + + public final int getBlockSkyLight(int x, int y, int z) { + return 15; + } + + public final int getBlockEmittedLight(int x, int y, int z) { + return 0; + } + + public final int getHighestBlockYAt(int x, int z) { + return 0; + } } + public static ChunkSnapshot getEmptyChunkSnapshot(int x, int z, CraftWorld w, boolean includeBiome, boolean includeBiomeTempRain) { + BiomeBase[] biome = null; + double[] biomeTemp = null; + double[] biomeRain = null; + + if (includeBiome || includeBiomeTempRain) { + WorldChunkManager wcm = w.getHandle().getWorldChunkManager(); + BiomeBase[] bb = wcm.a(x<<4, z<<4, 16, 16); + + if (includeBiome) { + biome = new BiomeBase[256]; + System.arraycopy(bb, 0, biome, 0, biome.length); + } + + if (includeBiomeTempRain) { + biomeTemp = new double[256]; + biomeRain = new double[256]; + System.arraycopy(wcm.a, 0, biomeTemp, 0, biomeTemp.length); + System.arraycopy(wcm.b, 0, biomeRain, 0, biomeRain.length); + } + } + return new EmptyChunkSnapshot(x, z, w.getName(), w.getFullTime(), biome, biomeTemp, biomeRain); + } } \ No newline at end of file diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java b/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java index 5004575e15..3337093aa4 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java @@ -1,6 +1,9 @@ package org.bukkit.craftbukkit; import org.bukkit.ChunkSnapshot; +import org.bukkit.block.Biome; + +import net.minecraft.server.BiomeBase; /** * Represents a static, thread-safe snapshot of chunk of blocks * Purpose is to allow clean, efficient copy of a chunk data to be made, and then handed off for processing in another thread (e.g. map rendering) @@ -10,7 +13,10 @@ public class CraftChunkSnapshot implements ChunkSnapshot { private final String worldname; private final byte[] buf; // Flat buffer in uncompressed chunk file format private final byte[] hmap; // Height map - private final long capture_fulltime; + private final long captureFulltime; + private final BiomeBase[] biome; + private final double[] biomeTemp; + private final double[] biomeRain; private static final int BLOCKDATA_OFF = 32768; private static final int BLOCKLIGHT_OFF = BLOCKDATA_OFF + 16384; @@ -19,13 +25,16 @@ public class CraftChunkSnapshot implements ChunkSnapshot { /** * Constructor */ - CraftChunkSnapshot(int x, int z, String wname, long wtime, byte[] buf, byte[] hmap) { + CraftChunkSnapshot(int x, int z, String wname, long wtime, byte[] buf, byte[] hmap, BiomeBase[] biome, double[] biomeTemp, double[] biomeRain) { this.x = x; this.z = z; this.worldname = wname; - this.capture_fulltime = wtime; + this.captureFulltime = wtime; this.buf = buf; this.hmap = hmap; + this.biome = biome; + this.biomeTemp = biomeTemp; + this.biomeRain = biomeRain; } /** @@ -120,11 +129,71 @@ public class CraftChunkSnapshot implements ChunkSnapshot { return hmap[z << 4 | x] & 255; } + /** + * Get biome at given coordinates + * + * @param x X-coordinate + * @param z Z-coordinate + * @return Biome at given coordinate + */ + public Biome getBiome(int x, int z) { + BiomeBase base = biome[x << 4 | z]; + + if (base == BiomeBase.RAINFOREST) { + return Biome.RAINFOREST; + } else if (base == BiomeBase.SWAMPLAND) { + return Biome.SWAMPLAND; + } else if (base == BiomeBase.SEASONAL_FOREST) { + return Biome.SEASONAL_FOREST; + } else if (base == BiomeBase.FOREST) { + return Biome.FOREST; + } else if (base == BiomeBase.SAVANNA) { + return Biome.SAVANNA; + } else if (base == BiomeBase.SHRUBLAND) { + return Biome.SHRUBLAND; + } else if (base == BiomeBase.TAIGA) { + return Biome.TAIGA; + } else if (base == BiomeBase.DESERT) { + return Biome.DESERT; + } else if (base == BiomeBase.PLAINS) { + return Biome.PLAINS; + } else if (base == BiomeBase.ICE_DESERT) { + return Biome.ICE_DESERT; + } else if (base == BiomeBase.TUNDRA) { + return Biome.TUNDRA; + } else if (base == BiomeBase.HELL) { + return Biome.HELL; + } + return null; + } + + /** + * Get raw biome temperature (0.0-1.0) at given coordinate + * + * @param x X-coordinate + * @param z Z-coordinate + * @return temperature at given coordinate + */ + public double getRawBiomeTemperature(int x, int z) { + return biomeTemp[x << 4 | z]; + } + + /** + * Get raw biome rainfall (0.0-1.0) at given coordinate + * + * @param x X-coordinate + * @param z Z-coordinate + * @return rainfall at given coordinate + */ + public double getRawBiomeRainfall(int x, int z) { + return biomeRain[x << 4 | z]; + } + /** * Get world full time when chunk snapshot was captured * @return time in ticks */ public long getCaptureFullTime() { - return capture_fulltime; + return captureFulltime; } } diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index 3053937d31..b31cf5f1a6 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -23,6 +23,7 @@ import org.bukkit.util.Vector; import org.bukkit.BlockChangeDelegate; import org.bukkit.Bukkit; import org.bukkit.generator.ChunkGenerator; +import org.bukkit.ChunkSnapshot; import org.bukkit.Location; import org.bukkit.TreeType; import org.bukkit.World; @@ -760,4 +761,8 @@ public class CraftWorld implements World { throw new IllegalArgumentException("Cannot spawn an entity for " + clazz.getName()); } + + public ChunkSnapshot getEmptyChunkSnapshot(int x, int z, boolean includeBiome, boolean includeBiomeTempRain) { + return CraftChunk.getEmptyChunkSnapshot(x, z, this, includeBiome, includeBiomeTempRain); + } }