Merge branch 'logging' of git://github.com/alexjurkiewicz/Minecraft-Overviewer
This commit is contained in:
17
gmap.py
17
gmap.py
@@ -26,6 +26,9 @@ from optparse import OptionParser
|
|||||||
import re
|
import re
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import time
|
import time
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO,format="%(asctime)s [%(levelname)s] %(message)s")
|
||||||
|
|
||||||
import world
|
import world
|
||||||
import quadtree
|
import quadtree
|
||||||
@@ -46,6 +49,8 @@ def main():
|
|||||||
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("--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("--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.")
|
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.")
|
||||||
|
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.")
|
||||||
|
|
||||||
options, args = parser.parse_args()
|
options, args = parser.parse_args()
|
||||||
|
|
||||||
@@ -93,6 +98,14 @@ def main():
|
|||||||
else:
|
else:
|
||||||
imgformat = 'png'
|
imgformat = 'png'
|
||||||
|
|
||||||
|
logging.getLogger().setLevel(
|
||||||
|
logging.getLogger().level + 10*options.quiet)
|
||||||
|
logging.getLogger().setLevel(
|
||||||
|
logging.getLogger().level - 10*options.verbose)
|
||||||
|
|
||||||
|
logging.info("Welcome to Minecraft Overviewer!")
|
||||||
|
logging.debug("Current log level: {0}".format(logging.getLogger().level))
|
||||||
|
|
||||||
# First generate the world's chunk images
|
# First generate the world's chunk images
|
||||||
w = world.WorldRenderer(worlddir, cachedir, chunklist=chunklist)
|
w = world.WorldRenderer(worlddir, cachedir, chunklist=chunklist)
|
||||||
w.go(options.procs)
|
w.go(options.procs)
|
||||||
@@ -110,7 +123,7 @@ def delete_all(worlddir, tiledir):
|
|||||||
for f in filenames:
|
for f in filenames:
|
||||||
if matcher.match(f):
|
if matcher.match(f):
|
||||||
filepath = os.path.join(dirpath, f)
|
filepath = os.path.join(dirpath, f)
|
||||||
print "Deleting {0}".format(filepath)
|
logging.info("Deleting {0}".format(filepath))
|
||||||
os.unlink(filepath)
|
os.unlink(filepath)
|
||||||
|
|
||||||
# Now delete all /hash/ files in the tile dir.
|
# Now delete all /hash/ files in the tile dir.
|
||||||
@@ -119,7 +132,7 @@ def delete_all(worlddir, tiledir):
|
|||||||
for f in filenames:
|
for f in filenames:
|
||||||
if f.endswith(".hash"):
|
if f.endswith(".hash"):
|
||||||
filepath = os.path.join(dirpath, f)
|
filepath = os.path.join(dirpath, f)
|
||||||
print "Deleting {0}".format(filepath)
|
logging.info("Deleting {0}".format(filepath))
|
||||||
os.unlink(filepath)
|
os.unlink(filepath)
|
||||||
|
|
||||||
def list_worlds():
|
def list_worlds():
|
||||||
|
|||||||
36
quadtree.py
36
quadtree.py
@@ -23,6 +23,7 @@ import re
|
|||||||
import shutil
|
import shutil
|
||||||
import collections
|
import collections
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
@@ -45,7 +46,7 @@ def catch_keyboardinterrupt(func):
|
|||||||
try:
|
try:
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print "Ctrl-C caught!"
|
logging.error("Ctrl-C caught!")
|
||||||
raise Exception("Exiting")
|
raise Exception("Exiting")
|
||||||
except:
|
except:
|
||||||
import traceback
|
import traceback
|
||||||
@@ -111,8 +112,8 @@ class QuadtreeGen(object):
|
|||||||
else:
|
else:
|
||||||
if not complete % 1000 == 0:
|
if not complete % 1000 == 0:
|
||||||
return
|
return
|
||||||
print "{0}/{1} tiles complete on level {2}/{3}".format(
|
logging.info("{0}/{1} tiles complete on level {2}/{3}".format(
|
||||||
complete, total, level, self.p)
|
complete, total, level, self.p))
|
||||||
|
|
||||||
def write_html(self, zoomlevel, imgformat):
|
def write_html(self, zoomlevel, imgformat):
|
||||||
"""Writes out index.html"""
|
"""Writes out index.html"""
|
||||||
@@ -258,12 +259,12 @@ class QuadtreeGen(object):
|
|||||||
curdepth = self._get_cur_depth()
|
curdepth = self._get_cur_depth()
|
||||||
if curdepth != -1:
|
if curdepth != -1:
|
||||||
if self.p > curdepth:
|
if self.p > curdepth:
|
||||||
print "Your map seemes to have expanded beyond its previous bounds."
|
logging.warning("Your map seemes to have expanded beyond its previous bounds.")
|
||||||
print "Doing some tile re-arrangements... just a sec..."
|
logging.warning( "Doing some tile re-arrangements... just a sec...")
|
||||||
for _ in xrange(self.p-curdepth):
|
for _ in xrange(self.p-curdepth):
|
||||||
self._increase_depth()
|
self._increase_depth()
|
||||||
elif self.p < curdepth:
|
elif self.p < curdepth:
|
||||||
print "Your map seems to have shrunk. Re-arranging tiles, just a sec..."
|
logging.warning("Your map seems to have shrunk. Re-arranging tiles, just a sec...")
|
||||||
for _ in xrange(curdepth - self.p):
|
for _ in xrange(curdepth - self.p):
|
||||||
self._decrease_depth()
|
self._decrease_depth()
|
||||||
|
|
||||||
@@ -279,11 +280,11 @@ class QuadtreeGen(object):
|
|||||||
results = collections.deque()
|
results = collections.deque()
|
||||||
complete = 0
|
complete = 0
|
||||||
total = 4**self.p
|
total = 4**self.p
|
||||||
print "Rendering highest zoom level of tiles now."
|
logging.info("Rendering highest zoom level of tiles now.")
|
||||||
print "There are {0} tiles to render".format(total)
|
logging.info("There are {0} tiles to render".format(total))
|
||||||
print "There are {0} total levels to render".format(self.p)
|
logging.info("There are {0} total levels to render".format(self.p))
|
||||||
print "Don't worry, each level has only 25% as many tiles as the last."
|
logging.info("Don't worry, each level has only 25% as many tiles as the last.")
|
||||||
print "The others will go faster"
|
logging.info("The others will go faster")
|
||||||
for result in self._apply_render_worldtiles(pool):
|
for result in self._apply_render_worldtiles(pool):
|
||||||
results.append(result)
|
results.append(result)
|
||||||
if len(results) > 10000:
|
if len(results) > 10000:
|
||||||
@@ -308,7 +309,7 @@ class QuadtreeGen(object):
|
|||||||
assert len(results) == 0
|
assert len(results) == 0
|
||||||
complete = 0
|
complete = 0
|
||||||
total = 4**zoom
|
total = 4**zoom
|
||||||
print "Starting level", level
|
logging.info("Starting level {0}".format(level))
|
||||||
for result in self._apply_render_inntertile(pool, zoom):
|
for result in self._apply_render_inntertile(pool, zoom):
|
||||||
results.append(result)
|
results.append(result)
|
||||||
if len(results) > 10000:
|
if len(results) > 10000:
|
||||||
@@ -324,7 +325,7 @@ class QuadtreeGen(object):
|
|||||||
|
|
||||||
self.print_statusline(complete, total, level, True)
|
self.print_statusline(complete, total, level, True)
|
||||||
|
|
||||||
print "Done"
|
logging.info("Done")
|
||||||
|
|
||||||
pool.close()
|
pool.close()
|
||||||
pool.join()
|
pool.join()
|
||||||
@@ -561,8 +562,7 @@ def render_worldtile(chunks, colstart, colend, rowstart, rowend, path, imgformat
|
|||||||
# corrupting it), then this could error.
|
# corrupting it), then this could error.
|
||||||
# Since we have no easy way of determining how this chunk was
|
# Since we have no easy way of determining how this chunk was
|
||||||
# generated, we need to just ignore it.
|
# generated, we need to just ignore it.
|
||||||
print "Error opening file", chunkfile
|
logging.warning("Could not open chunk '{0}' ({1})".format(chunkfile,e))
|
||||||
print "(Error was {0})".format(e)
|
|
||||||
try:
|
try:
|
||||||
# Remove the file so that the next run will re-generate it.
|
# Remove the file so that the next run will re-generate it.
|
||||||
os.unlink(chunkfile)
|
os.unlink(chunkfile)
|
||||||
@@ -571,12 +571,12 @@ def render_worldtile(chunks, colstart, colend, rowstart, rowend, path, imgformat
|
|||||||
# Ignore if file doesn't exist, another task could have already
|
# Ignore if file doesn't exist, another task could have already
|
||||||
# removed it.
|
# removed it.
|
||||||
if e.errno != errno.ENOENT:
|
if e.errno != errno.ENOENT:
|
||||||
print "Could not remove the corrupt chunk!"
|
logging.warning("Could not remove chunk '{0}'!".format(chunkfile))
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
print "Removed the corrupt file"
|
logging.warning("Removed the corrupt file")
|
||||||
|
|
||||||
print "You will need to re-run the Overviewer to fix this chunk"
|
logging.warning("You will need to re-run the Overviewer to fix this chunk")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
xpos = -192 + (col-colstart)*192
|
xpos = -192 + (col-colstart)*192
|
||||||
|
|||||||
18
world.py
18
world.py
@@ -18,6 +18,7 @@ import os
|
|||||||
import os.path
|
import os.path
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import sys
|
import sys
|
||||||
|
import logging
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
@@ -170,8 +171,9 @@ class WorldRenderer(object):
|
|||||||
def go(self, procs):
|
def go(self, procs):
|
||||||
"""Starts the render. This returns when it is finished"""
|
"""Starts the render. This returns when it is finished"""
|
||||||
|
|
||||||
print "Scanning chunks"
|
logging.info("Scanning chunks")
|
||||||
raw_chunks = self._find_chunkfiles()
|
raw_chunks = self._find_chunkfiles()
|
||||||
|
logging.debug("Done scanning chunks")
|
||||||
|
|
||||||
# Translate chunks to our diagonal coordinate system
|
# Translate chunks to our diagonal coordinate system
|
||||||
mincol, maxcol, minrow, maxrow, chunks = _convert_coords(raw_chunks)
|
mincol, maxcol, minrow, maxrow, chunks = _convert_coords(raw_chunks)
|
||||||
@@ -205,9 +207,11 @@ class WorldRenderer(object):
|
|||||||
p = f.split(".")
|
p = f.split(".")
|
||||||
all_chunks.append((base36decode(p[1]), base36decode(p[2]),
|
all_chunks.append((base36decode(p[1]), base36decode(p[2]),
|
||||||
os.path.join(dirpath, f)))
|
os.path.join(dirpath, f)))
|
||||||
|
logging.debug((base36decode(p[1]), base36decode(p[2]),
|
||||||
|
os.path.join(dirpath, f)))
|
||||||
|
|
||||||
if not all_chunks:
|
if not all_chunks:
|
||||||
print "Error: No chunks found!"
|
logging.error("Error: No chunks found!")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
return all_chunks
|
return all_chunks
|
||||||
|
|
||||||
@@ -228,7 +232,7 @@ class WorldRenderer(object):
|
|||||||
results = {}
|
results = {}
|
||||||
if processes == 1:
|
if processes == 1:
|
||||||
# Skip the multiprocessing stuff
|
# Skip the multiprocessing stuff
|
||||||
print "Rendering chunks synchronously since you requested 1 process"
|
logging.debug("Rendering chunks synchronously since you requested 1 process")
|
||||||
for i, (col, row, chunkfile) in enumerate(chunks):
|
for i, (col, row, chunkfile) in enumerate(chunks):
|
||||||
if inclusion_set and (col, row) not in inclusion_set:
|
if inclusion_set and (col, row) not in inclusion_set:
|
||||||
# Skip rendering, just find where the existing image is
|
# Skip rendering, just find where the existing image is
|
||||||
@@ -242,9 +246,9 @@ class WorldRenderer(object):
|
|||||||
results[(col, row)] = result
|
results[(col, row)] = result
|
||||||
if i > 0:
|
if i > 0:
|
||||||
if 1000 % i == 0 or i % 1000 == 0:
|
if 1000 % i == 0 or i % 1000 == 0:
|
||||||
print "{0}/{1} chunks rendered".format(i, len(chunks))
|
logging.info("{0}/{1} chunks rendered".format(i, len(chunks)))
|
||||||
else:
|
else:
|
||||||
print "Rendering chunks in {0} processes".format(processes)
|
logging.debug("Rendering chunks in {0} processes".format(processes))
|
||||||
pool = multiprocessing.Pool(processes=processes)
|
pool = multiprocessing.Pool(processes=processes)
|
||||||
asyncresults = []
|
asyncresults = []
|
||||||
for col, row, chunkfile in chunks:
|
for col, row, chunkfile in chunks:
|
||||||
@@ -267,10 +271,10 @@ class WorldRenderer(object):
|
|||||||
results[(col, row)] = result.get()
|
results[(col, row)] = result.get()
|
||||||
if i > 0:
|
if i > 0:
|
||||||
if 1000 % i == 0 or i % 1000 == 0:
|
if 1000 % i == 0 or i % 1000 == 0:
|
||||||
print "{0}/{1} chunks rendered".format(i, len(asyncresults))
|
logging.info("{0}/{1} chunks rendered".format(i, len(asyncresults)))
|
||||||
|
|
||||||
pool.join()
|
pool.join()
|
||||||
print "Done!"
|
logging.info("Done!")
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user