Added: Add copyChunkFrom to MCInfdevOldLevel and MCRegionFile.
This function copies a single chunk from the same location in another world. This is much faster than using copyBlocksFrom because it doesn't have to adjust entity locations or even fully load the chunk if it's not necessary.
This commit is contained in:
parent
ec52850bd6
commit
03566f957f
@ -4,6 +4,7 @@ Created on Jul 22, 2011
|
||||
@author: Rio
|
||||
'''
|
||||
|
||||
import copy
|
||||
from datetime import datetime
|
||||
import itertools
|
||||
from logging import getLogger
|
||||
@ -971,13 +972,17 @@ class AnvilWorldFolder(object):
|
||||
def readChunk(self, cx, cz):
|
||||
if not self.containsChunk(cx, cz):
|
||||
raise ChunkNotPresent((cx, cz))
|
||||
|
||||
|
||||
return self.getRegionForChunk(cx, cz).readChunk(cx, cz)
|
||||
|
||||
def saveChunk(self, cx, cz, data):
|
||||
regionFile = self.getRegionForChunk(cx, cz)
|
||||
regionFile.saveChunk(cx, cz, data)
|
||||
|
||||
def copyChunkFrom(self, worldFolder, cx, cz):
|
||||
fromRF = worldFolder.getRegionForChunk(cx, cz)
|
||||
rf = self.getRegionForChunk(cx, cz)
|
||||
rf.copyChunkFrom(fromRF, cx, cz)
|
||||
|
||||
class MCInfdevOldLevel(ChunkedLevelMixin, EntityLevel):
|
||||
|
||||
@ -1329,6 +1334,45 @@ class MCInfdevOldLevel(ChunkedLevelMixin, EntityLevel):
|
||||
self.preloadChunkPositions()
|
||||
return self._allChunks.__iter__()
|
||||
|
||||
def copyChunkFrom(self, world, cx, cz):
|
||||
"""
|
||||
Copy a chunk from world into the same chunk position in self.
|
||||
"""
|
||||
assert isinstance(world, MCInfdevOldLevel)
|
||||
|
||||
destChunk = self._loadedChunks.get((cx, cz))
|
||||
sourceChunk = world._loadedChunks.get((cx, cz))
|
||||
|
||||
if sourceChunk:
|
||||
if destChunk:
|
||||
# Both chunks loaded. Use block copy.
|
||||
destChunk.copyBlocksFrom(sourceChunk, destChunk.bounds, destChunk.bounds.origin)
|
||||
return
|
||||
else:
|
||||
# Only source chunk loaded. Discard destination chunk and save source chunk in its place.
|
||||
self._loadedChunkData.pop((cx, cz), None)
|
||||
self.unsavedWorkFolder.saveChunk(cx, cz, sourceChunk.savedTagData())
|
||||
return
|
||||
else:
|
||||
if destChunk:
|
||||
# Only destination chunk loaded. Use block copy.
|
||||
destChunk.copyBlocksFrom(world.getChunk(cx, cz), destChunk.bounds, destChunk.bounds.origin)
|
||||
else:
|
||||
# Neither chunk loaded. Copy via world folders.
|
||||
self._loadedChunkData.pop((cx, cz), None)
|
||||
|
||||
# If the source chunk is dirty, write it to the work folder.
|
||||
chunkData = world._loadedChunkData.pop((cx, cz), None)
|
||||
if chunkData and chunkData.dirty:
|
||||
data = chunkData.savedTagData()
|
||||
world.unsavedWorkFolder.saveChunk(cx, cz, data)
|
||||
|
||||
if world.unsavedWorkFolder.containsChunk(cx, cz):
|
||||
sourceFolder = world.unsavedWorkFolder
|
||||
else:
|
||||
sourceFolder = world.worldFolder
|
||||
|
||||
self.unsavedWorkFolder.copyChunkFrom(sourceFolder, cx, cz)
|
||||
|
||||
def _getChunkBytes(self, cx, cz):
|
||||
try:
|
||||
|
@ -167,7 +167,7 @@ class MCRegionFile(object):
|
||||
log.info("Repair complete. Removed {0} chunks, recovered {1} chunks, net {2}".format(deleted, recovered, recovered - deleted))
|
||||
|
||||
|
||||
def readChunk(self, cx, cz):
|
||||
def _readChunk(self, cx, cz):
|
||||
cx &= 0x1f
|
||||
cz &= 0x1f
|
||||
offset = self.getOffset(cx, cz)
|
||||
@ -191,7 +191,10 @@ class MCRegionFile(object):
|
||||
length = struct.unpack_from(">I", data)[0]
|
||||
format = struct.unpack_from("B", data, 4)[0]
|
||||
data = data[5:length + 5]
|
||||
return data, format
|
||||
|
||||
def readChunk(self, cx, cz):
|
||||
data, format = self._readChunk(cx, cz)
|
||||
if format == self.VERSION_GZIP:
|
||||
return nbt.gunzip(data)
|
||||
if format == self.VERSION_DEFLATE:
|
||||
@ -199,14 +202,28 @@ class MCRegionFile(object):
|
||||
|
||||
raise IOError("Unknown compress format: {0}".format(format))
|
||||
|
||||
def copyChunkFrom(self, regionFile, cx, cz):
|
||||
"""
|
||||
Silently fails if regionFile does not contain the requested chunk.
|
||||
"""
|
||||
try:
|
||||
data, format = regionFile._readChunk(cx, cz)
|
||||
self._saveChunk(cx, cz, data, format)
|
||||
except ChunkNotPresent:
|
||||
pass
|
||||
|
||||
def saveChunk(self, cx, cz, uncompressedData):
|
||||
data = deflate(uncompressedData)
|
||||
self._saveChunk(cx, cz, data, self.VERSION_DEFLATE)
|
||||
|
||||
def _saveChunk(self, cx, cz, data, format):
|
||||
cx &= 0x1f
|
||||
cz &= 0x1f
|
||||
offset = self.getOffset(cx, cz)
|
||||
sectorNumber = offset >> 8
|
||||
sectorsAllocated = offset & 0xff
|
||||
format = self.VERSION_DEFLATE
|
||||
data = deflate(uncompressedData)
|
||||
|
||||
|
||||
|
||||
sectorsNeeded = (len(data) + self.CHUNK_HEADER_SIZE) / self.SECTOR_BYTES + 1
|
||||
if sectorsNeeded >= 256:
|
||||
|
@ -45,6 +45,16 @@ class TestAnvilLevel(unittest.TestCase):
|
||||
level.deleteChunk(*ch)
|
||||
level.createChunksInBox(BoundingBox((0, 0, 0), (32, 0, 32)))
|
||||
|
||||
def testCopyChunks(self):
|
||||
level = self.anvilLevel.level
|
||||
temppath = mktemp("AnvilCreate")
|
||||
newLevel = MCInfdevOldLevel(filename=temppath, create=True)
|
||||
for cx, cz in level.allChunks:
|
||||
newLevel.copyChunkFrom(level, cx, cz)
|
||||
|
||||
newLevel.close()
|
||||
shutil.rmtree(temppath)
|
||||
|
||||
def testCopyConvertBlocks(self):
|
||||
indevlevel = self.indevLevel.level
|
||||
level = self.anvilLevel.level
|
||||
|
Reference in New Issue
Block a user