diff --git a/.gitignore b/.gitignore index 7bbc71c..36a11ed 100644 --- a/.gitignore +++ b/.gitignore @@ -99,3 +99,4 @@ ENV/ # mypy .mypy_cache/ +/err.txt diff --git a/README.md b/README.md index 426b22c..ec2d0e3 100644 --- a/README.md +++ b/README.md @@ -35,3 +35,30 @@ Use python to make minetest maps from worlds. ## Requirements * Python Imaging Library: http://www.pythonware.com/products/pil/ * The colors.txt should be generated by for the most complete colors list (unless you use the more complicated [minetestmapper method]([https://github.com/minetest/minetestmapper])) and prettiest map until that code is moved from EnlivenMinetest to the minetestmapper-python project. + +## Developer notes: +(Poikilos) + +### minetestmapper-numpy.py +- self.db is an iterator from get_db (which returns either an SQLDB or LVLDB object). +- The db iterators stop (via break) whenever `not r[0]`, but still there + are problems (below is from `__iter__` in `LVLDB`). +```Python + for k in self.conn.RangeIter(): + #if k is not None and len(k)>0: + try: + # print("getting int from first index of value "+str(k)) + # SOMETIMES has an integer, such as getting int from first index of value ('905945065', '\x19\x01\x02\x02x\x9c\xedX\xdbu\xdb0\x0c\xa5\xa2Jv\xbf|\xbaBW\xe8\n]!+t\x85\xac\x90\x15\xbc\x82V\xd0\n\x1a\xaa\xa4\x14\x8a\xc0%\xf8\x90DK~\xe4\xf2\x9c\xc4 \xf8\x00pA\x90\xb6R\x04\x95noc\xab\xe6\xcf\x1cV_\xa2\xd5\xba\xfd\x88\xe8\xa6\xe6\xfa*\xd2$\xbd?\x17\xf5\xae\xb7!\x9f[-\xb5_\xad\x9e\xff\xd7D\xa2\xcd\xaeE\xc7\xd7L\xc3wkE\x1bC>\xe6\xeb\x9d\xce\x1f\xc5\xfd\xaf\xa3-\x86*\xa8w;\xb9]\x97\xeb\xeb\x05\xfaJ\xc8\xc3p~H\xeb\xf3H4\xe4SK\xf8\xb7\xac\xc9\xec[\x96\x1b\xc8\x1a\x9f\x9fv^\xc7\xcf\x0cjK\x93`\xffM\\\x9d\xe7\x06\x8e\x90\xf2\x7f\r\xffa\xfdr\xfen\xab\xaf\xbc\xfc\xa8\xbc\xf9~\x1cm\x0cN\x1e\xdf\x86\x97Fau\xa0\x8d\xf7\x86\xf9i2\xf8[\xaa\x0f\xd5\xb7\xd2\xfc\xc7Aw\xdc\xe8Z|~\x13\xcd\x8e4\x7f\x92\x17!\xfdY\xe0W\x9e5\xe5eS\x88\xffc\xf52\xfb\xf45\x93\xc3?2\xee\x98Oq\xefF\xd7\xde|~\xfe\xa5\xf3G\xad<\x0bc\xb8^:\xbfUt\xfd0\xff\xcd\xc6\xf3o9\xb8\xad>\xc4n\x9e>\x9d\x01\x9c\xfd:\xc0i\x0e\xff\xf4\xe4\xbb\xdc\xe1\xaf\x91\x18\xff\x12\xbb\xf5\x1c\xa5\xd0|\xe7\xa5\xac\x0f\xf1O\xe7\xafG\x9c\xbd=\xf4)\xfeC5\x96GE\xee\xcdc\x9e\xe6JM\xfeO\xf3+\xf2\x16\t\xf1\x1f\xe3o\xf22\xc5\x7fx\x87X\xed\xd8\xce\xff\xf1\x88\xf3\xaf\xb2\xf8\x975\xfe[ 7\x03h\x1eL6\xdao\xacM \xfe\x95\xa8\xa3\xfc\x84\xf2\'\xa5\xb7\xde\xc8\xab?>\xff)\xa4_\x00\xa1\xb6\xf4\xf4\xe3\xf9\x9fz-&\xf6OQ\xfe$\x8e\xa8\xbeN\xcc\x8f\xf1\xefn\xfco\xfei\xe3\xbf\x18\xf8\x19\xe0\xeb9\xf3\x16\xee\xd4\xf3L@\xfd\xfa\xf3\xdd\x88\x0c\xa6\xe6\xc7o\xffo\xfe\xf17#\xcc\x00I\xdf\xea\xdb\xd4\xf2\x8c;\xe1-\xe0\x90\x8a\x7f\x9a\x9f5z\xbc\xeb^\x8d\xff\x18\xbb\x9c\x7fiL\xcb\xd6\xc1\x1a\x80\xf1\x93bLQ\x82_\xac\x00\xd2|:\x86Z-\xe9\x9f\x1dq~9\xff\xfe\x88\xd6[\x0bO9\xee\x869\x80H\x9d\xbfZ\xfd\xdc8\x9f\xf3\x8b\x1e\xe1;\xe0\xd9\x11\xe7\xf7\r\xc6\xe6\xf3_\x0b\xe7\xbf\x0c\xffe\xf5\xe8\x13V\x90gG\x95\xc8\x00\x1c\xcb\xb5\r\xac\xe6\xa28\xfd\xb6\xd7\x82>\xc5\x7f\xbc\xbe\xdfB_\x01\xfb\xa9\xfcy6 \xff\xc80"v\xfe\x9d\xbe\r\xc6\xef\xde\xce\xbf_\xff\xc5\xf9\xc30\x98\xbf=\x915\xfanp\xe8\xfb\xbe\xbb\xf6\x06V\xa9\xc7u\x1a_\xf28\xefz\xd5"1\xe7S\xcb\xfd\xacU\xeaCwt\xbdZ\x86?\xbf\x7f9\xe1\xe3\xdf\xfb_\xda\xf1\t\x1d\\66*"_\xf3w\x1d\xadv\x9e)\x13\x005\xbao\xe5n\xf4\x9f\xc9\xd4}\x13M#[\xbd\x91\xa7x\xe4\x1b\xa1\x11\xf7\xb7\xbb\xb2\x8e^\x81\xdc\xd9\tv\xf857\x04\xa3\xe3\xd4\x7fc\xff\xc8>\x91\xbb.!Sg\x07\x1b\x0f\x9bQYv\x00\xbf\xe0_?\x07`\xdab\x96\x95(k1?\x05H^\x7f-\xd63\xf2\xf4\xe2\x1d\xc8\xa8\xe7\\\x8f\x1d\xf4Hu\x19Vh\xf3\xa9\x08\xfe\xcd\x0e\xba-|\x99Y@\xc58\xc0}\xb3Y\x9f\x90q<\xa4\xfa\xc0\xdc\xcf\xcd\x00\x06t\xc0x\xa4\xdeAF=5\xa9[T\x03\x16\xca8\xdf_\x90#\xdf\x94y\x05H\x9a\x94\xacX\x90s\x93\xce\x0e/,+\x8c\x80\x1aK\xca\x88\xd56\xf1\x99~R\xf1\x94\\\x13\xf3\x92@\xff{\x87\x9dv\xdfa\x9b\xe8\xfe\x88W\xf6\x7f\xec\x83\x00\x98ke\xc1\x19]\xbc\xff\x8d\x96\xce\xde\xdf\xab\x7f~\x00z\x16\x80\xa1\\j\xac\xad\xba%\xe1\xd7\xff\x01N\x80)\xd2,\x00\xe5,\xbe\x03\xffi\x04l\x07V\x00\xffV+\xbbs\x99\xd56\x9b\xb1\xbb\xff\xea>\xfc\xf7x\x18\xb8\xff\x90\xff\xc5\xfd/\xb4\xd8f;B\xfe\xef\x9c\xff\x97\xcb\xf8\xe7\xe2:6mr\x19\xb1i\x89cP\xc6\xe6\x0b.v\x1aQd\xed\x07\x84\xcd\x84\x87\xcc\x88\xed\xb8\x88\x1fo\x85sx\xf7c@\r\xd8!\x03\xbc-v\x0e@b\xfb\xe2\xd6\xa4Czl\x06@\xd5\xdbl\x0c\x96\xd1\xf4=sl\xd9)_\xf5\xbd\x8b\xe4\xf0#\xfe\x8d\xf58kP\xf9A\x1fN\xabq\x86\x00<\xea\xcbq-\x8c\xbb\xe73\xf6\x1dc\xcb\x11\x98\xbe*x\xaf\x96\xbb{\xc6\x94\x82\xfcU\xc0{5\xbc\x9a\xff\xc9K\xfcF\xe6\xec\x8e\xd3\xe9\xbe\xbe\x0c\xfe\x07\x80pRrx\x9cc\x00\x00\x00\x01\x00\x01\x00\x00\x00\xff\xff\xff\xff\x00\x00\n\x00\x00\x00\x13default:lava_source\x00\x01\x00\x14default:lava_flowing\x00\x02\x00\x03air\x00\x03\x00\rdefault:stone\x00\x04\x00\x17default:stone_with_mese\x00\x05\x00\x17default:stone_with_coal\x00\x06\x00\x0etechnic:marble\x00\x07\x00\x14moreores:mineral_tin\x00\x08\x00\x17default:stone_with_iron\x00\t\x00\x17moreores:mineral_silver\n\x00\x00') + val = k[0] + # if k[0][:2]=="\\x": + # in leveldb, minetest stores \x before every byte of the value, so remove all and prepend 0x so python can convert to int + # but this doesn't work (resulting val still throws exception) + # val = "\\0x" + k[0].replace("\\x", "") + x, y, z = getIntegerAsBlock(int(val)) + yield x, y, z, val + except Exception as e: + print("Could not finish getting int from first index of value "+str(k)) + # SOMETIMES prints tons of output such as Could not finish getting int from first index of value ('\x00\x00\x00\x0e\x00\x02\x85 ', '\x19\x08\x02\x02x\x9c\xed\xc11\x01\x00\x00\x00\xc2\xa0\xf5Om\x0c\x1f\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xb7\x01@\x00\x00\x01x\x9cc\x00\x00\x00\x01\x00\x01\x00\x00\x00\xff\xff\xff\xff\x00\x00\x01\x00\x00\x00\x06ignore\n\x00\x00') + raise e + +``` diff --git a/minetestmapper-numpy.py b/minetestmapper-numpy.py index e4ebb50..b677c3c 100644 --- a/minetestmapper-numpy.py +++ b/minetestmapper-numpy.py @@ -17,32 +17,35 @@ import sys import traceback import numpy import itertools +import io +try: + BytesIO = io.BytesIO +except AttributeError: + BytesIO = io.StringIO + +PIL_HELP = """ +You must first install Pillow (fork of PIL). + +- On Windows: +Right-click windows menu, 'Command Prompt (Admin)' then: +pip install Pillow + +- On *nix-like systems: +sudo python2 -m pip install --upgrade pip +sudo python2 -m pip install --upgrade pip wheel +#then: +# +python2 -m pip install Pillow # sudo pip install Pillow +#or +#same but python3 instead # sudo pip install Pillow + +""" try: from PIL import Image, ImageDraw, ImageFont, ImageColor -except: - print("You must first install Pillow's PIL.") - print("On Windows:") - print("Right-click windows menu, 'Command Prompt (Admin)' then:") - print("pip install Pillow") - print("") - print("On *nix-like systems:") - print("sudo python2 -m pip install --upgrade pip") - print("sudo python2 -m pip install --upgrade pip wheel") - print("#then:") - #print("sudo pip install Pillow") - print("python2 -m pip install Pillow") - print("#or") - print("#same but python3 instead") - #print("sudo pip install Pillow") +except ImportError: + print(PIL_HELP) exit() -try: - import io - BytesIO = io.BytesIO -except: - import io - BytesIO = io.StringIO - # # wrapper around PIL 1.1.6 Image.save to preserve PNG metadata @@ -58,29 +61,18 @@ def pngsave(im, file): # undocumented class try: from PIL import PngImagePlugin - except: - print("ERROR: Could not finish at `from PIL import PngImagePlugin`") - print("You must first install Pillow's PIL.") - print("On Windows:") - print("Right-click windows menu, 'Command Prompt (Admin)' then:") - print("pip install Pillow") - print("") - print("On *nix-like systems:") - print("sudo python2 -m pip install --upgrade pip") - print("sudo python2 -m pip install --upgrade pip wheel") - print("#then:") - #print("sudo pip install Pillow") - print("python2 -m pip install Pillow") - print("#or") - print("#same but python3 instead") - #print("sudo pip install Pillow") + except ImportError: + print("ERROR: Could not finish at `from PIL import" + " PngImagePlugin`") + print(PIL_HELP) exit(1) meta = PngImagePlugin.PngInfo() # copy metadata into new object - for k,v in im.info.items(): - if k in reserved: continue + for k, v in im.info.items(): + if k in reserved: + continue meta.add_text(k, v, 0) # and save @@ -140,64 +132,84 @@ def int_to_hex4(i): # def signedToUnsigned(i, max_positive): - # if i >= 0: - # return i - # else: - # return i + 2*max_positive +# if i >= 0: +# 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) +# 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] + def unsignedToSigned(i, max_positive): if i < max_positive: return i else: return i - 2*max_positive + def getIntegerAsBlock(i): x = unsignedToSigned(i % 4096, 2048) i = int((i - x) / 4096) y = unsignedToSigned(i % 4096, 2048) i = int((i - y) / 4096) z = unsignedToSigned(i % 4096, 2048) - return x,y,z + return x, y, z + def readU8(f): return ord(f.read(1)) + def readU16(f): return ord(f.read(1))*256 + ord(f.read(1)) + def readU32(f): - return ord(f.read(1))*256*256*256 + ord(f.read(1))*256*256 + ord(f.read(1))*256 + ord(f.read(1)) + return (ord(f.read(1))*256*256*256 + + ord(f.read(1))*256*256 + + ord(f.read(1))*256 + + ord(f.read(1))) + def readS32(f): - return unsignedToSigned(ord(f.read(1))*256*256*256 + ord(f.read(1))*256*256 + ord(f.read(1))*256 + ord(f.read(1)), 2**31) + return unsignedToSigned( + ord(f.read(1)) * 256*256*256 + + ord(f.read(1))*256*256 + + ord(f.read(1))*256 + + ord(f.read(1)), + 2**31 + ) + CONTENT_WATER = 2 + def content_is_ignore(d): return d == 0 - #return d in [0, "ignore"] + # return d in [0, "ignore"] + def content_is_water(d): return (d == 2) | (d == 9) - #return d in [2, 9] + # return d in [2, 9] + def content_is_air(d): return (d == 126) | (d == 127) | (d == 254) -# return d in [126, 127, 254, "air"] + # return d in [126, 127, 254, "air"] -#NOT USED + +# NOT USED def read_content(mapdata, version, datapos=None): - if datapos==None: + if datapos is None: if version >= 24: mapdata = numpy.array(mapdata) - x=numpy.arange(4096) + x = numpy.arange(4096) return (mapdata[x*2] << 8) | (mapdata[x*2 + 1]) if version >= 24: @@ -214,48 +226,115 @@ def read_content(mapdata, version, datapos=None): def parse_args(): - parser = argparse.ArgumentParser(description='A mapper for minetest') - parser.add_argument('--bgcolor', default='black', metavar = 'COLOR', type=ImageColor.getrgb, help = 'set the background color (e.g. white or "#FFFFFF")') - parser.add_argument('--scalecolor', default='white', metavar = 'COLOR', type=ImageColor.getrgb, help = 'set the ruler and text color for the scale') - parser.add_argument('--origincolor', default='red', metavar = 'COLOR', type=ImageColor.getrgb, help = 'set the color for the map origin') - parser.add_argument('--playercolor', default='red', metavar = 'COLOR', type=ImageColor.getrgb, help = 'set the color for player markers') - parser.add_argument('--fogcolor', default='grey', metavar = 'COLOR', type=ImageColor.getrgb, help = 'set the color for fog (default grey)') - parser.add_argument('--ugcolor', default='purple', metavar = 'COLOR', type=ImageColor.getrgb, help = 'set the color for underground areas (default purple)') - parser.add_argument('--makethumb',action='store_const', const = True, default=False, help = 'create a thumbnail image in addition to the full size image with the file name _thumb.png') - parser.add_argument('--drawscale',action='store_const', const = True, default=False, help = 'draw a scale on the border of the map') - parser.add_argument('--drawplayers',action='store_const', const = True, default = False, help = 'draw markers for players') - parser.add_argument('--draworigin',action='store_const', const = True, default = False, help = 'draw the position of the origin (0,0)') - parser.add_argument('--drawalpha',dest='drawalpha',action='store_const', const = 1, default = 0, help = 'accepted for compatibility but NOT YET IMPLEMENTED in this version') - parser.add_argument('--noshading',dest='noshading',action='store_const', const = 1, default = 0, help = 'accepted for compatibility but NOT YET IMPLEMENTED in this version') - parser.add_argument('--drawunderground',dest='drawunderground',action='store_const', const = 1, default = 0, help = 'draw underground areas overlaid on the map') - parser.add_argument('--drawunderground-standalone',dest='drawunderground',action='store_const', const = 2, help = 'draw underground areas as a standalone map') - parser.add_argument('--region', nargs=4, type = int, metavar = ('XMIN','XMAX','ZMIN','ZMAX'), default = (-2000,2000,-2000,2000),help = 'set the bounding x,z coordinates for the map (units are nodes, default = -2000 2000 -2000 2000)') - parser.add_argument('--max-y', dest='maxheight', type = int, metavar = ('YMAX'), default = 500, help = 'don\'t draw above height YMAX (default = 500)') - parser.add_argument('--min-y', dest='minheight', type = int, metavar = ('YMIN'), default = -500, help = 'don\'t draw below height YMIN (defualt = -500)') - parser.add_argument('--zoom', dest='pixelspernode', type = int, metavar = ('PPN'), default = 1, help = 'number of pixels per node (default = 1)') - parser.add_argument('--facing', type = str, choices = ('up','down','north','south','east','west'),default='down',help = 'direction to face when drawing (north, south, east or west will draw a cross-section)') - parser.add_argument('--geometry', type = str, default='',help = 'accepted for compatibility but NOT YET IMPLEMENTED in this version') - parser.add_argument('--scales', type = str, default='',help = 'accepted for compatibility but NOT YET IMPLEMENTED in this version') - parser.add_argument('--colors', type = str, default='colors.txt',help = 'accepted for compatibility but NOT YET IMPLEMENTED in this version') - parser.add_argument('--backend', type = str, choices = ('leveldb'),default='leveldb',help = 'accepted for compatibility but NOT YET IMPLEMENTED in this version') - parser.add_argument('--fog', type = float, metavar = ('FOGSTRENGTH'), default = 0.0, help = 'use fog strength of FOGSTRENGTH (0.0 by default, max of 1.0)') - parser.add_argument('world_dir',help='the path to the world you want to map') - parser.add_argument('output',nargs='?',default='map.png',help='the output filename') - args = parser.parse_args() + ap = argparse.ArgumentParser(description='A mapper for minetest') + ap.add_argument('--bgcolor', default='black', metavar='COLOR', + type=ImageColor.getrgb, + help=('set the background color (e.g. white or' + ' "#FFFFFF")')) + ap.add_argument('--scalecolor', default='white', metavar='COLOR', + type=ImageColor.getrgb, + help='set the ruler and text color for the scale') + ap.add_argument('--origincolor', default='red', metavar='COLOR', + type=ImageColor.getrgb, + help='set the color for the map origin') + ap.add_argument('--playercolor', default='red', metavar='COLOR', + type=ImageColor.getrgb, + help='set the color for player markers') + ap.add_argument('--fogcolor', default='grey', metavar='COLOR', + type=ImageColor.getrgb, + help='set the color for fog (default grey)') + ap.add_argument('--ugcolor', default='purple', metavar='COLOR', + type=ImageColor.getrgb, + help=('set the color for underground areas (default' + ' purple)')) + ap.add_argument('--makethumb', action='store_const', const=True, + default=False, + help=('create a thumbnail image in addition to the' + ' full size image with the file name' + ' _thumb.png')) + ap.add_argument('--drawscale', action='store_const', const=True, + default=False, help=('draw a scale on the border of' + ' the map')) + ap.add_argument('--drawplayers', action='store_const', const=True, + default=False, help='draw markers for players') + ap.add_argument('--draworigin', action='store_const', const=True, + default=False, help=('draw the position of the' + ' origin (0,0)')) + ap.add_argument('--drawalpha', dest='drawalpha', + action='store_const', const=1, default=0, + help=('accepted for compatibility but' + ' NOT YET IMPLEMENTED in this version')) + ap.add_argument('--noshading', dest='noshading', + action='store_const', const=1, default=0, + help=('accepted for compatibility but' + ' NOT YET IMPLEMENTED in this version')) + ap.add_argument('--drawunderground', dest='drawunderground', + action='store_const', const=1, default=0, + help='draw underground areas overlaid on the map') + ap.add_argument('--drawunderground-standalone', + dest='drawunderground', action='store_const', + const=2, help=('draw underground areas as a' + ' standalone map')) + ap.add_argument('--region', nargs=4, type=int, + metavar=('XMIN', 'XMAX', 'ZMIN', 'ZMAX'), + default=(-2000, 2000, -2000, 2000), + help=('set the bounding x,z coordinates for the map' + ' (units are nodes,' + ' default=-2000 2000 -2000 2000)')) + ap.add_argument('--max-y', dest='maxheight', type=int, + metavar=('YMAX'), default=500, + help='don\'t draw above height YMAX (default=500)') + ap.add_argument('--min-y', dest='minheight', type=int, + metavar=('YMIN'), default=-500, + help='don\'t draw below height YMIN (defualt = -500)') + ap.add_argument('--zoom', dest='pixelspernode', type=int, + metavar=('PPN'), default=1, + help='number of pixels per node (default=1)') + ap.add_argument('--facing', type=str, + choices=('up', 'down', 'north', 'south', 'east', + 'west'), + default='down', + help=('direction to face when drawing (north,' + ' south, east or west will draw a' + ' cross-section)')) + ap.add_argument('--geometry', type=str, default='', + help=('accepted for compatibility but' + ' NOT YET IMPLEMENTED in this version')) + ap.add_argument('--scales', type=str, default='', + 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=('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', + help='the path to the world you want to map') + ap.add_argument('output', nargs='?', default='map.png', + help='the output filename') + args = ap.parse_args() if args.world_dir is None: - print("Please select world path (eg. -i ../worlds/yourworld) (or use --help)") + print("Please select world path (eg. -i ../worlds/yourworld)" + " (or use --help)") sys.exit(1) if not os.path.isdir(args.world_dir): - print ("World does not exist") + print("World does not exist") sys.exit(1) args.world_dir = os.path.abspath(args.world_dir) + os.path.sep return args + # Load color information for the blocks. -def load_colors(fname = "colors.txt"): +def load_colors(fname="colors.txt"): uid_to_color = {} str_to_uid = {} - uid=2 #unique id, we always use ignore == 0, air == 1 because these are never drawn + uid = 2 # unique id; always ignore == 0, air == 1 since never drawn try: f = open("colors.txt") except IOError: @@ -272,7 +351,7 @@ def load_colors(fname = "colors.txt"): is_hex = False break if is_hex: - str_to_uid[int(values[0],16)] = uid + str_to_uid[int(values[0], 16)] = uid uid_to_color[uid] = ( int(values[1]), int(values[2]), @@ -283,26 +362,31 @@ def load_colors(fname = "colors.txt"): int(values[1]), int(values[2]), int(values[3])) - uid+=1 + uid += 1 f.close() return uid_to_color, str_to_uid -#print("colors: "+repr(colors)) -#sys.exit(1) +# print("colors: "+repr(colors)) +# sys.exit(1) + def legacy_fetch_sector_data(args, sectortype, sector_data, ypos): yhex = int_to_hex4(ypos) if sectortype == "old": - filename = args.world_dir + "sectors/" + sector_data[0] + "/" + yhex.lower() + ss_path = args.world_dir + "sectors/" + filename = ss_path + sector_data[0] + "/" + yhex.lower() else: - filename = args.world_dir + "sectors2/" + sector_data[1] + "/" + yhex.lower() + ss2_path = args.world_dir + "sectors2/" + filename = ss2_path + sector_data[1] + "/" + yhex.lower() return open(filename, "rb") -def legacy_sector_scan(args,sectors_xmin, sector_xmax, sector_zmin, sector_zmax): +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"): - for filename2 in os.listdir(args.world_dir + "sectors2/" + filename): + ss2_f_path = args.world_dir + "sectors2/" + filename + for filename2 in os.listdir(ss2_f_path): x = hex_to_int(filename) z = hex_to_int(filename2) if x < sector_xmin or x > sector_xmax: @@ -323,8 +407,9 @@ def legacy_sector_scan(args,sectors_xmin, sector_xmax, sector_zmin, sector_zmax) xlist.append(x) zlist.append(z) -def legacy_fetch_ylist(args,xpos,zpos,ylist): - sectortype ="" + +def legacy_fetch_ylist(args, xpos, zpos, ylist): + sectortype = "" xhex = int_to_hex3(xpos) zhex = int_to_hex3(zpos) xhex4 = int_to_hex4(xpos) @@ -333,19 +418,21 @@ def legacy_fetch_ylist(args,xpos,zpos,ylist): sector1 = xhex4.lower() + zhex4.lower() sector2 = xhex.lower() + "/" + zhex.lower() try: - for filename in os.listdir(args.world_dir + "sectors/" + sector1): + ss_s1_path = args.world_dir + "sectors/" + sector1 + for filename in os.listdir(ss_s1_path): if(filename != "meta"): pos = int(filename, 16) if(pos > 32767): pos -= 65536 ylist.append(pos) - if len(ylist)>0: + if len(ylist) > 0: sectortype = "old" if sectortype == "": try: - for filename in os.listdir(args.world_dir + "sectors2/" + sector2): + ss2_s2_path = args.world_dir + "sectors2/" + sector2 + for filename in os.listdir(ss2_s2_path): if(filename != "meta"): pos = int(filename, 16) if(pos > 32767): @@ -360,9 +447,9 @@ def legacy_fetch_ylist(args,xpos,zpos,ylist): return sectortype -#Alternative map_block -def find(arr,value,axis=-1): - return ((arr==value).cumsum(axis=axis)==0).sum(axis=axis) +# Alternative map_block +def find(arr, value, axis=-1): + return ((arr == value).cumsum(axis=axis) == 0).sum(axis=axis) # # if False: # mapdata = numpy.swapaxes(mapdata.reshape(16,16,16),0,2) @@ -378,80 +465,88 @@ def find(arr,value,axis=-1): # plist = plist[~po] -def map_block(mapdata, version, ypos, maxy, plist, cdata, hdata, dnddata, day_night_differs, id_map, ignore, air, face_swap_order): +def map_block(mapdata, version, ypos, maxy, plist, cdata, hdata, + dnddata, day_night_differs, id_map, ignore, air, + face_swap_order): chunkypos = ypos * 16 mapdata = mapdata[:4096] mapdata = id_map[mapdata] - if (mapdata==ignore).all(): -## return (~( (cdata == ignore) | (cdata == air) )).all() + if (mapdata == ignore).all(): + # return (~( (cdata == ignore) | (cdata == air) )).all() return plist - (swap1a,swap1b),(swap2a,swap2b) = face_swap_order[1:] - mapdata = numpy.swapaxes(mapdata.reshape(16,16,16),swap1a,swap1b) - mapdata = numpy.swapaxes(mapdata,swap2a,swap2b).reshape(16,256) - if face_swap_order[0]>0: - r = list(range(maxy,-1,-1)) + (swap1a, swap1b), (swap2a, swap2b) = face_swap_order[1:] + mapdata = numpy.swapaxes(mapdata.reshape(16, 16, 16), swap1a, swap1b) + mapdata = numpy.swapaxes(mapdata, swap2a, swap2b).reshape(16, 256) + if face_swap_order[0] > 0: + r = list(range(maxy, -1, -1)) else: - r = list(range(maxy,16,1)) -# mapdata=mapdata[::-1] - y=maxy -# if True: -# mapdata = mapdata[y:] -# opaques = ~( (mapdata == ignore) | (mapdata == air) ) -# copaques = ~( (cdata == ignore) | (cdata == air) ) -# h = find(opaques,True,0) -# po = (h<16-y) -# hpo = h*po -# hdata[~copaques] = chunkypos + 16 - hpo[~copaques] -# cdata[~copaques] = mapdata[hpo][~copaques] -# dnddata[~copaques] = day_night_differs -# if (~( (cdata == ignore) | (cdata == air) )).all(): -# return [] -# else: -# return plist + r = list(range(maxy, 16, 1)) + # mapdata=mapdata[::-1] + y = maxy + # if True: + # mapdata = mapdata[y:] + # opaques = ~( (mapdata == ignore) | (mapdata == air) ) + # copaques = ~( (cdata == ignore) | (cdata == air) ) + # h = find(opaques,True,0) + # po = (h<16-y) + # hpo = h*po + # hdata[~copaques] = chunkypos + 16 - hpo[~copaques] + # cdata[~copaques] = mapdata[hpo][~copaques] + # dnddata[~copaques] = day_night_differs + # if (~( (cdata == ignore) | (cdata == air) )).all(): + # return [] + # else: + # return plist for y in r: - if len(plist)==0: + if len(plist) == 0: break content = mapdata[y][plist] -# watercontent = content_is_water(content) -# wdata[plist] += watercontent -# opaques = ~( (content_is_air(content) | content_is_ignore(content) | watercontent)) - opaques = ~( (content == ignore) | (content == air) ) + # watercontent = content_is_water(content) + # wdata[plist] += watercontent + # opaques = ~((content_is_air(content) + # | content_is_ignore(content) | watercontent)) + opaques = ~((content == ignore) | (content == air)) po = plist[opaques] pno = plist[~opaques] cdata[po] = content[opaques] hdata[po] = chunkypos + y dnddata[po] = day_night_differs plist = plist[~opaques] - y-=1 + y -= 1 return plist -def map_block_ug(mapdata, version, ypos, maxy, cdata, hdata, udata, uhdata, dnddata, day_night_differs, id_map, ignore, air, underground, face_swap_order): + +def map_block_ug(mapdata, version, ypos, maxy, cdata, hdata, udata, + uhdata, dnddata, day_night_differs, id_map, ignore, + air, underground, face_swap_order): chunkypos = ypos * 16 mapdata = mapdata[:4096] mapdata = id_map[mapdata] - if (mapdata==ignore).all(): - return (~( (cdata == ignore) | (cdata == air) )).all() - (swap1a,swap1b),(swap2a,swap2b) = face_swap_order[1:] - mapdata = numpy.swapaxes(mapdata.reshape(16,16,16),swap1a,swap1b) - mapdata = numpy.swapaxes(mapdata,swap2a,swap2b).reshape(16,256) - if face_swap_order[0]>0: - r = list(range(maxy,-1,-1)) + if (mapdata == ignore).all(): + return (~((cdata == ignore) | (cdata == air))).all() + (swap1a, swap1b), (swap2a, swap2b) = face_swap_order[1:] + mapdata = numpy.swapaxes(mapdata.reshape(16, 16, 16), swap1a, swap1b) + mapdata = numpy.swapaxes(mapdata, swap2a, swap2b).reshape(16, 256) + if face_swap_order[0] > 0: + r = list(range(maxy, -1, -1)) else: - r = list(range(maxy,16,1)) - y=maxy + r = list(range(maxy, 16, 1)) + y = maxy for y in r: content = mapdata[y] - opaques = ~( (content == ignore) | (content == air) ) - copaques = ~( (cdata == ignore) | (cdata == air) ) + opaques = ~((content == ignore) | (content == air)) + copaques = ~((cdata == ignore) | (cdata == air)) air = (content == air) cdata[~copaques] = content[~copaques] hdata[~copaques] = chunkypos + y dnddata[~copaques] = day_night_differs - uhdata += (udata==0)*(chunkypos + y)*(air * copaques)*(~opaques)*underground + uhdata += ((udata == 0) * (chunkypos + y) * (air * copaques) + * (~opaques) * underground) udata += (air * copaques)*(~opaques)*underground - return (~( (cdata == ignore) | (cdata == air) )).all() + return (~((cdata == ignore) | (cdata == air))).all() # y-=1 + def get_db(args): if not os.path.exists(args.world_dir+"world.mt"): return None @@ -459,7 +554,7 @@ def get_db(args): keyvals = f.read().splitlines() keyvals = [kv.split("=") for kv in keyvals] backend = None - for k,v in keyvals: + for k, v in keyvals: if k.strip() == "backend": backend = v.strip() break @@ -468,6 +563,7 @@ def get_db(args): if backend == "leveldb": return LVLDB(args.world_dir + "map.db") + class SQLDB: def __init__(self, path): import sqlite3 @@ -482,15 +578,17 @@ class SQLDB: if not r: break x, y, z = getIntegerAsBlock(r[0]) - yield x,y,z,r[0] + yield x, y, z, r[0] def get(self, pos): - self.cur.execute("SELECT `data` FROM `blocks` WHERE `pos`==? LIMIT 1", (pos,)) + self.cur.execute("SELECT `data` FROM `blocks`" + " WHERE `pos`==? LIMIT 1", (pos,)) r = self.cur.fetchone() if not r: return return BytesIO(r[0]) + class LVLDB: def __init__(self, path): import leveldb @@ -498,32 +596,26 @@ class LVLDB: def __iter__(self): for k in self.conn.RangeIter(): - #if k is not None and len(k)>0: try: - #print("getting int from first index of value "+str(k)) - #SOMETIMES has an integer, such as getting int from first index of value ('905945065', '\x19\x01\x02\x02x\x9c\xedX\xdbu\xdb0\x0c\xa5\xa2Jv\xbf|\xbaBW\xe8\n]!+t\x85\xac\x90\x15\xbc\x82V\xd0\n\x1a\xaa\xa4\x14\x8a\xc0%\xf8\x90DK~\xe4\xf2\x9c\xc4 \xf8\x00pA\x90\xb6R\x04\x95noc\xab\xe6\xcf\x1cV_\xa2\xd5\xba\xfd\x88\xe8\xa6\xe6\xfa*\xd2$\xbd?\x17\xf5\xae\xb7!\x9f[-\xb5_\xad\x9e\xff\xd7D\xa2\xcd\xaeE\xc7\xd7L\xc3wkE\x1bC>\xe6\xeb\x9d\xce\x1f\xc5\xfd\xaf\xa3-\x86*\xa8w;\xb9]\x97\xeb\xeb\x05\xfaJ\xc8\xc3p~H\xeb\xf3H4\xe4SK\xf8\xb7\xac\xc9\xec[\x96\x1b\xc8\x1a\x9f\x9fv^\xc7\xcf\x0cjK\x93`\xffM\\\x9d\xe7\x06\x8e\x90\xf2\x7f\r\xffa\xfdr\xfen\xab\xaf\xbc\xfc\xa8\xbc\xf9~\x1cm\x0cN\x1e\xdf\x86\x97Fau\xa0\x8d\xf7\x86\xf9i2\xf8[\xaa\x0f\xd5\xb7\xd2\xfc\xc7Aw\xdc\xe8Z|~\x13\xcd\x8e4\x7f\x92\x17!\xfdY\xe0W\x9e5\xe5eS\x88\xffc\xf52\xfb\xf45\x93\xc3?2\xee\x98Oq\xefF\xd7\xde|~\xfe\xa5\xf3G\xad<\x0bc\xb8^:\xbfUt\xfd0\xff\xcd\xc6\xf3o9\xb8\xad>\xc4n\x9e>\x9d\x01\x9c\xfd:\xc0i\x0e\xff\xf4\xe4\xbb\xdc\xe1\xaf\x91\x18\xff\x12\xbb\xf5\x1c\xa5\xd0|\xe7\xa5\xac\x0f\xf1O\xe7\xafG\x9c\xbd=\xf4)\xfeC5\x96GE\xee\xcdc\x9e\xe6JM\xfeO\xf3+\xf2\x16\t\xf1\x1f\xe3o\xf22\xc5\x7fx\x87X\xed\xd8\xce\xff\xf1\x88\xf3\xaf\xb2\xf8\x975\xfe[ 7\x03h\x1eL6\xdao\xacM \xfe\x95\xa8\xa3\xfc\x84\xf2\'\xa5\xb7\xde\xc8\xab?>\xff)\xa4_\x00\xa1\xb6\xf4\xf4\xe3\xf9\x9fz-&\xf6OQ\xfe$\x8e\xa8\xbeN\xcc\x8f\xf1\xefn\xfco\xfei\xe3\xbf\x18\xf8\x19\xe0\xeb9\xf3\x16\xee\xd4\xf3L@\xfd\xfa\xf3\xdd\x88\x0c\xa6\xe6\xc7o\xffo\xfe\xf17#\xcc\x00I\xdf\xea\xdb\xd4\xf2\x8c;\xe1-\xe0\x90\x8a\x7f\x9a\x9f5z\xbc\xeb^\x8d\xff\x18\xbb\x9c\x7fiL\xcb\xd6\xc1\x1a\x80\xf1\x93bLQ\x82_\xac\x00\xd2|:\x86Z-\xe9\x9f\x1dq~9\xff\xfe\x88\xd6[\x0bO9\xee\x869\x80H\x9d\xbfZ\xfd\xdc8\x9f\xf3\x8b\x1e\xe1;\xe0\xd9\x11\xe7\xf7\r\xc6\xe6\xf3_\x0b\xe7\xbf\x0c\xffe\xf5\xe8\x13V\x90gG\x95\xc8\x00\x1c\xcb\xb5\r\xac\xe6\xa28\xfd\xb6\xd7\x82>\xc5\x7f\xbc\xbe\xdfB_\x01\xfb\xa9\xfcy6 \xff\xc80"v\xfe\x9d\xbe\r\xc6\xef\xde\xce\xbf_\xff\xc5\xf9\xc30\x98\xbf=\x915\xfanp\xe8\xfb\xbe\xbb\xf6\x06V\xa9\xc7u\x1a_\xf28\xefz\xd5"1\xe7S\xcb\xfd\xacU\xeaCwt\xbdZ\x86?\xbf\x7f9\xe1\xe3\xdf\xfb_\xda\xf1\t\x1d\\66*"_\xf3w\x1d\xadv\x9e)\x13\x005\xbao\xe5n\xf4\x9f\xc9\xd4}\x13M#[\xbd\x91\xa7x\xe4\x1b\xa1\x11\xf7\xb7\xbb\xb2\x8e^\x81\xdc\xd9\tv\xf857\x04\xa3\xe3\xd4\x7fc\xff\xc8>\x91\xbb.!Sg\x07\x1b\x0f\x9bQYv\x00\xbf\xe0_?\x07`\xdab\x96\x95(k1?\x05H^\x7f-\xd63\xf2\xf4\xe2\x1d\xc8\xa8\xe7\\\x8f\x1d\xf4Hu\x19Vh\xf3\xa9\x08\xfe\xcd\x0e\xba-|\x99Y@\xc58\xc0}\xb3Y\x9f\x90q<\xa4\xfa\xc0\xdc\xcf\xcd\x00\x06t\xc0x\xa4\xdeAF=5\xa9[T\x03\x16\xca8\xdf_\x90#\xdf\x94y\x05H\x9a\x94\xacX\x90s\x93\xce\x0e/,+\x8c\x80\x1aK\xca\x88\xd56\xf1\x99~R\xf1\x94\\\x13\xf3\x92@\xff{\x87\x9dv\xdfa\x9b\xe8\xfe\x88W\xf6\x7f\xec\x83\x00\x98ke\xc1\x19]\xbc\xff\x8d\x96\xce\xde\xdf\xab\x7f~\x00z\x16\x80\xa1\\j\xac\xad\xba%\xe1\xd7\xff\x01N\x80)\xd2,\x00\xe5,\xbe\x03\xffi\x04l\x07V\x00\xffV+\xbbs\x99\xd56\x9b\xb1\xbb\xff\xea>\xfc\xf7x\x18\xb8\xff\x90\xff\xc5\xfd/\xb4\xd8f;B\xfe\xef\x9c\xff\x97\xcb\xf8\xe7\xe2:6mr\x19\xb1i\x89cP\xc6\xe6\x0b.v\x1aQd\xed\x07\x84\xcd\x84\x87\xcc\x88\xed\xb8\x88\x1fo\x85sx\xf7c@\r\xd8!\x03\xbc-v\x0e@b\xfb\xe2\xd6\xa4Czl\x06@\xd5\xdbl\x0c\x96\xd1\xf4=sl\xd9)_\xf5\xbd\x8b\xe4\xf0#\xfe\x8d\xf58kP\xf9A\x1fN\xabq\x86\x00<\xea\xcbq-\x8c\xbb\xe73\xf6\x1dc\xcb\x11\x98\xbe*x\xaf\x96\xbb{\xc6\x94\x82\xfcU\xc0{5\xbc\x9a\xff\xc9K\xfcF\xe6\xec\x8e\xd3\xe9\xbe\xbe\x0c\xfe\x07\x80pRrx\x9cc\x00\x00\x00\x01\x00\x01\x00\x00\x00\xff\xff\xff\xff\x00\x00\n\x00\x00\x00\x13default:lava_source\x00\x01\x00\x14default:lava_flowing\x00\x02\x00\x03air\x00\x03\x00\rdefault:stone\x00\x04\x00\x17default:stone_with_mese\x00\x05\x00\x17default:stone_with_coal\x00\x06\x00\x0etechnic:marble\x00\x07\x00\x14moreores:mineral_tin\x00\x08\x00\x17default:stone_with_iron\x00\t\x00\x17moreores:mineral_silver\n\x00\x00') val = k[0] - #if k[0][:2]=="\\x": - #in leveldb, minetest stores \x before every byte of the value, so remove all and prepend 0x so python can convert to int - #but this doesn't work (resulting val still throws exception) - #val = "\\0x" + k[0].replace("\\x", "") x, y, z = getIntegerAsBlock(int(val)) yield x, y, z, val except Exception as e: - print("Could not finish getting int from first index of value "+str(k)) - #SOMETIMES prints tons of output such as Could not finish getting int from first index of value ('\x00\x00\x00\x0e\x00\x02\x85 ', '\x19\x08\x02\x02x\x9c\xed\xc11\x01\x00\x00\x00\xc2\xa0\xf5Om\x0c\x1f\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xb7\x01@\x00\x00\x01x\x9cc\x00\x00\x00\x01\x00\x01\x00\x00\x00\xff\xff\xff\xff\x00\x00\x01\x00\x00\x00\x06ignore\n\x00\x00') + print("Could not finish getting int from first index of" + " value {}".format(k)) raise e def get(self, pos): return BytesIO(self.conn.Get(pos)) -R_MSG = ("<(The following issue occurred while handling the exception)" - " Could not finish writing r error since r was not" +R_MSG = ("<(The following issue occurred while handling the exception" + " above) Could not finish writing r error since r was not" " initialized>") + class World: - def __init__(self,args): + def __init__(self, args): self.r_error_enable = True self.xlist = [] self.zlist = [] @@ -535,13 +627,13 @@ class World: self.maxz = None self.mapinfo = None - def facing(self,x,y,z): - if self.args.facing in ['up','down']: - return x,y,z - if self.args.facing in ['east','west']: - return z,x,y - if self.args.facing in ['north','south']: - return x,z,y + def facing(self, x, y, z): + if self.args.facing in ['up', 'down']: + return x, y, z + if self.args.facing in ['east', 'west']: + return z, x, y + if self.args.facing in ['north', 'south']: + return x, z, y def generate_sector_list(self): ''' @@ -549,12 +641,17 @@ class World: resulting picture. ''' args = self.args - sector_xmin,sector_xmax,sector_zmin,sector_zmax = numpy.array(args.region)/16 + ( + sector_xmin, + sector_xmax, + sector_zmin, + sector_zmax + ) = numpy.array(args.region)/16 sector_ymin = args.minheight/16 sector_ymax = args.maxheight/16 xlist = [] zlist = [] - self.lookup={} + self.lookup = {} self.db = get_db(args) if self.db is not None: for x, y, z, pos in self.db: @@ -567,33 +664,35 @@ class World: x, y, z = self.facing(x, y, z) try: - self.lookup[(x,z)].append((y,pos)) + self.lookup[(x, z)].append((y, pos)) except KeyError: - self.lookup[(x,z)]=[(y,pos)] + self.lookup[(x, z)] = [(y, pos)] xlist.append(x) zlist.append(z) else: - legacy_sector_scan(args, sectors_xmin, sector_xmax, sector_zmin, sector_zmax) + legacy_sector_scan(args, sectors_xmin, sector_xmax, + sector_zmin, sector_zmax) - if len(xlist)>0: + if len(xlist) > 0: # Get rid of duplicates - self.xlist, self.zlist = list(zip(*sorted(set(zip(xlist, zlist))))) + self.xlist, self.zlist = list(zip(*sorted(set(zip(xlist, + zlist))))) self.minx = min(xlist) self.minz = min(zlist) self.maxx = max(xlist) self.maxz = max(zlist) - x0,x1,z0,z1 = numpy.array(args.region) + x0, x1, z0, z1 = numpy.array(args.region) y0 = args.minheight y1 = args.maxheight - self.minypos = self.facing(int(x0),int(y0),int(z0))[1] - self.maxypos = self.facing(int(x1),int(y1),int(z1))[1] + self.minypos = self.facing(int(x0), int(y0), int(z0))[1] + self.maxypos = self.facing(int(x1), int(y1), int(z1))[1] self.w = (self.maxx - self.minx) * 16 + 16 self.h = (self.maxz - self.minz) * 16 + 16 - def generate_map_info(self,str_to_uid): + def generate_map_info(self, str_to_uid): read_map_time = 0 db = self.db xlist = self.xlist @@ -606,27 +705,26 @@ class World: w = self.w h = self.h - #x,y,z becomes y,x,z for up/down + # x,y,z becomes y,x,z for up/down # becomes x,z,y for east/west # becomes z,x,y for north/south - if args.facing in ['up','down']: - face_swap_order = [1,(1,0),(1,2)] - elif args.facing in ['east','west']: - face_swap_order = [1,(2,0),(2,1)] - elif args.facing in ['north','south']: - face_swap_order = [1,(0,0),(1,2)] - if args.facing in ['up','east','north']: + if args.facing in ['up', 'down']: + face_swap_order = [1, (1, 0), (1, 2)] + elif args.facing in ['east', 'west']: + face_swap_order = [1, (2, 0), (2, 1)] + elif args.facing in ['north', 'south']: + face_swap_order = [1, (0, 0), (1, 2)] + if args.facing in ['up', 'east', 'north']: face_swap_order[0] = -1 mapinfo = { - 'height':numpy.zeros([w,h],dtype = 'i2'), - 'content':numpy.zeros([w,h],dtype='u2'), - 'water':numpy.zeros([w,h],dtype = 'u2'), - 'dnd':numpy.zeros([w,h],dtype=bool)} + 'height': numpy.zeros([w, h], dtype='i2'), + 'content': numpy.zeros([w, h], dtype='u2'), + 'water': numpy.zeros([w, h], dtype='u2'), + 'dnd': numpy.zeros([w, h], dtype=bool)} if args.drawunderground: - mapinfo['underground'] = numpy.zeros([w,h],dtype = 'u2') - mapinfo['undergroundh'] = numpy.zeros([w,h],dtype = 'i2') - + mapinfo['underground'] = numpy.zeros([w, h], dtype='u2') + mapinfo['undergroundh'] = numpy.zeros([w, h], dtype='i2') unknown_node_names = set() unknown_node_ids = set() @@ -634,8 +732,8 @@ class World: starttime = time.time() # Go through all sectors. for n in range(len(xlist)): - #if n > 500: - # break + # if n > 500: + # break if n % 200 == 0: nowtime = time.time() dtime = nowtime - starttime @@ -649,10 +747,11 @@ class World: remaining_s = time_guess - dtime remaining_minutes = int(remaining_s / 60) remaining_s -= remaining_minutes * 60 - print(("Processing sector " + str(n) + " of " + str(len(xlist)) - + " (" + str(round(100.0 * n / len(xlist), 1)) + "%)" - + " (ETA: " + str(remaining_minutes) + "m " - + str(int(remaining_s)) + "s)")) + print("Processing sector " + str(n) + " of " + + str(len(xlist)) + " (" + + str(round(100.0 * n / len(xlist), 1)) + "%)" + + " (ETA: " + str(remaining_minutes) + "m " + + str(int(remaining_s)) + "s)") xpos = xlist[n] zpos = zlist[n] @@ -662,50 +761,59 @@ class World: sectortype = "" if db is not None: - ymin = self.minypos/16 #-2048 if args.minheight is None else args.minheight/16+1 - ymax = self.maxypos/16+1 #2047 if args.maxheight is None else args.maxheight/16+1 - for k in self.lookup[(xpos,zpos)]: + ymin = self.minypos / 16 + # -2048 if args.minheight is None + # else args.minheight/16+1 + ymax = self.maxypos / 16 + 1 + # 2047 if args.maxheight is None + # else args.maxheight/16+1 + for k in self.lookup[(xpos, zpos)]: ylist.append(k) sectortype = "sqlite" else: - sectortype,sector_data = legacy_fetch_ylist(args,xpos,zpos,ylist) + sectortype, sector_data = legacy_fetch_ylist(args, xpos, + zpos, + ylist) if sectortype == "": continue ylist.sort() - if face_swap_order[0]>0: + if face_swap_order[0] > 0: ylist.reverse() - if args.facing in ['south','west','down']: + if args.facing in ['south', 'west', 'down']: miny = self.minypos-1 else: miny = self.maxypos+1 - # Create map related info for the sector that will be filled as we seek down the y axis - cdata = numpy.zeros(256,dtype='i4') - hdata = numpy.ones(256,dtype='i4')*miny - wdata = numpy.zeros(256,dtype='i4') - dnddata = numpy.zeros(256,dtype=bool) + # Create map related info for the sector that will be filled + # as we seek down the y axis + cdata = numpy.zeros(256, dtype='i4') + hdata = numpy.ones(256, dtype='i4')*miny + wdata = numpy.zeros(256, dtype='i4') + dnddata = numpy.zeros(256, dtype=bool) if args.drawunderground: - udata = numpy.zeros(256,dtype='i4') - uhdata = numpy.zeros(256,dtype='i4') + udata = numpy.zeros(256, dtype='i4') + uhdata = numpy.zeros(256, dtype='i4') plist = numpy.arange(256) # Go through the Y axis from top to bottom. - for ypos,ps in ylist: + for ypos, ps in ylist: try: if db is not None: f = db.get(ps) else: - f = legacy_fetch_sector_data(args, sectortype, sector_data, ypos) + f = legacy_fetch_sector_data(args, sectortype, + sector_data, ypos) - # Let's just memorize these even though it's not really necessary. + # Let's just memorize these even though it's not + # really necessary. version = readU8(f) flags = f.read(1) - #print("version="+str(version)) - #print("flags="+str(version)) + # print("version="+str(version)) + # print("flags="+str(version)) # Check flags is_underground = ((ord(flags) & 1) != 0) @@ -713,10 +821,10 @@ class World: lighting_expired = ((ord(flags) & 4) != 0) generated = ((ord(flags) & 8) != 0) - #print("is_underground="+str(is_underground)) - #print("day_night_differs="+str(day_night_differs)) - #print("lighting_expired="+str(lighting_expired)) - #print("generated="+str(generated)) + # print("is_underground="+str(is_underground)) + # print("day_night_differs="+str(day_night_differs)) + # print("lighting_expired="+str(lighting_expired)) + # print("generated="+str(generated)) if version >= 22: content_width = readU8(f) @@ -726,28 +834,29 @@ class World: dec_o = zlib.decompressobj() try: s = dec_o.decompress(f.read()) - mapdata = numpy.fromstring(s,">u2") + mapdata = numpy.fromstring(s, ">u2") except: mapdata = [] # Reuse the unused tail of the file - f.close(); + f.close() f = BytesIO(dec_o.unused_data) - #print("unused data: "+repr(dec_o.unused_data)) + # print("unused data: " + repr(dec_o.unused_data)) # zlib-compressed node metadata list dec_o = zlib.decompressobj() try: s = dec_o.decompress(f.read()) - metaliststr = numpy.frombuffer(s,"u1") + metaliststr = numpy.frombuffer(s, "u1") # And do nothing with it except: metaliststr = [] # Reuse the unused tail of the file - f.close(); + f.close() f = BytesIO(dec_o.unused_data) - #print("* dec_o.unused_data: "+repr(dec_o.unused_data)) + # print("* dec_o.unused_data: " + # + repr(dec_o.unused_data)) data_after_node_metadata = dec_o.unused_data if version <= 21: @@ -755,12 +864,12 @@ class World: readU16(f) if version == 23: - readU8(f) # Unused node timer version (always 0) + readU8(f) # Unused node timer version--always 0 if version == 24: ver = readU8(f) if ver == 1: num = readU16(f) - for i in range(0,num): + for i in range(0, num): readU16(f) readS32(f) readS32(f) @@ -782,7 +891,7 @@ class World: data = f.read(data_size) timestamp = readU32(f) - #print("* timestamp="+str(timestamp)) + # print("* timestamp="+str(timestamp)) id_to_name = {} name_to_id = {} @@ -791,7 +900,8 @@ class World: if version >= 22: name_id_mapping_version = readU8(f) num_name_id_mappings = readU16(f) - #print("* num_name_id_mappings: "+str(num_name_id_mappings)) + # print("* num_name_id_mappings: " + # + str(num_name_id_mappings)) for i in range(0, num_name_id_mappings): node_id = readU16(f) name_len = readU16(f) @@ -799,7 +909,6 @@ class World: try: id_to_name[node_id] = str_to_uid[name] except: - ##TODO: Add to list of unknown colors unknown_node_names.add(name) unknown_node_ids.add(node_id) id_to_name[node_id] = 0 @@ -807,21 +916,25 @@ class World: air = id_to_name[node_id] if name == 'ignore': ignore = id_to_name[node_id] - if len(id_to_name)==0: - id_map = numpy.array([0,1],dtype='i4') + if len(id_to_name) == 0: + id_map = numpy.array([0, 1], dtype='i4') else: - id_map = numpy.array([id_to_name[i] for i in sorted(id_to_name)],dtype='i4') + id_map = numpy.array( + [id_to_name[i] for i in sorted(id_to_name)], + dtype='i4' + ) # Node timers if version >= 25: timer_size = readU8(f) num = readU16(f) - for i in range(0,num): + for i in range(0, num): readU16(f) readS32(f) readS32(f) - ##facing in down,south,west use maxheight, otherwise use minheight - if face_swap_order[0]>0: + # if facing in down, south, west: use maxheight; + # else use minheight + if face_swap_order[0] > 0: maxy = 15 if ypos*16 + 15 > self.maxypos: maxy = self.maxypos - ypos*16 @@ -829,38 +942,58 @@ class World: maxy = 0 if ypos*16 + 15 < self.minypos: maxy = ypos*16 - self.minypos - if maxy>=0: + if maxy >= 0: if args.drawunderground: - plist = map_block_ug(mapdata, version, ypos, maxy, cdata, hdata, udata, uhdata, dnddata, day_night_differs, id_map, ignore, air, is_underground, face_swap_order) + plist = map_block_ug(mapdata, version, ypos, + maxy, cdata, hdata, + udata, uhdata, dnddata, + day_night_differs, + id_map, ignore, air, + is_underground, + face_swap_order) else: - plist = map_block(mapdata, version, ypos, maxy, plist, cdata, hdata, dnddata, day_night_differs, id_map, ignore, air, face_swap_order) - ##plist = map_block(mapdata, version, ypos, maxy, cdata, hdata, dnddata, day_night_differs, id_map, ignore, air, face_swap_order) - # After finding all the pixels in the sector, we can move on to - # the next sector without having to continue the Y axis. - if (not args.drawunderground and len(plist) == 0) or ypos==ylist[-1][0]: - ##if plist == True or ypos==ylist[-1][0]: + plist = map_block(mapdata, version, ypos, + maxy, plist, cdata, hdata, + dnddata, + day_night_differs, + id_map, ignore, air, + face_swap_order) + # plist = map_block(mapdata, version, ypos, + # maxy, cdata, hdata, dnddata, + # day_night_differs, id_map, ignore, air, + # face_swap_order) + # After finding all the pixels in the sector, we can + # move on to the next sector without having to + # continue the Y axis. + # if plist == True or ypos==ylist[-1][0]: + if ((not args.drawunderground and len(plist) == 0) + or (ypos == ylist[-1][0])): chunkxpos = (xpos-minx)*16 chunkzpos = (zpos-minz)*16 - if True: #face_swap_order[0]<0: + if True: # face_swap_order[0]<0: pass - #chunkxpos = (maxx-minx)*16 - chunkxpos #-16? - #chunkzpos = (maxz-minz)*16 - chunkzpos #-16? - pos = (slice(chunkxpos,chunkxpos+16),slice(chunkzpos,chunkzpos+16)) - mapinfo['height'][pos] = hdata.reshape(16,16) - mapinfo['content'][pos] = cdata.reshape(16,16) - mapinfo['water'][pos] = wdata.reshape(16,16) - mapinfo['dnd'][pos] = dnddata.reshape(16,16) + # chunkxpos = (maxx-minx)*16-chunkxpos #-16? + # chunkzpos = (maxz-minz)*16-chunkzpos #-16? + pos = (slice(chunkxpos, chunkxpos+16), + slice(chunkzpos, chunkzpos+16)) + mapinfo['height'][pos] = hdata.reshape(16, 16) + mapinfo['content'][pos] = cdata.reshape(16, 16) + mapinfo['water'][pos] = wdata.reshape(16, 16) + mapinfo['dnd'][pos] = dnddata.reshape(16, 16) if args.drawunderground: - mapinfo['underground'][pos] = udata.reshape(16,16) - mapinfo['undergroundh'][pos] = uhdata.reshape(16,16) + mapinfo['underground'][pos] = \ + udata.reshape(16, 16) + mapinfo['undergroundh'][pos] = \ + uhdata.reshape(16, 16) break except Exception as e: - print(("Error at ("+str(xpos)+","+str(ypos)+","+str(zpos)+"):")) + print("Error at {}:".format((xpos, ypos, zpos))) traceback.print_exc() + sys.stdout.write(os.linesep) sys.stdout.write("Block data: ") try: for c in r[0]: - sys.stdout.write("%2.2x "%ord(c)) + sys.stdout.write("%2.2x " % ord(c)) except NameError: if self.r_error_enable: sys.stdout.write(R_MSG) @@ -868,24 +1001,33 @@ class World: self.r_error_enable = False sys.stdout.write(os.linesep) sys.stdout.write("Data after node metadata:") + d_a_n_md = data_after_node_metadata try: count = 0 for c in data_after_node_metadata: - sys.stdout.write("%2.2x "%ord(c)) + sys.stdout.write("%2.2x " % ord(c)) count += 1 if count == 0: - sys.stdout.write("<(The following issue occurred while handling the exception) Uh oh, got {}: zero characters to convert to ord>".format(data_after_node_metadata)) + sys.stdout.write("<(The following issue" + " occurred while handling" + " the exception above) Uh" + " oh, got {}: zero" + " characters to convert to" + " ord>".format(d_a_n_md)) except TypeError: - sys.stdout.write("<(The following issue occurred while handling the" - + " Exception) Uh oh, expected characters in" - " data_after_node_metadata; got:") + sys.stdout.write("<(The following issue" + " occurred while handling the" + " exception above) Uh oh," + " expected characters in" + " data_after_node_metadata;" + " got:") sys.stdout.flush() - sys.stdout.write(os.linesep)(type(data_after_node_metadata) + " length " - + str(len(data_after_node_metadata)) - + ":>") + sys.stdout.write(str(len(d_a_n_md)) + "-length " + + type(d_a_n_md).__name__ + + ":>") sys.stdout.write(os.linesep) sys.stdout.write(os.linesep) - exit(1) # stop HUGE stdout + exit(1) # stop HUGE stdout self.mapinfo = mapinfo if unknown_node_names: sys.stdout.write("Unknown node names:") @@ -899,7 +1041,8 @@ class World: sys.stdout.write(os.linesep) # print str_to_uid -def draw_image(world,uid_to_color): + +def draw_image(world, uid_to_color): # Drawing the picture args = world.args stuff = world.mapinfo @@ -909,142 +1052,247 @@ def draw_image(world,uid_to_color): maxz = world.maxz w = world.w h = world.h - reverse_dirs = ['east','south','up'] + reverse_dirs = ['east', 'south', 'up'] print("Drawing image") starttime = time.time() border = 40 if args.drawscale else 0 - im = Image.new("RGB", (w*args.pixelspernode + border, h*args.pixelspernode + border), args.bgcolor) + im = Image.new( + "RGB", + (w*args.pixelspernode + border, h*args.pixelspernode + border), + args.bgcolor + ) draw = ImageDraw.Draw(im) - if args.pixelspernode>1: - stuff['content'] = stuff['content'].repeat(args.pixelspernode,axis=0).repeat(args.pixelspernode,axis=1) - stuff['dnd'] = stuff['dnd'].repeat(args.pixelspernode,axis=0).repeat(args.pixelspernode,axis=1) - stuff['height'] = stuff['height'].repeat(args.pixelspernode,axis=0).repeat(args.pixelspernode,axis=1) - stuff['water'] = stuff['water'].repeat(args.pixelspernode,axis=0).repeat(args.pixelspernode,axis=1) + if args.pixelspernode > 1: + stuff['content'] = stuff['content'].repeat( + args.pixelspernode, + axis=0 + ).repeat(args.pixelspernode, axis=1) + stuff['dnd'] = stuff['dnd'].repeat( + args.pixelspernode, + axis=0 + ).repeat(args.pixelspernode, axis=1) + stuff['height'] = stuff['height'].repeat( + args.pixelspernode, + axis=0 + ).repeat(args.pixelspernode, axis=1) + stuff['water'] = stuff['water'].repeat( + args.pixelspernode, + axis=0 + ).repeat(args.pixelspernode, axis=1) if args.facing in reverse_dirs: - stuff['content'] = stuff['content'][::-1,:] - stuff['dnd'] = stuff['dnd'][::-1,:] - stuff['height'] = stuff['height'][::-1,:] - stuff['water'] = stuff['water'][::-1,:] + stuff['content'] = stuff['content'][::-1, :] + stuff['dnd'] = stuff['dnd'][::-1, :] + stuff['height'] = stuff['height'][::-1, :] + stuff['water'] = stuff['water'][::-1, :] - count_dnd=0 - count_height=0 - count_zero=0 + count_dnd = 0 + count_height = 0 + count_zero = 0 c = stuff['content'] dnd = stuff['dnd'] hgh = stuff['height'] - c0 = c[1:,:-1] - c1 = c[:-1,1:] + c0 = c[1:, :-1] + c1 = c[:-1, 1:] c2 = c[1:, 1:] - dnd0 = dnd[1:,:-1] - dnd1 = dnd[:-1,1:] + dnd0 = dnd[1:, :-1] + dnd1 = dnd[:-1, 1:] dnd2 = dnd[1:, 1:] - h0 = hgh[1:,:-1] - h1 = hgh[:-1,1:] + h0 = hgh[1:, :-1] + h1 = hgh[:-1, 1:] h2 = hgh[1:, 1:] drop = (2*h0 - h1 - h2) * 12 - if args.facing in ['east','north','up']: + if args.facing in ['east', 'north', 'up']: drop = -drop - drop = numpy.clip(drop,-32,32) + drop = numpy.clip(drop, -32, 32) - if args.fog>0: - fogstrength = 1.0* (stuff['height']-stuff['height'].min())/(stuff['height'].max()-stuff['height'].min()) + if args.fog > 0: + fogstrength = ( + 1.0 * (stuff['height'] - stuff['height'].min()) + / (stuff['height'].max() - stuff['height'].min()) + ) if args.facing in reverse_dirs: fogstrength = 1-fogstrength fogstrength = args.fog * fogstrength - fogstrength = fogstrength[:,:,numpy.newaxis] + fogstrength = fogstrength[:, :, numpy.newaxis] if args.drawunderground: ugcoeff = 0.9 if args.drawunderground == 2 else 0.4 - ugstrength = 1.0*(stuff['underground'])/6 #normalize so that 6 blocks of air underground is considered "big" - ugstrength = (ugstrength>0)*0.1 + ugcoeff*ugstrength - ugstrength = ugstrength - (ugstrength-0.75)*(ugstrength>0.75) - ugstrength = ugstrength[:,:,numpy.newaxis] - print(('ugmin',stuff['undergroundh'].min())) - print(('ugmax',stuff['undergroundh'].max())) - ugdepth = 1.0* (stuff['undergroundh']-stuff['undergroundh'].min())/(stuff['undergroundh'].max()-stuff['undergroundh'].min()) - ugdepth = ugdepth[:,:,numpy.newaxis] + # normalize so that 6 blocks of air underground is considered + # "big": + ugstrength = 1.0 * (stuff['underground']) / 6 + ugstrength = (ugstrength > 0) * 0.1 + ugcoeff * ugstrength + ugstrength = (ugstrength - (ugstrength - 0.75) + * (ugstrength > 0.75)) + ugstrength = ugstrength[:, :, numpy.newaxis] + print(('ugmin', stuff['undergroundh'].min())) + print(('ugmax', stuff['undergroundh'].max())) + ugdepth = ( + 1.0 * (stuff['undergroundh']-stuff['undergroundh'].min()) + / (stuff['undergroundh'].max()-stuff['undergroundh'].min()) + ) + ugdepth = ugdepth[:, :, numpy.newaxis] u = stuff['underground'] - u0 = u[1:,:-1]>0 - u1 = u[:-1,1:]>0 - u2 = u[1:, 1:]>0 + u0 = u[1:, :-1] > 0 + u1 = u[:-1, 1:] > 0 + u2 = u[1:, 1:] > 0 hgh = stuff['undergroundh'] - h0 = hgh[1:,:-1] - h1 = hgh[:-1,1:] + h0 = hgh[1:, :-1] + h1 = hgh[:-1, 1:] h2 = hgh[1:, 1:] dropg = (2*h0 - h1 - h2) * 12 * u0 * u1 * u2 if args.facing in reverse_dirs: dropg = -dropg - dropg = numpy.clip(dropg,-32,32) + dropg = numpy.clip(dropg, -32, 32) - - if args.drawunderground < 2: #normal map or cave with map overlay - colors = numpy.array([args.bgcolor,args.bgcolor]+[uid_to_color[c] for c in sorted(uid_to_color)],dtype = 'i2') + if args.drawunderground < 2: # regular map or cave with map overlay + colors = numpy.array( + [args.bgcolor, args.bgcolor] + + [uid_to_color[c] for c in sorted(uid_to_color)], + dtype='i2' + ) else: - colors = numpy.array([args.bgcolor,args.bgcolor]+[args.bgcolor for c in sorted(uid_to_color)],dtype = 'i2') + colors = numpy.array( + [args.bgcolor, args.bgcolor] + + [args.bgcolor for c in sorted(uid_to_color)], + dtype='i2' + ) pix = colors[stuff['content']] if args.drawunderground < 2: - pix[1:,:-1] += drop[:,:,numpy.newaxis] - pix = numpy.clip(pix,0,255) - if args.fog>0: + pix[1:, :-1] += drop[:, :, numpy.newaxis] + pix = numpy.clip(pix, 0, 255) + if args.fog > 0: pix = args.fogcolor*fogstrength + pix*(1-fogstrength) - pix = numpy.clip(pix,0,255) + pix = numpy.clip(pix, 0, 255) if args.drawunderground: - ugpd = args.ugcolor*ugdepth + args.bgcolor * (1-ugdepth) ##average with background color based on depth (deeper caves will be more bg color) + # Average the color with background color based on depth (deeper + # caves will be closer to the bg color). + ugpd = args.ugcolor * ugdepth + args.bgcolor * (1-ugdepth) pix = ugpd*ugstrength + pix*(1-ugstrength) - pix[1:,:-1] += dropg[:,:,numpy.newaxis] - pix = numpy.clip(pix,0,255) + pix[1:, :-1] += dropg[:, :, numpy.newaxis] + pix = numpy.clip(pix, 0, 255) - pix = numpy.array(pix,dtype = 'u1') - impix = Image.fromarray(pix,'RGB') + pix = numpy.array(pix, dtype='u1') + impix = Image.fromarray(pix, 'RGB') impix = impix.transpose(Image.ROTATE_90) - im.paste(impix,(border,border)) - + im.paste(impix, (border, border)) if args.draworigin: - if args.facing in ['east','north','up']: - draw.ellipse(((w - (minx * -16 - 5))*args.pixelspernode + border, (h - minz * -16 - 6)*args.pixelspernode + border, - (w - (minx * -16 + 5))*args.pixelspernode + border, (h - minz * -16 + 4))*args.pixelspernode + border, - outline=args.origincolor) + if args.facing in ['east', 'north', 'up']: + draw.ellipse( + ( + (w - (minx * -16 - 5))*args.pixelspernode + border, + (h - minz * -16 - 6)*args.pixelspernode + border, + (w - (minx * -16 + 5))*args.pixelspernode + border, + (h - minz * -16 + 4) + )*args.pixelspernode + border, + outline=args.origincolor + ) else: - draw.ellipse(((minx * -16 - 5)*args.pixelspernode + border, (h - minz * -16 - 6)*args.pixelspernode + border, - (minx * -16 + 5)*args.pixelspernode + border, (h - minz * -16 + 4)*args.pixelspernode + border), - outline=args.origincolor) + draw.ellipse( + ( + (minx * -16 - 5) * args.pixelspernode + border, + (h - minz * -16 - 6) * args.pixelspernode + border, + (minx * -16 + 5) * args.pixelspernode + border, + (h - minz * -16 + 4) * args.pixelspernode + border + ), + outline=args.origincolor + ) font = ImageFont.load_default() if args.drawscale: - if args.facing in ['up','down']: + if args.facing in ['up', 'down']: draw.text((24, 0), "X", font=font, fill=args.scalecolor) draw.text((2, 24), "Z", font=font, fill=args.scalecolor) - elif args.facing in ['east','west']: + elif args.facing in ['east', 'west']: draw.text((24, 0), "Z", font=font, fill=args.scalecolor) draw.text((2, 24), "Y", font=font, fill=args.scalecolor) - elif args.facing in ['north','south']: + elif args.facing in ['north', 'south']: draw.text((24, 0), "X", font=font, fill=args.scalecolor) draw.text((2, 24), "Y", font=font, fill=args.scalecolor) if args.facing in reverse_dirs: for n in range(int(minx / -4) * -4, maxx+1, 4): - draw.text(((w - (minx * -16 + n * 16))*args.pixelspernode + border + 2, 0), str(n * 16), - font=font, fill=args.scalecolor) - draw.line(((w - (minx * -16 + n * 16))*args.pixelspernode + border, 0, - (w - (minx * -16 + n * 16))*args.pixelspernode + border, border - 1), fill=args.scalecolor) + draw.text( + ( + ( + w - (minx * -16 + n * 16) + ) * args.pixelspernode + border + 2, + 0 + ), + str(n * 16), + font=font, + fill=args.scalecolor + ) + draw.line( + ( + ( + w - (minx * -16 + n * 16) + ) * args.pixelspernode + border, + 0, + ( + w - (minx * -16 + n * 16) + ) * args.pixelspernode + border, + border - 1 + ), + fill=args.scalecolor + ) else: for n in range(int(minx / -4) * -4, maxx, 4): - draw.text(((minx * -16 + n * 16)*args.pixelspernode + border + 2 , 0), str(n * 16), - font=font, fill=args.scalecolor) - draw.line(((minx * -16 + n * 16)*args.pixelspernode + border, 0, - (minx * -16 + n * 16)*args.pixelspernode + border, border - 1), fill=args.scalecolor) + draw.text( + ( + ( + minx * -16 + n * 16 + ) * args.pixelspernode + border + 2, + 0 + ), + str(n * 16), + font=font, + fill=args.scalecolor + ) + draw.line( + ( + ( + minx * -16 + n * 16 + ) * args.pixelspernode + border, + 0, + ( + minx * -16 + n * 16 + ) * args.pixelspernode + border, + border - 1 + ), + fill=args.scalecolor + ) for n in range(int(maxz / 4) * 4, minz, -4): - draw.text((2, (h - 1 - (n * 16 - minz * 16))*args.pixelspernode + border), str(n * 16), - font=font, fill=args.scalecolor) - draw.line((0, (h - 1 - (n * 16 - minz * 16))*args.pixelspernode + border, border - 1, - (h - 1 - (n * 16 - minz * 16))*args.pixelspernode + border), fill=args.scalecolor) + draw.text( + ( + 2, + ( + h - 1 - (n * 16 - minz * 16) + ) * args.pixelspernode + border + ), + str(n * 16), + font=font, + fill=args.scalecolor + ) + draw.line( + ( + 0, + ( + h - 1 - (n * 16 - minz * 16) + )*args.pixelspernode + border, + border - 1, + ( + h - 1 - (n * 16 - minz * 16) + )*args.pixelspernode + border + ), + fill=args.scalecolor + ) if args.drawplayers: try: @@ -1062,18 +1310,32 @@ def draw_image(world,uid_to_color): position = p[2][1:-1].split(",") print((filename + ": position = " + p[2])) if len(name) < 0 and len(position) == 3: - x,y,z = [int(float(p)/10) for p in position] - x,y,z = world.facing(x,y,z) + x, y, z = [int(float(p)/10) for p in position] + x, y, z = world.facing(x, y, z) if args.facing in reverse_dirs: - x = (w - x - minx * 16)*args.pixelspernode - z = (h - z - minz * 16)*args.pixelspernode + x = (w - x - minx * 16) * args.pixelspernode + z = (h - z - minz * 16) * args.pixelspernode else: - x = (x - minx * 16)*args.pixelspernode - z = (h - z - minz * 16)*args.pixelspernode - draw.ellipse(((x - 2)*args.pixelspernode + border, (z - 2)*args.pixelspernode + border, - (x + 2)*args.pixelspernode + border, (z + 2)*args.pixelspernode + border), outline=args.playercolor) - draw.text(((x + 2)*args.pixelspernode + border, (z + 2)*args.pixelspernode + border), name, - font=font, fill=args.playercolor) + x = (x - minx * 16) * args.pixelspernode + z = (h - z - minz * 16) * args.pixelspernode + draw.ellipse( + ( + (x - 2) * args.pixelspernode + border, + (z - 2) * args.pixelspernode + border, + (x + 2) * args.pixelspernode + border, + (z + 2) * args.pixelspernode + border + ), + outline=args.playercolor + ) + draw.text( + ( + (x + 2) * args.pixelspernode + border, + (z + 2) * args.pixelspernode + border + ), + name, + font=font, + fill=args.playercolor + ) f.close() except OSError: pass @@ -1083,17 +1345,18 @@ def draw_image(world,uid_to_color): pngmaxx = maxx*16+16 pngminz = minz*16 pngmaxz = maxz*16+16 - pngregion=[pngminx, pngmaxx, pngminz, pngmaxz] + pngregion = [pngminx, pngmaxx, pngminz, pngmaxz] - print(("Saving to: "+ args.output)) + print(("Saving to: " + args.output)) print(("PNG Region: ", pngregion)) print(("pngMinX: ", str(pngminx))) print(("pngMaxZ: ", str(pngmaxz))) print(("Pixels PerNode: ", args.pixelspernode)) print(("border: ", border)) - # This saves data in tEXt chunks (non-standard naming tags are allowed according to the PNG specification) - im.info["pngRegion"] = str(pngregion[0])+ ","+ str(pngregion[1])+ ","+ str(pngregion[2])+ ","+ str(pngregion[3]) + # This saves data in tEXt chunks (non-standard naming tags are + # allowed according to the PNG specification) + im.info["pngRegion"] = ",".join(pngregion) im.info["pngMinX"] = str(pngminx) im.info["pngMaxZ"] = str(pngmaxz) im.info["border"] = str(border) @@ -1101,22 +1364,31 @@ def draw_image(world,uid_to_color): pngsave(im, args.output) if args.makethumb: - # Now create a square 'thumbnail' for display on square faces (which turns out to benefit from quite high resolution). + # Now create a square 'thumbnail' for display on square faces + # (which turns out to benefit from quite high resolution). thumbSize = 512 imSize = im.size print(imSize) if imSize[0] > imSize[1]: - reSize=(thumbSize, int(thumbSize*(float(imSize[1])/imSize[0]))) + reSize = (thumbSize, + int(thumbSize*(float(imSize[1])/imSize[0]))) else: - reSize=(int(thumbSize*(float(imSize[0])/imSize[1])), thumbSize) + reSize = (int(thumbSize*(float(imSize[0])/imSize[1])), + thumbSize) print(reSize) - thumbBorder=((thumbSize-reSize[0])/2, (thumbSize-reSize[1])/2, thumbSize-(thumbSize-reSize[0])/2, thumbSize-(thumbSize-reSize[1])/2) + thumbBorder = ( + (thumbSize-reSize[0])/2, + (thumbSize-reSize[1])/2, + thumbSize-(thumbSize-reSize[0])/2, + thumbSize-(thumbSize-reSize[1])/2 + ) print(thumbBorder) - thumbIm = Image.new("RGB", (thumbSize,thumbSize), args.bgcolor) - thumbIm.paste(im.resize(reSize),thumbBorder) + thumbIm = Image.new("RGB", (thumbSize, thumbSize), args.bgcolor) + thumbIm.paste(im.resize(reSize), thumbBorder) thumbIm.save(os.path.splitext(args.output)[0]+"_thumb.png", "PNG") + def main(): args = parse_args() @@ -1130,12 +1402,13 @@ def main(): print("World data does not exist.") sys.exit(1) - print(("Result image (w=" + str(world.w) + " h=" + str(world.h) + ") will be written to " - + args.output)) + print("Result image (w=" + str(world.w) + " h=" + str(world.h) + + ") will be written to " + args.output) world.generate_map_info(str_to_uid) - draw_image(world,uid_to_color) + draw_image(world, uid_to_color) + if __name__ == '__main__': main() diff --git a/minetestmapper.py b/minetestmapper.py index 099934d..3184272 100644 --- a/minetestmapper.py +++ b/minetestmapper.py @@ -17,32 +17,36 @@ import string import time import getopt import sys +import traceback import array +import io try: - import io BytesIO = io.BytesIO -except: - import io +except AttributeError: BytesIO = io.StringIO -import traceback + +PIL_HELP = """ +You must first install Pillow (fork of PIL). + +- On Windows: +Right-click windows menu, 'Command Prompt (Admin)' then: +pip install Pillow + +- On *nix-like systems: +sudo python2 -m pip install --upgrade pip +sudo python2 -m pip install --upgrade pip wheel +#then: +# +python2 -m pip install Pillow # sudo pip install Pillow +#or +#same but python3 instead # sudo pip install Pillow + +""" try: from PIL import Image, ImageDraw, ImageFont, ImageColor -except: - print("You must first install Pillow's PIL.") - print("On Windows:") - print("Right-click windows menu, 'Command Prompt (Admin)' then:") - print("pip install Pillow") - print("") - print("On *nix-like systems:") - print("sudo python2 -m pip install --upgrade pip") - print("sudo python2 -m pip install --upgrade pip wheel") - print("#then:") - #print("sudo pip install Pillow") - print("python2 -m pip install Pillow") - print("#or") - print("#same but python3 instead") - #print("sudo pip install Pillow") +except ImportError: + print(PIL_HELP) exit() TRANSLATION_TABLE = { @@ -143,6 +147,7 @@ def readS32(f): ord(f.read(1))*256*256 + ord(f.read(1))*256 + ord(f.read(1)), 2**31) + usagetext = """minetestmapper.py [options] -i/--input -o/--output @@ -261,16 +266,16 @@ if geometry_string is not None: nonchunky_xmax = nonchunky_xmin + this_width - 1 # inclusive rect nonchunky_zmin = z nonchunky_zmax = nonchunky_zmin + this_height - 1 # inclusive rect - print(("#geometry:" + "\n" + - "# x:" + str(x) + "\n" + - "# z:" + str(z) + "\n" + - "# width:" + str(this_width) + "\n" + - "# height:" + str(this_height) + "\n" + - "region:" + "\n" + - " xmin:" + str(nonchunky_xmin) + "\n" + - " xmax:" + str(nonchunky_xmax) + "\n" + - " zmin:" + str(nonchunky_zmin) + "\n" + - " zmax:" + str(nonchunky_zmax))) + print("#geometry:" + "\n" + + "# x:" + str(x) + "\n" + + "# z:" + str(z) + "\n" + + "# width:" + str(this_width) + "\n" + + "# height:" + str(this_height) + "\n" + + "region:" + "\n" + + " xmin:" + str(nonchunky_xmin) + "\n" + + " xmax:" + str(nonchunky_xmax) + "\n" + + " zmin:" + str(nonchunky_zmin) + "\n" + + " zmax:" + str(nonchunky_zmax)) else: print(("ERROR: Missing coordinates in '" + geometry_string + "' for geometry (must be in the form: x:z+width+height)")) @@ -293,11 +298,11 @@ elif region_string is not None: nonchunky_xmax = int(xmax_string) nonchunky_zmin = int(zmin_string) nonchunky_zmax = int(zmax_string) - print(("region:" + "\n" + - " xmin:" + str(nonchunky_xmin) + "\n" + - " xmax:" + str(nonchunky_xmax) + "\n" + - " zmin:" + str(nonchunky_zmin) + "\n" + - " zmax:" + str(nonchunky_zmax))) + print("region:" + "\n" + + " xmin:" + str(nonchunky_xmin) + "\n" + + " xmax:" + str(nonchunky_xmax) + "\n" + + " zmin:" + str(nonchunky_zmin) + "\n" + + " zmax:" + str(nonchunky_zmax)) else: print(("ERROR: Incorrect value '" + region_string + "' for region (must be in the form: xmin:xmax,zmin:zmax)")) @@ -566,10 +571,11 @@ for n in range(len(xlist)): remaining_s = time_guess - dtime remaining_minutes = int(remaining_s / 60) remaining_s -= remaining_minutes * 60 - print(("Processing sector " + str(n) + " of " + str(len(xlist)) + - " (" + str(round(100.0 * n / len(xlist), 1)) + "%)" + - " (ETA: " + str(remaining_minutes) + "m " + - str(int(remaining_s)) + "s)")) + print("Processing sector " + str(n) + " of " + + str(len(xlist)) + " (" + + str(round(100.0 * n / len(xlist), 1)) + "%)" + + " (ETA: " + str(remaining_minutes) + "m " + + str(int(remaining_s)) + "s)") xpos = xlist[n] zpos = zlist[n] @@ -769,9 +775,9 @@ for n in range(len(xlist)): if(len(pixellist) == 0): break except Exception as e: - print(("Error at (" + str(xpos) + "," + str(ypos) + "," + - str(zpos) + "):")) + print("Error at {}:".format((xpos, ypos, zpos))) traceback.print_exc() + sys.stdout.write(os.linesep) sys.stdout.write("Block data: ") last_c = None try: @@ -780,36 +786,46 @@ for n in range(len(xlist)): sys.stdout.write("%2.2x " % ord(c)) except TypeError: if last_c is not None: - got = "got {}s".format(type(last_c)) + got = "got {}s".format(type(last_c).__name__) else: - got = "but r was {}".format(type(r)) + got = "but r was {}".format(type(r).__name__) try: - got = "but r[0] was {}".format(type(r[0])) - except: + r0_tn = type(r[0]).__name__ + got = "but r[0] was {}".format(r0_tn) + except Exception as e: + sys.stdout.write("<(The following exception" + " occurred while handling the" + " exception above:" + " {})".format(e)) pass - sys.stdout.write("<(The following issue occurred while handling the" - + " Exception)") - sys.stdout.write("...Uh oh, expected characters in r, " + got - + ":>") + sys.stdout.write("<(The following issue occurred while" + " handling the exception above)") + sys.stdout.write("...Uh oh, expected characters in r, " + + got + ":>") for c in r[0]: sys.stdout.write("%2.2x " % c) sys.stdout.write(os.linesep) sys.stdout.write("Data after node metadata:") + d_a_n_md = data_after_node_metadata try: count = 0 for c in data_after_node_metadata: sys.stdout.write("%2.2x " % ord(c)) count += 1 if count == 0: - sys.stdout.write("<(The following issue occurred while handling the exception): uh oh, got {}: zero characters to convert to ord>".format(data_after_node_metadata)) + sys.stdout.write("<(The following issue occurred" + " while handling the exception):" + " uh oh, got {}: zero characters" + " to convert to " + "ord>".format(d_a_n_md)) except TypeError: - sys.stdout.write("<(The following issue occurred while handling the" - + " Exception)...Uh oh, expected characters in" - " data_after_node_metadata; got:") + sys.stdout.write("<(The following issue occurred while" + " handling the exception above)...Uh" + " oh, expected characters in" + " data_after_node_metadata; got:") sys.stdout.flush() - sys.stdout.write(os.linesep)(type(data_after_node_metadata) + " length " - + str(len(data_after_node_metadata)) - + " :>") + sys.stdout.write(str(len(d_a_n_md)) + "-length " + + type(d_a_n_md).__name__ + " :>") sys.stdout.write(os.linesep) sys.stdout.write(os.linesep) exit(1) # stop HUGE stdout @@ -833,10 +849,10 @@ for (x, z) in stuff.keys(): remaining_s = time_guess - dtime remaining_minutes = int(remaining_s / 60) remaining_s -= remaining_minutes * 60 - print(("Drawing pixel " + str(n) + " of " + str(listlen) + - " (" + str(round(100.0 * n / listlen, 1)) + "%)" + - " (ETA: " + str(remaining_minutes) + "m " + - str(int(remaining_s)) + "s)")) + print("Drawing pixel " + str(n) + " of " + str(listlen) + + " (" + str(round(100.0 * n / listlen, 1)) + "%)" + + " (ETA: " + str(remaining_minutes) + "m " + + str(int(remaining_s)) + "s)") n += 1 (r, g, b) = colors[stuff[(x, z)][1]] @@ -886,9 +902,15 @@ for (x, z) in stuff.keys(): if draworigin: - draw.ellipse((minx * -16 - 5 + border, h - minz * -16 - 6 + border, - minx * -16 + 5 + border, h - minz * -16 + 4 + border), - outline=origincolor) + draw.ellipse( + ( + minx * -16 - 5 + border, + h - minz * -16 - 6 + border, + minx * -16 + 5 + border, + h - minz * -16 + 4 + border + ), + outline=origincolor + ) font = ImageFont.load_default() @@ -897,16 +919,41 @@ if drawscale: draw.text((2, 24), "Z", font=font, fill=scalecolor) for n in range(int(minx / -4) * -4, maxx, 4): - draw.text((minx * -16 + n * 16 + 2 + border, 0), str(n * 16), - font=font, fill=scalecolor) - draw.line((minx * -16 + n * 16 + border, 0, - minx * -16 + n * 16 + border, border - 1), fill=scalecolor) + draw.text( + ( + minx * -16 + n * 16 + 2 + border, + 0 + ), + str(n * 16), + font=font, + fill=scalecolor + ) + draw.line( + ( + minx * -16 + n * 16 + border, + 0, + minx * -16 + n * 16 + border, + border - 1 + ), + fill=scalecolor + ) for n in range(int(maxz / 4) * 4, minz, -4): - draw.text((2, h - 1 - (n * 16 - minz * 16) + border), str(n * 16), - font=font, fill=scalecolor) - draw.line((0, h - 1 - (n * 16 - minz * 16) + border, border - 1, - h - 1 - (n * 16 - minz * 16) + border), fill=scalecolor) + draw.text( + (2, h - 1 - (n * 16 - minz * 16) + border), + str(n * 16), + font=font, + fill=scalecolor + ) + draw.line( + ( + 0, + h - 1 - (n * 16 - minz * 16) + border, + border - 1, + h - 1 - (n * 16 - minz * 16) + border + ), + fill=scalecolor + ) if drawplayers: try: @@ -926,11 +973,19 @@ if drawplayers: if len(name) > 0 and len(position) == 3: x = (int(float(position[0]) / 10 - minx * 16)) z = int(h - (float(position[2]) / 10 - minz * 16)) - draw.ellipse((x - 2 + border, z - 2 + border, - x + 2 + border, z + 2 + border), - outline=playercolor) - draw.text((x + 2 + border, z + 2 + border), name, - font=font, fill=playercolor) + draw.ellipse( + ( + x - 2 + border, z - 2 + border, + x + 2 + border, z + 2 + border + ), + outline=playercolor + ) + draw.text( + (x + 2 + border, z + 2 + border), + name, + font=font, + fill=playercolor + ) f.close() except OSError: pass diff --git a/quality.sh b/quality.sh new file mode 100755 index 0000000..3414d1c --- /dev/null +++ b/quality.sh @@ -0,0 +1,31 @@ +#!/bin/sh +if [ ! -f "`command -v pycodestyle-3`" ]; then + 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" +# 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