Schematics are now exported with an ItemIDs mapping and an itemStackVersion value.

Remove and comment out some dead/unused code. INVEditChest shouldn't be important now that we have an inventory editor.
This commit is contained in:
David Vierra 2015-05-08 23:11:02 -10:00
parent 839ae3c3d3
commit 346f5c7c73

View File

@ -8,14 +8,15 @@ from collections import defaultdict
import os
from logging import getLogger
import itertools
from numpy import array, swapaxes, uint8, zeros
from numpy import swapaxes, uint8, zeros
import numpy
from mceditlib.anvil.adapter import AnvilWorldAdapter
from mceditlib.anvil.entities import PCEntityRef
from mceditlib.anvil.entities import PCTileEntityRef
from mceditlib.exceptions import PlayerNotFound
from mceditlib.anvil.adapter import VERSION_1_7, VERSION_1_8
from mceditlib.anvil.entities import PCEntityRef, PCTileEntityRef, \
ItemStackRef
from mceditlib.exceptions import PlayerNotFound, LevelFormatError
from mceditlib.selection import BoundingBox
from mceditlib.fakechunklevel import FakeChunkedLevelAdapter, FakeChunkData
from mceditlib.blocktypes import BlockTypeSet, PCBlockTypeSet
@ -41,7 +42,10 @@ def blockIDMapping(blocktypes):
return mapping
def itemIDMapping(blocktypes):
return nbt.TAG_Compound()
mapping = nbt.TAG_Compound()
for name, ID in blocktypes.itemTypes.IDsByInternalName.iteritems():
mapping[str(ID)] = nbt.TAG_String(name)
return mapping
class SchematicChunkData(FakeChunkData):
def addEntity(self, entity):
@ -55,8 +59,6 @@ class SchematicFileAdapter(FakeChunkedLevelAdapter):
"""
# XXX use abstract entity ref or select correct ref for contained level format
EntityRef = PCEntityRef
TileEntityRef = PCTileEntityRef
ChunkDataClass = SchematicChunkData
@ -78,6 +80,9 @@ class SchematicFileAdapter(FakeChunkedLevelAdapter):
:rtype: SchematicFileAdapter
"""
self.EntityRef = PCEntityRef
self.TileEntityRef = PCTileEntityRef
if filename is None and shape is None:
raise ValueError("shape or filename required to create %s" % self.__class__.__name__)
@ -141,14 +146,35 @@ class SchematicFileAdapter(FakeChunkedLevelAdapter):
if "Biomes" in self.rootTag:
self.rootTag["Biomes"].value.shape = (l, w)
if "BlockIDs" in self.rootTag or "ItemIDs" in self.rootTag:
self.blocktypes = type(self.blocktypes)()
# If BlockIDs is present, it contains an ID->internalName mapping
# from the source level's FML tag.
if "BlockIDs" in self.rootTag:
self.blocktypes.addBlockIDsFromSchematicTag(self.rootTag["BlockIDs"])
if "ItemIDs" in self.rootTag:
self.blocktypes.addItemIDsFromSchematicTag(self.rootTag["ItemIDs"])
# If itemStackVersion is present, it was exported from MCEdit 2.0.
# Its value is either 17 or 18, the values of the version constants.
# ItemIDs will also be present.
# If itemStackVersion is not present, this schematic was exported from
# WorldEdit or MCEdit 1.0. The itemStackVersion cannot be determined
# without searching the entities for an itemStack and checking
# the type of its `id` tag. If no itemStacks are found, the
# version defaults to 1.8 which does not need an ItemIDs tag.
if "itemStackVersion" in self.rootTag:
itemStackVersion = self.rootTag["itemStackVersion"].value
if itemStackVersion not in (VERSION_1_7, VERSION_1_8):
raise LevelFormatError("Unknown item stack version %d" % itemStackVersion)
if itemStackVersion == VERSION_1_7:
itemIDs = self.rootTag.get("ItemIDs")
if itemIDs is not None:
self.blocktypes.addItemIDsFromSchematicTag(itemIDs)
self.blocktypes.itemStackVersion = itemStackVersion
else:
self.blocktypes.itemStackVersion = self.getItemStackVersionFromEntities()
else:
@ -160,6 +186,8 @@ class SchematicFileAdapter(FakeChunkedLevelAdapter):
rootTag["Entities"] = nbt.TAG_List()
rootTag["TileEntities"] = nbt.TAG_List()
rootTag["Materials"] = nbt.TAG_String(self.blocktypes.name)
rootTag["itemStackVersion"] = nbt.TAG_Byte(self.blocktypes.itemStackVersion)
self._Blocks = zeros((shape[1], shape[2], shape[0]), 'uint16')
rootTag["Data"] = nbt.TAG_Byte_Array(zeros((shape[1], shape[2], shape[0]), uint8))
@ -169,9 +197,11 @@ class SchematicFileAdapter(FakeChunkedLevelAdapter):
self.rootTag = rootTag
self.rootTag["BlockIDs"] = blockIDMapping(blocktypes)
self.rootTag["ItemIDs"] = itemIDMapping(blocktypes)
itemMapping = itemIDMapping(blocktypes)
if itemMapping is not None:
self.rootTag["ItemIDs"] = itemMapping # Only present for Forge 1.7
#expand blocks and data to chunk edges
# Expand blocks and data to chunk edges
h16 = (self.Height + 15) & ~0xf
l16 = (self.Length + 15) & ~0xf
w16 = (self.Width + 15) & ~0xf
@ -200,6 +230,21 @@ class SchematicFileAdapter(FakeChunkedLevelAdapter):
cx, cy, cz = pos.chunkPos()
self.tileEntitiesByChunk[cx, cz].append(tag)
def getItemStackVersionFromEntities(self):
for name, tag, path in nbt.walk(
itertools.chain(self.rootTag["Entities"],
self.rootTag["TileEntities"])):
if ItemStackRef.tagIsItemStack(tag):
if tag["id"].tagID == nbt.ID_STRING:
return VERSION_1_8
if tag["id"].tagID == nbt.ID_SHORT:
return VERSION_1_7
# No itemstacks - use version 1.8 since ItemIDs won't need to
# be added to the root tag.
return VERSION_1_8
def fakeEntitiesForChunk(self, cx, cz):
return self.entitiesByChunk[cx, cz], self.tileEntitiesByChunk[cx, cz]
@ -494,26 +539,6 @@ class SchematicFileAdapter(FakeChunkedLevelAdapter):
return 0
return self.Data[x, z, y]
@classmethod
def chestWithItemID(cls, itemID, count=64, damage=0):
""" Creates a chest with a stack of 'itemID' in each slot.
Optionally specify the count of items in each stack. Pass a negative
value for damage to create unnaturally sturdy tools. """
rootTag = nbt.TAG_Compound()
invTag = nbt.TAG_List()
rootTag["Inventory"] = invTag
for slot in range(9, 36):
itemTag = nbt.TAG_Compound()
itemTag["Slot"] = nbt.TAG_Byte(slot)
itemTag["Count"] = nbt.TAG_Byte(count)
itemTag["id"] = nbt.TAG_Short(itemID)
itemTag["Damage"] = nbt.TAG_Short(damage)
invTag.append(itemTag)
chest = INVEditChest(rootTag, "")
return chest
def readChunk(self, cx, cz, dimName, create=False):
chunk = super(SchematicFileAdapter, self).readChunk(cx, cz, dimName, create)
if "Biomes" in self.rootTag:
@ -523,108 +548,3 @@ class SchematicFileAdapter(FakeChunkedLevelAdapter):
srcBiomes = self.Biomes[x:x + 16, z:z + 16]
chunk.Biomes[0:srcBiomes.shape[0], 0:srcBiomes.shape[1]] = srcBiomes
return chunk
#
# class INVEditChest(FakeChunkedLevelAdapter):
# Width = 1
# Height = 1
# Length = 1
# Blocks = None
# Data = array([[[0]]], 'uint8')
# Entities = nbt.TAG_List()
# Materials = pc_blocktypes
#
# @classmethod
# def _isTagLevel(cls, rootTag):
# return "Inventory" in rootTag
#
# def __init__(self, filename):
# self.filename = filename
# rootTag = nbt.load(filename)
# self.Blocks = array([[[pc_blocktypes.Chest.ID]]], 'uint8')
# for item in list(rootTag["Inventory"]):
# slot = item["Slot"].value
# if slot < 9 or slot >= 36:
# rootTag["Inventory"].remove(item)
# else:
# item["Slot"].value -= 9 # adjust for different chest slot indexes
#
# self.rootTag = rootTag
#
# @property
# def TileEntities(self):
# chestTag = nbt.TAG_Compound()
# chestTag["id"] = nbt.TAG_String("Chest")
# chestTag["Items"] = nbt.TAG_List(self.rootTag["Inventory"])
# chestTag["x"] = nbt.TAG_Int(0)
# chestTag["y"] = nbt.TAG_Int(0)
# chestTag["z"] = nbt.TAG_Int(0)
#
# return nbt.TAG_List([chestTag], name="TileEntities")
#
#
# class ZipSchematic (AnvilWorldAdapter):
# def __init__(self, filename, create=False):
# raise NotImplementedError("No adapter for zipped world/schematic files yet!!!")
# self.zipfilename = filename
#
# tempdir = tempfile.mktemp("schematic")
# if create is False:
# zf = zipfile.ZipFile(filename)
# zf.extractall(tempdir)
# zf.close()
#
# super(ZipSchematic, self).__init__(tempdir, create)
# atexit.register(shutil.rmtree, self.worldFolder.filename, True)
#
#
# try:
# schematicDat = nbt.load(self.worldFolder.getFilePath("schematic.dat"))
#
# self.Width = schematicDat['Width'].value
# self.Height = schematicDat['Height'].value
# self.Length = schematicDat['Length'].value
#
# if "Materials" in schematicDat:
# self.blocktypes = blocktypeClassesByName[schematicDat["Materials"].value]()
#
# except Exception as e:
# print "Exception reading schematic.dat, skipping: {0!r}".format(e)
# self.Width = 0
# self.Length = 0
#
# def __del__(self):
# shutil.rmtree(self.worldFolder.filename, True)
#
# def saveChanges(self):
# self.saveToFile(self.zipfilename)
#
# def saveToFile(self, filename):
# super(ZipSchematic, self).saveChanges()
# schematicDat = nbt.TAG_Compound()
# schematicDat.name = "Mega Schematic"
#
# schematicDat["Width"] = nbt.TAG_Int(self.size[0])
# schematicDat["Height"] = nbt.TAG_Int(self.size[1])
# schematicDat["Length"] = nbt.TAG_Int(self.size[2])
# schematicDat["Materials"] = nbt.TAG_String(self.blocktypes.name)
#
# schematicDat.save(self.worldFolder.getFilePath("schematic.dat"))
#
# basedir = self.worldFolder.filename
# assert os.path.isdir(basedir)
# with closing(zipfile.ZipFile(filename, "w", zipfile.ZIP_STORED)) as z:
# for root, dirs, files in os.walk(basedir):
# # NOTE: ignore empty directories
# for fn in files:
# absfn = os.path.join(root, fn)
# zfn = absfn[len(basedir) + len(os.sep):] # XXX: relative path
# z.write(absfn, zfn)
#
# def getWorldBounds(self):
# return BoundingBox((0, 0, 0), (self.Width, self.Height, self.Length))
#
# @classmethod
# def canOpenFile(cls, filename):
# return zipfile.is_zipfile(filename)
#