2019-03-16 19:43:25 +00:00
|
|
|
#!/usr/bin/env python3
|
2010-09-04 23:22:32 +00:00
|
|
|
|
2010-09-22 02:51:12 +00:00
|
|
|
# This file is part of the Minecraft Overviewer.
|
|
|
|
#
|
|
|
|
# Minecraft Overviewer is free software: you can redistribute it and/or
|
|
|
|
# modify it under the terms of the GNU General Public License as published
|
|
|
|
# by the Free Software Foundation, either version 3 of the License, or (at
|
|
|
|
# your option) any later version.
|
|
|
|
#
|
|
|
|
# Minecraft Overviewer is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
|
|
# Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License along
|
|
|
|
# with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
2019-03-14 15:03:35 +00:00
|
|
|
from __future__ import print_function
|
|
|
|
|
2011-10-27 02:34:34 +00:00
|
|
|
import platform
|
2010-09-27 03:04:12 +00:00
|
|
|
import sys
|
2011-10-27 02:34:34 +00:00
|
|
|
|
2012-01-14 06:11:05 +00:00
|
|
|
# quick version check
|
2019-03-16 19:43:25 +00:00
|
|
|
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.")
|
2011-11-25 16:30:56 +00:00
|
|
|
sys.exit(1)
|
|
|
|
|
2010-09-04 23:22:32 +00:00
|
|
|
import os
|
|
|
|
import os.path
|
|
|
|
import re
|
2011-03-20 00:45:40 +00:00
|
|
|
import subprocess
|
2010-09-15 03:51:05 +00:00
|
|
|
import multiprocessing
|
2010-09-21 20:07:23 +00:00
|
|
|
import time
|
2010-09-30 04:35:37 +00:00
|
|
|
import logging
|
2019-03-08 15:48:31 +00:00
|
|
|
from argparse import ArgumentParser
|
2019-03-16 19:43:25 +00:00
|
|
|
from collections import OrderedDict
|
2011-03-19 22:53:29 +00:00
|
|
|
|
2012-01-14 06:11:05 +00:00
|
|
|
from overviewer_core import util
|
2012-03-05 17:12:27 +00:00
|
|
|
from overviewer_core import logger
|
2011-09-10 02:40:42 +00:00
|
|
|
from overviewer_core import textures
|
2012-01-02 04:58:18 +00:00
|
|
|
from overviewer_core import optimizeimages, world
|
2019-07-24 07:18:02 +00:00
|
|
|
from overviewer_core import config_parser, tileset, assetmanager, dispatcher
|
2012-03-04 04:56:38 +00:00
|
|
|
from overviewer_core import cache
|
2012-03-17 18:34:57 +00:00
|
|
|
from overviewer_core import observer
|
2016-12-19 15:50:01 +00:00
|
|
|
from overviewer_core.nbt import CorruptNBTError
|
2011-03-31 01:29:10 +00:00
|
|
|
|
2010-09-04 23:22:32 +00:00
|
|
|
helptext = """
|
2019-03-08 15:48:31 +00:00
|
|
|
%(prog)s [--rendermodes=...] [options] <World> <Output Dir>
|
|
|
|
%(prog)s --config=<config file> [options]"""
|
2011-03-25 01:58:25 +00:00
|
|
|
|
2019-03-06 13:27:04 +00:00
|
|
|
|
2010-09-04 23:22:32 +00:00
|
|
|
def main():
|
2011-11-04 00:41:02 +00:00
|
|
|
# bootstrap the logger with defaults
|
2012-03-05 17:12:27 +00:00
|
|
|
logger.configure()
|
2011-10-31 23:01:39 +00:00
|
|
|
|
2014-10-18 15:44:59 +00:00
|
|
|
if os.name == "posix":
|
|
|
|
if os.geteuid() == 0:
|
|
|
|
logging.warning("You are running Overviewer as root. "
|
|
|
|
"It is recommended that you never do this, "
|
|
|
|
"as it is dangerous for your system. If you are running "
|
|
|
|
"into permission errors, fix your file/directory "
|
|
|
|
"permissions instead. Overviewer does not need access to "
|
|
|
|
"critical system resources and therefore does not require "
|
|
|
|
"root access.")
|
2019-05-20 13:21:07 +00:00
|
|
|
try:
|
|
|
|
with open("/etc/redhat-release", "r") as release_f:
|
|
|
|
rel_contents = release_f.read()
|
|
|
|
try:
|
|
|
|
major_rel = re.search(r'\d(\.\d+)?', rel_contents).group(0).split('.')[0]
|
|
|
|
if major_rel == "6":
|
|
|
|
logging.warning(
|
|
|
|
"We will be dropping support for this release of your distribution "
|
|
|
|
"soon. Please upgrade as soon as possible, or you will not receive "
|
|
|
|
"future Overviewer updates.")
|
|
|
|
except AttributeError:
|
|
|
|
pass
|
|
|
|
except IOError:
|
|
|
|
pass
|
2014-10-18 15:44:59 +00:00
|
|
|
|
2010-09-15 03:51:05 +00:00
|
|
|
try:
|
|
|
|
cpus = multiprocessing.cpu_count()
|
|
|
|
except NotImplementedError:
|
|
|
|
cpus = 1
|
2012-03-16 17:25:03 +00:00
|
|
|
|
2011-08-17 22:22:50 +00:00
|
|
|
avail_north_dirs = ['lower-left', 'upper-left', 'upper-right', 'lower-right', 'auto']
|
2012-02-12 20:30:05 +00:00
|
|
|
|
2012-02-04 02:13:22 +00:00
|
|
|
# Parse for basic options
|
2019-03-08 15:48:31 +00:00
|
|
|
parser = ArgumentParser(usage=helptext)
|
|
|
|
parser.add_argument("-c", "--config", dest="config", action="store",
|
|
|
|
help="Specify the config file to use.")
|
|
|
|
parser.add_argument("-p", "--processes", dest="procs", action="store", type=int,
|
|
|
|
help="The number of local worker processes to spawn. Defaults to the "
|
|
|
|
"number of CPU cores your computer has.")
|
|
|
|
|
|
|
|
parser.add_argument("--pid", dest="pid", action="store", help="Specify the pid file to use.")
|
2012-02-12 20:30:05 +00:00
|
|
|
# Options that only apply to the config-less render usage
|
2019-03-08 15:48:31 +00:00
|
|
|
parser.add_argument("--rendermodes", dest="rendermodes", action="store",
|
|
|
|
help="If you're not using a config file, specify which rendermodes to "
|
|
|
|
"render with this option. This is a comma-separated list.")
|
|
|
|
parser.add_argument("world", nargs='?',
|
|
|
|
help="Path or name of the world you want to render.")
|
|
|
|
parser.add_argument("output", nargs='?',
|
|
|
|
help="Output directory for the rendered map.")
|
2012-03-16 17:25:03 +00:00
|
|
|
|
2012-02-04 02:13:22 +00:00
|
|
|
# Useful one-time render modifiers:
|
2019-03-08 15:48:31 +00:00
|
|
|
render_modifiers = parser.add_mutually_exclusive_group()
|
|
|
|
render_modifiers.add_argument("--forcerender", dest="forcerender", action="store_true",
|
|
|
|
help="Force re-render the entire map.")
|
|
|
|
render_modifiers.add_argument("--check-tiles", dest="checktiles", action="store_true",
|
|
|
|
help="Check each tile on disk and re-render old tiles.")
|
|
|
|
render_modifiers.add_argument("--no-tile-checks", dest="notilechecks", action="store_true",
|
|
|
|
help="Only render tiles that come from chunks that have changed "
|
|
|
|
"since the last render (the default).")
|
2012-02-04 02:13:22 +00:00
|
|
|
|
|
|
|
# Useful one-time debugging options:
|
2019-03-08 15:48:31 +00:00
|
|
|
parser.add_argument("--check-terrain", dest="check_terrain", action="store_true",
|
|
|
|
help="Try to locate the texture files. Useful for debugging texture"
|
|
|
|
" problems.")
|
|
|
|
parser.add_argument("-V", "--version", dest="version",
|
|
|
|
help="Display version information and then exits.", action="store_true")
|
|
|
|
parser.add_argument("--check-version", dest="checkversion",
|
|
|
|
help="Fetch information about the latest version of Overviewer.",
|
|
|
|
action="store_true")
|
|
|
|
parser.add_argument("--update-web-assets", dest='update_web_assets', action="store_true",
|
|
|
|
help="Update web assets. Will *not* render tiles or update "
|
|
|
|
"overviewerConfig.js.")
|
2012-02-04 02:13:22 +00:00
|
|
|
|
|
|
|
# Log level options:
|
2019-03-08 15:48:31 +00:00
|
|
|
parser.add_argument("-q", "--quiet", dest="quiet", action="count", default=0,
|
|
|
|
help="Print less output. You can specify this option multiple times.")
|
|
|
|
parser.add_argument("-v", "--verbose", dest="verbose", action="count", default=0,
|
|
|
|
help="Print more output. You can specify this option multiple times.")
|
|
|
|
parser.add_argument("--simple-output", dest="simple", action="store_true", default=False,
|
|
|
|
help="Use a simple output format, with no colors or progress bars.")
|
2010-09-04 23:22:32 +00:00
|
|
|
|
2019-03-06 13:27:04 +00:00
|
|
|
# create a group for "plugin exes"
|
|
|
|
# (the concept of a plugin exe is only loosely defined at this point)
|
2019-03-08 15:48:31 +00:00
|
|
|
exegroup = parser.add_argument_group("Other Scripts", "These scripts may accept different "
|
|
|
|
"arguments than the ones listed above.")
|
|
|
|
exegroup.add_argument("--genpoi", dest="genpoi", action="store_true",
|
|
|
|
help="Run the genPOI script.")
|
|
|
|
exegroup.add_argument("--skip-scan", dest="skipscan", action="store_true",
|
|
|
|
help="When running GenPOI, don't scan for entities.")
|
|
|
|
exegroup.add_argument("--skip-players", dest="skipplayers", action="store_true",
|
|
|
|
help="When running GenPOI, don't scan player data.")
|
2012-03-19 23:56:25 +00:00
|
|
|
|
2019-03-09 17:26:43 +00:00
|
|
|
args, unknowns = parser.parse_known_args()
|
|
|
|
|
|
|
|
# Check for possible shell quoting issues
|
2019-03-18 18:36:24 +00:00
|
|
|
if len(unknowns) > 0 and args.world and args.output:
|
2019-03-09 17:26:43 +00:00
|
|
|
possible_mistakes = []
|
2019-03-16 19:43:25 +00:00
|
|
|
for i in range(len(unknowns) + 1):
|
2019-03-09 17:26:43 +00:00
|
|
|
possible_mistakes.append(" ".join([args.world, args.output] + unknowns[:i]))
|
|
|
|
possible_mistakes.append(" ".join([args.output] + unknowns[:i]))
|
|
|
|
for mistake in possible_mistakes:
|
|
|
|
if os.path.exists(mistake):
|
|
|
|
logging.warning("Looks like you tried to make me use {0} as an argument, but "
|
|
|
|
"forgot to quote the argument correctly. Try using \"{0}\" "
|
|
|
|
"instead if the spaces are part of the path.".format(mistake))
|
|
|
|
parser.error("Too many arguments.")
|
|
|
|
parser.error("Too many arguments.")
|
2010-09-04 23:22:32 +00:00
|
|
|
|
2012-03-19 23:56:25 +00:00
|
|
|
# first thing to do is check for stuff in the exegroup:
|
2019-03-08 15:48:31 +00:00
|
|
|
if args.genpoi:
|
2012-03-19 23:56:25 +00:00
|
|
|
# remove the "--genpoi" option from sys.argv before running genPI
|
|
|
|
sys.argv.remove("--genpoi")
|
2012-05-03 00:25:23 +00:00
|
|
|
g = __import__("overviewer_core.aux_files", {}, {}, ["genPOI"])
|
|
|
|
g.genPOI.main()
|
2012-03-19 23:56:25 +00:00
|
|
|
return 0
|
|
|
|
|
2011-11-08 01:11:10 +00:00
|
|
|
# re-configure the logger now that we've processed the command line options
|
2019-03-08 15:48:31 +00:00
|
|
|
logger.configure(logging.INFO + 10 * args.quiet - 10 * args.verbose,
|
|
|
|
verbose=args.verbose > 0, simple=args.simple)
|
Added a new 'listify' parameter to the config file parser
Specifying listify on an option will cause it to be parsed as a list.
Use listdelim to specify the delimiter (defaults to ',')
Examples, assuming you had the following option:
add_option("--test","test", type="int", listify=True)
Command line:
--test 1 results in [1]
--test 1,2,3 results in [1,2,3]
Config file:
test=1 results in [1]
test="1,2,3" results in [1,2,3]
test=[1,4,9] results in [1,4,9]
2011-03-26 00:38:25 +00:00
|
|
|
|
2012-02-04 02:13:22 +00:00
|
|
|
##########################################################################
|
|
|
|
# This section of main() runs in response to any one-time options we have,
|
|
|
|
# such as -V for version reporting
|
2019-03-08 15:48:31 +00:00
|
|
|
if args.version:
|
2019-03-07 15:04:56 +00:00
|
|
|
print("Minecraft Overviewer %s" % util.findGitVersion() +
|
|
|
|
" (%s)" % util.findGitHash()[:7])
|
2011-03-25 01:58:25 +00:00
|
|
|
try:
|
2011-05-14 02:24:49 +00:00
|
|
|
import overviewer_core.overviewer_version as overviewer_version
|
2013-03-11 17:06:54 +00:00
|
|
|
print("built on %s" % overviewer_version.BUILD_DATE)
|
2019-03-08 15:48:31 +00:00
|
|
|
if args.verbose > 0:
|
2019-03-06 13:27:04 +00:00
|
|
|
print("Build machine: %s %s" % (overviewer_version.BUILD_PLATFORM,
|
|
|
|
overviewer_version.BUILD_OS))
|
|
|
|
print("Read version information from %r" % overviewer_version.__file__)
|
2011-12-17 08:20:38 +00:00
|
|
|
except ImportError:
|
2013-03-11 17:06:54 +00:00
|
|
|
print("(build info not found)")
|
2019-03-08 15:48:31 +00:00
|
|
|
if args.verbose > 0:
|
2013-12-14 23:40:05 +00:00
|
|
|
print("Python executable: %r" % sys.executable)
|
|
|
|
print(sys.version)
|
2019-03-08 15:48:31 +00:00
|
|
|
if not args.checkversion:
|
2014-05-03 04:23:10 +00:00
|
|
|
return 0
|
2019-03-08 15:48:31 +00:00
|
|
|
if args.checkversion:
|
2019-03-07 15:04:56 +00:00
|
|
|
print("Currently running Minecraft Overviewer %s" % util.findGitVersion() +
|
|
|
|
" (%s)" % util.findGitHash()[:7])
|
2014-05-03 04:23:10 +00:00
|
|
|
try:
|
2019-03-16 19:43:25 +00:00
|
|
|
from urllib import request
|
2014-05-03 04:23:10 +00:00
|
|
|
import json
|
2019-03-16 19:43:25 +00:00
|
|
|
latest_ver = json.loads(request.urlopen("http://overviewer.org/download.json")
|
2019-03-06 13:27:04 +00:00
|
|
|
.read())['src']
|
|
|
|
print("Latest version of Minecraft Overviewer %s (%s)" % (latest_ver['version'],
|
|
|
|
latest_ver['commit'][:7]))
|
|
|
|
print("See https://overviewer.org/downloads for more information.")
|
2014-05-03 04:23:10 +00:00
|
|
|
except Exception:
|
|
|
|
print("Failed to fetch latest version info.")
|
2019-03-08 15:48:31 +00:00
|
|
|
if args.verbose > 0:
|
2014-05-03 04:23:10 +00:00
|
|
|
import traceback
|
|
|
|
traceback.print_exc()
|
|
|
|
else:
|
2019-03-06 13:27:04 +00:00
|
|
|
print("Re-run with --verbose for more details.")
|
2014-05-03 04:23:10 +00:00
|
|
|
return 1
|
2012-01-14 06:11:05 +00:00
|
|
|
return 0
|
2011-03-25 01:58:25 +00:00
|
|
|
|
2019-03-08 15:48:31 +00:00
|
|
|
if args.pid:
|
|
|
|
if os.path.exists(args.pid):
|
2013-11-26 19:36:53 +00:00
|
|
|
try:
|
2019-03-08 15:48:31 +00:00
|
|
|
with open(args.pid, 'r') as fpid:
|
2013-11-26 19:36:53 +00:00
|
|
|
pid = int(fpid.read())
|
|
|
|
if util.pid_exists(pid):
|
2019-03-06 13:27:04 +00:00
|
|
|
print("Overviewer is already running (pid exists) - exiting.")
|
2013-11-26 19:36:53 +00:00
|
|
|
return 0
|
2014-01-06 13:47:03 +00:00
|
|
|
except (IOError, ValueError):
|
2013-11-26 19:36:53 +00:00
|
|
|
pass
|
2019-03-08 15:48:31 +00:00
|
|
|
with open(args.pid, "w") as f:
|
2013-11-21 04:52:55 +00:00
|
|
|
f.write(str(os.getpid()))
|
2013-03-17 16:12:12 +00:00
|
|
|
# if --check-terrain was specified, but we have NO config file, then we cannot
|
|
|
|
# operate on a custom texture path. we do terrain checking with a custom texture
|
|
|
|
# pack later on, after we've parsed the config file
|
2019-03-08 15:48:31 +00:00
|
|
|
if args.check_terrain and not args.config:
|
2011-09-06 02:35:13 +00:00
|
|
|
import hashlib
|
2012-01-14 06:11:05 +00:00
|
|
|
from overviewer_core.textures import Textures
|
|
|
|
tex = Textures()
|
2011-09-06 02:35:13 +00:00
|
|
|
|
2013-12-02 12:40:00 +00:00
|
|
|
logging.info("Looking for a few common texture files...")
|
2011-09-06 02:35:13 +00:00
|
|
|
try:
|
2018-08-02 02:25:10 +00:00
|
|
|
f = tex.find_file("assets/minecraft/textures/block/sandstone_top.png", verbose=True)
|
|
|
|
f = tex.find_file("assets/minecraft/textures/block/grass_block_top.png", verbose=True)
|
|
|
|
f = tex.find_file("assets/minecraft/textures/block/diamond_ore.png", verbose=True)
|
|
|
|
f = tex.find_file("assets/minecraft/textures/block/acacia_planks.png", verbose=True)
|
2020-06-25 20:49:06 +00:00
|
|
|
# 1.16
|
|
|
|
f = tex.find_file("assets/minecraft/textures/block/ancient_debris_top.png",
|
|
|
|
verbose=True)
|
2011-09-06 02:35:13 +00:00
|
|
|
except IOError:
|
2013-12-02 12:40:00 +00:00
|
|
|
logging.error("Could not find any texture files.")
|
2012-01-14 06:11:05 +00:00
|
|
|
return 1
|
2011-09-06 02:35:13 +00:00
|
|
|
|
2012-01-14 06:11:05 +00:00
|
|
|
return 0
|
|
|
|
|
|
|
|
# if no arguments are provided, print out a helpful message
|
2019-03-08 15:48:31 +00:00
|
|
|
if not (args.world and args.output) and not args.config:
|
2012-01-14 06:11:05 +00:00
|
|
|
# first provide an appropriate error for bare-console users
|
|
|
|
# that don't provide any options
|
2012-03-15 08:03:46 +00:00
|
|
|
if util.is_bare_console():
|
2013-03-11 17:06:54 +00:00
|
|
|
print("\n")
|
|
|
|
print("The Overviewer is a console program. Please open a Windows command prompt")
|
|
|
|
print("first and run Overviewer from there. Further documentation is available at")
|
|
|
|
print("http://docs.overviewer.org/\n")
|
|
|
|
print("\n")
|
|
|
|
print("For a quick-start guide on Windows, visit the following URL:\n")
|
|
|
|
print("http://docs.overviewer.org/en/latest/win_tut/windowsguide/\n")
|
2012-10-18 23:48:34 +00:00
|
|
|
|
2012-01-14 06:11:05 +00:00
|
|
|
else:
|
|
|
|
# more helpful message for users who know what they're doing
|
2019-03-06 13:27:04 +00:00
|
|
|
logging.error("You must either specify --config or give me a world directory "
|
|
|
|
"and output directory.")
|
2012-01-14 06:11:05 +00:00
|
|
|
parser.print_help()
|
|
|
|
list_worlds()
|
|
|
|
return 1
|
2012-03-16 17:25:03 +00:00
|
|
|
|
2012-02-04 02:13:22 +00:00
|
|
|
##########################################################################
|
|
|
|
# This section does some sanity checking on the command line options passed
|
|
|
|
# in. It checks to see if --config was given that no worldname/destdir were
|
|
|
|
# given, and vice versa
|
2019-03-08 15:48:31 +00:00
|
|
|
if args.config and (args.world and args.output):
|
2013-03-11 17:06:54 +00:00
|
|
|
print()
|
2019-03-06 13:27:04 +00:00
|
|
|
print("If you specify --config, you need to specify the world to render as well as "
|
|
|
|
"the destination in the config file, not on the command line.")
|
2013-03-11 17:06:54 +00:00
|
|
|
print("Put something like this in your config file:")
|
|
|
|
print("worlds['myworld'] = %r" % args[0])
|
|
|
|
print("outputdir = %r" % (args[1] if len(args) > 1 else "/path/to/output"))
|
|
|
|
print()
|
2019-03-06 13:27:04 +00:00
|
|
|
logging.error("You cannot specify both --config AND a world + output directory on the "
|
|
|
|
"command line.")
|
2012-02-04 02:13:22 +00:00
|
|
|
parser.print_help()
|
|
|
|
return 1
|
2012-03-16 17:25:03 +00:00
|
|
|
|
2019-03-08 15:48:31 +00:00
|
|
|
if not args.config and (args.world or args.output) and not (args.world and args.output):
|
2013-12-02 12:40:00 +00:00
|
|
|
logging.error("You must specify both the world directory and an output directory")
|
2012-02-04 02:13:22 +00:00
|
|
|
parser.print_help()
|
|
|
|
return 1
|
2010-09-04 23:22:32 +00:00
|
|
|
|
2012-02-04 02:13:22 +00:00
|
|
|
#########################################################################
|
2012-02-05 02:23:44 +00:00
|
|
|
# These two halfs of this if statement unify config-file mode and
|
|
|
|
# command-line mode.
|
2019-07-24 07:18:02 +00:00
|
|
|
mw_parser = config_parser.MultiWorldParser()
|
2012-02-05 02:23:44 +00:00
|
|
|
|
2019-03-08 15:48:31 +00:00
|
|
|
if not args.config:
|
2012-02-04 02:13:22 +00:00
|
|
|
# No config file mode.
|
2019-03-08 15:48:31 +00:00
|
|
|
worldpath, destdir = map(os.path.expanduser, [args.world, args.output])
|
2013-12-02 12:40:00 +00:00
|
|
|
logging.debug("Using %r as the world directory", worldpath)
|
|
|
|
logging.debug("Using %r as the output directory", destdir)
|
2012-03-16 17:25:03 +00:00
|
|
|
|
2012-02-12 18:45:43 +00:00
|
|
|
mw_parser.set_config_item("worlds", {'world': worldpath})
|
2012-02-05 02:23:44 +00:00
|
|
|
mw_parser.set_config_item("outputdir", destdir)
|
2012-02-12 20:30:05 +00:00
|
|
|
|
|
|
|
rendermodes = ['lighting']
|
2019-03-08 15:48:31 +00:00
|
|
|
if args.rendermodes:
|
|
|
|
rendermodes = args.rendermodes.replace("-", "_").split(",")
|
2012-02-12 20:30:05 +00:00
|
|
|
|
2012-02-05 02:23:44 +00:00
|
|
|
# Now for some good defaults
|
2019-03-16 19:43:25 +00:00
|
|
|
renders = OrderedDict()
|
2012-02-12 20:30:05 +00:00
|
|
|
for rm in rendermodes:
|
|
|
|
renders["world-" + rm] = {
|
2019-03-06 13:27:04 +00:00
|
|
|
"world": "world",
|
|
|
|
"title": "Overviewer Render (%s)" % rm,
|
|
|
|
"rendermode": rm,
|
|
|
|
}
|
2012-02-12 20:35:37 +00:00
|
|
|
mw_parser.set_config_item("renders", renders)
|
2011-09-17 10:37:04 +00:00
|
|
|
|
2012-02-04 02:13:22 +00:00
|
|
|
else:
|
2019-03-08 15:48:31 +00:00
|
|
|
if args.rendermodes:
|
2019-03-06 13:27:04 +00:00
|
|
|
logging.error("You cannot specify --rendermodes if you give a config file. "
|
|
|
|
"Configure your rendermodes in the config file instead.")
|
2012-02-12 20:30:05 +00:00
|
|
|
parser.print_help()
|
|
|
|
return 1
|
|
|
|
|
2012-02-04 02:13:22 +00:00
|
|
|
# Parse the config file
|
2013-03-16 00:05:30 +00:00
|
|
|
try:
|
2019-03-08 15:48:31 +00:00
|
|
|
mw_parser.parse(os.path.expanduser(args.config))
|
2019-07-24 07:18:02 +00:00
|
|
|
except config_parser.MissingConfigException as e:
|
2013-03-16 00:05:30 +00:00
|
|
|
# this isn't a "bug", so don't print scary traceback
|
2013-12-02 12:40:00 +00:00
|
|
|
logging.error(str(e))
|
2013-03-16 00:05:30 +00:00
|
|
|
util.nice_exit(1)
|
2012-02-04 02:13:22 +00:00
|
|
|
|
2012-02-09 02:05:02 +00:00
|
|
|
# Add in the command options here, perhaps overriding values specified in
|
|
|
|
# the config
|
2019-03-08 15:48:31 +00:00
|
|
|
if args.procs:
|
|
|
|
mw_parser.set_config_item("processes", args.procs)
|
2012-02-09 02:05:02 +00:00
|
|
|
|
|
|
|
# Now parse and return the validated config
|
2012-02-05 02:23:44 +00:00
|
|
|
try:
|
|
|
|
config = mw_parser.get_validated_config()
|
2012-05-02 00:38:14 +00:00
|
|
|
except Exception as ex:
|
2019-03-08 15:48:31 +00:00
|
|
|
if args.verbose:
|
2019-03-06 13:27:04 +00:00
|
|
|
logging.exception("An error was encountered with your configuration. "
|
|
|
|
"See the information below.")
|
|
|
|
else: # no need to print scary traceback!
|
2013-12-02 12:40:00 +00:00
|
|
|
logging.error("An error was encountered with your configuration.")
|
|
|
|
logging.error(str(ex))
|
2012-02-05 02:23:44 +00:00
|
|
|
return 1
|
2012-02-04 02:13:22 +00:00
|
|
|
|
2019-03-08 15:48:31 +00:00
|
|
|
if args.check_terrain: # we are already in the "if configfile" branch
|
2013-12-02 12:40:00 +00:00
|
|
|
logging.info("Looking for a few common texture files...")
|
2019-03-16 19:43:25 +00:00
|
|
|
for render_name, render in config['renders'].items():
|
2019-03-06 13:27:04 +00:00
|
|
|
logging.info("Looking at render %r.", render_name)
|
2012-03-16 17:25:03 +00:00
|
|
|
|
2013-03-17 16:12:12 +00:00
|
|
|
# find or create the textures object
|
|
|
|
texopts = util.dict_subset(render, ["texturepath"])
|
|
|
|
|
|
|
|
tex = textures.Textures(**texopts)
|
2018-08-02 02:25:10 +00:00
|
|
|
f = tex.find_file("assets/minecraft/textures/block/sandstone_top.png", verbose=True)
|
|
|
|
f = tex.find_file("assets/minecraft/textures/block/grass_block_top.png", verbose=True)
|
|
|
|
f = tex.find_file("assets/minecraft/textures/block/diamond_ore.png", verbose=True)
|
|
|
|
f = tex.find_file("assets/minecraft/textures/block/oak_planks.png", verbose=True)
|
2013-03-17 16:12:12 +00:00
|
|
|
return 0
|
2012-02-04 02:13:22 +00:00
|
|
|
|
|
|
|
############################################################
|
2012-02-05 02:23:44 +00:00
|
|
|
# Final validation steps and creation of the destination directory
|
2020-06-27 17:47:33 +00:00
|
|
|
logging.info("Welcome to Minecraft Overviewer version %s (%s)!" % (util.findGitVersion(), util.findGitHash()[:7]))
|
2019-03-06 13:27:04 +00:00
|
|
|
logging.debug("Current log level: {0}.".format(logging.getLogger().level))
|
2012-02-16 16:36:31 +00:00
|
|
|
|
2014-05-01 20:48:28 +00:00
|
|
|
def set_renderchecks(checkname, num):
|
2019-03-16 19:43:25 +00:00
|
|
|
for name, render in config['renders'].items():
|
2014-05-01 20:48:28 +00:00
|
|
|
if render.get('renderchecks', 0) == 3:
|
2019-03-06 13:27:04 +00:00
|
|
|
logging.warning(checkname + " ignoring render " + repr(name) + " since it's "
|
|
|
|
"marked as \"don't render\".")
|
2014-05-01 20:48:28 +00:00
|
|
|
else:
|
|
|
|
render['renderchecks'] = num
|
2019-03-06 13:27:04 +00:00
|
|
|
|
2019-03-08 15:48:31 +00:00
|
|
|
if args.forcerender:
|
2019-03-06 13:27:04 +00:00
|
|
|
logging.info("Forcerender mode activated. ALL tiles will be rendered.")
|
2014-05-01 20:48:28 +00:00
|
|
|
set_renderchecks("forcerender", 2)
|
2019-03-08 15:48:31 +00:00
|
|
|
elif args.checktiles:
|
2013-12-02 12:40:00 +00:00
|
|
|
logging.info("Checking all tiles for updates manually.")
|
2014-05-01 20:48:28 +00:00
|
|
|
set_renderchecks("checktiles", 1)
|
2019-03-08 15:48:31 +00:00
|
|
|
elif args.notilechecks:
|
2019-03-06 13:27:04 +00:00
|
|
|
logging.info("Disabling all tile mtime checks. Only rendering tiles "
|
|
|
|
"that need updating since last render.")
|
2014-05-01 20:48:28 +00:00
|
|
|
set_renderchecks("notilechecks", 0)
|
2012-02-16 16:36:31 +00:00
|
|
|
|
2012-02-12 18:45:43 +00:00
|
|
|
if not config['renders']:
|
2019-03-06 13:27:04 +00:00
|
|
|
logging.error("You must specify at least one render in your config file. Check the "
|
|
|
|
"documentation at http://docs.overviewer.org if you're having trouble.")
|
2012-02-05 02:23:44 +00:00
|
|
|
return 1
|
|
|
|
|
2012-03-03 19:50:39 +00:00
|
|
|
#####################
|
|
|
|
# Do a few last minute things to each render dictionary here
|
2019-03-16 19:43:25 +00:00
|
|
|
for rname, render in config['renders'].items():
|
2012-02-15 01:39:05 +00:00
|
|
|
# Convert render['world'] to the world path, and store the original
|
2012-02-05 02:23:44 +00:00
|
|
|
# in render['worldname_orig']
|
|
|
|
try:
|
2012-02-15 01:39:05 +00:00
|
|
|
worldpath = config['worlds'][render['world']]
|
2012-02-05 02:23:44 +00:00
|
|
|
except KeyError:
|
2019-03-06 13:27:04 +00:00
|
|
|
logging.error("Render %s's world is '%s', but I could not find a corresponding entry "
|
|
|
|
"in the worlds dictionary.", rname, render['world'])
|
2012-02-05 02:23:44 +00:00
|
|
|
return 1
|
2012-02-15 01:39:05 +00:00
|
|
|
render['worldname_orig'] = render['world']
|
|
|
|
render['world'] = worldpath
|
2012-02-05 02:23:44 +00:00
|
|
|
|
2012-03-03 19:50:39 +00:00
|
|
|
# If 'forcerender' is set, change renderchecks to 2
|
|
|
|
if render.get('forcerender', False):
|
|
|
|
render['renderchecks'] = 2
|
|
|
|
|
2012-04-09 19:20:58 +00:00
|
|
|
# check if overlays are set, if so, make sure that those renders exist
|
|
|
|
if render.get('overlay', []) != []:
|
|
|
|
for x in render.get('overlay'):
|
|
|
|
if x != rname:
|
|
|
|
try:
|
|
|
|
renderLink = config['renders'][x]
|
|
|
|
except KeyError:
|
2019-03-06 13:27:04 +00:00
|
|
|
logging.error("Render %s's overlay is '%s', but I could not find a "
|
|
|
|
"corresponding entry in the renders dictionary.", rname, x)
|
2012-04-09 19:20:58 +00:00
|
|
|
return 1
|
|
|
|
else:
|
2013-12-02 12:40:00 +00:00
|
|
|
logging.error("Render %s's overlay contains itself.", rname)
|
2012-04-09 19:20:58 +00:00
|
|
|
return 1
|
|
|
|
|
2012-02-05 02:23:44 +00:00
|
|
|
destdir = config['outputdir']
|
2012-02-04 02:13:22 +00:00
|
|
|
if not destdir:
|
2013-12-02 12:40:00 +00:00
|
|
|
logging.error("You must specify the output directory in your config file.")
|
|
|
|
logging.error("e.g. outputdir = '/path/to/outputdir'")
|
2012-01-28 00:37:16 +00:00
|
|
|
return 1
|
2012-02-04 02:13:22 +00:00
|
|
|
if not os.path.exists(destdir):
|
|
|
|
try:
|
|
|
|
os.mkdir(destdir)
|
|
|
|
except OSError:
|
2013-12-02 12:40:00 +00:00
|
|
|
logging.exception("Could not create the output directory.")
|
2012-02-04 02:13:22 +00:00
|
|
|
return 1
|
2012-01-28 00:37:16 +00:00
|
|
|
|
2012-08-12 15:37:35 +00:00
|
|
|
########################################################################
|
|
|
|
# Now we start the actual processing, now that all the configuration has
|
|
|
|
# been gathered and validated
|
|
|
|
# create our asset manager... ASSMAN
|
|
|
|
assetMrg = assetmanager.AssetManager(destdir, config.get('customwebassets', None))
|
|
|
|
|
2019-03-06 13:27:04 +00:00
|
|
|
# If we've been asked to update web assets, do that and then exit
|
2019-03-08 15:48:31 +00:00
|
|
|
if args.update_web_assets:
|
2012-08-12 15:37:35 +00:00
|
|
|
assetMrg.output_noconfig()
|
2019-03-06 13:27:04 +00:00
|
|
|
logging.info("Web assets have been updated.")
|
2012-08-12 15:37:35 +00:00
|
|
|
return 0
|
|
|
|
|
2012-03-07 04:37:29 +00:00
|
|
|
# The changelist support.
|
|
|
|
changelists = {}
|
2019-03-16 19:43:25 +00:00
|
|
|
for render in config['renders'].values():
|
2012-03-07 04:37:29 +00:00
|
|
|
if 'changelist' in render:
|
|
|
|
path = render['changelist']
|
|
|
|
if path not in changelists:
|
|
|
|
out = open(path, "w")
|
2019-03-06 13:27:04 +00:00
|
|
|
logging.debug("Opening changelist %s (%s).", out, out.fileno())
|
2012-03-07 04:37:29 +00:00
|
|
|
changelists[path] = out
|
|
|
|
else:
|
|
|
|
out = changelists[path]
|
|
|
|
render['changelist'] = out.fileno()
|
|
|
|
|
2012-01-01 21:12:10 +00:00
|
|
|
tilesets = []
|
2012-01-05 05:27:22 +00:00
|
|
|
|
2012-01-22 00:04:21 +00:00
|
|
|
# saves us from creating the same World object over and over again
|
|
|
|
worldcache = {}
|
2012-01-29 22:18:21 +00:00
|
|
|
# same for textures
|
|
|
|
texcache = {}
|
2012-01-22 00:04:21 +00:00
|
|
|
|
2012-03-04 04:56:38 +00:00
|
|
|
# Set up the cache objects to use
|
|
|
|
caches = []
|
2012-03-04 23:46:02 +00:00
|
|
|
caches.append(cache.LRUCache(size=100))
|
2012-03-04 04:56:38 +00:00
|
|
|
# TODO: optionally more caching layers here
|
|
|
|
|
2012-02-12 18:45:43 +00:00
|
|
|
renders = config['renders']
|
2019-03-16 19:43:25 +00:00
|
|
|
for render_name, render in renders.items():
|
2013-12-02 12:40:00 +00:00
|
|
|
logging.debug("Found the following render thing: %r", render)
|
2012-01-01 21:12:10 +00:00
|
|
|
|
2012-01-29 22:18:21 +00:00
|
|
|
# find or create the world object
|
2012-03-04 04:56:38 +00:00
|
|
|
try:
|
|
|
|
w = worldcache[render['world']]
|
|
|
|
except KeyError:
|
2016-12-19 15:50:01 +00:00
|
|
|
try:
|
|
|
|
w = world.World(render['world'])
|
2019-03-07 15:02:14 +00:00
|
|
|
except CorruptNBTError as e:
|
2019-03-06 13:27:04 +00:00
|
|
|
logging.error("Failed to open world %r.", render['world'])
|
2016-12-19 15:50:01 +00:00
|
|
|
raise e
|
2019-03-07 15:02:14 +00:00
|
|
|
except world.UnsupportedVersion as e:
|
2018-05-09 12:25:05 +00:00
|
|
|
for ln in str(e).split('\n'):
|
|
|
|
logging.error(ln)
|
|
|
|
sys.exit(1)
|
2016-12-19 15:50:01 +00:00
|
|
|
|
2012-02-15 01:39:05 +00:00
|
|
|
worldcache[render['world']] = w
|
2012-03-16 17:25:03 +00:00
|
|
|
|
2012-01-29 22:18:21 +00:00
|
|
|
# find or create the textures object
|
|
|
|
texopts = util.dict_subset(render, ["texturepath", "bgcolor", "northdirection"])
|
|
|
|
texopts_key = tuple(texopts.items())
|
|
|
|
if texopts_key not in texcache:
|
|
|
|
tex = textures.Textures(**texopts)
|
2014-02-22 03:11:29 +00:00
|
|
|
logging.info("Generating textures...")
|
2012-01-29 22:18:21 +00:00
|
|
|
tex.generate()
|
2019-03-06 13:27:04 +00:00
|
|
|
logging.debug("Finished generating textures.")
|
2012-01-29 22:18:21 +00:00
|
|
|
texcache[texopts_key] = tex
|
|
|
|
else:
|
|
|
|
tex = texcache[texopts_key]
|
2019-03-06 13:27:04 +00:00
|
|
|
|
2013-03-16 21:48:25 +00:00
|
|
|
try:
|
2019-03-06 13:27:04 +00:00
|
|
|
logging.debug("Asking for regionset %r." % render['dimension'][1])
|
2013-03-16 21:48:25 +00:00
|
|
|
rset = w.get_regionset(render['dimension'][1])
|
|
|
|
except IndexError:
|
2019-03-06 13:27:04 +00:00
|
|
|
logging.error("Sorry, I can't find anything to render! Are you sure there are .mca "
|
2020-11-28 13:40:02 +00:00
|
|
|
"files in the world directory of %s?" % render['world'])
|
2013-03-16 21:48:25 +00:00
|
|
|
return 1
|
2019-03-06 13:27:04 +00:00
|
|
|
if rset is None: # indicates no such dimension was found
|
2019-12-11 17:42:35 +00:00
|
|
|
logging.warning("Sorry, you requested dimension '%s' for %s, but I couldn't find it.",
|
2019-03-06 13:27:04 +00:00
|
|
|
render['dimension'][0], render_name)
|
2015-02-05 18:52:31 +00:00
|
|
|
continue
|
2012-02-27 01:19:26 +00:00
|
|
|
|
2012-03-04 04:56:38 +00:00
|
|
|
#################
|
|
|
|
# Apply any regionset transformations here
|
|
|
|
|
|
|
|
# Insert a layer of caching above the real regionset. Any world
|
|
|
|
# tranformations will pull from this cache, but their results will not
|
|
|
|
# be cached by this layer. This uses a common pool of caches; each
|
|
|
|
# regionset cache pulls from the same underlying cache object.
|
|
|
|
rset = world.CachedRegionSet(rset, caches)
|
|
|
|
|
2014-05-10 03:44:46 +00:00
|
|
|
# If a crop is requested, wrap the regionset here
|
|
|
|
if "crop" in render:
|
|
|
|
rsets = []
|
|
|
|
for zone in render['crop']:
|
|
|
|
rsets.append(world.CroppedRegionSet(rset, *zone))
|
|
|
|
else:
|
|
|
|
rsets = [rset]
|
|
|
|
|
2016-03-05 03:09:10 +00:00
|
|
|
# If this is to be a rotated regionset, wrap it in a RotatedRegionSet
|
|
|
|
# object
|
|
|
|
if (render['northdirection'] > 0):
|
|
|
|
newrsets = []
|
|
|
|
for r in rsets:
|
|
|
|
r = world.RotatedRegionSet(r, render['northdirection'])
|
|
|
|
newrsets.append(r)
|
|
|
|
rsets = newrsets
|
|
|
|
|
2012-03-04 04:56:38 +00:00
|
|
|
###############################
|
|
|
|
# Do the final prep and create the TileSet object
|
|
|
|
|
2012-01-01 21:12:10 +00:00
|
|
|
# create our TileSet from this RegionSet
|
|
|
|
tileset_dir = os.path.abspath(os.path.join(destdir, render_name))
|
2012-01-17 01:35:13 +00:00
|
|
|
|
|
|
|
# only pass to the TileSet the options it really cares about
|
2019-03-06 13:27:04 +00:00
|
|
|
render['name'] = render_name # perhaps a hack. This is stored here for the asset manager
|
2019-03-04 16:04:09 +00:00
|
|
|
tileSetOpts = util.dict_subset(render, [
|
|
|
|
"name", "imgformat", "renderchecks", "rerenderprob", "bgcolor", "defaultzoom",
|
|
|
|
"imgquality", "imglossless", "optimizeimg", "rendermode", "worldname_orig", "title",
|
|
|
|
"dimension", "changelist", "showspawn", "overlay", "base", "poititle", "maxzoom",
|
2019-07-06 17:06:15 +00:00
|
|
|
"showlocationmarker", "minzoom", "center"])
|
2019-03-06 13:27:04 +00:00
|
|
|
tileSetOpts.update({"spawn": w.find_true_spawn()}) # TODO find a better way to do this
|
2014-05-10 03:44:46 +00:00
|
|
|
for rset in rsets:
|
|
|
|
tset = tileset.TileSet(w, rset, assetMrg, tex, tileSetOpts, tileset_dir)
|
|
|
|
tilesets.append(tset)
|
2012-01-01 21:12:10 +00:00
|
|
|
|
2015-02-08 02:32:24 +00:00
|
|
|
# If none of the requested dimenstions exist, tilesets will be empty
|
|
|
|
if not tilesets:
|
2019-03-06 13:27:04 +00:00
|
|
|
logging.error("There are no tilesets to render! There's nothing to do, so exiting.")
|
2015-02-08 02:32:24 +00:00
|
|
|
return 1
|
|
|
|
|
2012-02-26 00:33:35 +00:00
|
|
|
# Do tileset preprocessing here, before we start dispatching jobs
|
2014-02-22 03:11:29 +00:00
|
|
|
logging.info("Preprocessing...")
|
2012-02-26 00:33:35 +00:00
|
|
|
for ts in tilesets:
|
|
|
|
ts.do_preprocessing()
|
|
|
|
|
|
|
|
# Output initial static data and configuration
|
|
|
|
assetMrg.initialize(tilesets)
|
2012-03-16 17:25:03 +00:00
|
|
|
|
2012-01-07 02:56:26 +00:00
|
|
|
# multiprocessing dispatcher
|
2012-02-12 06:20:32 +00:00
|
|
|
if config['processes'] == 1:
|
|
|
|
dispatch = dispatcher.Dispatcher()
|
|
|
|
else:
|
2012-03-17 18:34:57 +00:00
|
|
|
dispatch = dispatcher.MultiprocessingDispatcher(
|
|
|
|
local_procs=config['processes'])
|
2012-03-22 21:32:28 +00:00
|
|
|
dispatch.render_all(tilesets, config['observer'])
|
2012-01-07 02:56:26 +00:00
|
|
|
dispatch.close()
|
2012-01-01 21:12:10 +00:00
|
|
|
|
2012-01-08 04:04:42 +00:00
|
|
|
assetMrg.finalize(tilesets)
|
2012-03-04 23:46:02 +00:00
|
|
|
|
2019-03-16 19:43:25 +00:00
|
|
|
for out in changelists.values():
|
2019-03-06 13:27:04 +00:00
|
|
|
logging.debug("Closing %s (%s).", out, out.fileno())
|
2012-03-07 04:37:29 +00:00
|
|
|
out.close()
|
|
|
|
|
2012-03-04 05:20:22 +00:00
|
|
|
if config['processes'] == 1:
|
2013-12-02 12:40:00 +00:00
|
|
|
logging.debug("Final cache stats:")
|
2012-03-04 05:20:22 +00:00
|
|
|
for c in caches:
|
2013-12-02 12:40:00 +00:00
|
|
|
logging.debug("\t%s: %s hits, %s misses", c.__class__.__name__, c.hits, c.misses)
|
2019-03-08 15:48:31 +00:00
|
|
|
if args.pid:
|
|
|
|
os.remove(args.pid)
|
2012-03-04 23:46:02 +00:00
|
|
|
|
2019-03-06 13:27:04 +00:00
|
|
|
logging.info("Your render has been written to '%s', open index.html to view it." % destdir)
|
|
|
|
|
2012-01-14 06:11:05 +00:00
|
|
|
return 0
|
2011-08-05 20:40:48 +00:00
|
|
|
|
2019-03-06 13:27:04 +00:00
|
|
|
|
2010-09-21 20:07:23 +00:00
|
|
|
def list_worlds():
|
|
|
|
"Prints out a brief summary of saves found in the default directory"
|
2019-03-14 15:03:35 +00:00
|
|
|
print()
|
2010-09-21 20:07:23 +00:00
|
|
|
worlds = world.get_worlds()
|
|
|
|
if not worlds:
|
2019-03-06 13:27:04 +00:00
|
|
|
print('No world saves found in the usual place.')
|
2010-09-21 20:07:23 +00:00
|
|
|
return
|
2013-03-11 17:06:54 +00:00
|
|
|
print("Detected saves:")
|
2011-11-08 01:37:21 +00:00
|
|
|
|
|
|
|
# get max length of world name
|
2014-02-16 18:35:16 +00:00
|
|
|
worldNameLen = max([len(x) for x in worlds] + [len("World")])
|
2011-11-08 01:37:21 +00:00
|
|
|
|
2014-02-22 03:23:25 +00:00
|
|
|
formatString = "%-" + str(worldNameLen) + "s | %-8s | %-16s | %s "
|
|
|
|
print(formatString % ("World", "Playtime", "Modified", "Path"))
|
2019-03-06 13:27:04 +00:00
|
|
|
print(formatString % ("-" * worldNameLen, "-" * 8, '-' * 16, '-' * 4))
|
2019-03-16 19:43:25 +00:00
|
|
|
for name, info in sorted(worlds.items()):
|
|
|
|
if isinstance(name, str) and name.startswith("World") and len(name) == 6:
|
2011-02-23 18:07:21 +00:00
|
|
|
try:
|
|
|
|
world_n = int(name[-1])
|
|
|
|
# we'll catch this one later, when it shows up as an
|
|
|
|
# integer key
|
|
|
|
continue
|
|
|
|
except ValueError:
|
|
|
|
pass
|
2016-12-19 15:50:01 +00:00
|
|
|
if info['LastPlayed'] > 0:
|
2019-03-06 13:27:04 +00:00
|
|
|
timestamp = time.strftime("%Y-%m-%d %H:%M", time.localtime(info['LastPlayed'] / 1000))
|
2016-12-19 15:50:01 +00:00
|
|
|
else:
|
|
|
|
timestamp = ""
|
|
|
|
if info['Time'] > 0:
|
|
|
|
playtime = info['Time'] / 20
|
|
|
|
playstamp = '%d:%02d' % (playtime / 3600, playtime / 60 % 60)
|
|
|
|
else:
|
|
|
|
playstamp = ""
|
2012-03-02 03:23:12 +00:00
|
|
|
path = info['path']
|
2014-02-22 03:23:25 +00:00
|
|
|
print(formatString % (name, playstamp, timestamp, path))
|
2016-12-19 16:08:35 +00:00
|
|
|
found_corrupt = any([x.get("IsCorrupt") for x in worlds.values()])
|
|
|
|
if found_corrupt:
|
2019-03-14 15:03:35 +00:00
|
|
|
print()
|
2016-12-19 16:08:35 +00:00
|
|
|
print("An error has been detected in one or more of your worlds (see the above table).")
|
2019-03-06 13:27:04 +00:00
|
|
|
print("This is usually due to a corrupt level.dat file. Corrupt worlds need to be "
|
|
|
|
"repaired before Overviewer can render them.")
|
|
|
|
|
2010-09-21 20:07:23 +00:00
|
|
|
|
2010-09-04 23:22:32 +00:00
|
|
|
if __name__ == "__main__":
|
2010-09-29 03:04:21 +00:00
|
|
|
multiprocessing.freeze_support()
|
2011-11-08 01:36:00 +00:00
|
|
|
try:
|
2012-01-14 06:11:05 +00:00
|
|
|
ret = main()
|
2012-03-15 08:03:46 +00:00
|
|
|
util.nice_exit(ret)
|
2013-03-11 17:06:54 +00:00
|
|
|
except textures.TextureException as e:
|
2013-01-02 02:08:59 +00:00
|
|
|
# this isn't a "bug", so don't print scary traceback
|
2013-12-02 12:40:00 +00:00
|
|
|
logging.error(str(e))
|
2013-01-02 02:08:59 +00:00
|
|
|
util.nice_exit(1)
|
2013-03-11 17:06:54 +00:00
|
|
|
except Exception as e:
|
2013-12-02 12:40:00 +00:00
|
|
|
logging.exception("""An error has occurred. This may be a bug. Please let us know!
|
2011-11-08 01:36:00 +00:00
|
|
|
See http://docs.overviewer.org/en/latest/index.html#help
|
|
|
|
|
|
|
|
This is the error that occurred:""")
|
2012-03-15 08:03:46 +00:00
|
|
|
util.nice_exit(1)
|
2019-07-12 15:15:51 +00:00
|
|
|
except KeyboardInterrupt:
|
|
|
|
logging.info("Interrupted by user. Aborting.")
|
|
|
|
util.nice_exit(2)
|