Fix [Tile]Entities not getting exported to schematics
Because nobody was really sure where they were being stored.
This commit is contained in:
parent
31dc9d49bb
commit
34285d3dc9
@ -129,7 +129,7 @@ def copyBlocksIter(destLevel, sourceLevel, sourceSelection, destinationPoint, bl
|
||||
continue
|
||||
|
||||
# Recompute destSectionBox and intersect using the shape of destSection.Blocks
|
||||
# after destChunk is loaded to work with odd shaped FakeChunks XXXXXXXXXXXX
|
||||
# after destChunk is loaded to work with odd shaped FakeChunkDatas XXXXXXXXXXXX
|
||||
destSectionBox = SectionBox(destCpos[0], destCy, destCpos[1], destSection)
|
||||
intersect = destSectionBox.intersect(destBox)
|
||||
if intersect.volume == 0:
|
||||
|
@ -23,6 +23,96 @@ log = getLogger(__name__)
|
||||
GetBlocksResult = namedtuple("GetBlocksResult", ["Blocks", "Data", "BlockLight", "SkyLight", "Biomes"])
|
||||
|
||||
|
||||
class FakeSection(object):
|
||||
pass
|
||||
|
||||
|
||||
class FakeChunkData(object):
|
||||
dirty = False
|
||||
|
||||
dimension = NotImplemented #: The parent dimension that this chunk belongs to
|
||||
cx = cz = NotImplemented #: This chunk's position as a tuple (cx, cz)
|
||||
|
||||
Width = Length = 16
|
||||
|
||||
dimName = ""
|
||||
_heightMap = None
|
||||
HeightMap = None
|
||||
#
|
||||
# @property
|
||||
# def HeightMap(self):
|
||||
# """
|
||||
# Compute, cache, and return an artificial HeightMap for levels that don't provide one.
|
||||
# :return: Array of height info.
|
||||
# :rtype: ndarray(shape=(16, 16))
|
||||
# """
|
||||
# if self._heightMap is not None:
|
||||
# return self._heightMap
|
||||
# from mceditlib.heightmaps import computeChunkHeightMap
|
||||
#
|
||||
# self._heightMap = computeChunkHeightMap(self)
|
||||
# return self._heightMap
|
||||
#
|
||||
# pass
|
||||
|
||||
def sectionPositions(self):
|
||||
return self.dimension.bounds.sectionPositions(self.cx, self.cz)
|
||||
|
||||
@property
|
||||
def Height(self):
|
||||
return self.dimension.Height
|
||||
|
||||
@property
|
||||
def bounds(self):
|
||||
return BoundingBox((self.cx << 4, 0, self.cz << 4), self.size)
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
return self.Width, self.Height, self.Length
|
||||
|
||||
def sectionPositions(self):
|
||||
return range(0, (self.Height + 15) >> 4)
|
||||
|
||||
def chunkChanged(self, needsLighting=True):
|
||||
"""
|
||||
You are required to call this function after directly modifying any of a chunk's
|
||||
arrays or its rootTag. Alternately, just set `chunk.dirty = True`
|
||||
|
||||
needsLighting is deprecated; Use the updateLights= argument
|
||||
of setBlocks and other editing functions, or call updateLights(level, x, y, z) to
|
||||
explicitly update lights yourself.
|
||||
|
||||
"""
|
||||
self.dirty = True
|
||||
|
||||
@property
|
||||
def blocktypes(self):
|
||||
return self.dimension.blocktypes
|
||||
|
||||
def getSection(self, cy, create=False):
|
||||
y = cy << 4
|
||||
if y < self.bounds.miny or y >= self.bounds.maxy:
|
||||
return None
|
||||
|
||||
section = FakeSection()
|
||||
section.chunk = self
|
||||
slices = numpy.s_[:, :, cy << 4:(cy + 1 << 4)]
|
||||
if hasattr(self, 'Blocks'):
|
||||
section.Blocks = self.Blocks[slices].swapaxes(0, 2)
|
||||
else:
|
||||
raise NotImplementedError("ChunkBase.getSection is only implemented for chunks providing a Blocks array")
|
||||
if hasattr(self, 'Data'):
|
||||
section.Data = self.Data[slices].swapaxes(0, 2)
|
||||
if hasattr(self, 'BlockLight'):
|
||||
section.BlockLight = self.BlockLight[slices].swapaxes(0, 2)
|
||||
if hasattr(self, 'SkyLight'):
|
||||
section.SkyLight = self.SkyLight[slices].swapaxes(0, 2)
|
||||
|
||||
section.Y = cy
|
||||
|
||||
return section
|
||||
|
||||
|
||||
class FakeChunkedLevelAdapter(object):
|
||||
""" FakeChunkedLevelAdapter is an abstract class for implementing fixed size, non-chunked storage formats.
|
||||
Classic, Indev, and Schematic formats inherit from this class. FakeChunkedLevelAdapter has dummy functions for
|
||||
@ -58,6 +148,8 @@ class FakeChunkedLevelAdapter(object):
|
||||
|
||||
hasLights = False
|
||||
|
||||
ChunkDataClass = FakeChunkData
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
return self.Width, self.Height, self.Length
|
||||
@ -68,7 +160,7 @@ class FakeChunkedLevelAdapter(object):
|
||||
|
||||
def readChunk(self, cx, cz, dimName, create=False):
|
||||
"""
|
||||
Creates a FakeChunk object representing the chunk at the given
|
||||
Creates a FakeChunkData object representing the chunk at the given
|
||||
position. Subclasses may choose to override
|
||||
fakeBlocksForChunk and fakeDataForChunk to provide block and blockdata arrays.
|
||||
They may instead override getChunk and return a ChunkBase subclass.
|
||||
@ -78,7 +170,7 @@ class FakeChunkedLevelAdapter(object):
|
||||
"""
|
||||
if not self.bounds.containsChunk(cx, cz):
|
||||
raise ChunkNotPresent((cx, cz))
|
||||
chunk = FakeChunk()
|
||||
chunk = self.ChunkDataClass()
|
||||
chunk.dimension = self
|
||||
chunk.cx = cx
|
||||
chunk.cz = cz
|
||||
@ -360,95 +452,6 @@ class GenericEntityRef(object):
|
||||
# """
|
||||
|
||||
|
||||
class FakeChunk(object):
|
||||
dirty = False
|
||||
|
||||
dimension = NotImplemented #: The parent dimension that this chunk belongs to
|
||||
cx = cz = NotImplemented #: This chunk's position as a tuple (cx, cz)
|
||||
|
||||
Width = Length = 16
|
||||
|
||||
dimName = ""
|
||||
_heightMap = None
|
||||
HeightMap = None
|
||||
#
|
||||
# @property
|
||||
# def HeightMap(self):
|
||||
# """
|
||||
# Compute, cache, and return an artificial HeightMap for levels that don't provide one.
|
||||
# :return: Array of height info.
|
||||
# :rtype: ndarray(shape=(16, 16))
|
||||
# """
|
||||
# if self._heightMap is not None:
|
||||
# return self._heightMap
|
||||
# from mceditlib.heightmaps import computeChunkHeightMap
|
||||
#
|
||||
# self._heightMap = computeChunkHeightMap(self)
|
||||
# return self._heightMap
|
||||
#
|
||||
# pass
|
||||
|
||||
def sectionPositions(self):
|
||||
return self.dimension.bounds.sectionPositions(self.cx, self.cz)
|
||||
|
||||
@property
|
||||
def Height(self):
|
||||
return self.dimension.Height
|
||||
|
||||
@property
|
||||
def bounds(self):
|
||||
return BoundingBox((self.cx << 4, 0, self.cz << 4), self.size)
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
return self.Width, self.Height, self.Length
|
||||
|
||||
def sectionPositions(self):
|
||||
return range(0, (self.Height + 15) >> 4)
|
||||
|
||||
def chunkChanged(self, needsLighting=True):
|
||||
"""
|
||||
You are required to call this function after directly modifying any of a chunk's
|
||||
arrays or its rootTag. Alternately, just set `chunk.dirty = True`
|
||||
|
||||
needsLighting is deprecated; Use the updateLights= argument
|
||||
of setBlocks and other editing functions, or call updateLights(level, x, y, z) to
|
||||
explicitly update lights yourself.
|
||||
|
||||
"""
|
||||
self.dirty = True
|
||||
|
||||
@property
|
||||
def blocktypes(self):
|
||||
return self.dimension.blocktypes
|
||||
|
||||
def getSection(self, cy, create=False):
|
||||
y = cy << 4
|
||||
if y < self.bounds.miny or y >= self.bounds.maxy:
|
||||
return None
|
||||
|
||||
section = FakeSection()
|
||||
section.chunk = self
|
||||
slices = numpy.s_[:, :, cy << 4:(cy + 1 << 4)]
|
||||
if hasattr(self, 'Blocks'):
|
||||
section.Blocks = self.Blocks[slices].swapaxes(0, 2)
|
||||
else:
|
||||
raise NotImplementedError("ChunkBase.getSection is only implemented for chunks providing a Blocks array")
|
||||
if hasattr(self, 'Data'):
|
||||
section.Data = self.Data[slices].swapaxes(0, 2)
|
||||
if hasattr(self, 'BlockLight'):
|
||||
section.BlockLight = self.BlockLight[slices].swapaxes(0, 2)
|
||||
if hasattr(self, 'SkyLight'):
|
||||
section.SkyLight = self.SkyLight[slices].swapaxes(0, 2)
|
||||
|
||||
section.Y = cy
|
||||
|
||||
return section
|
||||
|
||||
|
||||
class FakeSection(object):
|
||||
pass
|
||||
|
||||
|
||||
class LightedChunk(object):
|
||||
"""
|
||||
|
@ -5,6 +5,7 @@ Created on Jul 22, 2011
|
||||
'''
|
||||
from __future__ import absolute_import
|
||||
import atexit
|
||||
from collections import defaultdict
|
||||
from contextlib import closing
|
||||
import os
|
||||
import shutil
|
||||
@ -20,7 +21,7 @@ from mceditlib.anvil.entities import PCEntityRef
|
||||
from mceditlib.anvil.entities import PCTileEntityRef
|
||||
from mceditlib.exceptions import PlayerNotFound
|
||||
from mceditlib.selection import BoundingBox
|
||||
from mceditlib.levelbase import FakeChunkedLevelAdapter
|
||||
from mceditlib.levelbase import FakeChunkedLevelAdapter, FakeChunkData
|
||||
from mceditlib.blocktypes import pc_blocktypes, BlockTypeSet, blocktypes_named
|
||||
from mceditlib import nbt
|
||||
|
||||
@ -33,6 +34,14 @@ def createSchematic(shape, blocktypes='Alpha'):
|
||||
editor = WorldEditor(adapter=adapter)
|
||||
return editor
|
||||
|
||||
|
||||
class SchematicChunkData(FakeChunkData):
|
||||
def addEntity(self, entity):
|
||||
self.dimension.addEntity(entity)
|
||||
|
||||
def addTileEntity(self, tileEntity):
|
||||
self.dimension.addTileEntity(tileEntity)
|
||||
|
||||
class SchematicFileAdapter(FakeChunkedLevelAdapter):
|
||||
"""
|
||||
|
||||
@ -43,6 +52,8 @@ class SchematicFileAdapter(FakeChunkedLevelAdapter):
|
||||
EntityRef = PCEntityRef
|
||||
TileEntityRef = PCTileEntityRef
|
||||
|
||||
ChunkDataClass = SchematicChunkData
|
||||
|
||||
def __init__(self, shape=None, filename=None, blocktypes='Alpha', readonly=False, resume=False):
|
||||
"""
|
||||
Creates an object which stores a section of a Minecraft world as an
|
||||
@ -156,8 +167,22 @@ class SchematicFileAdapter(FakeChunkedLevelAdapter):
|
||||
|
||||
self.rootTag["Data"].value &= 0xF # discard high bits
|
||||
|
||||
self.Entities = [self.EntityRef(tag, None) for tag in self.rootTag["Entities"]]
|
||||
self.TileEntities = [self.EntityRef(tag, None) for tag in self.rootTag["TileEntities"]]
|
||||
self.entitiesByChunk = defaultdict(list)
|
||||
for tag in self.rootTag["Entities"]:
|
||||
ref = self.EntityRef(tag)
|
||||
pos = ref.Position
|
||||
cx, cy, cz = pos.chunkPos()
|
||||
self.entitiesByChunk[cx, cz].append(tag)
|
||||
|
||||
self.tileEntitiesByChunk = defaultdict(list)
|
||||
for tag in self.rootTag["TileEntities"]:
|
||||
ref = self.TileEntityRef(tag)
|
||||
pos = ref.Position
|
||||
cx, cy, cz = pos.chunkPos()
|
||||
self.tileEntitiesByChunk[cx, cz].append(tag)
|
||||
|
||||
def fakeEntitiesForChunk(self, cx, cz):
|
||||
return self.entitiesByChunk[cx, cz], self.tileEntitiesByChunk[cx, cz]
|
||||
|
||||
def syncToDisk(self):
|
||||
"""
|
||||
@ -196,6 +221,24 @@ class SchematicFileAdapter(FakeChunkedLevelAdapter):
|
||||
packed_add = packed_add[0::2]
|
||||
self.rootTag["AddBlocks"] = nbt.TAG_Byte_Array(packed_add)
|
||||
|
||||
entities = []
|
||||
for e in self.entitiesByChunk.values():
|
||||
entities.extend(e)
|
||||
|
||||
tileEntities = []
|
||||
for te in self.tileEntitiesByChunk.values():
|
||||
tileEntities.extend(te)
|
||||
|
||||
self.rootTag["Entities"] = nbt.TAG_List(entities)
|
||||
self.rootTag["TileEntities"] = nbt.TAG_List(tileEntities)
|
||||
|
||||
log.info("Saving schematic %s with %d blocks, %d Entities and %d TileEntities",
|
||||
os.path.basename(filename),
|
||||
self.rootTag["Blocks"].value.size,
|
||||
len(self.rootTag["Entities"]),
|
||||
len(self.rootTag["TileEntities"]),
|
||||
)
|
||||
|
||||
with open(filename, 'wb') as chunkfh:
|
||||
self.rootTag.save(chunkfh)
|
||||
|
||||
|
Reference in New Issue
Block a user