Support backend option and i/o options like the C++ minetestmapper

master
poikilos 2020-04-29 17:08:07 -04:00
parent 4502b5565e
commit fda8d5db15
4 changed files with 150 additions and 101 deletions

View File

@ -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

113
minetestmapper-numpy.py Normal file → Executable file
View File

@ -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
<http://blog.client9.com/2007/08/28/
python-pil-and-png-metadata-take-2.html>
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)

71
minetestmapper.py Normal file → Executable file
View File

@ -148,6 +148,13 @@ def readS32(f):
ord(f.read(1)), 2**31)
colors_msg = """<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).
Otherwise, use the --colors option followed by a path.
"""
usagetext = """minetestmapper.py [options]
-i/--input <world_path>
-o/--output <output_image.png>
@ -161,10 +168,8 @@ usagetext = """minetestmapper.py [options]
--region <xmin>:<xmax>,<zmin>:<zmax>
--geometry <xmin>:<zmin>+<width>+<height>
--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 <path to colors.txt>
--backend <sqlite3> (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 = []

View File

@ -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