Removed all old code from RegionSet. Added RotatedRegionSet
Nothing has been tested
This commit is contained in:
@@ -79,22 +79,11 @@ def get_skylight_array(level):
|
|||||||
"""Returns the skylight array. This is 4 bits per block, but it is
|
"""Returns the skylight array. This is 4 bits per block, but it is
|
||||||
expanded for you so you may index it normally."""
|
expanded for you so you may index it normally."""
|
||||||
skylight = level['SkyLight']
|
skylight = level['SkyLight']
|
||||||
# this array is 2 blocks per byte, so expand it
|
return skylight
|
||||||
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
|
|
||||||
return skylight_expanded
|
|
||||||
|
|
||||||
def get_blocklight_array(level):
|
def get_blocklight_array(level):
|
||||||
"""Returns the blocklight array. This is 4 bits per block, but it
|
"""Returns the blocklight array. This is 4 bits per block, but it
|
||||||
is expanded for you so you may index it normally."""
|
is expanded for you so you may index it normally."""
|
||||||
# expand just like get_skylight_array()
|
|
||||||
blocklight = level['BlockLight']
|
|
||||||
blocklight_expanded = numpy.empty((16,16,128), dtype=numpy.uint8)
|
|
||||||
blocklight_expanded[:,:,::2] = blocklight & 0x0F
|
|
||||||
blocklight_expanded[:,:,1::2] = (blocklight & 0xF0) >> 4
|
|
||||||
return blocklight_expanded
|
return blocklight_expanded
|
||||||
|
|
||||||
def get_blockdata_array(level):
|
def get_blockdata_array(level):
|
||||||
|
|||||||
@@ -71,6 +71,12 @@ class World(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
mincol = maxcol = minrow = maxrow = 0
|
mincol = maxcol = minrow = maxrow = 0
|
||||||
|
|
||||||
|
# see RegionSet.rotate. These values are chosen so that they can be passed directly to rot90
|
||||||
|
UPPER_LEFT = 1 ## - Return the world such that north is down the -Z axis (no rotation)
|
||||||
|
UPPER_RIGHT = 2 ## - Return the world such that north is down the +X axis (rotate 90 degrees clockwise)
|
||||||
|
LOWER_RIGHT = 3 ## - Return the world such that north is down the +Z axis (rotate 180 degrees)
|
||||||
|
LOWER_LEFT = 0 ## - Return the world such that north is down the -X axis (rotate 90 degrees counterclockwise)
|
||||||
|
|
||||||
def __init__(self, worlddir):
|
def __init__(self, worlddir):
|
||||||
self.worlddir = worlddir
|
self.worlddir = worlddir
|
||||||
@@ -196,24 +202,13 @@ but may be several per invocation of the Overviewer in the case of multi-world.
|
|||||||
|
|
||||||
logging.info("Scanning regions")
|
logging.info("Scanning regions")
|
||||||
|
|
||||||
# This is populated by reload_region(). It is a mapping from region
|
# This is populated below. It is a mapping from (x,y) region coords to filename
|
||||||
# filename to: (region object, mtime, chunkcache)
|
|
||||||
self.regions = {}
|
|
||||||
|
|
||||||
# This is populated below. It is a mapping from (x,y) region coords to
|
|
||||||
# (x,y,filename, region object)
|
|
||||||
self.regionfiles = {}
|
self.regionfiles = {}
|
||||||
|
|
||||||
# Loads requested/all regions, caching region header info
|
|
||||||
for x, y, regionfile in self._iterate_regionfiles():
|
for x, y, regionfile in self._iterate_regionfiles():
|
||||||
# reload_region caches the region object in self.regions
|
# regionfile is a pathname
|
||||||
mcr = self._reload_region(regionfile)
|
self.regionfiles[(x,y)] = regionfile
|
||||||
#mcr.get_chunk_info() # get_chunk_info was removed from nbt.py. needs to be reimplemented
|
|
||||||
self.regionfiles[(x,y)] = (x,y,regionfile,mcr)
|
|
||||||
|
|
||||||
# the max number of chunks we will keep before removing them (includes emptry chunks)
|
|
||||||
self.chunklimit = 1024
|
|
||||||
self.chunkcount = 0
|
|
||||||
self.empty_chunk = [None,None]
|
self.empty_chunk = [None,None]
|
||||||
logging.debug("Done scanning regions")
|
logging.debug("Done scanning regions")
|
||||||
|
|
||||||
@@ -222,112 +217,83 @@ but may be several per invocation of the Overviewer in the case of multi-world.
|
|||||||
|
|
||||||
def get_region_path(self, chunkX, chunkY):
|
def get_region_path(self, chunkX, chunkY):
|
||||||
"""Returns the path to the region that contains chunk (chunkX, chunkY)
|
"""Returns the path to the region that contains chunk (chunkX, chunkY)
|
||||||
|
Coords can be either be global chunk coords, or local to a region
|
||||||
"""
|
"""
|
||||||
_, _, regionfile,_ = self.regionfiles.get((chunkX//32, chunkY//32),(None,None,None,None))
|
regionfile = self.regionfiles.get((chunkX//32, chunkY//32),None)
|
||||||
return regionfile
|
return regionfile
|
||||||
|
|
||||||
def load_from_region(self,filename, x, y):
|
def get_chunk(self,x, y):
|
||||||
#we need to manage the chunk cache
|
|
||||||
regioninfo = self.regions[filename]
|
|
||||||
if regioninfo is None:
|
|
||||||
return None
|
|
||||||
chunks = regioninfo[2]
|
|
||||||
chunk_data = chunks.get((x,y))
|
|
||||||
if chunk_data is None:
|
|
||||||
#prune the cache if required
|
|
||||||
if self.chunkcount > self.chunklimit: #todo: make the emptying the chunk cache slightly less crazy
|
|
||||||
[self.reload_region(regionfile) for regionfile in self.regions if regionfile <> filename]
|
|
||||||
self.chunkcount = 0
|
|
||||||
self.chunkcount += 1
|
|
||||||
|
|
||||||
nbt = self.load_region(filename).load_chunk(x, y)
|
|
||||||
if nbt is None:
|
|
||||||
chunks[(x,y)] = self.empty_chunk
|
|
||||||
return None ## return none. I think this is who we should indicate missing chunks
|
|
||||||
#raise IOError("No such chunk in region: (%i, %i)" % (x, y))
|
|
||||||
|
|
||||||
#we cache the transformed data, not its raw form
|
|
||||||
data = nbt.read_all()
|
|
||||||
level = data[1]['Level']
|
|
||||||
chunk_data = level
|
|
||||||
chunk_data['Blocks'] = numpy.array(numpy.rot90(numpy.frombuffer(
|
|
||||||
level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)),
|
|
||||||
self._get_north_rotations()))
|
|
||||||
chunk_data['Data'] = numpy.array(numpy.rot90(numpy.frombuffer(
|
|
||||||
level['Data'], dtype=numpy.uint8).reshape((16,16,64)),
|
|
||||||
self._get_north_rotations()))
|
|
||||||
chunk_data['SkyLight'] = numpy.array(numpy.rot90(numpy.frombuffer(
|
|
||||||
level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64)),
|
|
||||||
self._get_north_rotations()))
|
|
||||||
chunk_data['BlockLight'] = numpy.array(numpy.rot90(numpy.frombuffer(
|
|
||||||
level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64)),
|
|
||||||
self._get_north_rotations()))
|
|
||||||
#chunk_data = {}
|
|
||||||
#chunk_data['skylight'] = chunk.get_skylight_array(level)
|
|
||||||
#chunk_data['blocklight'] = chunk.get_blocklight_array(level)
|
|
||||||
#chunk_data['blockarray'] = chunk.get_blockdata_array(level)
|
|
||||||
#chunk_data['TileEntities'] = chunk.get_tileentity_data(level)
|
|
||||||
|
|
||||||
chunks[(x,y)] = [level,time.time()]
|
|
||||||
else:
|
|
||||||
chunk_data = chunk_data[0]
|
|
||||||
return chunk_data
|
|
||||||
|
|
||||||
def load_region(self,filename):
|
|
||||||
return self.regions[filename][0]
|
|
||||||
|
|
||||||
def get_chunk(self, x, z):
|
|
||||||
"""Returns a dictionary representing the top-level NBT Compound for a chunk given
|
"""Returns a dictionary representing the top-level NBT Compound for a chunk given
|
||||||
its x, z coordinates. The coordinates are chunk coordinates.
|
its x, z coordinates. The coordinates are chunk coordinates.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
regionfile = self.get_region_path(x, z)
|
regionfile = self.regionfiles[(x,y)]
|
||||||
regioninfo = self.regions[regionfile]
|
if regionfile is None:
|
||||||
if regioninfo is None:
|
|
||||||
return None
|
return None
|
||||||
data = regioninfo[0].load_chunk(x,z)
|
|
||||||
level = chunk_data[1]['Level']
|
data = nbt.load_region(regionfile)
|
||||||
|
|
||||||
|
level = data[1]['Level']
|
||||||
chunk_data = level
|
chunk_data = level
|
||||||
chunk_data['Blocks'] = numpy.array(numpy.rot90(numpy.frombuffer(
|
chunk_data['Blocks'] = numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128))
|
||||||
level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)),
|
chunk_data['Data'] = numpy.frombuffer(level['Data'], dtype=numpy.uint8).reshape((16,16,64))
|
||||||
self._get_north_rotations()))
|
|
||||||
chunk_data['Data'] = numpy.array(numpy.rot90(numpy.frombuffer(
|
|
||||||
level['Data'], dtype=numpy.uint8).reshape((16,16,64)),
|
|
||||||
self._get_north_rotations()))
|
|
||||||
chunk_data['SkyLight'] = numpy.array(numpy.rot90(numpy.frombuffer(
|
|
||||||
level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64)),
|
|
||||||
self._get_north_rotations()))
|
|
||||||
chunk_data['BlockLight'] = numpy.array(numpy.rot90(numpy.frombuffer(
|
|
||||||
level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64)),
|
|
||||||
self._get_north_rotations()))
|
|
||||||
|
|
||||||
## TODO some clever caching stuff
|
|
||||||
return chunk_data
|
|
||||||
|
|
||||||
|
|
||||||
def iterate_chunks(self):
|
skylight = numpy.frombuffer(level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64))
|
||||||
|
|
||||||
|
# this array is 2 blocks per byte, so expand it
|
||||||
|
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
|
||||||
|
blocklight = numpy.frombuffer(level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64))
|
||||||
|
blocklight_expanded = numpy.empty((16,16,128), dtype=numpy.uint8)
|
||||||
|
blocklight_expanded[:,:,::2] = blocklight & 0x0F
|
||||||
|
blocklight_expanded[:,:,1::2] = (blocklight & 0xF0) >> 4
|
||||||
|
chunk_data['BlockLight'] = blocklight_expanded
|
||||||
|
|
||||||
|
#chunk_data = {}
|
||||||
|
#chunk_data['skylight'] = chunk.get_skylight_array(level)
|
||||||
|
#chunk_data['blocklight'] = chunk.get_blocklight_array(level)
|
||||||
|
#chunk_data['blockarray'] = chunk.get_blockdata_array(level)
|
||||||
|
#chunk_data['TileEntities'] = chunk.get_tileentity_data(level)
|
||||||
|
|
||||||
|
return chunk_data
|
||||||
|
|
||||||
|
|
||||||
|
def rotate(self, north_direction):
|
||||||
|
return RotatedRegionSet(self.worldobj, self.regiondir, north_direction)
|
||||||
|
|
||||||
|
def iterate_chunks(self, north_dir):
|
||||||
"""Returns an iterator over all chunk metadata in this world. Iterates over tuples
|
"""Returns an iterator over all chunk metadata in this world. Iterates over tuples
|
||||||
of integers (x,z,mtime) for each chunk. Other chunk data is not returned here.
|
of integers (x,z,mtime) for each chunk. Other chunk data is not returned here.
|
||||||
|
|
||||||
Old name: world.iterate_chunk_metadata
|
Old name: world.iterate_chunk_metadata
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for regionx, regiony, _, mcr in self.regionfiles.itervalues():
|
for regionx, regiony, regionfile in self.regionfiles.itervalues():
|
||||||
|
mcr = nbt.load_region(regionfile)
|
||||||
for chunkx, chunky in mcr.get_chunks():
|
for chunkx, chunky in mcr.get_chunks():
|
||||||
yield chunkx+32*regionx, chunky+32*regiony, mcr.get_chunk_timestamp(chunkx, chunky)
|
yield chunkx+32*regionx, chunky+32*regiony, mcr.get_chunk_timestamp(chunkx, chunky)
|
||||||
|
|
||||||
def chunk_exists(self, x, z):
|
def get_chunk_mtime(self, x, z, north_dir):
|
||||||
"""Returns True or False depending on whether the given chunk exists. """
|
"""Returns a chunk’s mtime, or False if the chunk does not exist.
|
||||||
return self.regions[self.get_region_path(x,z)][0].chunk_exists(x,z)
|
This is therefore a dual purpose method. It corrects for the given north
|
||||||
|
direction as described in the docs for get_chunk()"""
|
||||||
#used to reload a changed region
|
|
||||||
def _reload_region(self,filename):
|
regionfile = self.regionfiles[(x,y)]
|
||||||
if self.regions.get(filename) is not None:
|
if regionfile is None:
|
||||||
self.regions[filename][0].closefile()
|
return None
|
||||||
chunkcache = {}
|
|
||||||
mcr = nbt.load_region(filename)
|
data = nbt.load_region(regionfile)
|
||||||
self.regions[filename] = (mcr,os.path.getmtime(filename),chunkcache)
|
if data.chunk_exists(x,z):
|
||||||
return mcr
|
return data.get_chunk_timestamp(x,z)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _iterate_regionfiles(self,regionlist=None):
|
def _iterate_regionfiles(self,regionlist=None):
|
||||||
"""Returns an iterator of all of the region files, along with their
|
"""Returns an iterator of all of the region files, along with their
|
||||||
@@ -382,48 +348,19 @@ Old name: world.iterate_chunk_metadata
|
|||||||
##TODO y = -temp-1
|
##TODO y = -temp-1
|
||||||
yield (x, y, join(dirpath, f))
|
yield (x, y, join(dirpath, f))
|
||||||
|
|
||||||
def determine_bounds(self):
|
|
||||||
"""Scan the world directory, to fill in
|
|
||||||
self.{min,max}{col,row} for use later in quadtree.py.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
logging.info("Scanning chunks")
|
|
||||||
# find the dimensions of the map, in region files
|
|
||||||
minx = maxx = miny = maxy = 0
|
|
||||||
found_regions = False
|
|
||||||
for x, y in self.regionfiles:
|
|
||||||
found_regions = True
|
|
||||||
minx = min(minx, x)
|
|
||||||
maxx = max(maxx, x)
|
|
||||||
miny = min(miny, y)
|
|
||||||
maxy = max(maxy, y)
|
|
||||||
if not found_regions:
|
|
||||||
logging.error("Error: No chunks found!")
|
|
||||||
sys.exit(1)
|
|
||||||
logging.debug("Done scanning chunks")
|
|
||||||
|
|
||||||
# turn our region coordinates into chunk coordinates
|
|
||||||
minx = minx * 32
|
|
||||||
miny = miny * 32
|
|
||||||
maxx = maxx * 32 + 32
|
|
||||||
maxy = maxy * 32 + 32
|
|
||||||
|
|
||||||
# Translate chunks to our diagonal coordinate system
|
|
||||||
mincol = maxcol = minrow = maxrow = 0
|
|
||||||
for chunkx, chunky in [(minx, miny), (minx, maxy), (maxx, miny), (maxx, maxy)]:
|
|
||||||
col, row = util.convert_coords(chunkx, chunky)
|
|
||||||
mincol = min(mincol, col)
|
|
||||||
maxcol = max(maxcol, col)
|
|
||||||
minrow = min(minrow, row)
|
|
||||||
maxrow = max(maxrow, row)
|
|
||||||
|
|
||||||
#logging.debug("map size: (%i, %i) to (%i, %i)" % (mincol, minrow, maxcol, maxrow))
|
|
||||||
|
|
||||||
self.mincol = mincol
|
class RotatedRegionSet(RegionSet):
|
||||||
self.maxcol = maxcol
|
def __init__(self, worldobj, regiondir, north_dir):
|
||||||
self.minrow = minrow
|
super(RotatedRegionSet, self).__init__(worldobj, regiondir)
|
||||||
self.maxrow = maxrow
|
self.north_dir = north_dir
|
||||||
|
def get_chunk(self, x, z):
|
||||||
|
chunk_data = super(RotatedRegionSet, self).get_chunk(x,z)
|
||||||
|
chunk_data['Blocks'] = numpy.rot90(chunk_data['Blocks'], self.north_dir)
|
||||||
|
chunk_data['Data'] = numpy.rot90(chunk_data['Data'], self.north_dir)
|
||||||
|
chunk_data['SkyLight'] = numpy.rot90(chunk_data['SkyLight'], self.north_dir)
|
||||||
|
chunk_data['BlockLight'] = numpy.rot90(chunk_data['BlockLight'], self.north_dir)
|
||||||
|
return chunk_data
|
||||||
|
|
||||||
|
|
||||||
def get_save_dir():
|
def get_save_dir():
|
||||||
"""Returns the path to the local saves directory
|
"""Returns the path to the local saves directory
|
||||||
|
|||||||
Reference in New Issue
Block a user