diff --git a/chunk.py b/chunk.py
index ad4f45c..ab5d41f 100644
--- a/chunk.py
+++ b/chunk.py
@@ -46,12 +46,12 @@ image
# alpha_over extension, BUT this extension may fall back to PIL's
# paste(), which DOES need the workaround.)
-def get_lvldata(filename, x, y):
+def get_lvldata(world,filename, x, y):
"""Takes a filename and chunkcoords and returns the Level struct, which contains all the
level info"""
try:
- d = nbt.load_from_region(filename, x, y)
+ d = world.load_from_region(filename, x, y)
except Exception, e:
logging.warning("Error opening chunk (%i, %i) in %s. It may be corrupt. %s", x, y, filename, e)
raise ChunkCorrupt(str(e))
@@ -64,10 +64,10 @@ def get_blockarray(level):
Block array, which just contains all the block ids"""
return numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128))
-def get_blockarray_fromfile(filename):
+def get_blockarray_fromfile(world,filename):
"""Same as get_blockarray except takes a filename and uses get_lvldata to
open it. This is a shortcut"""
- level = get_lvldata(filename)
+ level = get_lvldata(world,filename)
return get_blockarray(level)
def get_skylight_array(level):
@@ -196,7 +196,7 @@ class ChunkRenderer(object):
"""Loads and returns the level structure"""
if not hasattr(self, "_level"):
try:
- self._level = get_lvldata(self.regionfile, self.chunkX, self.chunkY)
+ self._level = get_lvldata(self.world,self.regionfile, self.chunkX, self.chunkY)
except NoSuchChunk, e:
logging.debug("Skipping non-existant chunk")
raise
@@ -228,7 +228,7 @@ class ChunkRenderer(object):
"""Loads and sets data from lower-left chunk"""
chunk_path = self.world.get_region_path(self.chunkX - 1, self.chunkY)
try:
- chunk_data = get_lvldata(chunk_path, self.chunkX - 1, self.chunkY)
+ chunk_data = get_lvldata(self.world,chunk_path, self.chunkX - 1, self.chunkY)
self._left_skylight = get_skylight_array(chunk_data)
self._left_blocklight = get_blocklight_array(chunk_data)
self._left_blocks = get_blockarray(chunk_data)
@@ -262,7 +262,7 @@ class ChunkRenderer(object):
"""Loads and sets data from lower-right chunk"""
chunk_path = self.world.get_region_path(self.chunkX, self.chunkY + 1)
try:
- chunk_data = get_lvldata(chunk_path, self.chunkX, self.chunkY + 1)
+ chunk_data = get_lvldata(self.world,chunk_path, self.chunkX, self.chunkY + 1)
self._right_skylight = get_skylight_array(chunk_data)
self._right_blocklight = get_blocklight_array(chunk_data)
self._right_blocks = get_blockarray(chunk_data)
@@ -296,7 +296,7 @@ class ChunkRenderer(object):
"""Loads and sets data from upper-right chunk"""
chunk_path = self.world.get_region_path(self.chunkX + 1, self.chunkY)
try:
- chunk_data = get_lvldata(chunk_path, self.chunkX + 1, self.chunkY)
+ chunk_data = get_lvldata(self.world,chunk_path, self.chunkX + 1, self.chunkY)
self._up_right_skylight = get_skylight_array(chunk_data)
self._up_right_blocklight = get_blocklight_array(chunk_data)
self._up_right_blocks = get_blockarray(chunk_data)
@@ -316,7 +316,7 @@ class ChunkRenderer(object):
"""Loads and sets data from upper-left chunk"""
chunk_path = self.world.get_region_path(self.chunkX, self.chunkY - 1)
try:
- chunk_data = get_lvldata(chunk_path, self.chunkX, self.chunkY - 1)
+ chunk_data = get_lvldata(self.world,chunk_path, self.chunkX, self.chunkY - 1)
self._up_left_skylight = get_skylight_array(chunk_data)
self._up_left_blocklight = get_blocklight_array(chunk_data)
self._up_left_blocks = get_blockarray(chunk_data)
diff --git a/nbt.py b/nbt.py
index 82f1aae..52d7545 100644
--- a/nbt.py
+++ b/nbt.py
@@ -34,14 +34,16 @@ def _file_loader(func):
def load(fileobj):
return NBTFileReader(fileobj).read_all()
-@_file_loader
-def load_from_region(fileobj, x, y):
- nbt = MCRFileReader(fileobj).load_chunk(x, y)
- if not nbt:
+def load_from_region(filename, x, y):
+ nbt = load_region(filename).load_chunk(x, y)
+ if nbt is None:
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))
- return nbt.read_all()
-
+ #raise IOError("No such chunk in region: (%i, %i)" % (x, y))
+ return nbt.read_all()
+
+def load_region(filename):
+ return MCRFileReader(filename)
+
class NBTFileReader(object):
def __init__(self, fileobj, is_gzip=True):
if is_gzip:
@@ -178,9 +180,9 @@ class MCRFileReader(object):
chunks (as instances of NBTFileReader), getting chunk timestamps,
and for listing chunks contained in the file."""
- def __init__(self, fileobj):
- self._file = fileobj
-
+ def __init__(self, filename):
+ self._file = None
+ self._filename = filename
# cache used when the entire header tables are read in get_chunks()
self._locations = None
self._timestamps = None
@@ -250,7 +252,7 @@ class MCRFileReader(object):
return timestamp
- def get_chunks(self):
+ def get_chunk_info(self,closeFile = True):
"""Return a list of all chunks contained in this region file,
as a list of (x, y) coordinate tuples. To load these chunks,
provide these coordinates to load_chunk()."""
@@ -258,6 +260,9 @@ class MCRFileReader(object):
if self._chunks:
return self._chunks
+ if self._file is None:
+ self._file = open(self._filename,'rb');
+
self._chunks = []
self._locations = []
self._timestamps = []
@@ -278,7 +283,11 @@ class MCRFileReader(object):
for x in xrange(32):
timestamp = self._read_chunk_timestamp()
self._timestamps.append(timestamp)
-
+
+ if closeFile:
+ #free the file object since it isn't safe to be reused in child processes (seek point goes wonky!)
+ self._file.close()
+ self._file = None
return self._chunks
def get_chunk_timestamp(self, x, y):
@@ -289,20 +298,16 @@ class MCRFileReader(object):
x = x % 32
y = y % 32
if self._timestamps is None:
- #self.get_chunks()
- return self._read_chunk_timestamp(x, y)
- else:
- return self._timestamps[x + y * 32]
+ self.get_chunk_info()
+ return self._timestamps[x + y * 32]
def chunkExists(self, x, y):
"""Determines if a chunk exists without triggering loading of the backend data"""
x = x % 32
y = y % 32
if self._locations is None:
- #self.get_chunks()
- location = self._read_chunk_location(x, y)
- else:
- location = self._locations[x + y * 32]
+ self.get_chunk_info()
+ location = self._locations[x + y * 32]
return location is not None
def load_chunk(self, x, y):
@@ -315,13 +320,14 @@ class MCRFileReader(object):
x = x % 32
y = y % 32
if self._locations is None:
- #self.get_chunks()
- location = self._read_chunk_location(x % 32, y % 32)
- else:
- location = self._locations[x + y * 32]
+ self.get_chunk_info()
+
+ location = self._locations[x + y * 32]
if location is None:
return None
-
+
+ if self._file is None:
+ self._file = open(self._filename,'rb');
# seek to the data
self._file.seek(location[0])
diff --git a/quadtree.py b/quadtree.py
index f33cacc..692a9a5 100644
--- a/quadtree.py
+++ b/quadtree.py
@@ -556,13 +556,13 @@ def render_worldtile(quadtree, chunks, colstart, colend, rowstart, rowend, path)
imgpath = path + "." + quadtree.imgformat
+ world = quadtree.world
# first, remove chunks from `chunks` that don't actually exist in
# their region files
def chunk_exists(chunk):
_, _, chunkx, chunky, region = chunk
- with open(region, 'rb') as region:
- r = nbt.MCRFileReader(region)
- return r.chunkExists(chunkx, chunky)
+ r = world.load_region(region)
+ return r.chunkExists(chunkx, chunky)
chunks = filter(chunk_exists, chunks)
#stat the file, we need to know if it exists or it's mtime
@@ -607,11 +607,9 @@ def render_worldtile(quadtree, chunks, colstart, colend, rowstart, rowend, path)
continue
# checking chunk mtime
- with open(regionfile, 'rb') as regionfile:
- region = nbt.MCRFileReader(regionfile)
- if region.get_chunk_timestamp(chunkx, chunky) > tile_mtime:
- needs_rerender = True
- if needs_rerender:
+ region = world.load_region(regionfile)
+ if region.get_chunk_timestamp(chunkx, chunky) > tile_mtime:
+ needs_rerender = True
break
# if after all that, we don't need a rerender, return
diff --git a/world.py b/world.py
index b50ddfa..4a0de3f 100644
--- a/world.py
+++ b/world.py
@@ -11,7 +11,7 @@
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
-# with the Overviewer. If not, see .
+# with the Overviewer. If not, see .
import functools
import os
@@ -72,10 +72,16 @@ class World(object):
self.useBiomeData = useBiomeData
#find region files, or load the region list
+ #this also caches all the region file header info
regionfiles = {}
+ regions = {}
for x, y, regionfile in self._iterate_regionfiles():
+ mcr = nbt.MCRFileReader(regionfile)
+ mcr.get_chunk_info()
+ regions[regionfile] = mcr
regionfiles[(x,y)] = (x,y,regionfile)
self.regionfiles = regionfiles
+ self.regions = regions
# figure out chunk format is in use
# if not mcregion, error out early
@@ -110,6 +116,22 @@ class World(object):
"""
_, _, regionfile = self.regionfiles.get((chunkX//32, chunkY//32),(None,None,None));
return regionfile
+
+
+
+ def load_from_region(self,filename, x, y):
+ nbt = self.load_region(filename).load_chunk(x, y)
+ if nbt is None:
+ 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))
+ return nbt.read_all()
+
+
+ #filo region cache
+ def load_region(self,filename):
+ #return nbt.MCRFileReader(filename)
+ return self.regions[filename]
+
def convert_coords(self, chunkx, chunky):
"""Takes a coordinate (chunkx, chunky) where chunkx and chunky are