152 lines
5.8 KiB
Python
152 lines
5.8 KiB
Python
from datetime import datetime
|
|
import logging
|
|
log = logging.getLogger(__name__)
|
|
|
|
import numpy
|
|
from box import BoundingBox, Vector
|
|
from mclevelbase import exhaust
|
|
import materials
|
|
from entity import Entity, TileEntity
|
|
|
|
|
|
def convertBlocks(destLevel, sourceLevel, blocks, blockData):
|
|
return materials.convertBlocks(destLevel.materials, sourceLevel.materials, blocks, blockData)
|
|
|
|
def sourceMaskFunc(blocksToCopy):
|
|
if blocksToCopy is not None:
|
|
typemask = numpy.zeros(materials.id_limit, dtype='bool')
|
|
typemask[blocksToCopy] = 1
|
|
|
|
def maskedSourceMask(sourceBlocks):
|
|
return typemask[sourceBlocks]
|
|
|
|
return maskedSourceMask
|
|
|
|
def unmaskedSourceMask(_sourceBlocks):
|
|
return slice(None, None)
|
|
|
|
return unmaskedSourceMask
|
|
|
|
|
|
def adjustCopyParameters(destLevel, sourceLevel, sourceBox, destinationPoint):
|
|
# if the destination box is outside the level, it and the source corners are moved inward to fit.
|
|
(dx, dy, dz) = map(int, destinationPoint)
|
|
|
|
log.debug(u"Asked to copy {} blocks \n\tfrom {} in {}\n\tto {} in {}" .format(
|
|
sourceBox.volume, sourceBox, sourceLevel, destinationPoint, destLevel))
|
|
if destLevel.Width == 0:
|
|
return sourceBox, destinationPoint
|
|
|
|
destBox = BoundingBox(destinationPoint, sourceBox.size)
|
|
actualDestBox = destBox.intersect(destLevel.bounds)
|
|
|
|
actualSourceBox = BoundingBox(sourceBox.origin + actualDestBox.origin - destBox.origin, destBox.size)
|
|
actualDestPoint = actualDestBox.origin
|
|
|
|
return actualSourceBox, actualDestPoint
|
|
|
|
|
|
|
|
def copyBlocksFromIter(destLevel, sourceLevel, sourceBox, destinationPoint, blocksToCopy=None, entities=True, create=False, biomes=False):
|
|
""" copy blocks between two infinite levels by looping through the
|
|
destination's chunks. make a sub-box of the source level for each chunk
|
|
and copy block and entities in the sub box to the dest chunk."""
|
|
|
|
(lx, ly, lz) = sourceBox.size
|
|
|
|
sourceBox, destinationPoint = adjustCopyParameters(destLevel, sourceLevel, sourceBox, destinationPoint)
|
|
# needs work xxx
|
|
log.info(u"Copying {0} blocks from {1} to {2}" .format(ly * lz * lx, sourceBox, destinationPoint))
|
|
startTime = datetime.now()
|
|
|
|
destBox = BoundingBox(destinationPoint, sourceBox.size)
|
|
chunkCount = destBox.chunkCount
|
|
i = 0
|
|
e = 0
|
|
t = 0
|
|
|
|
sourceMask = sourceMaskFunc(blocksToCopy)
|
|
|
|
copyOffset = [d - s for s, d in zip(sourceBox.origin, destinationPoint)]
|
|
|
|
# Visit each chunk in the destination area.
|
|
# Get the region of the source area corresponding to that chunk
|
|
# Visit each chunk of the region of the source area
|
|
# Get the slices of the destination chunk
|
|
# Get the slices of the source chunk
|
|
# Copy blocks and data
|
|
|
|
for destCpos in destBox.chunkPositions:
|
|
cx, cz = destCpos
|
|
|
|
destChunkBox = BoundingBox((cx << 4, 0, cz << 4), (16, destLevel.Height, 16)).intersect(destBox)
|
|
destChunkBoxInSourceLevel = BoundingBox([d - o for o, d in zip(copyOffset, destChunkBox.origin)], destChunkBox.size)
|
|
|
|
if not destLevel.containsChunk(*destCpos):
|
|
if create and any(sourceLevel.containsChunk(*c) for c in destChunkBoxInSourceLevel.chunkPositions):
|
|
# Only create chunks in the destination level if the source level has chunks covering them.
|
|
destLevel.createChunk(*destCpos)
|
|
else:
|
|
continue
|
|
|
|
destChunk = destLevel.getChunk(*destCpos)
|
|
|
|
|
|
i += 1
|
|
yield (i, chunkCount)
|
|
if i % 100 == 0:
|
|
log.info("Chunk {0}...".format(i))
|
|
|
|
for srcCpos in destChunkBoxInSourceLevel.chunkPositions:
|
|
if not sourceLevel.containsChunk(*srcCpos):
|
|
continue
|
|
|
|
sourceChunk = sourceLevel.getChunk(*srcCpos)
|
|
|
|
sourceChunkBox, sourceSlices = sourceChunk.getChunkSlicesForBox(destChunkBoxInSourceLevel)
|
|
if sourceChunkBox.volume == 0:
|
|
continue
|
|
|
|
sourceChunkBoxInDestLevel = BoundingBox([d + o for o, d in zip(copyOffset, sourceChunkBox.origin)], sourceChunkBox.size)
|
|
|
|
_, destSlices = destChunk.getChunkSlicesForBox(sourceChunkBoxInDestLevel)
|
|
|
|
sourceBlocks = sourceChunk.Blocks[sourceSlices]
|
|
sourceData = sourceChunk.Data[sourceSlices]
|
|
|
|
mask = sourceMask(sourceBlocks)
|
|
convertedSourceBlocks, convertedSourceData = convertBlocks(destLevel, sourceLevel, sourceBlocks, sourceData)
|
|
|
|
destChunk.Blocks[destSlices][mask] = convertedSourceBlocks[mask]
|
|
if convertedSourceData is not None:
|
|
destChunk.Data[destSlices][mask] = convertedSourceData[mask]
|
|
|
|
if entities:
|
|
ents = sourceChunk.getEntitiesInBox(destChunkBoxInSourceLevel)
|
|
e += len(ents)
|
|
for entityTag in ents:
|
|
eTag = Entity.copyWithOffset(entityTag, copyOffset)
|
|
destLevel.addEntity(eTag)
|
|
|
|
tileEntities = sourceChunk.getTileEntitiesInBox(destChunkBoxInSourceLevel)
|
|
t += len(tileEntities)
|
|
for tileEntityTag in tileEntities:
|
|
eTag = TileEntity.copyWithOffset(tileEntityTag, copyOffset)
|
|
destLevel.addTileEntity(eTag)
|
|
|
|
if biomes and hasattr(destChunk, 'Biomes') and hasattr(sourceChunk, 'Biomes'):
|
|
destChunk.Biomes[destSlices[:2]] = sourceChunk.Biomes[sourceSlices[:2]]
|
|
|
|
destChunk.chunkChanged()
|
|
|
|
log.info("Duration: {0}".format(datetime.now() - startTime))
|
|
log.info("Copied {0} entities and {1} tile entities".format(e, t))
|
|
|
|
def copyBlocksFrom(destLevel, sourceLevel, sourceBox, destinationPoint, blocksToCopy=None, entities=True, create=False, biomes=False):
|
|
return exhaust(copyBlocksFromIter(destLevel, sourceLevel, sourceBox, destinationPoint, blocksToCopy, entities, create, biomes))
|
|
|
|
|
|
|
|
|
|
|