示例#1
0
    def _create(
        self,
        overwrite: bool,
        bounds: Union[SelectionGroup,
                      Dict[Dimension, Optional[SelectionGroup]], None] = None,
        **kwargs,
    ):
        if os.path.isdir(self.path):
            if overwrite:
                shutil.rmtree(self.path)
            else:
                raise ObjectWriteError(
                    f"A world already exists at the path {self.path}")

        version = self.translation_manager.get_version(
            self.platform, self.version).version_number
        self._version = version + (0, ) * (5 - len(version))

        self.root_tag = root = nbt.TAG_Compound()
        root["StorageVersion"] = nbt.TAG_Int(8)
        root["lastOpenedWithVersion"] = nbt.TAG_List(
            [nbt.TAG_Int(i) for i in self._version])
        root["Generator"] = nbt.TAG_Int(1)
        root["LastPlayed"] = nbt.TAG_Long(int(time.time()))
        root["LevelName"] = nbt.TAG_String("World Created By Amulet")

        os.makedirs(self.path, exist_ok=True)
        self.root_tag.save(os.path.join(self.path, "level.dat"))

        db = LevelDB(os.path.join(self.path, "db"), True)
        db.close()

        self._reload_world()
示例#2
0
    def test_put(self):
        self._clear_db()
        db = LevelDB(DB_PATH, True)

        db.putBatch(incr_db)

        for k, v in num_db.items():
            db.put(k, v)

        self.assertEqual(dict(db.iterate()), full_db)
        # TODO: Uncomment this when the new db wrapper is fixed
        # self.assertEqual(dict(db.items()), full_db)
        self.assertEqual(set(db.keys()), full_db.keys())
        self.assertEqual(set(db), full_db.keys())

        db.close()
示例#3
0
    def __init__(self, directory: str):
        self._directory = directory
        self._db = LevelDB(os.path.join(self._directory, "db"))
        # self._levels format Dict[level, Dict[Tuple[cx, cz], List[Tuple[full_key, key_extension]]]]
        self._levels: Dict[InternalDimension, Set[ChunkCoordinates]] = {}
        self._dimension_name_map: Dict["Dimension", InternalDimension] = {}
        self._batch_temp: Dict[bytes, Union[bytes, None]] = {}

        self.register_dimension(None, "overworld")
        self.register_dimension(1, "nether")
        self.register_dimension(2, "end")

        for key in self._db.keys():
            if 9 <= len(key) <= 10 and key[8] == 118:
                self._add_chunk(key)

            elif 13 <= len(key) <= 14 and key[12] == 118:
                self._add_chunk(key, has_level=True)
示例#4
0
    def __init__(self, directory: str):
        self._directory = directory
        self._db = LevelDB(os.path.join(self._directory, "db"))
        # self._levels format Dict[level, Dict[Tuple[cx, cz], List[Tuple[full_key, key_extension]]]]
        self._levels: Dict[InternalDimension, Set[ChunkCoordinates]] = {}
        self._dimension_name_map: Dict["Dimension", InternalDimension] = {}
        self._batch_temp: Dict[bytes, Union[bytes, None]] = {}

        self.register_dimension(0, OVERWORLD)
        self.register_dimension(1, THE_NETHER)
        self.register_dimension(2, THE_END)

        for key in self._db.keys():
            if 9 <= len(key) <= 10 and key[8] in [44, 118]:  # "," "v"
                self._add_chunk(key)

            elif 13 <= len(key) <= 14 and key[12] in [44, 118]:  # "," "v"
                self._add_chunk(key, has_level=True)
示例#5
0
    def test_delete(self):
        self._clear_db()
        db = LevelDB(DB_PATH, True)

        self.assertFalse(b"test_key3" in db)

        db.put(b"test_key3", b"test")

        self.assertTrue(b"test_key3" in db)

        db.delete(b"test_key3")

        self.assertFalse(b"test_key3" in db)

        db.close()
示例#6
0
    def test_contains(self):
        self._clear_db()
        db = LevelDB(DB_PATH, True)

        self.assertFalse(b"test_key2" in db)

        db.put(b"test_key2", b"test")

        self.assertTrue(b"test_key2" in db)

        db.close()
示例#7
0
def get_cache_db() -> LevelDB:
    global _cache_db
    if _cache_db is None:
        _cache_db = LevelDB(_path, True)
        atexit.register(_clear_db)
    return _cache_db
示例#8
0
class LevelDBLevelManager:
    # tag_ids = {45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 118}

    def __init__(self, directory: str):
        self._directory = directory
        self._db = LevelDB(os.path.join(self._directory, "db"))
        # self._levels format Dict[level, Dict[Tuple[cx, cz], List[Tuple[full_key, key_extension]]]]
        self._levels: Dict[InternalDimension, Set[ChunkCoordinates]] = {}
        self._dimension_name_map: Dict["Dimension", InternalDimension] = {}
        self._batch_temp: Dict[bytes, Union[bytes, None]] = {}

        self.register_dimension(None, "overworld")
        self.register_dimension(1, "nether")
        self.register_dimension(2, "end")

        for key in self._db.keys():
            if 9 <= len(key) <= 10 and key[8] == 118:
                self._add_chunk(key)

            elif 13 <= len(key) <= 14 and key[12] == 118:
                self._add_chunk(key, has_level=True)

    def save(self):
        batch = {}
        for key, val in self._batch_temp.items():
            if val is None:
                self._db.delete(key)
            else:
                batch[key] = val
        self._db.putBatch(batch)
        self._batch_temp.clear()

    def close(self):
        self._db.close()

    @property
    def dimensions(self) -> List["Dimension"]:
        """A list of all the levels contained in the world"""
        return list(self._dimension_name_map.keys())

    def register_dimension(
        self,
        dimension_internal: InternalDimension,
        dimension_name: Optional["Dimension"] = None,
    ):
        """
        Register a new dimension.
        :param dimension_internal: The internal representation of the dimension
        :param dimension_name: The name of the dimension shown to the user
        :return:
        """
        if dimension_name is None:
            dimension_name: "Dimension" = f"DIM{dimension_internal}"

        if (
            dimension_internal not in self._levels
            and dimension_name not in self._dimension_name_map
        ):
            self._levels[dimension_internal] = set()
            self._dimension_name_map[dimension_name] = dimension_internal

    def _get_internal_dimension(self, dimension: "Dimension") -> InternalDimension:
        if dimension in self._dimension_name_map:
            return self._dimension_name_map[dimension]
        else:
            raise LevelDoesNotExist(dimension)

    def all_chunk_coords(self, dimension: "Dimension") -> Set[ChunkCoordinates]:
        internal_dimension = self._get_internal_dimension(dimension)
        if internal_dimension in self._levels:
            return self._levels[internal_dimension]
        else:
            return set()

    def _add_chunk(self, key_: bytes, has_level: bool = False):
        if has_level:
            cx, cz, level = struct.unpack("<iii", key_[:12])
        else:
            cx, cz = struct.unpack("<ii", key_[:8])
            level = None
        if level not in self._levels:
            self.register_dimension(level)
        self._levels[level].add((cx, cz))

    def get_chunk_data(
        self, cx: int, cz: int, dimension: "Dimension"
    ) -> Dict[bytes, bytes]:
        """Get a dictionary of chunk key extension in bytes to the raw data in the key.
        chunk key extension are the character(s) after <cx><cz>[level] in the key
        Will raise ChunkDoesNotExist if the chunk does not exist
        """
        iter_start = struct.pack("<ii", cx, cz)
        iter_end = iter_start + b"\xff"
        internal_dimension = self._get_internal_dimension(dimension)
        if (
            internal_dimension in self._levels
            and (cx, cz) in self._levels[internal_dimension]
        ):
            chunk_data = {}
            for key, val in self._db.iterate(iter_start, iter_end):
                if internal_dimension is None:
                    if 9 <= len(key) <= 10:
                        key_extension = key[8:]
                    else:
                        continue
                else:
                    if (
                        13 <= len(key) <= 14
                        and struct.unpack("<i", key[8:12])[0] == internal_dimension
                    ):
                        key_extension = key[12:]
                    else:
                        continue
                chunk_data[key_extension] = val
            return chunk_data
        else:
            raise ChunkDoesNotExist

    def put_chunk_data(
        self, cx: int, cz: int, data: Dict[bytes, bytes], dimension: "Dimension"
    ):
        """pass data to the region file class"""
        # get the region key
        internal_dimension = self._get_internal_dimension(dimension)
        self._levels[internal_dimension].add((cx, cz))
        if internal_dimension is None:
            key_prefix = struct.pack("<ii", cx, cz)
        else:
            key_prefix = struct.pack("<iii", cx, cz, internal_dimension)
        for key, val in data.items():
            self._batch_temp[key_prefix + key] = val

    def delete_chunk(self, cx: int, cz: int, dimension: "Dimension"):
        if dimension in self._dimension_name_map:
            internal_dimension = self._dimension_name_map[dimension]
            if (
                internal_dimension in self._levels
                and (cx, cz) in self._levels[internal_dimension]
            ):
                chunk_data = self.get_chunk_data(cx, cz, dimension)
                self._levels[internal_dimension].remove((cx, cz))
                for key in chunk_data.keys():
                    if internal_dimension is None:
                        key_prefix = struct.pack("<ii", cx, cz)
                    else:
                        key_prefix = struct.pack("<iii", cx, cz, internal_dimension)

                    self._batch_temp[key_prefix + key] = None
示例#9
0
# A cache for objects implemented using leveldb for speed.
import os
import shutil
import atexit

from amulet.libs.leveldb import LevelDB
from amulet.api.paths import get_temp_dir
from amulet import log

_path = os.path.join(get_temp_dir("cache"), "db")

CacheDB = LevelDB(_path, True)


def clear_db():
    log.info("Removing cache.")
    CacheDB.close(compact=False)
    shutil.rmtree(_path)


atexit.register(clear_db)
示例#10
0
    def test_read_write(self):
        self._clear_db()
        db = LevelDB(DB_PATH, True)

        with self.assertRaises(KeyError):
            db.get(b"random_key")

        key1 = b"key"
        value1 = b"value"
        db.put(key1, value1)
        self.assertEqual(db.get(key1), value1)

        key2 = key1 * 1000
        value2 = value1 * 1000
        db.put(key2, value2)
        self.assertEqual(db.get(key2), value2)

        db.close()
示例#11
0
 def test_create_fail(self):
     self._clear_db()
     with self.assertRaises(LevelDBException):
         db = LevelDB(DB_PATH)
示例#12
0
 def test_create_ldb(self):
     self._clear_db()
     db = LevelDB(DB_PATH, True)
     db.close()