From c51c895cd88fab3a5fd8cc812abc4d188a94c7b7 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Thu, 1 Mar 2012 13:54:59 -0600 Subject: [PATCH] Implemented snapshot support for 1.2. --- .../org/bukkit/craftbukkit/CraftChunk.java | 156 ++++++++++++------ .../craftbukkit/CraftChunkSnapshot.java | 61 +++---- 2 files changed, 143 insertions(+), 74 deletions(-) diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java index f4d2d0bde0..1634072cb6 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java @@ -1,9 +1,13 @@ package org.bukkit.craftbukkit; import java.lang.ref.WeakReference; -import net.minecraft.server.ChunkPosition; +import java.util.Arrays; +import net.minecraft.server.BiomeBase; +import net.minecraft.server.ChunkPosition; +import net.minecraft.server.ChunkSection; import net.minecraft.server.EmptyChunk; +import net.minecraft.server.WorldChunkManager; import net.minecraft.server.WorldServer; import org.bukkit.Chunk; @@ -13,20 +17,21 @@ import org.bukkit.block.BlockState; import org.bukkit.craftbukkit.block.CraftBlock; import org.bukkit.entity.Entity; import org.bukkit.ChunkSnapshot; -import net.minecraft.server.BiomeBase; -import net.minecraft.server.WorldChunkManager; -import org.apache.commons.lang.NotImplementedException; public class CraftChunk implements Chunk { private WeakReference weakChunk; private WorldServer worldServer; private int x; private int z; + private static final byte[] emptyData = new byte[2048]; + private static final short[] emptyBlockIDs = new short[4096]; + private static final byte[] emptySkyLight = new byte[2048]; public CraftChunk(net.minecraft.server.Chunk chunk) { if (!(chunk instanceof EmptyChunk)) { this.weakChunk = new WeakReference(chunk); } + worldServer = (WorldServer) getHandle().world; x = getHandle().x; z = getHandle().z; @@ -42,12 +47,15 @@ public class CraftChunk implements Chunk { public net.minecraft.server.Chunk getHandle() { net.minecraft.server.Chunk c = weakChunk.get(); + if (c == null) { c = worldServer.getChunkAt(x, z); + if (!(c instanceof EmptyChunk)) { weakChunk = new WeakReference(c); } } + return c; } @@ -75,19 +83,23 @@ public class CraftChunk implements Chunk { public Entity[] getEntities() { int count = 0, index = 0; net.minecraft.server.Chunk chunk = getHandle(); + for (int i = 0; i < 8; i++) { count += chunk.entitySlices[i].size(); } Entity[] entities = new Entity[count]; + for (int i = 0; i < 8; i++) { for (Object obj : chunk.entitySlices[i].toArray()) { if (!(obj instanceof net.minecraft.server.Entity)) { continue; } + entities[index++] = ((net.minecraft.server.Entity) obj).getBukkitEntity(); } } + return entities; } @@ -95,10 +107,12 @@ public class CraftChunk implements Chunk { int index = 0; net.minecraft.server.Chunk chunk = getHandle(); BlockState[] entities = new BlockState[chunk.tileEntities.size()]; + for (Object obj : chunk.tileEntities.keySet().toArray()) { if (!(obj instanceof ChunkPosition)) { continue; } + ChunkPosition position = (ChunkPosition) obj; entities[index++] = worldServer.getWorld().getBlockAt(position.x + (chunk.x << 4), position.y, position.z + (chunk.z << 4)).getState(); } @@ -133,16 +147,63 @@ public class CraftChunk implements Chunk { return getChunkSnapshot(true, false, false); } - public ChunkSnapshot getChunkSnapshot(boolean includeMaxblocky, boolean includeBiome, boolean includeBiomeTempRain) { + 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.getData(buf, 0, 0, 0, 16, 128, 16, 0); // Get whole chunk - byte[] hmap = null; - if (true) throw new NotImplementedException("Chunk snapshots do not yet work"); // TODO: Snapshots. + ChunkSection[] cs = chunk.h(); /* Get sections */ + short[][] sectionBlockIDs = new short[cs.length][]; + byte[][] sectionBlockData = new byte[cs.length][]; + byte[][] sectionSkyLights = new byte[cs.length][]; + byte[][] sectionEmitLights = new byte[cs.length][]; + boolean[] sectionEmpty = new boolean[cs.length]; - if (includeMaxblocky) { - hmap = new byte[256]; // Get copy of height map + for (int i = 0; i < cs.length; i++) { + if (cs[i] == null) { /* Section is empty? */ + sectionBlockIDs[i] = emptyBlockIDs; + sectionBlockData[i] = emptyData; + sectionSkyLights[i] = emptySkyLight; + sectionEmitLights[i] = emptyData; + sectionEmpty[i] = true; + } else { /* Not empty */ + short[] blockids = new short[4096]; + byte[] baseids = cs[i].g(); + + /* Copy base IDs */ + for (int j = 0; j < 4096; j++) { + blockids[j] = (short) (baseids[j] & 0xFF); + } + + if (cs[i].h() != null) { /* If we've got extended IDs */ + byte[] extids = cs[i].h().a; + + for (int j = 0; j < 2048; j++) { + short b = (short) (extids[j] & 0xFF); + + if (b == 0) { + continue; + } + + blockids[j<<1] |= (b & 0xF0) << 4; + blockids[(j<<1)+1] |= (b & 0x0F) << 8; + } + } + + sectionBlockIDs[i] = blockids; + + /* Get block data nibbles */ + sectionBlockData[i] = new byte[2048]; + System.arraycopy(cs[i].i().a, 0, sectionBlockData[i], 0, 2048); + sectionSkyLights[i] = new byte[2048]; + System.arraycopy(cs[i].k().a, 0, sectionSkyLights[i], 0, 2048); + sectionEmitLights[i] = new byte[2048]; + System.arraycopy(cs[i].j().a, 0, sectionEmitLights[i], 0, 2048); + } + } + + int[] hmap = null; + + if (includeMaxBlockY) { + hmap = new int[256]; // Get copy of height map System.arraycopy(chunk.heightMap, 0, hmap, 0, 256); } @@ -162,44 +223,21 @@ public class CraftChunk implements Chunk { biomeTemp = new double[256]; biomeRain = new double[256]; float[] dat = wcm.getTemperatures((float[]) null, getX() << 4, getZ() << 4, 16, 16); - for (int i = 0; i < 256; i++) + + for (int i = 0; i < 256; i++) { biomeTemp[i] = dat[i]; + } + dat = wcm.getWetness((float[]) null, getX() << 4, getZ() << 4, 16, 16); - for (int i = 0; i < 256; i++) + + for (int i = 0; i < 256; i++) { biomeRain[i] = dat[i]; + } } } + World world = getWorld(); - return new CraftChunkSnapshot(getX(), getZ(), world.getName(), world.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 worldName, long time, BiomeBase[] biome, double[] biomeTemp, double[] biomeRain) { - super(x, z, worldName, time, 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; - } + return new CraftChunkSnapshot(getX(), getZ(), world.getName(), world.getFullTime(), sectionBlockIDs, sectionBlockData, sectionSkyLights, sectionEmitLights, sectionEmpty, hmap, biome, biomeTemp, biomeRain); } public static ChunkSnapshot getEmptyChunkSnapshot(int x, int z, CraftWorld world, boolean includeBiome, boolean includeBiomeTempRain) { @@ -219,13 +257,39 @@ public class CraftChunk implements Chunk { biomeTemp = new double[256]; biomeRain = new double[256]; float[] dat = wcm.getTemperatures((float[]) null, x << 4, z << 4, 16, 16); - for (int i = 0; i < 256; i++) + + for (int i = 0; i < 256; i++) { biomeTemp[i] = dat[i]; + } + dat = wcm.getWetness((float[]) null, x << 4, z << 4, 16, 16); - for (int i = 0; i < 256; i++) + + for (int i = 0; i < 256; i++) { biomeRain[i] = dat[i]; + } } } - return new EmptyChunkSnapshot(x, z, world.getName(), world.getFullTime(), biome, biomeTemp, biomeRain); + + /* Fill with empty data */ + int hSection = world.getMaxHeight() >> 4; + short[][] blockIDs = new short[hSection][]; + byte[][] skyLight = new byte[hSection][]; + byte[][] emitLight = new byte[hSection][]; + byte[][] blockData = new byte[hSection][]; + boolean[] empty = new boolean[hSection]; + + for (int i = 0; i < hSection; i++) { + blockIDs[i] = emptyBlockIDs; + skyLight[i] = emptySkyLight; + emitLight[i] = emptyData; + blockData[i] = emptyData; + empty[i] = true; + } + + return new CraftChunkSnapshot(x, z, world.getName(), world.getFullTime(), blockIDs, blockData, skyLight, emitLight, empty, new int[256], biome, biomeTemp, biomeRain); + } + + static { + Arrays.fill(emptySkyLight, (byte) 0xFF); } } diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java b/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java index 6d0e8b1c88..edf701be62 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java @@ -13,23 +13,27 @@ import net.minecraft.server.BiomeBase; public class CraftChunkSnapshot implements ChunkSnapshot { private final int x, z; private final String worldname; - private final byte[] buf; // Flat buffer in uncompressed chunk file format - private final byte[] hmap; // Height map + private final short[][] blockids; /* Block IDs, by section */ + private final byte[][] blockdata; + private final byte[][] skylight; + private final byte[][] emitlight; + private final boolean[] empty; + private final int[] hmap; // Height map 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; - private static final int SKYLIGHT_OFF = BLOCKLIGHT_OFF + 16384; - - CraftChunkSnapshot(int x, int z, String wname, long wtime, byte[] buf, byte[] hmap, BiomeBase[] biome, double[] biomeTemp, double[] biomeRain) { + CraftChunkSnapshot(int x, int z, String wname, long wtime, short[][] sectionBlockIDs, byte[][] sectionBlockData, byte[][] sectionSkyLights, byte[][] sectionEmitLights, boolean[] sectionEmpty, int[] hmap, BiomeBase[] biome, double[] biomeTemp, double[] biomeRain) { this.x = x; this.z = z; this.worldname = wname; this.captureFulltime = wtime; - this.buf = buf; + this.blockids = sectionBlockIDs; + this.blockdata = sectionBlockData; + this.skylight = sectionSkyLights; + this.emitlight = sectionEmitLights; + this.empty = sectionEmpty; this.hmap = hmap; this.biome = biome; this.biomeTemp = biomeTemp; @@ -48,45 +52,46 @@ public class CraftChunkSnapshot implements ChunkSnapshot { return worldname; } - public int getBlockTypeId(int x, int y, int z) { - return buf[x << 11 | z << 7 | y] & 255; + public final int getBlockTypeId(int x, int y, int z) { + return blockids[y >> 4][((y & 0xF) << 8) | (z << 4) | x]; } - public int getBlockData(int x, int y, int z) { - int off = ((x << 10) | (z << 6) | (y >> 1)) + BLOCKDATA_OFF; - - return ((y & 1) == 0) ? (buf[off] & 0xF) : ((buf[off] >> 4) & 0xF); + public final int getBlockData(int x, int y, int z) { + int off = ((y & 0xF) << 7) | (z << 3) | (x >> 1); + return (blockdata[y >> 4][off] >> ((x & 1) << 2)) & 0xF; } - public int getBlockSkyLight(int x, int y, int z) { - int off = ((x << 10) | (z << 6) | (y >> 1)) + SKYLIGHT_OFF; - - return ((y & 1) == 0) ? (buf[off] & 0xF) : ((buf[off] >> 4) & 0xF); + public final int getBlockSkyLight(int x, int y, int z) { + int off = ((y & 0xF) << 7) | (z << 3) | (x >> 1); + return (skylight[y >> 4][off] >> ((x & 1) << 2)) & 0xF; } - public int getBlockEmittedLight(int x, int y, int z) { - int off = ((x << 10) | (z << 6) | (y >> 1)) + BLOCKLIGHT_OFF; - - return ((y & 1) == 0) ? (buf[off] & 0xF) : ((buf[off] >> 4) & 0xF); + public final int getBlockEmittedLight(int x, int y, int z) { + int off = ((y & 0xF) << 7) | (z << 3) | (x >> 1); + return (emitlight[y >> 4][off] >> ((x & 1) << 2)) & 0xF; } - public int getHighestBlockYAt(int x, int z) { - return hmap[z << 4 | x] & 255; + public final int getHighestBlockYAt(int x, int z) { + return hmap[z << 4 | x]; } - public Biome getBiome(int x, int z) { + public final Biome getBiome(int x, int z) { return CraftBlock.biomeBaseToBiome(biome[z << 4 | x]); } - public double getRawBiomeTemperature(int x, int z) { + public final double getRawBiomeTemperature(int x, int z) { return biomeTemp[z << 4 | x]; } - public double getRawBiomeRainfall(int x, int z) { + public final double getRawBiomeRainfall(int x, int z) { return biomeRain[z << 4 | x]; } - public long getCaptureFullTime() { + public final long getCaptureFullTime() { return captureFulltime; } + + public final boolean isSectionEmpty(int sy) { + return empty[sy]; + } }