def load_level(path: str) -> Union[World, Structure]: """ Load and return a :class:`World` or :class:`Structure` class exposing the data at ``path`` Calls :func:`load_format` to try and find a :class:`FormatWrapper` that can open the data. If one is found it will wrap it with either a :class:`World` or :class:`Structure` class and return it. :param path: The file path to a file or directory for the object to be loaded. :return: A World or Structure class instance containing the data. :raises: LoaderNoneMatched: If no loader could be found that can open the data at path. Exception: Other errors. """ log.info(f"Loading level {path}") format_wrapper = load_format(path) if isinstance(format_wrapper, WorldFormatWrapper): return World(path, format_wrapper) elif isinstance(format_wrapper, StructureFormatWrapper): return Structure(path, format_wrapper) else: raise Exception( f"FormatWrapper of type {format_wrapper.__class__.__name__} is not supported. Report this to a developer." )
def load_world(directory: str, _format: str = None, forced: bool = False) -> World: """ Creates a Format loader from the given inputs and wraps it in a World class :param directory: :param _format: :param forced: :return: """ log.info(f"Loading world {directory}") return World(directory, load_format(directory, _format, forced))
def load_level(path: str) -> Union[World, Structure]: """ Creates a Format loader from the given inputs and wraps it in a World class :param path: :return: """ log.info(f"Loading level {path}") format_wrapper = load_format(path) if isinstance(format_wrapper, WorldFormatWrapper): return World(path, format_wrapper) elif isinstance(format_wrapper, StructureFormatWrapper): return Structure(path, format_wrapper) else: raise Exception
def is_valid(directory) -> bool: """ Returns whether this format is able to load a given world. :param directory: The path to the root of the world to load. :return: True if the world can be loaded by this format, False otherwise. """ if not check_all_exist(directory, "level.dat"): return False try: level_dat_root = load_leveldat(directory) except: return False if "Data" not in level_dat_root: return False if "FML" in level_dat_root: return True log.info("Forge support is experimental. Use at your own risk.") return True
def _clear_db(): if _cache_db is not None: log.info("Removing cache.") _cache_db.close(compact=False) shutil.rmtree(os.path.dirname(_path))
def save_iter( self, wrapper: api_wrapper.FormatWrapper = None ) -> Generator[Tuple[int, int], None, None]: """ Save the level to the given :class:`FormatWrapper`. This will yield the progress which can be used to update a UI. :param wrapper: If specified will save the data to this wrapper instead of self.level_wrapper :return: A generator of the number of chunks completed and the total number of chunks """ # TODO change the yield type to match OperationReturnType chunk_index = 0 changed_chunks = list(self._chunks.changed_chunks()) chunk_count = len(changed_chunks) if wrapper is None: wrapper = self.level_wrapper output_dimension_map = wrapper.dimensions # perhaps make this check if the directory is the same rather than if the class is the same save_as = wrapper is not self.level_wrapper if save_as: # The input wrapper is not the same as the loading wrapper (save-as) # iterate through every chunk in the input level and save them to the wrapper log.info( f"Converting level {self.level_wrapper.path} to level {wrapper.path}" ) wrapper.translation_manager = ( self.level_wrapper.translation_manager ) # TODO: this might cause issues in the future for dimension in self.level_wrapper.dimensions: chunk_count += len( list(self.level_wrapper.all_chunk_coords(dimension))) for dimension in self.level_wrapper.dimensions: try: if dimension not in output_dimension_map: continue for cx, cz in self.level_wrapper.all_chunk_coords( dimension): log.info(f"Converting chunk {dimension} {cx}, {cz}") try: chunk = self.level_wrapper.load_chunk( cx, cz, dimension) wrapper.commit_chunk(chunk, dimension) except ChunkLoadError: log.info(f"Error loading chunk {cx} {cz}", exc_info=True) chunk_index += 1 yield chunk_index, chunk_count if not chunk_index % 10000: wrapper.save() self.level_wrapper.unload() wrapper.unload() except DimensionDoesNotExist: continue for dimension, cx, cz in changed_chunks: if dimension not in output_dimension_map: continue try: chunk = self.get_chunk(cx, cz, dimension) except ChunkDoesNotExist: wrapper.delete_chunk(cx, cz, dimension) except ChunkLoadError: pass else: wrapper.commit_chunk(chunk, dimension) chunk.changed = False chunk_index += 1 yield chunk_index, chunk_count if not chunk_index % 10000: wrapper.save() wrapper.unload() self.history_manager.mark_saved() log.info(f"Saving changes to level {wrapper.path}") wrapper.save() log.info(f"Finished saving changes to level {wrapper.path}")
def clear_db(): log.info("Removing cache.") CacheDB.close(compact=False) shutil.rmtree(_path)
def save_iter( self, wrapper: "WorldFormatWrapper" = None ) -> Generator[Tuple[int, int], None, None]: """Save the world using the given wrapper. Leave as None to save back to the input wrapper.""" chunk_index = 0 if self._needs_undo_point or any( chunk is not None and chunk.changed for chunk in self._chunk_cache.values() ): self.create_undo_point() self._needs_undo_point = False changed_chunks = list(self._chunk_history_manager.changed_chunks()) chunk_count = len(changed_chunks) if wrapper is None: wrapper = self._world_wrapper output_dimension_map = wrapper.dimensions # perhaps make this check if the directory is the same rather than if the class is the same save_as = wrapper is not self._world_wrapper if save_as: # The input wrapper is not the same as the loading wrapper (save-as) # iterate through every chunk in the input world and save them to the wrapper log.info( f"Converting world {self._world_wrapper.path} to world {wrapper.path}" ) wrapper.translation_manager = ( self._world_wrapper.translation_manager ) # TODO: this might cause issues in the future for dimension in self._world_wrapper.dimensions: chunk_count += len( list(self._world_wrapper.all_chunk_coords(dimension)) ) for dimension in self._world_wrapper.dimensions: try: if dimension not in output_dimension_map: continue for cx, cz in self._world_wrapper.all_chunk_coords(dimension): log.info(f"Converting chunk {dimension} {cx}, {cz}") try: chunk = self._world_wrapper.load_chunk(cx, cz, dimension) wrapper.commit_chunk(chunk, dimension) except ChunkLoadError: log.info(f"Error loading chunk {cx} {cz}", exc_info=True) chunk_index += 1 yield chunk_index, chunk_count if not chunk_index % 10000: wrapper.save() self._world_wrapper.unload() wrapper.unload() except LevelDoesNotExist: continue for dimension, cx, cz in changed_chunks: if dimension not in output_dimension_map: continue chunk = self._chunk_history_manager.get_current( dimension, cx, cz, self._block_palette, self._biome_palette ) if chunk is None: wrapper.delete_chunk(cx, cz, dimension) else: wrapper.commit_chunk(chunk, dimension) chunk_index += 1 yield chunk_index, chunk_count if not chunk_index % 10000: wrapper.save() wrapper.unload() self._chunk_history_manager.mark_saved() log.info(f"Saving changes to world {wrapper.path}") wrapper.save() log.info(f"Finished saving changes to world {wrapper.path}")
def world_path(self) -> str: """The path to the world directory""" log.info( f"Format.world_path is depreciated. Please used Format.path{traceback.format_exc()}" ) return self._path