Example #1
0
def copyToFolderIter(destFolder, sourceNode, presaveNode=None):
    # Progress counts:
    # 0-10:   deleted chunks
    # 10-80:  new/modified chunks
    # 80-90:  deleted files
    # 90-100: new/modified files

    if presaveNode:
        presaveFolder = presaveNode.worldFolder
    else:
        presaveFolder = None

    sourceFolder = sourceNode.worldFolder

    maxprogress = 100

    # Remove deleted chunks
    for deadProgress, (cx, cz, dimName) in enumProgress(sourceNode.deadChunks, 0, 10):
        yield deadProgress, maxprogress, "Removing deleted chunks"

        if destFolder.containsChunk(cx, cz, dimName):
            if presaveFolder and not presaveFolder.containsChunk(cx, cz, dimName):
                presaveFolder.writeChunkBytes(cx, cz, dimName, destFolder.readChunkBytes(cx, cz, dimName))
            destFolder.deleteChunk(cx, cz, dimName)

    # Write new and modified chunks
    dims = list(sourceFolder.listDimensions())
    dimProgress = 70 / len(dims)

    for i, dimName in enumerate(dims):
        progress = 10 + i * dimProgress

        cPos = list(sourceFolder.chunkPositions(dimName))
        for chunkProgress, (cx, cz) in enumProgress(cPos, progress, progress + dimProgress):
            yield chunkProgress, maxprogress, "Writing new and modified chunks"

            if presaveFolder and not presaveFolder.containsChunk(cx, cz, dimName):
                if destFolder.containsChunk(cx, cz, dimName):
                    presaveFolder.writeChunkBytes(cx, cz, dimName, destFolder.readChunkBytes(cx, cz, dimName))
                else:  # new chunk
                    presaveNode.deleteChunk(cx, cz, dimName)
            destFolder.writeChunkBytes(cx, cz, dimName, sourceFolder.readChunkBytes(cx, cz, dimName))

    # Remove deleted files
    for delProgress, path in enumProgress(sourceNode.deadFiles, 80, 10):
        yield delProgress, maxprogress, "Removing deleted files"

        if destFolder.containsFile(path):
            if presaveFolder and not presaveFolder.containsFile(path):
                presaveFolder.writeFile(path, destFolder.readFile(path))
            destFolder.deleteFile(path)

    # Write new and modified files

    files = list(sourceFolder.listAllFiles())
    for delProgress, path in enumProgress(files, 90, 10):
        yield delProgress, maxprogress, "Writing new and modified files"

        if presaveFolder and not presaveFolder.containsFile(path):
            if destFolder.containsFile(path):
                presaveFolder.writeFile(path, destFolder.readFile(path))
            else:  # new file
                presaveNode.deleteFile(path)
        destFolder.writeFile(path, sourceFolder.readFile(path))

    yield maxprogress, maxprogress, "Done"
Example #2
0
    def writeAllChangesIter(self, requestedRevision=None):
        """
        Write all changes to the root world folder, preserving undo history. The world folder
        becomes the new head node. The previous head node is no longer valid after calling
        writeAllChanges. Specify a revision to only save changes up to and including that
        revision.

        :return:
        :rtype:
        """
        # XXXXX wait for async writes to complete here
        # Progress counts:
        # 0-20:   Orphaned chains
        # 20-100: History nodes

        maxprogress = 100

        if isinstance(requestedRevision, RevisionHistoryNode):
            requestedIndex = self.nodes.index(requestedRevision)
        elif requestedRevision is None:
            requestedIndex = len(self.nodes) - 1
        else:
            requestedIndex = requestedRevision

        orphanChainProgress = 20
        if self.orphanChainIndex is not None:
            # Root node is orphaned - collapse orphan chain into it in reverse order
            orphanNodes = []
            orphanChainNode = self.nodes[self.orphanChainIndex]
            while orphanChainNode is not self.rootNode:
                orphanNodes.append(orphanChainNode)
                orphanChainNode = orphanChainNode.parentNode

            for progress, orphanChainNode in enumProgress(reversed(orphanNodes), 0, 20):
                yield (progress, maxprogress, "Collapsing orphaned chain")

                copyTask = copyToFolderIter(self.rootFolder, orphanChainNode)
                copyTask = rescaleProgress(copyTask, progress, 20/len(orphanNodes))
                for current, _, status in copyTask:
                    yield current, maxprogress, status

            self.nodes[self.orphanChainIndex] = self.rootNode
            self.orphanChainIndex = None

        if requestedIndex == self.rootNodeIndex:
            return  # nothing to do
        elif requestedIndex < self.rootNodeIndex:
            direction = -1
            indexes = xrange(self.rootNodeIndex-1, requestedIndex-1, -1)
        else:
            direction = 1
            indexes = xrange(self.rootNodeIndex+1, requestedIndex+1)
        log.info("writeAllChanges: moving %s", "forwards" if direction == 1 else "backwards")

        for progress, currentIndex in enumProgress(indexes, 20, 80):
            # Write all changes from each node into the initial folder. Save the previous
            # chunk and file data from the initial folder into a reverse revision.

            currentNode = self.nodes[currentIndex]

            reverseFolder = self._createRevisionFolder()
            reverseNode = RevisionHistoryNode(self, reverseFolder, self.nodes[currentIndex - direction])

            reverseNode.differences = self.rootNode.differences
            self.rootNode.differences = currentNode.getChanges()

            copyTask = copyToFolderIter(self.rootFolder, currentNode, reverseNode)
            copyTask = rescaleProgress(copyTask, progress, 80 / len(indexes))
            for current, _, status in copyTask:
                yield current, maxprogress, status

            # xxx look ahead one or more nodes to skip some copies

            reverseNode.setRevisionInfo(self.rootNode.getRevisionInfo())
            reverseNode.readonly = True
            self.rootNode.setRevisionInfo(currentNode.getRevisionInfo())

            # Replace the previousNode with the reverse node, and the currentNode with the rootNode
            self.nodes[currentIndex - direction] = reverseNode
            self.nodes[currentIndex] = self.rootNode
            self.rootNodeIndex = currentIndex

            log.info("Root node now at index %d", currentIndex)

            assert currentNode is not self.rootNode, "Root node appears twice in nodes!"
            currentNode.worldFolder.close()
            currentNode.invalid = True
            shutil.rmtree(currentNode.worldFolder.filename, ignore_errors=True)
Example #3
0
def copyToFolderIter(destFolder, sourceNode, reversionNode=None):
    # Progress counts:
    # 0-10:   deleted chunks
    # 10-80:  new/modified chunks
    # 80-90:  deleted files
    # 90-100: new/modified files

    if reversionNode:
        reversionFolder = reversionNode.worldFolder
    else:
        reversionFolder = None

    sourceFolder = sourceNode.worldFolder

    maxprogress = 100

    # Remove deleted chunks
    for deadProgress, (cx, cz,
                       dimName) in enumProgress(sourceNode.deadChunks, 0, 10):
        yield deadProgress, maxprogress, "Removing deleted chunks"

        if destFolder.containsChunk(cx, cz, dimName):
            if reversionFolder and not reversionFolder.containsChunk(
                    cx, cz, dimName):
                reversionFolder.writeChunkBytes(
                    cx, cz, dimName,
                    destFolder.readChunkBytes(cx, cz, dimName))
            destFolder.deleteChunk(cx, cz, dimName)

    # Write new and modified chunks
    dims = list(sourceFolder.listDimensions())
    dimProgress = 70. / len(dims)

    for i, dimName in enumerate(dims):
        progress = 10 + i * dimProgress

        cPos = list(sourceFolder.chunkPositions(dimName))
        for chunkProgress, (cx, cz) in enumProgress(cPos, progress,
                                                    progress + dimProgress):
            yield chunkProgress, maxprogress, "Writing new and modified chunks"

            if reversionFolder and not reversionFolder.containsChunk(
                    cx, cz, dimName):
                if destFolder.containsChunk(cx, cz, dimName):
                    reversionFolder.writeChunkBytes(
                        cx, cz, dimName,
                        destFolder.readChunkBytes(cx, cz, dimName))
                else:  # new chunk
                    reversionNode.deleteChunk(cx, cz, dimName)
            destFolder.writeChunkBytes(
                cx, cz, dimName, sourceFolder.readChunkBytes(cx, cz, dimName))

    # Remove deleted files
    for delProgress, path in enumProgress(sourceNode.deadFiles, 80, 10):
        yield delProgress, maxprogress, "Removing deleted files"

        if destFolder.containsFile(path):
            if reversionFolder and not reversionFolder.containsFile(path):
                reversionFolder.writeFile(path, destFolder.readFile(path))
            destFolder.deleteFile(path)

    # Write new and modified files

    files = list(sourceFolder.listAllFiles())
    for delProgress, path in enumProgress(files, 90, 10):
        yield delProgress, maxprogress, "Writing new and modified files"

        if reversionFolder and not reversionFolder.containsFile(path):
            if destFolder.containsFile(path):
                reversionFolder.writeFile(path, destFolder.readFile(path))
            else:  # new file
                reversionNode.deleteFile(path)
        destFolder.writeFile(path, sourceFolder.readFile(path))

    yield maxprogress, maxprogress, "Done"
Example #4
0
    def writeAllChangesIter(self, requestedRevision=None):
        """
        Write all changes to the root world folder, preserving undo history.
        
        If a revision is requested, the state of the world at that revision will be
        written, otherwise, the last node in the history is used.
        
        All nodes between the root node and the requested node, inclusive, are replaced
        with new nodes. The old nodes are no longer valid. The root node will be placed at
        the position in the nodes list previously occupied by the requested node.

        Parameters
        ----------
        
        requestedRevision: RevisionHistoryNode | int | None
            If given, this specifies the revision to write to the world folder, otherwise
            the most recent revision is written.
        
        Returns
        -------
        
        progress: Iterator[(current, max, status)]
            Progress information for the write-changes task.
            
        """
        # XXXXX wait for async writes to complete here
        # Progress counts:
        # 0-20:   Orphaned chains
        # 20-100: History nodes

        maxprogress = 100

        if isinstance(requestedRevision, RevisionHistoryNode):
            requestedIndex = self.nodes.index(requestedRevision)
        elif requestedRevision is None:
            requestedIndex = len(self.nodes) - 1
        else:
            requestedIndex = requestedRevision

        if self.orphanChainIndex is not None:
            # Root node is orphaned - collapse orphan chain into it in reverse order
            orphanNodes = []
            orphanChainNode = self.nodes[self.orphanChainIndex]
            while orphanChainNode is not self.rootNode:
                orphanNodes.append(orphanChainNode)
                orphanChainNode = orphanChainNode.parentNode

            # Apply each orphaned node onto the root node.
            for progress, orphanChainNode in enumProgress(
                    orphanNodes[::-1], 0, 20):
                yield (progress, maxprogress, "Collapsing orphaned chain")

                copyTask = copyToFolderIter(self.rootFolder, orphanChainNode)
                copyTask = rescaleProgress(copyTask, progress,
                                           progress + 20. / len(orphanNodes))
                for current, _, status in copyTask:
                    yield current, maxprogress, status

            # Root node now replaces the orphan chain's tail in the history.
            # (the nodes ahead and behind of the root node should now point to this node)
            self.nodes[self.orphanChainIndex] = self.rootNode
            self.orphanChainIndex = None

        if requestedIndex == self.rootNodeIndex:
            return  # nothing to do
        elif requestedIndex < self.rootNodeIndex:
            # Nodes behind the root node in the history will be re-reverted and replaced
            # with plain nodes
            direction = -1
            indexes = xrange(self.rootNodeIndex - 1, requestedIndex - 1, -1)
        else:
            # Nodes ahead of the root node will be reverted and replaced with
            # "reversion" nodes.
            direction = 1
            indexes = xrange(self.rootNodeIndex + 1, requestedIndex + 1)

        log.info("writeAllChanges: moving %s",
                 "forwards" if direction == 1 else "backwards")

        for progress, currentIndex in enumProgress(indexes, 20, 100):
            # Write all changes from each node into the initial folder. Save the previous
            # chunk and file data from the initial folder into a reverse revision.

            currentNode = self.nodes[currentIndex]

            reverseFolder = self._createRevisionFolder()
            reverseNode = RevisionHistoryNode(
                self, reverseFolder, self.nodes[currentIndex - direction])

            reverseNode.differences = self.rootNode.differences
            self.rootNode.differences = currentNode.getChanges()

            copyTask = copyToFolderIter(self.rootFolder, currentNode,
                                        reverseNode)
            copyTask = rescaleProgress(copyTask, progress,
                                       progress + 80. / len(indexes))
            for current, _, status in copyTask:
                yield current, maxprogress, status

            # xxx look ahead one or more nodes to skip some copies

            reverseNode.setRevisionInfo(self.rootNode.getRevisionInfo())
            reverseNode.readonly = True
            self.rootNode.setRevisionInfo(currentNode.getRevisionInfo())

            # Replace the previousNode with the reverse node, and the currentNode with the rootNode
            self.nodes[currentIndex - direction] = reverseNode
            self.nodes[currentIndex] = self.rootNode
            self.rootNodeIndex = currentIndex

            log.info("Root node now at index %d", currentIndex)

            assert currentNode is not self.rootNode, "Root node appears twice in nodes!"
            currentNode.worldFolder.close()
            currentNode.invalid = True
            shutil.rmtree(currentNode.worldFolder.filename, ignore_errors=True)
Example #5
0
    def writeAllChangesIter(self, requestedRevision=None):
        """
        Write all changes to the root world folder, preserving undo history.
        
        If a revision is requested, the state of the world at that revision will be
        written, otherwise, the last node in the history is used.
        
        All nodes between the root node and the requested node, inclusive, are replaced
        with new nodes. The old nodes are no longer valid. The root node will be placed at
        the position in the nodes list previously occupied by the requested node.

        Parameters
        ----------
        
        requestedRevision: RevisionHistoryNode | int | None
            If given, this specifies the revision to write to the world folder, otherwise
            the most recent revision is written.
        
        Returns
        -------
        
        progress: Iterator[(current, max, status)]
            Progress information for the write-changes task.
            
        """
        # XXXXX wait for async writes to complete here
        # Progress counts:
        # 0-20:   Orphaned chains
        # 20-100: History nodes

        maxprogress = 100

        if isinstance(requestedRevision, RevisionHistoryNode):
            requestedIndex = self.nodes.index(requestedRevision)
        elif requestedRevision is None:
            requestedIndex = len(self.nodes) - 1
        else:
            requestedIndex = requestedRevision

        if self.orphanChainIndex is not None:
            # Root node is orphaned - collapse orphan chain into it in reverse order
            orphanNodes = []
            orphanChainNode = self.nodes[self.orphanChainIndex]
            while orphanChainNode is not self.rootNode:
                orphanNodes.append(orphanChainNode)
                orphanChainNode = orphanChainNode.parentNode
            
            # Apply each orphaned node onto the root node.
            for progress, orphanChainNode in enumProgress(orphanNodes[::-1], 0, 20):
                yield (progress, maxprogress, "Collapsing orphaned chain")

                copyTask = copyToFolderIter(self.rootFolder, orphanChainNode)
                copyTask = rescaleProgress(copyTask, progress, progress + 20./len(orphanNodes))
                for current, _, status in copyTask:
                    yield current, maxprogress, status

            # Root node now replaces the orphan chain's tail in the history.
            # (the nodes ahead and behind of the root node should now point to this node)
            self.nodes[self.orphanChainIndex] = self.rootNode
            self.orphanChainIndex = None
            
        if requestedIndex == self.rootNodeIndex:
            return  # nothing to do
        elif requestedIndex < self.rootNodeIndex:
            # Nodes behind the root node in the history will be re-reverted and replaced
            # with plain nodes
            direction = -1
            indexes = xrange(self.rootNodeIndex-1, requestedIndex-1, -1)
        else:
            # Nodes ahead of the root node will be reverted and replaced with
            # "reversion" nodes.
            direction = 1
            indexes = xrange(self.rootNodeIndex+1, requestedIndex+1)
            
        log.info("writeAllChanges: moving %s", "forwards" if direction == 1 else "backwards")

        for progress, currentIndex in enumProgress(indexes, 20, 100):
            # Write all changes from each node into the initial folder. Save the previous
            # chunk and file data from the initial folder into a reverse revision.

            currentNode = self.nodes[currentIndex]

            reverseFolder = self._createRevisionFolder()
            reverseNode = RevisionHistoryNode(self, reverseFolder, self.nodes[currentIndex - direction])

            reverseNode.differences = self.rootNode.differences
            self.rootNode.differences = currentNode.getChanges()

            copyTask = copyToFolderIter(self.rootFolder, currentNode, reverseNode)
            copyTask = rescaleProgress(copyTask, progress, progress + 80. / len(indexes))
            for current, _, status in copyTask:
                yield current, maxprogress, status

            # xxx look ahead one or more nodes to skip some copies

            reverseNode.setRevisionInfo(self.rootNode.getRevisionInfo())
            reverseNode.readonly = True
            self.rootNode.setRevisionInfo(currentNode.getRevisionInfo())

            # Replace the previousNode with the reverse node, and the currentNode with the rootNode
            self.nodes[currentIndex - direction] = reverseNode
            self.nodes[currentIndex] = self.rootNode
            self.rootNodeIndex = currentIndex

            log.info("Root node now at index %d", currentIndex)

            assert currentNode is not self.rootNode, "Root node appears twice in nodes!"
            currentNode.worldFolder.close()
            currentNode.invalid = True
            shutil.rmtree(currentNode.worldFolder.filename, ignore_errors=True)
    def writeAllChangesIter(self, requestedRevision=None):
        """
        Write all changes to the root world folder, preserving undo history. The world folder
        becomes the new head node. The previous head node is no longer valid after calling
        writeAllChanges. Specify a revision to only save changes up to and including that
        revision.

        :return:
        :rtype:
        """
        # XXXXX wait for async writes to complete here
        # Progress counts:
        # 0-20:   Orphaned chains
        # 20-100: History nodes

        maxprogress = 100

        if isinstance(requestedRevision, RevisionHistoryNode):
            requestedIndex = self.nodes.index(requestedRevision)
        elif requestedRevision is None:
            requestedIndex = len(self.nodes) - 1
        else:
            requestedIndex = requestedRevision

        orphanChainProgress = 20
        if self.orphanChainIndex is not None:
            # Root node is orphaned - collapse orphan chain into it in reverse order
            orphanNodes = []
            orphanChainNode = self.nodes[self.orphanChainIndex]
            while orphanChainNode is not self.rootNode:
                orphanNodes.append(orphanChainNode)
                orphanChainNode = orphanChainNode.parentNode

            for progress, orphanChainNode in enumProgress(
                    reversed(orphanNodes), 0, 20):
                yield (progress, maxprogress, "Collapsing orphaned chain")

                copyTask = copyToFolderIter(self.rootFolder, orphanChainNode)
                copyTask = rescaleProgress(copyTask, progress,
                                           20 / len(orphanNodes))
                for current, _, status in copyTask:
                    yield current, maxprogress, status

            self.nodes[self.orphanChainIndex] = self.rootNode
            self.orphanChainIndex = None

        if requestedIndex == self.rootNodeIndex:
            return  # nothing to do
        elif requestedIndex < self.rootNodeIndex:
            direction = -1
            indexes = xrange(self.rootNodeIndex - 1, requestedIndex - 1, -1)
        else:
            direction = 1
            indexes = xrange(self.rootNodeIndex + 1, requestedIndex + 1)
        log.info("writeAllChanges: moving %s",
                 "forwards" if direction == 1 else "backwards")

        for progress, currentIndex in enumProgress(indexes, 20, 80):
            # Write all changes from each node into the initial folder. Save the previous
            # chunk and file data from the initial folder into a reverse revision.

            currentNode = self.nodes[currentIndex]

            reverseFolder = self._createRevisionFolder()
            reverseNode = RevisionHistoryNode(
                self, reverseFolder, self.nodes[currentIndex - direction])

            reverseNode.differences = self.rootNode.differences
            self.rootNode.differences = currentNode.getChanges()

            copyTask = copyToFolderIter(self.rootFolder, currentNode,
                                        reverseNode)
            copyTask = rescaleProgress(copyTask, progress, 80 / len(indexes))
            for current, _, status in copyTask:
                yield current, maxprogress, status

            # xxx look ahead one or more nodes to skip some copies

            reverseNode.setRevisionInfo(self.rootNode.getRevisionInfo())
            reverseNode.readonly = True
            self.rootNode.setRevisionInfo(currentNode.getRevisionInfo())

            # Replace the previousNode with the reverse node, and the currentNode with the rootNode
            self.nodes[currentIndex - direction] = reverseNode
            self.nodes[currentIndex] = self.rootNode
            self.rootNodeIndex = currentIndex

            log.info("Root node now at index %d", currentIndex)

            assert currentNode is not self.rootNode, "Root node appears twice in nodes!"
            currentNode.worldFolder.close()
            currentNode.invalid = True
            shutil.rmtree(currentNode.worldFolder.filename, ignore_errors=True)