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,18 +307,25 @@ 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 "SkyLight" byte string is transformed into a 16x16x128 numpy * The Biomes array is transformed into a 16x16 numpy array
array
* The "BlockLight" byte string is transformed into a 16x16x128 numpy * For each chunk section:
array
* The "Data" byte string is transformed into a 16x16x128 numpy array * 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
array
* The "BlockLight" byte string is transformed into a 16x16x128 numpy
array
* The "Data" byte string is transformed into a 16x16x128 numpy array
Warning: the returned data may be cached and thus should not be Warning: the returned data may be cached and thus should not be
modified, lest it affect the return values of future calls for the same modified, lest it affect the return values of future calls for the same
@@ -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
skylight_expanded[:,:,::2] = skylight & 0x0F
# Odd elements get the upper 4 bits
skylight_expanded[:,:,1::2] = (skylight & 0xF0) >> 4
chunk_data['SkyLight'] = skylight_expanded
# expand just like skylight # Turn the Blocks array into a 16x16x16 numpy matrix of shorts,
blocklight = numpy.frombuffer(level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64)) # adding in the additional block array if included.
blocklight_expanded = numpy.empty((16,16,128), dtype=numpy.uint8) blocks = numpy.frombuffer(section['Blocks'], dtype=numpy.uint8)
blocklight_expanded[:,:,::2] = blocklight & 0x0F # Cast up to uint16, blocks can have up to 12 bits of data
blocklight_expanded[:,:,1::2] = (blocklight & 0xF0) >> 4 blocks = blocks.astype(numpy.uint16)
chunk_data['BlockLight'] = blocklight_expanded 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
# expand just like skylight # Turn the skylight array into a 16x16x16 matrix. The array comes
blockdata = numpy.frombuffer(level['Data'], dtype=numpy.uint8).reshape((16,16,64)) # packed 2 elements per byte, so we need to expand it.
blockdata_expanded = numpy.empty((16,16,128), dtype=numpy.uint8) skylight = numpy.frombuffer(section['SkyLight'], dtype=numpy.uint8)
blockdata_expanded[:,:,::2] = blockdata & 0x0F skylight = skylight.reshape((16,16,8))
blockdata_expanded[:,:,1::2] = (blockdata & 0xF0) >> 4 skylight_expanded = numpy.empty((16,16,16), dtype=numpy.uint8)
chunk_data['Data'] = blockdata_expanded skylight_expanded[:,:,::2] = skylight & 0x0F
skylight_expanded[:,:,1::2] = (skylight & 0xF0) >> 4
del skylight
section['SkyLight'] = skylight_expanded
# Turn the BlockLight array into a 16x16x16 matrix, same as SkyLight
blocklight = numpy.frombuffer(section['BlockLight'], 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[:,:,1::2] = (blocklight & 0xF0) >> 4
del blocklight
section['BlockLight'] = blocklight_expanded
# Turn the Data array into a 16x16x16 matrix, same as SkyLight
data = numpy.frombuffer(section['Data'], dtype=numpy.uint8)
data = data.reshape((16,16,8))
data_expanded = numpy.empty((16,16,16), dtype=numpy.uint8)
data_expanded[:,:,::2] = data & 0x0F
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])