From 880a53248e003fde93be5952574705d67155fe09 Mon Sep 17 00:00:00 2001 From: Antony Riley Date: Tue, 28 Jul 2015 16:22:13 +0300 Subject: New ChunkGenerator.generate api for block ids and avoiding magic values. --- .../java/org/bukkit/craftbukkit/CraftServer.java | 6 + .../craftbukkit/generator/CraftChunkData.java | 197 +++++++++++++++++++++ .../generator/CustomChunkGenerator.java | 153 +++++++++------- 3 files changed, 295 insertions(+), 61 deletions(-) create mode 100644 src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java (limited to 'src/main/java/org/bukkit') diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java index f5722c6c..82a41b04 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -48,6 +48,7 @@ import org.bukkit.configuration.serialization.ConfigurationSerialization; import org.bukkit.conversations.Conversable; import org.bukkit.craftbukkit.command.VanillaCommandWrapper; import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.generator.CraftChunkData; import org.bukkit.craftbukkit.help.SimpleHelpMap; import org.bukkit.craftbukkit.inventory.CraftFurnaceRecipe; import org.bukkit.craftbukkit.inventory.CraftInventoryCustom; @@ -1631,6 +1632,11 @@ public final class CraftServer implements Server { return console.getIdleTimeout(); } + @Override + public ChunkGenerator.ChunkData createChunkData(World world) { + return new CraftChunkData(world); + } + @Deprecated @Override public UnsafeValues getUnsafe() { diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java new file mode 100644 index 00000000..438bed8c --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java @@ -0,0 +1,197 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.bukkit.craftbukkit.generator; + +import java.util.Arrays; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.material.MaterialData; + +/** + * Data to be used for the block types and data in a newly generated chunk. + */ +public final class CraftChunkData implements ChunkGenerator.ChunkData { + private final int maxHeight; + private final char[][] sections; + + public CraftChunkData(World world) { + this(world.getMaxHeight()); + } + + /* pp for tests */ CraftChunkData(int maxHeight) { + if (maxHeight > 256) { + throw new IllegalArgumentException("World height exceeded max chunk height"); + } + this.maxHeight = maxHeight; + // Minecraft hardcodes this to 16 chunk sections. + sections = new char[16][]; + } + + @Override + public int getMaxHeight() { + return maxHeight; + } + + @Override + public void setBlock(int x, int y, int z, Material material) { + setBlock(x, y, z, material.getId()); + } + + @Override + public void setBlock(int x, int y, int z, MaterialData material) { + setBlock(x, y, z, material.getItemTypeId(), material.getData()); + } + + @Override + public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, Material material) { + setRegion(xMin, yMin, zMin, xMax, yMax, zMax, material.getId()); + } + + @Override + public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, MaterialData material) { + setRegion(xMin, yMin, zMin, xMax, yMax, zMax, material.getItemTypeId(), material.getData()); + } + + @Override + public Material getType(int x, int y, int z) { + return Material.getMaterial(getTypeId(x, y, z)); + } + + @Override + public MaterialData getTypeAndData(int x, int y, int z) { + return getType(x, y, z).getNewData(getData(x, y, z)); + } + + @Override + public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, int blockId) { + setRegion(xMin, yMin, zMin, xMax, yMax, zMax, blockId, (byte) 0); + } + + @Override + public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, int blockId, int data) { + // Clamp to sane values. + if (xMin > 0xf || yMin >= maxHeight || zMin > 0xf) { + return; + } + if (xMin < 0) { + xMin = 0; + } + if (yMin < 0) { + yMin = 0; + } + if (zMin < 0) { + zMin = 0; + } + if (xMax > 0x10) { + xMax = 0x10; + } + if (yMax > maxHeight) { + yMax = maxHeight; + } + if (zMax > 0x10) { + zMax = 0x10; + } + if (xMin >= xMax || yMin >= yMax || zMin >= zMax) { + return; + } + char typeChar = (char) (blockId << 4 | data); + if (xMin == 0 && xMax == 0x10) { + if (zMin == 0 && zMax == 0x10) { + for (int y = yMin & 0xf0; y < yMax; y += 0x10) { + char[] section = getChunkSection(y, true); + if (y <= yMin) { + if (y + 0x10 > yMax) { + // First and last chunk section + Arrays.fill(section, (yMin & 0xf) << 8, (yMax & 0xf) << 8, typeChar); + } else { + // First chunk section + Arrays.fill(section, (yMin & 0xf) << 8, 0x1000, typeChar); + } + } else if (y + 0x10 >= yMax) { + // Last chunk section + Arrays.fill(section, 0, (yMax & 0xf) << 8, typeChar); + } else { + // Full chunk section + Arrays.fill(section, 0, 0x1000, typeChar); + } + } + } else { + for (int y = yMin; y < yMax; y++) { + char[] section = getChunkSection(y, true); + int offsetBase = (y & 0xf) << 8; + int min = offsetBase | (zMin << 4); + int max = offsetBase | (zMax << 4); + Arrays.fill(section, min, max, typeChar); + } + } + } else { + for (int y = yMin; y < yMax; y++) { + char[] section = getChunkSection(y, true); + int offsetBase = (y & 0xf) << 8; + for (int z = zMin; z < zMax; z++) { + int offset = offsetBase | z << 4; + Arrays.fill(section, offset | xMin, offset | xMax, typeChar); + } + } + } + } + + @Override + public void setBlock(int x, int y, int z, int blockId) { + setBlock(x, y, z, blockId, (byte) 0); + } + + @Override + public void setBlock(int x, int y, int z, int blockId, byte data) { + setBlock(x, y, z, (char) (blockId << 4 | data)); + } + + @Override + public int getTypeId(int x, int y, int z) { + if (x != (x & 0xf) || y < 0 || y >= maxHeight || z != (z & 0xf)) { + return 0; + } + char[] section = getChunkSection(y, false); + if (section == null) { + return 0; + } else { + return section[(y & 0xf) << 8 | z << 4 | x] >> 4; + } + } + + @Override + public byte getData(int x, int y, int z) { + if (x != (x & 0xf) || y < 0 || y >= maxHeight || z != (z & 0xf)) { + return (byte) 0; + } + char[] section = getChunkSection(y, false); + if (section == null) { + return (byte) 0; + } else { + return (byte) (section[(y & 0xf) << 8 | z << 4 | x] & 0xf); + } + } + + private void setBlock(int x, int y, int z, char type) { + if (x != (x & 0xf) || y < 0 || y >= maxHeight || z != (z & 0xf)) { + return; + } + char[] section = getChunkSection(y, true); + section[(y & 0xf) << 8 | z << 4 | x] = type; + } + + private char[] getChunkSection(int y, boolean create) { + char[] section = sections[y >> 4]; + if (create && section == null) { + sections[y >> 4] = section = new char[0x1000]; + } + return section; + } + + char[][] getRawChunkData() { + return sections; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java index 9ecdad45..04dd15db 100644 --- a/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java +++ b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java @@ -49,91 +49,122 @@ public class CustomChunkGenerator extends InternalChunkGenerator { biomegrid.biome = new BiomeBase[256]; world.getWorldChunkManager().getBiomeBlock(biomegrid.biome, x << 4, z << 4, 16, 16); - // Try extended block method (1.2+) - short[][] xbtypes = generator.generateExtBlockSections(this.world.getWorld(), this.random, x, z, biomegrid); - if (xbtypes != null) { + // Try ChunkData method (1.8+) + CraftChunkData data = (CraftChunkData) generator.generateChunkData(this.world.getWorld(), random, x, z, biomegrid); + if (data != null) { + char[][] sections = data.getRawChunkData(); chunk = new Chunk(this.world, x, z); - + ChunkSection[] csect = chunk.getSections(); - int scnt = Math.min(csect.length, xbtypes.length); - + int scnt = Math.min(csect.length, sections.length); + // Loop through returned sections for (int sec = 0; sec < scnt; sec++) { - if (xbtypes[sec] == null) { + if(sections[sec] == null) { continue; } - char[] secBlkID = new char[4096]; // Allocate blk ID bytes - short[] bdata = xbtypes[sec]; - for (int i = 0; i < bdata.length; i++) { - Block b = Block.getById(bdata[i]); - secBlkID[i] = (char) Block.d.b(b.getBlockData()); + char[] section = sections[sec]; + char emptyTest = 0; + for (int i = 0; i < 4096; i++) { + // Filter invalid block id & data values. + if (Block.d.a(section[i]) == null) { + section[i] = 0; + } + emptyTest |= section[i]; } // Build chunk section - csect[sec] = new ChunkSection(sec << 4, true, secBlkID); + if (emptyTest != 0) { + csect[sec] = new ChunkSection(sec << 4, true, section); + } } } - else { // Else check for byte-per-block section data - byte[][] btypes = generator.generateBlockSections(this.world.getWorld(), this.random, x, z, biomegrid); - - if (btypes != null) { + else { + // Try extended block method (1.2+) + short[][] xbtypes = generator.generateExtBlockSections(this.world.getWorld(), this.random, x, z, biomegrid); + if (xbtypes != null) { chunk = new Chunk(this.world, x, z); - + ChunkSection[] csect = chunk.getSections(); - int scnt = Math.min(csect.length, btypes.length); - + int scnt = Math.min(csect.length, xbtypes.length); + + // Loop through returned sections for (int sec = 0; sec < scnt; sec++) { - if (btypes[sec] == null) { + if (xbtypes[sec] == null) { continue; } - - char[] secBlkID = new char[4096]; // Allocate block ID bytes - for (int i = 0; i < secBlkID.length; i++) { - Block b = Block.getById(btypes[sec][i] & 0xFF); + char[] secBlkID = new char[4096]; // Allocate blk ID bytes + short[] bdata = xbtypes[sec]; + for (int i = 0; i < bdata.length; i++) { + Block b = Block.getById(bdata[i]); secBlkID[i] = (char) Block.d.b(b.getBlockData()); } + // Build chunk section csect[sec] = new ChunkSection(sec << 4, true, secBlkID); } } - else { // Else, fall back to pre 1.2 method - @SuppressWarnings("deprecation") - byte[] types = generator.generate(this.world.getWorld(), this.random, x, z); - int ydim = types.length / 256; - int scnt = ydim / 16; - - chunk = new Chunk(this.world, x, z); // Create empty chunk - - ChunkSection[] csect = chunk.getSections(); - - scnt = Math.min(scnt, csect.length); - // Loop through sections - for (int sec = 0; sec < scnt; sec++) { - ChunkSection cs = null; // Add sections when needed - char[] csbytes = null; - - for (int cy = 0; cy < 16; cy++) { - int cyoff = cy | (sec << 4); - - for (int cx = 0; cx < 16; cx++) { - int cxyoff = (cx * ydim * 16) + cyoff; - - for (int cz = 0; cz < 16; cz++) { - byte blk = types[cxyoff + (cz * ydim)]; - - if (blk != 0) { // If non-empty - if (cs == null) { // If no section yet, get one - cs = csect[sec] = new ChunkSection(sec << 4, true); - csbytes = cs.getIdArray(); + else { // Else check for byte-per-block section data + byte[][] btypes = generator.generateBlockSections(this.world.getWorld(), this.random, x, z, biomegrid); + + if (btypes != null) { + chunk = new Chunk(this.world, x, z); + + ChunkSection[] csect = chunk.getSections(); + int scnt = Math.min(csect.length, btypes.length); + + for (int sec = 0; sec < scnt; sec++) { + if (btypes[sec] == null) { + continue; + } + + char[] secBlkID = new char[4096]; // Allocate block ID bytes + for (int i = 0; i < secBlkID.length; i++) { + Block b = Block.getById(btypes[sec][i] & 0xFF); + secBlkID[i] = (char) Block.d.b(b.getBlockData()); + } + csect[sec] = new ChunkSection(sec << 4, true, secBlkID); + } + } + else { // Else, fall back to pre 1.2 method + @SuppressWarnings("deprecation") + byte[] types = generator.generate(this.world.getWorld(), this.random, x, z); + int ydim = types.length / 256; + int scnt = ydim / 16; + + chunk = new Chunk(this.world, x, z); // Create empty chunk + + ChunkSection[] csect = chunk.getSections(); + + scnt = Math.min(scnt, csect.length); + // Loop through sections + for (int sec = 0; sec < scnt; sec++) { + ChunkSection cs = null; // Add sections when needed + char[] csbytes = null; + + for (int cy = 0; cy < 16; cy++) { + int cyoff = cy | (sec << 4); + + for (int cx = 0; cx < 16; cx++) { + int cxyoff = (cx * ydim * 16) + cyoff; + + for (int cz = 0; cz < 16; cz++) { + byte blk = types[cxyoff + (cz * ydim)]; + + if (blk != 0) { // If non-empty + if (cs == null) { // If no section yet, get one + cs = csect[sec] = new ChunkSection(sec << 4, true); + csbytes = cs.getIdArray(); + } + + Block b = Block.getById(blk & 0xFF); + csbytes[(cy << 8) | (cz << 4) | cx] = (char) Block.d.b(b.getBlockData()); } - - Block b = Block.getById(blk & 0xFF); - csbytes[(cy << 8) | (cz << 4) | cx] = (char) Block.d.b(b.getBlockData()); } } } - } - // If section built, finish prepping its state - if (cs != null) { - cs.recalcBlockCounts(); + // If section built, finish prepping its state + if (cs != null) { + cs.recalcBlockCounts(); + } } } } -- cgit v1.2.3