Permalink
Browse files

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.
  • Loading branch information...
1 parent ec52850 commit 03566f957f0352761807ba9cfee7de7ea491d01b @codewarrior0 codewarrior0 committed Nov 13, 2012
Showing with 75 additions and 4 deletions.
  1. +45 −1 infiniteworld.py
  2. +20 −3 regionfile.py
  3. +10 −0 test/anvil_test.py
View
@@ -4,6 +4,7 @@
@author: Rio
'''
+import copy
from datetime import datetime
import itertools
from logging import getLogger
@@ -971,13 +972,17 @@ def deleteChunk(self, cx, cz):
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 @@ def allChunks(self):
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:
View
@@ -167,7 +167,7 @@ def repair(self):
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,22 +191,39 @@ def readChunk(self, cx, cz):
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:
return inflate(data)
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:
View
@@ -45,6 +45,16 @@ def testCreateChunks(self):
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

0 comments on commit 03566f9

Please sign in to comment.