From e8870e76e4bfa6cfceb8d69e91facc3fad71b53d Mon Sep 17 00:00:00 2001 From: David Vierra Date: Sat, 13 Aug 2011 09:57:59 -1000 Subject: [PATCH] add iterator-type counterparts to extractSchematic, extractZipSchematic and copyBlocksFrom to facilitate progress bars. iterators repeatedly yield a tuple of (progress, total). extract methods finally yield the extracted schematic object. --- infiniteworld.py | 62 +++++++++++++++++++++++++++++++++++------------- level.py | 35 +++++++++++++++++++++------ schematic.py | 29 ++++++++++++++++------ 3 files changed, 96 insertions(+), 30 deletions(-) diff --git a/infiniteworld.py b/infiniteworld.py index a193d40..860801f 100644 --- a/infiniteworld.py +++ b/infiniteworld.py @@ -1576,6 +1576,10 @@ class MCInfdevOldLevel(MCLevel): info(u"Saved {0} chunks".format(dirtyChunkCount)) def generateLights(self, dirtyChunks=None): + for i in generateLightsIter(self, dirtyChunks): + pass + + def generateLightsIter(self, dirtyChunks=None): """ dirtyChunks may be an iterable yielding (xPos,zPos) tuples if none, generate lights for all chunks that need lighting """ @@ -1629,13 +1633,16 @@ class MCInfdevOldLevel(MCLevel): info(u"Using {0} batches to conserve memory.".format(len(chunkLists))) i = 0 + for dc in chunkLists: i += 1; info(u"Batch {0}/{1}".format(i, len(chunkLists))) dc = sorted(dc, key=lambda x:x.chunkPosition) - self._generateLights(dc) + for j in self._generateLightsIter(dc): + yield j + for ch in dc: ch.compress(); timeDelta = datetime.now() - startTime; @@ -1645,7 +1652,7 @@ class MCInfdevOldLevel(MCLevel): return; - def _generateLights(self, dirtyChunks): + def _generateLightsIter(self, dirtyChunks): conserveMemory = False la = array(self.materials.lightAbsorption) @@ -1692,6 +1699,8 @@ class MCInfdevOldLevel(MCLevel): else: lights = ("BlockLight", "SkyLight") info(u"Dispersing light...") + j = 0 + for light in lights: zerochunkLight = getattr(zeroChunk, light); @@ -1721,6 +1730,8 @@ class MCInfdevOldLevel(MCLevel): for chunk in dirtyChunks: #xxx code duplication + yield (j, len(dirtyChunks)) + j += 1 (cx, cz) = chunk.chunkPosition neighboringChunks = {}; try: @@ -2030,7 +2041,7 @@ class MCInfdevOldLevel(MCLevel): - def copyBlocksFromFinite(self, sourceLevel, sourceBox, destinationPoint, blocksToCopy): + def copyBlocksFromFiniteIter(self, sourceLevel, sourceBox, destinationPoint, blocksToCopy): #assumes destination point and bounds have already been checked. (sx, sy, sz) = sourceBox.origin @@ -2043,11 +2054,15 @@ class MCInfdevOldLevel(MCLevel): typemask[blocksToCopy] = 1; - destChunks = self.getChunkSlices(BoundingBox(destinationPoint, sourceBox.size)) + destBox = BoundingBox(destinationPoint, sourceBox.size) + destChunks = self.getChunkSlices(destBox) i = 0; + chunkCount = float(destBox.chunkCount) for (chunk, slices, point) in destChunks: i += 1; + yield (i, chunkCount) + if i % 100 == 0: info("Chunk {0}...".format(i)) @@ -2098,19 +2113,21 @@ class MCInfdevOldLevel(MCLevel): #chunk.compress(); #xxx find out why this trashes changes to tile entities - def copyBlocksFromInfinite(self, sourceLevel, sourceBox, destinationPoint, blocksToCopy): + def copyBlocksFromInfiniteIter(self, sourceLevel, sourceBox, destinationPoint, blocksToCopy): """ copy blocks between two infinite levels via repeated export/import. hilariously slow. """ #assumes destination point and bounds have already been checked. tempSize = 128 + dx, dy, dz = destinationPoint + ox, oy, oz = sourceBox.origin + sx, sy, sz = sourceBox.size + mx, my, mz = sourceBox.maximum + def subsectionCount(): + return (ox, ox + sx, tempSize) * (oz, oz + sz, tempSize) def iterateSubsections(): #tempShape = (tempSize, sourceBox.height, tempSize) - dx, dy, dz = destinationPoint - ox, oy, oz = sourceBox.origin - sx, sy, sz = sourceBox.size - mx, my, mz = sourceBox.maximum for x, z in itertools.product(arange(ox, ox + sx, tempSize), arange(oz, oz + sz, tempSize)): box = BoundingBox((x, oy, z), (min(tempSize, mx - x), sy, min(tempSize, mz - z))) destPoint = (dx + x - ox, dy, dz + z - oz) @@ -2121,9 +2138,13 @@ class MCInfdevOldLevel(MCLevel): def isChunkBox(box): return box.isChunkAligned and box.miny == 0 and box.height == sourceLevel.Height + i = 0 + print sourceBox.chunkCount + chunkCount = float(sourceBox.chunkCount) if isChunkBox(sourceBox) and isChunkBox(destBox): print "Copying with chunk alignment!" + cxoffset = destBox.mincx - sourceBox.mincx czoffset = destBox.mincz - sourceBox.mincz @@ -2132,9 +2153,9 @@ class MCInfdevOldLevel(MCLevel): typemask[blocksToCopy] = True changedChunks = deque(); - i = 0; for cx, cz in sourceBox.chunkPositions: - i += 1; + i += 1 + yield (i, chunkCount) if i % 100 == 0: info("Chunk {0}...".format(i)) @@ -2186,15 +2207,21 @@ class MCInfdevOldLevel(MCLevel): ch.needsLighting = True else: - i = 0; + chunkCount = subsectionCount() for box, destPoint in iterateSubsections(): info("Subsection {0} at {1}".format(i, destPoint)) temp = sourceLevel.extractSchematic(box); self.copyBlocksFrom(temp, BoundingBox((0, 0, 0), box.size), destPoint, blocksToCopy); - i += 1; + i += 1 + yield (i, chunkCount) + def copyBlocksFrom(self, sourceLevel, sourceBox, destinationPoint, blocksToCopy=None, entities=True): + for i in self.copyBlocksFromIter(sourceLevel, sourceBox, destinationPoint, blocksToCopy, entities): + pass + + def copyBlocksFromIter(self, sourceLevel, sourceBox, destinationPoint, blocksToCopy=None, entities=True): (x, y, z) = destinationPoint; (lx, ly, lz) = sourceBox.size #sourcePoint, sourcePoint1 = sourceBox @@ -2205,13 +2232,16 @@ class MCInfdevOldLevel(MCLevel): startTime = datetime.now() if(not isinstance(sourceLevel, MCInfdevOldLevel)): - self.copyBlocksFromFinite(sourceLevel, sourceBox, destinationPoint, blocksToCopy) + for i in self.copyBlocksFromFiniteIter(sourceLevel, sourceBox, destinationPoint, blocksToCopy): + yield i else: - self.copyBlocksFromInfinite(sourceLevel, sourceBox, destinationPoint, blocksToCopy) + for i in self.copyBlocksFromInfiniteIter(sourceLevel, sourceBox, destinationPoint, blocksToCopy): + yield i - self.copyEntitiesFrom(sourceLevel, sourceBox, destinationPoint, entities) + for i in self.copyEntitiesFromIter(sourceLevel, sourceBox, destinationPoint, entities): + yield i info("Duration: {0}".format(datetime.now() - startTime)) #self.saveInPlace() diff --git a/level.py b/level.py index 75db813..c313176 100644 --- a/level.py +++ b/level.py @@ -526,7 +526,12 @@ class MCLevel(object): return sourceBox, destinationPoint + def copyBlocksFrom(self, sourceLevel, sourceBox, destinationPoint, blocksToCopy=None, entities=True): + for i in self.copyBlocksFromIter(sourceLevel, sourceBox, destinationPoint, blocksToCopy, entities): + pass + + def copyBlocksFromIter(self, sourceLevel, sourceBox, destinationPoint, blocksToCopy=None, entities=True): if (not sourceLevel.isInfinite) and not( sourceLevel.containsPoint(*sourceBox.origin) and sourceLevel.containsPoint(*map(lambda x:x - 1, sourceBox.maximum))): @@ -546,11 +551,12 @@ class MCLevel(object): else: self.copyBlocksFromInfinite(sourceLevel, sourceBox, destinationPoint, blocksToCopy) - self.copyEntitiesFrom(sourceLevel, sourceBox, destinationPoint, entities) + for i in self.copyEntitiesFromIter(sourceLevel, sourceBox, destinationPoint, entities): + yield i def saveInPlace(self): self.saveToFile(self.filename); - @classmethod + def setPlayerPosition(self, pos, player="Player"): pass; @@ -574,9 +580,10 @@ class MCLevel(object): return (-45., 0.) - def copyEntitiesFromInfinite(self, sourceLevel, sourceBox, destinationPoint): + def copyEntitiesFromInfiniteIter(self, sourceLevel, sourceBox, destinationPoint): chunkIterator = sourceLevel.getChunkSlices(sourceBox); - + chunkCount = sourceBox.chunkCount + i = 0 for (chunk, slices, point) in chunkIterator: #remember, slices are ordered x,z,y so you can subscript them like so: chunk.Blocks[slices] cx, cz = chunk.chunkPosition @@ -602,16 +609,22 @@ class MCLevel(object): self.addTileEntity(eTag) chunk.compress(); - + yield float(i) / float(chunkCount) + i += 1 def copyEntitiesFrom(self, sourceLevel, sourceBox, destinationPoint, entities=True): + for i in self.copyEntitiesFromIter(sourceLevel, sourceBox, destinationPoint, entities): + pass + + def copyEntitiesFromIter(self, sourceLevel, sourceBox, destinationPoint, entities=True): #assume coords have already been adjusted by copyBlocks if not self.hasEntities or not sourceLevel.hasEntities: return; sourcePoint0 = sourceBox.origin; sourcePoint1 = sourceBox.maximum; if sourceLevel.isInfinite: - self.copyEntitiesFromInfinite(sourceLevel, sourceBox, destinationPoint) + for i in self.copyEntitiesFromInfiniteIter(sourceLevel, sourceBox, destinationPoint): + yield i else: entsCopied = 0; tileEntsCopied = 0; @@ -623,8 +636,12 @@ class MCLevel(object): self.addEntity(eTag) entsCopied += 1; - + i = 0 for entity in getTileEntitiesInRange(sourceBox, sourceLevel.TileEntities): + i += 1 + if i % 100 == 0: + yield + if not 'x' in entity: continue eTag = TileEntity.copyWithOffset(entity, copyOffset) @@ -633,6 +650,8 @@ class MCLevel(object): tileEntsCopied += 1; except ChunkNotPresent: pass + + yield debug(u"Copied {0} entities, {1} tile entities".format(entsCopied, tileEntsCopied)) @@ -671,6 +690,8 @@ class MCLevel(object): def generateLights(self, dirtyChunks=None): pass; + def generateLightsIter(self, dirtyChunks=None): + yield 0 def adjustExtractionParameters(self, box): x, y, z = box.origin diff --git a/schematic.py b/schematic.py index 88f5b11..580f65c 100644 --- a/schematic.py +++ b/schematic.py @@ -459,20 +459,32 @@ class INVEditChest(MCSchematic): def extractSchematicFrom(sourceLevel, box, entities=True): + for i in extractSchematicFromIter(sourceLevel, box, entities): + pass + return i + +def extractSchematicFromIter(sourceLevel, box, entities=True): p = sourceLevel.adjustExtractionParameters(box); if p is None: return newbox, destPoint = p tempSchematic = MCSchematic(shape=box.size) tempSchematic.materials = sourceLevel.materials - tempSchematic.copyBlocksFrom(sourceLevel, newbox, destPoint, entities=entities) + for i in tempSchematic.copyBlocksFromIter(sourceLevel, newbox, destPoint, entities=entities): + yield i - return tempSchematic + yield tempSchematic MCLevel.extractSchematic = extractSchematicFrom +MCLevel.extractSchematicIter = extractSchematicFromIter import tempfile def extractZipSchematicFrom(sourceLevel, box, zipfilename=None, entities=True): + for i in extractZipSchematicFromIter(sourceLevel, box, zipfilename, entities): + pass + return i + +def extractZipSchematicFromIter(sourceLevel, box, zipfilename=None, entities=True): #converts classic blocks to alpha #probably should only apply to alpha levels @@ -498,13 +510,15 @@ def extractZipSchematicFrom(sourceLevel, box, zipfilename=None, entities=True): destChunks = destBox.chunkPositions chunkIter = itertools.izip(chunks, destChunks) - chunks = (x[1] for x in chunkIter if sourceLevel.containsChunk(*x[0])) - tempSchematic.createChunks(chunks) - + for i, (src, chunk) in enumerate(chunkIter): + if sourceLevel.containsChunk(*src): + tempSchematic.createChunk(*chunk) + yield i, sourceBox.chunkCount else: tempSchematic.createChunksInBox(destBox) - tempSchematic.copyBlocksFrom(sourceLevel, sourceBox, destPoint, entities=entities) + for i in tempSchematic.copyBlocksFromIter(sourceLevel, sourceBox, destPoint, entities=entities): + yield i tempSchematic.saveInPlace(); #lights not needed for this format - crashes minecraft though schematicDat = TAG_Compound() @@ -520,9 +534,10 @@ def extractZipSchematicFrom(sourceLevel, box, zipfilename=None, entities=True): import shutil shutil.rmtree(filename) import mclevel - return mclevel.fromFile(zipfilename) + yield mclevel.fromFile(zipfilename) MCLevel.extractZipSchematic = extractZipSchematicFrom +MCLevel.extractZipSchematicIter = extractZipSchematicFromIter from zipfile import ZipFile, ZIP_STORED def zipdir(basedir, archivename):