0

Preliminary modifications to world.py for new format. Untested.

This commit is contained in:
Andrew Brown
2012-02-18 11:04:19 -05:00
parent d669034c4c
commit f7cc50fa9e

View File

@@ -95,10 +95,10 @@ class World(object):
if not os.path.exists(os.path.join(self.worlddir, "level.dat")): if not os.path.exists(os.path.join(self.worlddir, "level.dat")):
raise ValueError("level.dat not found in %s" % self.worlddir) raise ValueError("level.dat not found in %s" % self.worlddir)
# figure out chunk format is in use if not mcregion, error out early # Hard-code this to only work with format version 19133, "Anvil"
data = nbt.load(os.path.join(self.worlddir, "level.dat"))[1]['Data'] data = nbt.load(os.path.join(self.worlddir, "level.dat"))[1]['Data']
if not ('version' in data and data['version'] == 19132): if not ('version' in data and data['version'] == 19133):
logging.critical("Sorry, This version of Minecraft-Overviewer only works with the new McRegion chunk format") logging.critical("Sorry, This version of Minecraft-Overviewer only works with the 'Anvil' chunk format")
raise ValueError("World at %s is not compatible with Overviewer" % self.worlddir) raise ValueError("World at %s is not compatible with Overviewer" % self.worlddir)
# This isn't much data, around 15 keys and values for vanilla worlds. # This isn't much data, around 15 keys and values for vanilla worlds.
@@ -108,12 +108,12 @@ class World(object):
# Scan worlddir to try to identify all region sets. Since different # Scan worlddir to try to identify all region sets. Since different
# server mods like to arrange regions differently and there does not # server mods like to arrange regions differently and there does not
# seem to be any set standard on what dimensions are in each world, # seem to be any set standard on what dimensions are in each world,
# just scan the directory heirarchy to find a directory with .mcr # just scan the directory heirarchy to find a directory with .mca
# files. # files.
for root, dirs, files in os.walk(self.worlddir): for root, dirs, files in os.walk(self.worlddir):
# any .mcr files in this directory? # any .mca files in this directory?
mcrs = filter(lambda x: x.endswith(".mcr"), files) mcas = filter(lambda x: x.endswith(".mca"), files)
if mcrs: if mcas:
# construct a regionset object for this # construct a regionset object for this
rset = RegionSet(root) rset = RegionSet(root)
if root == os.path.join(self.worlddir, "region"): if root == os.path.join(self.worlddir, "region"):
@@ -307,13 +307,20 @@ class RegionSet(object):
@log_other_exceptions @log_other_exceptions
def get_chunk(self, x, z): def get_chunk(self, x, z):
"""Returns a dictionary object representing the "Level" NBT Compound """Returns a dictionary object representing the "Level" NBT Compound
structure for a chunk given its x, z coordinates. The coordinates are structure for a chunk given its x, z coordinates. The coordinates given
chunk coordinates. Raises ChunkDoesntExist exception if the given chunk are chunk coordinates. Raises ChunkDoesntExist exception if the given
does not exist. chunk does not exist.
The returned dictionary corresponds to the "Level" structure in the The returned dictionary corresponds to the "Level" structure in the
chunk file, with a few changes: chunk file, with a few changes:
* The "Blocks" byte string is transformed into a 16x16x128 numpy array
* The Biomes array is transformed into a 16x16 numpy array
* For each chunk section:
* The "Blocks" byte string is transformed into a 16x16x16 numpy array
* The AddBlocks array, if it exists, is bitshifted left 8 bits and
added into the Blocks array
* The "SkyLight" byte string is transformed into a 16x16x128 numpy * The "SkyLight" byte string is transformed into a 16x16x128 numpy
array array
* The "BlockLight" byte string is transformed into a 16x16x128 numpy * The "BlockLight" byte string is transformed into a 16x16x128 numpy
@@ -337,31 +344,62 @@ class RegionSet(object):
level = data[1]['Level'] level = data[1]['Level']
chunk_data = level chunk_data = level
chunk_data['Blocks'] = numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128))
skylight = numpy.frombuffer(level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64)) # Turn the Biomes array into a 16x16 numpy array
biomes = numpy.frombuffer(section['Biomes'], dtype=numpy.uint8)
biomes = biomes.reshape((16,16))
section['Biomes'] = biomes
# this array is 2 blocks per byte, so expand it for section in chunk_data['Sections']:
skylight_expanded = numpy.empty((16,16,128), dtype=numpy.uint8)
# Even elements get the lower 4 bits # Turn the Blocks array into a 16x16x16 numpy matrix of shorts,
# adding in the additional block array if included.
blocks = numpy.frombuffer(section['Blocks'], dtype=numpy.uint8)
# Cast up to uint16, blocks can have up to 12 bits of data
blocks = blocks.astype(numpy.uint16)
blocks.reshape((16,16,16))
if "AddBlocks" in section:
# This section has additional bits to tack on to the blocks
# array. AddBlocks is a packed array with 4 bits per slot, so
# it needs expanding
additional = numpy.frombuffer(section['AddBlocks'], dtype=numpy.uint8)
additional = additional.astype(numpy.uint16).reshape((16,16,8))
additional_expanded = numpy.empty((16,16,16), dtype=numpy.uint16)
additional_expanded[:,:,::2] = (additional & 0x0F) << 8
additional_expanded[:,:,1::2] = (additional & 0xF0) << 4
blocks += additional_expanded
del additional
del additional_expanded
del section['AddBlocks'] # Save some memory
section['Blocks'] = blocks
# Turn the skylight array into a 16x16x16 matrix. The array comes
# packed 2 elements per byte, so we need to expand it.
skylight = numpy.frombuffer(section['SkyLight'], dtype=numpy.uint8)
skylight = skylight.reshape((16,16,8))
skylight_expanded = numpy.empty((16,16,16), dtype=numpy.uint8)
skylight_expanded[:,:,::2] = skylight & 0x0F skylight_expanded[:,:,::2] = skylight & 0x0F
# Odd elements get the upper 4 bits
skylight_expanded[:,:,1::2] = (skylight & 0xF0) >> 4 skylight_expanded[:,:,1::2] = (skylight & 0xF0) >> 4
chunk_data['SkyLight'] = skylight_expanded del skylight
section['SkyLight'] = skylight_expanded
# expand just like skylight # Turn the BlockLight array into a 16x16x16 matrix, same as SkyLight
blocklight = numpy.frombuffer(level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64)) blocklight = numpy.frombuffer(section['BlockLight'], dtype=numpy.uint8)
blocklight_expanded = numpy.empty((16,16,128), dtype=numpy.uint8) blocklight = blocklight.reshape((16,16,8))
blocklight_expanded = numpy.empty((16,16,16), dtype=numpy.uint8)
blocklight_expanded[:,:,::2] = blocklight & 0x0F blocklight_expanded[:,:,::2] = blocklight & 0x0F
blocklight_expanded[:,:,1::2] = (blocklight & 0xF0) >> 4 blocklight_expanded[:,:,1::2] = (blocklight & 0xF0) >> 4
chunk_data['BlockLight'] = blocklight_expanded del blocklight
section['BlockLight'] = blocklight_expanded
# expand just like skylight # Turn the Data array into a 16x16x16 matrix, same as SkyLight
blockdata = numpy.frombuffer(level['Data'], dtype=numpy.uint8).reshape((16,16,64)) data = numpy.frombuffer(section['Data'], dtype=numpy.uint8)
blockdata_expanded = numpy.empty((16,16,128), dtype=numpy.uint8) data = data.reshape((16,16,8))
blockdata_expanded[:,:,::2] = blockdata & 0x0F data_expanded = numpy.empty((16,16,16), dtype=numpy.uint8)
blockdata_expanded[:,:,1::2] = (blockdata & 0xF0) >> 4 data_expanded[:,:,::2] = data & 0x0F
chunk_data['Data'] = blockdata_expanded data_expanded[:,:,1::2] = (data & 0xF0) >> 4
del data
section['Data'] = data_expanded
return chunk_data return chunk_data
@@ -413,7 +451,7 @@ class RegionSet(object):
logging.debug("regiondir is %s", self.regiondir) logging.debug("regiondir is %s", self.regiondir)
for path in glob(self.regiondir + "/r.*.*.mcr"): for path in glob(self.regiondir + "/r.*.*.mca"):
dirpath, f = os.path.split(path) dirpath, f = os.path.split(path)
p = f.split(".") p = f.split(".")
x = int(p[1]) x = int(p[1])