diff --git a/sources/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/sources/src/main/java/org/bukkit/craftbukkit/CraftChunk.java new file mode 100644 index 000000000..13e429ded --- /dev/null +++ b/sources/src/main/java/org/bukkit/craftbukkit/CraftChunk.java @@ -0,0 +1,317 @@ +package org.bukkit.craftbukkit; + +import java.lang.ref.WeakReference; +import java.util.Arrays; + +import java.util.Random; +import net.minecraft.server.*; + +import org.bukkit.Chunk; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.entity.Entity; +import org.bukkit.ChunkSnapshot; + +public class CraftChunk implements Chunk { + private WeakReference weakChunk; + private final WorldServer worldServer; + private final int x; + private final 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) { + this.weakChunk = new WeakReference(chunk); + + worldServer = (WorldServer) getHandle().world; + x = getHandle().locX; + z = getHandle().locZ; + } + + public World getWorld() { + return worldServer.getWorld(); + } + + public CraftWorld getCraftWorld() { + return (CraftWorld) getWorld(); + } + + public net.minecraft.server.Chunk getHandle() { + net.minecraft.server.Chunk c = weakChunk.get(); + + if (c == null) { + c = worldServer.getChunkAt(x, z); + + weakChunk = new WeakReference(c); + } + + return c; + } + + void breakLink() { + weakChunk.clear(); + } + + public int getX() { + return x; + } + + public int getZ() { + return z; + } + + @Override + public String toString() { + return "CraftChunk{" + "x=" + getX() + "z=" + getZ() + '}'; + } + + public Block getBlock(int x, int y, int z) { + return new CraftBlock(this, (getX() << 4) | (x & 0xF), y, (getZ() << 4) | (z & 0xF)); + } + + public Entity[] getEntities() { + int count = 0, index = 0; + net.minecraft.server.Chunk chunk = getHandle(); + + for (int i = 0; i < 16; i++) { + count += chunk.entitySlices[i].size(); + } + + Entity[] entities = new Entity[count]; + + for (int i = 0; i < 16; i++) { + + //for (Object obj : chunk.entitySlices[i].toArray()) { + // if (!(obj instanceof net.minecraft.server.Entity)) { + // NeonPaper start - speed up (was with chunk.entitySlices[i].toArray() and cast checks which costs a lot of performance if called often) + for (net.minecraft.server.Entity entity : chunk.entitySlices[i]) { + if (entity == null) { + continue; + } + + //entities[index++] = ((net.minecraft.server.Entity) obj).getBukkitEntity(); + entities[index++] = entity.getBukkitEntity(); + } + //NeonPaper end + } + + return entities; + } + + public BlockState[] getTileEntities() { + 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 BlockPosition)) { + continue; + } + + BlockPosition position = (BlockPosition) obj; + entities[index++] = worldServer.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()).getState(); + } + + return entities; + } + + public boolean isLoaded() { + return getWorld().isChunkLoaded(this); + } + + public boolean load() { + return getWorld().loadChunk(getX(), getZ(), true); + } + + public boolean load(boolean generate) { + return getWorld().loadChunk(getX(), getZ(), generate); + } + + public boolean unload() { + return getWorld().unloadChunk(getX(), getZ()); + } + + @Override + public boolean isSlimeChunk() { + // 987234911L is deterimined in EntitySlime when seeing if a slime can spawn in a chunk + return getHandle().a(worldServer.spigotConfig.slimeSeed).nextInt(10) == 0; + } + + public boolean unload(boolean save) { + return getWorld().unloadChunk(getX(), getZ(), save); + } + + public boolean unload(boolean save, boolean safe) { + return getWorld().unloadChunk(getX(), getZ(), save, safe); + } + + public ChunkSnapshot getChunkSnapshot() { + return getChunkSnapshot(true, false, false); + } + + public ChunkSnapshot getChunkSnapshot(boolean includeMaxBlockY, boolean includeBiome, boolean includeBiomeTempRain) { + net.minecraft.server.Chunk chunk = getHandle(); + + ChunkSection[] cs = chunk.getSections(); + 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]; + + 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[] rawIds = new byte[4096]; + NibbleArray data = new NibbleArray(); + cs[i].getBlocks().exportData(rawIds, data); + + byte[] dataValues = sectionBlockData[i] = data.asBytes(); + + // Copy base IDs + for (int j = 0; j < 4096; j++) { + blockids[j] = (short) (rawIds[j] & 0xFF); + } + + sectionBlockIDs[i] = blockids; + + if (cs[i].getSkyLightArray() == null) { + sectionSkyLights[i] = emptyData; + } else { + sectionSkyLights[i] = new byte[2048]; + System.arraycopy(cs[i].getSkyLightArray().asBytes(), 0, sectionSkyLights[i], 0, 2048); + } + sectionEmitLights[i] = new byte[2048]; + System.arraycopy(cs[i].getEmittedLightArray().asBytes(), 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); + } + + BiomeBase[] biome = null; + double[] biomeTemp = null; + double[] biomeRain = null; + + if (includeBiome || includeBiomeTempRain) { + WorldChunkManager wcm = chunk.world.getWorldChunkManager(); + + if (includeBiome) { + biome = new BiomeBase[256]; + for (int i = 0; i < 256; i++) { + biome[i] = chunk.getBiome(new BlockPosition(i & 0xF, 0, i >> 4), wcm); + } + } + + if (includeBiomeTempRain) { + biomeTemp = new double[256]; + biomeRain = new double[256]; + float[] dat = getTemperatures(wcm, getX() << 4, getZ() << 4); + + for (int i = 0; i < 256; i++) { + biomeTemp[i] = dat[i]; + } + + /* Removed 15w46a + dat = wcm.getWetness(null, getX() << 4, getZ() << 4, 16, 16); + + for (int i = 0; i < 256; i++) { + biomeRain[i] = dat[i]; + } + */ + } + } + + World world = getWorld(); + 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) { + BiomeBase[] biome = null; + double[] biomeTemp = null; + double[] biomeRain = null; + + if (includeBiome || includeBiomeTempRain) { + WorldChunkManager wcm = world.getHandle().getWorldChunkManager(); + + if (includeBiome) { + biome = new BiomeBase[256]; + for (int i = 0; i < 256; i++) { + biome[i] = world.getHandle().getBiome(new BlockPosition((x << 4) + (i & 0xF), 0, (z << 4) + (i >> 4))); + } + } + + if (includeBiomeTempRain) { + biomeTemp = new double[256]; + biomeRain = new double[256]; + float[] dat = getTemperatures(wcm, x << 4, z << 4); + + for (int i = 0; i < 256; i++) { + biomeTemp[i] = dat[i]; + } + + /* Removed 15w46a + dat = wcm.getWetness(null, x << 4, z << 4, 16, 16); + + for (int i = 0; i < 256; i++) { + biomeRain[i] = dat[i]; + } + */ + } + } + + /* 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); + } + + private static float[] getTemperatures(WorldChunkManager chunkmanager, int chunkX, int chunkZ) { + BiomeBase[] biomes = chunkmanager.getBiomes(null, chunkX, chunkZ, 16, 16); + float[] temps = new float[biomes.length]; + + for (int i = 0; i < biomes.length; i++) { + float temp = biomes[i].getTemperature(); // Vanilla of olde: ((int) biomes[i].temperature * 65536.0F) / 65536.0F + + if (temp > 1F) { + temp = 1F; + } + + temps[i] = temp; + } + + return temps; + } + + static { + Arrays.fill(emptySkyLight, (byte) 0xFF); + } +}