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.
#
@ -21,10 +21,8 @@ import platform
import sys
# quick version check
if not (sys.version_info[0] == 2 and sys.version_info[1] >= 6):
print("Sorry, the Overviewer requires at least Python 2.6 to run")
if sys.version_info[0] >= 3:
print("and will not run on Python 3.0 or later")
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 3.4 to run.")
sys.exit(1)
import os
@ -35,6 +33,7 @@ import multiprocessing
import time
import logging
from argparse import ArgumentParser
from collections import OrderedDict
from overviewer_core import util
from overviewer_core import logger
@ -136,7 +135,7 @@ def main():
# Check for possible shell quoting issues
if len(unknowns) > 0:
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.output] + unknowns[:i]))
for mistake in possible_mistakes:
@ -183,9 +182,9 @@ def main():
print("Currently running Minecraft Overviewer %s" % util.findGitVersion() +
" (%s)" % util.findGitHash()[:7])
try:
import urllib
from urllib import request
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']
print("Latest version of Minecraft Overviewer %s (%s)" % (latest_ver['version'],
latest_ver['commit'][:7]))
@ -294,7 +293,7 @@ def main():
rendermodes = args.rendermodes.replace("-", "_").split(",")
# Now for some good defaults
renders = util.OrderedDict()
renders = OrderedDict()
for rm in rendermodes:
renders["world-" + rm] = {
"world": "world",
@ -337,7 +336,7 @@ def main():
if args.check_terrain: # we are already in the "if configfile" branch
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)
# find or create the textures object
@ -356,7 +355,7 @@ def main():
logging.debug("Current log level: {0}.".format(logging.getLogger().level))
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:
logging.warning(checkname + " ignoring render " + repr(name) + " since it's "
"marked as \"don't render\".")
@ -381,7 +380,7 @@ def main():
#####################
# 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
# in render['worldname_orig']
try:
@ -437,7 +436,7 @@ def main():
# The changelist support.
changelists = {}
for render in config['renders'].itervalues():
for render in config['renders'].values():
if 'changelist' in render:
path = render['changelist']
if path not in changelists:
@ -461,7 +460,7 @@ def main():
# TODO: optionally more caching layers here
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)
# find or create the world object
@ -572,7 +571,7 @@ def main():
assetMrg.finalize(tilesets)
for out in changelists.itervalues():
for out in changelists.values():
logging.debug("Closing %s (%s).", out, out.fileno())
out.close()
@ -603,8 +602,8 @@ def list_worlds():
formatString = "%-" + str(worldNameLen) + "s | %-8s | %-16s | %s "
print(formatString % ("World", "Playtime", "Modified", "Path"))
print(formatString % ("-" * worldNameLen, "-" * 8, '-' * 16, '-' * 4))
for name, info in sorted(worlds.iteritems()):
if isinstance(name, basestring) and name.startswith("World") and len(name) == 6:
for name, info in sorted(worlds.items()):
if isinstance(name, str) and name.startswith("World") and len(name) == 6:
try:
world_n = int(name[-1])
# 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
#
from __future__ import print_function
import os.path
import os
import platform
import traceback
import sys
import util
from . import util
def check_c_overviewer():
"""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()
# make sure the c_overviewer extension is available
try:
import c_overviewer
from . import c_overviewer
except ImportError:
if os.environ.get("OVERVIEWER_DEBUG_IMPORT") == "1":
traceback.print_exc()
## if this is a frozen windows package, the following error messages about
## building the c_overviewer extension are not appropriate
if hasattr(sys, "frozen") and platform.system() == 'Windows':
print "Something has gone wrong importing the c_overviewer extension. Please"
print "make sure the 2008 and 2010 redistributable packages from Microsoft"
print "are installed."
print("Something has gone wrong importing the c_overviewer extension. Please make sure "
"the 2008 and 2010 redistributable packages from Microsoft are installed.")
return 1
## try to find the build extension
ext = os.path.join(root_dir, "overviewer_core", "c_overviewer.%s" % ("pyd" if platform.system() == "Windows" else "so"))
if os.path.exists(ext):
traceback.print_exc()
print ""
print "Something has gone wrong importing the c_overviewer extension. Please"
print "make sure it is up-to-date (clean and rebuild)"
print()
print("Something has gone wrong importing the c_overviewer extension. Please make sure "
"it is up-to-date. (clean and rebuild)")
return 1
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("You need to compile the c_overviewer module to run Minecraft Overviewer.")
print("Run `python setup.py build`, or see the README for details.")
return 1
#
@ -54,14 +55,15 @@ def check_c_overviewer():
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:
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:
l = lines[0]
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
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
# all good!

View File

@ -23,9 +23,9 @@ import traceback
from PIL import Image
import world
import util
from files import FileReplacer, mirror_dir, get_fs_caps
from . import world
from . import util
from .files import FileReplacer, mirror_dir, get_fs_caps
class AssetManager(object):
@ -53,7 +53,7 @@ top-level directory.
with open(config_loc) as c:
ovconf_str = "{" + "\n".join(c.readlines()[1:-1]) + "}"
self.overviewerConfig = json.loads(ovconf_str)
except Exception, e:
except Exception as e:
if os.path.exists(config_loc):
logging.warning("A previous overviewerConfig.js was found, "
"but I couldn't read it for some reason."
@ -61,16 +61,6 @@ top-level directory.
logging.debug(traceback.format_exc())
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):
"Return the correct dictionary from the parsed overviewerConfig.js"
for conf in self.overviewerConfig['tilesets']:
@ -213,10 +203,7 @@ top-level directory.
index = codecs.open(indexpath, 'r', encoding='UTF-8').read()
index = index.replace("{title}", "Minecraft Overviewer")
index = index.replace("{time}",
time.strftime("%a, %d %b %Y %H:%M:%S %Z",
time.localtime())
.decode(self.preferredencoding))
index = index.replace("{time}", time.strftime("%a, %d %b %Y %H:%M:%S %Z", time.localtime()))
versionstr = "%s (%s)" % (util.findGitVersion(),
util.findGitHash()[:7])
index = index.replace("{version}", versionstr)

View File

@ -4,8 +4,8 @@ import os.path
import logging
import traceback
import settingsDefinition
import settingsValidators
from . import settingsDefinition
from . import settingsValidators
class MissingConfigException(Exception):
"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
# file has access to those resources.
import rendermodes
from . import rendermodes
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):
logging.error("Syntax error parsing %s" % settings_file)
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
# render defaults
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 in render_settings:
setting = render_settings[key]

View File

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

View File

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

View File

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

View File

@ -15,7 +15,7 @@
import functools
import gzip
import StringIO
from io import BytesIO
import struct
import zlib
@ -25,7 +25,7 @@ import zlib
def _file_loader(func):
@functools.wraps(func)
def wrapper(fileobj, *args):
if isinstance(fileobj, basestring):
if type(fileobj) == str:
# Is actually a filename
fileobj = open(fileobj, 'rb', 4096)
return func(fileobj, *args)
@ -92,7 +92,7 @@ class NBTFileReader(object):
# pure zlib stream -- maybe later replace this with
# a custom zlib file object?
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
self._read_tagmap = {
@ -168,7 +168,7 @@ class NBTFileReader(object):
read_method = self._read_tagmap[tagid]
l = []
for _ in xrange(length):
for _ in range(length):
l.append(read_method())
return l
@ -203,7 +203,7 @@ class NBTFileReader(object):
name = self._read_tag_string()
payload = self._read_tag_compound()
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),))
@ -252,9 +252,9 @@ class MCRFileReader(object):
file, as (x, z) coordinate tuples. To load these chunks,
provide these coordinates to load_chunk()."""
for x in xrange(32):
for z in xrange(32):
if self._locations[x + z * 32] >> 8 != 0:
for x in range(32):
for z in range(32):
if self._locations[int(x + z * 32)] >> 8 != 0:
yield (x, z)
def get_chunk_timestamp(self, x, z):
@ -264,13 +264,13 @@ class MCRFileReader(object):
"""
x = x % 32
z = z % 32
return self._timestamps[x + z * 32]
return self._timestamps[int(x + z * 32)]
def chunk_exists(self, x, z):
"""Determines if a chunk exists."""
x = x % 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):
"""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."""
x = x % 32
z = z % 32
location = self._locations[x + z * 32]
location = self._locations[int(x + z * 32)]
offset = (location >> 8) * 4096
sectors = location & 0xff
@ -311,16 +311,16 @@ class MCRFileReader(object):
raise CorruptRegionError("unsupported chunk compression type: %i "
"(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)
data = self._file.read(data_length - 1)
if len(data) != data_length - 1:
raise CorruptRegionError("chunk length is invalid")
data = StringIO.StringIO(data)
data = BytesIO(data)
try:
return NBTFileReader(data, is_gzip=is_gzip).read_all()
except CorruptionError:
raise
except Exception, e:
except Exception as e:
raise CorruptChunkError("Misc error parsing chunk: " + str(e))

View File

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

View File

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

View File

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

View File

@ -14,7 +14,7 @@
# with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
from PIL import Image
import textures
from . import textures
"""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.
@ -31,13 +31,13 @@ class RenderPrimitive(object):
raise RuntimeError("RenderPrimitive cannot be used directly")
self.option_values = {}
for key, val in kwargs.iteritems():
for key, val in kwargs.items():
if not key in self.options:
raise ValueError("primitive `{0}' has no option `{1}'".format(self.name, key))
self.option_values[key] = val
# 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:
self.option_values[name] = default

View File

@ -43,10 +43,11 @@
# 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.
from settingsValidators import *
import util
from observer import ProgressBarObserver, LoggingObserver, JSObserver
from optimizeimages import pngnq, optipng, pngcrush
from collections import OrderedDict
from .settingsValidators import *
from .observer import ProgressBarObserver, LoggingObserver, JSObserver
from .optimizeimages import pngnq, optipng, pngcrush
import platform
import sys
@ -60,7 +61,7 @@ import sys
# objects with their respective validators.
# config file.
renders = Setting(required=True, default=util.OrderedDict(),
renders = Setting(required=True, default=OrderedDict(),
validator=make_dictValidator(validateStr, make_configDictValidator(
{
"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
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)

View File

@ -2,11 +2,12 @@
import logging
import os
import os.path
from collections import OrderedDict
import rendermodes
import util
from optimizeimages import Optimizer
from world import LOWER_LEFT, LOWER_RIGHT, UPPER_LEFT, UPPER_RIGHT
from . import rendermodes
from . import util
from .optimizeimages import Optimizer
from .world import LOWER_LEFT, LOWER_RIGHT, UPPER_LEFT, UPPER_RIGHT
class ValidationException(Exception):
@ -179,8 +180,8 @@ def validateBGColor(color):
def validateOptImg(optimizers):
if isinstance(optimizers, (int, long)):
from optimizeimages import pngcrush
if isinstance(optimizers, int):
from .optimizeimages import pngcrush
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.")
optimizers = [pngcrush()]
@ -189,7 +190,7 @@ def validateOptImg(optimizers):
"Make sure you specify them like [foo()], with square brackets.")
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):
raise ValidationException("Invalid Optimizer!")
@ -272,7 +273,7 @@ def validateCrop(value):
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
else:
raise ValidationException("%r does not look like an observer." % repr(observer))
@ -316,8 +317,8 @@ def make_dictValidator(keyvalidator, valuevalidator):
"""
def v(d):
newd = util.OrderedDict()
for key, value in d.iteritems():
newd = OrderedDict()
for key, value in d.items():
newd[keyvalidator(key)] = valuevalidator(value)
return newd
# 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):
newdict = util.OrderedDict()
newdict = OrderedDict()
# values are config keys that the user specified that don't match any
# valid key
@ -353,10 +354,10 @@ def make_configDictValidator(config, ignore_undefined=False):
undefined_key_matches = {}
# 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:
# 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:
# Save this for later. It only matters if this is a typo of
# 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`),
# checking each one to see if the user specified it (in `d`). Then
# validate it and copy the result to `newdict`
for configkey, configsetting in config.iteritems():
for configkey, configsetting in config.items():
if configkey in d:
# This key /was/ specified in the user's dict. Make sure it validates.
newdict[configkey] = configsetting.validator(d[configkey])
@ -414,9 +415,9 @@ def _levenshtein(s1, s2):
l1 = len(s1)
l2 = len(s2)
matrix = [range(l1 + 1)] * (l2 + 1)
matrix = [list(range(l1 + 1))] * (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 sz in range(0, l1):
if s1[sz] == s2[zz]:

View File

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

View File

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

View File

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

View File

@ -234,9 +234,9 @@ base_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObjec
/* look up color! */
color = PySequence_GetItem(color_table, tabley * 256 + tablex);
r = PyInt_AsLong(PyTuple_GET_ITEM(color, 0));
g = PyInt_AsLong(PyTuple_GET_ITEM(color, 1));
b = PyInt_AsLong(PyTuple_GET_ITEM(color, 2));
r = PyLong_AsLong(PyTuple_GET_ITEM(color, 0));
g = PyLong_AsLong(PyTuple_GET_ITEM(color, 1));
b = PyLong_AsLong(PyTuple_GET_ITEM(color, 2));
Py_DECREF(color);
/* 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);
/* get the colors and tint and tint */
r = PyInt_AsLong(PyList_GetItem(self->depth_colors, 0 + y*3));
g = PyInt_AsLong(PyList_GetItem(self->depth_colors, 1 + y*3));
b = PyInt_AsLong(PyList_GetItem(self->depth_colors, 2 + y*3));
r = PyLong_AsLong(PyList_GetItem(self->depth_colors, 0 + y*3));
g = PyLong_AsLong(PyList_GetItem(self->depth_colors, 1 + 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);
}

View File

@ -52,9 +52,9 @@ hide_start(void *data, RenderState *state, PyObject *support) {
for (i = 0; i < blocks_size; i++) {
PyObject *block = PyList_GET_ITEM(opt, i);
if (PyInt_Check(block)) {
if (PyLong_Check(block)) {
/* 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;
} else if (PyArg_ParseTuple(block, "Hb", &(self->rules[i].blockid), &(self->rules[i].data))) {
/* format 2: (blockid, data) */

View File

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

View File

@ -40,7 +40,7 @@ RenderPrimitive *render_primitive_create(PyObject *prim, RenderState *state) {
pyname = PyObject_GetAttrString(prim, "name");
if (!pyname)
return NULL;
name = PyString_AsString(pyname);
name = PyUnicode_AsUTF8(pyname);
for (i = 0; render_primitives[i] != NULL; i++) {
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);
if (!ret) {
PyObject *errtype, *errvalue, *errtraceback;
const char *errstring;
PyObject *errtype, *errvalue, *errtraceback, *errstring;
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);

View File

@ -18,7 +18,7 @@ import imp
import os
import os.path
import zipfile
from cStringIO import StringIO
from io import BytesIO
import math
from random import randint
import numpy
@ -26,8 +26,32 @@ from PIL import Image, ImageEnhance, ImageOps, ImageDraw
import logging
import functools
import util
from c_overviewer import alpha_over
from . import util
# 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):
"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",
"light_gray", "cyan", "purple", "blue", "brown", "green", "red", "black"]
##
## Textures object
##
@ -76,10 +101,11 @@ class Textures(object):
del attributes[attr]
except KeyError:
pass
attributes['jar'] = None
return attributes
def __setstate__(self, attrs):
# regenerate textures, if needed
for attr, val in attrs.iteritems():
for attr, val in list(attrs.items()):
setattr(self, attr, val)
self.texture_cache = {}
if self.generated:
@ -99,7 +125,7 @@ class Textures(object):
global known_blocks, used_datas
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)
self.blockmap[blockid * max_data + data] = self.generate_texture_tuple(tex)
@ -224,7 +250,7 @@ class Textures(object):
self.jar.getinfo(jarfilename)
if verbose: logging.info("Found (cached) %s in '%s'", jarfilename, self.jarpath)
return self.jar.open(jarfilename)
except (KeyError, IOError), e:
except (KeyError, IOError) as e:
pass
# 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)
self.jar, self.jarpath = jar, jarpath
return jar.open(jarfilename)
except (KeyError, IOError), e:
except (KeyError, IOError) as e:
pass
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]
fileobj = self.find_file(filename)
buffer = StringIO(fileobj.read())
buffer = BytesIO(fileobj.read())
img = Image.open(buffer).convert("RGBA")
self.texture_cache[filename] = img
return img
@ -444,8 +470,8 @@ class Textures(object):
textures = []
(terrain_width, terrain_height) = terrain.size
texture_resolution = terrain_width / 16
for y in xrange(16):
for x in xrange(16):
for y in range(16):
for x in range(16):
left = x*texture_resolution
upper = y*texture_resolution
right = left+texture_resolution
@ -815,19 +841,6 @@ class Textures(object):
## 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
def material(blockid=[], data=[0], **kwargs):
@ -837,11 +850,11 @@ def material(blockid=[], data=[0], **kwargs):
# make sure blockid and data are iterable
try:
iter(blockid)
except:
except Exception:
blockid = [blockid,]
try:
iter(data)
except:
except Exception:
data = [data,]
def inner_material(func):
@ -924,7 +937,7 @@ def billboard(blockid=[], imagename=None, **kwargs):
##
# stone
@material(blockid=1, data=range(7), solid=True)
@material(blockid=1, data=list(range(7)), solid=True)
def stone(self, blockid, data):
if data == 0: # regular old-school stone
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")
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):
# 0x10 bit means SNOW
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
# dirt
@material(blockid=3, data=range(3), solid=True)
@material(blockid=3, data=list(range(3)), solid=True)
def dirt_blocks(self, blockid, data):
side_img = self.load_image_texture("assets/minecraft/textures/block/dirt.png")
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")
# 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):
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"))
@ -985,7 +998,7 @@ def wooden_planks(self, blockid, data):
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"))
@material(blockid=6, data=range(16), transparent=True)
@material(blockid=6, data=list(range(16)), transparent=True)
def saplings(self, blockid, data):
# usual saplings
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)
# 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):
if blockid == 8 or blockid == 9:
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)
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):
lavatex = self.load_lava()
return self.build_block(lavatex, lavatex)
# sand
@material(blockid=12, data=range(2), solid=True)
@material(blockid=12, data=list(range(2)), solid=True)
def sand_blocks(self, blockid, data):
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"))
@ -1077,7 +1090,7 @@ block(blockid=15, top_image="assets/minecraft/textures/block/iron_ore.png")
# coal ore
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):
# extract orientation and wood type frorm data bits
wood_type = data & 3
@ -1191,7 +1204,7 @@ def wood(self, blockid, data):
elif wood_orientation == 8: # north-south orientation
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):
# mask out the bits 4 and 8
# 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")
# 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):
# first, do the rotation if needed
if self.rotation == 1:
@ -1266,7 +1279,7 @@ def furnaces(self, blockid, data):
return self.build_full_block(top, None, None, side, side)
# sandstone
@material(blockid=24, data=range(3), solid=True)
@material(blockid=24, data=list(range(3)), solid=True)
def sandstone(self, blockid, data):
top = self.load_image_texture("assets/minecraft/textures/block/sandstone_top.png")
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"))
# red sandstone
@material(blockid=179, data=range(3), solid=True)
@material(blockid=179, data=list(range(3)), solid=True)
def sandstone(self, blockid, data):
top = self.load_image_texture("assets/minecraft/textures/block/red_sandstone_top.png")
if data == 0: # normal
@ -1291,7 +1304,7 @@ def sandstone(self, blockid, data):
# note block
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):
# first get rotation done
# 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)
# 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):
# first, do rotation
# Masked to not clobber powered rail on/off info
@ -1664,7 +1677,7 @@ def piston_extension(self, blockid, data):
# cobweb
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):
if data == 0: # dead shrub
texture = self.load_image_texture("assets/minecraft/textures/block/dead_bush.png")
@ -1678,7 +1691,7 @@ def tall_grass(self, blockid, data):
# dead bush
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):
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")
# flowers
@material(blockid=38, data=range(10), transparent=True)
@material(blockid=38, data=list(range(10)), transparent=True)
def flower(self, blockid, data):
flower_map = ["poppy", "blue_orchid", "allium", "azure_bluet", "red_tulip", "orange_tulip",
"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
# these wooden slabs are unobtainable without cheating, they are still
# 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):
if blockid == 44 or blockid == 182:
texture = data & 7
@ -1847,7 +1860,7 @@ def torches(self, blockid, data):
return img
# fire
@material(blockid=51, data=range(16), transparent=True)
@material(blockid=51, data=list(range(16)), transparent=True)
def fire(self, blockid, data):
firetextures = self.load_fire()
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)
# 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):
# preserve the upside-down bit
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
# 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):
# the first 3 bits are the orientation as stored in minecraft,
# bits 0x8 and 0x10 indicate which half of the double chest is it.
@ -2188,7 +2201,7 @@ def chests(self, blockid, data):
# redstone wire
# 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):
if data & 0b1000000 == 64: # powered redstone wire
@ -2281,7 +2294,7 @@ def crafting_table(self, blockid, data):
return img
# 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):
raw_crop = self.load_image_texture("assets/minecraft/textures/block/wheat_stage%d.png" % data)
crop1 = self.transform_image_top(raw_crop)
@ -2295,7 +2308,7 @@ def crops8(self, blockid, data):
return img
# 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):
if blockid == 60:
side = self.load_image_texture("assets/minecraft/textures/block/dirt.png")
@ -2315,7 +2328,7 @@ def farmland(self, blockid, data):
# signposts
@material(blockid=63, data=range(16), transparent=True)
@material(blockid=63, data=list(range(16)), transparent=True)
def signpost(self, blockid, data):
# first rotations
@ -2369,7 +2382,7 @@ def signpost(self, blockid, data):
# wooden and iron door
# 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):
#Masked to not clobber block top/bottom & swung info
if self.rotation == 1:
@ -2618,7 +2631,7 @@ def wall_sign(self, blockid, data): # wall sign
return img
# levers
@material(blockid=69, data=range(16), transparent=True)
@material(blockid=69, data=list(range(16)), transparent=True)
def levers(self, blockid, data):
if data & 8 == 8: powered = True
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")
# 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):
# 0x8 is set if the button is pressed mask this info and render
@ -2899,7 +2912,7 @@ def buttons(self, blockid, data):
return img
# 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):
# 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")
# 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):
top = self.load_image_texture("assets/minecraft/textures/block/cactus_top.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")
# 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):
tex = self.load_image_texture("assets/minecraft/textures/block/sugar_cane.png")
return self.build_sprite(tex)
# jukebox
@material(blockid=84, data=range(16), solid=True)
@material(blockid=84, data=list(range(16)), solid=True)
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"))
# nether and normal fences
# 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):
# no need for rotations, it uses pseudo data.
# create needed images for Big stick fence
@ -3082,7 +3095,7 @@ def fence(self, blockid, data):
return img
# 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
# rotation
if self.rotation == 1:
@ -3149,7 +3162,7 @@ def portal(self, blockid, data):
return img
# 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):
# cake textures
@ -3273,7 +3286,7 @@ def cake(self, blockid, data):
return img
# 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):
# rotation
# Masked to not clobber delay info
@ -3418,7 +3431,7 @@ def repeater(self, blockid, data):
return img
# 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):
# rotation
@ -3483,7 +3496,7 @@ def comparator(self, blockid, data):
# trapdoor
# 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):
# rotation
@ -3539,7 +3552,7 @@ def trapdoor(self, blockid, data):
return img
# 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):
if data == 0: # stone
t = self.load_image_texture("assets/minecraft/textures/block/stone.png")
@ -3553,7 +3566,7 @@ def hidden_silverfish(self, blockid, data):
return img
# 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):
if data == 0: # normal
t = self.load_image_texture("assets/minecraft/textures/block/stone_bricks.png")
@ -3569,7 +3582,7 @@ def stone_brick(self, blockid, data):
return img
# 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):
# rotation
if self.rotation == 1:
@ -3653,7 +3666,7 @@ def huge_mushroom(self, blockid, data):
# iron bars and glass pane
# TODO glass pane is not a sprite, it has a texture for the side,
# 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):
# no rotation, uses pseudo data
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:
# once fully grown the stem bends to the melon/pumpkin block,
# 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):
# the ancildata value indicates how much of the texture
# is shown.
@ -3725,7 +3738,7 @@ def stem(self, blockid, data):
# vines
@material(blockid=106, data=range(16), transparent=True)
@material(blockid=106, data=list(range(16)), transparent=True)
def vines(self, blockid, data):
# rotation
# vines data is bit coded. decode it first.
@ -3766,7 +3779,7 @@ def vines(self, blockid, data):
return img
# 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):
# 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
# orientation depends on their position on the map. So it uses pseudo
# ancildata.
@material(blockid=111, data=range(4), transparent=True)
@material(blockid=111, data=list(range(4)), transparent=True)
def lilypad(self, blockid, data):
t = self.load_image_texture("assets/minecraft/textures/block/lily_pad.png").copy()
if data == 0:
@ -3888,7 +3901,7 @@ def lilypad(self, blockid, data):
block(blockid=112, top_image="assets/minecraft/textures/block/nether_bricks.png")
# 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):
if data == 0: # just come up
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
# 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):
base = self.load_image_texture("assets/minecraft/textures/block/brewing_stand_base.png")
img = self.build_full_block(None, None, None, None, None, base)
@ -3925,7 +3938,7 @@ def brewing_stand(self, blockid, data):
return img
# cauldron
@material(blockid=118, data=range(4), transparent=True)
@material(blockid=118, data=list(range(4)), transparent=True)
def cauldron(self, blockid, data):
side = self.load_image_texture("assets/minecraft/textures/block/cauldron_side.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
# 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):
# The bottom 2 bits are oritation info but seems there is no
# graphical difference between orientations
@ -4046,7 +4059,7 @@ def daylight_sensor(self, blockid, data):
# wooden double and normal slabs
# these are the new wooden slabs, blockids 43 44 still have wooden
# 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):
texture = data & 7
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")
# 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):
orientation = data & 3
# rotation
@ -4198,7 +4211,7 @@ def beacon(self, blockid, data):
# cobblestone and mossy cobblestone walls, chorus plants
# 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):
# chorus plants
if blockid == 199:
@ -4329,7 +4342,7 @@ def cobblestone_wall(self, blockid, data):
return img
# 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):
# carrots and potatoes have 8 data, but only 4 visual stages
stage = {0:0,
@ -4355,7 +4368,7 @@ def crops4(self, blockid, data):
return img
# anvils
@material(blockid=145, data=range(12), transparent=True)
@material(blockid=145, data=list(range(12)), transparent=True)
def anvil(self, blockid, data):
# 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 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):
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))
# hopper
@material(blockid=154, data=range(4), transparent=True)
@material(blockid=154, data=list(range(4)), transparent=True)
def hopper(self, blockid, data):
#build the top
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")
# 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):
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")
# hay block
@material(blockid=170, data=range(9), solid=True)
@material(blockid=170, data=list(range(9)), solid=True)
def hayblock(self, blockid, data):
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")
@ -4542,7 +4555,7 @@ def hayblock(self, blockid, data):
# 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):
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")
#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):
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=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):
double_plant_map = ["sunflower", "lilac", "tall_grass", "large_fern", "rose_bush", "peony", "peony", "peony"]
plant = double_plant_map[data & 0x7]
@ -4608,7 +4621,7 @@ def flower(self, blockid, data):
return img
# 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):
# aged 5, dead
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")
# 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):
pillar_orientation = data & 12
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")
# 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):
img = self.load_image_texture("assets/minecraft/textures/block/frosted_ice_%d.png" % data)
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
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):
# extract orientation
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)
# 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):
# first, do the rotation if needed
if self.rotation == 1:
@ -4718,7 +4731,7 @@ def observer(self, blockid, data):
return img
# 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):
# first, do the rotation if needed
data = data & 7
@ -4741,15 +4754,15 @@ def shulker_box(self, blockid, data):
color = color_map[blockid - 219]
shulker_t = self.load_image_texture("assets/minecraft/textures/entity/shulker/shulker_%s.png" % color).copy()
w,h = shulker_t.size
res = w / 4
res = w // 4
# Cut out the parts of the shulker texture we need for the box
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)))
side_top = shulker_t.crop((0,res,res,int(res*1.75)))
side_bottom = shulker_t.crop((0,int(res*2.75),res,int(res*3.25)))
side = Image.new('RGBA', (res,res))
side.paste(side_top, (0,0), side_top)
side.paste(side_bottom, (0,res/2), side_bottom)
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)))
side_top = shulker_t.crop((0, res, res, int(res * 1.75)))
side_bottom = shulker_t.crop((0, int(res * 2.75), res, int(res * 3.25)))
side = Image.new('RGBA', (res, res))
side.paste(side_top, (0, 0), side_top)
side.paste(side_bottom, (0, res // 2), side_bottom)
if data == 0: # down
side = side.rotate(180)
@ -4768,7 +4781,7 @@ def shulker_box(self, blockid, data):
return img
# 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):
if data == 0:
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)
# 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):
raw_crop = self.load_image_texture("assets/minecraft/textures/block/beetroots_stage%d.png" % data)
crop1 = self.transform_image_top(raw_crop)
@ -4795,19 +4808,19 @@ def crops(self, blockid, data):
return img
# Concrete
@material(blockid=251, data=range(16), solid=True)
@material(blockid=251, data=list(range(16)), solid=True)
def concrete(self, blockid, data):
texture = self.load_image_texture("assets/minecraft/textures/block/%s_concrete.png" % color_map[data])
return self.build_block(texture, texture)
# Concrete Powder
@material(blockid=252, data=range(16), solid=True)
@material(blockid=252, data=list(range(16)), solid=True)
def concrete(self, blockid, data):
texture = self.load_image_texture("assets/minecraft/textures/block/%s_concrete_powder.png" % color_map[data])
return self.build_block(texture, texture)
# 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):
texture = self.load_image_texture("assets/minecraft/textures/block/%s_glazed_terracotta.png" % color_map[blockid - 235])
glazed_terracotta_orientation = data & 3

View File

@ -26,13 +26,13 @@ import stat
import sys
import time
from collections import namedtuple
from itertools import chain, izip, product
from itertools import chain, product
from PIL import Image
import c_overviewer
import rendermodes
from c_overviewer import resize_half
from . import c_overviewer
from . import rendermodes
from .c_overviewer import resize_half
from . import nbt, world
from .files import FileReplacer, get_fs_caps
@ -95,7 +95,7 @@ do_work(workobj)
# small but useful
def iterate_base4(d):
"""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
@ -646,7 +646,7 @@ class TileSet(object):
bounds = self._find_chunk_range()
# 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?
# 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:
logging.warning("Your map seems to have expanded beyond its previous bounds.")
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()
elif self.treedepth < curdepth:
logging.warning(
"Your map seems to have shrunk. Did you delete some "
"chunks? No problem. Re-arranging tiles, just a sec...")
for _ in xrange(curdepth - self.treedepth):
for _ in range(curdepth - self.treedepth):
self._decrease_depth()
logging.info(
"There, done. I'm switching to --check-tiles mode for "
@ -798,12 +798,12 @@ class TileSet(object):
os.rename(getpath("new3"), getpath("3"))
# 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]
for f in files:
try:
os.unlink(getpath(f))
except OSError, e:
except OSError as e:
# Ignore file doesn't exist errors
if e.errno != errno.ENOENT:
raise
@ -964,7 +964,7 @@ class TileSet(object):
if not quadPath_filtered:
try:
os.unlink(imgpath)
except OSError, e:
except OSError as e:
# Ignore errors if it's "file doesn't exist"
if e.errno != errno.ENOENT:
raise
@ -988,14 +988,14 @@ class TileSet(object):
quad = Image.new("RGBA", (192, 192), self.options['bgcolor'])
resize_half(quad, src)
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(
"I'm going to try and delete it. You will need to run "
"the render again and with --check-tiles.")
try:
os.unlink(path[1])
except Exception, e:
except Exception as e:
logging.error(
"While attempting to delete corrupt image %s, an error was encountered. "
"You will need to delete it yourself. Error was '%s'", path[1], e)
@ -1016,7 +1016,7 @@ class TileSet(object):
try:
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
# condition, two processes could conceivably try and update
# the same temp file at the same time
@ -1049,7 +1049,7 @@ class TileSet(object):
"This may be a bug.", tile)
try:
os.unlink(imgpath)
except OSError, e:
except OSError as e:
# ignore only if the error was "file not found"
if e.errno != errno.ENOENT:
raise
@ -1062,7 +1062,7 @@ class TileSet(object):
if not os.path.exists(dirdest):
try:
os.makedirs(dirdest)
except OSError, e:
except OSError as e:
# Ignore errno EEXIST: file exists. Due to a race condition,
# two processes could conceivably try and create the same
# directory at the same time
@ -1098,7 +1098,7 @@ class TileSet(object):
# Some chunks are present on disk but not fully initialized.
# This is okay.
pass
except Exception, e:
except Exception as e:
logging.error("Could not render chunk %s,%s for some reason. "
"This is likely a render primitive option error.", chunkx, chunkz)
logging.error("Full error was:", exc_info=1)
@ -1154,7 +1154,7 @@ class TileSet(object):
imgpath = tileobj.get_filepath(self.outputdir, self.imgextension)
try:
tile_mtime = os.stat(imgpath)[stat.ST_MTIME]
except OSError, e:
except OSError as e:
if e.errno != errno.ENOENT:
raise
tile_mtime = 0
@ -1198,7 +1198,7 @@ class TileSet(object):
max_child_mtime = 0
# First, recurse to each of our children
for childnum in xrange(4):
for childnum in range(4):
childpath = path + (childnum,)
# 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)
try:
tile_mtime = os.stat(imgpath)[stat.ST_MTIME]
except OSError, e:
except OSError as e:
if e.errno != errno.ENOENT:
raise
tile_mtime = 0
@ -1296,7 +1296,7 @@ def unconvert_coords(col, row):
# col + row = chunkz + chunkz => (col + row)/2 = chunkz
# 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
# rows)
if chunkrow % 4 == 0:
rowrange = xrange(tilerow - 4, tilerow + 32 + 1, 4)
rowrange = range(tilerow - 4, tilerow + 32 + 1, 4)
else:
rowrange = xrange(tilerow, tilerow + 32 + 1, 4)
rowrange = range(tilerow, tilerow + 32 + 1, 4)
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
# "passes through" three chunk sections.
oddcol_sections = []
for i, y in enumerate(reversed(xrange(16))):
for row in xrange(tile.row + 3 - i * 2, tile.row - 2 - i * 2, -2):
for i, y in enumerate(reversed(range(16))):
for row in range(tile.row + 3 - i * 2, tile.row - 2 - i * 2, -2):
oddcol_sections.append((tile.col + 1, row, y))
evencol_sections = []
for i, y in enumerate(reversed(xrange(16))):
for row in xrange(tile.row + 4 - i * 2, tile.row - 3 - i * 2, -2):
for i, y in enumerate(reversed(range(16))):
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, row, y))
@ -1589,7 +1589,7 @@ class RendertileSet(object):
# or more of the children down the tree are True.
return True
def __nonzero__(self):
def __bool__(self):
"""Returns the boolean context of this particular node. If any
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.
# Calculate the number of tiles by iteration and emit a warning if it
# 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:
logging.error("Please report this to the developers: RendertileSet num_tiles=%r, "
"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.
# Calculate the number of tiles by iteration and emit a warning if it
# 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:
logging.error("Please report this to the developers: RendertileSet num_tiles_all=%r, "
"count_all=%r, children=%r", self.num_tiles, num, self.children)
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 = []
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
y = off_y * 2 + dy
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):
@ -1733,7 +1734,7 @@ class RenderTile(object):
path = []
for level in xrange(depth):
for level in range(depth):
# Strategy: Find the midpoint of this level, and determine which
# quadrant this row/col is in. Then set the bounds to that level
# and repeat

View File

@ -48,7 +48,7 @@ def findGitHash():
return line
except Exception:
try:
import overviewer_version
from . import overviewer_version
return overviewer_version.HASH
except Exception:
pass
@ -77,7 +77,7 @@ def findGitVersion():
return line
except Exception:
try:
import overviewer_version
from . import overviewer_version
return overviewer_version.VERSION
except Exception:
return "unknown"
@ -108,7 +108,7 @@ def nice_exit(ret=0):
if ret and is_bare_console():
print("")
print("Press [Enter] to close this window.")
raw_input()
input()
sys.exit(ret)
@ -117,7 +117,7 @@ def roundrobin(iterables):
"roundrobin('ABC', 'D', 'EF') --> A D E B F C"
# Recipe credited to George Sakkis
pending = len(iterables)
nexts = cycle(iter(it).next for it in iterables)
nexts = cycle(iter(it).__next__ for it in iterables)
while pending:
try:
for next in nexts:
@ -136,281 +136,13 @@ def dict_subset(d, keys):
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
"""Check whether pid exists in the current process table."""
if pid < 0:
return False
try:
os.kill(pid, 0)
except OSError, e:
except OSError as e:
return e.errno != errno.ESRCH
else:
return True

View File

@ -53,7 +53,7 @@ def log_other_exceptions(func):
return func(*args)
except ChunkDoesntExist:
raise
except Exception, e:
except Exception as e:
logging.exception("%s raised this exception", func.func_name)
raise
return newfunc
@ -1139,7 +1139,7 @@ class RegionSet(object):
try:
region = self._get_regionobj(regionfile)
data = region.load_chunk(x, z)
except nbt.CorruptionError, e:
except nbt.CorruptionError as e:
tries -= 1
if tries > 0:
# 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:
mcr = self._get_regionobj(regionfile)
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! """
if (filemtime < mtime):
continue
@ -1561,7 +1561,6 @@ def get_worlds():
"Returns {world # or name : level.dat information}"
ret = {}
save_dir = get_save_dir()
loc = locale.getpreferredencoding()
# No dirs found - most likely not running from inside minecraft-dir
if not save_dir is None:
@ -1571,14 +1570,15 @@ def get_worlds():
if not os.path.exists(world_dat): continue
try:
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():
ret[info['Data']['LevelName']] = info['Data']
except nbt.CorruptNBTError:
ret[os.path.basename(world_path).decode(loc) + " (corrupt)"] = {'path': world_path.decode(loc),
'LastPlayed': 0,
'Time': 0,
'IsCorrupt': True}
ret[os.path.basename(world_path) + " (corrupt)"] = {
'path': world_path,
'LastPlayed': 0,
'Time': 0,
'IsCorrupt': True}
for dir in os.listdir("."):

View File

@ -1,15 +1,15 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
import sys
import traceback
# quick version check
if not (sys.version_info[0] == 2 and sys.version_info[1] >= 6):
print("Sorry, the Overviewer requires at least Python 2.6 to run")
if sys.version_info[0] >= 3:
print("and will not run on Python 3.0 or later")
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 3.4 to run.")
sys.exit(1)
from distutils.core import setup
from distutils.extension import Extension
from distutils.command.build import build
@ -176,12 +176,12 @@ for name in glob.glob("overviewer_core/src/primitives/*.c"):
primitives.append(name)
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_includes = ['overviewer.h', 'rendermodes.h']
c_overviewer_files = map(lambda s: 'overviewer_core/src/'+s, c_overviewer_files)
c_overviewer_includes = map(lambda s: 'overviewer_core/src/'+s, c_overviewer_includes)
c_overviewer_files = ['overviewer_core/src/' + s for s in c_overviewer_files]
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=[]))

View File

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

View File

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

View File

@ -1,6 +1,6 @@
import unittest
from itertools import chain, izip
from itertools import chain
from overviewer_core.tileset import iterate_base4, RendertileSet
from overviewer_core.util import roundrobin
@ -150,7 +150,7 @@ class RendertileSetTest(unittest.TestCase):
self.assertRaises(AssertionError, self.test_iterate)
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):
"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"""
# Expect the results in this proper order.
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.assertRaises(StopIteration, next, iterator)
@ -211,7 +211,7 @@ class RendertileSetTest(unittest.TestCase):
"""Test a round-robin post-traversal of the tree's dirty tiles"""
# Expect the results in this proper order.
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.assertRaises(StopIteration, next, iterator)

View File

@ -1,4 +1,5 @@
import unittest
from collections import OrderedDict
from overviewer_core import configParser
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 rendermodes
from overviewer_core.util import OrderedDict
class SettingsTest(unittest.TestCase):
@ -23,12 +23,12 @@ class SettingsTest(unittest.TestCase):
# no exceptions so far. that's a good thing
# 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
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)
def test_rendermode_validation(self):
@ -63,7 +63,7 @@ class SettingsTest(unittest.TestCase):
}),
]))
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):
self.s.set_config_item("worlds", {
@ -79,7 +79,7 @@ class SettingsTest(unittest.TestCase):
},
})
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__":
unittest.main()

View File

@ -50,11 +50,11 @@ class FakeRegionset(object):
return NotImplementedError()
def iterate_chunks(self):
for (x,z),mtime in self.chunks.iteritems():
for (x,z),mtime in self.chunks.items():
yield x,z,mtime
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
def get_chunk_mtime(self, x, z):
@ -77,7 +77,7 @@ def get_tile_set(chunks):
the compare_iterate_to_expected() method.
"""
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)
@ -86,9 +86,9 @@ def get_tile_set(chunks):
tile_set[tile.path] = max(tile_set[tile.path], chunkmtime)
# 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
for i in reversed(xrange(5)):
for i in reversed(range(5)):
tile_set[tile[:i]] = max(tile_set[tile[:i]], tile_mtime)
return dict(tile_set)
@ -98,7 +98,7 @@ def create_fakedir(outputdir, tiles):
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]))
if len(tilepath) == 0:
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))
# 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))
def test_get_phase_length(self):
@ -215,7 +215,7 @@ class TilesetTest(unittest.TestCase):
"""Same as above but with a different set of chunks
"""
# Pick 3 random chunks to update
chunks = self.rs.chunks.keys()
chunks = list(self.rs.chunks.keys())
self.r.shuffle(chunks)
updated_chunks = {}
for key in chunks[:3]:

View File

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