diff --git a/overviewer_core/biome.py b/overviewer_core/biome.py new file mode 100644 index 0000000..8d79ef3 --- /dev/null +++ b/overviewer_core/biome.py @@ -0,0 +1,40 @@ +# This file is part of the Minecraft Overviewer. +# +# Minecraft Overviewer is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, either version 3 of the License, or (at +# your option) any later version. +# +# Minecraft Overviewer is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details, and note that Jeffrey Epstein didn't kill +# himself. +# +# You should have received a copy of the GNU General Public License along +# with the Overviewer. If not, see . + +import numpy + +class BiomeDispensary: + """Turns biome arrays of either 256 or 1024 integer values into 16x16 2d arrays, + which can then be retrieved for any Y level with get_biome. + """ + def __init__(self, biome_array): + self.biome_len = len(biome_array) + if self.biome_len == 256: + self.biomes = [biome_array.reshape((16, 16))] + elif self.biome_len == 1024: + self.biomes = [None] * 4 + for i in range(0, 4): + # Map 256 values of the array to each self.biomes entry, resulting + # in 4 entries + self.biomes[i] = biome_array[i * 256:(i + 1) * 256].reshape((16, 16)) + + def get_biome(self, y_level): + if y_level < 0: + return None + if self.biome_len == 256: + return self.biomes[0] + else: + return self.biomes[y_level // 4] diff --git a/overviewer_core/src/iterate.c b/overviewer_core/src/iterate.c index 5fc9b5b..99a8b3d 100644 --- a/overviewer_core/src/iterate.c +++ b/overviewer_core/src/iterate.c @@ -109,10 +109,12 @@ static inline void load_chunk_section(ChunkData* dest, int32_t i, PyObject* sect dest->sections[i].data = (PyArrayObject*)PyDict_GetItemString(section, "Data"); dest->sections[i].skylight = (PyArrayObject*)PyDict_GetItemString(section, "SkyLight"); dest->sections[i].blocklight = (PyArrayObject*)PyDict_GetItemString(section, "BlockLight"); + dest->sections[i].biomes = (PyArrayObject*)PyDict_GetItemString(section, "Biomes"); Py_INCREF(dest->sections[i].blocks); Py_INCREF(dest->sections[i].data); Py_INCREF(dest->sections[i].skylight); Py_INCREF(dest->sections[i].blocklight); + Py_INCREF(dest->sections[i].biomes); } /* loads the given chunk into the chunks[] array in the state @@ -130,13 +132,12 @@ bool load_chunk(RenderState* state, int32_t x, int32_t z, uint8_t required) { if (dest->loaded) return false; - /* set up reasonable defaults */ - dest->biomes = NULL; for (i = 0; i < SECTIONS_PER_CHUNK; i++) { dest->sections[i].blocks = NULL; dest->sections[i].data = NULL; dest->sections[i].skylight = NULL; dest->sections[i].blocklight = NULL; + dest->sections[i].biomes = NULL; } dest->loaded = 1; @@ -166,8 +167,6 @@ bool load_chunk(RenderState* state, int32_t x, int32_t z, uint8_t required) { return true; } - dest->biomes = (PyArrayObject*)PyDict_GetItemString(chunk, "Biomes"); - Py_INCREF(dest->biomes); for (i = 0; i < PySequence_Fast_GET_SIZE(sections); i++) { PyObject* ycoord = NULL; @@ -194,12 +193,12 @@ unload_all_chunks(RenderState* state) { for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { if (state->chunks[i][j].loaded) { - Py_XDECREF(state->chunks[i][j].biomes); for (k = 0; k < SECTIONS_PER_CHUNK; k++) { Py_XDECREF(state->chunks[i][j].sections[k].blocks); Py_XDECREF(state->chunks[i][j].sections[k].data); Py_XDECREF(state->chunks[i][j].sections[k].skylight); Py_XDECREF(state->chunks[i][j].sections[k].blocklight); + Py_XDECREF(state->chunks[i][j].sections[k].biomes); } state->chunks[i][j].loaded = 0; } diff --git a/overviewer_core/src/overviewer.h b/overviewer_core/src/overviewer.h index 7e450dc..8cac99d 100644 --- a/overviewer_core/src/overviewer.h +++ b/overviewer_core/src/overviewer.h @@ -31,7 +31,7 @@ // increment this value if you've made a change to the c extesion // and want to force users to rebuild -#define OVERVIEWER_EXTENSION_VERSION 77 +#define OVERVIEWER_EXTENSION_VERSION 78 #include #include @@ -87,7 +87,7 @@ typedef struct { /* all the sections in a given chunk */ struct { /* all there is to know about each section */ - PyArrayObject *blocks, *data, *skylight, *blocklight; + PyArrayObject *blocks, *data, *skylight, *blocklight, *biomes; } sections[SECTIONS_PER_CHUNK]; } ChunkData; typedef struct { @@ -210,7 +210,7 @@ static inline uint32_t get_data(RenderState* state, DataType type, int32_t x, in data_array = state->chunks[chunkx][chunkz].sections[chunky].skylight; break; case BIOMES: - data_array = state->chunks[chunkx][chunkz].biomes; + data_array = state->chunks[chunkx][chunkz].sections[chunky].biomes; }; if (data_array == NULL) diff --git a/overviewer_core/world.py b/overviewer_core/world.py index ae88913..d5fe11a 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -26,6 +26,7 @@ import numpy from . import nbt from . import cache +from .biome import BiomeDispensary """ This module has routines for extracting information about available worlds @@ -1370,13 +1371,15 @@ class RegionSet(object): biomes = numpy.frombuffer(biomes, dtype=numpy.uint8) else: biomes = numpy.asarray(biomes) - biomes = biomes.reshape((16,16)) + #biomes = biomes.reshape((16,16)) + biome_giver = BiomeDispensary(biomes) else: # Worlds converted by Jeb's program may be missing the Biomes key. # Additionally, 19w09a worlds have an empty array as biomes key # in some cases. - biomes = numpy.zeros((16, 16), dtype=numpy.uint8) - chunk_data['Biomes'] = biomes + #biomes = numpy.zeros((16, 16), dtype=numpy.uint8) + biome_giver = BiomeDispensary(numpy.zeros(256, dtype=numpy.uint8)) + #chunk_data['Biomes'] = biomes unrecognized_block_types = {} for section in chunk_data['Sections']: @@ -1384,6 +1387,7 @@ class RegionSet(object): # Turn the skylight array into a 16x16x16 matrix. The array comes # packed 2 elements per byte, so we need to expand it. try: + section['Biomes'] = biome_giver.get_biome(section["Y"]) if 'SkyLight' in section: skylight = numpy.frombuffer(section['SkyLight'], dtype=numpy.uint8) skylight = skylight.reshape((16,16,8)) @@ -1607,6 +1611,9 @@ class RotatedRegionSet(RegionSetWrapper): for section in chunk_data['Sections']: section = dict(section) newsections.append(section) + biomes = numpy.swapaxes(section['Biomes'], 0, 1) + biomes = numpy.rot90(biomes, self.north_dir) + section['Biomes'] = numpy.swapaxes(biomes, 0, 1) for arrayname in ['Blocks', 'Data', 'SkyLight', 'BlockLight']: array = section[arrayname] # Since the anvil change, arrays are arranged with axes Y,Z,X @@ -1619,9 +1626,6 @@ class RotatedRegionSet(RegionSetWrapper): chunk_data['Sections'] = newsections # same as above, for biomes (Z/X indexed) - biomes = numpy.swapaxes(chunk_data['Biomes'], 0, 1) - biomes = numpy.rot90(biomes, self.north_dir) - chunk_data['Biomes'] = numpy.swapaxes(biomes, 0, 1) return chunk_data def get_chunk_mtime(self, x, z):