Performance improvements on update scan
This commit is contained in:
10
chunk.py
10
chunk.py
@@ -371,16 +371,16 @@ class ChunkRenderer(object):
|
|||||||
# and finally check for a block with same blockid. I we aren't in the border of a chunk,
|
# and finally check for a block with same blockid. I we aren't in the border of a chunk,
|
||||||
# check for the block having the sme blockid.
|
# check for the block having the sme blockid.
|
||||||
|
|
||||||
if (up_right_blocks != None and up_right_blocks[0,y,z] == blockid) if x == 15 else blocks[x+1,y,z] == blockid:
|
if (up_right_blocks is not None and up_right_blocks[0,y,z] == blockid) if x == 15 else blocks[x+1,y,z] == blockid:
|
||||||
pseudo_data = pseudo_data | 0b1000
|
pseudo_data = pseudo_data | 0b1000
|
||||||
|
|
||||||
if (right_blocks != None and right_blocks[x,0,z] == blockid) if y == 15 else blocks[x,y + 1,z] == blockid:
|
if (right_blocks is not None and right_blocks[x,0,z] == blockid) if y == 15 else blocks[x,y + 1,z] == blockid:
|
||||||
pseudo_data = pseudo_data | 0b0100
|
pseudo_data = pseudo_data | 0b0100
|
||||||
|
|
||||||
if (left_blocks != None and left_blocks[15,y,z] == blockid) if x == 0 else blocks[x - 1,y,z] == blockid:
|
if (left_blocks is not None and left_blocks[15,y,z] == blockid) if x == 0 else blocks[x - 1,y,z] == blockid:
|
||||||
pseudo_data = pseudo_data | 0b0010
|
pseudo_data = pseudo_data | 0b0010
|
||||||
|
|
||||||
if (up_left_blocks != None and up_left_blocks[x,15,z] == blockid) if y == 0 else blocks[x,y - 1,z] == blockid:
|
if (up_left_blocks is not None and up_left_blocks[x,15,z] == blockid) if y == 0 else blocks[x,y - 1,z] == blockid:
|
||||||
pseudo_data = pseudo_data | 0b0001
|
pseudo_data = pseudo_data | 0b0001
|
||||||
|
|
||||||
# rotate the bits for other north orientations
|
# rotate the bits for other north orientations
|
||||||
@@ -443,7 +443,7 @@ class ChunkRenderer(object):
|
|||||||
|
|
||||||
# make sure we have a correctly-ranged coordinates and enough
|
# make sure we have a correctly-ranged coordinates and enough
|
||||||
# info about the chunk
|
# info about the chunk
|
||||||
if not (blocks != None and skylight != None and blocklight != None and
|
if not (blocks is not None and skylight is not None and blocklight is not None and
|
||||||
local_x >= 0 and local_x < 16 and local_y >= 0 and local_y < 16 and
|
local_x >= 0 and local_x < 16 and local_y >= 0 and local_y < 16 and
|
||||||
local_z >= 0 and local_z < 128):
|
local_z >= 0 and local_z < 128):
|
||||||
# we have no useful info, return default
|
# we have no useful info, return default
|
||||||
|
|||||||
@@ -36,11 +36,11 @@ def alpha_over(dest, src, pos_or_rect=(0, 0), mask=None):
|
|||||||
either be a position or a rectangle, specifying where on dest to
|
either be a position or a rectangle, specifying where on dest to
|
||||||
put src. Falls back to dest.paste() if the alpha_over extension
|
put src. Falls back to dest.paste() if the alpha_over extension
|
||||||
can't be found."""
|
can't be found."""
|
||||||
if mask == None:
|
if mask is None:
|
||||||
mask = src
|
mask = src
|
||||||
|
|
||||||
global extension_alpha_over
|
global extension_alpha_over
|
||||||
if extension_alpha_over != None:
|
if extension_alpha_over is not None:
|
||||||
# extension ALWAYS expects rects, so convert if needed
|
# extension ALWAYS expects rects, so convert if needed
|
||||||
if len(pos_or_rect) == 2:
|
if len(pos_or_rect) == 2:
|
||||||
pos_or_rect = (pos_or_rect[0], pos_or_rect[1], src.size[0], src.size[1])
|
pos_or_rect = (pos_or_rect[0], pos_or_rect[1], src.size[0], src.size[1])
|
||||||
|
|||||||
121
quadtree.py
121
quadtree.py
@@ -26,6 +26,7 @@ import logging
|
|||||||
import util
|
import util
|
||||||
import cPickle
|
import cPickle
|
||||||
import stat
|
import stat
|
||||||
|
import errno
|
||||||
from time import gmtime, strftime, sleep
|
from time import gmtime, strftime, sleep
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
@@ -438,7 +439,7 @@ class QuadtreeGen(object):
|
|||||||
# return (col, row, chunkx, chunky, regionpath)
|
# return (col, row, chunkx, chunky, regionpath)
|
||||||
chunkx, chunky = self.world.unconvert_coords(col, row)
|
chunkx, chunky = self.world.unconvert_coords(col, row)
|
||||||
c = self.world.get_region_path(chunkx, chunky)
|
c = self.world.get_region_path(chunkx, chunky)
|
||||||
if os.path.exists(c):
|
if c is not None:
|
||||||
chunklist.append((col, row, chunkx, chunky, c))
|
chunklist.append((col, row, chunkx, chunky, c))
|
||||||
return chunklist
|
return chunklist
|
||||||
|
|
||||||
@@ -451,53 +452,38 @@ def render_innertile(dest, name, imgformat, optimizeimg):
|
|||||||
imgpath = os.path.join(dest, name) + "." + imgformat
|
imgpath = os.path.join(dest, name) + "." + imgformat
|
||||||
|
|
||||||
if name == "base":
|
if name == "base":
|
||||||
q0path = os.path.join(dest, "0." + imgformat)
|
quadPath = [[(0,0),os.path.join(dest, "0." + imgformat)],[(192,0),os.path.join(dest, "1." + imgformat)], [(0, 192),os.path.join(dest, "2." + imgformat)],[(192,192),os.path.join(dest, "3." + imgformat)]]
|
||||||
q1path = os.path.join(dest, "1." + imgformat)
|
|
||||||
q2path = os.path.join(dest, "2." + imgformat)
|
|
||||||
q3path = os.path.join(dest, "3." + imgformat)
|
|
||||||
else:
|
else:
|
||||||
q0path = os.path.join(dest, name, "0." + imgformat)
|
quadPath = [[(0,0),os.path.join(dest, name, "0." + imgformat)],[(192,0),os.path.join(dest, name, "1." + imgformat)],[(0, 192),os.path.join(dest, name, "2." + imgformat)],[(192,192),os.path.join(dest, name, "3." + imgformat)]]
|
||||||
q1path = os.path.join(dest, name, "1." + imgformat)
|
|
||||||
q2path = os.path.join(dest, name, "2." + imgformat)
|
|
||||||
q3path = os.path.join(dest, name, "3." + imgformat)
|
|
||||||
|
|
||||||
# Check which ones exist
|
#stat the tile, we need to know if it exists or it's mtime
|
||||||
if not os.path.exists(q0path):
|
try:
|
||||||
q0path = None
|
tile_mtime = os.stat(imgpath)[stat.ST_MTIME];
|
||||||
if not os.path.exists(q1path):
|
except OSError, e:
|
||||||
q1path = None
|
if e.errno != errno.ENOENT:
|
||||||
if not os.path.exists(q2path):
|
raise
|
||||||
q2path = None
|
tile_mtime = None
|
||||||
if not os.path.exists(q3path):
|
|
||||||
q3path = None
|
|
||||||
|
|
||||||
|
#check mtimes on each part of the quad, this also checks if they exist
|
||||||
|
needs_rerender = tile_mtime is None
|
||||||
|
quadPath_filtered = []
|
||||||
|
for path in quadPath:
|
||||||
|
try:
|
||||||
|
quad_mtime = os.stat(path[1])[stat.ST_MTIME];
|
||||||
|
quadPath_filtered.append(path)
|
||||||
|
if quad_mtime > tile_mtime:
|
||||||
|
needs_rerender = True
|
||||||
|
except OSError:
|
||||||
|
# We need to stat all the quad files, so keep looping
|
||||||
|
pass
|
||||||
# do they all not exist?
|
# do they all not exist?
|
||||||
if not (q0path or q1path or q2path or q3path):
|
if quadPath_filtered == []:
|
||||||
if os.path.exists(imgpath):
|
if tile_mtime is not None:
|
||||||
os.unlink(imgpath)
|
os.unlink(imgpath)
|
||||||
return
|
return
|
||||||
|
# quit now if we don't need rerender
|
||||||
# check the mtimes
|
if not needs_rerender:
|
||||||
try:
|
return
|
||||||
tile_mtime = os.path.getmtime(imgpath)
|
|
||||||
needs_rerender = False
|
|
||||||
|
|
||||||
# remove non-existant paths
|
|
||||||
components = [q0path, q1path, q2path, q3path]
|
|
||||||
components = filter(lambda p: p != None, components)
|
|
||||||
|
|
||||||
for mtime in [os.path.getmtime(path) for path in components]:
|
|
||||||
if mtime > tile_mtime:
|
|
||||||
needs_rerender = True
|
|
||||||
break
|
|
||||||
|
|
||||||
# quit now if we don't need rerender
|
|
||||||
if not needs_rerender:
|
|
||||||
return
|
|
||||||
except OSError:
|
|
||||||
# one of our mtime calls failed, so we'll continue
|
|
||||||
pass
|
|
||||||
|
|
||||||
#logging.debug("writing out innertile {0}".format(imgpath))
|
#logging.debug("writing out innertile {0}".format(imgpath))
|
||||||
|
|
||||||
# Create the actual image now
|
# Create the actual image now
|
||||||
@@ -506,30 +492,12 @@ def render_innertile(dest, name, imgformat, optimizeimg):
|
|||||||
# we'll use paste (NOT alpha_over) for quadtree generation because
|
# we'll use paste (NOT alpha_over) for quadtree generation because
|
||||||
# this is just straight image stitching, not alpha blending
|
# this is just straight image stitching, not alpha blending
|
||||||
|
|
||||||
if q0path:
|
for path in quadPath_filtered:
|
||||||
try:
|
try:
|
||||||
quad0 = Image.open(q0path).resize((192,192), Image.ANTIALIAS)
|
quad = Image.open(path[1]).resize((192,192), Image.ANTIALIAS)
|
||||||
img.paste(quad0, (0,0))
|
img.paste(quad, path[0])
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logging.warning("Couldn't open %s. It may be corrupt, you may need to delete it. %s", q0path, e)
|
logging.warning("Couldn't open %s. It may be corrupt, you may need to delete it. %s", path[1], e)
|
||||||
if q1path:
|
|
||||||
try:
|
|
||||||
quad1 = Image.open(q1path).resize((192,192), Image.ANTIALIAS)
|
|
||||||
img.paste(quad1, (192,0))
|
|
||||||
except Exception, e:
|
|
||||||
logging.warning("Couldn't open %s. It may be corrupt, you may need to delete it. %s", q1path, e)
|
|
||||||
if q2path:
|
|
||||||
try:
|
|
||||||
quad2 = Image.open(q2path).resize((192,192), Image.ANTIALIAS)
|
|
||||||
img.paste(quad2, (0, 192))
|
|
||||||
except Exception, e:
|
|
||||||
logging.warning("Couldn't open %s. It may be corrupt, you may need to delete it. %s", q2path, e)
|
|
||||||
if q3path:
|
|
||||||
try:
|
|
||||||
quad3 = Image.open(q3path).resize((192,192), Image.ANTIALIAS)
|
|
||||||
img.paste(quad3, (192, 192))
|
|
||||||
except Exception, e:
|
|
||||||
logging.warning("Couldn't open %s. It may be corrupt, you may need to delete it. %s", q3path, e)
|
|
||||||
|
|
||||||
# Save it
|
# Save it
|
||||||
if imgformat == 'jpg':
|
if imgformat == 'jpg':
|
||||||
@@ -594,12 +562,20 @@ def render_worldtile(quadtree, chunks, colstart, colend, rowstart, rowend, path)
|
|||||||
_, _, chunkx, chunky, region = chunk
|
_, _, chunkx, chunky, region = chunk
|
||||||
with open(region, 'rb') as region:
|
with open(region, 'rb') as region:
|
||||||
r = nbt.MCRFileReader(region)
|
r = nbt.MCRFileReader(region)
|
||||||
return r.load_chunk(chunkx, chunky) != None
|
return r.chunkExists(chunkx, chunky)
|
||||||
chunks = filter(chunk_exists, chunks)
|
chunks = filter(chunk_exists, chunks)
|
||||||
|
|
||||||
|
#stat the file, we need to know if it exists or it's mtime
|
||||||
|
try:
|
||||||
|
tile_mtime = os.stat(imgpath)[stat.ST_MTIME];
|
||||||
|
except OSError, e:
|
||||||
|
if e.errno != errno.ENOENT:
|
||||||
|
raise
|
||||||
|
tile_mtime = None
|
||||||
|
|
||||||
if not chunks:
|
if not chunks:
|
||||||
# No chunks were found in this tile
|
# No chunks were found in this tile
|
||||||
if os.path.exists(imgpath):
|
if tile_mtime is not None:
|
||||||
os.unlink(imgpath)
|
os.unlink(imgpath)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -612,17 +588,22 @@ def render_worldtile(quadtree, chunks, colstart, colend, rowstart, rowend, path)
|
|||||||
# Ignore errno EEXIST: file exists. Since this is multithreaded,
|
# Ignore errno EEXIST: file exists. Since this is multithreaded,
|
||||||
# two processes could conceivably try and create the same directory
|
# two processes could conceivably try and create the same directory
|
||||||
# at the same time.
|
# at the same time.
|
||||||
import errno
|
|
||||||
if e.errno != errno.EEXIST:
|
if e.errno != errno.EEXIST:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
# check chunk mtimes to see if they are newer
|
# check chunk mtimes to see if they are newer
|
||||||
try:
|
try:
|
||||||
tile_mtime = os.path.getmtime(imgpath)
|
#tile_mtime = os.path.getmtime(imgpath)
|
||||||
|
regionMtimes = {}
|
||||||
needs_rerender = False
|
needs_rerender = False
|
||||||
for col, row, chunkx, chunky, regionfile in chunks:
|
for col, row, chunkx, chunky, regionfile in chunks:
|
||||||
# check region file mtime first
|
# check region file mtime first.
|
||||||
if os.path.getmtime(regionfile) <= tile_mtime:
|
# Note: we cache the value since it's actually very likely we will have multipule chunks in the same region, and syscalls are expensive
|
||||||
|
regionMtime = regionMtimes.get(regionfile,None)
|
||||||
|
if regionMtime is None:
|
||||||
|
regionMtime = os.path.getmtime(regionfile)
|
||||||
|
regionMtimes[regionfile] = regionMtime
|
||||||
|
if regionMtime <= tile_mtime:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# checking chunk mtime
|
# checking chunk mtime
|
||||||
|
|||||||
2
setup.py
2
setup.py
@@ -23,7 +23,7 @@ setup_kwargs['cmdclass'] = {}
|
|||||||
# py2exe options
|
# py2exe options
|
||||||
#
|
#
|
||||||
|
|
||||||
if py2exe != None:
|
if py2exe is not None:
|
||||||
setup_kwargs['console'] = ['gmap.py']
|
setup_kwargs['console'] = ['gmap.py']
|
||||||
setup_kwargs['data_files'] = [('textures', ['textures/lava.png', 'textures/water.png', 'textures/fire.png']),
|
setup_kwargs['data_files'] = [('textures', ['textures/lava.png', 'textures/water.png', 'textures/fire.png']),
|
||||||
('', ['config.js', 'COPYING.txt', 'README.rst']),
|
('', ['config.js', 'COPYING.txt', 'README.rst']),
|
||||||
|
|||||||
19
textures.py
19
textures.py
@@ -878,15 +878,16 @@ def getBiomeData(worlddir, chunkX, chunkY):
|
|||||||
if biomeFile == currentBiomeFile:
|
if biomeFile == currentBiomeFile:
|
||||||
return currentBiomeData
|
return currentBiomeData
|
||||||
|
|
||||||
f = open(os.path.join(worlddir, "biomes", biomeFile), "rb")
|
try:
|
||||||
rawdata = f.read()
|
with open(os.path.join(worlddir, "biomes", biomeFile), "rb") as f:
|
||||||
f.close()
|
rawdata = f.read()
|
||||||
|
# make sure the file size is correct
|
||||||
# make sure the file size is correct
|
if not len(rawdata) == 512 * 512 * 2:
|
||||||
if not len(rawdata) == 512 * 512 * 2:
|
raise Exception("Biome file %s is not valid." % (biomeFile,))
|
||||||
raise Exception("Biome file %s is not valid." % (biomeFile,))
|
data = numpy.frombuffer(rawdata, dtype=numpy.dtype(">u2"))
|
||||||
|
except IOError:
|
||||||
data = numpy.frombuffer(rawdata, dtype=numpy.dtype(">u2"))
|
data = None
|
||||||
|
pass # no biome data
|
||||||
|
|
||||||
currentBiomeFile = biomeFile
|
currentBiomeFile = biomeFile
|
||||||
currentBiomeData = data
|
currentBiomeData = data
|
||||||
|
|||||||
41
world.py
41
world.py
@@ -67,10 +67,16 @@ class World(object):
|
|||||||
|
|
||||||
mincol = maxcol = minrow = maxrow = 0
|
mincol = maxcol = minrow = maxrow = 0
|
||||||
|
|
||||||
def __init__(self, worlddir, useBiomeData=False):
|
def __init__(self, worlddir, useBiomeData=False,regionlist=None):
|
||||||
self.worlddir = worlddir
|
self.worlddir = worlddir
|
||||||
self.useBiomeData = useBiomeData
|
self.useBiomeData = useBiomeData
|
||||||
|
|
||||||
|
#find region files, or load the region list
|
||||||
|
regionfiles = {}
|
||||||
|
for x, y, regionfile in self._iterate_regionfiles():
|
||||||
|
regionfiles[(x,y)] = (x,y,regionfile)
|
||||||
|
self.regionfiles = regionfiles
|
||||||
|
|
||||||
# figure out chunk format is in use
|
# figure out chunk format is in use
|
||||||
# if not mcregion, error out early
|
# if not mcregion, error out early
|
||||||
data = nbt.load(os.path.join(self.worlddir, "level.dat"))[1]['Data']
|
data = nbt.load(os.path.join(self.worlddir, "level.dat"))[1]['Data']
|
||||||
@@ -102,10 +108,8 @@ class World(object):
|
|||||||
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)
|
||||||
"""
|
"""
|
||||||
|
_, _, regionfile = self.regionfiles.get((chunkX//32, chunkY//32),(None,None,None));
|
||||||
chunkFile = "region/r.%s.%s.mcr" % (chunkX//32, chunkY//32)
|
return regionfile
|
||||||
|
|
||||||
return os.path.join(self.worlddir, chunkFile)
|
|
||||||
|
|
||||||
def convert_coords(self, chunkx, chunky):
|
def convert_coords(self, chunkx, chunky):
|
||||||
"""Takes a coordinate (chunkx, chunky) where chunkx and chunky are
|
"""Takes a coordinate (chunkx, chunky) where chunkx and chunky are
|
||||||
@@ -168,7 +172,7 @@ class World(object):
|
|||||||
# find the dimensions of the map, in region files
|
# find the dimensions of the map, in region files
|
||||||
minx = maxx = miny = maxy = 0
|
minx = maxx = miny = maxy = 0
|
||||||
found_regions = False
|
found_regions = False
|
||||||
for x, y, regionfile in self._iterate_regionfiles():
|
for x, y in self.regionfiles:
|
||||||
found_regions = True
|
found_regions = True
|
||||||
minx = min(minx, x)
|
minx = min(minx, x)
|
||||||
maxx = max(maxx, x)
|
maxx = max(maxx, x)
|
||||||
@@ -203,18 +207,27 @@ class World(object):
|
|||||||
|
|
||||||
self.findTrueSpawn()
|
self.findTrueSpawn()
|
||||||
|
|
||||||
def _iterate_regionfiles(self):
|
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
|
||||||
coordinates
|
coordinates
|
||||||
|
|
||||||
Returns (regionx, regiony, filename)"""
|
Returns (regionx, regiony, filename)"""
|
||||||
|
join = os.path.join
|
||||||
for dirpath, dirnames, filenames in os.walk(os.path.join(self.worlddir, 'region')):
|
if regionlist is not None:
|
||||||
if not dirnames and filenames and "DIM-1" not in dirpath:
|
for path in regionlist:
|
||||||
for f in filenames:
|
if path.endswith("\n"):
|
||||||
if f.startswith("r.") and f.endswith(".mcr"):
|
path = path[:-1]
|
||||||
p = f.split(".")
|
f = os.path.basename(path)
|
||||||
yield (int(p[1]), int(p[2]), os.path.join(dirpath, f))
|
if f.startswith("r.") and f.endswith(".mcr"):
|
||||||
|
p = f.split(".")
|
||||||
|
yield (int(p[1]), int(p[2]), join(self.worlddir, 'region', f))
|
||||||
|
else:
|
||||||
|
for dirpath, dirnames, filenames in os.walk(os.path.join(self.worlddir, 'region')):
|
||||||
|
if not dirnames and filenames and "DIM-1" not in dirpath:
|
||||||
|
for f in filenames:
|
||||||
|
if f.startswith("r.") and f.endswith(".mcr"):
|
||||||
|
p = f.split(".")
|
||||||
|
yield (int(p[1]), int(p[2]), join(dirpath, f))
|
||||||
|
|
||||||
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