added the option to specify a list of chunks to update.
This commit is contained in:
76
world.py
76
world.py
@@ -17,8 +17,9 @@ import functools
|
||||
import os
|
||||
import os.path
|
||||
import multiprocessing
|
||||
import numpy
|
||||
import sys
|
||||
|
||||
import numpy
|
||||
from PIL import Image
|
||||
|
||||
import chunk
|
||||
@@ -85,16 +86,51 @@ class WorldRenderer(object):
|
||||
"""Renders a world's worth of chunks.
|
||||
worlddir is the path to the minecraft world
|
||||
cachedir is the path to a directory that should hold the resulting images.
|
||||
It may be the same as worlddir (which used to be the default)"""
|
||||
def __init__(self, worlddir, cachedir):
|
||||
It may be the same as worlddir (which used to be the default).
|
||||
|
||||
If chunklist is given, it is assumed to be an iterator over paths to chunk
|
||||
files to update. If it includes a trailing newline, it is stripped, so you
|
||||
can pass in file handles just fine.
|
||||
"""
|
||||
def __init__(self, worlddir, cachedir, chunklist=None):
|
||||
self.worlddir = worlddir
|
||||
self.caves = False
|
||||
self.cachedir = cachedir
|
||||
|
||||
self.chunklist = chunklist
|
||||
|
||||
# stores Points Of Interest to be mapped with markers
|
||||
# a list of dictionaries, see below for an example
|
||||
self.POI = []
|
||||
|
||||
def _get_chunk_renderset(self):
|
||||
"""Returns a set of (col, row) chunks that should be rendered. Returns
|
||||
None if all chunks should be rendered"""
|
||||
if not self.chunklist:
|
||||
return None
|
||||
|
||||
# Get a list of the (chunks, chunky, filename) from the passed in list
|
||||
# of filenames
|
||||
chunklist = []
|
||||
for path in self.chunklist:
|
||||
if path.endswith("\n"):
|
||||
path = path[:-1]
|
||||
f = os.path.basename(path)
|
||||
if f and f.startswith("c.") and f.endswith(".dat"):
|
||||
p = f.split(".")
|
||||
chunklist.append((base36decode(p[1]), base36decode(p[2]),
|
||||
path))
|
||||
|
||||
# Translate to col, row coordinates
|
||||
_, _, _, _, chunklist = _convert_coords(chunklist)
|
||||
|
||||
# Build a set from the col, row pairs
|
||||
inclusion_set = set()
|
||||
for col, row, filename in chunklist:
|
||||
inclusion_set.add((col, row))
|
||||
|
||||
return inclusion_set
|
||||
|
||||
def findTrueSpawn(self):
|
||||
"""Adds the true spawn location to self.POI. The spawn Y coordinate
|
||||
is almost always the default of 64. Find the first air block above
|
||||
@@ -157,8 +193,12 @@ class WorldRenderer(object):
|
||||
|
||||
Returns a list of (chunkx, chunky, filename) where chunkx and chunky are
|
||||
given in chunk coordinates. Use convert_coords() to turn the resulting list
|
||||
into an oblique coordinate system"""
|
||||
into an oblique coordinate system.
|
||||
|
||||
Usually this scans the given worlddir, but will use the chunk list
|
||||
given to the constructor if one was provided."""
|
||||
all_chunks = []
|
||||
|
||||
for dirpath, dirnames, filenames in os.walk(self.worlddir):
|
||||
if not dirnames and filenames:
|
||||
for f in filenames:
|
||||
@@ -166,6 +206,10 @@ class WorldRenderer(object):
|
||||
p = f.split(".")
|
||||
all_chunks.append((base36decode(p[1]), base36decode(p[2]),
|
||||
os.path.join(dirpath, f)))
|
||||
|
||||
if not all_chunks:
|
||||
print "Error: No chunks found!"
|
||||
sys.exit(1)
|
||||
return all_chunks
|
||||
|
||||
def _render_chunks_async(self, chunks, processes):
|
||||
@@ -176,11 +220,25 @@ class WorldRenderer(object):
|
||||
Returns a dictionary mapping (col, row) to the file where that
|
||||
chunk is rendered as an image
|
||||
"""
|
||||
# The set of chunks to render, or None for all of them. The logic is
|
||||
# slightly more compliated than it should seem, since we still need to
|
||||
# build the results dict out of all chunks, even if they're not being
|
||||
# rendered.
|
||||
inclusion_set = self._get_chunk_renderset()
|
||||
|
||||
results = {}
|
||||
if processes == 1:
|
||||
# Skip the multiprocessing stuff
|
||||
print "Rendering chunks synchronously since you requested 1 process"
|
||||
for i, (col, row, chunkfile) in enumerate(chunks):
|
||||
if inclusion_set and (col, row) not in inclusion_set:
|
||||
# Skip rendering, just find where the existing image is
|
||||
_, imgpath = chunk.ChunkRenderer(chunkfile,
|
||||
self.cachedir).find_oldimage(False)
|
||||
if imgpath:
|
||||
results[(col, row)] = imgpath
|
||||
continue
|
||||
|
||||
result = chunk.render_and_save(chunkfile, self.cachedir, cave=self.caves)
|
||||
results[(col, row)] = result
|
||||
if i > 0:
|
||||
@@ -191,6 +249,14 @@ class WorldRenderer(object):
|
||||
pool = multiprocessing.Pool(processes=processes)
|
||||
asyncresults = []
|
||||
for col, row, chunkfile in chunks:
|
||||
if inclusion_set and (col, row) not in inclusion_set:
|
||||
# Skip rendering, just find where the existing image is
|
||||
_, imgpath = chunk.ChunkRenderer(chunkfile,
|
||||
self.cachedir).find_oldimage(False)
|
||||
if imgpath:
|
||||
results[(col, row)] = imgpath
|
||||
continue
|
||||
|
||||
result = pool.apply_async(chunk.render_and_save,
|
||||
args=(chunkfile,self.cachedir),
|
||||
kwds=dict(cave=self.caves))
|
||||
@@ -202,7 +268,7 @@ class WorldRenderer(object):
|
||||
results[(col, row)] = result.get()
|
||||
if i > 0:
|
||||
if 1000 % i == 0 or i % 1000 == 0:
|
||||
print "{0}/{1} chunks rendered".format(i, len(chunks))
|
||||
print "{0}/{1} chunks rendered".format(i, len(asyncresults))
|
||||
|
||||
pool.join()
|
||||
print "Done!"
|
||||
|
||||
Reference in New Issue
Block a user