Added misc comments, and debugging print() Corrected mirrored import of x axis for Anvil format files Changed so unknown blocks are imported and will be displayed as unknown (otherwise blocks are ignored and imports can have significant holes in them)

This commit is contained in:
dgm3333 2015-03-09 09:36:34 +00:00 committed by Novatux
parent e52fe25b26
commit d5ae91aa04
3 changed files with 91 additions and 21 deletions

View File

@ -19,9 +19,13 @@ class MCMap:
if len(filenames) > 0:
self.ext = ext
break
chunkCounta = 0
chunkCountb = 0
for filename in filenames:
chunkCounta += 1
s = filename.split(".")
cx, cz = int(s[1])*32, int(s[2])*32
with open(os.path.join(self.world_path, filename), "rb") as f:
for chkx in range(cx, cx+32):
for chkz in range(cz, cz+32):
@ -29,7 +33,9 @@ class MCMap:
f.seek(offset)
if bytesToInt(f.read(3)) != 0:
self.chunk_pos.append((chkx, chkz))
chunkCountb += 1
# print('Total Chunks: ' + str(len(self.chunk_pos)))
def getChunk(self, chkx, chkz):
return MCChunk(chkx, chkz, self.world_path, self.ext)
@ -67,14 +73,19 @@ class MCChunk:
else:
for yslice in range(8):
self.blocks.append(MCBlock(raw_data, (chkx, chkz), yslice, False))
class MCBlock:
"""A 16x16x16 block"""
def __init__(self, chunk, chunkpos, yslice, is_anvil=True):
self.pos = (chunkpos[0], yslice, chunkpos[1])
self.pos = (-chunkpos[0]-1, yslice, chunkpos[1])
if is_anvil:
# the x axis has to be inverted to convert to minetest (the chunk location is at the L lower corner, so subtract one or there would be 2 chunks at 0).
# This converts the chunk location (node level data is converted by reverse_X_axis)
# It would be better here rather than the line above so it doesn't affect pre-anvil conversion (as this hasn't been implemented within the chunks)
# but doesn't like it, so...
#self.pos[0] = -self.pos[0]-1
# Find the slice
for section in chunk["Sections"]:
if section["Y"] == yslice:
@ -83,24 +94,56 @@ class MCBlock:
else:
# No luck, we have to convert
self.from_chunk(chunk, yslice)
self.tile_entities = []
for te in chunk["TileEntities"]:
if (te["y"]>>4) == yslice:
t = te.copy()
# Entity data stores it's own position information, so has to be modified independently in addition to other blocks.
t["y"] &= 0xf
t["y"] = t["y"] -16
# within the chunk x position has to be inverted to convert to minetest:-
if is_anvil:
t["x"] = self.pos[0]*16 + 15-t["x"]%16
self.tile_entities.append(t)
@staticmethod
def expand_half_bytes(l):
l2 = []
for i in l:
l2.append(i&0xf)
l2.append((i>>4)&0xf)
return l2
# This function reverses x axis node order within each slice, and
# expands the 4bit sequences into 8bit sequences
l3=[]
for y in range(0,2047,128):
for z in range(0,127,8):
locSt=y+z
l2 = l[locSt:locSt+8]
for i in reversed(l2):
l3.append((i>>4)&0xf)
l3.append(i&0xf)
return l3
@staticmethod
def reverse_X_axis(l):
# Anvil format is YZX ((y * 16 + z) * 16 + x)
# block data is actually u12 per data point (ie per node)
# but is split into u8 (='blocks') dealt with in reverse_X_axis() and u4 (='data') dealt with in expand_half_bytes()
# NB data, skylight and blocklight are only 4bits of data
# To convert minecraft to minetest coordinates you must invert the x order while leaving y and z the same
l3=[]
for y in range(0,4095,256):
for z in range(0,255,16):
locSt=y+z
l2 = l[locSt:locSt+16]
for i in reversed(l2):
l3.append(i)
return l3
def from_section(self, section):
self.blocks = section["Blocks"]
self.blocks = self.reverse_X_axis(section["Blocks"])
self.data = self.expand_half_bytes(section["Data"])
self.sky_light = self.expand_half_bytes(section["SkyLight"])
self.block_light = self.expand_half_bytes(section["BlockLight"])
@ -147,7 +190,7 @@ class MCBlock:
k2 += 256 # Skip a layer
k3 += 256
return data2
def from_chunk(self, chunk, yslice):
self.blocks = self.extract_slice(chunk["Blocks"], yslice)
self.data = self.extract_slice_half_bytes(chunk["Data"], yslice)
@ -158,24 +201,32 @@ class MTBlock:
def __init__(self, name_id_mapping):
self.name_id_mapping = name_id_mapping
self.content = [0]*4096
self.mcblockidentifier = ['']*4096
self.param1 = [0]*4096
self.param2 = [0]*4096
self.metadata = {}
self.pos = (0, 0, 0)
def fromMCBlock(self, mcblock, conversion_table):
# print('\n***fromMCBlock: Starting New Block***')
self.pos = (mcblock.pos[0], mcblock.pos[1]-4, mcblock.pos[2])
content = self.content
mcblockidentifier = self.mcblockidentifier
param1 = self.param1
param2 = self.param2
blocks = mcblock.blocks
data = mcblock.data
skylight = mcblock.sky_light
blocklight = mcblock.block_light
# now load all the nodes in the 16x16x16 (=4096) block
for i in range(4096):
content[i], param2[i] = conversion_table[blocks[i]][data[i]]
param1[i] = max(blocklight[i], skylight[i])|(blocklight[i]<<4)
mcblockidentifier[i] = str(blocks[i]) + ':' + str(data[i])
if content[i]==0 and param2[i]==0 and not (blocks[i]==0):
print('Unknown Minecraft Block:' + str(mcblockidentifier[i])) # This is the minecraft ID#/data as listed in map_content.txt
for te in mcblock.tile_entities:
id = te["id"]
@ -183,6 +234,10 @@ class MTBlock:
index = ((y&0xf)<<8)|((z&0xf)<<4)|(x&0xf)
f = te_convert.get(id.lower(), lambda arg: (None, None, None)) # Do nothing if not found
block, p2, meta = f(te)
# print('\nEntityInfoPre: ' +str(te)) # EntityInfo: if you want to print pre-conversion entity information then uncomment this line
# print('EntityInfoPost: ' +' y='+str(y)+' z='+str(z)+' x='+str(x)+' Meta:'+str(meta)) # EntityInfo: if you want to print post-conversion entity information then uncomment this line
# NB block and p2 never seems to be returned, but if this is important, then just change the above 'meta' to 'f(te)'
if block != None:
blocks[index] = block
if p2 != None:
@ -193,10 +248,16 @@ class MTBlock:
def save(self):
os = BytesIO()
writeU8(os, 25) # Version
#flags
flags = 0x00
if self.pos[1] < -1:
flags |= 0x01
flags |= 0x01 #is_underground
flags |= 0x02 #day_night_differs
flags |= 0x04 #lighting_expired
flags |= 0x08 #generated
writeU8(os, flags)
writeU8(os, 2) # content_width
writeU8(os, 2) # params_width
@ -244,6 +305,7 @@ class MTBlock:
# Nodemeta
meta = self.metadata
cbuffer = BytesIO()
writeU8(cbuffer, 1) # Version
writeU16(cbuffer, len(meta))
@ -273,7 +335,7 @@ class MTBlock:
# Node timer
writeU8(os, 2+4+4) # Timer data len
writeU16(os, 0) # Number of timers
return os.getvalue()
class MTMap:
@ -309,20 +371,21 @@ class MTMap:
conn.commit()
num_saved += 1
cur.execute("INSERT INTO blocks VALUES (?,?)",
(self.getBlockAsInteger(block.pos),
# (self.getBlockAsInteger((-block.pos[0],block.pos[1],block.pos[2])),
(self.getBlockAsInteger((block.pos[0],block.pos[1],block.pos[2])),
block.save()))
conn.commit()
conn.close()
if __name__ == "__main__":
# Tests
from random import randrange
t = [randrange(256) for i in range(2048*8)]
assert(MCBlock.extract_slice(MCBlock.expand_half_bytes(t), 0)
== MCBlock.extract_slice_half_bytes(t, 0))
from time import time
t0 = time()
s1 = MCBlock.extract_slice(MCBlock.expand_half_bytes(t), 1)

View File

@ -26,18 +26,23 @@ def preprocess(lines, flags):
def get_id(name_id_mapping, name):
try:
# if the name of the node is known then return the location with the name_id_mapping list
return name_id_mapping.index(name)
except:
# else add the name of the node to the list then return the location
name_id_mapping.append(name)
return len(name_id_mapping)-1
def read_content(flags):
with open("map_content.txt", "r") as f:
lines = f.readlines()
lines = preprocess(lines, flags)
name_id_mapping = ["air"]
bd = {}
# if you map to air, then unknown blocks will be ignored
# name_id_mapping = ["air"]
name_id_mapping = ["mcblock:unknown"]
bd = {} # bd is block data, and keeps a list of the node names in the block
# iterate through all the lines in the map_content.txt file
for line in lines:
s = line.split("\t")
if len(s) >= 2:

View File

@ -1,4 +1,6 @@
from itemstack import *
#If you wish to add more entities, then...
# To print out pre and post-conversion entity information uncomment line 237 (ish) in blocks.py (search for 'EntityInfo' to locate it)
def convert_chest(te):
formspec = "size[8,9]"+\