diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 794f34b..eaeff99 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,7 +25,7 @@ jobs: - run: unzip assets/vanilla_structures.zip -d structures - - run: python __main__.py + - run: python cli/__main__.py test_regolith_filter: runs-on: ubuntu-latest @@ -65,4 +65,4 @@ jobs: - name: Compiles shell: bash working-directory: './regolith-test-project' - run: regolith run default \ No newline at end of file + run: regolith run default diff --git a/__main__.py b/__main__.py deleted file mode 100644 index efa8227..0000000 --- a/__main__.py +++ /dev/null @@ -1,83 +0,0 @@ -from rich.progress import Progress, TextColumn, BarColumn, DownloadColumn, TransferSpeedColumn, TextColumn, TimeRemainingColumn -import pathlib -import gzip -import pynbt -from time import time -from java_structures import javaToBedrock -from os import listdir, makedirs, mkdir -from os.path import isfile, join, isdir -import threading - -structurePath = 'structures' ## path of structures - -def convert(file): - mcstructureFile = file.replace(pathlib.Path(file).suffix, '') + '.mcstructure' - print(f"Converting '{file}' to '{mcstructureFile}'") - - print("Reading " + file) - io = gzip.open(file, mode="rb+") - - print("Parsing " + file) - startTime = time() - nbt = pynbt.NBTFile(io) - print(f"Loaded {file} in {round((time() - startTime) * 1000, 2)} ms") - - mcstructure, size = javaToBedrock(nbt) - startTime = time() - - with open(mcstructureFile, 'wb') as io: - columns = ( - TextColumn("[progress.description]{task.description}"), - BarColumn(), - DownloadColumn(), - TransferSpeedColumn(), - TextColumn("eta"), - TimeRemainingColumn(), - ) - with Progress(*columns, refresh_per_second=30) as progress: - task = progress.add_task("[cyan]Writing mcstructure...", total=size) - global completed - global timer - completed = 0 - - def write (data: bytes): - global completed - io.write(data) - completed += len(data) - - def renderWriteProgress(): - global timer - progress.update(task, completed=completed) - timer = threading.Timer(1 / 30, renderWriteProgress) - timer.start() - - renderWriteProgress() - mcstructure.save(write, True) - - # stop progress as mcstructure file has been saved - timer.cancel() - progress.update(task, completed=completed, total=completed) - progress.stop() - - print(f"Wrote {mcstructureFile} in {round((time() - startTime) * 1000, 2)} ms\n") - -def get_nbtFiles (dirpath: str) -> list[str]: - files = [] - for f in listdir(dirpath): - path = join(dirpath, f) - if isfile(path) and f.endswith(".nbt"): - files.append(path) - elif isdir(path): - for file in get_nbtFiles(path): - files.append(file) - return files - -if __name__ == "__main__": # pragma: no coverage - makedirs(structurePath, exist_ok=True) - nbtFiles = get_nbtFiles(structurePath) - - if len(nbtFiles) == 0: - print(f"There are 0 .nbt files in '{structurePath}' folder.") - else: - for file in nbtFiles: - convert(file=file) \ No newline at end of file diff --git a/cli/__main__.py b/cli/__main__.py new file mode 100644 index 0000000..b5dffa4 --- /dev/null +++ b/cli/__main__.py @@ -0,0 +1,124 @@ +from rich.progress import Progress, TextColumn, BarColumn, DownloadColumn, TransferSpeedColumn, TimeRemainingColumn +import pathlib +import gzip +import pynbt +from time import time +from java_structures import javaToBedrock +from os import listdir, makedirs, remove +from os.path import isfile, join, isdir +import threading +import json +import re + +structurePath = 'structures' # Path of structures +settings_path = './settings.json' + +def get_block_mapping(structure_id: str, settings: dict): + for entry in settings.get("block_mapping", []): + pattern = entry["structure_id"] + if "*" in pattern: + regex = re.compile("^" + re.escape(pattern).replace("\\*", ".*") + "$") + if regex.match(structure_id): + return entry["mapping"] + elif pattern == structure_id: + return entry["mapping"] + return {} + +def get_structure_id(file_path: str) -> str: + return pathlib.Path(file_path).stem + +def convert(file: str, settings: dict): + mcstructureFile = file.replace(pathlib.Path(file).suffix, '') + '.mcstructure' + structure_id = get_structure_id(file) + print(f"Converting '{file}' to '{mcstructureFile}' with structure ID '{structure_id}'") + + print("Reading " + file) + io = gzip.open(file, mode="rb+") + + print("Parsing " + file) + startTime = time() + nbt = pynbt.NBTFile(io) + print(f"Loaded {file} in {round((time() - startTime) * 1000, 2)} ms") + + # Get custom mapping + block_mapping = get_block_mapping(structure_id, settings) + + mcstructure, size = javaToBedrock(nbt, structure_id, block_mapping) + startTime = time() + + with open(mcstructureFile, 'wb') as io: + columns = ( + TextColumn("[progress.description]{task.description}"), + BarColumn(), + DownloadColumn(), + TransferSpeedColumn(), + TextColumn("eta"), + TimeRemainingColumn(), + ) + with Progress(*columns, refresh_per_second=30) as progress: + task = progress.add_task("[cyan]Writing mcstructure...", total=size) + global completed + global timer + completed = 0 + + def write(data: bytes): + global completed + io.write(data) + completed += len(data) + + def renderWriteProgress(): + global timer + progress.update(task, completed=completed) + timer = threading.Timer(1 / 30, renderWriteProgress) + timer.start() + + renderWriteProgress() + mcstructure.save(write, True) + + # stop progress as mcstructure file has been saved + timer.cancel() + progress.update(task, completed=completed, total=completed) + progress.stop() + + print(f"Wrote {mcstructureFile} in {round((time() - startTime) * 1000, 2)} ms\n") + +def get_nbtFiles(dirpath: str) -> list[str]: + files = [] + for f in listdir(dirpath): + path = join(dirpath, f) + if isfile(path) and f.endswith(".nbt"): + files.append(path) + elif isdir(path): + for file in get_nbtFiles(path): + files.append(file) + return files + +## ----------- +## MAIN +## ----------- +def main(): + # Load settings file + if isfile(settings_path): + f = open(settings_path, "r") + json_file = f.read() + settings = json.loads(json_file) + else: + settings = {} + + # Convert structures + makedirs(structurePath, exist_ok=True) + nbtFiles = get_nbtFiles(structurePath) + if len(nbtFiles) == 0: + print(f"There are 0 .nbt files in '{structurePath}' folder.") + else: + for file in nbtFiles: + convert(file, settings) + + # Delete .nbt files after processing + for file in nbtFiles: + try: + remove(file) + except Exception as e: + print(f"Error deleting file {file}: {e}") + +main() \ No newline at end of file diff --git a/java_structures.py b/cli/java_structures.py similarity index 92% rename from java_structures.py rename to cli/java_structures.py index b607c87..03f1ef0 100644 --- a/java_structures.py +++ b/cli/java_structures.py @@ -1,4 +1,5 @@ from time import time +import os import json, re from pynbt import ( NBTFile, @@ -12,10 +13,27 @@ ) from progress_bar import track -blocksj2b = json.loads(open("./assets/blocksJ2B.json", "r").read()) -bedsj2b = json.loads(open("./assets/bedsJ2B.json", "r").read()) -skullj2b = json.loads(open("./assets/skullJ2B.json", "r").read()) -blockstates = json.loads(open("./assets/blockstates.json", "r").read()) +assets_dir = os.path.abspath('./assets/') + +# Construct full paths to your files +blocksj2b_path = os.path.join(assets_dir, "blocksJ2B.json") +bedsj2b_path = os.path.join(assets_dir, "bedsJ2B.json") +skullj2b_path = os.path.join(assets_dir, "skullJ2B.json") +blockstates_path = os.path.join(assets_dir, "blockstates.json") + +# Load JSON files +with open(blocksj2b_path, "r") as f: + blocksj2b = json.load(f) + +with open(bedsj2b_path, "r") as f: + bedsj2b = json.load(f) + +with open(skullj2b_path, "r") as f: + skullj2b = json.load(f) + +with open(blockstates_path, "r") as f: + blockstates = json.load(f) + MC_VERSION = "1.21.0.03" def checkEntry(blocks, entry): @@ -106,8 +124,8 @@ def getDynamicBlockIdentifier(blockobject): def getBlockObject(dynamicblockid: str, format="bedrock"): baseidentifier = dynamicblockid.split("[")[0] - properties = dynamicblockid.split("[")[1].replace("]", "") - if properties.split(",")[0] != "": + properties = dynamicblockid.split("[")[1].replace("]", "") if "[" in dynamicblockid else "" + if properties: properties = properties.split(",") else: properties = [] @@ -150,7 +168,7 @@ def getBlockObject(dynamicblockid: str, format="bedrock"): print(f"Warning: State name {statename} with value {statevalue} is not a valid state for {baseidentifier}. Valid values: {validValues}.") return object -def javaToBedrock(structure: NBTFile): +def javaToBedrock(structure: NBTFile, structure_id: str, block_mapping: dict): blocks: TAG_List = structure["blocks"].value palette: TAG_List = structure["palette"].value oldsize: TAG_List = structure["size"].value @@ -191,13 +209,25 @@ def javaToBedrock(structure: NBTFile): # applying palette startTime = time() for i in track(sequence=palette, description="[green]Applying Palette"): - # Using prismarine-data, find the java edition ID blockId = getDynamicBlockIdentifier(i) - if not blockId in blocksj2b: - newPalette.append(getBlockObject("minecraft:air[]", "bedrock")) - else: + baseBlockId = blockId.split("[")[0] # Get the base block identifier + + # Check for exact match in custom mapping (with brackets) + if blockId in block_mapping: + mapped_id = block_mapping[blockId] + newPalette.append(getBlockObject(mapped_id, "bedrock")) + # Check for base identifier match in custom mapping (ignore brackets) + elif baseBlockId in block_mapping: + mapped_id = block_mapping[baseBlockId] + newPalette.append(getBlockObject(mapped_id, "bedrock")) + # Fallback to default mapping + elif blockId in blocksj2b: javaId = blocksj2b[blockId] newPalette.append(getBlockObject(javaId, "bedrock")) + # Fallback to air if no mapping found + else: + newPalette.append(getBlockObject("minecraft:air[]", "bedrock")) + if applyWaterloggedBlock: newPalette.append(getBlockObject("minecraft:water[liquid_depth=0]", "bedrock")) print(f"Finished applying palette in {round((time() - startTime) * 1000, 2)} ms") diff --git a/progress_bar.py b/cli/progress_bar.py similarity index 100% rename from progress_bar.py rename to cli/progress_bar.py diff --git a/pynbt.py b/cli/pynbt.py similarity index 100% rename from pynbt.py rename to cli/pynbt.py diff --git a/nbt-to-mcstructure.spec b/nbt-to-mcstructure.spec index 7203323..1eab402 100644 --- a/nbt-to-mcstructure.spec +++ b/nbt-to-mcstructure.spec @@ -5,7 +5,7 @@ block_cipher = None a = Analysis( - ['__main__.py'], + ['cli/__main__.py'], pathex=[], binaries=[], datas=[],