0

Initial Python 3 port

Many things work, some don't. Notably, genPOI doesn't work, and
there's some signedness comparison stuff going on in the C extension.

This also completely drops support for Python 2, as maintaining a C
extension for both Python 2 and 3 is a pain and not worth it for the
9 months that Python 2 is still going to be supported upstream.

The documentation needs to be adjusted as well.

All of the few tests we have pass, and rendering a map works, both
with a configuration file and without. We can also use optimizeimages.

Concerns #1528.
This commit is contained in:
Nicolas F
2019-03-16 20:43:25 +01:00
parent 99eebd5b69
commit e348a548b6
33 changed files with 369 additions and 625 deletions

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2 #!/usr/bin/env python3
# This file is part of the Minecraft Overviewer. # This file is part of the Minecraft Overviewer.
# #
@@ -21,10 +21,8 @@ import platform
import sys import sys
# quick version check # quick version check
if not (sys.version_info[0] == 2 and sys.version_info[1] >= 6): if sys.version_info[0] == 2 or (sys.version_info[0] == 3 and sys.version_info[1] < 4):
print("Sorry, the Overviewer requires at least Python 2.6 to run") print("Sorry, the Overviewer requires at least Python 3.4 to run.")
if sys.version_info[0] >= 3:
print("and will not run on Python 3.0 or later")
sys.exit(1) sys.exit(1)
import os import os
@@ -35,6 +33,7 @@ import multiprocessing
import time import time
import logging import logging
from argparse import ArgumentParser from argparse import ArgumentParser
from collections import OrderedDict
from overviewer_core import util from overviewer_core import util
from overviewer_core import logger from overviewer_core import logger
@@ -136,7 +135,7 @@ def main():
# Check for possible shell quoting issues # Check for possible shell quoting issues
if len(unknowns) > 0: if len(unknowns) > 0:
possible_mistakes = [] possible_mistakes = []
for i in xrange(len(unknowns) + 1): for i in range(len(unknowns) + 1):
possible_mistakes.append(" ".join([args.world, args.output] + unknowns[:i])) possible_mistakes.append(" ".join([args.world, args.output] + unknowns[:i]))
possible_mistakes.append(" ".join([args.output] + unknowns[:i])) possible_mistakes.append(" ".join([args.output] + unknowns[:i]))
for mistake in possible_mistakes: for mistake in possible_mistakes:
@@ -183,9 +182,9 @@ def main():
print("Currently running Minecraft Overviewer %s" % util.findGitVersion() + print("Currently running Minecraft Overviewer %s" % util.findGitVersion() +
" (%s)" % util.findGitHash()[:7]) " (%s)" % util.findGitHash()[:7])
try: try:
import urllib from urllib import request
import json import json
latest_ver = json.loads(urllib.urlopen("http://overviewer.org/download.json") latest_ver = json.loads(request.urlopen("http://overviewer.org/download.json")
.read())['src'] .read())['src']
print("Latest version of Minecraft Overviewer %s (%s)" % (latest_ver['version'], print("Latest version of Minecraft Overviewer %s (%s)" % (latest_ver['version'],
latest_ver['commit'][:7])) latest_ver['commit'][:7]))
@@ -294,7 +293,7 @@ def main():
rendermodes = args.rendermodes.replace("-", "_").split(",") rendermodes = args.rendermodes.replace("-", "_").split(",")
# Now for some good defaults # Now for some good defaults
renders = util.OrderedDict() renders = OrderedDict()
for rm in rendermodes: for rm in rendermodes:
renders["world-" + rm] = { renders["world-" + rm] = {
"world": "world", "world": "world",
@@ -337,7 +336,7 @@ def main():
if args.check_terrain: # we are already in the "if configfile" branch if args.check_terrain: # we are already in the "if configfile" branch
logging.info("Looking for a few common texture files...") logging.info("Looking for a few common texture files...")
for render_name, render in config['renders'].iteritems(): for render_name, render in config['renders'].items():
logging.info("Looking at render %r.", render_name) logging.info("Looking at render %r.", render_name)
# find or create the textures object # find or create the textures object
@@ -356,7 +355,7 @@ def main():
logging.debug("Current log level: {0}.".format(logging.getLogger().level)) logging.debug("Current log level: {0}.".format(logging.getLogger().level))
def set_renderchecks(checkname, num): def set_renderchecks(checkname, num):
for name, render in config['renders'].iteritems(): for name, render in config['renders'].items():
if render.get('renderchecks', 0) == 3: if render.get('renderchecks', 0) == 3:
logging.warning(checkname + " ignoring render " + repr(name) + " since it's " logging.warning(checkname + " ignoring render " + repr(name) + " since it's "
"marked as \"don't render\".") "marked as \"don't render\".")
@@ -381,7 +380,7 @@ def main():
##################### #####################
# Do a few last minute things to each render dictionary here # Do a few last minute things to each render dictionary here
for rname, render in config['renders'].iteritems(): for rname, render in config['renders'].items():
# Convert render['world'] to the world path, and store the original # Convert render['world'] to the world path, and store the original
# in render['worldname_orig'] # in render['worldname_orig']
try: try:
@@ -437,7 +436,7 @@ def main():
# The changelist support. # The changelist support.
changelists = {} changelists = {}
for render in config['renders'].itervalues(): for render in config['renders'].values():
if 'changelist' in render: if 'changelist' in render:
path = render['changelist'] path = render['changelist']
if path not in changelists: if path not in changelists:
@@ -461,7 +460,7 @@ def main():
# TODO: optionally more caching layers here # TODO: optionally more caching layers here
renders = config['renders'] renders = config['renders']
for render_name, render in renders.iteritems(): for render_name, render in renders.items():
logging.debug("Found the following render thing: %r", render) logging.debug("Found the following render thing: %r", render)
# find or create the world object # find or create the world object
@@ -572,7 +571,7 @@ def main():
assetMrg.finalize(tilesets) assetMrg.finalize(tilesets)
for out in changelists.itervalues(): for out in changelists.values():
logging.debug("Closing %s (%s).", out, out.fileno()) logging.debug("Closing %s (%s).", out, out.fileno())
out.close() out.close()
@@ -603,8 +602,8 @@ def list_worlds():
formatString = "%-" + str(worldNameLen) + "s | %-8s | %-16s | %s " formatString = "%-" + str(worldNameLen) + "s | %-8s | %-16s | %s "
print(formatString % ("World", "Playtime", "Modified", "Path")) print(formatString % ("World", "Playtime", "Modified", "Path"))
print(formatString % ("-" * worldNameLen, "-" * 8, '-' * 16, '-' * 4)) print(formatString % ("-" * worldNameLen, "-" * 8, '-' * 16, '-' * 4))
for name, info in sorted(worlds.iteritems()): for name, info in sorted(worlds.items()):
if isinstance(name, basestring) and name.startswith("World") and len(name) == 6: if isinstance(name, str) and name.startswith("World") and len(name) == 6:
try: try:
world_n = int(name[-1]) world_n = int(name[-1])
# we'll catch this one later, when it shows up as an # we'll catch this one later, when it shows up as an

View File

@@ -2,13 +2,15 @@
# Code to check to make sure c_overviewer is built and working # Code to check to make sure c_overviewer is built and working
# #
from __future__ import print_function
import os.path import os.path
import os import os
import platform import platform
import traceback import traceback
import sys import sys
import util from . import util
def check_c_overviewer(): def check_c_overviewer():
"""Check to make sure c_overviewer works and is up-to-date. Prints """Check to make sure c_overviewer works and is up-to-date. Prints
@@ -18,29 +20,28 @@ def check_c_overviewer():
root_dir = util.get_program_path() root_dir = util.get_program_path()
# make sure the c_overviewer extension is available # make sure the c_overviewer extension is available
try: try:
import c_overviewer from . import c_overviewer
except ImportError: except ImportError:
if os.environ.get("OVERVIEWER_DEBUG_IMPORT") == "1": if os.environ.get("OVERVIEWER_DEBUG_IMPORT") == "1":
traceback.print_exc() traceback.print_exc()
## if this is a frozen windows package, the following error messages about ## if this is a frozen windows package, the following error messages about
## building the c_overviewer extension are not appropriate ## building the c_overviewer extension are not appropriate
if hasattr(sys, "frozen") and platform.system() == 'Windows': if hasattr(sys, "frozen") and platform.system() == 'Windows':
print "Something has gone wrong importing the c_overviewer extension. Please" print("Something has gone wrong importing the c_overviewer extension. Please make sure "
print "make sure the 2008 and 2010 redistributable packages from Microsoft" "the 2008 and 2010 redistributable packages from Microsoft are installed.")
print "are installed."
return 1 return 1
## try to find the build extension ## try to find the build extension
ext = os.path.join(root_dir, "overviewer_core", "c_overviewer.%s" % ("pyd" if platform.system() == "Windows" else "so")) ext = os.path.join(root_dir, "overviewer_core", "c_overviewer.%s" % ("pyd" if platform.system() == "Windows" else "so"))
if os.path.exists(ext): if os.path.exists(ext):
traceback.print_exc() traceback.print_exc()
print "" print()
print "Something has gone wrong importing the c_overviewer extension. Please" print("Something has gone wrong importing the c_overviewer extension. Please make sure "
print "make sure it is up-to-date (clean and rebuild)" "it is up-to-date. (clean and rebuild)")
return 1 return 1
print "You need to compile the c_overviewer module to run Minecraft Overviewer." print("You need to compile the c_overviewer module to run Minecraft Overviewer.")
print "Run `python setup.py build`, or see the README for details." print("Run `python setup.py build`, or see the README for details.")
return 1 return 1
# #
@@ -54,14 +55,15 @@ def check_c_overviewer():
if os.path.exists(os.path.join(root_dir, "overviewer_core", "src", "overviewer.h")): if os.path.exists(os.path.join(root_dir, "overviewer_core", "src", "overviewer.h")):
with open(os.path.join(root_dir, "overviewer_core", "src", "overviewer.h")) as f: with open(os.path.join(root_dir, "overviewer_core", "src", "overviewer.h")) as f:
lines = f.readlines() lines = f.readlines()
lines = filter(lambda x: x.startswith("#define OVERVIEWER_EXTENSION_VERSION"), lines) lines = list(filter(lambda x: x.startswith("#define OVERVIEWER_EXTENSION_VERSION"),
lines))
if lines: if lines:
l = lines[0] l = lines[0]
if int(l.split()[2].strip()) != c_overviewer.extension_version(): if int(l.split()[2].strip()) != c_overviewer.extension_version():
print "Please rebuild your c_overviewer module. It is out of date!" print("Please rebuild your c_overviewer module. It is out of date!")
return 1 return 1
else: else:
print "Please rebuild your c_overviewer module. It is out of date!" print("Please rebuild your c_overviewer module. It is out of date!")
return 1 return 1
# all good! # all good!

View File

@@ -23,9 +23,9 @@ import traceback
from PIL import Image from PIL import Image
import world from . import world
import util from . import util
from files import FileReplacer, mirror_dir, get_fs_caps from .files import FileReplacer, mirror_dir, get_fs_caps
class AssetManager(object): class AssetManager(object):
@@ -53,7 +53,7 @@ top-level directory.
with open(config_loc) as c: with open(config_loc) as c:
ovconf_str = "{" + "\n".join(c.readlines()[1:-1]) + "}" ovconf_str = "{" + "\n".join(c.readlines()[1:-1]) + "}"
self.overviewerConfig = json.loads(ovconf_str) self.overviewerConfig = json.loads(ovconf_str)
except Exception, e: except Exception as e:
if os.path.exists(config_loc): if os.path.exists(config_loc):
logging.warning("A previous overviewerConfig.js was found, " logging.warning("A previous overviewerConfig.js was found, "
"but I couldn't read it for some reason." "but I couldn't read it for some reason."
@@ -61,16 +61,6 @@ top-level directory.
logging.debug(traceback.format_exc()) logging.debug(traceback.format_exc())
self.overviewerConfig = dict(tilesets=dict()) self.overviewerConfig = dict(tilesets=dict())
# Make sure python knows the preferred encoding. If it does not, set it
# to utf-8"
self.preferredencoding = locale.getpreferredencoding()
try:
# We don't care what is returned, just that we can get a codec.
codecs.lookup(self.preferredencoding)
except LookupError:
self.preferredencoding = "utf_8"
logging.debug("Preferred enoding set to: %s", self.preferredencoding)
def get_tileset_config(self, name): def get_tileset_config(self, name):
"Return the correct dictionary from the parsed overviewerConfig.js" "Return the correct dictionary from the parsed overviewerConfig.js"
for conf in self.overviewerConfig['tilesets']: for conf in self.overviewerConfig['tilesets']:
@@ -213,10 +203,7 @@ top-level directory.
index = codecs.open(indexpath, 'r', encoding='UTF-8').read() index = codecs.open(indexpath, 'r', encoding='UTF-8').read()
index = index.replace("{title}", "Minecraft Overviewer") index = index.replace("{title}", "Minecraft Overviewer")
index = index.replace("{time}", index = index.replace("{time}", time.strftime("%a, %d %b %Y %H:%M:%S %Z", time.localtime()))
time.strftime("%a, %d %b %Y %H:%M:%S %Z",
time.localtime())
.decode(self.preferredencoding))
versionstr = "%s (%s)" % (util.findGitVersion(), versionstr = "%s (%s)" % (util.findGitVersion(),
util.findGitHash()[:7]) util.findGitHash()[:7])
index = index.replace("{version}", versionstr) index = index.replace("{version}", versionstr)

View File

@@ -4,8 +4,8 @@ import os.path
import logging import logging
import traceback import traceback
import settingsDefinition from . import settingsDefinition
import settingsValidators from . import settingsValidators
class MissingConfigException(Exception): class MissingConfigException(Exception):
"To be thrown when the config file can't be found" "To be thrown when the config file can't be found"
@@ -76,12 +76,14 @@ class MultiWorldParser(object):
# The global environment should be the rendermode module, so the config # The global environment should be the rendermode module, so the config
# file has access to those resources. # file has access to those resources.
import rendermodes from . import rendermodes
try: try:
execfile(settings_file, rendermodes.__dict__, self._config_state) with open(settings_file, "rb") as settings_file_handle:
exec(compile(settings_file_handle.read(), settings_file, 'exec'),
rendermodes.__dict__, self._config_state)
except Exception, ex: except Exception as ex:
if isinstance(ex, SyntaxError): if isinstance(ex, SyntaxError):
logging.error("Syntax error parsing %s" % settings_file) logging.error("Syntax error parsing %s" % settings_file)
logging.error("The traceback below will tell you which line triggered the syntax error\n") logging.error("The traceback below will tell you which line triggered the syntax error\n")
@@ -109,7 +111,7 @@ class MultiWorldParser(object):
# At this point, make a pass through the file to possibly set global # At this point, make a pass through the file to possibly set global
# render defaults # render defaults
render_settings = self._settings['renders'].validator.valuevalidator.config render_settings = self._settings['renders'].validator.valuevalidator.config
for key in self._config_state.iterkeys(): for key in self._config_state.keys():
if key not in self._settings: if key not in self._settings:
if key in render_settings: if key in render_settings:
setting = render_settings[key] setting = render_settings[key]

View File

@@ -13,12 +13,12 @@
# You should have received a copy of the GNU General Public License along # You should have received a copy of the GNU General Public License along
# with the Overviewer. If not, see <http://www.gnu.org/licenses/>. # with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
import util from . import util
import multiprocessing import multiprocessing
import multiprocessing.managers import multiprocessing.managers
import Queue import queue
import time import time
from signals import Signal from .signals import Signal
class Dispatcher(object): class Dispatcher(object):
"""This class coordinates the work of all the TileSet objects """This class coordinates the work of all the TileSet objects
@@ -51,7 +51,7 @@ class Dispatcher(object):
# iterate through all possible phases # iterate through all possible phases
num_phases = [tileset.get_num_phases() for tileset in tilesetlist] num_phases = [tileset.get_num_phases() for tileset in tilesetlist]
for phase in xrange(max(num_phases)): for phase in range(max(num_phases)):
# construct a list of iterators to use for this phase # construct a list of iterators to use for this phase
work_iterators = [] work_iterators = []
for i, tileset in enumerate(tilesetlist): for i, tileset in enumerate(tilesetlist):
@@ -235,7 +235,7 @@ class MultiprocessingDispatcherProcess(multiprocessing.Process):
def handler(*args, **kwargs): def handler(*args, **kwargs):
self.signal_queue.put((name, args, kwargs), False) self.signal_queue.put((name, args, kwargs), False)
sig.set_interceptor(handler) sig.set_interceptor(handler)
for name, sig in Signal.signals.iteritems(): for name, sig in Signal.signals.items():
register_signal(name, sig) register_signal(name, sig)
# notify that we're starting up # notify that we're starting up
@@ -259,7 +259,7 @@ class MultiprocessingDispatcherProcess(multiprocessing.Process):
ret = self.tilesets[ti].do_work(workitem) ret = self.tilesets[ti].do_work(workitem)
result = (ti, workitem, ret,) result = (ti, workitem, ret,)
self.result_queue.put(result, False) self.result_queue.put(result, False)
except Queue.Empty: except queue.Empty:
pass pass
class MultiprocessingDispatcher(Dispatcher): class MultiprocessingDispatcher(Dispatcher):
@@ -288,7 +288,7 @@ class MultiprocessingDispatcher(Dispatcher):
# create and fill the pool # create and fill the pool
self.pool = [] self.pool = []
for i in xrange(self.local_procs): for i in range(self.local_procs):
proc = MultiprocessingDispatcherProcess(self.manager) proc = MultiprocessingDispatcherProcess(self.manager)
proc.start() proc.start()
self.pool.append(proc) self.pool.append(proc)
@@ -300,7 +300,7 @@ class MultiprocessingDispatcher(Dispatcher):
self._handle_messages() self._handle_messages()
# send of the end-of-jobs sentinel # send of the end-of-jobs sentinel
for p in xrange(self.num_workers): for p in range(self.num_workers):
self.job_queue.put(None, False) self.job_queue.put(None, False)
# TODO better way to be sure worker processes get the message # TODO better way to be sure worker processes get the message
@@ -350,7 +350,7 @@ class MultiprocessingDispatcher(Dispatcher):
else: else:
# new worker # new worker
self.num_workers += 1 self.num_workers += 1
except Queue.Empty: except queue.Empty:
result_empty = True result_empty = True
if not signal_empty: if not signal_empty:
try: try:
@@ -363,7 +363,7 @@ class MultiprocessingDispatcher(Dispatcher):
sig = Signal.signals[name] sig = Signal.signals[name]
sig.emit_intercepted(*args, **kwargs) sig.emit_intercepted(*args, **kwargs)
except Queue.Empty: except queue.Empty:
signal_empty = True signal_empty = True
return finished_jobs return finished_jobs

View File

@@ -144,7 +144,7 @@ class FileReplacer(object):
# error # error
try: try:
os.remove(self.tmpname) os.remove(self.tmpname)
except Exception, e: except Exception as e:
logging.warning("An error was raised, so I was doing " logging.warning("An error was raised, so I was doing "
"some cleanup first, but I couldn't remove " "some cleanup first, but I couldn't remove "
"'%s'!", self.tmpname) "'%s'!", self.tmpname)
@@ -153,7 +153,7 @@ class FileReplacer(object):
if self.caps.get("chmod_works") and os.path.exists(self.destname): if self.caps.get("chmod_works") and os.path.exists(self.destname):
try: try:
shutil.copymode(self.destname, self.tmpname) shutil.copymode(self.destname, self.tmpname)
except OSError, e: except OSError as e:
# Ignore errno ENOENT: file does not exist. Due to a race # Ignore errno ENOENT: file does not exist. Due to a race
# condition, two processes could conceivably try and update # condition, two processes could conceivably try and update
# the same temp file at the same time # the same temp file at the same time
@@ -162,7 +162,7 @@ class FileReplacer(object):
# atomic rename into place # atomic rename into place
try: try:
os.rename(self.tmpname, self.destname) os.rename(self.tmpname, self.destname)
except OSError, e: except OSError as e:
# Ignore errno ENOENT: file does not exist. Due to a race # Ignore errno ENOENT: file does not exist. Due to a race
# condition, two processes could conceivably try and update # condition, two processes could conceivably try and update
# the same temp file at the same time # the same temp file at the same time

View File

@@ -18,7 +18,7 @@ import logging
import os import os
import platform import platform
import sys import sys
from cStringIO import StringIO from io import BytesIO
# Some cool code for colored logging: # Some cool code for colored logging:
# For background, add 40. For foreground, add 30 # For background, add 40. For foreground, add 30
@@ -88,7 +88,7 @@ class WindowsOutputStream(object):
def write(self, s): def write(self, s):
msg_strm = StringIO(s) msg_strm = BytesIO(s)
while (True): while (True):
c = msg_strm.read(1) c = msg_strm.read(1)

View File

@@ -15,7 +15,7 @@
import functools import functools
import gzip import gzip
import StringIO from io import BytesIO
import struct import struct
import zlib import zlib
@@ -25,7 +25,7 @@ import zlib
def _file_loader(func): def _file_loader(func):
@functools.wraps(func) @functools.wraps(func)
def wrapper(fileobj, *args): def wrapper(fileobj, *args):
if isinstance(fileobj, basestring): if type(fileobj) == str:
# Is actually a filename # Is actually a filename
fileobj = open(fileobj, 'rb', 4096) fileobj = open(fileobj, 'rb', 4096)
return func(fileobj, *args) return func(fileobj, *args)
@@ -92,7 +92,7 @@ class NBTFileReader(object):
# pure zlib stream -- maybe later replace this with # pure zlib stream -- maybe later replace this with
# a custom zlib file object? # a custom zlib file object?
data = zlib.decompress(fileobj.read()) data = zlib.decompress(fileobj.read())
self._file = StringIO.StringIO(data) self._file = BytesIO(data)
# mapping of NBT type ids to functions to read them out # mapping of NBT type ids to functions to read them out
self._read_tagmap = { self._read_tagmap = {
@@ -168,7 +168,7 @@ class NBTFileReader(object):
read_method = self._read_tagmap[tagid] read_method = self._read_tagmap[tagid]
l = [] l = []
for _ in xrange(length): for _ in range(length):
l.append(read_method()) l.append(read_method())
return l return l
@@ -203,7 +203,7 @@ class NBTFileReader(object):
name = self._read_tag_string() name = self._read_tag_string()
payload = self._read_tag_compound() payload = self._read_tag_compound()
return (name, payload) return (name, payload)
except (struct.error, ValueError, TypeError), e: except (struct.error, ValueError, TypeError) as e:
raise CorruptNBTError("could not parse nbt: %s" % (str(e),)) raise CorruptNBTError("could not parse nbt: %s" % (str(e),))
@@ -252,9 +252,9 @@ class MCRFileReader(object):
file, as (x, z) coordinate tuples. To load these chunks, file, as (x, z) coordinate tuples. To load these chunks,
provide these coordinates to load_chunk().""" provide these coordinates to load_chunk()."""
for x in xrange(32): for x in range(32):
for z in xrange(32): for z in range(32):
if self._locations[x + z * 32] >> 8 != 0: if self._locations[int(x + z * 32)] >> 8 != 0:
yield (x, z) yield (x, z)
def get_chunk_timestamp(self, x, z): def get_chunk_timestamp(self, x, z):
@@ -264,13 +264,13 @@ class MCRFileReader(object):
""" """
x = x % 32 x = x % 32
z = z % 32 z = z % 32
return self._timestamps[x + z * 32] return self._timestamps[int(x + z * 32)]
def chunk_exists(self, x, z): def chunk_exists(self, x, z):
"""Determines if a chunk exists.""" """Determines if a chunk exists."""
x = x % 32 x = x % 32
z = z % 32 z = z % 32
return self._locations[x + z * 32] >> 8 != 0 return self._locations[int(x + z * 32)] >> 8 != 0
def load_chunk(self, x, z): def load_chunk(self, x, z):
"""Return a (name, data) tuple for the given chunk, or """Return a (name, data) tuple for the given chunk, or
@@ -281,7 +281,7 @@ class MCRFileReader(object):
have the chunks load out of regions properly.""" have the chunks load out of regions properly."""
x = x % 32 x = x % 32
z = z % 32 z = z % 32
location = self._locations[x + z * 32] location = self._locations[int(x + z * 32)]
offset = (location >> 8) * 4096 offset = (location >> 8) * 4096
sectors = location & 0xff sectors = location & 0xff
@@ -311,16 +311,16 @@ class MCRFileReader(object):
raise CorruptRegionError("unsupported chunk compression type: %i " raise CorruptRegionError("unsupported chunk compression type: %i "
"(should be 1 or 2)" % (compression,)) "(should be 1 or 2)" % (compression,))
# turn the rest of the data into a StringIO object # turn the rest of the data into a BytesIO object
# (using data_length - 1, as we already read 1 byte for compression) # (using data_length - 1, as we already read 1 byte for compression)
data = self._file.read(data_length - 1) data = self._file.read(data_length - 1)
if len(data) != data_length - 1: if len(data) != data_length - 1:
raise CorruptRegionError("chunk length is invalid") raise CorruptRegionError("chunk length is invalid")
data = StringIO.StringIO(data) data = BytesIO(data)
try: try:
return NBTFileReader(data, is_gzip=is_gzip).read_all() return NBTFileReader(data, is_gzip=is_gzip).read_all()
except CorruptionError: except CorruptionError:
raise raise
except Exception, e: except Exception as e:
raise CorruptChunkError("Misc error parsing chunk: " + str(e)) raise CorruptChunkError("Misc error parsing chunk: " + str(e))

View File

@@ -19,8 +19,8 @@ import os
import sys import sys
import time import time
import progressbar from . import progressbar
import rcon from . import rcon
class Observer(object): class Observer(object):

View File

@@ -34,8 +34,7 @@ class Optimizer:
path = os.environ.get("PATH").split(os.pathsep) path = os.environ.get("PATH").split(os.pathsep)
def exists_in_path(prog): def exists_in_path(prog):
result = filter(lambda x: os.path.exists(os.path.join(x, prog)), result = [x for x in path if os.path.exists(os.path.join(x, prog))]
path)
return len(result) != 0 return len(result) != 0
binaries = self.binarynames + [x + ".exe" for x in self.binarynames] binaries = self.binarynames + [x + ".exe" for x in self.binarynames]

View File

@@ -189,13 +189,13 @@ class Bar(ProgressBarWidgetHFill):
self.left = left self.left = left
self.right = right self.right = right
def _format_marker(self, pbar): def _format_marker(self, pbar):
if isinstance(self.marker, (str, unicode)): if isinstance(self.marker, str):
return self.marker return self.marker
else: else:
return self.marker.update(pbar) return self.marker.update(pbar)
def update(self, pbar, width): def update(self, pbar, width):
percent = pbar.percentage() percent = pbar.percentage()
cwidth = width - len(self.left) - len(self.right) cwidth = int(width - len(self.left) - len(self.right))
marked_width = int(percent * cwidth / 100) marked_width = int(percent * cwidth / 100)
m = self._format_marker(pbar) m = self._format_marker(pbar)
bar = (self.left + (m*marked_width).ljust(cwidth) + self.right) bar = (self.left + (m*marked_width).ljust(cwidth) + self.right)
@@ -283,7 +283,7 @@ class ProgressBar(object):
r.append(w) r.append(w)
hfill_inds.append(i) hfill_inds.append(i)
num_hfill += 1 num_hfill += 1
elif isinstance(w, (str, unicode)): elif isinstance(w, str):
r.append(w) r.append(w)
currwidth += len(w) currwidth += len(w)
else: else:

View File

@@ -14,7 +14,7 @@
# with the Overviewer. If not, see <http://www.gnu.org/licenses/>. # with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
from PIL import Image from PIL import Image
import textures from . import textures
"""The contents of this file are imported into the namespace of config files. """The contents of this file are imported into the namespace of config files.
It also defines the render primitive objects, which are used by the C code. It also defines the render primitive objects, which are used by the C code.
@@ -31,13 +31,13 @@ class RenderPrimitive(object):
raise RuntimeError("RenderPrimitive cannot be used directly") raise RuntimeError("RenderPrimitive cannot be used directly")
self.option_values = {} self.option_values = {}
for key, val in kwargs.iteritems(): for key, val in kwargs.items():
if not key in self.options: if not key in self.options:
raise ValueError("primitive `{0}' has no option `{1}'".format(self.name, key)) raise ValueError("primitive `{0}' has no option `{1}'".format(self.name, key))
self.option_values[key] = val self.option_values[key] = val
# set up defaults # set up defaults
for name, (description, default) in self.options.iteritems(): for name, (description, default) in self.options.items():
if not name in self.option_values: if not name in self.option_values:
self.option_values[name] = default self.option_values[name] = default

View File

@@ -43,10 +43,11 @@
# available during the execution of the config file. This way, container types # available during the execution of the config file. This way, container types
# can be initialized and then appended/added to when the config file is parsed. # can be initialized and then appended/added to when the config file is parsed.
from settingsValidators import * from collections import OrderedDict
import util
from observer import ProgressBarObserver, LoggingObserver, JSObserver from .settingsValidators import *
from optimizeimages import pngnq, optipng, pngcrush from .observer import ProgressBarObserver, LoggingObserver, JSObserver
from .optimizeimages import pngnq, optipng, pngcrush
import platform import platform
import sys import sys
@@ -60,7 +61,7 @@ import sys
# objects with their respective validators. # objects with their respective validators.
# config file. # config file.
renders = Setting(required=True, default=util.OrderedDict(), renders = Setting(required=True, default=OrderedDict(),
validator=make_dictValidator(validateStr, make_configDictValidator( validator=make_dictValidator(validateStr, make_configDictValidator(
{ {
"world": Setting(required=True, validator=validateStr, default=None), "world": Setting(required=True, validator=validateStr, default=None),
@@ -99,7 +100,7 @@ renders = Setting(required=True, default=util.OrderedDict(),
))) )))
# The worlds dict, mapping world names to world paths # The worlds dict, mapping world names to world paths
worlds = Setting(required=True, validator=make_dictValidator(validateStr, validateWorldPath), default=util.OrderedDict()) worlds = Setting(required=True, validator=make_dictValidator(validateStr, validateWorldPath), default=OrderedDict())
outputdir = Setting(required=True, validator=validateOutputDir, default=None) outputdir = Setting(required=True, validator=validateOutputDir, default=None)

View File

@@ -2,11 +2,12 @@
import logging import logging
import os import os
import os.path import os.path
from collections import OrderedDict
import rendermodes from . import rendermodes
import util from . import util
from optimizeimages import Optimizer from .optimizeimages import Optimizer
from world import LOWER_LEFT, LOWER_RIGHT, UPPER_LEFT, UPPER_RIGHT from .world import LOWER_LEFT, LOWER_RIGHT, UPPER_LEFT, UPPER_RIGHT
class ValidationException(Exception): class ValidationException(Exception):
@@ -179,8 +180,8 @@ def validateBGColor(color):
def validateOptImg(optimizers): def validateOptImg(optimizers):
if isinstance(optimizers, (int, long)): if isinstance(optimizers, int):
from optimizeimages import pngcrush from .optimizeimages import pngcrush
logging.warning("You're using a deprecated definition of optimizeimg. " logging.warning("You're using a deprecated definition of optimizeimg. "
"We'll do what you say for now, but please fix this as soon as possible.") "We'll do what you say for now, but please fix this as soon as possible.")
optimizers = [pngcrush()] optimizers = [pngcrush()]
@@ -189,7 +190,7 @@ def validateOptImg(optimizers):
"Make sure you specify them like [foo()], with square brackets.") "Make sure you specify them like [foo()], with square brackets.")
if optimizers: if optimizers:
for opt, next_opt in zip(optimizers, optimizers[1:]) + [(optimizers[-1], None)]: for opt, next_opt in list(zip(optimizers, optimizers[1:])) + [(optimizers[-1], None)]:
if not isinstance(opt, Optimizer): if not isinstance(opt, Optimizer):
raise ValidationException("Invalid Optimizer!") raise ValidationException("Invalid Optimizer!")
@@ -272,7 +273,7 @@ def validateCrop(value):
def validateObserver(observer): def validateObserver(observer):
if all(map(lambda m: hasattr(observer, m), ['start', 'add', 'update', 'finish'])): if all([hasattr(observer, m) for m in ['start', 'add', 'update', 'finish']]):
return observer return observer
else: else:
raise ValidationException("%r does not look like an observer." % repr(observer)) raise ValidationException("%r does not look like an observer." % repr(observer))
@@ -316,8 +317,8 @@ def make_dictValidator(keyvalidator, valuevalidator):
""" """
def v(d): def v(d):
newd = util.OrderedDict() newd = OrderedDict()
for key, value in d.iteritems(): for key, value in d.items():
newd[keyvalidator(key)] = valuevalidator(value) newd[keyvalidator(key)] = valuevalidator(value)
return newd return newd
# Put these objects as attributes of the function so they can be accessed # Put these objects as attributes of the function so they can be accessed
@@ -345,7 +346,7 @@ def make_configDictValidator(config, ignore_undefined=False):
""" """
def configDictValidator(d): def configDictValidator(d):
newdict = util.OrderedDict() newdict = OrderedDict()
# values are config keys that the user specified that don't match any # values are config keys that the user specified that don't match any
# valid key # valid key
@@ -353,10 +354,10 @@ def make_configDictValidator(config, ignore_undefined=False):
undefined_key_matches = {} undefined_key_matches = {}
# Go through the keys the user gave us and make sure they're all valid. # Go through the keys the user gave us and make sure they're all valid.
for key in d.iterkeys(): for key in d.keys():
if key not in config: if key not in config:
# Try to find a probable match # Try to find a probable match
match = _get_closest_match(key, config.iterkeys()) match = _get_closest_match(key, iter(config.keys()))
if match and ignore_undefined: if match and ignore_undefined:
# Save this for later. It only matters if this is a typo of # Save this for later. It only matters if this is a typo of
# some required key that wasn't specified. (If all required # some required key that wasn't specified. (If all required
@@ -377,7 +378,7 @@ def make_configDictValidator(config, ignore_undefined=False):
# Iterate through the defined keys in the configuration (`config`), # Iterate through the defined keys in the configuration (`config`),
# checking each one to see if the user specified it (in `d`). Then # checking each one to see if the user specified it (in `d`). Then
# validate it and copy the result to `newdict` # validate it and copy the result to `newdict`
for configkey, configsetting in config.iteritems(): for configkey, configsetting in config.items():
if configkey in d: if configkey in d:
# This key /was/ specified in the user's dict. Make sure it validates. # This key /was/ specified in the user's dict. Make sure it validates.
newdict[configkey] = configsetting.validator(d[configkey]) newdict[configkey] = configsetting.validator(d[configkey])
@@ -414,9 +415,9 @@ def _levenshtein(s1, s2):
l1 = len(s1) l1 = len(s1)
l2 = len(s2) l2 = len(s2)
matrix = [range(l1 + 1)] * (l2 + 1) matrix = [list(range(l1 + 1))] * (l2 + 1)
for zz in range(l2 + 1): for zz in range(l2 + 1):
matrix[zz] = range(zz, zz + l1 + 1) matrix[zz] = list(range(zz, zz + l1 + 1))
for zz in range(0, l2): for zz in range(0, l2):
for sz in range(0, l1): for sz in range(0, l1):
if s1[sz] == s2[zz]: if s1[sz] == s2[zz]:

View File

@@ -40,7 +40,6 @@ PyObject *init_chunk_render(void) {
if (textures) { if (textures) {
Py_RETURN_NONE; Py_RETURN_NONE;
} }
textures = PyImport_ImportModule("overviewer_core.textures"); textures = PyImport_ImportModule("overviewer_core.textures");
/* ensure none of these pointers are NULL */ /* ensure none of these pointers are NULL */
if ((!textures)) { if ((!textures)) {
@@ -50,13 +49,13 @@ PyObject *init_chunk_render(void) {
tmp = PyObject_GetAttrString(textures, "max_blockid"); tmp = PyObject_GetAttrString(textures, "max_blockid");
if (!tmp) if (!tmp)
return NULL; return NULL;
max_blockid = PyInt_AsLong(tmp); max_blockid = PyLong_AsLong(tmp);
Py_DECREF(tmp); Py_DECREF(tmp);
tmp = PyObject_GetAttrString(textures, "max_data"); tmp = PyObject_GetAttrString(textures, "max_data");
if (!tmp) if (!tmp)
return NULL; return NULL;
max_data = PyInt_AsLong(tmp); max_data = PyLong_AsLong(tmp);
Py_DECREF(tmp); Py_DECREF(tmp);
/* assemble the property table */ /* assemble the property table */
@@ -81,7 +80,7 @@ PyObject *init_chunk_render(void) {
block_properties = calloc(max_blockid, sizeof(unsigned char)); block_properties = calloc(max_blockid, sizeof(unsigned char));
for (i = 0; i < max_blockid; i++) { for (i = 0; i < max_blockid; i++) {
PyObject *block = PyInt_FromLong(i); PyObject *block = PyLong_FromLong(i);
if (PySequence_Contains(known_blocks, block)) if (PySequence_Contains(known_blocks, block))
block_properties[i] |= 1 << KNOWN; block_properties[i] |= 1 << KNOWN;
@@ -177,7 +176,7 @@ int load_chunk(RenderState* state, int x, int z, unsigned char required) {
if (!ycoord) if (!ycoord)
continue; continue;
sectiony = PyInt_AsLong(ycoord); sectiony = PyLong_AsLong(ycoord);
if (sectiony >= 0 && sectiony < SECTIONS_PER_CHUNK) if (sectiony >= 0 && sectiony < SECTIONS_PER_CHUNK)
load_chunk_section(dest, sectiony, section); load_chunk_section(dest, sectiony, section);
} }
@@ -487,7 +486,7 @@ generate_pseudo_data(RenderState *state, unsigned short ancilData) {
PyObject *texrot; PyObject *texrot;
int northdir; int northdir;
texrot = PyObject_GetAttrString(state->textures, "rotation"); texrot = PyObject_GetAttrString(state->textures, "rotation");
northdir = PyInt_AsLong(texrot); northdir = PyLong_AsLong(texrot);
/* fix the rotation value for different northdirections */ /* fix the rotation value for different northdirections */
#define FIX_ROT(x) (((x) & ~0x3) | repair_rot[((x) & 0x3) | (northdir << 2)]) #define FIX_ROT(x) (((x) & ~0x3) | repair_rot[((x) & 0x3) | (northdir << 2)])
@@ -617,8 +616,8 @@ chunk_render(PyObject *self, PyObject *args) {
imgsize1_py = PySequence_GetItem(imgsize, 1); imgsize1_py = PySequence_GetItem(imgsize, 1);
Py_DECREF(imgsize); Py_DECREF(imgsize);
imgsize0 = PyInt_AsLong(imgsize0_py); imgsize0 = PyLong_AsLong(imgsize0_py);
imgsize1 = PyInt_AsLong(imgsize1_py); imgsize1 = PyLong_AsLong(imgsize1_py);
Py_DECREF(imgsize0_py); Py_DECREF(imgsize0_py);
Py_DECREF(imgsize1_py); Py_DECREF(imgsize1_py);

View File

@@ -38,12 +38,20 @@ static PyMethodDef COverviewerMethods[] = {
{NULL, NULL, 0, NULL} /* Sentinel */ {NULL, NULL, 0, NULL} /* Sentinel */
}; };
static PyModuleDef COverviewerModule = {
PyModuleDef_HEAD_INIT,
"c_overviewer",
"", // TODO: Add documentation here.
-1,
COverviewerMethods
};
PyMODINIT_FUNC PyMODINIT_FUNC
initc_overviewer(void) PyInit_c_overviewer(void)
{ {
PyObject *mod, *numpy; PyObject *mod, *numpy;
mod = Py_InitModule("c_overviewer", COverviewerMethods); mod = PyModule_Create(&COverviewerModule);
/* for numpy /* for numpy
normally you should use import_array(), but that will break across normally you should use import_array(), but that will break across
@@ -57,8 +65,9 @@ initc_overviewer(void)
if (!init_chunk_render()) { if (!init_chunk_render()) {
PyErr_Print(); PyErr_Print();
exit(1); exit(1);
return; return NULL;
} }
init_endian(); init_endian();
return mod;
} }

View File

@@ -33,7 +33,7 @@
// increment this value if you've made a change to the c extesion // increment this value if you've made a change to the c extesion
// and want to force users to rebuild // and want to force users to rebuild
#define OVERVIEWER_EXTENSION_VERSION 56 #define OVERVIEWER_EXTENSION_VERSION 57
/* Python PIL, and numpy headers */ /* Python PIL, and numpy headers */
#include <Python.h> #include <Python.h>

View File

@@ -234,9 +234,9 @@ base_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObjec
/* look up color! */ /* look up color! */
color = PySequence_GetItem(color_table, tabley * 256 + tablex); color = PySequence_GetItem(color_table, tabley * 256 + tablex);
r = PyInt_AsLong(PyTuple_GET_ITEM(color, 0)); r = PyLong_AsLong(PyTuple_GET_ITEM(color, 0));
g = PyInt_AsLong(PyTuple_GET_ITEM(color, 1)); g = PyLong_AsLong(PyTuple_GET_ITEM(color, 1));
b = PyInt_AsLong(PyTuple_GET_ITEM(color, 2)); b = PyLong_AsLong(PyTuple_GET_ITEM(color, 2));
Py_DECREF(color); Py_DECREF(color);
/* do the after-coloration */ /* do the after-coloration */

View File

@@ -56,9 +56,9 @@ depth_tinting_draw(void *data, RenderState *state, PyObject *src, PyObject *mask
y = (y * 128) / (16 * SECTIONS_PER_CHUNK); y = (y * 128) / (16 * SECTIONS_PER_CHUNK);
/* get the colors and tint and tint */ /* get the colors and tint and tint */
r = PyInt_AsLong(PyList_GetItem(self->depth_colors, 0 + y*3)); r = PyLong_AsLong(PyList_GetItem(self->depth_colors, 0 + y*3));
g = PyInt_AsLong(PyList_GetItem(self->depth_colors, 1 + y*3)); g = PyLong_AsLong(PyList_GetItem(self->depth_colors, 1 + y*3));
b = PyInt_AsLong(PyList_GetItem(self->depth_colors, 2 + y*3)); b = PyLong_AsLong(PyList_GetItem(self->depth_colors, 2 + y*3));
tint_with_mask(state->img, r, g, b, 255, mask, state->imgx, state->imgy, 0, 0); tint_with_mask(state->img, r, g, b, 255, mask, state->imgx, state->imgy, 0, 0);
} }

View File

@@ -52,9 +52,9 @@ hide_start(void *data, RenderState *state, PyObject *support) {
for (i = 0; i < blocks_size; i++) { for (i = 0; i < blocks_size; i++) {
PyObject *block = PyList_GET_ITEM(opt, i); PyObject *block = PyList_GET_ITEM(opt, i);
if (PyInt_Check(block)) { if (PyLong_Check(block)) {
/* format 1: just a block id */ /* format 1: just a block id */
self->rules[i].blockid = PyInt_AsLong(block); self->rules[i].blockid = PyLong_AsLong(block);
self->rules[i].has_data = 0; self->rules[i].has_data = 0;
} else if (PyArg_ParseTuple(block, "Hb", &(self->rules[i].blockid), &(self->rules[i].data))) { } else if (PyArg_ParseTuple(block, "Hb", &(self->rules[i].blockid), &(self->rules[i].data))) {
/* format 2: (blockid, data) */ /* format 2: (blockid, data) */

View File

@@ -45,9 +45,9 @@ calculate_light_color_fancy(void *data,
index = skylight + blocklight * 16; index = skylight + blocklight * 16;
color = PySequence_GetItem(mode->lightcolor, index); color = PySequence_GetItem(mode->lightcolor, index);
*r = PyInt_AsLong(PyTuple_GET_ITEM(color, 0)); *r = PyLong_AsLong(PyTuple_GET_ITEM(color, 0));
*g = PyInt_AsLong(PyTuple_GET_ITEM(color, 1)); *g = PyLong_AsLong(PyTuple_GET_ITEM(color, 1));
*b = PyInt_AsLong(PyTuple_GET_ITEM(color, 2)); *b = PyLong_AsLong(PyTuple_GET_ITEM(color, 2));
Py_DECREF(color); Py_DECREF(color);
} }
@@ -78,9 +78,9 @@ calculate_light_color_fancy_night(void *data,
index = skylight + blocklight * 16; index = skylight + blocklight * 16;
color = PySequence_GetItem(mode->lightcolor, index); color = PySequence_GetItem(mode->lightcolor, index);
*r = PyInt_AsLong(PyTuple_GET_ITEM(color, 0)); *r = PyLong_AsLong(PyTuple_GET_ITEM(color, 0));
*g = PyInt_AsLong(PyTuple_GET_ITEM(color, 1)); *g = PyLong_AsLong(PyTuple_GET_ITEM(color, 1));
*b = PyInt_AsLong(PyTuple_GET_ITEM(color, 2)); *b = PyLong_AsLong(PyTuple_GET_ITEM(color, 2));
Py_DECREF(color); Py_DECREF(color);
} }

View File

@@ -40,7 +40,7 @@ RenderPrimitive *render_primitive_create(PyObject *prim, RenderState *state) {
pyname = PyObject_GetAttrString(prim, "name"); pyname = PyObject_GetAttrString(prim, "name");
if (!pyname) if (!pyname)
return NULL; return NULL;
name = PyString_AsString(pyname); name = PyUnicode_AsUTF8(pyname);
for (i = 0; render_primitives[i] != NULL; i++) { for (i = 0; render_primitives[i] != NULL; i++) {
if (strcmp(render_primitives[i]->name, name) == 0) { if (strcmp(render_primitives[i]->name, name) == 0) {
@@ -204,11 +204,10 @@ int render_mode_parse_option(PyObject *support, const char *name, const char *fo
Py_DECREF(dict); Py_DECREF(dict);
if (!ret) { if (!ret) {
PyObject *errtype, *errvalue, *errtraceback; PyObject *errtype, *errvalue, *errtraceback, *errstring;
const char *errstring;
PyErr_Fetch(&errtype, &errvalue, &errtraceback); PyErr_Fetch(&errtype, &errvalue, &errtraceback);
errstring = PyString_AsString(errvalue); errstring = PyUnicode_AsUTF8String(errvalue);
PyErr_Format(PyExc_TypeError, "rendermode option \"%s\" has incorrect type (%s)", name, errstring); PyErr_Format(PyExc_TypeError, "rendermode option \"%s\" has incorrect type (%s)", name, errstring);

View File

@@ -18,7 +18,7 @@ import imp
import os import os
import os.path import os.path
import zipfile import zipfile
from cStringIO import StringIO from io import BytesIO
import math import math
from random import randint from random import randint
import numpy import numpy
@@ -26,8 +26,32 @@ from PIL import Image, ImageEnhance, ImageOps, ImageDraw
import logging import logging
import functools import functools
import util from . import util
from c_overviewer import alpha_over
# global variables to collate information in @material decorators
blockmap_generators = {}
known_blocks = set()
used_datas = set()
max_blockid = 0
max_data = 0
transparent_blocks = set()
solid_blocks = set()
fluid_blocks = set()
nospawn_blocks = set()
nodata_blocks = set()
# This is here for circular import reasons.
# Please don't ask, I choose to repress these memories.
# ... okay fine I'll tell you.
# Initialising the C extension requires access to the globals above.
# Due to the circular import, this wouldn't work, unless we reload the
# module in the C extension or just move the import below its dependencies.
from .c_overviewer import alpha_over
class TextureException(Exception): class TextureException(Exception):
"To be thrown when a texture is not found." "To be thrown when a texture is not found."
@@ -37,6 +61,7 @@ class TextureException(Exception):
color_map = ["white", "orange", "magenta", "light_blue", "yellow", "lime", "pink", "gray", color_map = ["white", "orange", "magenta", "light_blue", "yellow", "lime", "pink", "gray",
"light_gray", "cyan", "purple", "blue", "brown", "green", "red", "black"] "light_gray", "cyan", "purple", "blue", "brown", "green", "red", "black"]
## ##
## Textures object ## Textures object
## ##
@@ -76,10 +101,11 @@ class Textures(object):
del attributes[attr] del attributes[attr]
except KeyError: except KeyError:
pass pass
attributes['jar'] = None
return attributes return attributes
def __setstate__(self, attrs): def __setstate__(self, attrs):
# regenerate textures, if needed # regenerate textures, if needed
for attr, val in attrs.iteritems(): for attr, val in list(attrs.items()):
setattr(self, attr, val) setattr(self, attr, val)
self.texture_cache = {} self.texture_cache = {}
if self.generated: if self.generated:
@@ -99,7 +125,7 @@ class Textures(object):
global known_blocks, used_datas global known_blocks, used_datas
self.blockmap = [None] * max_blockid * max_data self.blockmap = [None] * max_blockid * max_data
for (blockid, data), texgen in blockmap_generators.iteritems(): for (blockid, data), texgen in list(blockmap_generators.items()):
tex = texgen(self, blockid, data) tex = texgen(self, blockid, data)
self.blockmap[blockid * max_data + data] = self.generate_texture_tuple(tex) self.blockmap[blockid * max_data + data] = self.generate_texture_tuple(tex)
@@ -224,7 +250,7 @@ class Textures(object):
self.jar.getinfo(jarfilename) self.jar.getinfo(jarfilename)
if verbose: logging.info("Found (cached) %s in '%s'", jarfilename, self.jarpath) if verbose: logging.info("Found (cached) %s in '%s'", jarfilename, self.jarpath)
return self.jar.open(jarfilename) return self.jar.open(jarfilename)
except (KeyError, IOError), e: except (KeyError, IOError) as e:
pass pass
# Find an installed minecraft client jar and look in it for the texture # Find an installed minecraft client jar and look in it for the texture
@@ -285,7 +311,7 @@ class Textures(object):
if verbose: logging.info("Found %s in '%s'", jarfilename, jarpath) if verbose: logging.info("Found %s in '%s'", jarfilename, jarpath)
self.jar, self.jarpath = jar, jarpath self.jar, self.jarpath = jar, jarpath
return jar.open(jarfilename) return jar.open(jarfilename)
except (KeyError, IOError), e: except (KeyError, IOError) as e:
pass pass
if verbose: logging.info("Did not find file {0} in jar {1}".format(filename, jarpath)) if verbose: logging.info("Did not find file {0} in jar {1}".format(filename, jarpath))
@@ -334,7 +360,7 @@ class Textures(object):
return self.texture_cache[filename] return self.texture_cache[filename]
fileobj = self.find_file(filename) fileobj = self.find_file(filename)
buffer = StringIO(fileobj.read()) buffer = BytesIO(fileobj.read())
img = Image.open(buffer).convert("RGBA") img = Image.open(buffer).convert("RGBA")
self.texture_cache[filename] = img self.texture_cache[filename] = img
return img return img
@@ -444,8 +470,8 @@ class Textures(object):
textures = [] textures = []
(terrain_width, terrain_height) = terrain.size (terrain_width, terrain_height) = terrain.size
texture_resolution = terrain_width / 16 texture_resolution = terrain_width / 16
for y in xrange(16): for y in range(16):
for x in xrange(16): for x in range(16):
left = x*texture_resolution left = x*texture_resolution
upper = y*texture_resolution upper = y*texture_resolution
right = left+texture_resolution right = left+texture_resolution
@@ -815,19 +841,6 @@ class Textures(object):
## The other big one: @material and associated framework ## The other big one: @material and associated framework
## ##
# global variables to collate information in @material decorators
blockmap_generators = {}
known_blocks = set()
used_datas = set()
max_blockid = 0
max_data = 0
transparent_blocks = set()
solid_blocks = set()
fluid_blocks = set()
nospawn_blocks = set()
nodata_blocks = set()
# the material registration decorator # the material registration decorator
def material(blockid=[], data=[0], **kwargs): def material(blockid=[], data=[0], **kwargs):
@@ -837,11 +850,11 @@ def material(blockid=[], data=[0], **kwargs):
# make sure blockid and data are iterable # make sure blockid and data are iterable
try: try:
iter(blockid) iter(blockid)
except: except Exception:
blockid = [blockid,] blockid = [blockid,]
try: try:
iter(data) iter(data)
except: except Exception:
data = [data,] data = [data,]
def inner_material(func): def inner_material(func):
@@ -924,7 +937,7 @@ def billboard(blockid=[], imagename=None, **kwargs):
## ##
# stone # stone
@material(blockid=1, data=range(7), solid=True) @material(blockid=1, data=list(range(7)), solid=True)
def stone(self, blockid, data): def stone(self, blockid, data):
if data == 0: # regular old-school stone if data == 0: # regular old-school stone
img = self.load_image_texture("assets/minecraft/textures/block/stone.png") img = self.load_image_texture("assets/minecraft/textures/block/stone.png")
@@ -942,7 +955,7 @@ def stone(self, blockid, data):
img = self.load_image_texture("assets/minecraft/textures/block/polished_andesite.png") img = self.load_image_texture("assets/minecraft/textures/block/polished_andesite.png")
return self.build_block(img, img) return self.build_block(img, img)
@material(blockid=2, data=range(11)+[0x10,], solid=True) @material(blockid=2, data=list(range(11))+[0x10,], solid=True)
def grass(self, blockid, data): def grass(self, blockid, data):
# 0x10 bit means SNOW # 0x10 bit means SNOW
side_img = self.load_image_texture("assets/minecraft/textures/block/grass_block_side.png") side_img = self.load_image_texture("assets/minecraft/textures/block/grass_block_side.png")
@@ -954,7 +967,7 @@ def grass(self, blockid, data):
return img return img
# dirt # dirt
@material(blockid=3, data=range(3), solid=True) @material(blockid=3, data=list(range(3)), solid=True)
def dirt_blocks(self, blockid, data): def dirt_blocks(self, blockid, data):
side_img = self.load_image_texture("assets/minecraft/textures/block/dirt.png") side_img = self.load_image_texture("assets/minecraft/textures/block/dirt.png")
if data == 0: # normal if data == 0: # normal
@@ -970,7 +983,7 @@ def dirt_blocks(self, blockid, data):
block(blockid=4, top_image="assets/minecraft/textures/block/cobblestone.png") block(blockid=4, top_image="assets/minecraft/textures/block/cobblestone.png")
# wooden planks # wooden planks
@material(blockid=5, data=range(6), solid=True) @material(blockid=5, data=list(range(6)), solid=True)
def wooden_planks(self, blockid, data): def wooden_planks(self, blockid, data):
if data == 0: # normal if data == 0: # normal
return self.build_block(self.load_image_texture("assets/minecraft/textures/block/oak_planks.png"), self.load_image_texture("assets/minecraft/textures/block/oak_planks.png")) return self.build_block(self.load_image_texture("assets/minecraft/textures/block/oak_planks.png"), self.load_image_texture("assets/minecraft/textures/block/oak_planks.png"))
@@ -985,7 +998,7 @@ def wooden_planks(self, blockid, data):
if data == 5: # dark oak if data == 5: # dark oak
return self.build_block(self.load_image_texture("assets/minecraft/textures/block/dark_oak_planks.png"),self.load_image_texture("assets/minecraft/textures/block/dark_oak_planks.png")) return self.build_block(self.load_image_texture("assets/minecraft/textures/block/dark_oak_planks.png"),self.load_image_texture("assets/minecraft/textures/block/dark_oak_planks.png"))
@material(blockid=6, data=range(16), transparent=True) @material(blockid=6, data=list(range(16)), transparent=True)
def saplings(self, blockid, data): def saplings(self, blockid, data):
# usual saplings # usual saplings
tex = self.load_image_texture("assets/minecraft/textures/block/oak_sapling.png") tex = self.load_image_texture("assets/minecraft/textures/block/oak_sapling.png")
@@ -1007,7 +1020,7 @@ block(blockid=7, top_image="assets/minecraft/textures/block/bedrock.png")
# water, glass, and ice (no inner surfaces) # water, glass, and ice (no inner surfaces)
# uses pseudo-ancildata found in iterate.c # uses pseudo-ancildata found in iterate.c
@material(blockid=[8, 9, 20, 79, 95], data=range(512), fluid=(8, 9), transparent=True, nospawn=True, solid=(79, 20, 95)) @material(blockid=[8, 9, 20, 79, 95], data=list(range(512)), fluid=(8, 9), transparent=True, nospawn=True, solid=(79, 20, 95))
def no_inner_surfaces(self, blockid, data): def no_inner_surfaces(self, blockid, data):
if blockid == 8 or blockid == 9: if blockid == 8 or blockid == 9:
texture = self.load_water() texture = self.load_water()
@@ -1054,13 +1067,13 @@ def no_inner_surfaces(self, blockid, data):
img = self.build_full_block(top,None,None,side3,side4) img = self.build_full_block(top,None,None,side3,side4)
return img return img
@material(blockid=[10, 11], data=range(16), fluid=True, transparent=False, nospawn=True) @material(blockid=[10, 11], data=list(range(16)), fluid=True, transparent=False, nospawn=True)
def lava(self, blockid, data): def lava(self, blockid, data):
lavatex = self.load_lava() lavatex = self.load_lava()
return self.build_block(lavatex, lavatex) return self.build_block(lavatex, lavatex)
# sand # sand
@material(blockid=12, data=range(2), solid=True) @material(blockid=12, data=list(range(2)), solid=True)
def sand_blocks(self, blockid, data): def sand_blocks(self, blockid, data):
if data == 0: # normal if data == 0: # normal
img = self.build_block(self.load_image_texture("assets/minecraft/textures/block/sand.png"), self.load_image_texture("assets/minecraft/textures/block/sand.png")) img = self.build_block(self.load_image_texture("assets/minecraft/textures/block/sand.png"), self.load_image_texture("assets/minecraft/textures/block/sand.png"))
@@ -1077,7 +1090,7 @@ block(blockid=15, top_image="assets/minecraft/textures/block/iron_ore.png")
# coal ore # coal ore
block(blockid=16, top_image="assets/minecraft/textures/block/coal_ore.png") block(blockid=16, top_image="assets/minecraft/textures/block/coal_ore.png")
@material(blockid=[17,162,11306,11307,11308,11309,11310,11311], data=range(12), solid=True) @material(blockid=[17,162,11306,11307,11308,11309,11310,11311], data=list(range(12)), solid=True)
def wood(self, blockid, data): def wood(self, blockid, data):
# extract orientation and wood type frorm data bits # extract orientation and wood type frorm data bits
wood_type = data & 3 wood_type = data & 3
@@ -1191,7 +1204,7 @@ def wood(self, blockid, data):
elif wood_orientation == 8: # north-south orientation elif wood_orientation == 8: # north-south orientation
return self.build_full_block(side, None, None, side.rotate(270), top) return self.build_full_block(side, None, None, side.rotate(270), top)
@material(blockid=[18, 161], data=range(16), transparent=True, solid=True) @material(blockid=[18, 161], data=list(range(16)), transparent=True, solid=True)
def leaves(self, blockid, data): def leaves(self, blockid, data):
# mask out the bits 4 and 8 # mask out the bits 4 and 8
# they are used for player placed and check-for-decay blocks # they are used for player placed and check-for-decay blocks
@@ -1217,7 +1230,7 @@ block(blockid=21, top_image="assets/minecraft/textures/block/lapis_ore.png")
block(blockid=22, top_image="assets/minecraft/textures/block/lapis_block.png") block(blockid=22, top_image="assets/minecraft/textures/block/lapis_block.png")
# dispensers, dropper, furnaces, and burning furnaces # dispensers, dropper, furnaces, and burning furnaces
@material(blockid=[23, 61, 62, 158], data=range(6), solid=True) @material(blockid=[23, 61, 62, 158], data=list(range(6)), solid=True)
def furnaces(self, blockid, data): def furnaces(self, blockid, data):
# first, do the rotation if needed # first, do the rotation if needed
if self.rotation == 1: if self.rotation == 1:
@@ -1266,7 +1279,7 @@ def furnaces(self, blockid, data):
return self.build_full_block(top, None, None, side, side) return self.build_full_block(top, None, None, side, side)
# sandstone # sandstone
@material(blockid=24, data=range(3), solid=True) @material(blockid=24, data=list(range(3)), solid=True)
def sandstone(self, blockid, data): def sandstone(self, blockid, data):
top = self.load_image_texture("assets/minecraft/textures/block/sandstone_top.png") top = self.load_image_texture("assets/minecraft/textures/block/sandstone_top.png")
if data == 0: # normal if data == 0: # normal
@@ -1277,7 +1290,7 @@ def sandstone(self, blockid, data):
return self.build_block(top, self.load_image_texture("assets/minecraft/textures/block/cut_sandstone.png")) return self.build_block(top, self.load_image_texture("assets/minecraft/textures/block/cut_sandstone.png"))
# red sandstone # red sandstone
@material(blockid=179, data=range(3), solid=True) @material(blockid=179, data=list(range(3)), solid=True)
def sandstone(self, blockid, data): def sandstone(self, blockid, data):
top = self.load_image_texture("assets/minecraft/textures/block/red_sandstone_top.png") top = self.load_image_texture("assets/minecraft/textures/block/red_sandstone_top.png")
if data == 0: # normal if data == 0: # normal
@@ -1291,7 +1304,7 @@ def sandstone(self, blockid, data):
# note block # note block
block(blockid=25, top_image="assets/minecraft/textures/block/note_block.png") block(blockid=25, top_image="assets/minecraft/textures/block/note_block.png")
@material(blockid=26, data=range(12), transparent=True, nospawn=True) @material(blockid=26, data=list(range(12)), transparent=True, nospawn=True)
def bed(self, blockid, data): def bed(self, blockid, data):
# first get rotation done # first get rotation done
# Masked to not clobber block head/foot info # Masked to not clobber block head/foot info
@@ -1381,7 +1394,7 @@ def bed(self, blockid, data):
return self.build_full_block(top_face, None, None, left_face, right_face) return self.build_full_block(top_face, None, None, left_face, right_face)
# powered, detector, activator and normal rails # powered, detector, activator and normal rails
@material(blockid=[27, 28, 66, 157], data=range(14), transparent=True) @material(blockid=[27, 28, 66, 157], data=list(range(14)), transparent=True)
def rails(self, blockid, data): def rails(self, blockid, data):
# first, do rotation # first, do rotation
# Masked to not clobber powered rail on/off info # Masked to not clobber powered rail on/off info
@@ -1664,7 +1677,7 @@ def piston_extension(self, blockid, data):
# cobweb # cobweb
sprite(blockid=30, imagename="assets/minecraft/textures/block/cobweb.png", nospawn=True) sprite(blockid=30, imagename="assets/minecraft/textures/block/cobweb.png", nospawn=True)
@material(blockid=31, data=range(3), transparent=True) @material(blockid=31, data=list(range(3)), transparent=True)
def tall_grass(self, blockid, data): def tall_grass(self, blockid, data):
if data == 0: # dead shrub if data == 0: # dead shrub
texture = self.load_image_texture("assets/minecraft/textures/block/dead_bush.png") texture = self.load_image_texture("assets/minecraft/textures/block/dead_bush.png")
@@ -1678,7 +1691,7 @@ def tall_grass(self, blockid, data):
# dead bush # dead bush
billboard(blockid=32, imagename="assets/minecraft/textures/block/dead_bush.png") billboard(blockid=32, imagename="assets/minecraft/textures/block/dead_bush.png")
@material(blockid=35, data=range(16), solid=True) @material(blockid=35, data=list(range(16)), solid=True)
def wool(self, blockid, data): def wool(self, blockid, data):
texture = self.load_image_texture("assets/minecraft/textures/block/%s_wool.png" % color_map[data]) texture = self.load_image_texture("assets/minecraft/textures/block/%s_wool.png" % color_map[data])
@@ -1688,7 +1701,7 @@ def wool(self, blockid, data):
sprite(blockid=37, imagename="assets/minecraft/textures/block/dandelion.png") sprite(blockid=37, imagename="assets/minecraft/textures/block/dandelion.png")
# flowers # flowers
@material(blockid=38, data=range(10), transparent=True) @material(blockid=38, data=list(range(10)), transparent=True)
def flower(self, blockid, data): def flower(self, blockid, data):
flower_map = ["poppy", "blue_orchid", "allium", "azure_bluet", "red_tulip", "orange_tulip", flower_map = ["poppy", "blue_orchid", "allium", "azure_bluet", "red_tulip", "orange_tulip",
"white_tulip", "pink_tulip", "oxeye_daisy", "dandelion"] "white_tulip", "pink_tulip", "oxeye_daisy", "dandelion"]
@@ -1708,7 +1721,7 @@ block(blockid=42, top_image="assets/minecraft/textures/block/iron_block.png")
# double slabs and slabs # double slabs and slabs
# these wooden slabs are unobtainable without cheating, they are still # these wooden slabs are unobtainable without cheating, they are still
# here because lots of pre-1.3 worlds use this blocks # here because lots of pre-1.3 worlds use this blocks
@material(blockid=[43, 44, 181, 182, 204, 205], data=range(16), transparent=(44,182,205), solid=True) @material(blockid=[43, 44, 181, 182, 204, 205], data=list(range(16)), transparent=(44,182,205), solid=True)
def slabs(self, blockid, data): def slabs(self, blockid, data):
if blockid == 44 or blockid == 182: if blockid == 44 or blockid == 182:
texture = data & 7 texture = data & 7
@@ -1847,7 +1860,7 @@ def torches(self, blockid, data):
return img return img
# fire # fire
@material(blockid=51, data=range(16), transparent=True) @material(blockid=51, data=list(range(16)), transparent=True)
def fire(self, blockid, data): def fire(self, blockid, data):
firetextures = self.load_fire() firetextures = self.load_fire()
side1 = self.transform_image_side(firetextures[0]) side1 = self.transform_image_side(firetextures[0])
@@ -1867,7 +1880,7 @@ def fire(self, blockid, data):
block(blockid=52, top_image="assets/minecraft/textures/block/spawner.png", transparent=True) block(blockid=52, top_image="assets/minecraft/textures/block/spawner.png", transparent=True)
# wooden, cobblestone, red brick, stone brick, netherbrick, sandstone, spruce, birch, jungle, quartz, and red sandstone stairs. # wooden, cobblestone, red brick, stone brick, netherbrick, sandstone, spruce, birch, jungle, quartz, and red sandstone stairs.
@material(blockid=[53,67,108,109,114,128,134,135,136,156,163,164,180,203], data=range(128), transparent=True, solid=True, nospawn=True) @material(blockid=[53,67,108,109,114,128,134,135,136,156,163,164,180,203], data=list(range(128)), transparent=True, solid=True, nospawn=True)
def stairs(self, blockid, data): def stairs(self, blockid, data):
# preserve the upside-down bit # preserve the upside-down bit
upside_down = data & 0x4 upside_down = data & 0x4
@@ -1991,7 +2004,7 @@ def stairs(self, blockid, data):
# normal, locked (used in april's fool day), ender and trapped chest # normal, locked (used in april's fool day), ender and trapped chest
# NOTE: locked chest used to be id95 (which is now stained glass) # NOTE: locked chest used to be id95 (which is now stained glass)
@material(blockid=[54,130,146], data=range(30), transparent = True) @material(blockid=[54,130,146], data=list(range(30)), transparent = True)
def chests(self, blockid, data): def chests(self, blockid, data):
# the first 3 bits are the orientation as stored in minecraft, # the first 3 bits are the orientation as stored in minecraft,
# bits 0x8 and 0x10 indicate which half of the double chest is it. # bits 0x8 and 0x10 indicate which half of the double chest is it.
@@ -2188,7 +2201,7 @@ def chests(self, blockid, data):
# redstone wire # redstone wire
# uses pseudo-ancildata found in iterate.c # uses pseudo-ancildata found in iterate.c
@material(blockid=55, data=range(128), transparent=True) @material(blockid=55, data=list(range(128)), transparent=True)
def wire(self, blockid, data): def wire(self, blockid, data):
if data & 0b1000000 == 64: # powered redstone wire if data & 0b1000000 == 64: # powered redstone wire
@@ -2281,7 +2294,7 @@ def crafting_table(self, blockid, data):
return img return img
# crops with 8 data values (like wheat) # crops with 8 data values (like wheat)
@material(blockid=59, data=range(8), transparent=True, nospawn=True) @material(blockid=59, data=list(range(8)), transparent=True, nospawn=True)
def crops8(self, blockid, data): def crops8(self, blockid, data):
raw_crop = self.load_image_texture("assets/minecraft/textures/block/wheat_stage%d.png" % data) raw_crop = self.load_image_texture("assets/minecraft/textures/block/wheat_stage%d.png" % data)
crop1 = self.transform_image_top(raw_crop) crop1 = self.transform_image_top(raw_crop)
@@ -2295,7 +2308,7 @@ def crops8(self, blockid, data):
return img return img
# farmland and grass path (15/16 blocks) # farmland and grass path (15/16 blocks)
@material(blockid=[60,208], data=range(9), solid=True) @material(blockid=[60,208], data=list(range(9)), solid=True)
def farmland(self, blockid, data): def farmland(self, blockid, data):
if blockid == 60: if blockid == 60:
side = self.load_image_texture("assets/minecraft/textures/block/dirt.png") side = self.load_image_texture("assets/minecraft/textures/block/dirt.png")
@@ -2315,7 +2328,7 @@ def farmland(self, blockid, data):
# signposts # signposts
@material(blockid=63, data=range(16), transparent=True) @material(blockid=63, data=list(range(16)), transparent=True)
def signpost(self, blockid, data): def signpost(self, blockid, data):
# first rotations # first rotations
@@ -2369,7 +2382,7 @@ def signpost(self, blockid, data):
# wooden and iron door # wooden and iron door
# uses pseudo-ancildata found in iterate.c # uses pseudo-ancildata found in iterate.c
@material(blockid=[64,71,193,194,195,196,197], data=range(32), transparent=True) @material(blockid=[64,71,193,194,195,196,197], data=list(range(32)), transparent=True)
def door(self, blockid, data): def door(self, blockid, data):
#Masked to not clobber block top/bottom & swung info #Masked to not clobber block top/bottom & swung info
if self.rotation == 1: if self.rotation == 1:
@@ -2618,7 +2631,7 @@ def wall_sign(self, blockid, data): # wall sign
return img return img
# levers # levers
@material(blockid=69, data=range(16), transparent=True) @material(blockid=69, data=list(range(16)), transparent=True)
def levers(self, blockid, data): def levers(self, blockid, data):
if data & 8 == 8: powered = True if data & 8 == 8: powered = True
else: powered = False else: powered = False
@@ -2802,7 +2815,7 @@ def pressure_plate(self, blockid, data):
block(blockid=[73, 74], top_image="assets/minecraft/textures/block/redstone_ore.png") block(blockid=[73, 74], top_image="assets/minecraft/textures/block/redstone_ore.png")
# stone a wood buttons # stone a wood buttons
@material(blockid=(77,143,11326,11327,11328,11329,11330), data=range(16), transparent=True) @material(blockid=(77,143,11326,11327,11328,11329,11330), data=list(range(16)), transparent=True)
def buttons(self, blockid, data): def buttons(self, blockid, data):
# 0x8 is set if the button is pressed mask this info and render # 0x8 is set if the button is pressed mask this info and render
@@ -2899,7 +2912,7 @@ def buttons(self, blockid, data):
return img return img
# snow # snow
@material(blockid=78, data=range(16), transparent=True, solid=True) @material(blockid=78, data=list(range(16)), transparent=True, solid=True)
def snow(self, blockid, data): def snow(self, blockid, data):
# still not rendered correctly: data other than 0 # still not rendered correctly: data other than 0
@@ -2926,7 +2939,7 @@ def snow(self, blockid, data):
block(blockid=80, top_image="assets/minecraft/textures/block/snow.png") block(blockid=80, top_image="assets/minecraft/textures/block/snow.png")
# cactus # cactus
@material(blockid=81, data=range(15), transparent=True, solid=True, nospawn=True) @material(blockid=81, data=list(range(15)), transparent=True, solid=True, nospawn=True)
def cactus(self, blockid, data): def cactus(self, blockid, data):
top = self.load_image_texture("assets/minecraft/textures/block/cactus_top.png") top = self.load_image_texture("assets/minecraft/textures/block/cactus_top.png")
side = self.load_image_texture("assets/minecraft/textures/block/cactus_side.png") side = self.load_image_texture("assets/minecraft/textures/block/cactus_side.png")
@@ -2954,19 +2967,19 @@ def cactus(self, blockid, data):
block(blockid=82, top_image="assets/minecraft/textures/block/clay.png") block(blockid=82, top_image="assets/minecraft/textures/block/clay.png")
# sugar cane # sugar cane
@material(blockid=83, data=range(16), transparent=True) @material(blockid=83, data=list(range(16)), transparent=True)
def sugar_cane(self, blockid, data): def sugar_cane(self, blockid, data):
tex = self.load_image_texture("assets/minecraft/textures/block/sugar_cane.png") tex = self.load_image_texture("assets/minecraft/textures/block/sugar_cane.png")
return self.build_sprite(tex) return self.build_sprite(tex)
# jukebox # jukebox
@material(blockid=84, data=range(16), solid=True) @material(blockid=84, data=list(range(16)), solid=True)
def jukebox(self, blockid, data): def jukebox(self, blockid, data):
return self.build_block(self.load_image_texture("assets/minecraft/textures/block/jukebox_top.png"), self.load_image_texture("assets/minecraft/textures/block/note_block.png")) return self.build_block(self.load_image_texture("assets/minecraft/textures/block/jukebox_top.png"), self.load_image_texture("assets/minecraft/textures/block/note_block.png"))
# nether and normal fences # nether and normal fences
# uses pseudo-ancildata found in iterate.c # uses pseudo-ancildata found in iterate.c
@material(blockid=[85, 188, 189, 190, 191, 192, 113], data=range(16), transparent=True, nospawn=True) @material(blockid=[85, 188, 189, 190, 191, 192, 113], data=list(range(16)), transparent=True, nospawn=True)
def fence(self, blockid, data): def fence(self, blockid, data):
# no need for rotations, it uses pseudo data. # no need for rotations, it uses pseudo data.
# create needed images for Big stick fence # create needed images for Big stick fence
@@ -3082,7 +3095,7 @@ def fence(self, blockid, data):
return img return img
# pumpkin # pumpkin
@material(blockid=[86, 91,11300], data=range(4), solid=True) @material(blockid=[86, 91,11300], data=list(range(4)), solid=True)
def pumpkin(self, blockid, data): # pumpkins, jack-o-lantern def pumpkin(self, blockid, data): # pumpkins, jack-o-lantern
# rotation # rotation
if self.rotation == 1: if self.rotation == 1:
@@ -3149,7 +3162,7 @@ def portal(self, blockid, data):
return img return img
# cake! # cake!
@material(blockid=92, data=range(6), transparent=True, nospawn=True) @material(blockid=92, data=list(range(6)), transparent=True, nospawn=True)
def cake(self, blockid, data): def cake(self, blockid, data):
# cake textures # cake textures
@@ -3273,7 +3286,7 @@ def cake(self, blockid, data):
return img return img
# redstone repeaters ON and OFF # redstone repeaters ON and OFF
@material(blockid=[93,94], data=range(16), transparent=True, nospawn=True) @material(blockid=[93,94], data=list(range(16)), transparent=True, nospawn=True)
def repeater(self, blockid, data): def repeater(self, blockid, data):
# rotation # rotation
# Masked to not clobber delay info # Masked to not clobber delay info
@@ -3418,7 +3431,7 @@ def repeater(self, blockid, data):
return img return img
# redstone comparator (149 is inactive, 150 is active) # redstone comparator (149 is inactive, 150 is active)
@material(blockid=[149,150], data=range(16), transparent=True, nospawn=True) @material(blockid=[149,150], data=list(range(16)), transparent=True, nospawn=True)
def comparator(self, blockid, data): def comparator(self, blockid, data):
# rotation # rotation
@@ -3483,7 +3496,7 @@ def comparator(self, blockid, data):
# trapdoor # trapdoor
# the trapdoor is looks like a sprite when opened, that's not good # the trapdoor is looks like a sprite when opened, that's not good
@material(blockid=[96,167,11332,11333,11334,11335,11336], data=range(16), transparent=True, nospawn=True) @material(blockid=[96,167,11332,11333,11334,11335,11336], data=list(range(16)), transparent=True, nospawn=True)
def trapdoor(self, blockid, data): def trapdoor(self, blockid, data):
# rotation # rotation
@@ -3539,7 +3552,7 @@ def trapdoor(self, blockid, data):
return img return img
# block with hidden silverfish (stone, cobblestone and stone brick) # block with hidden silverfish (stone, cobblestone and stone brick)
@material(blockid=97, data=range(3), solid=True) @material(blockid=97, data=list(range(3)), solid=True)
def hidden_silverfish(self, blockid, data): def hidden_silverfish(self, blockid, data):
if data == 0: # stone if data == 0: # stone
t = self.load_image_texture("assets/minecraft/textures/block/stone.png") t = self.load_image_texture("assets/minecraft/textures/block/stone.png")
@@ -3553,7 +3566,7 @@ def hidden_silverfish(self, blockid, data):
return img return img
# stone brick # stone brick
@material(blockid=98, data=range(4), solid=True) @material(blockid=98, data=list(range(4)), solid=True)
def stone_brick(self, blockid, data): def stone_brick(self, blockid, data):
if data == 0: # normal if data == 0: # normal
t = self.load_image_texture("assets/minecraft/textures/block/stone_bricks.png") t = self.load_image_texture("assets/minecraft/textures/block/stone_bricks.png")
@@ -3569,7 +3582,7 @@ def stone_brick(self, blockid, data):
return img return img
# huge brown and red mushroom # huge brown and red mushroom
@material(blockid=[99,100], data= range(11) + [14,15], solid=True) @material(blockid=[99,100], data= list(range(11)) + [14,15], solid=True)
def huge_mushroom(self, blockid, data): def huge_mushroom(self, blockid, data):
# rotation # rotation
if self.rotation == 1: if self.rotation == 1:
@@ -3653,7 +3666,7 @@ def huge_mushroom(self, blockid, data):
# iron bars and glass pane # iron bars and glass pane
# TODO glass pane is not a sprite, it has a texture for the side, # TODO glass pane is not a sprite, it has a texture for the side,
# at the moment is not used # at the moment is not used
@material(blockid=[101,102, 160], data=range(256), transparent=True, nospawn=True) @material(blockid=[101,102, 160], data=list(range(256)), transparent=True, nospawn=True)
def panes(self, blockid, data): def panes(self, blockid, data):
# no rotation, uses pseudo data # no rotation, uses pseudo data
if blockid == 101: if blockid == 101:
@@ -3705,7 +3718,7 @@ block(blockid=103, top_image="assets/minecraft/textures/block/melon_top.png", si
# TODO To render it as in game needs from pseudo data and ancil data: # TODO To render it as in game needs from pseudo data and ancil data:
# once fully grown the stem bends to the melon/pumpkin block, # once fully grown the stem bends to the melon/pumpkin block,
# at the moment only render the growing stem # at the moment only render the growing stem
@material(blockid=[104,105], data=range(8), transparent=True) @material(blockid=[104,105], data=list(range(8)), transparent=True)
def stem(self, blockid, data): def stem(self, blockid, data):
# the ancildata value indicates how much of the texture # the ancildata value indicates how much of the texture
# is shown. # is shown.
@@ -3725,7 +3738,7 @@ def stem(self, blockid, data):
# vines # vines
@material(blockid=106, data=range(16), transparent=True) @material(blockid=106, data=list(range(16)), transparent=True)
def vines(self, blockid, data): def vines(self, blockid, data):
# rotation # rotation
# vines data is bit coded. decode it first. # vines data is bit coded. decode it first.
@@ -3766,7 +3779,7 @@ def vines(self, blockid, data):
return img return img
# fence gates # fence gates
@material(blockid=[107, 183, 184, 185, 186, 187], data=range(8), transparent=True, nospawn=True) @material(blockid=[107, 183, 184, 185, 186, 187], data=list(range(8)), transparent=True, nospawn=True)
def fence_gate(self, blockid, data): def fence_gate(self, blockid, data):
# rotation # rotation
@@ -3870,7 +3883,7 @@ block(blockid=110, top_image="assets/minecraft/textures/block/mycelium_top.png",
# At the moment of writing this lilypads has no ancil data and their # At the moment of writing this lilypads has no ancil data and their
# orientation depends on their position on the map. So it uses pseudo # orientation depends on their position on the map. So it uses pseudo
# ancildata. # ancildata.
@material(blockid=111, data=range(4), transparent=True) @material(blockid=111, data=list(range(4)), transparent=True)
def lilypad(self, blockid, data): def lilypad(self, blockid, data):
t = self.load_image_texture("assets/minecraft/textures/block/lily_pad.png").copy() t = self.load_image_texture("assets/minecraft/textures/block/lily_pad.png").copy()
if data == 0: if data == 0:
@@ -3888,7 +3901,7 @@ def lilypad(self, blockid, data):
block(blockid=112, top_image="assets/minecraft/textures/block/nether_bricks.png") block(blockid=112, top_image="assets/minecraft/textures/block/nether_bricks.png")
# nether wart # nether wart
@material(blockid=115, data=range(4), transparent=True) @material(blockid=115, data=list(range(4)), transparent=True)
def nether_wart(self, blockid, data): def nether_wart(self, blockid, data):
if data == 0: # just come up if data == 0: # just come up
t = self.load_image_texture("assets/minecraft/textures/block/nether_wart_stage0.png") t = self.load_image_texture("assets/minecraft/textures/block/nether_wart_stage0.png")
@@ -3915,7 +3928,7 @@ def enchantment_table(self, blockid, data):
# brewing stand # brewing stand
# TODO this is a place holder, is a 2d image pasted # TODO this is a place holder, is a 2d image pasted
@material(blockid=117, data=range(5), transparent=True) @material(blockid=117, data=list(range(5)), transparent=True)
def brewing_stand(self, blockid, data): def brewing_stand(self, blockid, data):
base = self.load_image_texture("assets/minecraft/textures/block/brewing_stand_base.png") base = self.load_image_texture("assets/minecraft/textures/block/brewing_stand_base.png")
img = self.build_full_block(None, None, None, None, None, base) img = self.build_full_block(None, None, None, None, None, base)
@@ -3925,7 +3938,7 @@ def brewing_stand(self, blockid, data):
return img return img
# cauldron # cauldron
@material(blockid=118, data=range(4), transparent=True) @material(blockid=118, data=list(range(4)), transparent=True)
def cauldron(self, blockid, data): def cauldron(self, blockid, data):
side = self.load_image_texture("assets/minecraft/textures/block/cauldron_side.png") side = self.load_image_texture("assets/minecraft/textures/block/cauldron_side.png")
top = self.load_image_texture("assets/minecraft/textures/block/cauldron_top.png") top = self.load_image_texture("assets/minecraft/textures/block/cauldron_top.png")
@@ -3971,7 +3984,7 @@ def end_portal(self, blockid, data):
return img return img
# end portal frame (data range 8 to get all orientations of filled) # end portal frame (data range 8 to get all orientations of filled)
@material(blockid=120, data=range(8), transparent=True) @material(blockid=120, data=list(range(8)), transparent=True)
def end_portal_frame(self, blockid, data): def end_portal_frame(self, blockid, data):
# The bottom 2 bits are oritation info but seems there is no # The bottom 2 bits are oritation info but seems there is no
# graphical difference between orientations # graphical difference between orientations
@@ -4046,7 +4059,7 @@ def daylight_sensor(self, blockid, data):
# wooden double and normal slabs # wooden double and normal slabs
# these are the new wooden slabs, blockids 43 44 still have wooden # these are the new wooden slabs, blockids 43 44 still have wooden
# slabs, but those are unobtainable without cheating # slabs, but those are unobtainable without cheating
@material(blockid=[125, 126], data=range(16), transparent=(44,), solid=True) @material(blockid=[125, 126], data=list(range(16)), transparent=(44,), solid=True)
def wooden_slabs(self, blockid, data): def wooden_slabs(self, blockid, data):
texture = data & 7 texture = data & 7
if texture== 0: # oak if texture== 0: # oak
@@ -4076,7 +4089,7 @@ block(blockid=129, top_image="assets/minecraft/textures/block/emerald_ore.png")
block(blockid=133, top_image="assets/minecraft/textures/block/emerald_block.png") block(blockid=133, top_image="assets/minecraft/textures/block/emerald_block.png")
# cocoa plant # cocoa plant
@material(blockid=127, data=range(12), transparent=True) @material(blockid=127, data=list(range(12)), transparent=True)
def cocoa_plant(self, blockid, data): def cocoa_plant(self, blockid, data):
orientation = data & 3 orientation = data & 3
# rotation # rotation
@@ -4198,7 +4211,7 @@ def beacon(self, blockid, data):
# cobblestone and mossy cobblestone walls, chorus plants # cobblestone and mossy cobblestone walls, chorus plants
# one additional bit of data value added for mossy and cobblestone # one additional bit of data value added for mossy and cobblestone
@material(blockid=[139, 199], data=range(32), transparent=True, nospawn=True) @material(blockid=[139, 199], data=list(range(32)), transparent=True, nospawn=True)
def cobblestone_wall(self, blockid, data): def cobblestone_wall(self, blockid, data):
# chorus plants # chorus plants
if blockid == 199: if blockid == 199:
@@ -4329,7 +4342,7 @@ def cobblestone_wall(self, blockid, data):
return img return img
# carrots, potatoes # carrots, potatoes
@material(blockid=[141,142], data=range(8), transparent=True, nospawn=True) @material(blockid=[141,142], data=list(range(8)), transparent=True, nospawn=True)
def crops4(self, blockid, data): def crops4(self, blockid, data):
# carrots and potatoes have 8 data, but only 4 visual stages # carrots and potatoes have 8 data, but only 4 visual stages
stage = {0:0, stage = {0:0,
@@ -4355,7 +4368,7 @@ def crops4(self, blockid, data):
return img return img
# anvils # anvils
@material(blockid=145, data=range(12), transparent=True) @material(blockid=145, data=list(range(12)), transparent=True)
def anvil(self, blockid, data): def anvil(self, blockid, data):
# anvils only have two orientations, invert it for rotations 1 and 3 # anvils only have two orientations, invert it for rotations 1 and 3
@@ -4447,7 +4460,7 @@ block(blockid=152, top_image="assets/minecraft/textures/block/redstone_block.png
block(blockid=153, top_image="assets/minecraft/textures/block/nether_quartz_ore.png") block(blockid=153, top_image="assets/minecraft/textures/block/nether_quartz_ore.png")
# block of quartz # block of quartz
@material(blockid=155, data=range(5), solid=True) @material(blockid=155, data=list(range(5)), solid=True)
def quartz_block(self, blockid, data): def quartz_block(self, blockid, data):
if data in (0,1): # normal and chiseled quartz block if data in (0,1): # normal and chiseled quartz block
@@ -4475,7 +4488,7 @@ def quartz_block(self, blockid, data):
return self.build_full_block(side.rotate(90), None, None, top, side.rotate(90)) return self.build_full_block(side.rotate(90), None, None, top, side.rotate(90))
# hopper # hopper
@material(blockid=154, data=range(4), transparent=True) @material(blockid=154, data=list(range(4)), transparent=True)
def hopper(self, blockid, data): def hopper(self, blockid, data):
#build the top #build the top
side = self.load_image_texture("assets/minecraft/textures/block/hopper_outside.png") side = self.load_image_texture("assets/minecraft/textures/block/hopper_outside.png")
@@ -4502,7 +4515,7 @@ def hopper(self, blockid, data):
block(blockid=165, top_image="assets/minecraft/textures/block/slime_block.png") block(blockid=165, top_image="assets/minecraft/textures/block/slime_block.png")
# prismarine block # prismarine block
@material(blockid=168, data=range(3), solid=True) @material(blockid=168, data=list(range(3)), solid=True)
def prismarine_block(self, blockid, data): def prismarine_block(self, blockid, data):
if data == 0: # prismarine if data == 0: # prismarine
@@ -4520,7 +4533,7 @@ def prismarine_block(self, blockid, data):
block(blockid=169, top_image="assets/minecraft/textures/block/sea_lantern.png") block(blockid=169, top_image="assets/minecraft/textures/block/sea_lantern.png")
# hay block # hay block
@material(blockid=170, data=range(9), solid=True) @material(blockid=170, data=list(range(9)), solid=True)
def hayblock(self, blockid, data): def hayblock(self, blockid, data):
top = self.load_image_texture("assets/minecraft/textures/block/hay_block_top.png") top = self.load_image_texture("assets/minecraft/textures/block/hay_block_top.png")
side = self.load_image_texture("assets/minecraft/textures/block/hay_block_side.png") side = self.load_image_texture("assets/minecraft/textures/block/hay_block_side.png")
@@ -4542,7 +4555,7 @@ def hayblock(self, blockid, data):
# carpet - wool block that's small? # carpet - wool block that's small?
@material(blockid=171, data=range(16), transparent=True) @material(blockid=171, data=list(range(16)), transparent=True)
def carpet(self, blockid, data): def carpet(self, blockid, data):
texture = self.load_image_texture("assets/minecraft/textures/block/%s_wool.png" % color_map[data]) texture = self.load_image_texture("assets/minecraft/textures/block/%s_wool.png" % color_map[data])
@@ -4552,7 +4565,7 @@ def carpet(self, blockid, data):
block(blockid=172, top_image="assets/minecraft/textures/block/terracotta.png") block(blockid=172, top_image="assets/minecraft/textures/block/terracotta.png")
#stained hardened clay #stained hardened clay
@material(blockid=159, data=range(16), solid=True) @material(blockid=159, data=list(range(16)), solid=True)
def stained_clay(self, blockid, data): def stained_clay(self, blockid, data):
texture = self.load_image_texture("assets/minecraft/textures/block/%s_terracotta.png" % color_map[data]) texture = self.load_image_texture("assets/minecraft/textures/block/%s_terracotta.png" % color_map[data])
@@ -4586,7 +4599,7 @@ block(blockid=11323, top_image="assets/minecraft/textures/block/dead_fire_coral_
block(blockid=11324, top_image="assets/minecraft/textures/block/dead_horn_coral_block.png") block(blockid=11324, top_image="assets/minecraft/textures/block/dead_horn_coral_block.png")
block(blockid=11325, top_image="assets/minecraft/textures/block/dead_tube_coral_block.png") block(blockid=11325, top_image="assets/minecraft/textures/block/dead_tube_coral_block.png")
@material(blockid=175, data=range(16), transparent=True) @material(blockid=175, data=list(range(16)), transparent=True)
def flower(self, blockid, data): def flower(self, blockid, data):
double_plant_map = ["sunflower", "lilac", "tall_grass", "large_fern", "rose_bush", "peony", "peony", "peony"] double_plant_map = ["sunflower", "lilac", "tall_grass", "large_fern", "rose_bush", "peony", "peony", "peony"]
plant = double_plant_map[data & 0x7] plant = double_plant_map[data & 0x7]
@@ -4608,7 +4621,7 @@ def flower(self, blockid, data):
return img return img
# chorus flower # chorus flower
@material(blockid=200, data=range(6), solid=True) @material(blockid=200, data=list(range(6)), solid=True)
def chorus_flower(self, blockid, data): def chorus_flower(self, blockid, data):
# aged 5, dead # aged 5, dead
if data == 5: if data == 5:
@@ -4622,7 +4635,7 @@ def chorus_flower(self, blockid, data):
block(blockid=201, top_image="assets/minecraft/textures/block/purpur_block.png") block(blockid=201, top_image="assets/minecraft/textures/block/purpur_block.png")
# purpur pilar # purpur pilar
@material(blockid=202, data=range(12) , solid=True) @material(blockid=202, data=list(range(12)) , solid=True)
def purpur_pillar(self, blockid, data): def purpur_pillar(self, blockid, data):
pillar_orientation = data & 12 pillar_orientation = data & 12
top=self.load_image_texture("assets/minecraft/textures/block/purpur_pillar_top.png") top=self.load_image_texture("assets/minecraft/textures/block/purpur_pillar_top.png")
@@ -4639,7 +4652,7 @@ def purpur_pillar(self, blockid, data):
block(blockid=206, top_image="assets/minecraft/textures/block/end_stone_bricks.png") block(blockid=206, top_image="assets/minecraft/textures/block/end_stone_bricks.png")
# frosted ice # frosted ice
@material(blockid=212, data=range(4), solid=True) @material(blockid=212, data=list(range(4)), solid=True)
def frosted_ice(self, blockid, data): def frosted_ice(self, blockid, data):
img = self.load_image_texture("assets/minecraft/textures/block/frosted_ice_%d.png" % data) img = self.load_image_texture("assets/minecraft/textures/block/frosted_ice_%d.png" % data)
return self.build_block(img, img) return self.build_block(img, img)
@@ -4653,7 +4666,7 @@ block(blockid=214, top_image="assets/minecraft/textures/block/nether_wart_block.
# red nether brick # red nether brick
block(blockid=215, top_image="assets/minecraft/textures/block/red_nether_bricks.png") block(blockid=215, top_image="assets/minecraft/textures/block/red_nether_bricks.png")
@material(blockid=216, data=range(12), solid=True) @material(blockid=216, data=list(range(12)), solid=True)
def boneblock(self, blockid, data): def boneblock(self, blockid, data):
# extract orientation # extract orientation
boneblock_orientation = data & 12 boneblock_orientation = data & 12
@@ -4676,7 +4689,7 @@ def boneblock(self, blockid, data):
return self.build_full_block(side, None, None, side.rotate(270), top) return self.build_full_block(side, None, None, side.rotate(270), top)
# observer # observer
@material(blockid=218, data=range(6), solid=True, nospawn=True) @material(blockid=218, data=list(range(6)), solid=True, nospawn=True)
def observer(self, blockid, data): def observer(self, blockid, data):
# first, do the rotation if needed # first, do the rotation if needed
if self.rotation == 1: if self.rotation == 1:
@@ -4718,7 +4731,7 @@ def observer(self, blockid, data):
return img return img
# shulker box # shulker box
@material(blockid=range(219,235), data=range(6), solid=True, nospawn=True) @material(blockid=list(range(219,235)), data=list(range(6)), solid=True, nospawn=True)
def shulker_box(self, blockid, data): def shulker_box(self, blockid, data):
# first, do the rotation if needed # first, do the rotation if needed
data = data & 7 data = data & 7
@@ -4741,7 +4754,7 @@ def shulker_box(self, blockid, data):
color = color_map[blockid - 219] color = color_map[blockid - 219]
shulker_t = self.load_image_texture("assets/minecraft/textures/entity/shulker/shulker_%s.png" % color).copy() shulker_t = self.load_image_texture("assets/minecraft/textures/entity/shulker/shulker_%s.png" % color).copy()
w,h = shulker_t.size w,h = shulker_t.size
res = w / 4 res = w // 4
# Cut out the parts of the shulker texture we need for the box # Cut out the parts of the shulker texture we need for the box
top = shulker_t.crop((res, 0, res * 2, res)) top = shulker_t.crop((res, 0, res * 2, res))
bottom = shulker_t.crop((res * 2, int(res * 1.75), res * 3, int(res * 2.75))) bottom = shulker_t.crop((res * 2, int(res * 1.75), res * 3, int(res * 2.75)))
@@ -4749,7 +4762,7 @@ def shulker_box(self, blockid, data):
side_bottom = shulker_t.crop((0, int(res * 2.75), res, int(res * 3.25))) side_bottom = shulker_t.crop((0, int(res * 2.75), res, int(res * 3.25)))
side = Image.new('RGBA', (res, res)) side = Image.new('RGBA', (res, res))
side.paste(side_top, (0, 0), side_top) side.paste(side_top, (0, 0), side_top)
side.paste(side_bottom, (0,res/2), side_bottom) side.paste(side_bottom, (0, res // 2), side_bottom)
if data == 0: # down if data == 0: # down
side = side.rotate(180) side = side.rotate(180)
@@ -4768,7 +4781,7 @@ def shulker_box(self, blockid, data):
return img return img
# structure block # structure block
@material(blockid=255, data=range(4), solid=True) @material(blockid=255, data=list(range(4)), solid=True)
def structure_block(self, blockid, data): def structure_block(self, blockid, data):
if data == 0: if data == 0:
img = self.load_image_texture("assets/minecraft/textures/block/structure_block_save.png") img = self.load_image_texture("assets/minecraft/textures/block/structure_block_save.png")
@@ -4781,7 +4794,7 @@ def structure_block(self, blockid, data):
return self.build_block(img, img) return self.build_block(img, img)
# beetroots # beetroots
@material(blockid=207, data=range(4), transparent=True, nospawn=True) @material(blockid=207, data=list(range(4)), transparent=True, nospawn=True)
def crops(self, blockid, data): def crops(self, blockid, data):
raw_crop = self.load_image_texture("assets/minecraft/textures/block/beetroots_stage%d.png" % data) raw_crop = self.load_image_texture("assets/minecraft/textures/block/beetroots_stage%d.png" % data)
crop1 = self.transform_image_top(raw_crop) crop1 = self.transform_image_top(raw_crop)
@@ -4795,19 +4808,19 @@ def crops(self, blockid, data):
return img return img
# Concrete # Concrete
@material(blockid=251, data=range(16), solid=True) @material(blockid=251, data=list(range(16)), solid=True)
def concrete(self, blockid, data): def concrete(self, blockid, data):
texture = self.load_image_texture("assets/minecraft/textures/block/%s_concrete.png" % color_map[data]) texture = self.load_image_texture("assets/minecraft/textures/block/%s_concrete.png" % color_map[data])
return self.build_block(texture, texture) return self.build_block(texture, texture)
# Concrete Powder # Concrete Powder
@material(blockid=252, data=range(16), solid=True) @material(blockid=252, data=list(range(16)), solid=True)
def concrete(self, blockid, data): def concrete(self, blockid, data):
texture = self.load_image_texture("assets/minecraft/textures/block/%s_concrete_powder.png" % color_map[data]) texture = self.load_image_texture("assets/minecraft/textures/block/%s_concrete_powder.png" % color_map[data])
return self.build_block(texture, texture) return self.build_block(texture, texture)
# Glazed Terracotta # Glazed Terracotta
@material(blockid=range(235,251), data=range(8), solid=True) @material(blockid=list(range(235,251)), data=list(range(8)), solid=True)
def glazed_terracotta(self, blockid, data): def glazed_terracotta(self, blockid, data):
texture = self.load_image_texture("assets/minecraft/textures/block/%s_glazed_terracotta.png" % color_map[blockid - 235]) texture = self.load_image_texture("assets/minecraft/textures/block/%s_glazed_terracotta.png" % color_map[blockid - 235])
glazed_terracotta_orientation = data & 3 glazed_terracotta_orientation = data & 3

View File

@@ -26,13 +26,13 @@ import stat
import sys import sys
import time import time
from collections import namedtuple from collections import namedtuple
from itertools import chain, izip, product from itertools import chain, product
from PIL import Image from PIL import Image
import c_overviewer from . import c_overviewer
import rendermodes from . import rendermodes
from c_overviewer import resize_half from .c_overviewer import resize_half
from . import nbt, world from . import nbt, world
from .files import FileReplacer, get_fs_caps from .files import FileReplacer, get_fs_caps
@@ -95,7 +95,7 @@ do_work(workobj)
# small but useful # small but useful
def iterate_base4(d): def iterate_base4(d):
"""Iterates over a base 4 number with d digits""" """Iterates over a base 4 number with d digits"""
return product(xrange(4), repeat=d) return product(range(4), repeat=d)
# A named tuple class storing the row and column bounds for the to-be-rendered # A named tuple class storing the row and column bounds for the to-be-rendered
@@ -646,7 +646,7 @@ class TileSet(object):
bounds = self._find_chunk_range() bounds = self._find_chunk_range()
# Calculate the depth of the tree # Calculate the depth of the tree
for p in xrange(2, 33): # max 32 for p in range(2, 33): # max 32
# Will 2^p tiles wide and high suffice? # Will 2^p tiles wide and high suffice?
# X has twice as many chunks as tiles, then halved since this is a # X has twice as many chunks as tiles, then halved since this is a
@@ -686,13 +686,13 @@ class TileSet(object):
if self.treedepth > curdepth: if self.treedepth > curdepth:
logging.warning("Your map seems to have expanded beyond its previous bounds.") logging.warning("Your map seems to have expanded beyond its previous bounds.")
logging.warning("Doing some tile re-arrangements... just a sec...") logging.warning("Doing some tile re-arrangements... just a sec...")
for _ in xrange(self.treedepth - curdepth): for _ in range(self.treedepth - curdepth):
self._increase_depth() self._increase_depth()
elif self.treedepth < curdepth: elif self.treedepth < curdepth:
logging.warning( logging.warning(
"Your map seems to have shrunk. Did you delete some " "Your map seems to have shrunk. Did you delete some "
"chunks? No problem. Re-arranging tiles, just a sec...") "chunks? No problem. Re-arranging tiles, just a sec...")
for _ in xrange(curdepth - self.treedepth): for _ in range(curdepth - self.treedepth):
self._decrease_depth() self._decrease_depth()
logging.info( logging.info(
"There, done. I'm switching to --check-tiles mode for " "There, done. I'm switching to --check-tiles mode for "
@@ -798,12 +798,12 @@ class TileSet(object):
os.rename(getpath("new3"), getpath("3")) os.rename(getpath("new3"), getpath("3"))
# Delete the files in the top directory to make sure they get re-created. # Delete the files in the top directory to make sure they get re-created.
files = [str(num) + "." + self.imgextension for num in xrange(4)] + \ files = [str(num) + "." + self.imgextension for num in range(4)] + \
["base." + self.imgextension] ["base." + self.imgextension]
for f in files: for f in files:
try: try:
os.unlink(getpath(f)) os.unlink(getpath(f))
except OSError, e: except OSError as e:
# Ignore file doesn't exist errors # Ignore file doesn't exist errors
if e.errno != errno.ENOENT: if e.errno != errno.ENOENT:
raise raise
@@ -964,7 +964,7 @@ class TileSet(object):
if not quadPath_filtered: if not quadPath_filtered:
try: try:
os.unlink(imgpath) os.unlink(imgpath)
except OSError, e: except OSError as e:
# Ignore errors if it's "file doesn't exist" # Ignore errors if it's "file doesn't exist"
if e.errno != errno.ENOENT: if e.errno != errno.ENOENT:
raise raise
@@ -988,14 +988,14 @@ class TileSet(object):
quad = Image.new("RGBA", (192, 192), self.options['bgcolor']) quad = Image.new("RGBA", (192, 192), self.options['bgcolor'])
resize_half(quad, src) resize_half(quad, src)
img.paste(quad, path[0]) img.paste(quad, path[0])
except Exception, e: except Exception as e:
logging.warning("Couldn't open %s. It may be corrupt. Error was '%s'.", path[1], e) logging.warning("Couldn't open %s. It may be corrupt. Error was '%s'.", path[1], e)
logging.warning( logging.warning(
"I'm going to try and delete it. You will need to run " "I'm going to try and delete it. You will need to run "
"the render again and with --check-tiles.") "the render again and with --check-tiles.")
try: try:
os.unlink(path[1]) os.unlink(path[1])
except Exception, e: except Exception as e:
logging.error( logging.error(
"While attempting to delete corrupt image %s, an error was encountered. " "While attempting to delete corrupt image %s, an error was encountered. "
"You will need to delete it yourself. Error was '%s'", path[1], e) "You will need to delete it yourself. Error was '%s'", path[1], e)
@@ -1016,7 +1016,7 @@ class TileSet(object):
try: try:
os.utime(tmppath, (max_mtime, max_mtime)) os.utime(tmppath, (max_mtime, max_mtime))
except OSError, e: except OSError as e:
# Ignore errno ENOENT: file does not exist. Due to a race # Ignore errno ENOENT: file does not exist. Due to a race
# condition, two processes could conceivably try and update # condition, two processes could conceivably try and update
# the same temp file at the same time # the same temp file at the same time
@@ -1049,7 +1049,7 @@ class TileSet(object):
"This may be a bug.", tile) "This may be a bug.", tile)
try: try:
os.unlink(imgpath) os.unlink(imgpath)
except OSError, e: except OSError as e:
# ignore only if the error was "file not found" # ignore only if the error was "file not found"
if e.errno != errno.ENOENT: if e.errno != errno.ENOENT:
raise raise
@@ -1062,7 +1062,7 @@ class TileSet(object):
if not os.path.exists(dirdest): if not os.path.exists(dirdest):
try: try:
os.makedirs(dirdest) os.makedirs(dirdest)
except OSError, e: except OSError as e:
# Ignore errno EEXIST: file exists. Due to a race condition, # Ignore errno EEXIST: file exists. Due to a race condition,
# two processes could conceivably try and create the same # two processes could conceivably try and create the same
# directory at the same time # directory at the same time
@@ -1098,7 +1098,7 @@ class TileSet(object):
# Some chunks are present on disk but not fully initialized. # Some chunks are present on disk but not fully initialized.
# This is okay. # This is okay.
pass pass
except Exception, e: except Exception as e:
logging.error("Could not render chunk %s,%s for some reason. " logging.error("Could not render chunk %s,%s for some reason. "
"This is likely a render primitive option error.", chunkx, chunkz) "This is likely a render primitive option error.", chunkx, chunkz)
logging.error("Full error was:", exc_info=1) logging.error("Full error was:", exc_info=1)
@@ -1154,7 +1154,7 @@ class TileSet(object):
imgpath = tileobj.get_filepath(self.outputdir, self.imgextension) imgpath = tileobj.get_filepath(self.outputdir, self.imgextension)
try: try:
tile_mtime = os.stat(imgpath)[stat.ST_MTIME] tile_mtime = os.stat(imgpath)[stat.ST_MTIME]
except OSError, e: except OSError as e:
if e.errno != errno.ENOENT: if e.errno != errno.ENOENT:
raise raise
tile_mtime = 0 tile_mtime = 0
@@ -1198,7 +1198,7 @@ class TileSet(object):
max_child_mtime = 0 max_child_mtime = 0
# First, recurse to each of our children # First, recurse to each of our children
for childnum in xrange(4): for childnum in range(4):
childpath = path + (childnum,) childpath = path + (childnum,)
# Check if this sub-tree should actually exist, so that we only # Check if this sub-tree should actually exist, so that we only
@@ -1238,7 +1238,7 @@ class TileSet(object):
logging.debug("Testing mtime for composite-tile %s", imgpath) logging.debug("Testing mtime for composite-tile %s", imgpath)
try: try:
tile_mtime = os.stat(imgpath)[stat.ST_MTIME] tile_mtime = os.stat(imgpath)[stat.ST_MTIME]
except OSError, e: except OSError as e:
if e.errno != errno.ENOENT: if e.errno != errno.ENOENT:
raise raise
tile_mtime = 0 tile_mtime = 0
@@ -1296,7 +1296,7 @@ def unconvert_coords(col, row):
# col + row = chunkz + chunkz => (col + row)/2 = chunkz # col + row = chunkz + chunkz => (col + row)/2 = chunkz
# col - row = chunkx + chunkx => (col - row)/2 = chunkx # col - row = chunkx + chunkx => (col - row)/2 = chunkx
return ((col - row) / 2, (col + row) / 2) return ((col - row) // 2, (col + row) // 2)
###################### ######################
@@ -1325,9 +1325,9 @@ def get_tiles_by_chunk(chunkcol, chunkrow):
# tile above it as well. Also touches the next 4 tiles down (16 # tile above it as well. Also touches the next 4 tiles down (16
# rows) # rows)
if chunkrow % 4 == 0: if chunkrow % 4 == 0:
rowrange = xrange(tilerow - 4, tilerow + 32 + 1, 4) rowrange = range(tilerow - 4, tilerow + 32 + 1, 4)
else: else:
rowrange = xrange(tilerow, tilerow + 32 + 1, 4) rowrange = range(tilerow, tilerow + 32 + 1, 4)
return product(colrange, rowrange) return product(colrange, rowrange)
@@ -1358,13 +1358,13 @@ def get_chunks_by_tile(tile, regionset):
# First do the odd. For each chunk in the tile's odd column the tile # First do the odd. For each chunk in the tile's odd column the tile
# "passes through" three chunk sections. # "passes through" three chunk sections.
oddcol_sections = [] oddcol_sections = []
for i, y in enumerate(reversed(xrange(16))): for i, y in enumerate(reversed(range(16))):
for row in xrange(tile.row + 3 - i * 2, tile.row - 2 - i * 2, -2): for row in range(tile.row + 3 - i * 2, tile.row - 2 - i * 2, -2):
oddcol_sections.append((tile.col + 1, row, y)) oddcol_sections.append((tile.col + 1, row, y))
evencol_sections = [] evencol_sections = []
for i, y in enumerate(reversed(xrange(16))): for i, y in enumerate(reversed(range(16))):
for row in xrange(tile.row + 4 - i * 2, tile.row - 3 - i * 2, -2): for row in range(tile.row + 4 - i * 2, tile.row - 3 - i * 2, -2):
evencol_sections.append((tile.col + 2, row, y)) evencol_sections.append((tile.col + 2, row, y))
evencol_sections.append((tile.col, row, y)) evencol_sections.append((tile.col, row, y))
@@ -1589,7 +1589,7 @@ class RendertileSet(object):
# or more of the children down the tree are True. # or more of the children down the tree are True.
return True return True
def __nonzero__(self): def __bool__(self):
"""Returns the boolean context of this particular node. If any """Returns the boolean context of this particular node. If any
descendent of this node is True return True. Otherwise, False. descendent of this node is True return True. Otherwise, False.
@@ -1604,8 +1604,8 @@ class RendertileSet(object):
# XXX There seems to be something wrong with the num_tiles calculation. # XXX There seems to be something wrong with the num_tiles calculation.
# Calculate the number of tiles by iteration and emit a warning if it # Calculate the number of tiles by iteration and emit a warning if it
# does not match. # does not match.
from itertools import imap
num = sum(imap(lambda _: 1, self.iterate())) num = sum(map(lambda _: 1, self.iterate()))
if num != self.num_tiles: if num != self.num_tiles:
logging.error("Please report this to the developers: RendertileSet num_tiles=%r, " logging.error("Please report this to the developers: RendertileSet num_tiles=%r, "
"count=%r, children=%r", self.num_tiles, num, self.children) "count=%r, children=%r", self.num_tiles, num, self.children)
@@ -1619,22 +1619,23 @@ class RendertileSet(object):
# XXX There seems to be something wrong with the num_tiles calculation. # XXX There seems to be something wrong with the num_tiles calculation.
# Calculate the number of tiles by iteration and emit a warning if it # Calculate the number of tiles by iteration and emit a warning if it
# does not match. # does not match.
from itertools import imap
num = sum(imap(lambda _: 1, self.posttraversal())) num = sum(map(lambda _: 1, self.posttraversal()))
if num != self.num_tiles_all: if num != self.num_tiles_all:
logging.error("Please report this to the developers: RendertileSet num_tiles_all=%r, " logging.error("Please report this to the developers: RendertileSet num_tiles_all=%r, "
"count_all=%r, children=%r", self.num_tiles, num, self.children) "count_all=%r, children=%r", self.num_tiles, num, self.children)
return num return num
def distance_sort(children, (off_x, off_y)): def distance_sort(children, xxx_todo_changeme):
(off_x, off_y) = xxx_todo_changeme
order = [] order = []
for child, (dx, dy) in izip(children, [(-1, -1), (1, -1), (-1, 1), (1, 1)]): for child, (dx, dy) in zip(children, [(-1, -1), (1, -1), (-1, 1), (1, 1)]):
x = off_x * 2 + dx x = off_x * 2 + dx
y = off_y * 2 + dy y = off_y * 2 + dy
order.append((child, (x, y))) order.append((child, (x, y)))
return sorted(order, key=lambda (_, (x, y)): x * x + y * y) return sorted(order, key=lambda __x_y: __x_y[1][0] * __x_y[1][0] + __x_y[1][1] * __x_y[1][1])
class RenderTile(object): class RenderTile(object):
@@ -1733,7 +1734,7 @@ class RenderTile(object):
path = [] path = []
for level in xrange(depth): for level in range(depth):
# Strategy: Find the midpoint of this level, and determine which # Strategy: Find the midpoint of this level, and determine which
# quadrant this row/col is in. Then set the bounds to that level # quadrant this row/col is in. Then set the bounds to that level
# and repeat # and repeat

View File

@@ -48,7 +48,7 @@ def findGitHash():
return line return line
except Exception: except Exception:
try: try:
import overviewer_version from . import overviewer_version
return overviewer_version.HASH return overviewer_version.HASH
except Exception: except Exception:
pass pass
@@ -77,7 +77,7 @@ def findGitVersion():
return line return line
except Exception: except Exception:
try: try:
import overviewer_version from . import overviewer_version
return overviewer_version.VERSION return overviewer_version.VERSION
except Exception: except Exception:
return "unknown" return "unknown"
@@ -108,7 +108,7 @@ def nice_exit(ret=0):
if ret and is_bare_console(): if ret and is_bare_console():
print("") print("")
print("Press [Enter] to close this window.") print("Press [Enter] to close this window.")
raw_input() input()
sys.exit(ret) sys.exit(ret)
@@ -117,7 +117,7 @@ def roundrobin(iterables):
"roundrobin('ABC', 'D', 'EF') --> A D E B F C" "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
# Recipe credited to George Sakkis # Recipe credited to George Sakkis
pending = len(iterables) pending = len(iterables)
nexts = cycle(iter(it).next for it in iterables) nexts = cycle(iter(it).__next__ for it in iterables)
while pending: while pending:
try: try:
for next in nexts: for next in nexts:
@@ -136,281 +136,13 @@ def dict_subset(d, keys):
return n return n
# (from http://code.activestate.com/recipes/576693/ [r9])
# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy.
# Passes Python2.7's test suite and incorporates all the latest updates.
try:
from thread import get_ident as _get_ident
except ImportError:
from dummy_thread import get_ident as _get_ident
try:
from _abcoll import KeysView, ValuesView, ItemsView
except ImportError:
pass
class OrderedDict(dict):
'Dictionary that remembers insertion order'
# An inherited dict maps keys to values.
# The inherited dict provides __getitem__, __len__, __contains__, and get.
# The remaining methods are order-aware.
# Big-O running times for all methods are the same as for regular dictionaries.
# The internal self.__map dictionary maps keys to links in a doubly linked list.
# The circular doubly linked list starts and ends with a sentinel element.
# The sentinel element never gets deleted (this simplifies the algorithm).
# Each link is stored as a list of length three: [PREV, NEXT, KEY].
def __init__(self, *args, **kwds):
'''Initialize an ordered dictionary. Signature is the same as for
regular dictionaries, but keyword arguments are not recommended
because their insertion order is arbitrary.
'''
if len(args) > 1:
raise TypeError('expected at most 1 arguments, got %d' % len(args))
try:
self.__root
except AttributeError:
self.__root = root = [] # sentinel node
root[:] = [root, root, None]
self.__map = {}
self.__update(*args, **kwds)
def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
'od.__setitem__(i, y) <==> od[i]=y'
# Setting a new item creates a new link which goes at the end of the linked
# list, and the inherited dictionary is updated with the new key/value pair.
if key not in self:
root = self.__root
last = root[0]
last[1] = root[0] = self.__map[key] = [last, root, key]
dict_setitem(self, key, value)
def __delitem__(self, key, dict_delitem=dict.__delitem__):
'od.__delitem__(y) <==> del od[y]'
# Deleting an existing item uses self.__map to find the link which is
# then removed by updating the links in the predecessor and successor nodes.
dict_delitem(self, key)
link_prev, link_next, key = self.__map.pop(key)
link_prev[1] = link_next
link_next[0] = link_prev
def __iter__(self):
'od.__iter__() <==> iter(od)'
root = self.__root
curr = root[1]
while curr is not root:
yield curr[2]
curr = curr[1]
def __reversed__(self):
'od.__reversed__() <==> reversed(od)'
root = self.__root
curr = root[0]
while curr is not root:
yield curr[2]
curr = curr[0]
def clear(self):
'od.clear() -> None. Remove all items from od.'
try:
for node in self.__map.itervalues():
del node[:]
root = self.__root
root[:] = [root, root, None]
self.__map.clear()
except AttributeError:
pass
dict.clear(self)
def popitem(self, last=True):
'''od.popitem() -> (k, v), return and remove a (key, value) pair.
Pairs are returned in LIFO order if last is true or FIFO order if false.
'''
if not self:
raise KeyError('dictionary is empty')
root = self.__root
if last:
link = root[0]
link_prev = link[0]
link_prev[1] = root
root[0] = link_prev
else:
link = root[1]
link_next = link[1]
root[1] = link_next
link_next[0] = root
key = link[2]
del self.__map[key]
value = dict.pop(self, key)
return key, value
# -- the following methods do not depend on the internal structure --
def keys(self):
'od.keys() -> list of keys in od'
return list(self)
def values(self):
'od.values() -> list of values in od'
return [self[key] for key in self]
def items(self):
'od.items() -> list of (key, value) pairs in od'
return [(key, self[key]) for key in self]
def iterkeys(self):
'od.iterkeys() -> an iterator over the keys in od'
return iter(self)
def itervalues(self):
'od.itervalues -> an iterator over the values in od'
for k in self:
yield self[k]
def iteritems(self):
'od.iteritems -> an iterator over the (key, value) items in od'
for k in self:
yield (k, self[k])
def update(*args, **kwds):
'''od.update(E, **F) -> None. Update od from dict/iterable E and F.
If E is a dict instance, does: for k in E: od[k] = E[k]
If E has a .keys() method, does: for k in E.keys(): od[k] = E[k]
Or if E is an iterable of items, does: for k, v in E: od[k] = v
In either case, this is followed by: for k, v in F.items(): od[k] = v
'''
if len(args) > 2:
raise TypeError('update() takes at most 2 positional '
'arguments (%d given)' % (len(args),))
elif not args:
raise TypeError('update() takes at least 1 argument (0 given)')
self = args[0]
# Make progressively weaker assumptions about "other"
other = ()
if len(args) == 2:
other = args[1]
if isinstance(other, dict):
for key in other:
self[key] = other[key]
elif hasattr(other, 'keys'):
for key in other.keys():
self[key] = other[key]
else:
for key, value in other:
self[key] = value
for key, value in kwds.items():
self[key] = value
__update = update # let subclasses override update without breaking __init__
__marker = object()
def pop(self, key, default=__marker):
'''od.pop(k[,d]) -> v, remove specified key and return the corresponding value.
If key is not found, d is returned if given, otherwise KeyError is raised.
'''
if key in self:
result = self[key]
del self[key]
return result
if default is self.__marker:
raise KeyError(key)
return default
def setdefault(self, key, default=None):
'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'
if key in self:
return self[key]
self[key] = default
return default
def __repr__(self, _repr_running={}):
'od.__repr__() <==> repr(od)'
call_key = id(self), _get_ident()
if call_key in _repr_running:
return '...'
_repr_running[call_key] = 1
try:
if not self:
return '%s()' % (self.__class__.__name__,)
return '%s(%r)' % (self.__class__.__name__, self.items())
finally:
del _repr_running[call_key]
def __reduce__(self):
'Return state information for pickling'
items = [[k, self[k]] for k in self]
inst_dict = vars(self).copy()
for k in vars(OrderedDict()):
inst_dict.pop(k, None)
if inst_dict:
return (self.__class__, (items,), inst_dict)
return self.__class__, (items,)
def copy(self):
'od.copy() -> a shallow copy of od'
return self.__class__(self)
@classmethod
def fromkeys(cls, iterable, value=None):
'''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S
and values equal to v (which defaults to None).
'''
d = cls()
for key in iterable:
d[key] = value
return d
def __eq__(self, other):
'''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive
while comparison to a regular mapping is order-insensitive.
'''
if isinstance(other, OrderedDict):
return len(self) == len(other) and self.items() == other.items()
return dict.__eq__(self, other)
def __ne__(self, other):
return not self == other
# -- the following methods are only used in Python 2.7 --
def viewkeys(self):
"od.viewkeys() -> a set-like object providing a view on od's keys"
return KeysView(self)
def viewvalues(self):
"od.viewvalues() -> an object providing a view on od's values"
return ValuesView(self)
def viewitems(self):
"od.viewitems() -> a set-like object providing a view on od's items"
return ItemsView(self)
# now replace all that with the official version, if available
try:
import collections
OrderedDict = collections.OrderedDict
except (ImportError, AttributeError):
pass
def pid_exists(pid): # http://stackoverflow.com/a/6940314/1318435 def pid_exists(pid): # http://stackoverflow.com/a/6940314/1318435
"""Check whether pid exists in the current process table.""" """Check whether pid exists in the current process table."""
if pid < 0: if pid < 0:
return False return False
try: try:
os.kill(pid, 0) os.kill(pid, 0)
except OSError, e: except OSError as e:
return e.errno != errno.ESRCH return e.errno != errno.ESRCH
else: else:
return True return True

View File

@@ -53,7 +53,7 @@ def log_other_exceptions(func):
return func(*args) return func(*args)
except ChunkDoesntExist: except ChunkDoesntExist:
raise raise
except Exception, e: except Exception as e:
logging.exception("%s raised this exception", func.func_name) logging.exception("%s raised this exception", func.func_name)
raise raise
return newfunc return newfunc
@@ -1139,7 +1139,7 @@ class RegionSet(object):
try: try:
region = self._get_regionobj(regionfile) region = self._get_regionobj(regionfile)
data = region.load_chunk(x, z) data = region.load_chunk(x, z)
except nbt.CorruptionError, e: except nbt.CorruptionError as e:
tries -= 1 tries -= 1
if tries > 0: if tries > 0:
# Flush the region cache to possibly read a new region file # Flush the region cache to possibly read a new region file
@@ -1251,7 +1251,7 @@ class RegionSet(object):
""" """
for (regionx, regiony), (regionfile, filemtime) in self.regionfiles.iteritems(): for (regionx, regiony), (regionfile, filemtime) in self.regionfiles.items():
try: try:
mcr = self._get_regionobj(regionfile) mcr = self._get_regionobj(regionfile)
except nbt.CorruptRegionError: except nbt.CorruptRegionError:
@@ -1267,7 +1267,7 @@ class RegionSet(object):
""" """
for (regionx, regiony), (regionfile, filemtime) in self.regionfiles.iteritems(): for (regionx, regiony), (regionfile, filemtime) in self.regionfiles.items():
""" SKIP LOADING A REGION WHICH HAS NOT BEEN MODIFIED! """ """ SKIP LOADING A REGION WHICH HAS NOT BEEN MODIFIED! """
if (filemtime < mtime): if (filemtime < mtime):
continue continue
@@ -1561,7 +1561,6 @@ def get_worlds():
"Returns {world # or name : level.dat information}" "Returns {world # or name : level.dat information}"
ret = {} ret = {}
save_dir = get_save_dir() save_dir = get_save_dir()
loc = locale.getpreferredencoding()
# No dirs found - most likely not running from inside minecraft-dir # No dirs found - most likely not running from inside minecraft-dir
if not save_dir is None: if not save_dir is None:
@@ -1571,11 +1570,12 @@ def get_worlds():
if not os.path.exists(world_dat): continue if not os.path.exists(world_dat): continue
try: try:
info = nbt.load(world_dat)[1] info = nbt.load(world_dat)[1]
info['Data']['path'] = os.path.join(save_dir, dir).decode(loc) info['Data']['path'] = os.path.join(save_dir, dir)
if 'LevelName' in info['Data'].keys(): if 'LevelName' in info['Data'].keys():
ret[info['Data']['LevelName']] = info['Data'] ret[info['Data']['LevelName']] = info['Data']
except nbt.CorruptNBTError: except nbt.CorruptNBTError:
ret[os.path.basename(world_path).decode(loc) + " (corrupt)"] = {'path': world_path.decode(loc), ret[os.path.basename(world_path) + " (corrupt)"] = {
'path': world_path,
'LastPlayed': 0, 'LastPlayed': 0,
'Time': 0, 'Time': 0,
'IsCorrupt': True} 'IsCorrupt': True}

View File

@@ -1,15 +1,15 @@
#!/usr/bin/env python2 #!/usr/bin/env python3
import sys import sys
import traceback import traceback
# quick version check # quick version check
if not (sys.version_info[0] == 2 and sys.version_info[1] >= 6): if sys.version_info[0] == 2 or (sys.version_info[0] == 3 and sys.version_info[1] < 4):
print("Sorry, the Overviewer requires at least Python 2.6 to run") print("Sorry, the Overviewer requires at least Python 3.4 to run.")
if sys.version_info[0] >= 3:
print("and will not run on Python 3.0 or later")
sys.exit(1) sys.exit(1)
from distutils.core import setup from distutils.core import setup
from distutils.extension import Extension from distutils.extension import Extension
from distutils.command.build import build from distutils.command.build import build
@@ -176,12 +176,12 @@ for name in glob.glob("overviewer_core/src/primitives/*.c"):
primitives.append(name) primitives.append(name)
c_overviewer_files = ['main.c', 'composite.c', 'iterate.c', 'endian.c', 'rendermodes.c'] c_overviewer_files = ['main.c', 'composite.c', 'iterate.c', 'endian.c', 'rendermodes.c']
c_overviewer_files += map(lambda mode: 'primitives/%s.c' % (mode,), primitives) c_overviewer_files += ['primitives/%s.c' % (mode) for mode in primitives]
c_overviewer_files += ['Draw.c'] c_overviewer_files += ['Draw.c']
c_overviewer_includes = ['overviewer.h', 'rendermodes.h'] c_overviewer_includes = ['overviewer.h', 'rendermodes.h']
c_overviewer_files = map(lambda s: 'overviewer_core/src/'+s, c_overviewer_files) c_overviewer_files = ['overviewer_core/src/' + s for s in c_overviewer_files]
c_overviewer_includes = map(lambda s: 'overviewer_core/src/'+s, c_overviewer_includes) c_overviewer_includes = ['overviewer_core/src/' + s for s in c_overviewer_includes]
setup_kwargs['ext_modules'].append(Extension('overviewer_core.c_overviewer', c_overviewer_files, include_dirs=['.', numpy_include] + pil_include, depends=c_overviewer_includes, extra_link_args=[])) setup_kwargs['ext_modules'].append(Extension('overviewer_core.c_overviewer', c_overviewer_files, include_dirs=['.', numpy_include] + pil_include, depends=c_overviewer_includes, extra_link_args=[]))

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
import unittest import unittest
# For convenience # For convenience

View File

@@ -9,15 +9,15 @@ class TestLRU(unittest.TestCase):
def test_single_insert(self): def test_single_insert(self):
self.lru[1] = 2 self.lru[1] = 2
self.assertEquals(self.lru[1], 2) self.assertEqual(self.lru[1], 2)
def test_multiple_insert(self): def test_multiple_insert(self):
self.lru[1] = 2 self.lru[1] = 2
self.lru[3] = 4 self.lru[3] = 4
self.lru[5] = 6 self.lru[5] = 6
self.assertEquals(self.lru[1], 2) self.assertEqual(self.lru[1], 2)
self.assertEquals(self.lru[3], 4) self.assertEqual(self.lru[3], 4)
self.assertEquals(self.lru[5], 6) self.assertEqual(self.lru[5], 6)
def test_full(self): def test_full(self):
self.lru[1] = 'asdf' self.lru[1] = 'asdf'
@@ -27,11 +27,11 @@ class TestLRU(unittest.TestCase):
self.lru[5] = 'asdf' self.lru[5] = 'asdf'
self.lru[6] = 'asdf' self.lru[6] = 'asdf'
self.assertRaises(KeyError, self.lru.__getitem__, 1) self.assertRaises(KeyError, self.lru.__getitem__, 1)
self.assertEquals(self.lru[2], 'asdf') self.assertEqual(self.lru[2], 'asdf')
self.assertEquals(self.lru[3], 'asdf') self.assertEqual(self.lru[3], 'asdf')
self.assertEquals(self.lru[4], 'asdf') self.assertEqual(self.lru[4], 'asdf')
self.assertEquals(self.lru[5], 'asdf') self.assertEqual(self.lru[5], 'asdf')
self.assertEquals(self.lru[6], 'asdf') self.assertEqual(self.lru[6], 'asdf')
def test_lru(self): def test_lru(self):
self.lru[1] = 'asdf' self.lru[1] = 'asdf'
@@ -40,17 +40,17 @@ class TestLRU(unittest.TestCase):
self.lru[4] = 'asdf' self.lru[4] = 'asdf'
self.lru[5] = 'asdf' self.lru[5] = 'asdf'
self.assertEquals(self.lru[1], 'asdf') self.assertEqual(self.lru[1], 'asdf')
self.assertEquals(self.lru[2], 'asdf') self.assertEqual(self.lru[2], 'asdf')
self.assertEquals(self.lru[4], 'asdf') self.assertEqual(self.lru[4], 'asdf')
self.assertEquals(self.lru[5], 'asdf') self.assertEqual(self.lru[5], 'asdf')
# 3 should be evicted now # 3 should be evicted now
self.lru[6] = 'asdf' self.lru[6] = 'asdf'
self.assertRaises(KeyError, self.lru.__getitem__, 3) self.assertRaises(KeyError, self.lru.__getitem__, 3)
self.assertEquals(self.lru[1], 'asdf') self.assertEqual(self.lru[1], 'asdf')
self.assertEquals(self.lru[2], 'asdf') self.assertEqual(self.lru[2], 'asdf')
self.assertEquals(self.lru[4], 'asdf') self.assertEqual(self.lru[4], 'asdf')
self.assertEquals(self.lru[5], 'asdf') self.assertEqual(self.lru[5], 'asdf')
self.assertEquals(self.lru[6], 'asdf') self.assertEqual(self.lru[6], 'asdf')

View File

@@ -1,6 +1,6 @@
import unittest import unittest
from itertools import chain, izip from itertools import chain
from overviewer_core.tileset import iterate_base4, RendertileSet from overviewer_core.tileset import iterate_base4, RendertileSet
from overviewer_core.util import roundrobin from overviewer_core.util import roundrobin
@@ -150,7 +150,7 @@ class RendertileSetTest(unittest.TestCase):
self.assertRaises(AssertionError, self.test_iterate) self.assertRaises(AssertionError, self.test_iterate)
def test_count(self): def test_count(self):
self.assertEquals(self.tree.count(), len(self.tile_paths)) self.assertEqual(self.tree.count(), len(self.tile_paths))
def test_bool(self): def test_bool(self):
"Tests the boolean status of a node" "Tests the boolean status of a node"
@@ -202,7 +202,7 @@ class RendertileSetTest(unittest.TestCase):
"""Test a post-traversal of the tree's dirty tiles""" """Test a post-traversal of the tree's dirty tiles"""
# Expect the results in this proper order. # Expect the results in this proper order.
iterator = iter(self.tree.posttraversal()) iterator = iter(self.tree.posttraversal())
for expected, actual in izip(self.tile_paths_posttraversal, iterator): for expected, actual in zip(self.tile_paths_posttraversal, iterator):
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
self.assertRaises(StopIteration, next, iterator) self.assertRaises(StopIteration, next, iterator)
@@ -211,7 +211,7 @@ class RendertileSetTest(unittest.TestCase):
"""Test a round-robin post-traversal of the tree's dirty tiles""" """Test a round-robin post-traversal of the tree's dirty tiles"""
# Expect the results in this proper order. # Expect the results in this proper order.
iterator = iter(self.tree.posttraversal(robin=True)) iterator = iter(self.tree.posttraversal(robin=True))
for expected, actual in izip(self.tile_paths_posttraversal_robin, iterator): for expected, actual in zip(self.tile_paths_posttraversal_robin, iterator):
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
self.assertRaises(StopIteration, next, iterator) self.assertRaises(StopIteration, next, iterator)

View File

@@ -1,4 +1,5 @@
import unittest import unittest
from collections import OrderedDict
from overviewer_core import configParser from overviewer_core import configParser
from overviewer_core.settingsValidators import ValidationException from overviewer_core.settingsValidators import ValidationException
@@ -6,7 +7,6 @@ from overviewer_core.settingsValidators import ValidationException
from overviewer_core import world from overviewer_core import world
from overviewer_core import rendermodes from overviewer_core import rendermodes
from overviewer_core.util import OrderedDict
class SettingsTest(unittest.TestCase): class SettingsTest(unittest.TestCase):
@@ -23,12 +23,12 @@ class SettingsTest(unittest.TestCase):
# no exceptions so far. that's a good thing # no exceptions so far. that's a good thing
# Test the default # Test the default
self.assertEquals(things['renders']['myworld']['bgcolor'], (26,26,26,0)) self.assertEqual(things['renders']['myworld']['bgcolor'], (26,26,26,0))
# Test a non-default # Test a non-default
self.assertEquals(things['renders']['otherworld']['bgcolor'], (255,255,255,0)) self.assertEqual(things['renders']['otherworld']['bgcolor'], (255,255,255,0))
self.assertEquals(things['renders']['myworld']['northdirection'], self.assertEqual(things['renders']['myworld']['northdirection'],
world.UPPER_LEFT) world.UPPER_LEFT)
def test_rendermode_validation(self): def test_rendermode_validation(self):
@@ -63,7 +63,7 @@ class SettingsTest(unittest.TestCase):
}), }),
])) ]))
self.s.set_config_item("outputdir", "/tmp/fictional/outputdir") self.s.set_config_item("outputdir", "/tmp/fictional/outputdir")
self.assertEquals(fromfile.get_validated_config(), self.s.get_validated_config()) self.assertEqual(fromfile.get_validated_config(), self.s.get_validated_config())
def test_rendermode_string(self): def test_rendermode_string(self):
self.s.set_config_item("worlds", { self.s.set_config_item("worlds", {
@@ -79,7 +79,7 @@ class SettingsTest(unittest.TestCase):
}, },
}) })
p = self.s.get_validated_config() p = self.s.get_validated_config()
self.assertEquals(p['renders']['myworld']['rendermode'], rendermodes.normal) self.assertEqual(p['renders']['myworld']['rendermode'], rendermodes.normal)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

@@ -50,11 +50,11 @@ class FakeRegionset(object):
return NotImplementedError() return NotImplementedError()
def iterate_chunks(self): def iterate_chunks(self):
for (x,z),mtime in self.chunks.iteritems(): for (x,z),mtime in self.chunks.items():
yield x,z,mtime yield x,z,mtime
def iterate_newer_chunks(self, filemtime): def iterate_newer_chunks(self, filemtime):
for (x,z),mtime in self.chunks.iteritems(): for (x,z),mtime in self.chunks.items():
yield x,z,mtime yield x,z,mtime
def get_chunk_mtime(self, x, z): def get_chunk_mtime(self, x, z):
@@ -77,7 +77,7 @@ def get_tile_set(chunks):
the compare_iterate_to_expected() method. the compare_iterate_to_expected() method.
""" """
tile_set = defaultdict(int) tile_set = defaultdict(int)
for (chunkx, chunkz), chunkmtime in chunks.iteritems(): for (chunkx, chunkz), chunkmtime in chunks.items():
col, row = tileset.convert_coords(chunkx, chunkz) col, row = tileset.convert_coords(chunkx, chunkz)
@@ -86,9 +86,9 @@ def get_tile_set(chunks):
tile_set[tile.path] = max(tile_set[tile.path], chunkmtime) tile_set[tile.path] = max(tile_set[tile.path], chunkmtime)
# At this point, tile_set holds all the render-tiles # At this point, tile_set holds all the render-tiles
for tile, tile_mtime in tile_set.copy().iteritems(): for tile, tile_mtime in tile_set.copy().items():
# All render-tiles are length 5. Hard-code its upper tiles # All render-tiles are length 5. Hard-code its upper tiles
for i in reversed(xrange(5)): for i in reversed(range(5)):
tile_set[tile[:i]] = max(tile_set[tile[:i]], tile_mtime) tile_set[tile[:i]] = max(tile_set[tile[:i]], tile_mtime)
return dict(tile_set) return dict(tile_set)
@@ -98,7 +98,7 @@ def create_fakedir(outputdir, tiles):
files) and sets mtimes appropriately files) and sets mtimes appropriately
""" """
for tilepath, tilemtime in tiles.iteritems(): for tilepath, tilemtime in tiles.items():
dirpath = os.path.join(outputdir, *(str(x) for x in tilepath[:-1])) dirpath = os.path.join(outputdir, *(str(x) for x in tilepath[:-1]))
if len(tilepath) == 0: if len(tilepath) == 0:
imgname = "base.png" imgname = "base.png"
@@ -175,7 +175,7 @@ class TilesetTest(unittest.TestCase):
self.assertTrue(tilepath in expected, "%s was not expected to be returned. Expected %s" % (tilepath, expected)) self.assertTrue(tilepath in expected, "%s was not expected to be returned. Expected %s" % (tilepath, expected))
# Now check that all expected tiles were indeed returned # Now check that all expected tiles were indeed returned
for tilepath in expected.iterkeys(): for tilepath in expected.keys():
self.assertTrue(tilepath in paths, "%s was expected to be returned but wasn't: %s" % (tilepath, paths)) self.assertTrue(tilepath in paths, "%s was expected to be returned but wasn't: %s" % (tilepath, paths))
def test_get_phase_length(self): def test_get_phase_length(self):
@@ -215,7 +215,7 @@ class TilesetTest(unittest.TestCase):
"""Same as above but with a different set of chunks """Same as above but with a different set of chunks
""" """
# Pick 3 random chunks to update # Pick 3 random chunks to update
chunks = self.rs.chunks.keys() chunks = list(self.rs.chunks.keys())
self.r.shuffle(chunks) self.r.shuffle(chunks)
updated_chunks = {} updated_chunks = {}
for key in chunks[:3]: for key in chunks[:3]:

View File

@@ -17,19 +17,19 @@ class ExampleWorldTest(unittest.TestCase):
w = world.World("test/data/worlds/exmaple") w = world.World("test/data/worlds/exmaple")
regionsets = w.get_regionsets() regionsets = w.get_regionsets()
self.assertEquals(len(regionsets), 3) self.assertEqual(len(regionsets), 3)
regionset = regionsets[0] regionset = regionsets[0]
self.assertEquals(regionset.get_region_path(0,0), 'test/data/worlds/exmaple/DIM-1/region/r.0.0.mcr') self.assertEqual(regionset.get_region_path(0,0), 'test/data/worlds/exmaple/DIM-1/region/r.0.0.mcr')
self.assertEquals(regionset.get_region_path(-1,0), 'test/data/worlds/exmaple/DIM-1/region/r.-1.0.mcr') self.assertEqual(regionset.get_region_path(-1,0), 'test/data/worlds/exmaple/DIM-1/region/r.-1.0.mcr')
self.assertEquals(regionset.get_region_path(1,1), 'test/data/worlds/exmaple/DIM-1/region/r.0.0.mcr') self.assertEqual(regionset.get_region_path(1,1), 'test/data/worlds/exmaple/DIM-1/region/r.0.0.mcr')
self.assertEquals(regionset.get_region_path(35,35), None) self.assertEqual(regionset.get_region_path(35,35), None)
# a few random chunks. reference timestamps fetched with libredstone # a few random chunks. reference timestamps fetched with libredstone
self.assertEquals(regionset.get_chunk_mtime(0,0), 1316728885) self.assertEqual(regionset.get_chunk_mtime(0,0), 1316728885)
self.assertEquals(regionset.get_chunk_mtime(-1,-1), 1316728886) self.assertEqual(regionset.get_chunk_mtime(-1,-1), 1316728886)
self.assertEquals(regionset.get_chunk_mtime(5,0), 1316728905) self.assertEqual(regionset.get_chunk_mtime(5,0), 1316728905)
self.assertEquals(regionset.get_chunk_mtime(-22,16), 1316786786) self.assertEqual(regionset.get_chunk_mtime(-22,16), 1316786786)