Browse Source

made fork of current git version of minetestmapper

to allow setting boundaries (only geometry param tested, not region)
master
poikilos 9 years ago
committed by Jacob Gustafson
parent
commit
e82c7682dc
  1. 130
      chunkymap-regen.py
  2. 4
      install-chunkymap-on-ubuntu.sh
  3. 6
      minetestmapper TEST.bat
  4. 818
      minetestmapper.py

130
chunkymap-regen.py

@ -223,54 +223,67 @@ class MTChunk:
line = ins.readline() line = ins.readline()
if line: if line:
line_strip = line.strip() line_strip = line.strip()
if "data does not exist" in line_strip: try:
self.metadata["is_marked_empty"] = True if ("does not exist" in line_strip): # official minetestmapper.py says "World does not exist" but expertmm fork and minetestmapper-numpy.py say "data does not exist"
break self.metadata["is_marked_empty"] = True
elif "Result image" in line_strip: break
oparen_index = line_strip.find("(") elif "Result image" in line_strip:
if (oparen_index>-1): oparen_index = line_strip.find("(")
cparen_index = line_strip.find(")", oparen_index+1) if (oparen_index>-1):
if (cparen_index>-1): cparen_index = line_strip.find(")", oparen_index+1)
operations_string = line_strip[oparen_index+1:cparen_index] if (cparen_index>-1):
operation_list = operations_string.split(" ") operations_string = line_strip[oparen_index+1:cparen_index]
#if len(operation_list)==2: operation_list = operations_string.split(" ")
for operation_string in operation_list: #if len(operation_list)==2:
if "=" in operation_string: for operation_string in operation_list:
chunks = operation_string.split("=") if "=" in operation_string:
if len(chunks)==2: chunks = operation_string.split("=")
if chunks[0].strip()=="w": if len(chunks)==2:
try: if chunks[0].strip()=="w":
self.metadata["image_w"]=int(chunks[1].strip()) try:
except: self.metadata["image_w"]=int(chunks[1].strip())
print("Bad value for image w:"+str(chunks[1])) except:
elif chunks[0].strip()=="h": print("Bad value for image w:"+str(chunks[1]))
try: elif chunks[0].strip()=="h":
self.metadata["image_h"]=int(chunks[1].strip()) try:
except: self.metadata["image_h"]=int(chunks[1].strip())
print("Bad value for image h:"+str(chunks[1])) except:
print("Bad value for image h:"+str(chunks[1]))
else:
print("Bad name for image variable so ignoring variable named '"+str(chunks[0])+"'")
else: else:
print("Bad name for image variable so ignoring variable named '"+str(chunks[0])+"'") print("Bad assignment (not 2 sides) so ignoring command '"+operation_string+"'")
else: else:
print("Bad assignment (not 2 sides) so ignoring command '"+operation_string+"'") print("Bad assignment (operator) so ignoring command '"+operation_string+"'")
#else:
# print("Bad assignment count so ignoring operations string '"+operations_string+"'")
elif "PNG Region" in line_strip:
obracket_index = line_strip.find("[")
if obracket_index>-1:
cbracket_index = line_strip.find("]", obracket_index+1)
if cbracket_index>-1:
rect_values_string = line_strip[obracket_index+1:cbracket_index]
rect_values_list = rect_values_string.split(",")
if len(rect_values_list)==4:
#pngregion=[pngminx, pngmaxx, pngminz, pngmaxz] #from minetestmapper-numpy.py
self.metadata["image_left"]=int(rect_values_list[0].strip())
self.metadata["image_right"]=int(rect_values_list[1].strip())
self.metadata["image_top"]=int(rect_values_list[2].strip())
self.metadata["image_bottom"]=int(rect_values_list[3].strip())
else: else:
print("Bad assignment (operator) so ignoring command '"+operation_string+"'") print("Bad map rect, so ignoring: "+rect_values_string)
#else: elif (len(line_strip)>5) and (line_strip[:5]=="xmin:"):
# print("Bad assignment count so ignoring operations string '"+operations_string+"'") self.metadata["image_left"] = int(line_strip[5:].strip())
elif "PNG Region" in line_strip: elif (len(line_strip)>5) and (line_strip[:5]=="xmax:"):
obracket_index = line_strip.find("[") self.metadata["image_right"] = int(line_strip[5:].strip())
if obracket_index>-1: elif (len(line_strip)>5) and (line_strip[:5]=="zmin:"):
cbracket_index = line_strip.find("]", obracket_index+1) #(zmin is bottom since cartesian)
if cbracket_index>-1: self.metadata["image_bottom"] = int(line_strip[5:].strip())
rect_values_string = line_strip[obracket_index+1:cbracket_index] elif (len(line_strip)>5) and (line_strip[:5]=="zmax:"):
rect_values_list = rect_values_string.split(",") #(zmax is top since cartesian)
if len(rect_values_list)==4: self.metadata["image_top"] = int(line_strip[5:].strip())
#pngregion=[pngminx, pngmaxx, pngminz, pngmaxz] #from minetestmapper-numpy.py except:
self.metadata["image_left"]=int(rect_values_list[0].strip()) print("#failed to parse line:"+str(line_strip))
self.metadata["image_right"]=int(rect_values_list[1].strip())
self.metadata["image_top"]=int(rect_values_list[2].strip())
self.metadata["image_bottom"]=int(rect_values_list[3].strip())
else:
print("Bad map rect, so ignoring: "+rect_values_string)
ins.close() ins.close()
@ -607,17 +620,24 @@ class MTChunks:
cmd_string = self.python_exe_path + " \""+self.mtmn_path + "\" --region " + str(x_min) + " " + str(x_max) + " " + str(z_min) + " " + str(z_max) + " --maxheight "+str(self.maxheight)+" --minheight "+str(self.minheight)+" --pixelspernode "+str(self.pixelspernode)+" \""+self.world_path+"\" \""+tmp_png_path+"\"" + cmd_suffix cmd_string = self.python_exe_path + " \""+self.mtmn_path + "\" --region " + str(x_min) + " " + str(x_max) + " " + str(z_min) + " " + str(z_max) + " --maxheight "+str(self.maxheight)+" --minheight "+str(self.minheight)+" --pixelspernode "+str(self.pixelspernode)+" \""+self.world_path+"\" \""+tmp_png_path+"\"" + cmd_suffix
if self.os_name!="windows": #since windows client doesn't normally have minetest-mapper if self.os_name!="windows": #since windows client doesn't normally have minetest-mapper
# Since minetestmapper-numpy has trouble with leveldb:
# such as sudo minetest-mapper --input "/home/owner/.minetest/worlds/FCAGameAWorld" --geometry -32:-32+64+64 --output /var/www/html/minetest/try1.png
# where geometry option is like --geometry x:y+w+h
# output_type_string = "minetest-mapper"
# NOTE: minetest-mapper is part of the minetest-data package, which can be installed alongside the git version of minetestserver
# BUT *buntu Trusty version of it does NOT have geometry option
# cmd_string = "/usr/games/minetest-mapper --input \""+self.world_path+"\" --draworigin --geometry "+str(x_min)+":"+str(z_min)+"+"+str(int(x_max)-int(x_min))+"+"+str(int(z_max)-int(z_min))+" --output \""+tmp_png_path+"\""+cmd_suffix
# such as sudo python minetestmapper --input "/home/owner/.minetest/worlds/FCAGameAWorld" --geometry -32:-32+64+64 --output /var/www/html/minetest/try1.png
# OR try PYTHON version (doesn't have range):
script_path = "/home/owner/minetest/util/minetestmapper.py"
region_capable_script_path = "/home/owner/minetest/util/chunkymap/minetestmapper.py"
if os.path.isfile(region_capable_script_path):
script_path = region_capable_script_path
cmd_suffix=" > entire-mtmresult.txt"
cmd_string="sudo python /home/owner/minetest/util/minetestmapper.py --input \"/home/owner/.minetest/worlds/FCAGameAWorld\" --output \""+dest_png_path+"\""+cmd_suffix
#sudo python /home/owner/minetest/util/minetestmapper.py --input "/home/owner/.minetest/worlds/FCAGameAWorld" --output /var/www/html/minetest/chunkymapdata/entire.png > entire-mtmresult.txt
# sudo mv entire-mtmresult.txt /home/owner/minetest/util/chunkymap-genresults/
# since minetestmapper-numpy has trouble with leveldb:
# such as sudo minetest-mapper --input "/home/owner/.minetest/worlds/FCAGameAWorld" --geometry -32:-32+64+64 --output /var/www/html/minetest/try1.png
# where geometry option is like --geometry x:y+w+h
#output_type_string = "minetest-mapper"
# NOTE: minetest-mapper is part of the minetest-data package, which can be installed alongside the git version of minetestserver
# BUT *buntu Trusty version of it does NOT have geometry option
#cmd_string = "/usr/games/minetest-mapper --input \""+self.world_path+"\" --draworigin --geometry "+str(x_min)+":"+str(z_min)+"+"+str(int(x_max)-int(x_min))+"+"+str(int(z_max)-int(z_min))+" --output \""+tmp_png_path+"\""+cmd_suffix
# PYTHON VERSION (doesn't have range)
# such as sudo python /home/owner/minetest/util/minetestmapper.py --input "/home/owner/.minetest/worlds/FCAGameAWorld" --geometry -32:-32+64+64 --output /var/www/html/minetest/try1.png
pass
dest_png_path = self.get_chunk_image_path(chunk_luid) dest_png_path = self.get_chunk_image_path(chunk_luid)
#is_empty_chunk = is_chunk_yaml_marked(chunk_luid) and is_chunk_yaml_marked_empty(chunk_luid) #is_empty_chunk = is_chunk_yaml_marked(chunk_luid) and is_chunk_yaml_marked_empty(chunk_luid)
print ("Running generator for: "+str((x,z))) print ("Running generator for: "+str((x,z)))

4
install-chunkymap-on-ubuntu.sh

@ -41,6 +41,10 @@ cp -f "$CHUNKYMAP_INSTALLER_PATH/unused/chunkymap-players-cronjob" "$CHUNKYMAP_D
cp -f "$CHUNKYMAP_INSTALLER_PATH/unused/set-minutely-players-crontab-job.sh" "$CHUNKYMAP_DEST/unused/" cp -f "$CHUNKYMAP_INSTALLER_PATH/unused/set-minutely-players-crontab-job.sh" "$CHUNKYMAP_DEST/unused/"
cp -f "$CHUNKYMAP_INSTALLER_PATH/unused/set-minutely-crontab-job.sh" "$CHUNKYMAP_DEST/unused/" cp -f "$CHUNKYMAP_INSTALLER_PATH/unused/set-minutely-crontab-job.sh" "$CHUNKYMAP_DEST/unused/"
cp -f "$CHUNKYMAP_INSTALLER_PATH/chunkymap-regen-loop.sh" "$CHUNKYMAP_DEST/" cp -f "$CHUNKYMAP_INSTALLER_PATH/chunkymap-regen-loop.sh" "$CHUNKYMAP_DEST/"
if [ ! -d "$CHUNKYMAP_DEST/chunkymap" ]; then
mkdir "$CHUNKYMAP_DEST/chunkymap"
fi
cp -f "$CHUNKYMAP_INSTALLER_PATH/minetestmapper.py" "$CHUNKYMAP_DEST/chunkymap/"
cp --no-clobber $CHUNKYMAP_INSTALLER_PATH/chunkymap-signals* "$CHUNKYMAP_DEST/" cp --no-clobber $CHUNKYMAP_INSTALLER_PATH/chunkymap-signals* "$CHUNKYMAP_DEST/"
cd "$CHUNKYMAP_INSTALLER_PATH" cd "$CHUNKYMAP_INSTALLER_PATH"
python replace-with-current-user.py # the py file only manipulates the minetest/util directory python replace-with-current-user.py # the py file only manipulates the minetest/util directory

6
minetestmapper TEST.bat

@ -0,0 +1,6 @@
REM C:\Python27\python minetestmapper.py --input "C:\games\Minetest\worlds\try7amber" --region 0 64 0 64 --output "try7amber.png"
C:\Python27\python minetestmapper.py --input "C:\games\Minetest\worlds\try7amber" --geometry 0:0+16+16 --output "chunk_x0z0.png" > chunk_x0z0-genresult.txt
REM C:\Python27\python minetestmapper.py --input "C:\games\Minetest\worlds\try7amber" --geometry -100:-100+16+16 --output "chunk_x-100z-100.png" > chunk_x-100z-100-genresult.txt
pause

818
minetestmapper.py

@ -0,0 +1,818 @@
#!/usr/bin/env python2
# expertmm fork from 2016-02-01 git version
# -*- coding: utf-8 -*-
# This program is free software. It comes without any warranty, to
# the extent permitted by applicable law. You can redistribute it
# and/or modify it under the terms of the DWFYWT
# Public License, Version 2, as published by Sam Hocevar. See
# COPYING for more details.
# Made by Jogge, modified by celeron55
# 2011-05-29: j0gge: initial release
# 2011-05-30: celeron55: simultaneous support for sectors/sectors2, removed
# 2011-06-02: j0gge: command line parameters, coordinates, players, ...
# 2011-06-04: celeron55: added #!/usr/bin/python2 and converted \r\n to \n
# to make it easily executable on Linux
# 2011-07-30: WF: Support for content types extension, refactoring
# 2011-07-30: erlehmann: PEP 8 compliance.
# Requires Python Imaging Library: http://www.pythonware.com/products/pil/
# Some speed-up: ...lol, actually it slows it down.
#import psyco ; psyco.full()
#from psyco.classes import *
import zlib
import os
import string
import time
import getopt
import sys
import array
import cStringIO
import traceback
from PIL import Image, ImageDraw, ImageFont, ImageColor
TRANSLATION_TABLE = {
1: 0x800, # CONTENT_GRASS
4: 0x801, # CONTENT_TREE
5: 0x802, # CONTENT_LEAVES
6: 0x803, # CONTENT_GRASS_FOOTSTEPS
7: 0x804, # CONTENT_MESE
8: 0x805, # CONTENT_MUD
10: 0x806, # CONTENT_CLOUD
11: 0x807, # CONTENT_COALSTONE
12: 0x808, # CONTENT_WOOD
13: 0x809, # CONTENT_SAND
18: 0x80a, # CONTENT_COBBLE
19: 0x80b, # CONTENT_STEEL
20: 0x80c, # CONTENT_GLASS
22: 0x80d, # CONTENT_MOSSYCOBBLE
23: 0x80e, # CONTENT_GRAVEL
24: 0x80f, # CONTENT_SANDSTONE
25: 0x810, # CONTENT_CACTUS
26: 0x811, # CONTENT_BRICK
27: 0x812, # CONTENT_CLAY
28: 0x813, # CONTENT_PAPYRUS
29: 0x814} # CONTENT_BOOKSHELF
def hex_to_int(h):
i = int(h, 16)
if(i > 2047):
i -= 4096
return i
def hex4_to_int(h):
i = int(h, 16)
if(i > 32767):
i -= 65536
return i
def int_to_hex3(i):
if(i < 0):
return "%03X" % (i + 4096)
else:
return "%03X" % i
def int_to_hex4(i):
if(i < 0):
return "%04X" % (i + 65536)
else:
return "%04X" % i
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
def limit(i, l, h):
if(i > h):
i = h
if(i < l):
i = l
return i
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))
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)
usagetext = """minetestmapper.py [options]
-i/--input <world_path>
-o/--output <output_image.png>
--bgcolor <color>
--scalecolor <color>
--playercolor <color>
--origincolor <color>
--drawscale
--drawplayers
--draworigin
--region <xmin> <xmax> <zmin> <zmax>
--geometry <xmin>:<zmin>+<width>+<height>
--drawunderground
Color format: '#000000'"""
def usage():
print(usagetext)
try:
opts, args = getopt.getopt(sys.argv[1:], "hi:o:", ["help", "input=",
"output=", "bgcolor=", "scalecolor=", "origincolor=",
"playercolor=", "draworigin", "drawplayers", "drawscale",
"drawunderground", "geometry=", "region="])
except getopt.GetoptError as err:
# print help information and exit:
print(str(err)) # will print something like "option -a not recognized"
usage()
sys.exit(2)
path = None
output = "map.png"
border = 0
scalecolor = "black"
bgcolor = "white"
origincolor = "red"
playercolor = "red"
drawscale = False
drawplayers = False
draworigin = False
drawunderground = False
geometry_string = None
region_string = None
for o, a in opts:
if o in ("-h", "--help"):
usage()
sys.exit()
elif o in ("-i", "--input"):
path = a
elif o in ("-o", "--output"):
output = a
elif o == "--bgcolor":
bgcolor = ImageColor.getrgb(a)
elif o == "--scalecolor":
scalecolor = ImageColor.getrgb(a)
elif o == "--playercolor":
playercolor = ImageColor.getrgb(a)
elif o == "--origincolor":
origincolor = ImageColor.getrgb(a)
elif o == "--drawscale":
drawscale = True
border = 40
elif o == "--drawplayers":
drawplayers = True
elif o == "--draworigin":
draworigin = True
elif o == "--drawunderground":
drawunderground = True
elif o == "--geometry":
geometry_string = a
elif o == "--region":
region_string = a
else:
assert False, "unhandled option"
nonchunky_xmin=-1500
nonchunky_xmax=1500
nonchunky_zmin=-1500
nonchunky_zmax=1500
if geometry_string is not None:
parts = geometry_string.split("+")
if len(parts) == 3:
coords_string, width_string, height_string = parts
coords = coords_string.split(":")
if len(coords)==2:
x_string, z_string = coords
x = int(x_string)
z = int(z_string)
this_width = int(width_string)
this_height = int(height_string)
nonchunky_xmin = x
nonchunky_xmax = nonchunky_xmin + this_width
nonchunky_zmin = z
nonchunky_zmax = nonchunky_zmin + this_height
print("#geometry:")
print("# x:"+str(x))
print("# z:"+str(z))
print("# width:"+str(this_width))
print("# height:"+str(this_height))
print("region:")
print(" xmin:"+str(nonchunky_xmin))
print(" xmax:"+str(nonchunky_xmax))
print(" zmin:"+str(nonchunky_zmin))
print(" zmax:"+str(nonchunky_zmax))
else:
print("ERROR: (Missing coordinates in '"+geometry_string+"' for geometry) Geometry should be in the form: x:z+width+height")
usage()
sys.exit()
else:
print("ERROR: (Incorrect value '"+geometry_string+"' for geometry) Geometry should be in the form: x:z+width+height")
usage()
sys.exit()
elif region_string is not None:
parts = region_string.split(" ")
if len(parts) == 4:
xmin_string, xmax_string, zmin_string, zmax_string = parts
nonchunky_xmin = int(xmin_string)
nonchunky_xmax = int(xmax_string)
nonchunky_zmin = int(zmin_string)
nonchunky_zmax = int(zmax_string)
print("region:")
print(" xmin:"+str(nonchunky_xmin))
print(" xmax:"+str(nonchunky_xmax))
print(" zmin:"+str(nonchunky_zmin))
print(" zmax:"+str(nonchunky_zmax))
else:
print("ERROR: (Incorrect value '"+region_string+"' for region) Region should be in the form: xmin xmax zmin zmax")
usage()
sys.exit()
#answer=raw_input("press enter to continue")
sector_xmin = nonchunky_xmin / 16
sector_xmax = nonchunky_xmax / 16
sector_zmin = nonchunky_zmin / 16
sector_zmax = nonchunky_zmax / 16
if path is None:
print("Please select world path (eg. -i ../worlds/yourworld) (or use --help)")
sys.exit(1)
if path[-1:] != "/" and path[-1:] != "\\":
path = path + "/"
# Load color information for the blocks.
colors = {}
try:
f = file("colors.txt")
except IOError:
f = file(os.path.join(os.path.dirname(__file__), "colors.txt"))
for line in f:
values = string.split(line)
if len(values) < 4:
continue
identifier = values[0]
is_hex = True
for c in identifier:
if c not in "0123456789abcdefABCDEF":
is_hex = False
break
if is_hex:
colors[int(values[0], 16)] = (
int(values[1]),
int(values[2]),
int(values[3]))
else:
colors[values[0]] = (
int(values[1]),
int(values[2]),
int(values[3]))
f.close()
#print("colors: "+repr(colors))
#sys.exit(1)
xlist = []
zlist = []
# List all sectors to memory and calculate the width and heigth of the
# resulting picture.
conn = None
cur = None
if os.path.exists(path + "map.sqlite"):
import sqlite3
conn = sqlite3.connect(path + "map.sqlite")
cur = conn.cursor()
cur.execute("SELECT `pos` FROM `blocks`")
while True:
r = cur.fetchone()
if not r:
break
x, y, z = getIntegerAsBlock(r[0])
if x < sector_xmin or x >= sector_xmax:
continue
if z < sector_zmin or z >= sector_zmax:
continue
xlist.append(x)
zlist.append(z)
if 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)
z = hex_to_int(filename2)
if x < sector_xmin or x > sector_xmax:
continue
if z < sector_zmin or z > sector_zmax:
continue
xlist.append(x)
zlist.append(z)
if os.path.exists(path + "sectors"):
for filename in os.listdir(path + "sectors"):
x = hex4_to_int(filename[:4])
z = hex4_to_int(filename[-4:])
if x < sector_xmin or x > sector_xmax:
continue
if z < sector_zmin or z > sector_zmax:
continue
xlist.append(x)
zlist.append(z)
if len(xlist) == 0 or len(zlist) == 0:
print("At this chunk data does not exist.")
sys.exit(1)
# Get rid of doubles
xlist, zlist = zip(*sorted(set(zip(xlist, zlist))))
minx = min(xlist)
minz = min(zlist)
maxx = max(xlist)
maxz = max(zlist)
w = (maxx - minx) * 16 + 16
h = (maxz - minz) * 16 + 16
print("Result image (w=" + str(w) + " h=" + str(h) + ") will be written to "
+ output)
im = Image.new("RGB", (w + border, h + border), bgcolor)
draw = ImageDraw.Draw(im)
impix = im.load()
stuff = {}
unknown_node_names = []
unknown_node_ids = []
starttime = time.time()
CONTENT_WATER = 2
def content_is_ignore(d):
return d in [0, "ignore"]
def content_is_water(d):
return d in [2, 9]
def content_is_air(d):
return d in [126, 127, 254, "air"]
def read_content(mapdata, version, datapos):
if version >= 24:
return (mapdata[datapos*2] << 8) | (mapdata[datapos*2 + 1])
elif version >= 20:
if mapdata[datapos] < 0x80:
return mapdata[datapos]
else:
return (mapdata[datapos] << 4) | (mapdata[datapos + 0x2000] >> 4)
elif 16 <= version < 20:
return TRANSLATION_TABLE.get(mapdata[datapos], mapdata[datapos])
else:
raise Exception("Unsupported map format: " + str(version))
def read_mapdata(mapdata, version, pixellist, water, day_night_differs, id_to_name):
global stuff # oh my :-)
global unknown_node_names
global unknown_node_ids
if(len(mapdata) < 4096):
print("bad: " + xhex + "/" + zhex + "/" + yhex + " " + \
str(len(mapdata)))
else:
chunkxpos = xpos * 16
chunkypos = ypos * 16
chunkzpos = zpos * 16
content = 0
datapos = 0
for (x, z) in reversed(pixellist):
for y in reversed(range(16)):
datapos = x + y * 16 + z * 256
content = read_content(mapdata, version, datapos)
# Try to convert id to name
try:
content = id_to_name[content]
except KeyError:
pass
if content_is_ignore(content):
pass
elif content_is_air(content):
pass
elif content_is_water(content):
water[(x, z)] += 1
# Add dummy stuff for drawing sea without seabed
stuff[(chunkxpos + x, chunkzpos + z)] = (
chunkypos + y, content, water[(x, z)], day_night_differs)
elif content in colors:
# Memorize information on the type and height of
# the block and for drawing the picture.
stuff[(chunkxpos + x, chunkzpos + z)] = (
chunkypos + y, content, water[(x, z)], day_night_differs)
pixellist.remove((x, z))
break
else:
if type(content) == str:
if content not in unknown_node_names:
unknown_node_names.append(content)
#print("unknown node: %s/%s/%s x: %d y: %d z: %d block name: %s"
# % (xhex, zhex, yhex, x, y, z, content))
else:
if content not in unknown_node_ids:
unknown_node_ids.append(content)
#print("unknown node: %s/%s/%s x: %d y: %d z: %d block id: %x"
# % (xhex, zhex, yhex, x, y, z, content))
# Go through all sectors.
for n in range(len(xlist)):
#if n > 500:
# break
if n % 200 == 0:
nowtime = time.time()
dtime = nowtime - starttime
try:
n_per_second = 1.0 * n / dtime
except ZeroDivisionError:
n_per_second = 0
if n_per_second != 0:
seconds_per_n = 1.0 / n_per_second
time_guess = seconds_per_n * 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)")
xpos = xlist[n]
zpos = zlist[n]
xhex = int_to_hex3(xpos)
zhex = int_to_hex3(zpos)
xhex4 = int_to_hex4(xpos)
zhex4 = int_to_hex4(zpos)
sector1 = xhex4.lower() + zhex4.lower()
sector2 = xhex.lower() + "/" + zhex.lower()
ylist = []
sectortype = ""
if cur:
psmin = getBlockAsInteger((xpos, -2048, zpos))
psmax = getBlockAsInteger((xpos, 2047, zpos))
cur.execute("SELECT `pos` FROM `blocks` WHERE `pos`>=? AND `pos`<=? AND (`pos` - ?) % 4096 = 0", (psmin, psmax, psmin))
while True:
r = cur.fetchone()
if not r:
break
pos = getIntegerAsBlock(r[0])[1]
ylist.append(pos)
sectortype = "sqlite"
try:
for filename in os.listdir(path + "sectors/" + sector1):
if(filename != "meta"):
pos = int(filename, 16)
if(pos > 32767):
pos -= 65536
ylist.append(pos)
sectortype = "old"
except OSError:
pass
if sectortype == "":
try:
for filename in os.listdir(path + "sectors2/" + sector2):
if(filename != "meta"):
pos = int(filename, 16)
if(pos > 32767):
pos -= 65536
ylist.append(pos)
sectortype = "new"
except OSError:
pass
if sectortype == "":
continue
ylist.sort()
# Make a list of pixels of the sector that are to be looked for.
pixellist = []
water = {}
for x in range(16):
for z in range(16):
pixellist.append((x, z))
water[(x, z)] = 0
# Go through the Y axis from top to bottom.
for ypos in reversed(ylist):
try:
#print("("+str(xpos)+","+str(ypos)+","+str(zpos)+")")
yhex = int_to_hex4(ypos)
if sectortype == "sqlite":
ps = getBlockAsInteger((xpos, ypos, zpos))
cur.execute("SELECT `data` FROM `blocks` WHERE `pos`==? LIMIT 1", (ps,))
r = cur.fetchone()
if not r:
continue
f = cStringIO.StringIO(r[0])
else:
if sectortype == "old":
filename = path + "sectors/" + sector1 + "/" + yhex.lower()
else:
filename = path + "sectors2/" + sector2 + "/" + yhex.lower()
f = file(filename, "rb")
# 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))
# Check flags
is_underground = ((ord(flags) & 1) != 0)
day_night_differs = ((ord(flags) & 2) != 0)
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))
if version >= 22:
content_width = readU8(f)
params_width = readU8(f)
# Node data
dec_o = zlib.decompressobj()
try:
mapdata = array.array("B", dec_o.decompress(f.read()))
except:
mapdata = []
# Reuse the unused tail of the file
f.close();
f = cStringIO.StringIO(dec_o.unused_data)
#print("unused data: "+repr(dec_o.unused_data))
# zlib-compressed node metadata list
dec_o = zlib.decompressobj()
try:
metaliststr = array.array("B", dec_o.decompress(f.read()))
# And do nothing with it
except:
metaliststr = []
# Reuse the unused tail of the file
f.close();
f = cStringIO.StringIO(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:
# mapblockobject_count
readU16(f)
if version == 23:
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):
readU16(f)
readS32(f)
readS32(f)
static_object_version = readU8(f)
static_object_count = readU16(f)
for i in range(0, static_object_count):
# u8 type (object type-id)
object_type = readU8(f)
# s32 pos_x_nodes * 10000
pos_x_nodes = readS32(f)/10000
# s32 pos_y_nodes * 10000
pos_y_nodes = readS32(f)/10000
# s32 pos_z_nodes * 10000
pos_z_nodes = readS32(f)/10000
# u16 data_size
data_size = readU16(f)
# u8[data_size] data
data = f.read(data_size)
timestamp = readU32(f)
#print("* timestamp="+str(timestamp))
id_to_name = {}
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))
for i in range(0, num_name_id_mappings):
node_id = readU16(f)
name_len = readU16(f)
name = f.read(name_len)
#print(str(node_id)+" = "+name)
id_to_name[node_id] = name
# Node timers
if version >= 25:
timer_size = readU8(f)
num = readU16(f)
for i in range(0,num):
readU16(f)
readS32(f)
readS32(f)
read_mapdata(mapdata, version, pixellist, water, day_night_differs, id_to_name)
# After finding all the pixels in the sector, we can move on to
# the next sector without having to continue the Y axis.
if(len(pixellist) == 0):
break
except Exception as e:
print("Error at ("+str(xpos)+","+str(ypos)+","+str(zpos)+"): "+str(e))
sys.stdout.write("Block data: ")
for c in r[0]:
sys.stdout.write("%2.2x "%ord(c))
sys.stdout.write(os.linesep)
sys.stdout.write("Data after node metadata: ")
for c in data_after_node_metadata:
sys.stdout.write("%2.2x "%ord(c))
sys.stdout.write(os.linesep)
traceback.print_exc()
print("Drawing image")
# Drawing the picture
starttime = time.time()
n = 0
for (x, z) in stuff.iterkeys():
if n % 500000 == 0:
nowtime = time.time()
dtime = nowtime - starttime
try:
n_per_second = 1.0 * n / dtime
except ZeroDivisionError:
n_per_second = 0
if n_per_second != 0:
listlen = len(stuff)
seconds_per_n = 1.0 / n_per_second
time_guess = seconds_per_n * listlen
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)")
n += 1
(r, g, b) = colors[stuff[(x, z)][1]]
dnd = stuff[(x, z)][3] # day/night differs?
if not dnd and not drawunderground:
if stuff[(x, z)][2] > 0: # water
(r, g, b) = colors[CONTENT_WATER]
else:
continue
# Comparing heights of a couple of adjacent blocks and changing
# brightness accordingly.
try:
c = stuff[(x, z)][1]
c1 = stuff[(x - 1, z)][1]
c2 = stuff[(x, z + 1)][1]
dnd1 = stuff[(x - 1, z)][3]
dnd2 = stuff[(x, z + 1)][3]
if not dnd:
d = -69
elif not content_is_water(c1) and not content_is_water(c2) and \
not content_is_water(c):
y = stuff[(x, z)][0]
y1 = stuff[(x - 1, z)][0] if dnd1 else y
y2 = stuff[(x, z + 1)][0] if dnd2 else y
d = ((y - y1) + (y - y2)) * 12
else:
d = 0
if(d > 36):
d = 36
r = limit(r + d, 0, 255)
g = limit(g + d, 0, 255)
b = limit(b + d, 0, 255)
except:
pass
# Water
if(stuff[(x, z)][2] > 0):
r = int(r * .15 + colors[2][0] * .85)
g = int(g * .15 + colors[2][1] * .85)
b = int(b * .15 + colors[2][2] * .85)
impix[x - minx * 16 + border, h - 1 - (z - minz * 16) + border] = (r, g, b)
if draworigin:
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()
if drawscale:
draw.text((24, 0), "X", font=font, fill=scalecolor)
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)
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)
if drawplayers:
try:
for filename in os.listdir(path + "players"):
f = file(path + "players/" + filename)
lines = f.readlines()
name = ""
position = []
for line in lines:
p = string.split(line)
if p[0] == "name":
name = p[2]
print(filename + ": name = " + name)
if p[0] == "position":
position = string.split(p[2][1:-1], ",")
print(filename + ": position = " + p[2])
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)
f.close()
except OSError:
pass
print("Saving")
im.save(output)
if unknown_node_names:
sys.stdout.write("Unknown node names:")
for name in unknown_node_names:
sys.stdout.write(" "+name)
sys.stdout.write(os.linesep)
if unknown_node_ids:
sys.stdout.write("Unknown node ids:")
for node_id in unknown_node_ids:
sys.stdout.write(" "+str(hex(node_id)))
sys.stdout.write(os.linesep)
Loading…
Cancel
Save