예제 #1
0
    def loadMetadata(self):
        try:
            metadataTag = nbt.load(
                buf=self.selectedRevision.readFile("level.dat"))
            self.metadata = AnvilWorldMetadata(metadataTag)
            self.loadBlockMapping()
        except (EnvironmentError, zlib.error, NBTFormatError) as e:
            log.info(
                "Error loading level.dat, trying level.dat_old ({0})".format(
                    e))
            try:
                metadataTag = nbt.load(
                    buf=self.selectedRevision.readFile("level.dat_old"))
                self.metadata = AnvilWorldMetadata(metadataTag)
                self.metadata.dirty = True
                log.info("level.dat restored from backup.")
            except Exception as e:
                traceback.print_exc()
                log.info(
                    "%r while loading level.dat_old. Initializing with defaults.",
                    e)
                self._createMetadataTag()

        if self.metadata.version != VERSION_ANVIL:
            raise LevelFormatError(
                "Pre-Anvil world formats are not supported (for now)")
예제 #2
0
    def getPlayerTag(self, playerUUID=""):
        """
        Return the root NBT tag for the named player. Raise PlayerNotFound if not present.

        Parameters
        ----------
        playerUUID : unicode
            The player ID returned from :ref:`listPlayers`

        Returns
        -------
        player : AnvilPlayerRef
        """
        if playerUUID == "":
            if "Player" in self.metadata.rootTag:
                # single-player world
                playerTag = self.metadata.rootTag["Player"]
                return playerTag
            raise PlayerNotFound(playerUUID)
        else:
            playerFilePath = "playerdata/%s.dat" % playerUUID
            if self.selectedRevision.containsFile(playerFilePath):
                # multiplayer world, found this player
                playerTag = nbt.load(
                    buf=self.selectedRevision.readFile(playerFilePath))
                return playerTag
            else:
                raise PlayerNotFound(playerUUID)
예제 #3
0
    def __init__(self, filename, create=False):
        raise NotImplementedError("No adapter for zipped world/schematic files yet!!!")
        self.zipfilename = filename

        tempdir = tempfile.mktemp("schematic")
        if create is False:
            zf = zipfile.ZipFile(filename)
            zf.extractall(tempdir)
            zf.close()

        super(ZipSchematic, self).__init__(tempdir, create)
        atexit.register(shutil.rmtree, self.worldFolder.filename, True)


        try:
            schematicDat = nbt.load(self.worldFolder.getFilePath("schematic.dat"))

            self.Width = schematicDat['Width'].value
            self.Height = schematicDat['Height'].value
            self.Length = schematicDat['Length'].value

            if "Materials" in schematicDat:
                self.blocktypes = blocktypes_named[schematicDat["Materials"].value]

        except Exception as e:
            print "Exception reading schematic.dat, skipping: {0!r}".format(e)
            self.Width = 0
            self.Length = 0
예제 #4
0
    def testErrors(self):
        """
        attempt to name elements of a TAG_List
        named list elements are not allowed by the NBT spec,
        so we must discard any names when writing a list.
        """

        level = self.testCreate()
        level["Map"]["Spawn"][0].name = "Torg Potter"
        data = level.save()
        newlevel = nbt.load(buf=data)

        n = newlevel["Map"]["Spawn"][0].name
        if n:
            print "Named list element failed: %s" % n

        # attempt to delete non-existent TAG_Compound elements
        # this generates a KeyError like a python dict does.
        level = self.testCreate()
        try:
            del level["DEADBEEF"]
        except KeyError:
            pass
        else:
            assert False
예제 #5
0
파일: findadapter.py 프로젝트: wcpe/mcedit2
def isLevel(cls, filename):
    """
    Return True if the given level adapter can load the given filename, False otherwise.
    Tries to call cls.canOpenFile on the filename, then
    cls._isDataLevel on the file's data, then cls._isTagLevel on an NBT tree
    loaded from that data. If none of these methods are present, return False.

    Subclasses should implement one of canOpenFile, _isDataLevel, or _isTagLevel.
    """
    if hasattr(cls, "canOpenFile"):
        return cls.canOpenFile(filename)

    if os.path.isfile(filename):
        with open(filename, "rb") as f:
            data = f.read()

        if hasattr(cls, "_isDataLevel"):
            return cls._isDataLevel(data)

        if hasattr(cls, "_isTagLevel"):
            try:
                rootTag = nbt.load(filename, data)
            except:
                return False

            return cls._isTagLevel(rootTag)

    return False
예제 #6
0
    def __init__(self, filename, create=False):
        raise NotImplementedError(
            "No adapter for zipped world/schematic files yet!!!")
        self.zipfilename = filename

        tempdir = tempfile.mktemp("schematic")
        if create is False:
            zf = zipfile.ZipFile(filename)
            zf.extractall(tempdir)
            zf.close()

        super(ZipSchematic, self).__init__(tempdir, create)
        atexit.register(shutil.rmtree, self.worldFolder.filename, True)

        try:
            schematicDat = nbt.load(
                self.worldFolder.getFilePath("schematic.dat"))

            self.Width = schematicDat['Width'].value
            self.Height = schematicDat['Height'].value
            self.Length = schematicDat['Length'].value

            if "Materials" in schematicDat:
                self.blocktypes = blocktypes_named[
                    schematicDat["Materials"].value]

        except Exception as e:
            print "Exception reading schematic.dat, skipping: {0!r}".format(e)
            self.Width = 0
            self.Length = 0
예제 #7
0
    def readChunk(self, cx, cz, dimName):
        """
        Return chunk (cx, cz) in the given dimension as an AnvilChunkData. Raise ChunkNotPresent if not found.

        :type cx: int or dtype
        :type cz: int or dtype
        :type dimName: str
        :return:
        :rtype: AnvilChunkData
        """
        try:
            data = self.selectedRevision.readChunkBytes(cx, cz, dimName)
            chunkTag = nbt.load(buf=data)
            log.debug("_getChunkData: Chunk %s loaded (%s bytes)", (cx, cz),
                      len(data))
            chunkData = AnvilChunkData(self, cx, cz, dimName, chunkTag)
        except ChunkNotPresent:
            raise
        except (
                KeyError, IndexError, zlib.error, UnicodeError
        ) as e:  # Missing nbt keys, lists too short, decompression failure, unknown NBT tags
            raise AnvilChunkFormatError("Error loading chunk: %r" % e, None,
                                        sys.exc_info()[2])

        return chunkData
예제 #8
0
    def repair(self):
        """
        Fix the following problems with the region file:
         - remove offset table entries pointing past the end of the file
         - remove entries that overlap other entries
         - relocate offsets for chunks whose xPos,yPos don't match
        """

        lostAndFound = {}
        _freeSectors = [True] * len(self.freeSectors)
        _freeSectors[0] = _freeSectors[1] = False
        deleted = 0
        recovered = 0
        log.info("Beginning repairs on {file} ({chunks} chunks)".format(file=os.path.basename(self.path), chunks=sum(self.offsets > 0)))
        for index, offset in enumerate(self.offsets):
            if offset:
                cx = index & 0x1f
                cz = index >> 5
                sectorStart = offset >> 8
                sectorCount = offset & 0xff
                try:

                    if sectorStart + sectorCount > len(self.freeSectors):
                        raise RegionFormatError("Offset {start}:{end} ({offset}) at index {index} pointed outside of "
                                                "the file".format(start=sectorStart, end=sectorStart + sectorCount, index=index, offset=offset))

                    data = self.readChunkBytes(cx, cz)
                    chunkTag = nbt.load(buf=data)
                    lev = chunkTag["Level"]
                    xPos = lev["xPos"].value & 0x1f
                    zPos = lev["zPos"].value & 0x1f
                    overlaps = False

                    for i in xrange(sectorStart, sectorStart + sectorCount):
                        if _freeSectors[i] is False:
                            overlaps = True
                        _freeSectors[i] = False

                    if xPos != cx or zPos != cz or overlaps:
                        lostAndFound[xPos, zPos] = data

                        if (xPos, zPos) != (cx, cz):
                            raise RegionFormatError("Chunk {found} was found in the slot reserved for {expected}".format(found=(xPos, zPos), expected=(cx, cz)))
                        else:
                            raise RegionFormatError("Chunk {found} (in slot {expected}) has overlapping sectors with another chunk!".format(found=(xPos, zPos), expected=(cx, cz)))

                except Exception as e:
                    log.info("Unexpected chunk data at sector {sector} ({exc})".format(sector=sectorStart, exc=e))
                    self._setOffset(cx, cz, 0)
                    deleted += 1

        for cPos, foundData in lostAndFound.iteritems():
            cx, cz = cPos
            if self._getOffset(cx, cz) == 0:
                log.info("Found chunk {found} and its slot is empty, recovering it".format(found=cPos))
                self.writeChunk(cx, cz, foundData)
                recovered += 1

        log.info("Repair complete. Removed {0} chunks, recovered {1} chunks, net {2}".format(deleted, recovered, recovered - deleted))
예제 #9
0
    def getMapTag(self, mapID):
        mapPath = "data/map_%s.dat" % mapID
        if not self.selectedRevision.containsFile(mapPath):
            raise KeyError("Map %s not found" % mapID)

        mapData = self.selectedRevision.readFile(mapPath)
        mapNBT = nbt.load(buf=mapData)
        return mapNBT
예제 #10
0
    def getMapTag(self, mapID):
        mapPath = self._getMapPath(mapID)
        if not self.selectedRevision.containsFile(mapPath):
            raise KeyError("Map %s not found" % mapID)

        mapData = self.selectedRevision.readFile(mapPath)
        mapNBT = nbt.load(buf=mapData)
        return mapNBT
예제 #11
0
    def loadMetadata(self):
        try:
            metadataTag = nbt.load(buf=self.selectedRevision.readFile("level.dat"))
            self.metadata = AnvilWorldMetadata(metadataTag)
            self.loadBlockMapping()
        except (EnvironmentError, zlib.error, NBTFormatError) as e:
            log.info("Error loading level.dat, trying level.dat_old ({0})".format(e))
            try:
                metadataTag = nbt.load(buf=self.selectedRevision.readFile("level.dat_old"))
                self.metadata = AnvilWorldMetadata(metadataTag)
                self.metadata.dirty = True
                log.info("level.dat restored from backup.")
            except Exception as e:
                traceback.print_exc()
                log.info("%r while loading level.dat_old. Initializing with defaults.", e)
                self._createMetadataTag()

        assert self.metadata.version == VERSION_ANVIL, "Pre-Anvil world formats are not supported (for now)"
예제 #12
0
    def getWorldInfo(cls, filename, displayNameLimit=40):
        try:
            if os.path.isdir(filename):
                folderName = os.path.basename(filename)
                levelDat = os.path.join(filename, "level.dat")
            else:
                folderName = os.path.basename(os.path.dirname(filename))
                levelDat = filename

            levelTag = nbt.load(levelDat)
            try:
                displayName = levelTag['Data']['LevelName'].value

                if len(displayName) > displayNameLimit:
                    displayName = displayName[:displayNameLimit] + "..."
                if len(folderName) > displayNameLimit:
                    folderName = folderName[:displayNameLimit] + "..."

                if folderName != displayName:
                    displayName = "%s (%s)" % (displayName, folderName)
            except Exception as e:
                log.warn("Failed to get display name for level.", exc_info=1)
                displayName = folderName

            try:
                lastPlayedTime = levelTag['Data']['LastPlayed'].value
            except Exception as e:
                log.warn("Failed to get last-played time for level.",
                         exc_info=1)
                lastPlayedTime = 0

            version = "Unknown Version"
            try:
                metadata = AnvilWorldMetadata(levelTag)
                stackVersion = VERSION_1_8 if metadata.is1_8World(
                ) else VERSION_1_7

                if stackVersion == VERSION_1_7:
                    version = "Minecraft 1.7"
                    if "FML" in metadata.metadataTag:
                        version = "MinecraftForge 1.7"

                if stackVersion == VERSION_1_8:
                    version = "Minecraft 1.8"

            except Exception as e:
                log.warn("Failed to get version info for %s: %r",
                         filename,
                         e,
                         exc_info=1)

            return WorldInfo(displayName, lastPlayedTime, version)

        except Exception as e:
            log.error("Failed getting world info for %s: %r", filename, e)
            return WorldInfo(str(e), 0, "")
예제 #13
0
def testBigEndianIntHeightMap():
    """ Test modifying, saving, and loading the new TAG_Int_Array heightmap
    added with the Anvil format.
    """
    region = RegionFile(TempFile("AnvilWorld/region/r.0.0.mca"))
    chunk_data = region.readChunkBytes(0, 0)
    chunk = nbt.load(buf=chunk_data)

    hm = chunk["Level"]["HeightMap"]
    hm.value[2] = 500
    oldhm = numpy.array(hm.value)

    filename = mktemp("ChangedChunk")
    chunk.save(filename)
    changedChunk = nbt.load(filename)
    os.unlink(filename)

    eq = (changedChunk["Level"]["HeightMap"].value == oldhm)
    assert eq.all()
예제 #14
0
def testBigEndianIntHeightMap(tmpdir, temp_file):
    """ Test modifying, saving, and loading the new TAG_Int_Array heightmap
    added with the Anvil format.
    """
    region = RegionFile(temp_file.strpath)
    chunk_data = region.readChunkBytes(0, 0)
    chunk = nbt.load(buf=chunk_data)

    hm = chunk["Level"]["HeightMap"]
    hm.value[2] = 500
    oldhm = numpy.array(hm.value)

    filename = tmpdir.join("ChangedChunk").strpath
    chunk.save(filename)
    changedChunk = nbt.load(filename)
    os.unlink(filename)

    eq = (changedChunk["Level"]["HeightMap"].value == oldhm)
    assert eq.all()
예제 #15
0
def testBigEndianIntHeightMap(tmpdir, temp_file):
    """ Test modifying, saving, and loading the new TAG_Int_Array heightmap
    added with the Anvil format.
    """
    region = RegionFile(temp_file.strpath)
    chunk_data = region.readChunkBytes(0, 0)
    chunk = nbt.load(buf=chunk_data)

    hm = chunk["Level"]["HeightMap"]
    hm.value[2] = 500
    oldhm = numpy.array(hm.value)

    filename = tmpdir.join("ChangedChunk").strpath
    chunk.save(filename)
    changedChunk = nbt.load(filename)
    os.unlink(filename)

    eq = (changedChunk["Level"]["HeightMap"].value == oldhm)
    assert eq.all()
예제 #16
0
def testBigEndianIntHeightMap():
    """ Test modifying, saving, and loading the new TAG_Int_Array heightmap
    added with the Anvil format.
    """
    region = RegionFile(TempFile("AnvilWorld/region/r.0.0.mca"))
    chunk_data = region.readChunkBytes(0, 0)
    chunk = nbt.load(buf=chunk_data)

    hm = chunk["Level"]["HeightMap"]
    hm.value[2] = 500
    oldhm = numpy.array(hm.value)

    filename = mktemp("ChangedChunk")
    chunk.save(filename)
    changedChunk = nbt.load(filename)
    os.unlink(filename)

    eq = (changedChunk["Level"]["HeightMap"].value == oldhm)
    assert eq.all()
예제 #17
0
    def __init__(self, filename):
        self.filename = filename
        rootTag = nbt.load(filename)
        self.Blocks = array([[[pc_blocktypes.Chest.ID]]], 'uint8')
        for item in list(rootTag["Inventory"]):
            slot = item["Slot"].value
            if slot < 9 or slot >= 36:
                rootTag["Inventory"].remove(item)
            else:
                item["Slot"].value -= 9  # adjust for different chest slot indexes

        self.rootTag = rootTag
예제 #18
0
    def testSpeed(self):
        d = join("test_files", "TileTicks_chunks.zip")
        zf = zipfile.ZipFile(d)

        files = [zf.read(f) for f in zf.namelist()[:40]]
        startTime = time.time()
        for f in files:
            if len(f):
                n = nbt.load(buf=f)
        duration = time.time() - startTime

        assert duration < 1.0 # Will fail when not using _nbt.pyx
예제 #19
0
    def __init__(self, filename):
        self.filename = filename
        rootTag = nbt.load(filename)
        self.Blocks = array([[[pc_blocktypes.Chest.ID]]], 'uint8')
        for item in list(rootTag["Inventory"]):
            slot = item["Slot"].value
            if slot < 9 or slot >= 36:
                rootTag["Inventory"].remove(item)
            else:
                item[
                    "Slot"].value -= 9  # adjust for different chest slot indexes

        self.rootTag = rootTag
예제 #20
0
    def getWorldInfo(cls, filename, displayNameLimit=40):
        try:
            if os.path.isdir(filename):
                folderName = os.path.basename(filename)
                levelDat = os.path.join(filename, "level.dat")
            else:
                folderName = os.path.basename(os.path.dirname(filename))
                levelDat = filename

            levelTag = nbt.load(levelDat)
            try:
                displayName = levelTag['Data']['LevelName'].value

                if len(displayName) > displayNameLimit:
                    displayName = displayName[:displayNameLimit] + "..."
                if len(folderName) > displayNameLimit:
                    folderName = folderName[:displayNameLimit] + "..."

                if folderName != displayName:
                    displayName = "%s (%s)" % (displayName, folderName)
            except Exception as e:
                log.warn("Failed to get display name for level.", exc_info=1)
                displayName = folderName

            try:
                lastPlayedTime = levelTag['Data']['LastPlayed'].value
            except Exception as e:
                log.warn("Failed to get last-played time for level.", exc_info=1)
                lastPlayedTime = 0

            version = "Unknown Version"
            try:
                metadata = AnvilWorldMetadata(levelTag)
                stackVersion = VERSION_1_8 if metadata.is1_8World() else VERSION_1_7

                if stackVersion == VERSION_1_7:
                    version = "Minecraft 1.7"
                    if "FML" in metadata.metadataTag:
                        version = "MinecraftForge 1.7"

                if stackVersion == VERSION_1_8:
                    version = "Minecraft 1.8"

            except Exception as e:
                log.warn("Failed to get version info for %s: %r", filename, e, exc_info=1)

            return WorldInfo(displayName, lastPlayedTime, version)

        except Exception as e:
            log.error("Failed getting world info for %s: %r", filename, e)
            return WorldInfo(str(e), 0, "")
예제 #21
0
    def testLoad(self):
        "Load an indev level."
        level = nbt.load("test_files/indev.mclevel")

        # The root tag must have a name, and so must any tag within a TAG_Compound
        print level.name

        # Use the [] operator to look up subtags of a TAG_Compound.
        print level["Environment"]["SurroundingGroundHeight"].value

        # Numeric, string, and bytearray types have a value that can be accessed and changed.
        print level["Map"]["Blocks"].value

        return level
예제 #22
0
def testLoad(indev_file):
    "Load an indev level."
    level = nbt.load(indev_file.strpath)

    # The root tag must have a name, and so must any tag within a TAG_Compound
    print level.name

    # Use the [] operator to look up subtags of a TAG_Compound.
    print level["Environment"]["SurroundingGroundHeight"].value

    # Numeric, string, and bytearray types have a value that can be accessed and changed.
    print level["Map"]["Blocks"].value

    return level
예제 #23
0
파일: nbt_test.py 프로젝트: mcedit/mcedit2
def testErrors(created_nbt):
    """
    attempt to name elements of a TAG_List
    named list elements are not allowed by the NBT spec,
    so we must discard any names when writing a list.
    """

    level = created_nbt
    level["Map"]["Spawn"][0].name = "Torg Potter"
    data = level.save()
    newlevel = nbt.load(buf=data)

    n = newlevel["Map"]["Spawn"][0].name
    assert not n, "Named list element failed: %s" % n

    # attempt to delete non-existent TAG_Compound elements
    # this generates a KeyError like a python dict does.
    with pytest.raises(KeyError):
        del level["DEADBEEF"]
예제 #24
0
def testErrors(created_nbt):
    """
    attempt to name elements of a TAG_List
    named list elements are not allowed by the NBT spec,
    so we must discard any names when writing a list.
    """

    level = created_nbt
    level["Map"]["Spawn"][0].name = "Torg Potter"
    data = level.save()
    newlevel = nbt.load(buf=data)

    n = newlevel["Map"]["Spawn"][0].name
    assert not n, "Named list element failed: %s" % n

    # attempt to delete non-existent TAG_Compound elements
    # this generates a KeyError like a python dict does.
    with pytest.raises(KeyError):
        del level["DEADBEEF"]
예제 #25
0
    def readChunk(self, cx, cz, dimName):
        """
        Return chunk (cx, cz) in the given dimension as an AnvilChunkData. Raise ChunkNotPresent if not found.

        :type cx: int or dtype
        :type cz: int or dtype
        :type dimName: str
        :return:
        :rtype: AnvilChunkData
        """
        try:
            data = self.selectedRevision.readChunkBytes(cx, cz, dimName)
            chunkTag = nbt.load(buf=data)
            log.debug("_getChunkData: Chunk %s loaded (%s bytes)", (cx, cz), len(data))
            chunkData = AnvilChunkData(self, cx, cz, dimName, chunkTag)
        except ChunkNotPresent:
            raise
        except (KeyError, IndexError, zlib.error) as e:  # Missing nbt keys, lists too short, decompression failure
            raise AnvilChunkFormatError("Error loading chunk: %r" % e)

        return chunkData
예제 #26
0
    def getPlayerTag(self, playerUUID=""):
        """
        Return the root NBT tag for the named player. Raise PlayerNotFound if not present.

        :param playerUUID:
        :type playerUUID: unicode
        :return:
        :rtype: PCPlayer
        """
        if playerUUID == "":
            if "Player" in self.metadata.rootTag:
                # single-player world
                playerTag = self.metadata.rootTag["Player"]
                return playerTag
            raise PlayerNotFound(playerUUID)
        else:
            playerFilePath = "playerdata/%s.dat" % playerUUID
            if self.selectedRevision.containsFile(playerFilePath):
                # multiplayer world, found this player
                playerTag = nbt.load(buf=self.selectedRevision.readFile(playerFilePath))
                return playerTag
            else:
                raise PlayerNotFound(playerUUID)
예제 #27
0
    def getPlayerTag(self, playerUUID=""):
        """
        Return the root NBT tag for the named player. Raise PlayerNotFound if not present.

        :param playerUUID:
        :type playerUUID: unicode
        :return:
        :rtype: PCPlayer
        """
        if playerUUID == "":
            if "Player" in self.metadata.rootTag:
                # single-player world
                playerTag = self.metadata.rootTag["Player"]
                return playerTag
            raise PlayerNotFound(playerUUID)
        else:
            playerFilePath = "playerdata/%s.dat" % playerUUID
            if self.selectedRevision.containsFile(playerFilePath):
                # multiplayer world, found this player
                playerTag = nbt.load(buf=self.selectedRevision.readFile(playerFilePath))
                return playerTag
            else:
                raise PlayerNotFound(playerUUID)
예제 #28
0
    def __init__(self, shape=None, filename=None, blocktypes='Alpha', readonly=False, resume=False):
        """
        Creates an object which stores a section of a Minecraft world as an
        NBT structure. The order of the coordinates for the block arrays in
        the file is y,z,x. This is the same order used in Minecraft 1.4's
        chunk sections.

        Parameters
        ----------
        shape: tuple of int
            The shape of the schematic as (x, y, z)
        filename: basestring
            Path to a file to load a saved schematic from.
        blocktypes: basestring or BlockTypeSet
            The name of a builtin blocktypes set (one of
            "Classic", "Alpha", "Pocket") to indicate allowable blocks. The default
            is Alpha. An instance of BlockTypeSet may be passed instead.

        Returns
        ----------
        SchematicFileAdapter

        """
        self.EntityRef = PCEntityRef
        self.TileEntityRef = PCTileEntityRef

        if filename is None and shape is None:
            raise ValueError("shape or filename required to create %s" % self.__class__.__name__)

        if filename:
            self.filename = filename
            if os.path.exists(filename):
                rootTag = nbt.load(filename)
            else:
                rootTag = None
        else:
            self.filename = None
            rootTag = None

        if blocktypes in blocktypeClassesByName:
            self.blocktypes = blocktypeClassesByName[blocktypes]()
        else:
            if not isinstance(blocktypes, BlockTypeSet):
                raise ValueError("%s is not a recognized BlockTypeSet", blocktypes)
            self.blocktypes = blocktypes

        if rootTag:
            self.rootTag = rootTag
            if "Materials" in rootTag:
                self.blocktypes = blocktypeClassesByName[self.Materials]()
            else:
                rootTag["Materials"] = nbt.TAG_String(self.blocktypes.name)

            w = self.rootTag["Width"].value
            l = self.rootTag["Length"].value
            h = self.rootTag["Height"].value

            assert self.rootTag["Blocks"].value.size == w * l * h
            self._Blocks = self.rootTag["Blocks"].value.astype('uint16').reshape(h, l, w) # _Blocks is y, z, x

            del self.rootTag["Blocks"]
            if "AddBlocks" in self.rootTag:
                # Use WorldEdit's "AddBlocks" array to load and store the 4 high bits of a block ID.
                # Unlike Minecraft's NibbleArrays, this array stores the first block's bits in the
                # 4 high bits of the first byte.

                size = (h * l * w)

                # If odd, add one to the size to make sure the adjacent slices line up.
                add = numpy.empty(size + (size & 1), 'uint16')

                # Fill the even bytes with data
                add[::2] = self.rootTag["AddBlocks"].value

                # Copy the low 4 bits to the odd bytes
                add[1::2] = add[::2] & 0xf

                # Shift the even bytes down
                add[::2] >>= 4

                # Shift every byte up before merging it with Blocks
                add <<= 8
                self._Blocks |= add[:size].reshape(h, l, w)
                del self.rootTag["AddBlocks"]

            self.rootTag["Data"].value = self.rootTag["Data"].value.reshape(h, l, w)

            if "Biomes" in self.rootTag:
                self.rootTag["Biomes"].value.shape = (l, w)

            # If BlockIDs is present, it contains an ID->internalName mapping
            # from the source level's FML tag.

            if "BlockIDs" in self.rootTag:
                self.blocktypes.addBlockIDsFromSchematicTag(self.rootTag["BlockIDs"])

            # If itemStackVersion is present, it was exported from MCEdit 2.0.
            # Its value is either 17 or 18, the values of the version constants.
            # ItemIDs will also be present.

            # If itemStackVersion is not present, this schematic was exported from
            # WorldEdit or MCEdit 1.0. The itemStackVersion cannot be determined
            # without searching the entities for an itemStack and checking
            # the type of its `id` tag. If no itemStacks are found, the
            # version defaults to 1.8 which does not need an ItemIDs tag.


            if "itemStackVersion" in self.rootTag:
                itemStackVersion = self.rootTag["itemStackVersion"].value
                if itemStackVersion not in (VERSION_1_7, VERSION_1_8):
                    raise LevelFormatError("Unknown item stack version %d" % itemStackVersion)
                if itemStackVersion == VERSION_1_7:
                    itemIDs = self.rootTag.get("ItemIDs")
                    if itemIDs is not None:
                        self.blocktypes.addItemIDsFromSchematicTag(itemIDs)

                self.blocktypes.itemStackVersion = itemStackVersion
            else:
                self.blocktypes.itemStackVersion = self.getItemStackVersionFromEntities()


        else:
            rootTag = nbt.TAG_Compound(name="Schematic")
            rootTag["Height"] = nbt.TAG_Short(shape[1])
            rootTag["Length"] = nbt.TAG_Short(shape[2])
            rootTag["Width"] = nbt.TAG_Short(shape[0])

            rootTag["Entities"] = nbt.TAG_List()
            rootTag["TileEntities"] = nbt.TAG_List()
            rootTag["Materials"] = nbt.TAG_String(self.blocktypes.name)
            rootTag["itemStackVersion"] = nbt.TAG_Byte(self.blocktypes.itemStackVersion)


            self._Blocks = zeros((shape[1], shape[2], shape[0]), 'uint16')
            rootTag["Data"] = nbt.TAG_Byte_Array(zeros((shape[1], shape[2], shape[0]), uint8))

            rootTag["Biomes"] = nbt.TAG_Byte_Array(zeros((shape[2], shape[0]), uint8))

            self.rootTag = rootTag

            self.rootTag["BlockIDs"] = blockIDMapping(self.blocktypes)
            itemMapping = itemIDMapping(self.blocktypes)
            if itemMapping is not None:
                self.rootTag["ItemIDs"] = itemMapping  # Only present for Forge 1.7

        # Expand blocks and data to chunk edges
        h16 = (self.Height + 15) & ~0xf
        l16 = (self.Length + 15) & ~0xf
        w16 = (self.Width + 15) & ~0xf

        blocks = self._Blocks
        self._Blocks = numpy.zeros((h16, l16, w16), blocks.dtype)
        self._Blocks[:blocks.shape[0], :blocks.shape[1], :blocks.shape[2]] = blocks

        data = self.rootTag["Data"].value
        self.rootTag["Data"].value = numpy.zeros((h16, l16, w16), data.dtype)
        self.rootTag["Data"].value[:data.shape[0], :data.shape[1], :data.shape[2]] = data

        self.rootTag["Data"].value &= 0xF  # discard high bits

        self.entitiesByChunk = defaultdict(list)
        for tag in self.rootTag["Entities"]:
            ref = self.EntityRef(tag)
            pos = ref.Position
            cx, cy, cz = pos.chunkPos()
            self.entitiesByChunk[cx, cz].append(tag)

        self.tileEntitiesByChunk = defaultdict(list)
        for tag in self.rootTag["TileEntities"]:
            ref = self.TileEntityRef(tag)
            pos = ref.Position
            cx, cy, cz = pos.chunkPos()
            self.tileEntitiesByChunk[cx, cz].append(tag)
예제 #29
0
    def repair(self):
        """
        Fix the following problems with the region file:
         - remove offset table entries pointing past the end of the file
         - remove entries that overlap other entries
         - relocate offsets for chunks whose xPos,yPos don't match
        """

        lostAndFound = {}
        _freeSectors = [True] * len(self.freeSectors)
        _freeSectors[0] = _freeSectors[1] = False
        deleted = 0
        recovered = 0
        log.info("Beginning repairs on {file} ({chunks} chunks)".format(
            file=os.path.basename(self.path), chunks=sum(self.offsets > 0)))
        for index, offset in enumerate(self.offsets):
            if offset:
                cx = index & 0x1f
                cz = index >> 5
                sectorStart = offset >> 8
                sectorCount = offset & 0xff
                try:

                    if sectorStart + sectorCount > len(self.freeSectors):
                        raise RegionFormatError(
                            "Offset {start}:{end} ({offset}) at index {index} pointed outside of "
                            "the file".format(start=sectorStart,
                                              end=sectorStart + sectorCount,
                                              index=index,
                                              offset=offset))

                    data = self.readChunkBytes(cx, cz)
                    chunkTag = nbt.load(buf=data)
                    lev = chunkTag["Level"]
                    xPos = lev["xPos"].value & 0x1f
                    zPos = lev["zPos"].value & 0x1f
                    overlaps = False

                    for i in xrange(sectorStart, sectorStart + sectorCount):
                        if _freeSectors[i] is False:
                            overlaps = True
                        _freeSectors[i] = False

                    if xPos != cx or zPos != cz:
                        lostAndFound[xPos, zPos] = data
                        raise RegionFormatError(
                            "Chunk {found} was found in the slot reserved for {expected}"
                            .format(found=(xPos, zPos), expected=(cx, cz)))

                    if overlaps:
                        raise RegionFormatError(
                            "Chunk {found} (in slot {expected}) has overlapping sectors with another chunk!"
                            .format(found=(xPos, zPos), expected=(cx, cz)))

                except Exception as e:
                    log.info(
                        "Unexpected chunk data at sector {sector} ({exc})".
                        format(sector=sectorStart, exc=e))
                    self._setOffset(cx, cz, 0)
                    deleted += 1

        for cPos, foundData in lostAndFound.iteritems():
            cx, cz = cPos
            if self._getOffset(cx, cz) == 0:
                log.info(
                    "Found chunk {found} and its slot is empty, recovering it".
                    format(found=cPos))
                self.writeChunkBytes(cx, cz, foundData)
                recovered += 1

        log.info(
            "Repair complete. Removed {0} chunks, recovered {1} chunks, net {2}"
            .format(deleted, recovered, recovered - deleted))
예제 #30
0
파일: adapter.py 프로젝트: Fiskmans/mcedit2
    def __init__(self, filename=None, create=False, readonly=False, resume=None):
        """
        Load a Minecraft for PC level (Anvil format) from the given filename. It can point to either
        a level.dat or a folder containing one. If create is True, it will
        also create the world using a randomly selected seed.

        If you try to create an existing world, IOError will be raised.

        Uses a RevisionHistory to manage undo history. Upon creation, the world is read-only until createRevision() is
        called. Call createRevision() to create a new revision, or selectRevision() to revert to an earlier
        revision. Older revisions are read-only, so createRevision() must be called again to make further changes.

        Call writeAllChanges() to write all changes into the original world.

        :type filename: str or unicode
        :type create: bool
        :type readonly: bool
        :rtype: AnvilWorldAdapter
        """
        self.lockTime = 0

        assert not (create and readonly)

        if os.path.basename(filename) in ("level.dat", "level.dat_old"):
            filename = os.path.dirname(filename)

        if not os.path.exists(filename):
            if not create:
                raise IOError('File not found')

            os.mkdir(filename)
        else:
            if create:
                if not os.path.isdir(filename) or os.path.exists(os.path.join(filename, "level.dat")):
                    raise IOError('File exists!')

        if not os.path.isdir(filename):
            raise IOError('File is not a Minecraft Anvil world')

        if readonly:
            self.revisionHistory = AnvilWorldFolder(filename)
            self.selectedRevision = self.revisionHistory
        else:
            self.revisionHistory = RevisionHistory(filename, resume)
            self.selectedRevision = self.revisionHistory.getHead()

        self.filename = filename
        self.readonly = readonly
        if not readonly:
            self.acquireSessionLock()

        if create:
            self._createMetadataTag()
            self.selectedRevision.writeFile("level.dat", self.metadata.metadataTag.save())

        else:
            try:
                metadataTag = nbt.load(buf=self.selectedRevision.readFile("level.dat"))
                self.metadata = AnvilWorldMetadata(metadataTag)
                self.loadFMLMapping()
            except (EnvironmentError, zlib.error) as e:
                log.info("Error loading level.dat, trying level.dat_old ({0})".format(e))
                try:
                    metadataTag = nbt.load(buf=self.selectedRevision.readFile("level.dat_old"))
                    self.metadata = AnvilWorldMetadata(metadataTag)
                    log.info("level.dat restored from backup.")
                    self.saveChanges()
                except Exception as e:
                    traceback.print_exc()
                    log.info("%r while loading level.dat_old. Initializing with defaults.", e)
                    self._createMetadataTag()

        assert self.metadata.version == VERSION_ANVIL, "Pre-Anvil world formats are not supported (for now)"
예제 #31
0
    def __init__(self, shape=None, filename=None, blocktypes='Alpha', readonly=False, resume=False):
        """
        Creates an object which stores a section of a Minecraft world as an
        NBT structure. The order of the coordinates for the block arrays in
        the file is y,z,x. This is the same order used in Minecraft 1.4's
        chunk sections.

        :type shape: tuple
        :param shape: The shape of the schematic as (x, y, z)
        :type filename: basestring
        :param filename: Path to a file to load a saved schematic from.
        :type blocktypes: basestring or BlockTypeSet
        :param blocktypes: The name of a builtin blocktypes set (one of
            "Classic", "Alpha", "Pocket") to indicate allowable blocks. The default
            is Alpha. An instance of BlockTypeSet may be passed instead.
        :rtype: SchematicFileAdapter

        """
        if filename is None and shape is None:
            raise ValueError("shape or filename required to create %s" % self.__class__.__name__)

        if filename:
            self.filename = filename
            if os.path.exists(filename):
                rootTag = nbt.load(filename)
            else:
                rootTag = None
        else:
            self.filename = None
            rootTag = None

        if blocktypes in blocktypes_named:
            self.blocktypes = blocktypes_named[blocktypes]
        else:
            assert(isinstance(blocktypes, BlockTypeSet))
            self.blocktypes = blocktypes

        if rootTag:
            self.rootTag = rootTag
            if "Materials" in rootTag:
                self.blocktypes = blocktypes_named[self.Materials]
            else:
                rootTag["Materials"] = nbt.TAG_String(self.blocktypes.name)

            w = self.rootTag["Width"].value
            l = self.rootTag["Length"].value
            h = self.rootTag["Height"].value

            assert self.rootTag["Blocks"].value.size == w * l * h
            self._Blocks = self.rootTag["Blocks"].value.astype('uint16').reshape(h, l, w) # _Blocks is y, z, x

            del self.rootTag["Blocks"]
            if "AddBlocks" in self.rootTag:
                # Use WorldEdit's "AddBlocks" array to load and store the 4 high bits of a block ID.
                # Unlike Minecraft's NibbleArrays, this array stores the first block's bits in the
                # 4 high bits of the first byte.

                size = (h * l * w)

                # If odd, add one to the size to make sure the adjacent slices line up.
                add = numpy.empty(size + (size & 1), 'uint16')

                # Fill the even bytes with data
                add[::2] = self.rootTag["AddBlocks"].value

                # Copy the low 4 bits to the odd bytes
                add[1::2] = add[::2] & 0xf

                # Shift the even bytes down
                add[::2] >>= 4

                # Shift every byte up before merging it with Blocks
                add <<= 8
                self._Blocks |= add[:size].reshape(h, l, w)
                del self.rootTag["AddBlocks"]

            self.rootTag["Data"].value = self.rootTag["Data"].value.reshape(h, l, w)

            if "Biomes" in self.rootTag:
                self.rootTag["Biomes"].value.shape = (l, w)

            if "BlockIDs" in self.rootTag or "ItemIDs" in self.rootTag:
                self.blocktypes = type(self.blocktypes)()

            if "BlockIDs" in self.rootTag:
                self.blocktypes.addBlockIDsFromSchematicTag(self.rootTag["BlockIDs"])
            if "ItemIDs" in self.rootTag:
                self.blocktypes.addItemIDsFromSchematicTag(self.rootTag["ItemIDs"])



        else:
            rootTag = nbt.TAG_Compound(name="Schematic")
            rootTag["Height"] = nbt.TAG_Short(shape[1])
            rootTag["Length"] = nbt.TAG_Short(shape[2])
            rootTag["Width"] = nbt.TAG_Short(shape[0])

            rootTag["Entities"] = nbt.TAG_List()
            rootTag["TileEntities"] = nbt.TAG_List()
            rootTag["Materials"] = nbt.TAG_String(self.blocktypes.name)

            self._Blocks = zeros((shape[1], shape[2], shape[0]), 'uint16')
            rootTag["Data"] = nbt.TAG_Byte_Array(zeros((shape[1], shape[2], shape[0]), uint8))

            rootTag["Biomes"] = nbt.TAG_Byte_Array(zeros((shape[2], shape[0]), uint8))

            self.rootTag = rootTag

            self.rootTag["BlockIDs"] = blockIDMapping(blocktypes)
            self.rootTag["ItemIDs"] = itemIDMapping(blocktypes)

        #expand blocks and data to chunk edges
        h16 = (self.Height + 15) & ~0xf
        l16 = (self.Length + 15) & ~0xf
        w16 = (self.Width + 15) & ~0xf

        blocks = self._Blocks
        self._Blocks = numpy.zeros((h16, l16, w16), blocks.dtype)
        self._Blocks[:blocks.shape[0], :blocks.shape[1], :blocks.shape[2]] = blocks

        data = self.rootTag["Data"].value
        self.rootTag["Data"].value = numpy.zeros((h16, l16, w16), data.dtype)
        self.rootTag["Data"].value[:data.shape[0], :data.shape[1], :data.shape[2]] = data

        self.rootTag["Data"].value &= 0xF  # discard high bits

        self.entitiesByChunk = defaultdict(list)
        for tag in self.rootTag["Entities"]:
            ref = self.EntityRef(tag)
            pos = ref.Position
            cx, cy, cz = pos.chunkPos()
            self.entitiesByChunk[cx, cz].append(tag)

        self.tileEntitiesByChunk = defaultdict(list)
        for tag in self.rootTag["TileEntities"]:
            ref = self.TileEntityRef(tag)
            pos = ref.Position
            cx, cy, cz = pos.chunkPos()
            self.tileEntitiesByChunk[cx, cz].append(tag)
예제 #32
0
def readChunk(rev, cx, cz):
    return nbt.load(buf=rev.readChunkBytes(cx, cz, ""))
예제 #33
0
def load_file():
    global test_file
    test_file = nbt.load(buf=test_data)
예제 #34
0
def load_file():
    global test_file
    test_file = nbt.load(buf=test_data)
예제 #35
0
    def __init__(self,
                 shape=None,
                 filename=None,
                 blocktypes='Alpha',
                 readonly=False,
                 resume=False):
        """
        Creates an object which stores a section of a Minecraft world as an
        NBT structure. The order of the coordinates for the block arrays in
        the file is y,z,x. This is the same order used in Minecraft 1.4's
        chunk sections.

        :type shape: tuple
        :param shape: The shape of the schematic as (x, y, z)
        :type filename: basestring
        :param filename: Path to a file to load a saved schematic from.
        :type blocktypes: basestring or BlockTypeSet
        :param blocktypes: The name of a builtin blocktypes set (one of
            "Classic", "Alpha", "Pocket") to indicate allowable blocks. The default
            is Alpha. An instance of BlockTypeSet may be passed instead.
        :rtype: SchematicFileAdapter

        """
        if filename is None and shape is None:
            raise ValueError("shape or filename required to create %s" %
                             self.__class__.__name__)

        if filename:
            self.filename = filename
            if os.path.exists(filename):
                rootTag = nbt.load(filename)
            else:
                rootTag = None
        else:
            self.filename = None
            rootTag = None

        if blocktypes in blocktypes_named:
            self.blocktypes = blocktypes_named[blocktypes]
        else:
            assert (isinstance(blocktypes, BlockTypeSet))
            self.blocktypes = blocktypes

        if rootTag:
            self.rootTag = rootTag
            if "Materials" in rootTag:
                self.blocktypes = blocktypes_named[self.Materials]
            else:
                rootTag["Materials"] = nbt.TAG_String(self.blocktypes.name)

            w = self.rootTag["Width"].value
            l = self.rootTag["Length"].value
            h = self.rootTag["Height"].value

            assert self.rootTag["Blocks"].value.size == w * l * h
            self._Blocks = self.rootTag["Blocks"].value.astype(
                'uint16').reshape(h, l, w)  # _Blocks is y, z, x

            del self.rootTag["Blocks"]
            if "AddBlocks" in self.rootTag:
                # Use WorldEdit's "AddBlocks" array to load and store the 4 high bits of a block ID.
                # Unlike Minecraft's NibbleArrays, this array stores the first block's bits in the
                # 4 high bits of the first byte.

                size = (h * l * w)

                # If odd, add one to the size to make sure the adjacent slices line up.
                add = numpy.empty(size + (size & 1), 'uint16')

                # Fill the even bytes with data
                add[::2] = self.rootTag["AddBlocks"].value

                # Copy the low 4 bits to the odd bytes
                add[1::2] = add[::2] & 0xf

                # Shift the even bytes down
                add[::2] >>= 4

                # Shift every byte up before merging it with Blocks
                add <<= 8
                self._Blocks |= add[:size].reshape(h, l, w)
                del self.rootTag["AddBlocks"]

            self.rootTag["Data"].value = self.rootTag["Data"].value.reshape(
                h, l, w)

            if "Biomes" in self.rootTag:
                self.rootTag["Biomes"].value.shape = (l, w)

        else:
            rootTag = nbt.TAG_Compound(name="Schematic")
            rootTag["Height"] = nbt.TAG_Short(shape[1])
            rootTag["Length"] = nbt.TAG_Short(shape[2])
            rootTag["Width"] = nbt.TAG_Short(shape[0])

            rootTag["Entities"] = nbt.TAG_List()
            rootTag["TileEntities"] = nbt.TAG_List()
            rootTag["Materials"] = nbt.TAG_String(self.blocktypes.name)

            self._Blocks = zeros((shape[1], shape[2], shape[0]), 'uint16')
            rootTag["Data"] = nbt.TAG_Byte_Array(
                zeros((shape[1], shape[2], shape[0]), uint8))

            rootTag["Biomes"] = nbt.TAG_Byte_Array(
                zeros((shape[2], shape[0]), uint8))

            self.rootTag = rootTag

        #expand blocks and data to chunk edges
        h16 = (self.Height + 15) & ~0xf
        l16 = (self.Length + 15) & ~0xf
        w16 = (self.Width + 15) & ~0xf

        blocks = self._Blocks
        self._Blocks = numpy.zeros((h16, l16, w16), blocks.dtype)
        self._Blocks[:blocks.shape[0], :blocks.shape[1], :blocks.
                     shape[2]] = blocks

        data = self.rootTag["Data"].value
        self.rootTag["Data"].value = numpy.zeros((h16, l16, w16), data.dtype)
        self.rootTag["Data"].value[:data.shape[0], :data.shape[1], :data.
                                   shape[2]] = data

        self.rootTag["Data"].value &= 0xF  # discard high bits

        self.Entities = [
            self.EntityRef(tag) for tag in self.rootTag["Entities"]
        ]
        self.TileEntities = [
            self.EntityRef(tag) for tag in self.rootTag["TileEntities"]
        ]
예제 #36
0
def testRevision(history):
    revA = history.createRevision()
    log.info("revA")
    # rev A @1 - touch chunk 1
    cx, cz = iter(revA.chunkPositions("")).next()
    chunk = readChunkTag(revA, cx, cz)

    old_tag = tag = nbt.load(buf=history.rootFolder.readChunkBytes(cx, cz, ""))

    assert readChunkTag(history.rootNode, cx, cz) == tag
    assert chunk == tag
    chunk["Level"]["test"] = nbt.TAG_String("test string")

    writeChunkTag(revA, cx, cz, chunk)

    tag = readChunkTag(revA, cx, cz)
    assert "test" in tag["Level"] and tag["Level"]["test"].value == "test string"

    revB = history.createRevision()
    log.info("revB")

    # rev B @2 - delete chunk 2
    tag = readChunkTag(revB, cx, cz)
    assert "test" in tag["Level"] and tag["Level"]["test"].value == "test string"

    revB.deleteChunk(cx+1, cz, "")
    assert not revB.containsChunk(cx+1, cz, "")

    revC = history.createRevision()
    log.info("revC")

    # rev C @3 - delete file
    assert not revC.containsChunk(cx+1, cz, "")
    revC.deleteFile("level.dat")

    assert not revC.containsFile("level.dat")

    changes = revC.getChanges()
    assert changes.chunks[""] == set()
    assert changes.files == {"level.dat"}

    tailRev = history.getRevision(0)

    history.writeAllChanges()

    # initial folder (rev idx 0) and following nodes replaced by reverse nodes
    # rev C @3 replaced by initial folder
    assert revC.invalid

    revC = history.getHead()
    assert tailRev is revC

    changes = revC.getChanges()
    assert changes.chunks[""] == set()
    assert changes.files == {"level.dat"}

    # rev D - create chunk 3
    revD = history.createRevision()
    log.info("revD")

    assert not revD.containsFile("level.dat")
    writeChunkTag(revD, 1000, 1000, old_tag)

    assert not history.rootFolder.containsFile("level.dat")
    assert not history.rootFolder.containsChunk(cx+1, cz, "")

    assert "test" in tag["Level"]
    tag = readChunkTag(history.rootFolder, cx, cz)
    assert tag != old_tag
    assert "test" in tag["Level"]

    # grab rev B
    revBagain = history.getRevision(2)
    assert "test" in readChunkTag(revBagain, cx, cz)["Level"]
    assert not revBagain.containsChunk(cx+1, cz, "")

    # rev B should be read only
    with pytest.raises(IOError):
        writeChunkTag(revBagain, cx, cz, old_tag)

    # check all changes so far
    allChanges = history.getRevisionChanges(0, revD)
    assert allChanges.chunks[""] == {(cx, cz), (cx+1, cz), (1000, 1000)}
    assert allChanges.files == {"level.dat"}

    # insert rev E after rev B
    # world folder is now at the end of an orphan chain at rev @2
    # orphaned revisions are read only and still valid, but do not appear in the history
    revE = history.createRevision(2)
    assert "test" in readChunkTag(revE, cx, cz)["Level"]
    assert not revE.containsChunk(cx+1, cz, "")

    history.close()
예제 #37
0
파일: nbt_test.py 프로젝트: mcedit/mcedit2
def testLoadUncompressed(temp_file):
    rootTag = nbt.load(temp_file.strpath)
예제 #38
0
def testLoadUncompressed(temp_file):
    rootTag = nbt.load(temp_file.strpath)
예제 #39
0
    def __init__(self,
                 shape=None,
                 filename=None,
                 blocktypes='Alpha',
                 readonly=False,
                 resume=False):
        """
        Creates an object which stores a section of a Minecraft world as an
        NBT structure. The order of the coordinates for the block arrays in
        the file is y,z,x. This is the same order used in Minecraft 1.4's
        chunk sections.

        :type shape: tuple
        :param shape: The shape of the schematic as (x, y, z)
        :type filename: basestring
        :param filename: Path to a file to load a saved schematic from.
        :type blocktypes: basestring or BlockTypeSet
        :param blocktypes: The name of a builtin blocktypes set (one of
            "Classic", "Alpha", "Pocket") to indicate allowable blocks. The default
            is Alpha. An instance of BlockTypeSet may be passed instead.
        :rtype: SchematicFileAdapter

        """
        self.EntityRef = PCEntityRef
        self.TileEntityRef = PCTileEntityRef

        if filename is None and shape is None:
            raise ValueError("shape or filename required to create %s" %
                             self.__class__.__name__)

        if filename:
            self.filename = filename
            if os.path.exists(filename):
                rootTag = nbt.load(filename)
            else:
                rootTag = None
        else:
            self.filename = None
            rootTag = None

        if blocktypes in blocktypeClassesByName:
            self.blocktypes = blocktypeClassesByName[blocktypes]()
        else:
            assert (isinstance(blocktypes, BlockTypeSet))
            self.blocktypes = blocktypes

        if rootTag:
            self.rootTag = rootTag
            if "Materials" in rootTag:
                self.blocktypes = blocktypeClassesByName[self.Materials]()
            else:
                rootTag["Materials"] = nbt.TAG_String(self.blocktypes.name)

            w = self.rootTag["Width"].value
            l = self.rootTag["Length"].value
            h = self.rootTag["Height"].value

            assert self.rootTag["Blocks"].value.size == w * l * h
            self._Blocks = self.rootTag["Blocks"].value.astype(
                'uint16').reshape(h, l, w)  # _Blocks is y, z, x

            del self.rootTag["Blocks"]
            if "AddBlocks" in self.rootTag:
                # Use WorldEdit's "AddBlocks" array to load and store the 4 high bits of a block ID.
                # Unlike Minecraft's NibbleArrays, this array stores the first block's bits in the
                # 4 high bits of the first byte.

                size = (h * l * w)

                # If odd, add one to the size to make sure the adjacent slices line up.
                add = numpy.empty(size + (size & 1), 'uint16')

                # Fill the even bytes with data
                add[::2] = self.rootTag["AddBlocks"].value

                # Copy the low 4 bits to the odd bytes
                add[1::2] = add[::2] & 0xf

                # Shift the even bytes down
                add[::2] >>= 4

                # Shift every byte up before merging it with Blocks
                add <<= 8
                self._Blocks |= add[:size].reshape(h, l, w)
                del self.rootTag["AddBlocks"]

            self.rootTag["Data"].value = self.rootTag["Data"].value.reshape(
                h, l, w)

            if "Biomes" in self.rootTag:
                self.rootTag["Biomes"].value.shape = (l, w)

            # If BlockIDs is present, it contains an ID->internalName mapping
            # from the source level's FML tag.

            if "BlockIDs" in self.rootTag:
                self.blocktypes.addBlockIDsFromSchematicTag(
                    self.rootTag["BlockIDs"])

            # If itemStackVersion is present, it was exported from MCEdit 2.0.
            # Its value is either 17 or 18, the values of the version constants.
            # ItemIDs will also be present.

            # If itemStackVersion is not present, this schematic was exported from
            # WorldEdit or MCEdit 1.0. The itemStackVersion cannot be determined
            # without searching the entities for an itemStack and checking
            # the type of its `id` tag. If no itemStacks are found, the
            # version defaults to 1.8 which does not need an ItemIDs tag.

            if "itemStackVersion" in self.rootTag:
                itemStackVersion = self.rootTag["itemStackVersion"].value
                if itemStackVersion not in (VERSION_1_7, VERSION_1_8):
                    raise LevelFormatError("Unknown item stack version %d" %
                                           itemStackVersion)
                if itemStackVersion == VERSION_1_7:
                    itemIDs = self.rootTag.get("ItemIDs")
                    if itemIDs is not None:
                        self.blocktypes.addItemIDsFromSchematicTag(itemIDs)

                self.blocktypes.itemStackVersion = itemStackVersion
            else:
                self.blocktypes.itemStackVersion = self.getItemStackVersionFromEntities(
                )

        else:
            rootTag = nbt.TAG_Compound(name="Schematic")
            rootTag["Height"] = nbt.TAG_Short(shape[1])
            rootTag["Length"] = nbt.TAG_Short(shape[2])
            rootTag["Width"] = nbt.TAG_Short(shape[0])

            rootTag["Entities"] = nbt.TAG_List()
            rootTag["TileEntities"] = nbt.TAG_List()
            rootTag["Materials"] = nbt.TAG_String(self.blocktypes.name)
            rootTag["itemStackVersion"] = nbt.TAG_Byte(
                self.blocktypes.itemStackVersion)

            self._Blocks = zeros((shape[1], shape[2], shape[0]), 'uint16')
            rootTag["Data"] = nbt.TAG_Byte_Array(
                zeros((shape[1], shape[2], shape[0]), uint8))

            rootTag["Biomes"] = nbt.TAG_Byte_Array(
                zeros((shape[2], shape[0]), uint8))

            self.rootTag = rootTag

            self.rootTag["BlockIDs"] = blockIDMapping(blocktypes)
            itemMapping = itemIDMapping(blocktypes)
            if itemMapping is not None:
                self.rootTag[
                    "ItemIDs"] = itemMapping  # Only present for Forge 1.7

        # Expand blocks and data to chunk edges
        h16 = (self.Height + 15) & ~0xf
        l16 = (self.Length + 15) & ~0xf
        w16 = (self.Width + 15) & ~0xf

        blocks = self._Blocks
        self._Blocks = numpy.zeros((h16, l16, w16), blocks.dtype)
        self._Blocks[:blocks.shape[0], :blocks.shape[1], :blocks.
                     shape[2]] = blocks

        data = self.rootTag["Data"].value
        self.rootTag["Data"].value = numpy.zeros((h16, l16, w16), data.dtype)
        self.rootTag["Data"].value[:data.shape[0], :data.shape[1], :data.
                                   shape[2]] = data

        self.rootTag["Data"].value &= 0xF  # discard high bits

        self.entitiesByChunk = defaultdict(list)
        for tag in self.rootTag["Entities"]:
            ref = self.EntityRef(tag)
            pos = ref.Position
            cx, cy, cz = pos.chunkPos()
            self.entitiesByChunk[cx, cz].append(tag)

        self.tileEntitiesByChunk = defaultdict(list)
        for tag in self.rootTag["TileEntities"]:
            ref = self.TileEntityRef(tag)
            pos = ref.Position
            cx, cy, cz = pos.chunkPos()
            self.tileEntitiesByChunk[cx, cz].append(tag)
예제 #40
0
 def testLoadUncompressed(self):
     rootTag = nbt.load("test_files/uncompressed.nbt")
예제 #41
0
 def testLoadUncompressed(self):
     rootTag = nbt.load(TempFile("uncompressed.nbt"))