diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4271c76ba..63c06056c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,6 +15,33 @@ on: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: + # This workflow contains a single job called "lint" + lint: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + + # Install python + - uses: actions/setup-python@v3 + with: + python-version: '3.x' + + # Install isort and black for linting + - name: Install isort and black + run: pip install isort black + + # Run isort + - name: Run isort + run: isort --check-only --profile black . + + # Run black + - name: Run black + run: black --check --color --diff . + # This workflow contains a single job called "build" build: # The type of runner that the job will run on diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1a69f8956..265d433de 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -56,6 +56,21 @@ For that last one, you'll need to specify the location of the server at `tools/s One more thing. While changing scripts, you can run the command `/reload` ingame to reload them, without having to exit and enter the world. +### Linting + +#### Python + +The `isort` and `black` packages found in the `requirements.txt` file are responsible for automatically formatting your Python code! + +Simply run these commands locally at the root of the repository to automatically format the Python code: +``` bash +# Sort package imports. +isort --profile black . + +# Auto-format Python code. +black . +``` + ## Translations Speak another language? Help the addon become more localized by going to the addon's [Crowdin page](https://crowdin.com/project/worldedit-for-bedrock). Choose a language you're good with, and start contributing in places that don't have a translation. \ No newline at end of file diff --git a/README.md b/README.md index 2af40a58a..1d7732669 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # WorldEdit: Bedrock Edition ![Build Workflow](https://github.com/SIsilicon/WorldEdit-BE/actions/workflows/main.yml/badge.svg) [![Documentation Status](https://readthedocs.org/projects/ansicolortags/badge/?version=latest)](http://worldedit-be-docs.readthedocs.io/?badge=latest) +[![Python code style: black](https://img.shields.io/badge/python%20code%20style-black-000000.svg)](https://github.com/psf/black) +[![Python Imports: isort](https://img.shields.io/badge/python%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/) [![Support me on Patreon](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3DSIsilicon%26type%3Dpatrons&style=flat)](https://patreon.com/SIsilicon) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=XXXJ5ETNT5PSN) [![Twitter Follow](https://img.shields.io/twitter/follow/iSiliconS?style=social)](https://twitter.com/iSiliconS) diff --git a/app/addons/gdnative_data/leveldb/build.py b/app/addons/gdnative_data/leveldb/build.py index 7c91430cc..f5f5dbd26 100644 --- a/app/addons/gdnative_data/leveldb/build.py +++ b/app/addons/gdnative_data/leveldb/build.py @@ -1,6 +1,5 @@ - -from distutils.command.build import build import os +from distutils.command.build import build from pathlib import Path @@ -10,15 +9,22 @@ def execute(args): target_file_path = args["target_file_path"] build_target = "Debug" if args["target"] == "debug" else "Release" - run_command(str(source_path / "build"), [ - "cmake", "--build", ".", "--target", "leveldb", "--config", build_target - ]) - + run_command( + str(source_path / "build"), + ["cmake", "--build", ".", "--target", "leveldb", "--config", build_target], + ) + if os.path.exists(target_file_path + ".exp"): os.remove(target_file_path + ".exp") if os.path.exists(target_file_path + ".lib"): os.remove(target_file_path + ".lib") - os.rename(source_path / f"build/{build_target}/leveldb.dll", target_file_path + ".dll") - os.rename(source_path / f"build/{build_target}/leveldb.exp", target_file_path + ".exp") - os.rename(source_path / f"build/{build_target}/leveldb.lib", target_file_path + ".lib") + os.rename( + source_path / f"build/{build_target}/leveldb.dll", target_file_path + ".dll" + ) + os.rename( + source_path / f"build/{build_target}/leveldb.exp", target_file_path + ".exp" + ) + os.rename( + source_path / f"build/{build_target}/leveldb.lib", target_file_path + ".lib" + ) diff --git a/app/addons/silicon.util.gdnative_helper/main_build.py b/app/addons/silicon.util.gdnative_helper/main_build.py index 130a8ea21..99a6d8bd8 100644 --- a/app/addons/silicon.util.gdnative_helper/main_build.py +++ b/app/addons/silicon.util.gdnative_helper/main_build.py @@ -1,36 +1,45 @@ -import os, sys, json +import json +import os +import sys from pathlib import Path sys.path.append(sys.argv[1]) from build_project import execute -with open("addons/silicon.util.gdnative_helper/build_config.json", 'r') as config: - args = json.load(config) - args["library_name"] = sys.argv[2] - args["target_file_path"] = sys.argv[3] - args["source_path"] = sys.argv[4] - args["library_extension"] = sys.argv[5] - args["platform"] = sys.argv[6] - args["arch"] = sys.argv[7] - args["target"] = sys.argv[8] - args["gd_settings_dir"] = str(Path(sys.argv[1], "../..").resolve()) +with open("addons/silicon.util.gdnative_helper/build_config.json", "r") as config: + args = json.load(config) + args["library_name"] = sys.argv[2] + args["target_file_path"] = sys.argv[3] + args["source_path"] = sys.argv[4] + args["library_extension"] = sys.argv[5] + args["platform"] = sys.argv[6] + args["arch"] = sys.argv[7] + args["target"] = sys.argv[8] + args["gd_settings_dir"] = str(Path(sys.argv[1], "../..").resolve()) - os.chdir(sys.argv[1]) + os.chdir(sys.argv[1]) - # Get older dll out of the way on Windows. - lib_name = "%s.%s" % (args["target_file_path"], args["library_extension"]) - try: - if os.path.exists(lib_name) and args["platform"] == "windows" and os.name == "nt": - # if os.path.exists(lib_name + ".old"): - # os.remove(lib_name + ".old") - os.replace(lib_name, lib_name + ".old") - except OSError as e: - raise OSError("Cannot delete Windows DLL at \"%s\"! Please delete it manually." % lib_name) + # Get older dll out of the way on Windows. + lib_name = "%s.%s" % (args["target_file_path"], args["library_extension"]) + try: + if ( + os.path.exists(lib_name) + and args["platform"] == "windows" + and os.name == "nt" + ): + # if os.path.exists(lib_name + ".old"): + # os.remove(lib_name + ".old") + os.replace(lib_name, lib_name + ".old") + except OSError as e: + raise OSError( + 'Cannot delete Windows DLL at "%s"! Please delete it manually.' % lib_name + ) - # Create directory for library files to reside - try: - os.makedirs(Path(args["target_file_path"]).parent) - except: pass + # Create directory for library files to reside + try: + os.makedirs(Path(args["target_file_path"]).parent) + except: + pass - execute(args) + execute(args) diff --git a/build.py b/build.py index f675e0585..6a2f58888 100644 --- a/build.py +++ b/build.py @@ -1,70 +1,133 @@ +import argparse +import os +import re +import shutil +import subprocess +import sys from pathlib import Path -import subprocess, sys, os, shutil -import argparse, re - -parser = argparse.ArgumentParser(description='Build and package the addon.') -parser.add_argument('--watch', '-w', choices=['stable', 'preview', 'server'], help='Whether to continually build and where to sync the project while editing it.') -parser.add_argument('--target', choices=['release', 'debug', 'server'], default='debug', help='Whether to build the addon in debug or release mode.') -parser.add_argument('--clean', '-c', action='store_true', help='Clean "BP/scripts" folder before building.') -parser.add_argument('--package-only', '-p', action='store_true', help='Only package what\'s already there.') + +parser = argparse.ArgumentParser(description="Build and package the addon.") +parser.add_argument( + "--watch", + "-w", + choices=["stable", "preview", "server"], + help="Whether to continually build and where to sync the project while editing it.", +) +parser.add_argument( + "--target", + choices=["release", "debug", "server"], + default="debug", + help="Whether to build the addon in debug or release mode.", +) +parser.add_argument( + "--clean", + "-c", + action="store_true", + help='Clean "BP/scripts" folder before building.', +) +parser.add_argument( + "--package-only", + "-p", + action="store_true", + help="Only package what's already there.", +) args = parser.parse_args() -build_pack_name = 'WorldEdit' +build_pack_name = "WorldEdit" + def handleError(err): - if err: exit(err) + if err: + exit(err) + def regExpSub(regEx, replace, file): - with open(file, 'r') as f: + with open(file, "r") as f: content = f.read() - contentNew = re.sub(regEx, replace, content, flags = re.M) - with open(file, 'w') as f: + contentNew = re.sub(regEx, replace, content, flags=re.M) + with open(file, "w") as f: f.write(contentNew) + if not args.package_only: # Check for input and output folder - if not os.path.isdir('src'): - sys.exit('The src folder does not exist in the current working directory!') - elif not os.path.isdir('BP/scripts'): - sys.exit('The output scripts folder does not exist in the current working directory!') + if not os.path.isdir("src"): + sys.exit("The src folder does not exist in the current working directory!") + elif not os.path.isdir("BP/scripts"): + sys.exit( + "The output scripts folder does not exist in the current working directory!" + ) # Clean script output folder if args.clean: - print('cleaning script output folder...') - folder = 'BP/scripts' + print("cleaning script output folder...") + folder = "BP/scripts" for filename in os.listdir(folder): file_path = os.path.join(folder, filename) try: - if file_path.endswith('.txt'): + if file_path.endswith(".txt"): continue if os.path.isfile(file_path) or os.path.islink(file_path): os.unlink(file_path) elif os.path.isdir(file_path): shutil.rmtree(file_path) except Exception as e: - print('Failed to delete %s. Reason: %s' % (file_path, e)) - + print("Failed to delete %s. Reason: %s" % (file_path, e)) + # Build manifests - handleError(subprocess.call([sys.executable, 'tools/process_manifest.py', f'--target={args.target}'], stdout=subprocess.DEVNULL)) + handleError( + subprocess.call( + [sys.executable, "tools/process_manifest.py", f"--target={args.target}"], + stdout=subprocess.DEVNULL, + ) + ) if args.watch: - print('syncing com.mojang folder...') - watch_target = 'server' if args.watch == 'server' else 'debug' - subprocess.call([sys.executable, 'tools/process_config.py', f'--target={watch_target}'], stdout=subprocess.DEVNULL) - subprocess.call([sys.executable, 'tools/sync2com-mojang.py', f'--dest={args.watch}'], stdout=subprocess.DEVNULL) + print("syncing com.mojang folder...") + watch_target = "server" if args.watch == "server" else "debug" + subprocess.call( + [sys.executable, "tools/process_config.py", f"--target={watch_target}"], + stdout=subprocess.DEVNULL, + ) + subprocess.call( + [sys.executable, "tools/sync2com-mojang.py", f"--dest={args.watch}"], + stdout=subprocess.DEVNULL, + ) - print('Watch mode: press control-C to stop.') - tsc = subprocess.Popen('tsc -w', shell=True) + print("Watch mode: press control-C to stop.") + tsc = subprocess.Popen("tsc -w", shell=True) # Remap absolute imports - remap_imports = subprocess.Popen([sys.executable, 'tools/remap_imports.py', '-w'], stdout=subprocess.DEVNULL) + remap_imports = subprocess.Popen( + [sys.executable, "tools/remap_imports.py", "-w"], stdout=subprocess.DEVNULL + ) # Convert po to lang files - po2lang = subprocess.Popen([sys.executable, 'tools/po2lang.py', '-w'], stdout=subprocess.DEVNULL) + po2lang = subprocess.Popen( + [sys.executable, "tools/po2lang.py", "-w"], stdout=subprocess.DEVNULL + ) # Build settings file - process_cfg = subprocess.Popen([sys.executable, 'tools/process_config.py', '-w', f'--target={watch_target}'], stdout=subprocess.DEVNULL) + process_cfg = subprocess.Popen( + [ + sys.executable, + "tools/process_config.py", + "-w", + f"--target={watch_target}", + ], + stdout=subprocess.DEVNULL, + ) # Sync to com.mojang - sync_mojang = subprocess.Popen([sys.executable, 'tools/sync2com-mojang.py', '-w', '--init=False', f'--dest={args.watch}'], stdout=subprocess.DEVNULL) - + sync_mojang = subprocess.Popen( + [ + sys.executable, + "tools/sync2com-mojang.py", + "-w", + "--init=False", + f"--dest={args.watch}", + ], + stdout=subprocess.DEVNULL, + ) + from time import sleep + try: while True: sleep(1) @@ -76,48 +139,64 @@ def regExpSub(regEx, replace, file): sync_mojang.kill() exit() else: - print('building scripts...') - handleError(subprocess.call(['tsc', '-b'], shell=True)) + print("building scripts...") + handleError(subprocess.call(["tsc", "-b"], shell=True)) # Remap absolute imports - handleError(subprocess.call([sys.executable, 'tools/remap_imports.py'])) + handleError(subprocess.call([sys.executable, "tools/remap_imports.py"])) # Convert po to lang files - handleError(subprocess.call([sys.executable, 'tools/po2lang.py'])) + handleError(subprocess.call([sys.executable, "tools/po2lang.py"])) # Build manifests - handleError(subprocess.call([sys.executable, 'tools/process_manifest.py', f'--target={args.target}'])) + handleError( + subprocess.call( + [sys.executable, "tools/process_manifest.py", f"--target={args.target}"] + ) + ) # Build settings files - handleError(subprocess.call([sys.executable, 'tools/process_config.py', f'--target={args.target}'])) - if args.target == 'server': - handleError(subprocess.call([sys.executable, 'tools/process_config.py', '--generateConfigJSON'])) - - -if not os.path.isdir('builds'): - os.makedirs('builds') - -if os.path.exists(f'builds/{build_pack_name}BP'): - shutil.rmtree(f'builds/{build_pack_name}BP') -if os.path.exists(f'builds/{build_pack_name}RP'): - shutil.rmtree(f'builds/{build_pack_name}RP') -try: shutil.copytree('BP', f'builds/{build_pack_name}BP') -except: pass -try: shutil.copytree('RP', f'builds/{build_pack_name}RP') -except: pass - -if args.target != 'debug': - from zipfile import ZipFile; - + handleError( + subprocess.call( + [sys.executable, "tools/process_config.py", f"--target={args.target}"] + ) + ) + if args.target == "server": + handleError( + subprocess.call( + [sys.executable, "tools/process_config.py", "--generateConfigJSON"] + ) + ) + + +if not os.path.isdir("builds"): + os.makedirs("builds") + +if os.path.exists(f"builds/{build_pack_name}BP"): + shutil.rmtree(f"builds/{build_pack_name}BP") +if os.path.exists(f"builds/{build_pack_name}RP"): + shutil.rmtree(f"builds/{build_pack_name}RP") +try: + shutil.copytree("BP", f"builds/{build_pack_name}BP") +except: + pass +try: + shutil.copytree("RP", f"builds/{build_pack_name}RP") +except: + pass + +if args.target != "debug": + from zipfile import ZipFile + def zipWriteDir(zip, dirname, arcname): for folderName, _, filenames in os.walk(dirname): for filename in filenames: filePath = os.path.join(folderName, filename) zip.write(filePath, arcname / Path(filePath).relative_to(dirname)) - - if args.target == 'release': - with ZipFile(f'builds/{build_pack_name}.mcaddon', 'w') as zip: - zipWriteDir(zip, f'builds/{build_pack_name}BP', f'{build_pack_name}BP') - zipWriteDir(zip, f'builds/{build_pack_name}RP', f'{build_pack_name}RP') - elif args.target == 'server': - with ZipFile(f'builds/{build_pack_name}.server.zip', 'w') as zip: - zip.write('builds/variables.json', 'variables.json') - zipWriteDir(zip, f'builds/{build_pack_name}BP', f'{build_pack_name}BP') - zipWriteDir(zip, f'builds/{build_pack_name}RP', f'{build_pack_name}RP') + + if args.target == "release": + with ZipFile(f"builds/{build_pack_name}.mcaddon", "w") as zip: + zipWriteDir(zip, f"builds/{build_pack_name}BP", f"{build_pack_name}BP") + zipWriteDir(zip, f"builds/{build_pack_name}RP", f"{build_pack_name}RP") + elif args.target == "server": + with ZipFile(f"builds/{build_pack_name}.server.zip", "w") as zip: + zip.write("builds/variables.json", "variables.json") + zipWriteDir(zip, f"builds/{build_pack_name}BP", f"{build_pack_name}BP") + zipWriteDir(zip, f"builds/{build_pack_name}RP", f"{build_pack_name}RP") diff --git a/requirements.txt b/requirements.txt index 5d3cc01c2..0df3a49c9 100644 Binary files a/requirements.txt and b/requirements.txt differ diff --git a/tools/po2lang.py b/tools/po2lang.py index e72377332..2b5bd755e 100644 --- a/tools/po2lang.py +++ b/tools/po2lang.py @@ -1,122 +1,145 @@ -import glob, re, polib -import sys, os +import argparse +import glob +import os +import re +import sys from itertools import chain -import argparse +import polib -parser = argparse.ArgumentParser(description='Converts .po translatation files to .lang Minecraft translatation files.') -parser.add_argument('--watch', '-w', action='store_true', help='Whether to watch for file changes in the texts folder.') +parser = argparse.ArgumentParser( + description="Converts .po translatation files to .lang Minecraft translatation files." +) +parser.add_argument( + "--watch", + "-w", + action="store_true", + help="Whether to watch for file changes in the texts folder.", +) args = parser.parse_args() -BPdir = './BP/texts' -RPdir = './RP/texts' -srcdir = './texts' +BPdir = "./BP/texts" +RPdir = "./RP/texts" +srcdir = "./texts" + def get_lang(file): - return os.path.basename(file).replace('.po', '') + return os.path.basename(file).replace(".po", "") + def convert_file(in_path, out_path): newlines = [] for entry in polib.pofile(in_path): - if entry.msgid != "" and not(entry.msgid != 'pack.description' and 'BP' in out_path): + if entry.msgid != "" and not ( + entry.msgid != "pack.description" and "BP" in out_path + ): string = entry.msgstr.replace('\\"', '"') - if '\n' in string: - sys.exit(f'\033[0;31mNew line detected in translation string "{entry.msgid}" in file "{in_path}"!\033[0m') - newlines.append(f'{entry.msgid}={string}\n') - - if 'BP' in out_path: + if "\n" in string: + sys.exit( + f'\033[0;31mNew line detected in translation string "{entry.msgid}" in file "{in_path}"!\033[0m' + ) + newlines.append(f"{entry.msgid}={string}\n") + + if "BP" in out_path: break newlines[-1] = newlines[-1][:-1] - - with open(out_path, 'w', encoding='utf-8') as file: + + with open(out_path, "w", encoding="utf-8") as file: file.writelines(newlines) - print(f'{in_path} converted to {out_path}') + print(f"{in_path} converted to {out_path}") + def update_keys(filename): - if filename.endswith('en_US.po'): + if filename.endswith("en_US.po"): return - + base_entries = {} lang_entries = {} - - for entry in polib.pofile(f'{srcdir}/en_US.po'): + + for entry in polib.pofile(f"{srcdir}/en_US.po"): base_entries[entry.msgid] = entry.msgstr.replace('"', '\\"') for entry in polib.pofile(filename): lang_entries[entry.msgid] = entry.msgstr.replace('"', '\\"') - + meta = [] with open(filename) as file: reading_meta = False line = file.readline() while line: if reading_meta: - if line == '\n' or line.startswith('#') or line.startswith('msgid'): + if line == "\n" or line.startswith("#") or line.startswith("msgid"): break meta.append(line) if re.match(r'msgid\s+""', line): reading_meta = True line = file.readline() - - if get_lang(filename) == 'bg_BG': + + if get_lang(filename) == "bg_BG": print(meta) - - with open(filename, 'w', encoding='utf-8') as file: + + with open(filename, "w", encoding="utf-8") as file: file.write('msgid ""\n') for line in meta: file.write(line) - file.write('\n') - + file.write("\n") + for entry in base_entries: file.write(f'msgid "{entry}"\n') - file.write(f"msgstr \"{lang_entries.get(entry, base_entries.get(entry, ''))}\"\n") - file.write('\n') + file.write( + f"msgstr \"{lang_entries.get(entry, base_entries.get(entry, ''))}\"\n" + ) + file.write("\n") + def convert_lang(filename): lang = get_lang(filename) - convert_file(filename, f'{BPdir}/{lang}.lang') - convert_file(filename, f'{RPdir}/{lang}.lang') + convert_file(filename, f"{BPdir}/{lang}.lang") + convert_file(filename, f"{RPdir}/{lang}.lang") + def update_lang_json(): languages = [] - for file in glob.iglob(srcdir + '/*.po', recursive = True): + for file in glob.iglob(srcdir + "/*.po", recursive=True): languages.append(get_lang(file)) - + for folder in [RPdir, BPdir]: - with open(folder + '/languages.json', 'w') as file: - file.write('[\n'); + with open(folder + "/languages.json", "w") as file: + file.write("[\n") for i in range(len(languages)): - comma = ',' + comma = "," if i == len(languages) - 1: - comma = '' + comma = "" file.write(f' "{languages[i]}"{comma}\n') - file.write(']') + file.write("]") + -for file in chain(glob.iglob(BPdir + '/*.lang'), glob.iglob(RPdir + '/*.lang')): - if not 'AUTO_GENERATED' in file: +for file in chain(glob.iglob(BPdir + "/*.lang"), glob.iglob(RPdir + "/*.lang")): + if not "AUTO_GENERATED" in file: os.remove(file) -for file in glob.iglob(srcdir + '/*.po'): +for file in glob.iglob(srcdir + "/*.po"): convert_lang(file) update_lang_json() if args.watch: import time - from watchdog.observers import Observer + from watchdog.events import FileSystemEventHandler - + from watchdog.observers import Observer + def alert_watching(): - print('Watching for file changes...') - + print("Watching for file changes...") + updating_keys = False - + class MyHandler(FileSystemEventHandler): def __init__(self): self.updating_keys = False - + def on_modified(self, event): if self.updating_keys: return - + # if event.src_path.endswith('en_US.po'): # self.updating_keys = True # for filename in glob.iglob(srcdir + '/*.po', recursive = True): @@ -124,35 +147,35 @@ def on_modified(self, event): # convert_lang(filename) # self.updating_keys = False # alert_watching() - if event.src_path.endswith('.po'): + if event.src_path.endswith(".po"): convert_lang(event.src_path) alert_watching() - + def on_created(self, event): - if event.src_path.endswith('.po'): + if event.src_path.endswith(".po"): convert_lang(event.src_path) update_lang_json() alert_watching() - + def on_deleted(self, event): lang = get_lang(event.src_path) - if os.path.exists(BPdir + '/' + lang + '.lang'): - os.remove(BPdir + '/' + lang + '.lang') - if os.path.exists(RPdir + '/' + lang + '.lang'): - os.remove(RPdir + '/' + lang + '.lang') - print(f'Deleted {lang}.lang') + if os.path.exists(BPdir + "/" + lang + ".lang"): + os.remove(BPdir + "/" + lang + ".lang") + if os.path.exists(RPdir + "/" + lang + ".lang"): + os.remove(RPdir + "/" + lang + ".lang") + print(f"Deleted {lang}.lang") update_lang_json() alert_watching() - + observer = Observer() - observer.schedule(MyHandler(), path=srcdir, recursive=True) + observer.schedule(MyHandler(), path=srcdir, recursive=True) observer.start() - + try: alert_watching() while True: time.sleep(1) except KeyboardInterrupt: observer.stop() - print('\n') + print("\n") observer.join() diff --git a/tools/process_config.py b/tools/process_config.py index 519b4211b..bd438e346 100644 --- a/tools/process_config.py +++ b/tools/process_config.py @@ -1,26 +1,48 @@ -import json, argparse - -parser = argparse.ArgumentParser(description='Build config file from worldedit_settings.json') -parser.add_argument('--target', choices=['release', 'debug', 'server'], default='debug', help='Whether to build the addon in debug or release mode or for servers') -parser.add_argument('--watch', '-w', action='store_true', help='Watch config.js and wordedit_settings.json for changes and update') -parser.add_argument('--generateConfigTS', action='store_true', help='Generate/update config.ts in the src folder') -parser.add_argument('--generateConfigJSON', action='store_true', help='Generate/update variables.json in the builds folder') +import argparse +import json + +parser = argparse.ArgumentParser( + description="Build config file from worldedit_settings.json" +) +parser.add_argument( + "--target", + choices=["release", "debug", "server"], + default="debug", + help="Whether to build the addon in debug or release mode or for servers", +) +parser.add_argument( + "--watch", + "-w", + action="store_true", + help="Watch config.js and wordedit_settings.json for changes and update", +) +parser.add_argument( + "--generateConfigTS", + action="store_true", + help="Generate/update config.ts in the src folder", +) +parser.add_argument( + "--generateConfigJSON", + action="store_true", + help="Generate/update variables.json in the builds folder", +) args = parser.parse_args() settings = { "debug": { "description": "Enables debug messages to content logs.", - "default": args.target == 'debug' + "default": args.target == "debug", } } -version_str = '' +version_str = "" + def generateScript(isServer): - result = '' + result = "" if isServer: result += 'import { variables } from "@minecraft/server-admin";\n\n' - - result += 'export default {\n' + + result += "export default {\n" for name, data in settings.items(): if isServer: result += f' {name}: variables.get("{name}"),\n' @@ -31,20 +53,23 @@ def generateScript(isServer): value = f'"{value}"' elif type(value) is bool: value = "true" if value else "false" - - result += ' /**\n' - for line in data['description'].splitlines(): - result += f' * {line}\n' - result += ' */\n' - result += f' {name}: {value},\n' - result += '};\n\n' - - result += '\n'.join([ - '// WorldEdit version (do not change)', - f'export const VERSION = "{version_str}";' - ]) + + result += " /**\n" + for line in data["description"].splitlines(): + result += f" * {line}\n" + result += " */\n" + result += f" {name}: {value},\n" + result += "};\n\n" + + result += "\n".join( + [ + "// WorldEdit version (do not change)", + f'export const VERSION = "{version_str}";', + ] + ) return result + def generateVariables(): result = [] for name, data in settings.items(): @@ -54,73 +79,77 @@ def generateVariables(): value = f'"{value}"' elif type(value) is bool: value = "true" if value else "false" - - var = '\n /**\n' - for line in data['description'].splitlines(): - var += f' * {line}\n' - var += ' */\n' + + var = "\n /**\n" + for line in data["description"].splitlines(): + var += f" * {line}\n" + var += " */\n" var += f' "{name}": {value}' result.append(var) - return '{' + ",".join(result) + '\n}' + return "{" + ",".join(result) + "\n}" + + +prevResult = "" -prevResult = '' def update(): global prevResult try: - with open('BP/scripts/config.js', 'r') as file: + with open("BP/scripts/config.js", "r") as file: if len(prevResult) != 0 and prevResult == file.read(): return except IOError: pass - - with open('BP/scripts/config.js', 'w') as file: - prevResult = generateScript(args.target == 'server') + + with open("BP/scripts/config.js", "w") as file: + prevResult = generateScript(args.target == "server") file.write(prevResult) - + # load settings file -with open('worldedit_settings.json', 'r') as file: - settings = { **settings, **json.load(file) } +with open("worldedit_settings.json", "r") as file: + settings = {**settings, **json.load(file)} # load addon version -with open('mc_manifest.json', 'r') as file: +with open("mc_manifest.json", "r") as file: manifest = json.load(file) - version = manifest['header']['version'] + version = manifest["header"]["version"] if type(version) is str: version_str = version else: - version_str = '.'.join(map(str, version)) + (' [BETA]' if len(version) > 3 else '')\ - + version_str = ".".join(map(str, version)) + ( + " [BETA]" if len(version) > 3 else "" + ) # Generate src/config.ts if args.generateConfigTS: - with open('src/config.ts', 'w') as file: + with open("src/config.ts", "w") as file: file.write(generateScript(False)) exit(0) # Generate builds/variables.json if args.generateConfigJSON: - with open('builds/variables.json', 'w') as file: + with open("builds/variables.json", "w") as file: file.write(generateVariables()) exit(0) if args.watch: import time - from watchdog.observers import Observer + from watchdog.events import FileSystemEventHandler - + from watchdog.observers import Observer + class MyHandler(FileSystemEventHandler): def on_modified(self, ev): - if ev.src_path in ['.\worldedit_settings.json', 'BP\scripts\config.js']: + if ev.src_path in [".\worldedit_settings.json", "BP\scripts\config.js"]: update() - + obsSettings = Observer() - obsSettings.schedule(MyHandler(), path='.') + obsSettings.schedule(MyHandler(), path=".") obsSettings.start() obsConfigJS = Observer() - obsConfigJS.schedule(MyHandler(), path='BP\scripts') + obsConfigJS.schedule(MyHandler(), path="BP\scripts") obsConfigJS.start() try: @@ -129,8 +158,8 @@ def on_modified(self, ev): except KeyboardInterrupt: obsSettings.stop() obsConfigJS.stop() - + obsSettings.join() obsConfigJS.join() else: - update() \ No newline at end of file + update() diff --git a/tools/process_manifest.py b/tools/process_manifest.py index aef2dba9e..9f1725a43 100644 --- a/tools/process_manifest.py +++ b/tools/process_manifest.py @@ -1,12 +1,21 @@ -import json, argparse +import argparse +import json -parser = argparse.ArgumentParser(description='Build manifest files from \'mc_manifest.json\'.') -parser.add_argument('--target', choices=['release', 'debug', 'server'], default='debug', help='Whether to build the addon in debug or release mode or for servers.') +parser = argparse.ArgumentParser( + description="Build manifest files from 'mc_manifest.json'." +) +parser.add_argument( + "--target", + choices=["release", "debug", "server"], + default="debug", + help="Whether to build the addon in debug or release mode or for servers.", +) args = parser.parse_args() bp_manifest = {} rp_manifest = {} + def processJsonElement(element, bp_element, rp_element): def process(key, value): if isinstance(value, dict): @@ -25,19 +34,18 @@ def process(key, value): bp_element[key] = value rp_element[key] = value - if isinstance(element, dict): for [key, value] in element.items(): - if key.startswith('bp_'): - if key.startswith('bp_server_'): + if key.startswith("bp_"): + if key.startswith("bp_server_"): sub = bp_element[key[10:]] if isinstance(sub, list): bp_element[key[10:]] = sub + value elif isinstance(sub, dict): - bp_element[key[10:]] = { **sub, **value } + bp_element[key[10:]] = {**sub, **value} else: bp_element[key[3:]] = value - elif key.startswith('rp_'): + elif key.startswith("rp_"): rp_element[key[3:]] = value else: process(key, value) @@ -47,40 +55,39 @@ def process(key, value): process(i, value) i = i + 1 + # load base manifest -with open('mc_manifest.json', 'r') as file: +with open("mc_manifest.json", "r") as file: manifest = json.load(file) processJsonElement(manifest, bp_manifest, rp_manifest) -version = manifest['header']['version'] -bp_manifest['header']['name'] += ' ' + '.'.join(map(str, version)) -rp_manifest['header']['name'] += ' ' + '.'.join(map(str, version)) +version = manifest["header"]["version"] +bp_manifest["header"]["name"] += " " + ".".join(map(str, version)) +rp_manifest["header"]["name"] += " " + ".".join(map(str, version)) if not type(version) is str: version = version[:3] -bp_manifest['header']['version'] = version -rp_manifest['header']['version'] = version +bp_manifest["header"]["version"] = version +rp_manifest["header"]["version"] = version -if not 'dependencies' in bp_manifest: - bp_manifest['dependencies'] = [] -bp_manifest['dependencies'].append({ - 'uuid': rp_manifest['header']['uuid'], - 'version': rp_manifest['header']['version'] -}) +if not "dependencies" in bp_manifest: + bp_manifest["dependencies"] = [] +bp_manifest["dependencies"].append( + {"uuid": rp_manifest["header"]["uuid"], "version": rp_manifest["header"]["version"]} +) -if not 'dependencies' in rp_manifest: - rp_manifest['dependencies'] = [] -rp_manifest['dependencies'].append({ - 'uuid': bp_manifest['header']['uuid'], - 'version': bp_manifest['header']['version'] -}) +if not "dependencies" in rp_manifest: + rp_manifest["dependencies"] = [] +rp_manifest["dependencies"].append( + {"uuid": bp_manifest["header"]["uuid"], "version": bp_manifest["header"]["version"]} +) -if args.target == 'debug': - bp_manifest['header']['name'] += ' [DEBUG]' - rp_manifest['header']['name'] += ' [DEBUG]' +if args.target == "debug": + bp_manifest["header"]["name"] += " [DEBUG]" + rp_manifest["header"]["name"] += " [DEBUG]" # export behaviour and resource manifests -with open('BP/manifest.json', 'w') as file: +with open("BP/manifest.json", "w") as file: json.dump(bp_manifest, file, indent=4) -with open('RP/manifest.json', 'w') as file: - json.dump(rp_manifest, file, indent=4) \ No newline at end of file +with open("RP/manifest.json", "w") as file: + json.dump(rp_manifest, file, indent=4) diff --git a/tools/remap_imports.py b/tools/remap_imports.py index cb453bfa7..30bd4259f 100644 --- a/tools/remap_imports.py +++ b/tools/remap_imports.py @@ -1,22 +1,32 @@ -import json, glob, re -from os.path import relpath - import argparse +import glob +import json +import re +from os.path import relpath -parser = argparse.ArgumentParser(description='Remaps imports with absolute paths in the typescript build output.') -parser.add_argument('--watch', '-w', action='store_true', help='Whether to watch for file changes in the build output.') +parser = argparse.ArgumentParser( + description="Remaps imports with absolute paths in the typescript build output." +) +parser.add_argument( + "--watch", + "-w", + action="store_true", + help="Whether to watch for file changes in the build output.", +) args = parser.parse_args() -with open('tsconfig.json') as file: +with open("tsconfig.json") as file: tsconfig = json.load(file) - outdir = tsconfig['compilerOptions']['outDir'] - baseurl = tsconfig['compilerOptions']['baseUrl'] - paths = tsconfig['compilerOptions']['paths'] + outdir = tsconfig["compilerOptions"]["outDir"] + baseurl = tsconfig["compilerOptions"]["baseUrl"] + paths = tsconfig["compilerOptions"]["paths"] + +regex = re.compile(r"import\s+.+\s+from\s['|\"](.+)['|\"]") + -regex = re.compile(r"import\s+.+\s+from\s['|\"](.+)['|\"]"); def modify_file(path): modified = False - with open(path, 'r') as file: + with open(path, "r") as file: newlines = [] try: for line in file.readlines(): @@ -24,60 +34,66 @@ def modify_file(path): if match: package = match.group(1) for key, value in paths.items(): - module = re.match(key.replace('*', '(.+)'), package) + module = re.match(key.replace("*", "(.+)"), package) if module: - newpackage = outdir + '/' + value[0] + newpackage = outdir + "/" + value[0] for g in module.groups(): - newpackage = newpackage.replace('*', g, 1) - newpackage = relpath(newpackage, path).replace('\\', '/').replace('../', './', 1) + newpackage = newpackage.replace("*", g, 1) + newpackage = ( + relpath(newpackage, path) + .replace("\\", "/") + .replace("../", "./", 1) + ) line = line.replace(package, newpackage) modified = True break newlines.append(line) except: pass - + if modified: - print('remapped imports in: ' + path) - with open(path, 'w') as file: + print("remapped imports in: " + path) + with open(path, "w") as file: file.writelines(newlines) - + return modified -for filename in glob.iglob(outdir + '/**/*.js', recursive = True): + +for filename in glob.iglob(outdir + "/**/*.js", recursive=True): modify_file(filename) if args.watch: import time - from watchdog.observers import Observer + from watchdog.events import FileSystemEventHandler - + from watchdog.observers import Observer + def alert_watching(): - print('Watching for file changes...') - + print("Watching for file changes...") + class MyHandler(FileSystemEventHandler): - def on_modified(self, event): + def on_modified(self, event): if not event.is_directory: modify_file(event.src_path) - - def on_created(self, event): + + def on_created(self, event): if not event.is_directory: modify_file(event.src_path) - - def on_deleted(self, event): + + def on_deleted(self, event): pass - + observer = Observer() - observer.schedule(MyHandler(), path=outdir, recursive=True) + observer.schedule(MyHandler(), path=outdir, recursive=True) observer.start() - + try: alert_watching() - while True: + while True: time.sleep(2) - except KeyboardInterrupt: + except KeyboardInterrupt: observer.stop() - print('\n') + print("\n") observer.join() pass """ @@ -106,4 +122,4 @@ def on_deleted(self, event): print('modified ' + filename) with open(filename, 'w') as file: file.writelines(newlines) -""" \ No newline at end of file +""" diff --git a/tools/sync2com-mojang.py b/tools/sync2com-mojang.py index 8dc5cc48e..f8a9d7f4b 100644 --- a/tools/sync2com-mojang.py +++ b/tools/sync2com-mojang.py @@ -1,26 +1,51 @@ -from pathlib import Path -import glob, os, shutil import argparse +import glob +import os +import shutil +from pathlib import Path -SERVER_LOCATION = '%appdata%\\.minecraft_bedrock\\servers\\1.20.10.24' +SERVER_LOCATION = "%appdata%\\.minecraft_bedrock\\servers\\1.20.10.24" -parser = argparse.ArgumentParser(description='Syncs the project folder\'s data with Minecraft (Windows 10/11 only).\nNote: Will only sync CHANGED files in watch mode.') -parser.add_argument('--watch', '-w', action='store_true', help='Whether to watch for file changes.') -parser.add_argument('--init', choices=['False', 'True'], default='True', help='Whether to initially sync com.mojang before watching file changes.') -parser.add_argument('--dest', choices=['stable', 'preview', 'server'], default='stable', help='The place to sync the addon to') +parser = argparse.ArgumentParser( + description="Syncs the project folder's data with Minecraft (Windows 10/11 only).\nNote: Will only sync CHANGED files in watch mode." +) +parser.add_argument( + "--watch", "-w", action="store_true", help="Whether to watch for file changes." +) +parser.add_argument( + "--init", + choices=["False", "True"], + default="True", + help="Whether to initially sync com.mojang before watching file changes.", +) +parser.add_argument( + "--dest", + choices=["stable", "preview", "server"], + default="stable", + help="The place to sync the addon to", +) args = parser.parse_args() -if args.dest == 'stable': - com_mojang = os.path.expandvars('%localappdata%\\Packages\\Microsoft.MinecraftUWP_8wekyb3d8bbwe\\LocalState\\games\\com.mojang') -elif args.dest == 'preview': - com_mojang = os.path.expandvars('%localappdata%\\Packages\\Microsoft.MinecraftWindowsBeta_8wekyb3d8bbwe\\LocalState\\games\\com.mojang') -elif args.dest == 'server': +if args.dest == "stable": + com_mojang = os.path.expandvars( + "%localappdata%\\Packages\\Microsoft.MinecraftUWP_8wekyb3d8bbwe\\LocalState\\games\\com.mojang" + ) +elif args.dest == "preview": + com_mojang = os.path.expandvars( + "%localappdata%\\Packages\\Microsoft.MinecraftWindowsBeta_8wekyb3d8bbwe\\LocalState\\games\\com.mojang" + ) +elif args.dest == "server": com_mojang = os.path.expandvars(SERVER_LOCATION) -pack_folder = 'WorldEdit' +pack_folder = "WorldEdit" + +behaviour_pack = os.path.join( + com_mojang, "development_behavior_packs", f"{pack_folder} BP" +) +resource_pack = os.path.join( + com_mojang, "development_resource_packs", f"{pack_folder} RP" +) -behaviour_pack = os.path.join(com_mojang, 'development_behavior_packs', f'{pack_folder} BP') -resource_pack = os.path.join(com_mojang, 'development_resource_packs', f'{pack_folder} RP') def sync_file(path, from_root, to_root): from_file = Path(path).relative_to(from_root) @@ -31,67 +56,71 @@ def sync_file(path, from_root, to_root): if not os.path.exists(to_folder): os.makedirs(to_folder) shutil.copy(path, to_folder) - print(f'synced {path} to com.mojang') + print(f"synced {path} to com.mojang") else: os.remove(to_file) - print(f'deleted {path} from com.mojang') + print(f"deleted {path} from com.mojang") except OSError: pass + def remove_dir_if_exists(path): if os.path.exists(path): shutil.rmtree(path) + def sync_all(): remove_dir_if_exists(behaviour_pack) remove_dir_if_exists(resource_pack) - for file in glob.iglob('BP/**', recursive=True): + for file in glob.iglob("BP/**", recursive=True): if os.path.isfile(file): - sync_file(file, './BP', behaviour_pack) - for file in glob.iglob('RP/**', recursive=True): + sync_file(file, "./BP", behaviour_pack) + for file in glob.iglob("RP/**", recursive=True): if os.path.isfile(file): - sync_file(file, './RP', resource_pack) + sync_file(file, "./RP", resource_pack) + if args.watch: import time - from watchdog.observers import Observer + from watchdog.events import FileSystemEventHandler - + from watchdog.observers import Observer + def alert_watching(): - print('Watching for file changes...') - + print("Watching for file changes...") + class MyHandler(FileSystemEventHandler): def __init__(self, packtype): self.packtype = packtype - + def update(self, path): - if self.packtype == 'BP': - sync_file(path, './BP', behaviour_pack) + if self.packtype == "BP": + sync_file(path, "./BP", behaviour_pack) else: - sync_file(path, './RP', resource_pack) - + sync_file(path, "./RP", resource_pack) + def on_modified(self, ev): if not ev.is_directory: self.update(ev.src_path) - + def on_created(self, ev): if not ev.is_directory: self.update(ev.src_path) - + def on_deleted(self, ev): if not ev.is_directory: self.update(ev.src_path) - + observerBP = Observer() - observerBP.schedule(MyHandler('BP'), path='BP', recursive=True) + observerBP.schedule(MyHandler("BP"), path="BP", recursive=True) observerBP.start() observerRP = Observer() - observerRP.schedule(MyHandler('RP'), path='RP', recursive=True) + observerRP.schedule(MyHandler("RP"), path="RP", recursive=True) observerRP.start() - if args.init == 'True': + if args.init == "True": sync_all() try: alert_watching() @@ -100,8 +129,8 @@ def on_deleted(self, ev): except KeyboardInterrupt: observerBP.stop() observerRP.stop() - print('\n') + print("\n") observerBP.join() observerRP.join() else: - sync_all() \ No newline at end of file + sync_all()