0

Merge remote branch 'upstream/configfile' into dtt-config-merge

Conflicts:
	chunk.py
	gmap.py
	world.py
This commit is contained in:
Aaron Griffith
2011-03-19 21:10:40 -04:00
6 changed files with 220 additions and 16 deletions

View File

@@ -571,7 +571,7 @@ class ChunkRenderer(object):
# check to see if there are any signs in the persistentData list that are from this chunk. # check to see if there are any signs in the persistentData list that are from this chunk.
# if so, remove them from the persistentData list (since they're have been added to the world.POI # if so, remove them from the persistentData list (since they're have been added to the world.POI
# list above # list above.
if self.queue: if self.queue:
self.queue.put(['removePOI', (self.chunkX, self.chunkY)]) self.queue.put(['removePOI', (self.chunkX, self.chunkY)])

146
configParser.py Normal file
View File

@@ -0,0 +1,146 @@
from optparse import OptionParser
import sys
class OptionsResults(object):
pass
class ConfigOptionParser(object):
def __init__(self, **kwargs):
self.cmdParser = OptionParser(usage=kwargs.get("usage",""))
self.configFile = kwargs.get("config","settings.py")
self.configVars = []
# these are arguments not understood by OptionParser, so they must be removed
# in add_option before being passed to the OptionParser
# note that default is a valid OptionParser argument, but we remove it
# because we want to do our default value handling
self.customArgs = ["required", "commandLineOnly", "default"]
self.requiredArgs = []
def display_config(self):
for x in self.configVars:
n = x['dest']
print "%s: %r" % (n, self.configResults.__dict__[n])
def add_option(self, *args, **kwargs):
if kwargs.get("configFileOnly", False) and kwargs.get("commandLineOnly", False):
raise Exception(args, "configFileOnly and commandLineOnly are mututally exclusive")
self.configVars.append(kwargs.copy())
if not kwargs.get("configFileOnly", False):
for arg in self.customArgs:
if arg in kwargs.keys(): del kwargs[arg]
self.cmdParser.add_option(*args, **kwargs)
def print_help(self):
self.cmdParser.print_help()
def parse_args(self):
# first, load the results from the command line:
options, args = self.cmdParser.parse_args()
# second, use these values to seed the locals dict
l = dict()
g = dict()
for a in self.configVars:
n = a['dest']
if a.get('configFileOnly', False): continue
if a.get('commandLineOnly', False): continue
v = getattr(options, n)
if v != None:
#print "seeding %s with %s" % (n, v)
l[n] = v
else:
# if this has a default, use that to seed the globals dict
if a.get("default", None): g[n] = a['default']
g['args'] = args
try:
execfile(self.configFile, g, l)
except NameError, ex:
import traceback
traceback.print_exc()
print "\nError parsing %s. Please check the trackback above" % self.configFile
sys.exit(1)
except SyntaxError, ex:
import traceback
traceback.print_exc()
tb = sys.exc_info()[2]
#print tb.tb_frame.f_code.co_filename
print "\nError parsing %s. Please check the trackback above" % self.configFile
sys.exit(1)
#print l.keys()
configResults = OptionsResults()
# third, load the results from the config file:
for a in self.configVars:
n = a['dest']
if a.get('commandLineOnly', False):
if n in l.keys():
print "Error: %s can only be specified on the command line. It is not valid in the config file" % n
sys.exit(1)
configResults.__dict__[n] = l.get(n)
# third, merge options into configReslts (with options overwriting anything in configResults)
for a in self.configVars:
n = a['dest']
if a.get('configFileOnly', False): continue
if getattr(options, n) != None:
configResults.__dict__[n] = getattr(options, n)
# forth, set defaults for any empty values
for a in self.configVars:
n = a['dest']
if (n not in configResults.__dict__.keys() or configResults.__dict__[n] == None) and 'default' in a.keys():
configResults.__dict__[n] = a['default']
# fifth, check required args:
for a in self.configVars:
n = a['dest']
if configResults.__dict__[n] == None and a.get('required',False):
raise Exception("%s is required" % n)
# sixth, check types
for a in self.configVars:
n = a['dest']
if 'type' in a.keys() and configResults.__dict__[n] != None:
try:
# switch on type. there are only 6 types that can be used with optparse
if a['type'] == "int":
configResults.__dict__[n] = int(configResults.__dict__[n])
elif a['type'] == "string":
configResults.__dict__[n] = str(configResults.__dict__[n])
elif a['type'] == "long":
configResults.__dict__[n] = long(configResults.__dict__[n])
elif a['type'] == "choice":
if configResults.__dict__[n] not in a['choices']:
print "The value '%s' is not valid for config parameter '%s'" % (configResults.__dict__[n], n)
sys.exit(1)
elif a['type'] == "float":
configResults.__dict__[n] = long(configResults.__dict__[n])
elif a['type'] == "complex":
configResults.__dict__[n] = complex(configResults.__dict__[n])
else:
print "Unknown type!"
sys.exit(1)
except ValueError, ex:
print "There was a problem converting the value '%s' to type %s for config parameter '%s'" % (configResults.__dict__[n], a['type'], n)
import traceback
#traceback.print_exc()
sys.exit(1)
self.configResults = configResults
return configResults, args

51
contrib/benchmark.py Normal file
View File

@@ -0,0 +1,51 @@
import chunk
import world
import tempfile
import glob
import time
import cProfile
import os
import sys
import shutil
# Simple Benchmarking script. Usage and example:
# $ python contrib/benchmark.py World4/
# Rendering 50 chunks...
# Took 20.290062 seconds or 0.405801 seconds per chunk, or 2.464261 chunks per second
# create a new, empty, cache dir
cachedir = tempfile.mkdtemp(prefix="benchmark_cache", dir=".")
if os.path.exists("benchmark.prof"): os.unlink("benchmark.prof")
w = world.WorldRenderer("World4", cachedir)
numchunks = 50
chunklist = w._find_chunkfiles()[:numchunks]
print "Rendering %d chunks..." % (numchunks)
def go():
for f in chunklist:
chunk.render_and_save(f[2], w.cachedir, w, (None,None), None)
start = time.time()
if "-profile" in sys.argv:
cProfile.run("go()", 'benchmark.prof')
else:
go()
stop = time.time()
delta = stop - start
print "Took %f seconds or %f seconds per chunk, or %f chunks per second" % (delta, delta/numchunks, numchunks/delta)
if "-profile" in sys.argv:
print "Profile is below:\n----\n"
import pstats
p = pstats.Stats('benchmark.prof')
p.strip_dirs().sort_stats("cumulative").print_stats(20)
shutil.rmtree(cachedir)

27
gmap.py
View File

@@ -22,7 +22,7 @@ if not (sys.version_info[0] == 2 and sys.version_info[1] >= 6):
import os import os
import os.path import os.path
from optparse import OptionParser from configParser import ConfigOptionParser
import re import re
import multiprocessing import multiprocessing
import time import time
@@ -52,19 +52,21 @@ def main():
cpus = multiprocessing.cpu_count() cpus = multiprocessing.cpu_count()
except NotImplementedError: except NotImplementedError:
cpus = 1 cpus = 1
parser = OptionParser(usage=helptext)
parser = ConfigOptionParser(usage=helptext, config="settings.py")
parser.add_option("-p", "--processes", dest="procs", help="How many worker processes to start. Default %s" % cpus, default=cpus, action="store", type="int") parser.add_option("-p", "--processes", dest="procs", help="How many worker processes to start. Default %s" % cpus, default=cpus, action="store", type="int")
parser.add_option("-z", "--zoom", dest="zoom", help="Sets the zoom level manually instead of calculating it. This can be useful if you have outlier chunks that make your world too big. This value will make the highest zoom level contain (2**ZOOM)^2 tiles", action="store", type="int") parser.add_option("-z", "--zoom", dest="zoom", help="Sets the zoom level manually instead of calculating it. This can be useful if you have outlier chunks that make your world too big. This value will make the highest zoom level contain (2**ZOOM)^2 tiles", action="store", type="int", configFileOnly=True)
parser.add_option("-d", "--delete", dest="delete", help="Clear all caches. Next time you render your world, it will have to start completely over again. This is probably not a good idea for large worlds. Use this if you change texture packs and want to re-render everything.", action="store_true") parser.add_option("-d", "--delete", dest="delete", help="Clear all caches. Next time you render your world, it will have to start completely over again. This is probably not a good idea for large worlds. Use this if you change texture packs and want to re-render everything.", action="store_true", commandLineOnly=True)
parser.add_option("--cachedir", dest="cachedir", help="Sets the directory where the Overviewer will save chunk images, which is an intermediate step before the tiles are generated. You must use the same directory each time to gain any benefit from the cache. If not set, this defaults to your world directory.")
parser.add_option("--chunklist", dest="chunklist", help="A file containing, on each line, a path to a chunkfile to update. Instead of scanning the world directory for chunks, it will just use this list. Normal caching rules still apply.") parser.add_option("--chunklist", dest="chunklist", help="A file containing, on each line, a path to a chunkfile to update. Instead of scanning the world directory for chunks, it will just use this list. Normal caching rules still apply.")
parser.add_option("--lighting", dest="lighting", help="Renders shadows using light data from each chunk.", action="store_true") parser.add_option("--rendermode", dest="rendermode", help="Specifies the render type: normal (default), lighting, night, or spawn.", type="choice", choices=["normal", "lighting", "night", "spawn"], required=True, default="normal")
parser.add_option("--night", dest="night", help="Renders shadows using light data from each chunk, as if it were night. Implies --lighting.", action="store_true") parser.add_option("--imgformat", dest="imgformat", help="The image output format to use. Currently supported: png(default), jpg. NOTE: png will always be used as the intermediate image format.", configFileOnly=True )
parser.add_option("--spawn", dest="spawn", help="Renders shadows using light data from each chunk, as if it were night, while also highlighting areas that are dark enough to spawn mobs. Implies --lighting and --night.", action="store_true") parser.add_option("--optimize-img", dest="optimizeimg", help="If using png, perform image file size optimizations on the output. Specify 1 for pngcrush, 2 for pngcrush+optipng+advdef. This may double (or more) render times, but will produce up to 30% smaller images. NOTE: requires corresponding programs in $PATH or %PATH%", configFileOnly=True)
parser.add_option("--imgformat", dest="imgformat", help="The image output format to use. Currently supported: png(default), jpg.")
parser.add_option("--optimize-img", dest="optimizeimg", help="If using png, perform image file size optimizations on the output. Specify 1 for pngcrush, 2 for pngcrush+optipng+advdef. This may double (or more) render times, but will produce up to 30% smaller images. NOTE: requires corresponding programs in $PATH or %PATH%")
parser.add_option("-q", "--quiet", dest="quiet", action="count", default=0, help="Print less output. You can specify this option multiple times.") parser.add_option("-q", "--quiet", dest="quiet", action="count", default=0, help="Print less output. You can specify this option multiple times.")
parser.add_option("-v", "--verbose", dest="verbose", action="count", default=0, help="Print more output. You can specify this option multiple times.") parser.add_option("-v", "--verbose", dest="verbose", action="count", default=0, help="Print more output. You can specify this option multiple times.")
parser.add_option("--skip-js", dest="skipjs", action="store_true", help="Don't output marker.js or regions.js") parser.add_option("--skip-js", dest="skipjs", action="store_true", help="Don't output marker.js or regions.js")
parser.add_option("--display-config", dest="display_config", action="store_true", help="Display the configuration parameters, but don't render the map. Requires all required options to be specified", commandLineOnly=True)
#parser.add_option("--write-config", dest="write_config", action="store_true", help="Writes out a sample config file", commandLineOnly=True)
options, args = parser.parse_args() options, args = parser.parse_args()
@@ -109,6 +111,11 @@ def main():
parser.error("Where do you want to save the tiles?") parser.error("Where do you want to save the tiles?")
destdir = args[1] destdir = args[1]
if options.display_config:
# just display the config file and exit
parser.display_config()
sys.exit(0)
if options.delete: if options.delete:
return delete_all(worlddir, destdir) return delete_all(worlddir, destdir)
@@ -153,7 +160,7 @@ def main():
# Now generate the tiles # Now generate the tiles
# TODO chunklist # TODO chunklist
q = quadtree.QuadtreeGen(w, destdir, depth=options.zoom, imgformat=imgformat, optimizeimg=optimizeimg, lighting=options.lighting, night=options.night, spawn=options.spawn) q = quadtree.QuadtreeGen(w, destdir, depth=options.zoom, imgformat=imgformat, optimizeimg=optimizeimg, rendermode=options.rendermode)
q.write_html(options.skipjs) q.write_html(options.skipjs)
q.go(options.procs) q.go(options.procs)

View File

@@ -90,7 +90,7 @@ def pool_initializer(quadtree):
child_quadtree = quadtree child_quadtree = quadtree
class QuadtreeGen(object): class QuadtreeGen(object):
def __init__(self, worldobj, destdir, depth=None, tiledir="tiles", imgformat=None, optimizeimg=None, lighting=False, night=False, spawn=False): def __init__(self, worldobj, destdir, depth=None, tiledir="tiles", imgformat=None, optimizeimg=None, rendermode="normal"):
"""Generates a quadtree from the world given into the """Generates a quadtree from the world given into the
given dest directory given dest directory
@@ -104,9 +104,9 @@ class QuadtreeGen(object):
self.imgformat = imgformat self.imgformat = imgformat
self.optimizeimg = optimizeimg self.optimizeimg = optimizeimg
self.lighting = lighting self.lighting = rendermode in ("lighting", "night", "spawn")
self.night = night self.night = rendermode in ("night", "spawn")
self.spawn = spawn self.spawn = spawn in ("spawn",)
# Make the destination dir # Make the destination dir
if not os.path.exists(destdir): if not os.path.exists(destdir):

View File

@@ -64,7 +64,7 @@ class World(object):
"""Does world-level preprocessing to prepare for QuadtreeGen """Does world-level preprocessing to prepare for QuadtreeGen
worlddir is the path to the minecraft world worlddir is the path to the minecraft world
""" """
mincol = maxcol = minrow = maxrow = 0 mincol = maxcol = minrow = maxrow = 0
def __init__(self, worlddir, useBiomeData=False,regionlist=None): def __init__(self, worlddir, useBiomeData=False,regionlist=None):