From fda8d5db157397ea26f848c1837f35b3b7850918 Mon Sep 17 00:00:00 2001 From: poikilos <7557867+poikilos@users.noreply.github.com> Date: Wed, 29 Apr 2020 17:08:07 -0400 Subject: [PATCH] Support backend option and i/o options like the C++ minetestmapper --- CHANGELOG.md | 20 +++++++ minetestmapper-numpy.py | 113 +++++++++++++++++++++++----------------- minetestmapper.py | 71 ++++++++++++++----------- quality.sh | 47 +++++++++-------- 4 files changed, 150 insertions(+), 101 deletions(-) mode change 100644 => 100755 minetestmapper-numpy.py mode change 100644 => 100755 minetestmapper.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f866ea..3a07b9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [git] - 2020-02-29 +(Poikilos) +### Added +- Support the `--backend` argument. + - Look for it in `get_db`. +- Support the `--colors` argument. + +### Changed +- Change the `world_dir` argument to `-i` or `--input`. +- Allow `-o` as shorthand for `--output` +- No longer add `os.path.sep` to `args.input` + +### Fixed +- Use `os.path.join` + - ...when looking for world.mt if `backend` is not + specified in minetestmapper-numpy.py. + - No longer add `os.path.sep` to `args.input` and other paths. +- Use `elif` with database formats to prevent overlaying converted + databases onto each other in minetestmapper-numpy.py. + ## [git] - 2020-02-10 (Poikilos) ### Removed diff --git a/minetestmapper-numpy.py b/minetestmapper-numpy.py old mode 100644 new mode 100755 index 7445c18..124f6e9 --- a/minetestmapper-numpy.py +++ b/minetestmapper-numpy.py @@ -50,15 +50,17 @@ except ImportError: def join_as_str(delimiter, arr): return delimiter.join(str(x) for x in arr) -# -# wrapper around PIL 1.1.6 Image.save to preserve PNG metadata -# -# public domain, Nick Galbreath -# http://blog.client9.com/2007/08/28/python-pil-and-png-metadata-take-2.html -# + def pngsave(im, file): - # these can be automatically added to Image.info dict - # they are not user-added metadata + """ + This is a wrapper around PIL 1.1.6 Image.save to preserve PNG + metadata. + CC0 Nick Galbreath + + These can be automatically added to Image.info dict. + They are not user-added metadata. + """ reserved = ('interlace', 'gamma', 'dpi', 'transparency', 'aspect') # undocumented class @@ -139,11 +141,14 @@ def int_to_hex4(i): # return i # else: # return i + 2*max_positive + + # def getBlockAsInteger(p): # return signedToUnsigned(p[2],2048)*16777216 # + signedToUnsigned(p[1],2048)*4096 # + signedToUnsigned(p[0],2048) + def getBlockAsInteger(p): return p[2]*16777216 + p[1]*4096 + p[0] @@ -307,29 +312,30 @@ def parse_args(): help=('accepted for compatibility but' ' NOT YET IMPLEMENTED in this version')) ap.add_argument('--colors', type=str, default='colors.txt', - help=('accepted for compatibility but' - ' NOT YET IMPLEMENTED in this version')) - ap.add_argument('--backend', type=str, choices=('leveldb'), - default='leveldb', + help=('specify a colors list in colors.txt format' + '"80c 183 183 222 # CONTENT_GLASS"' + 'or "default:stone 128 128 128")')) + ap.add_argument('--backend', type=str, + choices=('leveldb', 'sqlite3'), default='leveldb', help=('accepted for compatibility but' ' NOT YET IMPLEMENTED in this version')) ap.add_argument('--fog', type=float, metavar=('FOGSTRENGTH'), default=0.0, help=('use fog strength of' ' FOGSTRENGTH (0.0 by' ' default, max of 1.0)')) - ap.add_argument('world_dir', + ap.add_argument('-i', '--input', help='the path to the world you want to map') - ap.add_argument('output', nargs='?', default='map.png', + ap.add_argument('-o', '--output', nargs="?", default='map.png', help='the output filename') args = ap.parse_args() - if args.world_dir is None: + if args.input is None: print("Please select world path (eg. -i ../worlds/yourworld)" " (or use --help)") sys.exit(1) - if not os.path.isdir(args.world_dir): + if not os.path.isdir(args.input): print("World does not exist") sys.exit(1) - args.world_dir = os.path.abspath(args.world_dir) + os.path.sep + args.input = os.path.abspath(args.input) return args @@ -376,19 +382,21 @@ def load_colors(fname="colors.txt"): def legacy_fetch_sector_data(args, sectortype, sector_data, ypos): yhex = int_to_hex4(ypos) if sectortype == "old": - ss_path = args.world_dir + "sectors/" - filename = ss_path + sector_data[0] + "/" + yhex.lower() + ss_path = os.path.join(args.input, "sectors") + filename = os.path.join(ss_path, sector_data[0], + yhex.lower()) else: - ss2_path = args.world_dir + "sectors2/" - filename = ss2_path + sector_data[1] + "/" + yhex.lower() + ss2_path = os.path.join(args.input, "sectors2") + filename = os.path.join(ss2_path, sector_data[1], yhex.lower()) return open(filename, "rb") def legacy_sector_scan(args, sectors_xmin, sector_xmax, sector_zmin, sector_zmax): - if os.path.exists(args.world_dir + "sectors2"): - for filename in os.listdir(args.world_dir + "sectors2"): - ss2_f_path = args.world_dir + "sectors2/" + filename + ss_path = os.path.join(args.input, "sectors") + ss2_path = os.path.join(args.input, "sectors2") + if os.path.exists(ss2_path): + for filename in os.listdir(ss2_path): + ss2_f_path = os.path.join(ss2_path, filename) for filename2 in os.listdir(ss2_f_path): x = hex_to_int(filename) z = hex_to_int(filename2) @@ -398,9 +406,8 @@ def legacy_sector_scan(args, sectors_xmin, sector_xmax, sector_zmin, continue xlist.append(x) zlist.append(z) - - if os.path.exists(args.world_dir + "sectors"): - for filename in os.listdir(args.world_dir + "sectors"): + elif os.path.exists(ss_path): + for filename in os.listdir(ss_path): x = hex4_to_int(filename[:4]) z = hex4_to_int(filename[-4:]) if x < sector_xmin or x > sector_xmax: @@ -419,13 +426,15 @@ def legacy_fetch_ylist(args, xpos, zpos, ylist): zhex4 = int_to_hex4(zpos) sector1 = xhex4.lower() + zhex4.lower() - sector2 = xhex.lower() + "/" + zhex.lower() + sector2 = os.path.join(xhex.lower(), zhex.lower()) + ss_path = os.path.join(args.input, "sectors") + ss2_path = os.path.join(args.input, "sectors2") try: - ss_s1_path = args.world_dir + "sectors/" + sector1 + ss_s1_path = ss_path + sector1 for filename in os.listdir(ss_s1_path): - if(filename != "meta"): + if (filename != "meta"): pos = int(filename, 16) - if(pos > 32767): + if (pos > 32767): pos -= 65536 ylist.append(pos) @@ -434,11 +443,11 @@ def legacy_fetch_ylist(args, xpos, zpos, ylist): if sectortype == "": try: - ss2_s2_path = args.world_dir + "sectors2/" + sector2 + ss2_s2_path = os.path.join(ss2_path, sector2) for filename in os.listdir(ss2_s2_path): - if(filename != "meta"): + if (filename != "meta"): pos = int(filename, 16) - if(pos > 32767): + if (pos > 32767): pos -= 65536 ylist.append(pos) sectortype = "new" @@ -551,20 +560,25 @@ def map_block_ug(mapdata, version, ypos, maxy, cdata, hdata, udata, def get_db(args): - if not os.path.exists(args.world_dir+"world.mt"): - return None - with open(args.world_dir+"world.mt") as f: - keyvals = f.read().splitlines() - keyvals = [kv.split("=") for kv in keyvals] - backend = None - for k, v in keyvals: - if k.strip() == "backend": - backend = v.strip() - break + backend = args.backend + if args.backend is None: + # This should never happen. + print("* detecting db type from world.mt in " + "{}".format(args.input)) + if not os.path.exists(os.path.join(args.input, "world.mt")): + return None + with open(os.path.join(args.input, "world.mt")) as f: + keyvals = f.read().splitlines() + keyvals = [kv.split("=") for kv in keyvals] + backend = None + for k, v in keyvals: + if k.strip() == "backend": + backend = v.strip() + break if backend == "sqlite3": - return SQLDB(args.world_dir + "map.sqlite") + return SQLDB(os.path.join(args.input, "map.sqlite")) if backend == "leveldb": - return LVLDB(args.world_dir + "map.db") + return LVLDB(os.path.join(args.input, "map.db")) class SQLDB: @@ -1299,8 +1313,9 @@ def draw_image(world, uid_to_color): if args.drawplayers: try: - for filename in os.listdir(args.world_dir + "players"): - f = open(args.world_dir + "players/" + filename) + players_path = os.path.join(args.input, "players") + for filename in os.listdir(players_path): + f = open(os.path.join(players_path, filename)) lines = f.readlines() name = "" position = [] @@ -1395,7 +1410,7 @@ def draw_image(world, uid_to_color): def main(): args = parse_args() - uid_to_color, str_to_uid = load_colors() + uid_to_color, str_to_uid = load_colors(fname=args.colors) world = World(args) diff --git a/minetestmapper.py b/minetestmapper.py old mode 100644 new mode 100755 index 78d8926..0a72741 --- a/minetestmapper.py +++ b/minetestmapper.py @@ -148,6 +148,13 @@ def readS32(f): ord(f.read(1)), 2**31) +colors_msg = """ format: '#000000' +NOTE: colors.txt must be in same directory as +this script or in the current working +directory (or util directory if installed). +Otherwise, use the --colors option followed by a path. +""" + usagetext = """minetestmapper.py [options] -i/--input -o/--output @@ -161,10 +168,8 @@ usagetext = """minetestmapper.py [options] --region :,: --geometry :++ --drawunderground -Color format: '#000000' -NOTE: colors.txt must be in same directory as -this script or in the current working -directory (or util directory if installed). + --colors + --backend (other db formats are NOT YET IMPLEMENTED) """ @@ -179,7 +184,7 @@ try: "bgcolor=", "scalecolor=", "origincolor=", "playercolor=", "draworigin", "drawplayers", "drawscale", "drawunderground", "geometry=", - "region="]) + "region=", "colors=", "backend="]) except getopt.GetoptError as err: # print help information and exit: print(str(err)) # something like "option -a not recognized" @@ -199,6 +204,7 @@ draworigin = False drawunderground = False geometry_string = None region_string = None +colors_path = None for o, a in opts: if o in ("-h", "--help"): @@ -229,6 +235,8 @@ for o, a in opts: geometry_string = a elif o == "--region": region_string = a + elif o == "--colors": + colors_path = a elif o == "--drawalpha": print("# ignored (NOT YET IMPLEMENTED) " + o) elif o == "--noshading": @@ -241,8 +249,6 @@ for o, a in opts: print("# ignored (NOT YET IMPLEMENTED) " + o) elif o == "--zoom": print("# ignored (NOT YET IMPLEMENTED) " + o) - elif o == "--colors": - print("# ignored (NOT YET IMPLEMENTED) " + o) elif o == "--scales": print("# ignored (NOT YET IMPLEMENTED) " + o) else: @@ -325,7 +331,7 @@ if path[-1:] != "/" and path[-1:] != "\\": # Load color information for the blocks. colors = {} -colors_path = "colors.txt" +colors_name = "colors.txt" profile_path = None if 'HOME' in os.environ: @@ -335,24 +341,27 @@ elif 'USERPROFILE' in os.environ: mt_path = None mt_util_path = None -abs_colors_path = None -if not os.path.isfile(colors_path): - colors_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), - "colors.txt") +try_dot_mt_path = os.path.join(profile_path, ".minetest") +mt_path = os.path.join(profile_path, "minetest") +mt_util_path = os.path.join(mt_path, "util") -if not os.path.isfile(colors_path): - if profile_path is not None: - try_path = os.path.join(profile_path, "minetest") - if os.path.isdir(try_path): - mt_path = try_path - mt_util_path = os.path.join(mt_path, "util") - abs_colors_path = os.path.join(mt_util_path, "colors.txt") - if os.path.isfile(abs_colors_path): - colors_path = abs_colors_path +if colors_path is None: + try_paths = [] + try_paths.append(try_dot_mt_path, colors_name) + try_paths.append( + os.path.join(os.path.dirname(os.path.abspath(__file__)), + colors_name) + ) + try_paths.append(mt_path, colors_name) + try_paths.append(mt_util_path, colors_name) + for try_path in try_paths: + if os.path.isfile(try_path): + colos_path = try_path + break -if not os.path.isfile(colors_path): - print("ERROR: could not find colors.txt") +if (colors_path is None) or (not os.path.isfile(colors_path)): + print("ERROR: could not find {}".format(colors_name)) usage() sys.exit(1) @@ -360,7 +369,7 @@ try: f = open(colors_path) except IOError: f = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), - "colors.txt")) + colors_name)) for line in f: values = line.split() if len(values) < 4: @@ -394,6 +403,8 @@ zlist = [] conn = None cur = None +look_for_names = ["map.sqlite", "sectors2", "sectors"] +look_for_paths = [os.path.join(path, x) for x in look_for_names] if os.path.exists(path + "map.sqlite"): import sqlite3 conn = sqlite3.connect(path + "map.sqlite") @@ -414,8 +425,7 @@ if os.path.exists(path + "map.sqlite"): xlist.append(x) zlist.append(z) - -if os.path.exists(path + "sectors2"): +elif os.path.exists(path + "sectors2"): for filename in os.listdir(path + "sectors2"): for filename2 in os.listdir(path + "sectors2/" + filename): x = hex_to_int(filename) @@ -426,8 +436,7 @@ if os.path.exists(path + "sectors2"): continue xlist.append(x) zlist.append(z) - -if os.path.exists(path + "sectors"): +elif os.path.exists(path + "sectors"): for filename in os.listdir(path + "sectors"): x = hex4_to_int(filename[:4]) z = hex4_to_int(filename[-4:]) @@ -439,7 +448,8 @@ if os.path.exists(path + "sectors"): zlist.append(z) if len(xlist) == 0 or len(zlist) == 0: - print("Data does not exist at this location.") + print("A compatible database was not found ({})." + "".format(look_for_paths)) sys.exit(1) # Get rid of doubles @@ -703,7 +713,8 @@ for n in range(len(xlist)): # zlib-compressed node metadata list dec_o = zlib.decompressobj() try: - metaliststr = array.array("B", dec_o.decompress(f.read())) + metaliststr = array.array("B", + dec_o.decompress(f.read())) # And do nothing with it except: metaliststr = [] diff --git a/quality.sh b/quality.sh index 3414d1c..3b3f17b 100755 --- a/quality.sh +++ b/quality.sh @@ -1,31 +1,34 @@ #!/bin/sh if [ ! -f "`command -v pycodestyle-3`" ]; then - echo "You must install the python3-pycodestyle package before using the quality script." - exit 1 + echo "You must install the python3-pycodestyle package before using the quality script." + exit 1 fi -target="minetestmapper-numpy.py" # target="__init__.py" # if [ ! -z "$1" ]; then -# target="$1" +# target="$1" # fi if [ -f err.txt ]; then - rm err.txt -fi -if [ -f "`command -v outputinspector`" ]; then - pycodestyle-3 "minetestmapper.py" > err.txt - pycodestyle-3 "$target" >> err.txt - # For one-liner, would use `||` not `&&`, because pycodestyle-3 returns nonzero (error state) if there are any errors - if [ -s "err.txt" ]; then - outputinspector - else - echo "No quality issues were detected." rm err.txt - # echo "Deleted empty 'err.txt'." - fi -else - pycodestyle-3 "minetestmapper.py" - pycodestyle-3 "$target" - echo - echo "If you install outputinspector, this output can be examined automatically, allowing double-click to skip to line in Geany/Kate" - echo +fi +ext="py" +if [ -f "`command -v outputinspector`" ]; then + for f in *.$ext; do + pycodestyle-3 "$f" >> err.txt + done + # For one-liner, would use `||` not `&&`, because pycodestyle-3 returns nonzero (error state) if there are any errors + if [ -s "err.txt" ]; then + # -s: exists and >0 bytes + outputinspector + else + echo "No quality issues were detected." + rm err.txt + # echo "Deleted empty 'err.txt'." + fi +else + for f in *.$ext; do + pycodestyle-3 "$f" >> err.txt + done + echo + echo "If you install outputinspector, this output can be examined automatically, allowing double-click to skip to line in Geany/Kate" + echo fi