forked from Mirrorlandia_minetest/minetest
Remove minetestmapper from this repository (#5901)
It's now located at https://github.com/minetest/minetestmapper Remove colors.txt and sectors2sqlite too
This commit is contained in:
parent
26e2eb019a
commit
03ff53e16b
@ -1,78 +0,0 @@
|
|||||||
0 128 128 128 # CONTENT_STONE
|
|
||||||
2 39 66 106 # CONTENT_WATER
|
|
||||||
3 255 255 0 # CONTENT_TORCH
|
|
||||||
9 39 66 106 # CONTENT_WATERSOURCE
|
|
||||||
e 117 86 41 # CONTENT_SIGN_WALL
|
|
||||||
f 128 79 0 # CONTENT_CHEST
|
|
||||||
10 118 118 118 # CONTENT_FURNACE
|
|
||||||
15 103 78 42 # CONTENT_FENCE
|
|
||||||
1e 162 119 53 # CONTENT_RAIL
|
|
||||||
1f 154 110 40 # CONTENT_LADDER
|
|
||||||
20 255 100 0 # CONTENT_LAVA
|
|
||||||
21 255 100 0 # CONTENT_LAVASOURCE
|
|
||||||
800 107 134 51 # CONTENT_GRASS
|
|
||||||
801 86 58 31 # CONTENT_TREE
|
|
||||||
802 48 95 8 # CONTENT_LEAVES
|
|
||||||
803 102 129 38 # CONTENT_GRASS_FOOTSTEPS
|
|
||||||
804 178 178 0 # CONTENT_MESE
|
|
||||||
805 101 84 36 # CONTENT_MUD
|
|
||||||
808 104 78 42 # CONTENT_WOOD
|
|
||||||
809 210 194 156 # CONTENT_SAND
|
|
||||||
80a 123 123 123 # CONTENT_COBBLE
|
|
||||||
80b 199 199 199 # CONTENT_STEEL
|
|
||||||
80c 183 183 222 # CONTENT_GLASS
|
|
||||||
80d 219 202 178 # CONTENT_MOSSYCOBBLE
|
|
||||||
80e 70 70 70 # CONTENT_GRAVEL
|
|
||||||
80f 204 0 0 # CONTENT_SANDSTONE
|
|
||||||
810 0 215 0 # CONTENT_CACTUS
|
|
||||||
811 170 50 25 # CONTENT_BRICK
|
|
||||||
812 104 78 42 # CONTENT_CLAY
|
|
||||||
813 58 105 18 # CONTENT_PAPYRUS
|
|
||||||
814 196 160 0 # CONTENT_BOOKSHELF
|
|
||||||
815 205 190 121 # CONTENT_JUNGLETREE
|
|
||||||
816 62 101 25 # CONTENT_JUNGLEGRASS
|
|
||||||
817 255 153 255 # CONTENT_NC
|
|
||||||
818 102 50 255 # CONTENT_NC_RB
|
|
||||||
819 200 0 0 # CONTENT_APPLE
|
|
||||||
|
|
||||||
default:stone 128 128 128
|
|
||||||
default:stone_with_coal 50 50 50
|
|
||||||
default:water_flowing 39 66 106
|
|
||||||
default:torch 255 255 0
|
|
||||||
default:water_source 39 66 106
|
|
||||||
default:sign_wall 117 86 41
|
|
||||||
default:chest 128 79 0
|
|
||||||
default:furnace 118 118 118
|
|
||||||
default:fence_wood 103 78 42
|
|
||||||
default:rail 162 119 53
|
|
||||||
default:ladder 154 110 40
|
|
||||||
default:lava_flowing 255 100 0
|
|
||||||
default:lava_source 255 100 0
|
|
||||||
default:dirt_with_grass 107 134 51
|
|
||||||
default:tree 86 58 31
|
|
||||||
default:leaves 48 95 8
|
|
||||||
default:dirt_with_grass_and_footsteps 102 129 38
|
|
||||||
default:mese 178 178 0
|
|
||||||
default:dirt 101 84 36
|
|
||||||
default:wood 104 78 42
|
|
||||||
default:sand 210 194 156
|
|
||||||
default:cobble 123 123 123
|
|
||||||
default:steelblock 199 199 199
|
|
||||||
default:glass 183 183 222
|
|
||||||
default:mossycobble 219 202 178
|
|
||||||
default:gravel 70 70 70
|
|
||||||
default:sandstone 204 0 0
|
|
||||||
default:cactus 0 215 0
|
|
||||||
default:brick 170 50 25
|
|
||||||
default:clay 104 78 42
|
|
||||||
default:papyrus 58 105 18
|
|
||||||
default:bookshelf 196 160 0
|
|
||||||
default:jungletree 205 190 121
|
|
||||||
default:junglegrass 62 101 25
|
|
||||||
default:nyancat 255 153 255
|
|
||||||
default:nyancat_rainbow 102 50 255
|
|
||||||
default:apple 200 0 0
|
|
||||||
default:desert_sand 210 180 50
|
|
||||||
default:desert_stone 150 100 30
|
|
||||||
default:dry_shrub 100 80 40
|
|
||||||
|
|
@ -1,752 +0,0 @@
|
|||||||
#!/usr/bin/env python2
|
|
||||||
# -*- 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 Do What The Fuck You Want
|
|
||||||
# To 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
|
|
||||||
--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"])
|
|
||||||
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
|
|
||||||
|
|
||||||
sector_xmin = -1500 / 16
|
|
||||||
sector_xmax = 1500 / 16
|
|
||||||
sector_zmin = -1500 / 16
|
|
||||||
sector_zmax = 1500 / 16
|
|
||||||
|
|
||||||
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
|
|
||||||
else:
|
|
||||||
assert False, "unhandled option"
|
|
||||||
|
|
||||||
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("World 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)
|
|
||||||
|
|
@ -1,132 +0,0 @@
|
|||||||
#!/usr/bin/python3
|
|
||||||
|
|
||||||
# Loads block files from sectors folders into map.sqlite database.
|
|
||||||
# The sectors folder should be safe to remove after this prints "Finished."
|
|
||||||
|
|
||||||
import time, os, sys
|
|
||||||
|
|
||||||
try:
|
|
||||||
import sqlite3
|
|
||||||
except:
|
|
||||||
exit('You need to have the Python sqlite3 module.')
|
|
||||||
|
|
||||||
path = "../world/"
|
|
||||||
|
|
||||||
paths = []
|
|
||||||
|
|
||||||
# sectors2 gets to try first
|
|
||||||
if os.path.isdir(path + 'sectors2/'):
|
|
||||||
paths.append('sectors2')
|
|
||||||
if os.path.isdir(path + 'sectors/'):
|
|
||||||
paths.append('sectors')
|
|
||||||
|
|
||||||
if not paths:
|
|
||||||
exit('Could not find sectors folder at ' + path + 'sectors2/ or ' + path + 'sectors/')
|
|
||||||
|
|
||||||
def parseSigned12bit(u):
|
|
||||||
u = int('0x'+u, 16)
|
|
||||||
return (u if u < 2**11 else u - 2**12)
|
|
||||||
|
|
||||||
def parseSigned16bit(u):
|
|
||||||
u = int('0x'+u, 16)
|
|
||||||
return (u if u < 2**15 else u - 2**16)
|
|
||||||
|
|
||||||
def int64(u):
|
|
||||||
while u >= 2**63:
|
|
||||||
u -= 2**64
|
|
||||||
while u <= -2**63:
|
|
||||||
u += 2**64
|
|
||||||
return u
|
|
||||||
|
|
||||||
# Convert sector folder(s) to integer
|
|
||||||
def getSectorPos(dirname):
|
|
||||||
if len(dirname) == 8:
|
|
||||||
# Old layout
|
|
||||||
x = parseSigned16bit(dirname[:4])
|
|
||||||
z = parseSigned16bit(dirname[4:])
|
|
||||||
elif len(dirname) == 7:
|
|
||||||
# New layout
|
|
||||||
x = parseSigned12bit(dirname[:3])
|
|
||||||
z = parseSigned12bit(dirname[4:])
|
|
||||||
else:
|
|
||||||
print('Terrible sector at ' + dirname)
|
|
||||||
return
|
|
||||||
|
|
||||||
return x, z
|
|
||||||
|
|
||||||
# Convert block file to integer position
|
|
||||||
def getBlockPos(sectordir, blockfile):
|
|
||||||
p2d = getSectorPos(sectordir)
|
|
||||||
|
|
||||||
if not p2d:
|
|
||||||
return
|
|
||||||
|
|
||||||
if len(blockfile) != 4:
|
|
||||||
print("Invalid block filename: " + blockfile)
|
|
||||||
|
|
||||||
y = parseSigned16bit(blockfile)
|
|
||||||
|
|
||||||
return p2d[0], y, p2d[1]
|
|
||||||
|
|
||||||
# Convert location to integer
|
|
||||||
def getBlockAsInteger(p):
|
|
||||||
return int64(p[2]*16777216 + p[1]*4096 + p[0])
|
|
||||||
|
|
||||||
# Init
|
|
||||||
|
|
||||||
create = False
|
|
||||||
if not os.path.isfile(path + 'map.sqlite'):
|
|
||||||
create = True
|
|
||||||
|
|
||||||
conn = sqlite3.connect(path + 'map.sqlite')
|
|
||||||
|
|
||||||
if not conn:
|
|
||||||
exit('Could not open database.')
|
|
||||||
|
|
||||||
cur = conn.cursor()
|
|
||||||
|
|
||||||
if create:
|
|
||||||
cur.execute("CREATE TABLE IF NOT EXISTS `blocks` (`pos` INT NOT NULL PRIMARY KEY, `data` BLOB);")
|
|
||||||
conn.commit()
|
|
||||||
print('Created database at ' + path + 'map.sqlite')
|
|
||||||
|
|
||||||
# Crawl the folders
|
|
||||||
|
|
||||||
count = 0
|
|
||||||
t = time.time()
|
|
||||||
for base in paths:
|
|
||||||
v = 0
|
|
||||||
if base == 'sectors':
|
|
||||||
v = 1
|
|
||||||
elif base == 'sectors2':
|
|
||||||
v= 2
|
|
||||||
else:
|
|
||||||
print('Ignoring base ' + base)
|
|
||||||
continue
|
|
||||||
|
|
||||||
for root, dirs, files in os.walk(path + base):
|
|
||||||
if files:
|
|
||||||
for block in files:
|
|
||||||
pos = getBlockAsInteger(getBlockPos(root[(-8 if v == 1 else -7 if v == 2 else 0):], block))
|
|
||||||
|
|
||||||
if pos is None:
|
|
||||||
print('Ignoring broken path ' + root + '/' + block)
|
|
||||||
continue
|
|
||||||
|
|
||||||
f = open(root+'/'+block, 'rb')
|
|
||||||
blob = f.read()
|
|
||||||
f.close()
|
|
||||||
if sys.version_info.major == 2:
|
|
||||||
blob = buffer(blob)
|
|
||||||
else:
|
|
||||||
blob = memoryview(blob)
|
|
||||||
cur.execute('INSERT OR IGNORE INTO `blocks` VALUES(?, ?)', (pos, blob))
|
|
||||||
count += 1
|
|
||||||
|
|
||||||
if(time.time() - t > 3):
|
|
||||||
t = time.time()
|
|
||||||
print(str(count)+' blocks processed...')
|
|
||||||
|
|
||||||
conn.commit()
|
|
||||||
|
|
||||||
print('Finished. (' + str(count) + ' blocks)')
|
|
Loading…
Reference in New Issue
Block a user