diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 41a3fe387..000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,12 +0,0 @@ -version: 2 - -updates: - - package-ecosystem: "npm" - directory: "/" - schedule: - interval: "daily" - - - package-ecosystem: "pip" - directory: "/" - schedule: - interval: "daily" diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index fd4c7995f..000000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Docs - -on: - workflow_dispatch: - push: - branches: [main] - paths: - - "docs/**" - - "README.md" - - "pyproject.toml" - - "CHANGELOG.md" - -env: - PYTHON_VERSION: "3.10" - POETRY_VERSION: "1.5.1" - -jobs: - main: - name: Build and deploy - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: ${{ env.PYTHON_VERSION }} - - name: Cache poetry - id: cache-poetry - uses: actions/cache@v3 - with: - path: ~/.local - key: poetry-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ env.POETRY_VERSION }} - - name: Install poetry - if: steps.cache-poetry.outputs.cache-hit != 'true' - uses: snok/install-poetry@v1 - with: - version: ${{ env.POETRY_VERSION }} - virtualenvs-in-project: true - - name: Cache venv - id: cache-venv - uses: actions/cache@v3 - with: - path: .venv - key: venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ env.POETRY_VERSION }}-${{ hashFiles('poetry.lock') }} - restore-keys: | - venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ env.POETRY_VERSION }}- - - name: Install dependencies - if: steps.cache-venv.outputs.cache-hit != 'true' - run: poetry install -E image - - name: Build and deploy documentation - run: | - git config --global user.name "github-actions" - git config --global user.email "action@github.com" - poetry run mudkip build --check --skip-broken-links --update-gh-pages --repository https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6c243b8c5..3c5fffbe6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,79 +2,90 @@ name: CI on: push: - branches: [main] - paths-ignore: - - "docs/**" + branches: [main, beta] pull_request: - branches: [main] - paths-ignore: - - "docs/**" + branches: [main, beta] env: - PYTHON_VERSION: "3.10" - POETRY_VERSION: "1.5.1" + PACKAGES: | + . + packages/lectern + packages/mecha + packages/bolt jobs: - main: - name: Test and release + check: runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v5 + - name: Set up Python + uses: actions/setup-python@v6 + - name: Set up uv + uses: astral-sh/setup-uv@v7 + - name: Install dependencies + run: uv sync --all-packages --locked --all-extras --dev + - name: Tests + run: | + status=0 + for p in $PACKAGES; do + if ! (uv run --directory "$p" pytest -v); then + status=1 + fi + done + exit $status + - name: Pyright + run: uv run basedpyright --level error + - name: Ruff + run: uv run ruff check + - name: Ruff format + run: uv run ruff format --check --diff + release: + if: github.event_name == 'push' && github.repository == 'mcbeet/beet' + needs: check + concurrency: + group: ${{ github.workflow }}-release-${{ github.ref_name }} + cancel-in-progress: false + environment: + name: pypi + permissions: + id-token: write + contents: write + runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: + ref: ${{ github.ref_name }} fetch-depth: 0 + - name: Reset workflow sha + run: git reset --hard ${{ github.sha }} - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: ${{ env.PYTHON_VERSION }} - - name: Cache poetry - id: cache-poetry - uses: actions/cache@v4 - with: - path: ~/.local - key: poetry-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ env.POETRY_VERSION }} - - name: Install poetry - if: steps.cache-poetry.outputs.cache-hit != 'true' - uses: snok/install-poetry@v1 - with: - version: ${{ env.POETRY_VERSION }} - virtualenvs-in-project: true - - name: Cache venv - id: cache-venv - uses: actions/cache@v4 - with: - path: .venv - key: venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ env.POETRY_VERSION }}-${{ hashFiles('poetry.lock') }} - restore-keys: | - venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ env.POETRY_VERSION }}- + uses: actions/setup-python@v6 + - name: Set up uv + uses: astral-sh/setup-uv@v7 - name: Install dependencies - if: steps.cache-venv.outputs.cache-hit != 'true' - run: poetry install -E image - - name: Setup npm cache - id: cache-npm - uses: actions/cache@v4 - with: - path: node_modules - key: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }} - - name: Install Pyright - if: steps.cache-npm.outputs.cache-hit != 'true' - run: npm install - - name: Run tests - run: poetry run pytest -v - - name: Run type-checking - run: npm run check - - name: Check formatting - run: poetry run black --check beet tests + run: uv sync --all-packages --locked --all-extras --dev - name: Release - if: | - github.repository == 'mcbeet/beet' - && github.event_name == 'push' - && github.ref == 'refs/heads/main' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} run: | - git config --global user.name "github-actions" - git config --global user.email "action@github.com" - poetry run semantic-release publish -v DEBUG -D commit_author="github-actions " + for p in $PACKAGES; do + uv run --directory "$p" bash -e <<"EOF" + if semantic-release --strict version --no-commit --no-tag; then + uv lock --upgrade-package $(uv version | cut -d " " -f1) + git add "$GITHUB_WORKSPACE/uv.lock" + grep "{ workspace = true }" pyproject.toml | { + while read dep _; do + uv add "$dep>=$(uv version --package $dep --short)" + done + } + git add pyproject.toml + uv build -o dist + semantic-release --strict version + uv publish dist/* + semantic-release publish + fi + EOF + done diff --git a/.python-version b/.python-version new file mode 100644 index 000000000..6324d401a --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.14 diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index cf97a3327..000000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "recommendations": [ - "ms-python.python", - "ms-python.vscode-pylance", - "ms-python.black-formatter", - "tamasfe.even-better-toml" - ] -} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..6b9c136db --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,32 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "pytest", + "type": "debugpy", + "request": "launch", + "module": "pytest", + }, + { + "name": "nosnap_vanilla", + "type": "debugpy", + "request": "launch", + "module": "beet", + "args": ["-p", "examples/nosnap_vanilla"] + }, + { + "name": "mecha_basic", + "type": "debugpy", + "request": "launch", + "module": "beet", + "args": ["-p", "packages/mecha/examples/basic"] + }, + { + "name": "mecha_stats", + "type": "debugpy", + "request": "launch", + "module": "mecha", + "args": ["-s", "packages/mecha/examples/basic/src/data/demo/functions/foo.mcfunction"] + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 1dd766983..000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "python.formatting.provider": "none", - "python.languageServer": "Pylance", - "python.linting.enabled": false, - "python.linting.pylintEnabled": false, - "python.linting.banditEnabled": false, - "python.linting.flake8Enabled": false, - "python.linting.mypyEnabled": false, - "python.linting.prospectorEnabled": false, - "python.linting.pycodestyleEnabled": false, - "python.linting.pydocstyleEnabled": false, - "python.linting.pylamaEnabled": false, - "[python]": { - "editor.tabSize": 4, - "editor.formatOnSave": true, - "editor.formatOnType": false, - "editor.formatOnPaste": false, - "editor.codeActionsOnSave": { - "source.organizeImports": "explicit" - }, - "editor.defaultFormatter": "ms-python.black-formatter" - } -} diff --git a/CHANGELOG.md b/CHANGELOG.md index cc0e284fb..f68fddafa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,93 @@ # Changelog - + + +## v0.113.0-beta.11 (2026-01-14) + +### Bug Fixes + +- Update format registry + ([`320ea89`](https://github.com/mcbeet/beet/commit/320ea896f97eec821c3047d4a13f1f8793757b7d)) + + +## v0.113.0-beta.10 (2025-12-11) + +### Features + +- Use full minecraft version to get pack_format ([#488](https://github.com/mcbeet/beet/pull/488), + [`5a51958`](https://github.com/mcbeet/beet/commit/5a51958a4bd575718b7c557e30f8b9142225957c)) + + +## v0.113.0-beta.9 (2025-12-03) + +### Bug Fixes + +- Include subprojects' ignore and output directories to prevent rebuild loops in watch when using + broadcast ([#482](https://github.com/mcbeet/beet/pull/482), + [`624eb96`](https://github.com/mcbeet/beet/commit/624eb96dbef139c671e2f1b316fcc00c132f0c51)) + + +## v0.113.0-beta.8 (2025-12-03) + +### Bug Fixes + +- Cleanup empty overlays + ([`15d2d86`](https://github.com/mcbeet/beet/commit/15d2d862281e4af36e41b41606669dd2ba5ada54)) + + +## v0.113.0-beta.7 (2025-11-17) + +### Features + +- Add log_time_scope + ([`3ef14eb`](https://github.com/mcbeet/beet/commit/3ef14ebf9f9ffe49ee69bd195926862899865f2a)) + + +## v0.113.0-beta.6 (2025-11-16) + +### Bug Fixes + +- Fetch_objects race condition (close #479) + ([`e7405ae`](https://github.com/mcbeet/beet/commit/e7405aeceb74927749bc9f9cf8bb8b96495f89e6)) + +- Locate_minecraft path separator (close #477) + ([`e445ea7`](https://github.com/mcbeet/beet/commit/e445ea75eefeebdb5b737d6b2e58540ae14d0728)) + +- Preload before MultiCache.clear (close #352) + ([`4e3d3ec`](https://github.com/mcbeet/beet/commit/4e3d3ec020300a7e82d0e4aad353b11a6aacf365)) + +- Start fixing type issues + ([`532d109`](https://github.com/mcbeet/beet/commit/532d109b0a86e6eda48caf77a431773dc4201395)) + +- Update click (close #484) + ([`054fed8`](https://github.com/mcbeet/beet/commit/054fed8d3334ff27420adbcf674d42faedeb9b2f)) + +- Use PathLike + ([`b140c99`](https://github.com/mcbeet/beet/commit/b140c995bb9b846691d00b1c9a1e591439c60b99)) + + +## v0.113.0-beta.5 (2025-11-15) + +### Bug Fixes + +- Change outdated license format + ([`a25ab4a`](https://github.com/mcbeet/beet/commit/a25ab4a0f0a42da927ec1d30d58bbd1e6b37e1ff)) + + +## v0.113.0-beta.4 (2025-11-15) + +### Bug Fixes + +- No print + ([`a5e3805`](https://github.com/mcbeet/beet/commit/a5e3805ad0c6ca04d5d0052157c05e3475321dfa)) + + +## v0.113.0-beta.3 (2025-11-15) + +### Features + +- Yo ([`b2817f0`](https://github.com/mcbeet/beet/commit/b2817f0a979e8d65b31ea81677c808c978cc935a)) + ## v0.112.2 (2025-12-01) diff --git a/README.md b/README.md index 4f1728448..b9c197070 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,3 @@ -> [!IMPORTANT] -> -> We're updating to [Pydantic v2](https://docs.pydantic.dev/2.0/migration/)! -> -> If your project defines custom Pydantic models and validators, try switching to the [`beta`](https://github.com/mcbeet/beet/tree/beta) branch to be ready for Pydantic v2 when it's released on `main`. - ---- - logo # Beet @@ -13,7 +5,6 @@ [![GitHub Actions](https://github.com/mcbeet/beet/workflows/CI/badge.svg)](https://github.com/mcbeet/beet/actions) [![PyPI](https://img.shields.io/pypi/v/beet.svg)](https://pypi.org/project/beet/) [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/beet.svg)](https://pypi.org/project/beet/) -[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) [![Discord](https://img.shields.io/discord/900530660677156924?color=7289DA&label=discord&logo=discord&logoColor=fff)](https://discord.gg/98MdSGMm8j) > The Minecraft pack development kit. @@ -72,22 +63,17 @@ The `beet` toolchain is designed to support a wide range of use-cases. The most ## Installation -The package can be installed with `pip`. +We recommend `uv` (https://github.com/astral-sh/uv#installation). With `uv` installed, you can try `beet` by running `uvx beet`. You can also install `beet` as a global tool on your machine. -```bash -$ pip install beet +```console +$ uv tool install beet ``` -To create and edit images programmatically you should install `beet` with the `image` extra or install `Pillow` separately. - -```bash -$ pip install beet[image] -$ pip install beet Pillow -``` +> If you see the message `warning: ... is not on your PATH`, you'll need to add the specified directory to your global path to invoke `beet` directly instead of using `uvx beet`. You can make sure that `beet` was successfully installed by trying to use the toolchain from the command-line. -```bash +```console $ beet --help Usage: beet [OPTIONS] COMMAND [ARGS]... @@ -109,38 +95,30 @@ Commands: ## Contributing -Contributions are welcome. Make sure to first open an issue discussing the problem or the new feature before creating a pull request. The project uses [`poetry`](https://python-poetry.org). - -```bash -$ poetry install --extras image -``` - -You can run the tests with `poetry run pytest`. We use [`pytest-minecraft`](https://github.com/vberlier/pytest-minecraft) to run tests against actual Minecraft releases. +Contributions are welcome. Make sure to first open an issue discussing the problem or the new feature before creating a pull request. The project uses [`uv`](https://github.com/astral-sh/uv). -```bash -$ poetry run pytest -$ poetry run pytest --minecraft-latest +```console +$ uv sync ``` -We also use [`pytest-insta`](https://github.com/vberlier/pytest-minecraft) for snapshot testing. Data pack and resource pack snapshots make it easy to monitor and review changes. +You can run the tests with `uv run pytest`. We use [`pytest-minecraft`](https://github.com/vberlier/pytest-minecraft) to run tests against actual Minecraft releases. -```bash -$ poetry run pytest --insta review +```console +$ uv run pytest +$ uv run pytest --minecraft-latest ``` -The project must type-check with [`pyright`](https://github.com/microsoft/pyright). If you're using VSCode the [`pylance`](https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance) extension should report diagnostics automatically. You can also install the type-checker locally with `npm install` and run it from the command-line. +We also use [`pytest-insta`](https://github.com/vberlier/pytest-insta) for snapshot testing. Data pack and resource pack snapshots make it easy to monitor and review changes. -```bash -$ npm run watch -$ npm run check +```console +$ uv run pytest --insta review ``` -The code follows the [`black`](https://github.com/psf/black) code style. Import statements are sorted with [`isort`](https://pycqa.github.io/isort/). +The code is formatted and checked with [`ruff`](https://github.com/astral-sh/ruff). -```bash -$ poetry run isort beet tests -$ poetry run black beet tests -$ poetry run black --check beet tests +```console +$ uv run ruff format +$ uv run ruff check ``` --- diff --git a/beet/__init__.py b/beet/__init__.py deleted file mode 100644 index 9787a6da7..000000000 --- a/beet/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -__version__ = "0.112.2" - - -from .core.cache import * -from .core.container import * -from .core.error import * -from .core.file import * -from .core.watch import * -from .library.base import * -from .library.data_pack import * -from .library.resource_pack import * -from .toolchain.config import * -from .toolchain.context import * -from .toolchain.generator import * -from .toolchain.helpers import * -from .toolchain.pipeline import * -from .toolchain.project import * -from .toolchain.query import * -from .toolchain.template import * -from .toolchain.tree import * -from .toolchain.worker import * diff --git a/docs/assets/beet_icon.png b/docs/assets/beet_icon.png deleted file mode 100644 index 4eefd67fe..000000000 Binary files a/docs/assets/beet_icon.png and /dev/null differ diff --git a/docs/assets/beet_icon_large.png b/docs/assets/beet_icon_large.png deleted file mode 100755 index cd1456910..000000000 Binary files a/docs/assets/beet_icon_large.png and /dev/null differ diff --git a/docs/assets/beet_logo.png b/docs/assets/beet_logo.png deleted file mode 100644 index 15d17fcdd..000000000 Binary files a/docs/assets/beet_logo.png and /dev/null differ diff --git a/docs/assets/beet_logo_large.png b/docs/assets/beet_logo_large.png deleted file mode 100755 index 0861547cb..000000000 Binary files a/docs/assets/beet_logo_large.png and /dev/null differ diff --git a/docs/assets/build_process.png b/docs/assets/build_process.png deleted file mode 100755 index ae5b86506..000000000 Binary files a/docs/assets/build_process.png and /dev/null differ diff --git a/docs/assets/favicon.png b/docs/assets/favicon.png deleted file mode 100644 index 9e8ec27e4..000000000 Binary files a/docs/assets/favicon.png and /dev/null differ diff --git a/docs/assets/python.svg b/docs/assets/python.svg deleted file mode 100644 index 7ab97aefb..000000000 --- a/docs/assets/python.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/docs/changelog.md b/docs/changelog.md deleted file mode 100644 index 93b6c1a98..000000000 --- a/docs/changelog.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -hide-toc: true ---- - -```{include} ../CHANGELOG.md - -``` diff --git a/docs/getting_started/index.md b/docs/getting_started/index.md deleted file mode 100644 index 8eeadf6a1..000000000 --- a/docs/getting_started/index.md +++ /dev/null @@ -1,56 +0,0 @@ -# Getting Started -```{toctree} -:hidden: - -plugins -``` -Beet is a development toolkit for working with Minecraft data and resource packs. This section will explain how to install beet and get started with a first project. - -## Installation -To start, you will need [Python 3.10+](https://python.org/). Then, you can install beet via pip. This will give you access to the CLI command that you can use inside your terminal. -```bash -pip install beet -``` - -## Creating a project -Beet projects start with a configuration file, `beet.json`, in the root of your project. - -```{tab} beet.json -```json -{ - "name": "My First Pack", - "description": "Learning beet!", - - "data_pack": { - "load": ["src"] - }, - - "output": "build" -} -``` - -This beet config describes a basic data pack which has a name, description, where to load in the data pack, and finally, where the output pack will be produced. To test it out, you can create a data pack function inside the `src` folder. - -```{tab} src/data/example/function/hello.mcfunction -```mcfunction -say hello beet -``` - -## Building the pack -Now, navigating back to the root directory, you can invoke the build process and your pack will build, check it out in the output directory! -```bash -beet build -``` - -Alternatively, beet can watch the source files and automatically build the pack when it detects changes: -```bash -beet watch -``` - -## Linking to a world -It is possible to automatically link the output pack to a world in your saves folder. -```bash -beet build --link "My World" -``` - -This will automatically look for the minecraft folder. If you are using a third party launcher, you can override this with the `MINECRAFT_PATH` environment variable. diff --git a/docs/getting_started/plugins.md b/docs/getting_started/plugins.md deleted file mode 100644 index 6dc400dd3..000000000 --- a/docs/getting_started/plugins.md +++ /dev/null @@ -1,39 +0,0 @@ -# Plugins -A beet plugin is essentially a Python function that takes the build context as an argument. - -```{tab} my_plugins.py -```py -from beet import Context, Function - -def add_greeting(ctx: Context): - ctx.data["example:hello"] = Function(["say hello from a plugin"]) -``` - -The `Context` object lets you access the data pack with the `data` attribute. Similarly, you are able to get the resource pack with the `assets` attribute. - -Just adding this file won't change anything. That's because plugins need to be used inside the beet pipeline in the config: - -```{tab} beet.json -```json -{ - "data_pack": { - "load": ["src"] - }, - "pipeline": [ - "my_plugins.add_greeting" - ], - "output": "build" -} -``` - -## Modifying existing files -Plugins can introspect and modify any part of the build. For example, the following plugin prepends every function with its name: - -```{tab} my_plugins.py -```py -from beet import Context, Function - -def function_headers(ctx: Context): - for name, func in ctx.data.functions.items(): - func.lines.insert(0, f"# {name}") -``` diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index c86b005d9..000000000 --- a/docs/index.md +++ /dev/null @@ -1,37 +0,0 @@ -# Beet documentation - -Welcome! This is the place where you'll eventually find everything there is to know about the `beet` project. The documentation is currently a work-in-progress. You're welcome to help write guides and contribute example use-cases. In the meantime, don't be afraid to check out the code! - -You can follow the development of the project, discuss ideas, get help, and hang out with cool people interested in map-making and programming on our brand new discord! You can also ask questions about `beet` and get general help about Minecraft map-making on the Minecraft Commands discord server. - -- **Beet** - The Minecraft pack development kit - - [![Discord](https://img.shields.io/discord/900530660677156924?color=7289DA&label=discord&logo=discord&logoColor=fff)](https://discord.gg/98MdSGMm8j) - -- **Minecraft Commands** - General discussions about commands and map-making - - [![Discord](https://img.shields.io/discord/154777837382008833?color=7289DA&label=discord&logo=discord&logoColor=fff)](https://discord.gg/QAFXFtZ) - -```{toctree} -:hidden: -:maxdepth: 2 - -getting_started/index -rationale -overview -changelog -``` - -```{toctree} -:caption: Project links -:hidden: - -GitHub Repository -PyPI Package -Beet Discord -``` - -```{include} ../README.md -:start-line: 12 -:end-line: -3 -``` diff --git a/docs/library_files/black.png b/docs/library_files/black.png deleted file mode 100644 index 320c3449e..000000000 Binary files a/docs/library_files/black.png and /dev/null differ diff --git a/docs/library_files/hello.bin b/docs/library_files/hello.bin deleted file mode 100644 index aed2973e4..000000000 --- a/docs/library_files/hello.bin +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/library_files/hello.json b/docs/library_files/hello.json deleted file mode 100644 index d79a86d58..000000000 --- a/docs/library_files/hello.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "hello": "world", - "number": 42 -} diff --git a/docs/library_files/hello.txt b/docs/library_files/hello.txt deleted file mode 100644 index 5dd01c177..000000000 --- a/docs/library_files/hello.txt +++ /dev/null @@ -1 +0,0 @@ -Hello, world! \ No newline at end of file diff --git a/docs/library_files/test1.zip b/docs/library_files/test1.zip deleted file mode 100644 index 698e7ba32..000000000 Binary files a/docs/library_files/test1.zip and /dev/null differ diff --git a/docs/overview.md b/docs/overview.md deleted file mode 100644 index da4cf1a22..000000000 --- a/docs/overview.md +++ /dev/null @@ -1,809 +0,0 @@ ---- -jupytext: - text_representation: - format_name: myst -kernelspec: - display_name: Python 3 - name: python3 ---- - -# General overview - -The `beet` project tries to provide consistent abstractions that work well together. A lot of care goes into leveraging existing patterns and intuitions to make the API as familiar as possible. - -To keep the API surface manageable, abstractions are designed to provide orthogonal functionality. When things happen to fit into each other it's generally not an accident. - -## Data packs and resource packs - -The `beet` library allows you to work with data packs and resource packs at a high level. Data pack and resource pack objects are fast and flexible primitives that make it easy inspect, generate, merge and transform files, and perform bulk operations. - -Data packs and resource packs are identical in nature and only differ in the types of resources they can contain. As a result, `beet` derives the concrete `DataPack` and `ResourcePack` classes from a generic `Pack` definition. It's the shared base class that's responsible for implementing all the operations that we'll be discussing in the next sections. - -### Object hierarchy - -Data packs are dict-like objects holding a collection of namespaces. Each key maps the name of the namespace to a `Namespace` instance that contains all the files within the namespace. Namespaces are created automatically and you shouldn't need to create them manually. - -```{code-cell} -from beet import DataPack - -pack = DataPack() -demo_namespace = pack["demo"] -demo_namespace -``` - -Namespaces themselves are also dict-like objects. Files inside namespaces are organized into homogeneous containers. Each key maps a file type to the associated file container that contains all the files of the given type within the namespace. - -```{code-cell} -from beet import Function - -demo_functions = demo_namespace[Function] -demo_functions -``` - -These namespace containers are dict-like objects as well. Each key corresponds to a file path from the root directory of the container and maps to the associated file instance. - -```{code-cell} -demo_functions["foo"] = Function(["say hello"]) -demo_functions -``` - -If you're already familiar with data packs, the overall structure of the object hierarchy shouldn't be surprising as it directly maps to the filesystem representation. - -```{code-cell} -pack == { - "demo": { - Function: { - "foo": Function(["say hello"]) - } - } -} -``` - -Namespace containers are also available through attribute access. This is usually the syntax you'll be looking for as it's a bit more readable and provides better autocompletion. - -```{code-cell} -demo_namespace.functions["foo"] is demo_namespace[Function]["foo"] -``` - -You can even omit the container entirely when adding files to the namespace. The namespace will dispatch the file to the appropriate container automatically depending on its type. - -```{code-cell} -demo_namespace["bar"] = Function(["say world"]) -demo_namespace.functions -``` - -### Resource locations - -The object hierarchy is a 1-to-1 representation that lets you work with data packs as pure Python objects. However, it can be a bit tedious to navigate depending on what you're trying to do. Most of the time, it's easier to reason about files in the data pack with their namespaced location, to reflect the way we interact with them in-game. - -Data pack objects let you access files in a single lookup with proxies that expose a namespaced view of all the files of a specific type over all the namespaces in the data pack. - -```{code-cell} -pack = DataPack() - -pack.functions["demo:foo"] = Function() -pack.functions["demo:foo"] is pack["demo"].functions["foo"] -``` - -Proxy attributes are always in sync with the underlying namespaces. You can also omit the attribute when adding files to data packs. The data pack object will dispatch the file to the appropriate proxy depending on its type. - -```{code-cell} -pack["demo:bar"] = Function() -pack.functions == { - "demo:foo": Function(), - "demo:bar": Function(), -} -``` - -### Extra files - -Unfortunately, data packs and resource packs aren't limited to neatly namespaced files and can contain extra files at the pack level and at the namespace level. Still, the `beet` library handles extra files as first-class citizens. In fact, `pack.mcmeta` and `pack.png` are both handled through this mechanism. - -```{code-cell} -from beet import JsonFile - -pack = DataPack() - -pack.extra == { - "pack.mcmeta": JsonFile({"pack": {"description": "", "pack_format": 6}}) -} -``` - -The `extra` attribute maps filenames to their respective file handles. To make it easier to access, the `pack.mcmeta` file is also directly pinned to the data pack instance. - -```{code-cell} -pack.mcmeta is pack.extra["pack.mcmeta"] -``` - -The `pack.png` file works exactly the same. It's pinned to the `icon` attribute. - -```{code-cell} -from beet import PngFile -from PIL import Image - -pack.icon = PngFile(Image.new("RGB", (128, 128), "red")) -pack.extra["pack.png"].image -``` - -For extra files at the namespace level we can look at the `sounds.json` file in resource packs. The file is pinned to the `sound_config` attribute. - -```{code-cell} -from beet import ResourcePack, SoundConfig - -pack = ResourcePack() - -pack["minecraft"].sound_config = SoundConfig() -pack["minecraft"].sound_config is pack["minecraft"].extra["sounds.json"] -``` - -Extra files are loaded according to the schema returned by the `get_extra_info` function. The returned dictionary tells the code that loads the data pack or the resource pack what files it should be looking for in addition to namespaced files, and how to load them depending on the associated type. You can add arbitrary files to the `extra` container when saving. - -```{code-cell} -DataPack.get_extra_info() -``` - -```{code-cell} -from beet import ResourcePackNamespace - -ResourcePackNamespace.get_extra_info() -``` - -### Mcmeta accessors - -Data pack metadata is stored in the `pack.mcmeta` file in the `extra` container. The file is pinned to the `mcmeta` attribute, but to make things even more convenient data pack objects let you access the description and the pack format directly through pinned attributes. - -```{code-cell} -pack = DataPack() - -pack.description = "My awesome pack" -pack.pack_format = 42 - -print(pack.mcmeta.text) -``` - -With resource packs you can use the pinned `language_config` attribute to register custom languages. - -```{code-cell} -from beet import Language - -pack = ResourcePack() - -pack["minecraft:custom"] = Language({"menu.singleplayer": "Modified singleplayer button"}) -pack.language_config["custom"] = { - "name": "Custom", - "region": "Custom", - "bidirectional": False, -} - -print(pack.mcmeta.text) -``` - -### Namespace binding callbacks - -Data packs and resource packs feature certain types of files that have a special kind of relationship and that are often created together. One example would be functions and function tags. Namespaced files have a binding callback that can run arbitrary code when they're added to data packs. For example the `Function` constructor allows you to specify a list of function tags that will be associated with the function once it's added to the data pack. - -```{code-cell} -from beet import FunctionTag - -pack = DataPack() -pack["demo:foo"] = Function(["say hello"], tags=["minecraft:tick"]) - -pack.function_tags == { - "minecraft:tick": FunctionTag({"values": ["demo:foo"]}), -} -``` - -The same thing works with textures and texture mcmeta in resource packs. Sound files can also automatically register themselves in `sounds.json`. - -```{code-cell} -from beet import Texture, TextureMcmeta -from PIL.ImageDraw import Draw - -pack = ResourcePack() - -image = Image.new("RGB", (16, 32), "green") -d = Draw(image) -d.rectangle([0, 16, 16, 32], fill="yellow") - -pack["minecraft:block/dirt"] = Texture(image, mcmeta={"animation": {"frametime": 20}}) - -pack.textures_mcmeta == { - "minecraft:block/dirt": TextureMcmeta({"animation": {"frametime": 20}}), -} -``` - -You can also create files with a custom `on_bind` callback. - -```{code-cell} -pack = DataPack() - -def on_bind(function: Function, pack: DataPack, path: str): - print(function) - print(pack) - print(path) - -pack["demo:foo"] = Function(["say hello"], on_bind=on_bind) -``` - -### Merging - -Because all the data stored in data packs is ultimately represented by file instances, `beet` can implement a very straight-forward merging strategy that lets conflicting files decide if they should overwrite or merge with existing ones. For example, tag files will be merged together. - -```{code-cell} -p1 = DataPack() -p1["demo:foo"] = Function(["say hello"], tags=["minecraft:tick"]) - -p2 = DataPack() -p2["demo:bar"] = Function(["say world"], tags=["minecraft:tick"]) - -p1.merge(p2) - -dict(p1.content) == { - "demo:foo": Function(["say hello"]), - "demo:bar": Function(["say world"]), - "minecraft:tick": FunctionTag({"values": ["demo:foo", "demo:bar"]}), -} -``` - -In resource packs, conflicting languages will preserve translations from both files. - -```{code-cell} -p1 = ResourcePack() -p1["minecraft:custom"] = Language({"demo.foo": "hello"}) - -p2 = ResourcePack() -p2["minecraft:custom"] = Language({"demo.bar": "world"}) - -p1.merge(p2) - -print(p1.languages["minecraft:custom"].text) -``` - -### Matching key patterns - -The object hierarchy that represents data packs and resource packs contains dict-like objects that have a few extra utilities compared to regular Python dictionaries. Data packs, namespace containers and proxies are all dict-like objects with string keys that expose a `match` method that returns a set of all the keys matching the given patterns. - -```{code-cell} -pack = DataPack() -pack["demo:foo"] = Function(["say hello"]) -pack["demo:bar"] = Function(["say world"]) - -pack.match("d*") -``` - -```{code-cell} -pack["demo"].functions.match("f*") -``` - -```{code-cell} -pack.functions.match("demo:*") -``` - -You can specify multiple patterns. The exclamation mark lets you invert a pattern. - -```{code-cell} -pack.functions.match("demo:*", "!demo:foo") -``` - -## File handles - -The core `beet` file handles make all the interactions with the filesystem lazy and as efficient as possible. They're responsible for exposing files in their serialized or deserialized state transparently and avoiding deserialization entirely whenever possible. This allows `beet` to load data packs and resource packs with thousands of files instantly. - -### Text files and binary files - -The implementation defines a base `File` class that's generic over the types of its serialized and deserialized representation. Files that inherit from `TextFileBase` store their serialized content as strings while files that inherit from `BinaryFileBase` store their serialized content as bytes. All concrete file types are then derived from one or the other. The most straight-forward concrete file types are `TextFile` and `BinaryFile`. - -```{code-cell} -from beet import TextFile - -TextFile("hello") -``` - -```{code-cell} -from beet import BinaryFile - -BinaryFile(b"\x00\x01\x02\x03") -``` - -You can create files by providing the serialized or deserialized content to the constructor, or specifying a source path to an existing file. `TextFile` and `BinaryFile` are a bit special though because their serialized and deserialized state are identical. - -```{code-cell} -handle = TextFile(source_path="../examples/load_basic/beet.json") -handle -``` - -Note that this didn't perform any filesystem operation. The file handle is still in an unloaded state. Accessing the content in one way or another will load the file and discard the source path. - -```{code-cell} -print(handle.text) -handle -``` - -### File states - -File handles can be in three distinct states: - -- Unloaded - - Files with a source path are unloaded. You can use the `ensure_source_path()` method to make sure that files are in an unloaded state and retrieve the source path. - -- Serialized - - Files with plain string or bytes content are treated as serialized. You can use the `ensure_serialized()` method to get the serialized content of the file no matter the state it's currently in. Text files let you access the string content through the `text` attribute and binary files let you access the raw bytes through the `blob` attribute. - -- Deserialized - - Files with a content that's different from a plain string or bytes are treated as deserialized. You can use the `ensure_deserialized()` method to get the deserialized content of the file no matter the state it's currently in. Classes deriving from `TextFile` and `BinaryFile` will expose the most suited and practical deserialized representation depending on the type of file. - -Most of the time, code that works with files doesn't need to know about the state it's currently in. If the file is already deserialized then accessing the deserialized representation will simply return the current content of the file, otherwise `beet` will transparently load the file if necessary and turn the string or bytes into the deserialized representation. The same thing happens when trying to access the string or bytes content. If the file is not loaded then `beet` will transparently load it on the fly and if the file is in its deserialized state it will automatically turn it into its serialized representation. - -The different states make it possible to optimize various operations. For instance, dumping an unloaded file to the filesystem results in a native file copy operation, so if you're shuffling files around in a data pack you're not incurring extra loading and parsing costs by using the provided abstractions. Similarly, if you're using `beet` to zip a data pack the file handles won't needlessly turn any of the files into their deserialized representation. Another example would be equality checks. If two files are unloaded and point to the same source path they're considered equal. - -### Json files - -With the `JsonFile` class we can play around and see the differences between the unloaded, serialized, and deserialized states. - -```{code-cell} -from beet import JsonFile - -handle = JsonFile(source_path="../examples/load_basic/beet.json") -handle -``` - -The file is currently unloaded. By accessing the `text` attribute, which is the same as calling the `ensure_serialized()` method, `beet` will load the file and return the string content. - -```{code-cell} -print(handle.text) -handle -``` - -We can see that now the content of the file holds the string representing the json file. Json files expose their deserialized content as a dictionary of plain Python objects. By accessing the `data` attribute, which is the same as calling the `ensure_deserialized()` method, `beet` will parse the json and return the deserialized content. - -```{code-cell} -del handle.data["data_pack"]["load"] -handle -``` - -Now the file is in its deserialized state, and any further code accessing the `data` attribute will be able to operate directly on the parsed dictionary. However, accessing the `text` attribute will transform the file into its serialized state again. - -```{code-cell} -print(handle.text) -handle -``` - -The different states aren't something you explicitly need to worry about in code that works with files but it's good to keep in mind that it doesn't play well with weird access patterns. - -```{code-cell} -handle.data["data_pack"]["pack_format"] = 0 - -for i in range(3): - handle.data["data_pack"]["pack_format"] += 1 - print(handle.text) -``` - -As you could've guessed, this kind of code is pretty problematic because even though it doesn't look like there's much going on, each iteration of the loop actually ends up parsing and serializing json. - -### Png files - -Another example of files with really distinct unloaded, serialized and deserialized states are png files. - -```{code-cell} -from beet import PngFile - -handle = PngFile(source_path="../logo.png") -handle -``` - -As usual, the file starts out unloaded. Because images are binary files, we need to access the `blob` attribute to get the serialized content. - -```{code-cell} -handle.blob[:20] -``` - -Now the file is loaded and isn't linked to the original source file anymore. - -```{code-cell} -handle.source_path is None -``` - -Accessing the `image` attribute will deserialize the file into a `PIL` image, which makes it possible to edit the image programmatically. - -```{code-cell} -handle.image = handle.image.rotate(45) -handle.image.thumbnail((128, 128)) -handle.image -``` - -### Files and data packs - -The files used in data packs and resource packs are derived from the core file handles. For example, you can create function files from a source path, a string representing the content of the function, or a list of strings corresponding to the lines of the function. - -```{code-cell} -pack = DataPack(path="../examples/load_basic/src") -pack.functions["demo:foo"] -``` - -As you can see, when we load an unzipped data pack all the files remain in their unloaded state. The moment we start interacting with the content of the function `beet` will load the file automatically. - -```{code-cell} -pack.functions["demo:foo"].lines.append("say bar") -pack.functions["demo:foo"] -``` - -## The toolchain - -In the previous sections we briefly introduced some of the operations that you can do with data packs and resource packs. For example, you can easily add generated resources to a data pack by writing a simple script that uses the previously mentioned APIs. The problem is that none of these scripts are likely to be reusable. - -The `beet` toolchain is a fully-fledged build system that lets you implement the behavior you need as plugins that can be composed with one another. It helps with making your code reusable and not just one-off scripts, and provides a cohesive developer experience. - -The idea is that each plugin is a callable Python object that accepts a context that exposes a data pack and a resource pack. The toolchain initializes the context with an empty data pack and an empty resource pack, and then feeds it to the plugins one by one. When a plugin is called it can load and merge existing data packs into the context, inspect the various files, or generate resources programmatically. When all the plugins are done, the toolchain can then output the generated data pack or link it to a Minecraft world. - -### Plugins - -We're going to transform a simple script into a plugin to introduce the concept one step at a time. - -```python -from beet import DataPack, Function - -with DataPack(path="out/greeting_data_pack") as data: - data["greeting:hello"] = Function(["say hello"] * 5, tags=["minecraft:load"]) -``` - -If you download and run this script with the Python interpreter, it will create a data pack in a new "out" directory called "greeting_data_pack". The script generates a function that says "hello" five times when the data pack is loaded. - -``` -$ tree out -out -└── greeting_data_pack - ├── data - │ ├── greeting - │ │ └── functions - │ │ └── hello.mcfunction - │ └── minecraft - │ └── tags - │ └── functions - │ └── load.json - └── pack.mcmeta - -7 directories, 3 files -``` - -Now, let's say you want to be able to greet players like this in another data pack. The first thing to do would be to separate the logic in its own function. - -```python -from beet import DataPack, Function - -def add_greeting(data: DataPack): - data["greeting:hello"] = Function(["say hello"] * 5, tags=["minecraft:load"]) - -with DataPack(path="out/greeting_data_pack") as data: - add_greeting(data) -``` - -Running the script still does the same thing as before, but now if you wanted to create a second data pack you could use the `add_greeting` function to add the same greeting to the second data pack. - -It turns out that now that we have extracted the logic into a function that takes a data pack as input, we can easily turn it into a `beet` plugin. - -```python -from beet import Context, Function - -def add_greeting(ctx: Context): - ctx.data["greeting:hello"] = Function(["say hello"] * 5, tags=["minecraft:load"]) -``` - -The plugin takes a `Context` object that lets you access the data pack with the `data` attribute. Implementing our logic as a plugin means that we no longer need to create the data pack ourselves and call the function manually. Instead, we can create a `beet.json` config file and let the toolchain create the data pack for us. The toolchain knows how to call our plugin on its own so we removed the rest of the code. - -```json -{ - "name": "greeting", - "output": "out", - "pipeline": ["my_plugins.add_greeting"] -} -``` - -The `name` option sets the name of the project to "greeting". The `output` option tells the toolchain to output the generated data pack into a directory called "out". The `pipeline` option lets you specify the plugins that should be called when building the data pack. If you save the `add_greeting` plugin in a file called "my_plugins.py", you'll be able to run the `beet` command to generate the data pack. - -``` -$ beet -Building project... - -Done! -``` - -We can see that again, this results in the exact same data pack as before, but with the added benefit that now we can potentially reuse our plugin in other projects. - -``` -$ tree out -out -└── greeting_data_pack - ├── data - │ ├── greeting - │ │ └── functions - │ │ └── hello.mcfunction - │ └── minecraft - │ └── tags - │ └── functions - │ └── load.json - └── pack.mcmeta - -7 directories, 3 files -``` - -### The context object - -We've seen that plugins take a `Context` object as input, but what exactly is it? What can you do with it? - -So far we know there's a `data` attribute that holds a `DataPack` instance. This data pack starts out completely empty, and then as plugins get called, they can inspect the content of the data pack and change it. When the build ends the toolchain then outputs the resulting data pack in one way or another. - -The `assets` attribute holds a `ResourcePack` instance. It works exactly like the `data` attribute and plugins can generate assets that the toolchain outputs alongside the data pack at the end of the build. We can try this out by making the `add_greeting` plugin greet players in their own language. - -```python -from beet import Context, Function, Language - -def add_greeting(ctx: Context): - ctx.assets["minecraft:en_us"] = Language({"greeting.hello": "hello"}) - ctx.assets["minecraft:fr_fr"] = Language({"greeting.hello": "bonjour"}) - - ctx.data["greeting:hello"] = Function( - ['tellraw @a {"translate": "greeting.hello"}'] * 5, - tags=["minecraft:load"], - ) -``` - -We're using the `assets` attribute to add language files to the resource pack. We also changed the function to use the `tellraw` command to translate the message depending on the player's language. - -``` -$ tree out -out -├── greeting_data_pack -│ ├── data -│ │ ├── greeting -│ │ │ └── functions -│ │ │ └── hello.mcfunction -│ │ └── minecraft -│ │ └── tags -│ │ └── functions -│ │ └── load.json -│ └── pack.mcmeta -└── greeting_resource_pack - ├── assets - │ └── minecraft - │ └── lang - │ ├── en_us.json - │ └── fr_fr.json - └── pack.mcmeta - -11 directories, 6 files -``` - -After running the `beet` command we can see that the toolchain generated a resource pack called "greeting_resource_pack" in the output directory. - -Now, let's say the translated message could be used on its own in another project that greets players differently. We can extract the code that adds the language files into its own plugin. - -```python -from beet import Context, Function, Language - -def add_greeting_translations(ctx: Context): - ctx.assets["minecraft:en_us"] = Language({"greeting.hello": "hello"}) - ctx.assets["minecraft:fr_fr"] = Language({"greeting.hello": "bonjour"}) - -def add_greeting(ctx: Context): - ctx.data["greeting:hello"] = Function( - ['tellraw @a {"translate": "greeting.hello"}'] * 5, - tags=["minecraft:load"], - ) -``` - -The `add_greeting_translations` plugin is now responsible for adding our translations to the generated resource pack. We can add it to the `pipeline` option in the `beet.json` config file. - -```json -{ - "name": "greeting", - "output": "out", - "pipeline": [ - "my_plugins.add_greeting_translations", - "my_plugins.add_greeting" - ] -} -``` - -The resulting data pack and resource pack didn't change, but now we're composing the behavior of two plugins together. - -The basic idea behind the `Context` object is that it's responsible for holding the shared state that makes it possible for plugins to cooperate. In addition to the data pack and the resource pack, plugins can use the `Context` object to access a bunch of other things such as the caching and generator APIs, the template manager, background workers and pipeline metadata. - -An example would be using the `meta` attribute to make the `add_greeting` plugin configurable. Right now it always shows the message five times but by using pipeline metadata we can configure how many repetitions we want right from the config file. - -```python -from beet import Context, Function, Language - -def add_greeting_translations(ctx: Context): - ctx.assets["minecraft:en_us"] = Language({"greeting.hello": "hello"}) - ctx.assets["minecraft:fr_fr"] = Language({"greeting.hello": "bonjour"}) - -def add_greeting(ctx: Context): - greeting_count = ctx.meta["greeting_count"] - - ctx.data["greeting:hello"] = Function( - ['tellraw @a {"translate": "greeting.hello"}'] * greeting_count, - tags=["minecraft:load"], - ) -``` - -Now in the config file we can use the `meta` option to specify the "greeting_count" used by the `add_greeting` plugin. - -```json -{ - "name": "greeting", - "output": "out", - "pipeline": [ - "my_plugins.add_greeting_translations", - "my_plugins.add_greeting" - ], - "meta": { - "greeting_count": 7 - } -} -``` - -The generated data pack now shows the greeting seven times when the data pack is loaded. - -### Plugin dependencies - -You might have noticed that in the previous example, when we separated the code that adds the translations into its own plugin, we made it easy to introduce a potential bug. - -The `add_greeting_translations` plugin can be used on its own, it simply adds language files to the resource pack. However, the `add_greeting` plugin relies on being able to use the message defined in the language files. Now that the translations are in their own plugin, the `add_greeting` plugin can't be used on its own anymore in the `pipeline` option. - -```json -{ - "name": "greeting", - "output": "out", - "pipeline": ["my_plugins.add_greeting"], - "meta": { - "greeting_count": 7 - } -} -``` - -If we forget to use `add_greeting_translations` we won't see any output in-game. The `add_greeting` plugin requires you to use `add_greeting_translations` as well. - -It's not always this trivial to keep track of plugin dependencies manually so the `Context` object provides a `require()` method that adds a given plugin to the pipeline if it hasn't already been called. - -```python -from beet import Context, Function, Language - -def add_greeting_translations(ctx: Context): - ctx.assets["minecraft:en_us"] = Language({"greeting.hello": "hello"}) - ctx.assets["minecraft:fr_fr"] = Language({"greeting.hello": "bonjour"}) - -def add_greeting(ctx: Context): - ctx.require(add_greeting_translations) - greeting_count = ctx.meta["greeting_count"] - - ctx.data["greeting:hello"] = Function( - ['tellraw @a {"translate": "greeting.hello"}'] * greeting_count, - tags=["minecraft:load"], - ) -``` - -Now the resource pack gets generated again and the message properly shows up in-game. The `add_greeting_translations` plugin can still be used on its own if we want to use the translated message for something else, but using the `add_greeting` plugin will now make sure that the `add_greeting_translations` plugin has been called before adding the function that greets players. - -### The pipeline - -We've seen how plugins can cooperate with the `Context` object, and we just learned how to make plugins that depend on other plugins. In these sections we mentioned the pipeline a few times already but never actually explained what it is. - -To put it simply, the pipeline is the thing that runs plugins. Plugins can only run once per pipeline. The pipeline can import plugins dynamically and knows when a plugin has already been executed. This means that if a plugin is required multiple times, it will still only be executed once. - -One thing that we didn't experiment with until now is that plugins are actually wrapping each other, and not just being called sequentially. Each plugin in the pipeline surrounds the ones after, like layers. The last plugin in the pipeline is the innermost layer. - -``` -┌────────────────────────┐ -│ Context initialization │ -└─┬──────────────────────┘ - │ - │ ┌──────────────────────────────────────────────┐ - │ │ def add_greeting_translations(ctx: Context): │ - │ │ ... │ - │ │ ┌─────────────────────────────────┐ │ - │ │ │ def add_greeting(ctx: Context): │ │ - │ │ │ ... │ │ - │ │ │ │ │ - └───┼───┼───► Entry phase ────────────────┼────────┼───► Exit phase - │ │ │ │ - │ └─────────────────────────────────┘ │ - └──────────────────────────────────────────────┘ -``` - -If we apply this to the example we've been using so far, `add_greeting_translations` conceptually surrounds `add_greeting`, because the `add_greeting_translations` plugin is required by `add_greeting`. - -The pipeline runs all plugins from the outermost layer until it reaches the innermost layer. Then the execution goes back through each plugin in reverse, like nested context managers. Because of this each plugin has an entry phase and an exit phase. Plugins can run code during the exit phase by using the `yield` statement to wait for the execution to come back, when all the dependent plugins are done. - -```python -from beet import Context, Function, Language - -def add_greeting_translations(ctx: Context): # [3] - ctx.meta["greeting_translations"] = {} - - yield # [4] - - for key, translations in ctx.meta["greeting_translations"].items(): # [6] - for code, value in translations.items(): - ctx.assets.languages.merge( - {f"minecraft:{code}": Language({f"greeting.{key}": value})} - ) - -def add_greeting(ctx: Context): # [1] - ctx.require(add_greeting_translations) # [2] - greeting_count = ctx.meta["greeting_count"] - - ctx.meta["greeting_translations"]["hello"] = { # [5] - "en_us": "hello", - "fr_fr": "bonjour", - } - - ctx.data["greeting:hello"] = Function( - ['tellraw @a {"translate": "greeting.hello"}'] * greeting_count, - tags=["minecraft:load"], - ) -``` - -To illustrate the idea, we made a few changes to the `add_greeting_translations` plugin. We now generate the language files depending on the messages added to the `greeting_translations` dictionary by dependent plugins. This definitely adds a lot of unnecessary complexity to our simple example, but you could imagine the pattern being useful on a larger scale. - -Let's walk through the example step by step: - -1. The pipeline first begins with `add_greeting`. -2. `add_greeting` requires `add_greeting_translations`, so all the remaining code gets conceptually surrounded by `add_greeting_translations`. -3. The execution then jumps to `add_greeting_translations` and the `greeting_translations` dictionary gets initialized. -4. Next, since the yield statement waits for dependent plugins to be done, the pipeline resumes the execution of the `add_greeting` plugin. -5. The `add_greeting` plugin runs to completion and the `greeting_translations` dictionary now contains the message needed by the tellraw command. -6. All the plugins that require `add_greeting_translations` are done so the execution resumes and the plugin generates language files according to the updated `greeting_translations` dictionary. - -This is a pretty advanced topic. Most plugins don't actually need to care about any of this, but it can be helpful to remember that the `yield` statement lets you wait for dependent plugins to be done. - -### Service injection - -Our previous attempt at generalizing the plugin responsible for generating language files helped us introduce the `yield` statement, but it's ultimately not that much of an improvement compared to dealing with the language files directly. The API is also kind of implicit so it would be nice if we could package everything into a proper abstraction. - -The context object acts as a very basic service container. The `inject()` method lets you instantiate and retrieve service objects that live for the duration of the current pipeline. - -```python -from beet import Context, Function - -def add_greeting(ctx: Context): - i18n = ctx.inject(Internationalization) - i18n.set("greeting.hello", en_us="hello", fr_fr="bonjour") - - greeting_count = ctx.meta["greeting_count"] - - ctx.data["greeting:hello"] = Function( - ['tellraw @a {"translate": "greeting.hello"}'] * greeting_count, - tags=["minecraft:load"], - ) -``` - -Let's try to come up with an `Internationalization` service that can be used to create translated messages. We removed the `add_greeting_translations` plugin and instead of having to populate some arbitrary context metadata, we're going to implement a more explicit `set()` method for creating translated messages. - -```python -from collections import defaultdict -from typing import DefaultDict - -from beet import Language - -class Internationalization: - languages: DefaultDict[str, Language] - - def __init__(self, ctx: Context): - self.languages = defaultdict(Language) - ctx.require(self.add_translations) - - def add_translations(self, ctx: Context): - yield - ctx.assets["minecraft"].languages.merge(self.languages) - - def set(self, key: str, **kwargs: str): - for code, message in kwargs.items(): - self.languages[code].data[key] = message -``` - -Services are instantiated when they're injected for the first time with the context object as argument. The `set()` method adds translated messages to the language files stored in the `languages` attribute. When the `Internationalization` service is created, the constructor requires its `add_translations` method as a plugin. The plugin uses the `yield` statement to wait for all the plugins using the `Internationalization` service to be done and then merges the generated language files. - -In general, service injection makes it possible to package context operations into proper abstractions. With the `inject()` method we refactored the awkward `add_greeting_translations` plugin into a decoupled, strongly-typed `Internationalization` service with an explicit API. diff --git a/docs/rationale.md b/docs/rationale.md deleted file mode 100644 index 1f1a1ff18..000000000 --- a/docs/rationale.md +++ /dev/null @@ -1,40 +0,0 @@ -# Rationale - -Data packs and resource packs were introduced by Mojang to make hard-coded logic data-driven. This turned Minecraft into an incredible parametric sandbox, where most aspects of the vanilla experience can be overridden at will by players. As a result, data packs and resource packs naturally became the primary entry point for customizing Minecraft and over time, they gained an impressive number of features that now make it possible to implement almost any game mechanic imaginable using a resource pack and a data pack. - -However, by design, Minecraft packs are not meant to be ergonomic for developers in the technical community. The format is constrained by the following requirements: - -- Data packs and resource packs should be simple and easily loadable by Minecraft. Mojang could introduce features targeted to developers to make their lives easier but it would affect loading times for all players. This means that there's an upper bound when it comes to complexity. -- Data packs and resource packs should be interoperable. Minecraft needs to be able to combine and merge resources from multiple packs, and the "simplicity constraint" limits the affordable strategies for doing that. - -This results in a simple and straightforward distribution format that happens to be pretty easily approachable despite not being optimized for developer experience. - -## Data pack tooling - -As creators become more and more ambitious, data packs become harder and harder to reason about due to the very limited abstractions available. The community is tackling the problem with tooling that alleviates some of the common use-cases. The ecosystem flourished with tons of generators, and a wide variety of projects from command pre-processors to frameworks of all kinds and full-blown programming languages. - -The problem is that these efforts are mostly disparate, and some of these tools end up having to re-implement the same things to develop their value proposition. - -It's also important to understand that they all have their strength and weaknesses and that depending on your use case some tools will provide more suitable abstractions than others. However, it's usually pretty difficult to use them together because the interoperability is generally quite poor due to the different toolchains. As a result, creators often need to compromise when a combination of tools would provide the ideal workflow. - -## What's a development kit for data packs anyway? - -The term "development kit" is technically the most accurate way to describe `beet` but it sounds slightly obscure. Since you can't directly map the term "development kit" to a specific use case it can take some time to wrap your head around it. Projects with a more bounded scope like "command pre-processor" or "framework for writing commands in TypeScript" are easier to grasp because they address a specific high-level need. `beet` is a level below that. It's a platform on top of which you can build and integrate any of the tools or frameworks you might need to streamline pack development. - -One analogy would be with the Java SDK. When you want to make programs in Java you install the JDK (Java Development Kit) which contains your standard library as well as tools for building your projects. `beet` is similar. The library contains primitives for working with data packs and resource packs programmatically, and you use the `beet` toolchain to build your projects. - -In practice, this means that `beet` is here to introduce a build step and rescue you from developing directly in your `.minecraft` folder. By default, the `beet` pipeline is a simple pass-through. You tell it to load your data pack and resource pack and it will output it in your development world or wherever you want. Even without introducing any new concept compared to regular data pack development, it's already much more ergonomic since you can have your data pack and resource pack side by side in the same directory under version control. It also lets you easily experiment with different worlds, and even benefit from live data pack reloading with the `beet watch --reload` command. - -Of course, the whole point of introducing a build step is that after loading your data pack, it lets you do more stuff. For example, it can help you automate releases and versioning now that there's a clear separation between your project sources and what you're going to publish. - -## A unified build process - -Data packs aren't optimized for developer experience so you often end up having to write a lot of repetitive code or use external generators. Depending on your use case, you may already have custom scripts that let you automate things. This quickly becomes a nightmare to maintain, and it clutters your workspace by making you navigate through lots of files that you wouldn't even edit manually anyway. `beet` makes it easy to make your project data-driven by letting you write plugins that operate on the output data pack and resource pack. You can think of the `beet` pipeline as a little conveyor belt that transports your data pack through a factory. Along the way, plugins can analyze, apply transformations, and generate anything that you wouldn't want to write manually. - -The plugin system is also what makes it possible to write higher-level tools and integrate external utilities into your build process. The `beet` ecosystem has been growing steadily and there are now really useful projects like [`mecha`](https://github.com/mcbeet/mecha) and [`lectern`](https://github.com/mcbeet/lectern) that leverage `beet` to extend the command syntax and provide a document-based authoring format for data packs and resource packs. - -The core idea behind the `beet` pipeline is that since different projects can have widely different requirements, it should be able to act as an interoperability layer between existing tools to let you set up a cohesive build system without compromising. This is implemented through merging by using data packs as a sort of "lingua franca" emitted and understood by pretty much everything built by the community. - -If you're familiar with web development, you could compare `beet` to `webpack` in the sense that it lets you process and bundle separate sources into a production package. For example, `beet` integrates with [`sandstone`](https://www.sandstone.dev/), which is a Node.js/TypeScript framework for writing commands. `beet` can run `sandstone` as part of your build and merge the generated data pack with anything else you might need. - -Another implication of the unified pipeline is that new utilities don't need to reinvent the wheel. They can leverage the toolchain and `beet` primitives to simplify their implementation. It also makes it easier for the end-user thanks to the seamless interoperability and the friendly development workflow. diff --git a/examples/basic_latest_1_20/assets/test/models/test.json b/examples/basic_latest_1_20/assets/test/models/test.json new file mode 100644 index 000000000..6b9d7a1c7 --- /dev/null +++ b/examples/basic_latest_1_20/assets/test/models/test.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "test:item/test" + } +} \ No newline at end of file diff --git a/examples/basic_latest_1_20/beet.yml b/examples/basic_latest_1_20/beet.yml new file mode 100644 index 000000000..37ef54ac0 --- /dev/null +++ b/examples/basic_latest_1_20/beet.yml @@ -0,0 +1,12 @@ +resource_pack: + load: "." +data_pack: + load: "." + +minecraft: "1.20.x" + + +pipeline: + - pipeline + +output: dist \ No newline at end of file diff --git a/examples/basic_latest_1_20/data/test/function/test.mcfunction b/examples/basic_latest_1_20/data/test/function/test.mcfunction new file mode 100644 index 000000000..493d2be14 --- /dev/null +++ b/examples/basic_latest_1_20/data/test/function/test.mcfunction @@ -0,0 +1,3 @@ +say HI + + diff --git a/examples/basic_latest_1_20/pipeline.py b/examples/basic_latest_1_20/pipeline.py new file mode 100644 index 000000000..80157a7a5 --- /dev/null +++ b/examples/basic_latest_1_20/pipeline.py @@ -0,0 +1,5 @@ +from beet import Context + + +def beet_default(ctx: Context): + print(ctx.assets.min_format) diff --git a/examples/code_context_generator/beet.json b/examples/code_context_generator/beet.json index 84c908e8b..546fa3be8 100644 --- a/examples/code_context_generator/beet.json +++ b/examples/code_context_generator/beet.json @@ -1,3 +1,3 @@ { - "pipeline": ["demo"] + "pipeline": ["demo", "beet.contrib.cleanup_overlays"] } diff --git a/examples/code_context_generator/demo.py b/examples/code_context_generator/demo.py index 31a30f42b..ecc3f71fd 100644 --- a/examples/code_context_generator/demo.py +++ b/examples/code_context_generator/demo.py @@ -49,7 +49,9 @@ def beet_default(ctx: Context): for i in range(3): ctx.generate("hoisted:foo", merge=FunctionTag({"values": [f"demo:foo{i}"]})) ctx.generate("hoisted:abc", default=Function).append(f"function {func}") - ctx.generate("hoisted:def", default=Function("say init")).prepend(f"say {i+1}") + ctx.generate("hoisted:def", default=Function("say init")).prepend( + f"say {i + 1}" + ) with ctx.generate.overlays["foo"].push(): ctx.generate("bop{incr}", Function(["say hello"])) diff --git a/examples/code_function_tree/demo.py b/examples/code_function_tree/demo.py index 8f6860508..9408ec9b8 100644 --- a/examples/code_function_tree/demo.py +++ b/examples/code_function_tree/demo.py @@ -18,7 +18,7 @@ def beet_default(ctx: Context): def handle_content(ctx: Context, node: TreeNode[str], function: Function, n: int): if node.root: ctx.generate(Function([f"function {node.parent}"], tags=["minecraft:tick"])) - function.lines.append(f"scoreboard players add #index temp 1") + function.lines.append("scoreboard players add #index temp 1") if node.partition(n): function.lines.append( f"execute if score #index temp matches {node.range} run function {node.children}" diff --git a/examples/code_load/demo.py b/examples/code_load/demo.py index 4b5fd048a..a416879fb 100644 --- a/examples/code_load/demo.py +++ b/examples/code_load/demo.py @@ -2,7 +2,7 @@ def beet_default(ctx: Context): - config = ProjectConfig(data_pack={"load": ["src"]}).resolve(ctx.directory) # type: ignore + config = ProjectConfig(data_pack={"load": ["src"]}).resolve(ctx.directory) # pyright: ignore[reportArgumentType] ctx.require( ProjectBuilder( Project( diff --git a/examples/code_overview7/my_plugins.py b/examples/code_overview7/my_plugins.py index 8e527d493..a87caff2f 100644 --- a/examples/code_overview7/my_plugins.py +++ b/examples/code_overview7/my_plugins.py @@ -8,7 +8,7 @@ class Internationalization: languages: DefaultDict[str, Language] def __init__(self, ctx: Context): - self.languages = defaultdict(Language) # type: ignore + self.languages = defaultdict(Language) ctx.require(self.add_translations) def add_translations(self, ctx: Context): diff --git a/examples/code_version_prefixing/beet.yml b/examples/code_version_prefixing/beet.yml new file mode 100644 index 000000000..57d860c77 --- /dev/null +++ b/examples/code_version_prefixing/beet.yml @@ -0,0 +1,6 @@ +name: demo +version: 1.2.3 +data_pack: + load: "src" +pipeline: + - version_prefixing diff --git a/examples/code_version_prefixing/src/data/demo/advancement/something.json b/examples/code_version_prefixing/src/data/demo/advancement/something.json new file mode 100644 index 000000000..09b1ac932 --- /dev/null +++ b/examples/code_version_prefixing/src/data/demo/advancement/something.json @@ -0,0 +1,9 @@ +{ + "display": { + "title": "Something", + "description": "", + "icon": { + "id": "minecraft:stone" + } + } +} diff --git a/examples/code_version_prefixing/src/data/demo/function/bar.mcfunction b/examples/code_version_prefixing/src/data/demo/function/bar.mcfunction new file mode 100644 index 000000000..cba837f35 --- /dev/null +++ b/examples/code_version_prefixing/src/data/demo/function/bar.mcfunction @@ -0,0 +1,2 @@ +advancement grant @s only demo:something +function demo:no_prefix/hello diff --git a/examples/code_version_prefixing/src/data/demo/function/foo.mcfunction b/examples/code_version_prefixing/src/data/demo/function/foo.mcfunction new file mode 100644 index 000000000..3d660e753 --- /dev/null +++ b/examples/code_version_prefixing/src/data/demo/function/foo.mcfunction @@ -0,0 +1,2 @@ +say foo +function demo:bar diff --git a/examples/code_version_prefixing/src/data/demo/function/nested/example.mcfunction b/examples/code_version_prefixing/src/data/demo/function/nested/example.mcfunction new file mode 100644 index 000000000..fbcf3e036 --- /dev/null +++ b/examples/code_version_prefixing/src/data/demo/function/nested/example.mcfunction @@ -0,0 +1 @@ +say nested diff --git a/examples/code_version_prefixing/src/data/demo/function/no_prefix/hello.mcfunction b/examples/code_version_prefixing/src/data/demo/function/no_prefix/hello.mcfunction new file mode 100644 index 000000000..565dca511 --- /dev/null +++ b/examples/code_version_prefixing/src/data/demo/function/no_prefix/hello.mcfunction @@ -0,0 +1,2 @@ +say hello +function demo:nested/example diff --git a/examples/code_version_prefixing/templates/version_check.mcfunction b/examples/code_version_prefixing/templates/version_check.mcfunction new file mode 100644 index 000000000..46a7b7645 --- /dev/null +++ b/examples/code_version_prefixing/templates/version_check.mcfunction @@ -0,0 +1 @@ +execute unless score #{{ project_version }} version matches 1 run return fail diff --git a/examples/code_version_prefixing/version_prefixing.py b/examples/code_version_prefixing/version_prefixing.py new file mode 100644 index 000000000..4b2a4d258 --- /dev/null +++ b/examples/code_version_prefixing/version_prefixing.py @@ -0,0 +1,27 @@ +from beet import Context +from beet.contrib.rename_files import rename_files +from beet.contrib.find_replace import find_replace +from beet.contrib.function_header import function_header + + +def beet_default(ctx: Context): + ctx.require( + rename_files( + data_pack={ + "match": f"{ctx.project_id}:*", + "find": rf"{ctx.project_id}:(?!no_prefix/)([a-z0-9_/]+)", + "replace": rf"{ctx.project_id}:v{ctx.project_version}/\1", + } + ), + find_replace( + data_pack={"match": f"{ctx.project_id}:*"}, + substitute={ + "find": rf"{ctx.project_id}:(?!no_prefix/)([a-z0-9_/]+)", + "replace": rf"{ctx.project_id}:v{ctx.project_version}/\1", + }, + ), + function_header( + match=[rf"{ctx.project_id}:*", rf"!{ctx.project_id}:no_prefix/*"], + template="version_check.mcfunction", + ), + ) diff --git a/examples/load_auto_yaml/demo.py b/examples/load_auto_yaml/demo.py index e940620e0..83b69ba33 100644 --- a/examples/load_auto_yaml/demo.py +++ b/examples/load_auto_yaml/demo.py @@ -1,7 +1,7 @@ import json from typing import ClassVar -from pydantic.v1 import BaseModel +from pydantic import BaseModel from beet import Context, DataPack, Drop, FileDeserialize, Function, JsonFileBase diff --git a/examples/load_auto_yaml/src/assets/minecraft/sounds.yml b/examples/load_auto_yaml/src/assets/minecraft/sounds.yml index 5c130245f..d3c85e1cd 100644 --- a/examples/load_auto_yaml/src/assets/minecraft/sounds.yml +++ b/examples/load_auto_yaml/src/assets/minecraft/sounds.yml @@ -1,128 +1,128 @@ -block.note_block.banjo_-1: - sounds: - - block/note_block/banjo_-1 - subtitle: subtitles.block.note_block.note -block.note_block.banjo_1: - sounds: - - block/note_block/banjo_1 - subtitle: subtitles.block.note_block.note -block.note_block.basedrum_-1: - sounds: - - block/note_block/bd_-1 - subtitle: subtitles.block.note_block.note -block.note_block.basedrum_1: - sounds: - - block/note_block/bd_1 - subtitle: subtitles.block.note_block.note -block.note_block.bell_-1: - sounds: - - block/note_block/bell_-1 - subtitle: subtitles.block.note_block.note -block.note_block.bell_1: - sounds: - - block/note_block/bell_1 - subtitle: subtitles.block.note_block.note -block.note_block.bit_-1: - sounds: - - block/note_block/bit_-1 - subtitle: subtitles.block.note_block.note -block.note_block.bit_1: - sounds: - - block/note_block/bit_1 - subtitle: subtitles.block.note_block.note -block.note_block.hat_-1: - sounds: - - block/note_block/hat_-1 - subtitle: subtitles.block.note_block.note -block.note_block.hat_1: - sounds: - - block/note_block/hat_1 - subtitle: subtitles.block.note_block.note -block.note_block.cow_bell_-1: - sounds: - - block/note_block/cow_bell_-1 - subtitle: subtitles.block.note_block.note -block.note_block.cow_bell_1: - sounds: - - block/note_block/cow_bell_1 - subtitle: subtitles.block.note_block.note -block.note_block.bass_-1: - sounds: - - block/note_block/bassattack_-1 - subtitle: subtitles.block.note_block.note -block.note_block.bass_1: - sounds: - - block/note_block/bassattack_1 - subtitle: subtitles.block.note_block.note -block.note_block.didgeridoo_-1: - sounds: - - block/note_block/didgeridoo_-1 - subtitle: subtitles.block.note_block.note -block.note_block.didgeridoo_1: - sounds: - - block/note_block/didgeridoo_1 - subtitle: subtitles.block.note_block.note -block.note_block.flute_-1: - sounds: - - block/note_block/flute_-1 - subtitle: subtitles.block.note_block.note -block.note_block.flute_1: - sounds: - - block/note_block/flute_1 - subtitle: subtitles.block.note_block.note -block.note_block.guitar_-1: - sounds: - - block/note_block/guitar_-1 - subtitle: subtitles.block.note_block.note -block.note_block.guitar_1: - sounds: - - block/note_block/guitar_1 - subtitle: subtitles.block.note_block.note -block.note_block.harp_-1: - sounds: - - block/note_block/harp_-1 - subtitle: subtitles.block.note_block.note -block.note_block.harp_1: - sounds: - - block/note_block/harp_1 - subtitle: subtitles.block.note_block.note -block.note_block.chime_-1: - sounds: - - block/note_block/icechime_-1 - subtitle: subtitles.block.note_block.note -block.note_block.chime_1: - sounds: - - block/note_block/icechime_1 - subtitle: subtitles.block.note_block.note -block.note_block.iron_xylophone_-1: - sounds: - - block/note_block/iron_xylophone_-1 - subtitle: subtitles.block.note_block.note -block.note_block.iron_xylophone_1: - sounds: - - block/note_block/iron_xylophone_1 - subtitle: subtitles.block.note_block.note -block.note_block.pling_-1: - sounds: - - block/note_block/pling_-1 - subtitle: subtitles.block.note_block.note -block.note_block.pling_1: - sounds: - - block/note_block/pling_1 - subtitle: subtitles.block.note_block.note -block.note_block.snare_-1: - sounds: - - block/note_block/snare_-1 - subtitle: subtitles.block.note_block.note -block.note_block.snare_1: - sounds: - - block/note_block/snare_1 - subtitle: subtitles.block.note_block.note -block.note_block.xylophone_-1: - sounds: - - block/note_block/xylobone_-1 - subtitle: subtitles.block.note_block.note -block.note_block.xylophone_1: - sounds: - - block/note_block/xylobone_1 - subtitle: subtitles.block.note_block.note +block.note_block.banjo_-1: + sounds: + - block/note_block/banjo_-1 + subtitle: subtitles.block.note_block.note +block.note_block.banjo_1: + sounds: + - block/note_block/banjo_1 + subtitle: subtitles.block.note_block.note +block.note_block.basedrum_-1: + sounds: + - block/note_block/bd_-1 + subtitle: subtitles.block.note_block.note +block.note_block.basedrum_1: + sounds: + - block/note_block/bd_1 + subtitle: subtitles.block.note_block.note +block.note_block.bell_-1: + sounds: + - block/note_block/bell_-1 + subtitle: subtitles.block.note_block.note +block.note_block.bell_1: + sounds: + - block/note_block/bell_1 + subtitle: subtitles.block.note_block.note +block.note_block.bit_-1: + sounds: + - block/note_block/bit_-1 + subtitle: subtitles.block.note_block.note +block.note_block.bit_1: + sounds: + - block/note_block/bit_1 + subtitle: subtitles.block.note_block.note +block.note_block.hat_-1: + sounds: + - block/note_block/hat_-1 + subtitle: subtitles.block.note_block.note +block.note_block.hat_1: + sounds: + - block/note_block/hat_1 + subtitle: subtitles.block.note_block.note +block.note_block.cow_bell_-1: + sounds: + - block/note_block/cow_bell_-1 + subtitle: subtitles.block.note_block.note +block.note_block.cow_bell_1: + sounds: + - block/note_block/cow_bell_1 + subtitle: subtitles.block.note_block.note +block.note_block.bass_-1: + sounds: + - block/note_block/bassattack_-1 + subtitle: subtitles.block.note_block.note +block.note_block.bass_1: + sounds: + - block/note_block/bassattack_1 + subtitle: subtitles.block.note_block.note +block.note_block.didgeridoo_-1: + sounds: + - block/note_block/didgeridoo_-1 + subtitle: subtitles.block.note_block.note +block.note_block.didgeridoo_1: + sounds: + - block/note_block/didgeridoo_1 + subtitle: subtitles.block.note_block.note +block.note_block.flute_-1: + sounds: + - block/note_block/flute_-1 + subtitle: subtitles.block.note_block.note +block.note_block.flute_1: + sounds: + - block/note_block/flute_1 + subtitle: subtitles.block.note_block.note +block.note_block.guitar_-1: + sounds: + - block/note_block/guitar_-1 + subtitle: subtitles.block.note_block.note +block.note_block.guitar_1: + sounds: + - block/note_block/guitar_1 + subtitle: subtitles.block.note_block.note +block.note_block.harp_-1: + sounds: + - block/note_block/harp_-1 + subtitle: subtitles.block.note_block.note +block.note_block.harp_1: + sounds: + - block/note_block/harp_1 + subtitle: subtitles.block.note_block.note +block.note_block.chime_-1: + sounds: + - block/note_block/icechime_-1 + subtitle: subtitles.block.note_block.note +block.note_block.chime_1: + sounds: + - block/note_block/icechime_1 + subtitle: subtitles.block.note_block.note +block.note_block.iron_xylophone_-1: + sounds: + - block/note_block/iron_xylophone_-1 + subtitle: subtitles.block.note_block.note +block.note_block.iron_xylophone_1: + sounds: + - block/note_block/iron_xylophone_1 + subtitle: subtitles.block.note_block.note +block.note_block.pling_-1: + sounds: + - block/note_block/pling_-1 + subtitle: subtitles.block.note_block.note +block.note_block.pling_1: + sounds: + - block/note_block/pling_1 + subtitle: subtitles.block.note_block.note +block.note_block.snare_-1: + sounds: + - block/note_block/snare_-1 + subtitle: subtitles.block.note_block.note +block.note_block.snare_1: + sounds: + - block/note_block/snare_1 + subtitle: subtitles.block.note_block.note +block.note_block.xylophone_-1: + sounds: + - block/note_block/xylobone_-1 + subtitle: subtitles.block.note_block.note +block.note_block.xylophone_1: + sounds: + - block/note_block/xylobone_1 + subtitle: subtitles.block.note_block.note diff --git a/examples/load_babelbox/beet.json b/examples/load_babelbox/beet.json index b76ec0daf..f14049df1 100644 --- a/examples/load_babelbox/beet.json +++ b/examples/load_babelbox/beet.json @@ -31,7 +31,8 @@ "meta": { "babelbox": { "load": ["unicode_escape.csv"], - "unicode_escape": true + "unicode_escape": true, + "dialect": "unix" } } } diff --git a/examples/load_babelbox/unicode_escape.csv b/examples/load_babelbox/unicode_escape.csv index 52774c284..173a8b37e 100644 --- a/examples/load_babelbox/unicode_escape.csv +++ b/examples/load_babelbox/unicode_escape.csv @@ -1,4 +1,4 @@ id,en_us,de_de -entity.gm4.congealed_shard,"Congealed Soul Shard\u00A7" -entity.gm4.fire_sprite,"Fire Sprite ""named bob""" -test,,Würde eine ziemlich merkwürde +entity.gm4.congealed_shard,"Congealed Soul Shard\u00A7",x +entity.gm4.fire_sprite,"Fire Sprite ""named bob""",x +test,x,Würde eine ziemlich merkwürde diff --git a/examples/load_extend/demo.py b/examples/load_extend/demo.py index 31e8c0562..d0ca294f7 100644 --- a/examples/load_extend/demo.py +++ b/examples/load_extend/demo.py @@ -1,6 +1,6 @@ from typing import ClassVar, cast -from pydantic.v1 import BaseModel +from pydantic import BaseModel from beet import ( Context, @@ -64,7 +64,7 @@ def process_functions(ctx: Context): del namespace[FunctionConfig] ctx.data.mcmeta.data["blueprints"] = { - k: v.data.dict() for k, v in ctx.data[Blueprint].items() + k: v.data.model_dump() for k, v in ctx.data[Blueprint].items() } ctx.data[Blueprint].clear() diff --git a/examples/load_version_prefixing/beet.yml b/examples/load_version_prefixing/beet.yml new file mode 100644 index 000000000..33ceda86b --- /dev/null +++ b/examples/load_version_prefixing/beet.yml @@ -0,0 +1,22 @@ +version: 1.2.3 +data_pack: + load: "src" +pipeline: + - beet.contrib.rename_files + - beet.contrib.find_replace + - beet.contrib.function_header +meta: + rename_files: + data_pack: + match: "demo:*" + find: 'demo:(?!no_prefix/)([a-z0-9_/]+)' + replace: 'demo:v{{ project_version }}/\1' + find_replace: + data_pack: + match: "demo:*" + substitute: + find: 'demo:(?!no_prefix/)([a-z0-9_/]+)' + replace: 'demo:v{{ project_version }}/\1' + function_header: + match: ["demo:*", "!demo:no_prefix/*"] + template: 'version_check.mcfunction' diff --git a/examples/load_version_prefixing/src/data/demo/advancement/something.json b/examples/load_version_prefixing/src/data/demo/advancement/something.json new file mode 100644 index 000000000..09b1ac932 --- /dev/null +++ b/examples/load_version_prefixing/src/data/demo/advancement/something.json @@ -0,0 +1,9 @@ +{ + "display": { + "title": "Something", + "description": "", + "icon": { + "id": "minecraft:stone" + } + } +} diff --git a/examples/load_version_prefixing/src/data/demo/function/bar.mcfunction b/examples/load_version_prefixing/src/data/demo/function/bar.mcfunction new file mode 100644 index 000000000..cba837f35 --- /dev/null +++ b/examples/load_version_prefixing/src/data/demo/function/bar.mcfunction @@ -0,0 +1,2 @@ +advancement grant @s only demo:something +function demo:no_prefix/hello diff --git a/examples/load_version_prefixing/src/data/demo/function/foo.mcfunction b/examples/load_version_prefixing/src/data/demo/function/foo.mcfunction new file mode 100644 index 000000000..3d660e753 --- /dev/null +++ b/examples/load_version_prefixing/src/data/demo/function/foo.mcfunction @@ -0,0 +1,2 @@ +say foo +function demo:bar diff --git a/examples/load_version_prefixing/src/data/demo/function/nested/example.mcfunction b/examples/load_version_prefixing/src/data/demo/function/nested/example.mcfunction new file mode 100644 index 000000000..fbcf3e036 --- /dev/null +++ b/examples/load_version_prefixing/src/data/demo/function/nested/example.mcfunction @@ -0,0 +1 @@ +say nested diff --git a/examples/load_version_prefixing/src/data/demo/function/no_prefix/hello.mcfunction b/examples/load_version_prefixing/src/data/demo/function/no_prefix/hello.mcfunction new file mode 100644 index 000000000..565dca511 --- /dev/null +++ b/examples/load_version_prefixing/src/data/demo/function/no_prefix/hello.mcfunction @@ -0,0 +1,2 @@ +say hello +function demo:nested/example diff --git a/examples/load_version_prefixing/templates/version_check.mcfunction b/examples/load_version_prefixing/templates/version_check.mcfunction new file mode 100644 index 000000000..46a7b7645 --- /dev/null +++ b/examples/load_version_prefixing/templates/version_check.mcfunction @@ -0,0 +1 @@ +execute unless score #{{ project_version }} version matches 1 run return fail diff --git a/examples/load_yaml_resources/src/assets/minecraft/sounds.yml b/examples/load_yaml_resources/src/assets/minecraft/sounds.yml index 5c130245f..d3c85e1cd 100644 --- a/examples/load_yaml_resources/src/assets/minecraft/sounds.yml +++ b/examples/load_yaml_resources/src/assets/minecraft/sounds.yml @@ -1,128 +1,128 @@ -block.note_block.banjo_-1: - sounds: - - block/note_block/banjo_-1 - subtitle: subtitles.block.note_block.note -block.note_block.banjo_1: - sounds: - - block/note_block/banjo_1 - subtitle: subtitles.block.note_block.note -block.note_block.basedrum_-1: - sounds: - - block/note_block/bd_-1 - subtitle: subtitles.block.note_block.note -block.note_block.basedrum_1: - sounds: - - block/note_block/bd_1 - subtitle: subtitles.block.note_block.note -block.note_block.bell_-1: - sounds: - - block/note_block/bell_-1 - subtitle: subtitles.block.note_block.note -block.note_block.bell_1: - sounds: - - block/note_block/bell_1 - subtitle: subtitles.block.note_block.note -block.note_block.bit_-1: - sounds: - - block/note_block/bit_-1 - subtitle: subtitles.block.note_block.note -block.note_block.bit_1: - sounds: - - block/note_block/bit_1 - subtitle: subtitles.block.note_block.note -block.note_block.hat_-1: - sounds: - - block/note_block/hat_-1 - subtitle: subtitles.block.note_block.note -block.note_block.hat_1: - sounds: - - block/note_block/hat_1 - subtitle: subtitles.block.note_block.note -block.note_block.cow_bell_-1: - sounds: - - block/note_block/cow_bell_-1 - subtitle: subtitles.block.note_block.note -block.note_block.cow_bell_1: - sounds: - - block/note_block/cow_bell_1 - subtitle: subtitles.block.note_block.note -block.note_block.bass_-1: - sounds: - - block/note_block/bassattack_-1 - subtitle: subtitles.block.note_block.note -block.note_block.bass_1: - sounds: - - block/note_block/bassattack_1 - subtitle: subtitles.block.note_block.note -block.note_block.didgeridoo_-1: - sounds: - - block/note_block/didgeridoo_-1 - subtitle: subtitles.block.note_block.note -block.note_block.didgeridoo_1: - sounds: - - block/note_block/didgeridoo_1 - subtitle: subtitles.block.note_block.note -block.note_block.flute_-1: - sounds: - - block/note_block/flute_-1 - subtitle: subtitles.block.note_block.note -block.note_block.flute_1: - sounds: - - block/note_block/flute_1 - subtitle: subtitles.block.note_block.note -block.note_block.guitar_-1: - sounds: - - block/note_block/guitar_-1 - subtitle: subtitles.block.note_block.note -block.note_block.guitar_1: - sounds: - - block/note_block/guitar_1 - subtitle: subtitles.block.note_block.note -block.note_block.harp_-1: - sounds: - - block/note_block/harp_-1 - subtitle: subtitles.block.note_block.note -block.note_block.harp_1: - sounds: - - block/note_block/harp_1 - subtitle: subtitles.block.note_block.note -block.note_block.chime_-1: - sounds: - - block/note_block/icechime_-1 - subtitle: subtitles.block.note_block.note -block.note_block.chime_1: - sounds: - - block/note_block/icechime_1 - subtitle: subtitles.block.note_block.note -block.note_block.iron_xylophone_-1: - sounds: - - block/note_block/iron_xylophone_-1 - subtitle: subtitles.block.note_block.note -block.note_block.iron_xylophone_1: - sounds: - - block/note_block/iron_xylophone_1 - subtitle: subtitles.block.note_block.note -block.note_block.pling_-1: - sounds: - - block/note_block/pling_-1 - subtitle: subtitles.block.note_block.note -block.note_block.pling_1: - sounds: - - block/note_block/pling_1 - subtitle: subtitles.block.note_block.note -block.note_block.snare_-1: - sounds: - - block/note_block/snare_-1 - subtitle: subtitles.block.note_block.note -block.note_block.snare_1: - sounds: - - block/note_block/snare_1 - subtitle: subtitles.block.note_block.note -block.note_block.xylophone_-1: - sounds: - - block/note_block/xylobone_-1 - subtitle: subtitles.block.note_block.note -block.note_block.xylophone_1: - sounds: - - block/note_block/xylobone_1 - subtitle: subtitles.block.note_block.note +block.note_block.banjo_-1: + sounds: + - block/note_block/banjo_-1 + subtitle: subtitles.block.note_block.note +block.note_block.banjo_1: + sounds: + - block/note_block/banjo_1 + subtitle: subtitles.block.note_block.note +block.note_block.basedrum_-1: + sounds: + - block/note_block/bd_-1 + subtitle: subtitles.block.note_block.note +block.note_block.basedrum_1: + sounds: + - block/note_block/bd_1 + subtitle: subtitles.block.note_block.note +block.note_block.bell_-1: + sounds: + - block/note_block/bell_-1 + subtitle: subtitles.block.note_block.note +block.note_block.bell_1: + sounds: + - block/note_block/bell_1 + subtitle: subtitles.block.note_block.note +block.note_block.bit_-1: + sounds: + - block/note_block/bit_-1 + subtitle: subtitles.block.note_block.note +block.note_block.bit_1: + sounds: + - block/note_block/bit_1 + subtitle: subtitles.block.note_block.note +block.note_block.hat_-1: + sounds: + - block/note_block/hat_-1 + subtitle: subtitles.block.note_block.note +block.note_block.hat_1: + sounds: + - block/note_block/hat_1 + subtitle: subtitles.block.note_block.note +block.note_block.cow_bell_-1: + sounds: + - block/note_block/cow_bell_-1 + subtitle: subtitles.block.note_block.note +block.note_block.cow_bell_1: + sounds: + - block/note_block/cow_bell_1 + subtitle: subtitles.block.note_block.note +block.note_block.bass_-1: + sounds: + - block/note_block/bassattack_-1 + subtitle: subtitles.block.note_block.note +block.note_block.bass_1: + sounds: + - block/note_block/bassattack_1 + subtitle: subtitles.block.note_block.note +block.note_block.didgeridoo_-1: + sounds: + - block/note_block/didgeridoo_-1 + subtitle: subtitles.block.note_block.note +block.note_block.didgeridoo_1: + sounds: + - block/note_block/didgeridoo_1 + subtitle: subtitles.block.note_block.note +block.note_block.flute_-1: + sounds: + - block/note_block/flute_-1 + subtitle: subtitles.block.note_block.note +block.note_block.flute_1: + sounds: + - block/note_block/flute_1 + subtitle: subtitles.block.note_block.note +block.note_block.guitar_-1: + sounds: + - block/note_block/guitar_-1 + subtitle: subtitles.block.note_block.note +block.note_block.guitar_1: + sounds: + - block/note_block/guitar_1 + subtitle: subtitles.block.note_block.note +block.note_block.harp_-1: + sounds: + - block/note_block/harp_-1 + subtitle: subtitles.block.note_block.note +block.note_block.harp_1: + sounds: + - block/note_block/harp_1 + subtitle: subtitles.block.note_block.note +block.note_block.chime_-1: + sounds: + - block/note_block/icechime_-1 + subtitle: subtitles.block.note_block.note +block.note_block.chime_1: + sounds: + - block/note_block/icechime_1 + subtitle: subtitles.block.note_block.note +block.note_block.iron_xylophone_-1: + sounds: + - block/note_block/iron_xylophone_-1 + subtitle: subtitles.block.note_block.note +block.note_block.iron_xylophone_1: + sounds: + - block/note_block/iron_xylophone_1 + subtitle: subtitles.block.note_block.note +block.note_block.pling_-1: + sounds: + - block/note_block/pling_-1 + subtitle: subtitles.block.note_block.note +block.note_block.pling_1: + sounds: + - block/note_block/pling_1 + subtitle: subtitles.block.note_block.note +block.note_block.snare_-1: + sounds: + - block/note_block/snare_-1 + subtitle: subtitles.block.note_block.note +block.note_block.snare_1: + sounds: + - block/note_block/snare_1 + subtitle: subtitles.block.note_block.note +block.note_block.xylophone_-1: + sounds: + - block/note_block/xylobone_-1 + subtitle: subtitles.block.note_block.note +block.note_block.xylophone_1: + sounds: + - block/note_block/xylobone_1 + subtitle: subtitles.block.note_block.note diff --git a/examples/minecraft_1_18/beet.yml b/examples/minecraft_1_18/beet.yml index 616670c7e..1529fb5e3 100644 --- a/examples/minecraft_1_18/beet.yml +++ b/examples/minecraft_1_18/beet.yml @@ -1 +1 @@ -minecraft: 1.18 +minecraft: "1.18" diff --git a/examples/nosnap_vanilla/demo.py b/examples/nosnap_vanilla/demo.py index 42ac6c309..9bf67e6d4 100644 --- a/examples/nosnap_vanilla/demo.py +++ b/examples/nosnap_vanilla/demo.py @@ -16,13 +16,13 @@ def beet_default(ctx: Context): client_jar = vanilla.mount("assets/minecraft/lang") minecraft = client_jar.assets["minecraft"] - assert minecraft.languages.keys() == {"en_us"} + assert minecraft.languages.keys() == {"deprecated", "en_us"} vanilla.mount("assets/minecraft/lang/fr_fr.json") - assert minecraft.languages.keys() == {"en_us"} + assert minecraft.languages.keys() == {"deprecated", "en_us"} vanilla.mount("assets/minecraft/lang/fr_fr.json", fetch_objects=True) - assert minecraft.languages.keys() == {"en_us", "fr_fr"} + assert minecraft.languages.keys() == {"deprecated", "en_us", "fr_fr"} vanilla.mount("assets/minecraft/lang/fr_fr.json") - assert minecraft.languages.keys() == {"en_us", "fr_fr"} + assert minecraft.languages.keys() == {"deprecated", "en_us", "fr_fr"} vanilla.mount("assets/minecraft/sounds/ui/toast/in.ogg") assert minecraft.sounds.keys() == set() diff --git a/examples/nosnap_vanilla_font/beet.yml b/examples/nosnap_vanilla_font/beet.yml new file mode 100644 index 000000000..743b2dcd3 --- /dev/null +++ b/examples/nosnap_vanilla_font/beet.yml @@ -0,0 +1,10 @@ +pipeline: + - beet.contrib.vanilla.load_vanilla + +meta: + vanilla: + version: "1.21.8" + + load_vanilla: + match: + fonts: "minecraft:*" diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 4724a6fa6..000000000 --- a/package-lock.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "beet", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "devDependencies": { - "pyright": "^1.1.293" - } - }, - "node_modules/pyright": { - "version": "1.1.293", - "resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.293.tgz", - "integrity": "sha512-VFRGaYwRp5vMTdSpw+0ZwdSz0c/3BRYrBaoSttSelJ6RVYrDkKOE0VKRIyXkA7wxCCqd29zRPbbK4RJWpYoHjg==", - "dev": true, - "bin": { - "pyright": "index.js", - "pyright-langserver": "langserver.index.js" - }, - "engines": { - "node": ">=12.0.0" - } - } - }, - "dependencies": { - "pyright": { - "version": "1.1.293", - "resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.293.tgz", - "integrity": "sha512-VFRGaYwRp5vMTdSpw+0ZwdSz0c/3BRYrBaoSttSelJ6RVYrDkKOE0VKRIyXkA7wxCCqd29zRPbbK4RJWpYoHjg==", - "dev": true - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 0ebdca53b..000000000 --- a/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "scripts": { - "watch": "poetry run pyright -w", - "check": "poetry run pyright" - }, - "devDependencies": { - "pyright": "^1.1.293" - } -} diff --git a/packages/bolt/CHANGELOG.md b/packages/bolt/CHANGELOG.md new file mode 100644 index 000000000..ba80b2752 --- /dev/null +++ b/packages/bolt/CHANGELOG.md @@ -0,0 +1,505 @@ +# Changelog + + + +## v0.50.0-beta.5 (2025-12-08) + +### Features + +- Major version + ([`9e89fec`](https://github.com/mcbeet/beet/commit/9e89fec9526b45f4271dc2f026d914e50cca581e)) + + +## v0.50.0-beta.4 (2025-12-08) + +### Bug Fixes + +- Release new beta + ([`4532692`](https://github.com/mcbeet/beet/commit/4532692e833df89961a9be608728288e285a13f8)) + + +## v0.50.0-beta.3 (2025-12-08) + +### Bug Fixes + +- Skip version released from old repo + ([`762ee6c`](https://github.com/mcbeet/beet/commit/762ee6ca4d649d101714fe73c57f23dd79f86bcf)) + + +## v0.50.0-beta.2 (2025-11-15) + +### Bug Fixes + +- Change outdated license format + ([`a25ab4a`](https://github.com/mcbeet/beet/commit/a25ab4a0f0a42da927ec1d30d58bbd1e6b37e1ff)) + + +## v0.49.2 (2025-06-17) + +### Fix + +* Make sure that load_prelude() keeps returning the same thing for the same file ([`e0bb5b8`](https://github.com/mcbeet/bolt/commit/e0bb5b820c2d364db04480b2fb9a6539b4302f77)) + +## v0.49.1 (2024-07-22) + +### Fix + +* Allow singular module folder ([`8e7de60`](https://github.com/mcbeet/bolt/commit/8e7de601a355cd76af46873d6f8db0317f2ecacb)) + +## v0.49.0 (2024-07-22) + +### Feature + +* Update beet and mecha ([`d55c37d`](https://github.com/mcbeet/bolt/commit/d55c37d6b1de4d997d2481f89c08d85f240695f4)) + +## v0.48.0 (2024-04-29) + +### Feature + +* Version 1.20.5 ([`959af66`](https://github.com/mcbeet/bolt/commit/959af66694d73442ce84a420d314b81720526716)) + +## v0.47.2 (2024-03-29) + +### Fix + +* Missed a file in previous commit ([`d9f27f5`](https://github.com/mcbeet/bolt/commit/d9f27f53bb7358cb59b9a37dc59e242efa550eeb)) + +## v0.47.1 (2024-03-29) + +### Fix + +* Interpolation for item_slots ([`2a8f9e0`](https://github.com/mcbeet/bolt/commit/2a8f9e0de038829fafdad234d44713db39fa0286)) + +## v0.47.0 (2024-02-23) + +### Feature + +* Fuse with statements across if statements ([`d93140a`](https://github.com/mcbeet/bolt/commit/d93140a587eb504bf99a3a5dac44dd7694edec70)) +* Fuse with statements ([`d60f460`](https://github.com/mcbeet/bolt/commit/d60f460298955b17ab33efa6e3a17551bd401178)) +* Refactor with statements ([`6770dc9`](https://github.com/mcbeet/bolt/commit/6770dc95bf76ad35dbbc8a7d89d80695282c70ea)) +* Add basic loop overloading ([`497502a`](https://github.com/mcbeet/bolt/commit/497502a1fac66306313d8cb51d802214f4bcd0b2)) + +## v0.46.0 (2024-02-20) + +### Feature + +* Add chained comparison ([`0cc5da8`](https://github.com/mcbeet/bolt/commit/0cc5da8369b5774bd73cb6d249c5545cc95b0a13)) + +## v0.45.0 (2024-01-17) + +### Feature + +* Make it possible to use variable names that conflict with commands ([`f19ae15`](https://github.com/mcbeet/bolt/commit/f19ae159e3d47e33d1bff89f40828167c7ad726c)) + +## v0.44.1 (2024-01-17) + +### Fix + +* Share walk_location_hierarchy with mecha ([`973f3c1`](https://github.com/mcbeet/bolt/commit/973f3c136ce3d7cf0a27c0c3e98a54fb69b82c9f)) +* Compat with mecha.contrib.json_files when it comes before bolt ([`430d488`](https://github.com/mcbeet/bolt/commit/430d4881812237ff797fe7d4499a008a9c65cb3c)) + +## v0.44.0 (2023-12-07) + +### Feature + +* Trigger rebind on set attribute ([`ed97089`](https://github.com/mcbeet/bolt/commit/ed9708942c23448c3e3297e19a48230f827d58ec)) +* Expose minimal context info in sandbox ([`3c839a0`](https://github.com/mcbeet/bolt/commit/3c839a0b5f32e60dac774ac81ae851202ded6d73)) + +### Fix + +* Fuse with statements in codegen ([`a7e9edf`](https://github.com/mcbeet/bolt/commit/a7e9edf1a6a2d1a86e76de49aca3908cfd9987df)) +* Refactor codegen to use a statement tree ([`53a4ba9`](https://github.com/mcbeet/bolt/commit/53a4ba9b8bb4a46173246d0f26d551277f8a3989)) +* Allow accumulator override ([`493c371`](https://github.com/mcbeet/bolt/commit/493c37169065694bbf62c69702ff5e50c86d08f1)) + +## v0.43.0 (2023-12-06) + +### Feature + +* Add codegen cli command ([`4e7db00`](https://github.com/mcbeet/bolt/commit/4e7db009930e93596d6ceea327e417cb60998c58)) +* Update to 1.20.3 ([`e0c2f11`](https://github.com/mcbeet/bolt/commit/e0c2f117905f3c126a3df920ceb06d3b7c66c059)) + +## v0.42.0 (2023-12-02) + +### Feature + +* Update deps ([`fcc4bde`](https://github.com/mcbeet/bolt/commit/fcc4bdea6c582933e905f4e3ee30c394eb607d1b)) + +## v0.41.1 (2023-11-28) + +### Fix + +* Early inverse binding for else condition ([`44a6dbf`](https://github.com/mcbeet/bolt/commit/44a6dbf3bdda67288c00417d364e3384e92d26b3)) + +## v0.41.0 (2023-11-13) + +### Feature + +* Class kwargs ([`c6d6c94`](https://github.com/mcbeet/bolt/commit/c6d6c94591322807b8fea6233e785b0f05666c6a)) + +## v0.40.0 (2023-10-31) + +### Feature + +* Asset module ([`3e3fa7d`](https://github.com/mcbeet/bolt/commit/3e3fa7d810595f7bc0dc8c1691e54d31c7ecc5aa)) + +## v0.39.2 (2023-10-30) + +### Fix + +* Update mecha ([`5bac0ec`](https://github.com/mcbeet/bolt/commit/5bac0ec7539a1e85884d92e42db85b56447be09a)) + +## v0.39.1 (2023-10-30) + +### Fix + +* Update deps ([`3a160b1`](https://github.com/mcbeet/bolt/commit/3a160b1418f7c004b16516008cc000a9234933f0)) + +## v0.39.0 (2023-10-28) + +### Feature + +* Refactor non-function root ([`b74f756`](https://github.com/mcbeet/bolt/commit/b74f756229f2ba4f22a09c00caa512aebf810a1e)) + +## v0.38.5 (2023-10-15) + +### Fix + +* Import from overlay fall back to main pack ([`ebe2434`](https://github.com/mcbeet/bolt/commit/ebe243453c108ac315b9c5cdd7fa0e14c9b3e2fa)) + +## v0.38.4 (2023-10-15) + +### Fix + +* Distinct preludes for overlays ([`5f858e5`](https://github.com/mcbeet/bolt/commit/5f858e558fec97d12fbf9436f1ac81006e1deb1a)) + +## v0.38.3 (2023-10-15) + +### Fix + +* Clear modules from overlays ([`b98f18d`](https://github.com/mcbeet/bolt/commit/b98f18d1401392d22b807eef1695f9b94db885f8)) + +## v0.38.2 (2023-10-14) + +### Fix + +* Compile memo with proper nesting ([`a88de67`](https://github.com/mcbeet/bolt/commit/a88de679eb307d9595366a2104ae659652cd70ed)) + +## v0.38.1 (2023-10-10) + +### Fix + +* Handle anonymous macro invocations ([`6d4d5c0`](https://github.com/mcbeet/bolt/commit/6d4d5c094caeae4fae79445fc5106cafea968a0d)) + +## v0.38.0 (2023-10-09) + +### Feature + +* Update deps ([`9a42907`](https://github.com/mcbeet/bolt/commit/9a42907cfad7fb5ed4eec202acd0e53f29c93e43)) + +## v0.37.0 (2023-08-03) + +### Feature + +* Bump mecha to support backslash continuations ([`3d25461`](https://github.com/mcbeet/bolt/commit/3d2546147b5be2438281ce44110444805ec25d05)) + +## v0.36.0 (2023-06-07) + +### Feature + +* Delegate to mecha for nested locations without formatting ([`b5b753e`](https://github.com/mcbeet/bolt/commit/b5b753e80a80adda916976daf17aa0660590e83a)) + +### Fix + +* Take whitespace into account for interpolation ([`3f59e63`](https://github.com/mcbeet/bolt/commit/3f59e6384bac384c46b9718cdf115fe7d994373d)) + +## v0.35.0 (2023-05-20) +### Feature +* Nested locations ([`a7fe760`](https://github.com/mcbeet/bolt/commit/a7fe760d552630d5528522f332bba211d4006233)) + +## v0.34.0 (2023-05-18) +### Feature +* Add runtime nesting info ([`2100ee7`](https://github.com/mcbeet/bolt/commit/2100ee773026b6dc353fb8d753034c6db09a0b9f)) + +## v0.33.2 (2023-04-26) +### Fix +* Don't handle docstrings behind subcommands ([`2fd8c25`](https://github.com/mcbeet/bolt/commit/2fd8c256cf9272fea7b908b654f0ef25d2118c13)) + +## v0.33.1 (2023-04-25) +### Fix +* Only rewrite vanilla return inside function scope ([`e874832`](https://github.com/mcbeet/bolt/commit/e874832ef13af24b69382c6d84a2a4fdaeac3fe3)) + +## v0.33.0 (2023-04-25) +### Feature +* Handle vanilla return ([`90c3f54`](https://github.com/mcbeet/bolt/commit/90c3f541c66af7e626f9bab83e4cd35ed71f4d05)) + +## v0.32.0 (2023-04-25) +### Feature +* Update mecha ([`fba874e`](https://github.com/mcbeet/bolt/commit/fba874e345e655231cf1a66951c78b719fbb68c1)) + +## v0.31.0 (2023-02-19) +### Feature +* Add prelude option ([`b0937e0`](https://github.com/mcbeet/bolt/commit/b0937e06affd62e8f358638358ce2797644d36e7)) + +## v0.30.1 (2023-02-15) +### Fix +* Properly track del statement semantics ([`66c543f`](https://github.com/mcbeet/bolt/commit/66c543fd82902ed7cbf7e04e6a0b57a45715c0bd)) + +## v0.30.0 (2023-02-09) +### Feature +* Macro subcommands ([`5db484e`](https://github.com/mcbeet/bolt/commit/5db484ef754bbd5eaeea0c16574ecc23e160a490)) + +## v0.29.0 (2023-02-06) +### Feature +* Update 1.19 ([`0a8ca58`](https://github.com/mcbeet/bolt/commit/0a8ca580f4e3b155154fd8714ddb6ccc4c13121f)) + +## v0.28.0 (2023-02-05) +### Feature +* Error when using unsupported statements in memo ([`e57c845`](https://github.com/mcbeet/bolt/commit/e57c845f28cd40669ace582dcb2f404cf384582d)) + +## v0.27.0 (2023-02-05) +### Feature +* Restore memo bindings ([`48c5fe9`](https://github.com/mcbeet/bolt/commit/48c5fe996ff5a575bf50b5bf5f572320fedfb39e)) + +## v0.26.0 (2023-02-02) +### Feature +* Add type annotations ([`a3970f1`](https://github.com/mcbeet/bolt/commit/a3970f1a61668c5cf772ad24eacd7a835aa1824e)) + +## v0.25.0 (2023-02-01) +### Feature +* Add docstrings ([`c1c1488`](https://github.com/mcbeet/bolt/commit/c1c1488d60d58abc2a66d8cd02b671831bbf4473)) + +## v0.24.1 (2023-02-01) +### Fix +* Update pyright ([`24120a9`](https://github.com/mcbeet/bolt/commit/24120a963b476676391a51fa25350a6a06225469)) + +## v0.24.0 (2023-02-01) +### Feature +* Atlas ([`8703186`](https://github.com/mcbeet/bolt/commit/87031867ab7ce7014f88ef30e129cf1b115f5945)) + +## v0.23.0 (2023-01-31) +### Feature +* Add memo and a bunch of tweaks ([`77326b7`](https://github.com/mcbeet/bolt/commit/77326b7056a038743a1f20511ae565cacbc15f10)) + +## v0.22.0 (2022-12-04) +### Feature +* Refactor identifier tracing with proper lexical scope abstraction ([`2f60bd4`](https://github.com/mcbeet/bolt/commit/2f60bd43c8affc2747c56e2d4dd4045e45561677)) + +## v0.21.1 (2022-12-03) +### Fix +* Default empty arguments for AstFunctionSignature ([`6c79443`](https://github.com/mcbeet/bolt/commit/6c794431fe5b4c826dea8cae54a63d8c54838ad9)) + +## v0.21.0 (2022-11-23) +### Feature +* Add bolt.contrib.defer ([`6a0527f`](https://github.com/mcbeet/bolt/commit/6a0527f4696081a168a5adbfbb6a5f74d4853c4b)) +* Add CommandCollector ([`a07cedd`](https://github.com/mcbeet/bolt/commit/a07cedd2e4636b50494ff7cdd914050d194fe922)) +* Disable mecha.contrib.inline_function_tag by default ([`a9bf8bf`](https://github.com/mcbeet/bolt/commit/a9bf8bfe3d7bb39d767c204e4f06a80cc4133d78)) + +## v0.20.1 (2022-10-26) +### Fix +* Reset token generator after discarding builtin identifier in interpolation ([`37f74bc`](https://github.com/mcbeet/bolt/commit/37f74bc8f9fbf60b0493bf8e158be678ced5f272)) + +## v0.20.0 (2022-10-24) +### Feature +* Interpolate advancement predicate ([`e4338b0`](https://github.com/mcbeet/bolt/commit/e4338b0f4004601daf9c86b169a3771f876cff16)) + +### Fix +* Refactor interpolation ([`44c2d0e`](https://github.com/mcbeet/bolt/commit/44c2d0ed74c8100698fa75931df626c96329b820)) + +## v0.19.3 (2022-10-19) +### Fix +* Reset generator to prevent dropping tokens ([`6551578`](https://github.com/mcbeet/bolt/commit/65515786d6a941a2f4e94c4e8932c0dc90faa14f)) +* Update deps ([`1a00f20`](https://github.com/mcbeet/bolt/commit/1a00f208fce10a37e2ef9003d478b8dddce30d73)) + +## v0.19.2 (2022-10-04) +### Fix +* Reported 0 errors message ([`16e64b7`](https://github.com/mcbeet/bolt/commit/16e64b77d5516fa04687f156ace6a4ee050be95b)) +* Proper error for statements used as subcommands ([`647b1c6`](https://github.com/mcbeet/bolt/commit/647b1c6685f5653bf34fdc87fab7ce2a7edc6fba)) + +## v0.19.1 (2022-09-12) +### Fix +* Add error for non-default args following default args ([`2af9854`](https://github.com/mcbeet/bolt/commit/2af9854d91818804b642ffe2a7b625a6412665ec)) + +## v0.19.0 (2022-09-11) +### Feature +* Update compilation priority depending on execution order ([`d6aa9ec`](https://github.com/mcbeet/bolt/commit/d6aa9ecf55027e411889459986d79123239ec81b)) + +## v0.18.1 (2022-09-11) +### Fix +* Update deps ([`89b37c1`](https://github.com/mcbeet/bolt/commit/89b37c1b1d5cac57b1fdc1f5bef0971c41dd3e1b)) + +## v0.18.0 (2022-09-11) +### Feature +* Positional-only, variadic, keyword-only, and variadic keyword arguments ([`6413593`](https://github.com/mcbeet/bolt/commit/64135935c8f54dbde20807cba6481202dbf394e6)) + +## v0.17.7 (2022-08-16) +### Fix +* Update beet ([`c494033`](https://github.com/mcbeet/bolt/commit/c494033639d389fb6a3165ddefa04d8dc3c4a08e)) + +## v0.17.6 (2022-08-12) +### Fix +* Import parser properly delegates to resource location parser ([#37](https://github.com/mcbeet/bolt/issues/37)) ([`a971b07`](https://github.com/mcbeet/bolt/commit/a971b07e15d7382216f90c51233e3e7762a05b47)) + +## v0.17.5 (2022-08-06) +### Fix +* Builtins used as dict keys are now unquoted strings ([`015ff65`](https://github.com/mcbeet/bolt/commit/015ff657229b02d5930b0a5756e95d515e8df70e)) +* Only allow call expressions on builtins for interpolation ([`0526a85`](https://github.com/mcbeet/bolt/commit/0526a85af232e05255e24e07a43ee720a1bb87f4)) + +## v0.17.4 (2022-08-06) +### Fix +* Truncate primary expression for interpolation ([`014802f`](https://github.com/mcbeet/bolt/commit/014802fcf98314b270868503a8f4bca1a3429d31)) + +## v0.17.3 (2022-07-25) +### Fix +* Enable interpolation for uuid ([`39cc462`](https://github.com/mcbeet/bolt/commit/39cc4624f555874808c629000c260c2feefbb5b4)) + +## v0.17.2 (2022-07-25) +### Fix +* Enable interpolation for game_profile argument ([`e844f49`](https://github.com/mcbeet/bolt/commit/e844f49906fa693edf9fe509427211b2b01ad39d)) + +## v0.17.1 (2022-07-21) +### Fix +* Only allow imports and macros directly at scope level ([`2f069bd`](https://github.com/mcbeet/bolt/commit/2f069bd6ae7e689605303fb99fa272bdc4b17e36)) + +## v0.17.0 (2022-07-20) +### Feature +* Add bolt classes ([`71c1162`](https://github.com/mcbeet/bolt/commit/71c1162c99c61eb747573a370646f80b787f95f5)) + +## v0.16.0 (2022-07-17) +### Feature +* Make modules lazy by default ([`370a759`](https://github.com/mcbeet/bolt/commit/370a7596d0a5764dfe0c2ce5e553483f8728ab60)) + +## v0.15.0 (2022-07-17) +### Feature +* Add raw strings ([`e24d772`](https://github.com/mcbeet/bolt/commit/e24d7721ddd8e708735f7e3b16c50b81cbfb4a89)) + +## v0.14.0 (2022-07-17) +### Feature +* Ergonomic improvements for dicts without quotes ([`18c747e`](https://github.com/mcbeet/bolt/commit/18c747e74aa2500fbb47414d0bc4ea24a3af04a1)) + +## v0.13.0 (2022-07-17) +### Feature +* Add raise statement ([`d5654a5`](https://github.com/mcbeet/bolt/commit/d5654a58c95ea326ff01da92b88bfd2e29e48b6c)) +* Add proc macro ([`97e1bea`](https://github.com/mcbeet/bolt/commit/97e1beadb033de13453ef9deee1c290bdd730f4e)) + +## v0.12.2 (2022-07-15) +### Fix +* Update beet ([`a4f2f97`](https://github.com/mcbeet/bolt/commit/a4f2f97feef98d9c9f624ffc82a3d8e5ff282337)) + +## v0.12.1 (2022-07-15) +### Fix +* Memoize command trees ([`8c8911a`](https://github.com/mcbeet/bolt/commit/8c8911ae16e3c2829c0e33dbc3a2fab248505b81)) +* Make command tree updates lazy ([`ec17f74`](https://github.com/mcbeet/bolt/commit/ec17f7479072a63e8e81a8c17163d69c680a9b5e)) +* Minor tweaks ([`35a537e`](https://github.com/mcbeet/bolt/commit/35a537e9a4a6e471a17cfc99b8776bdc3135fd88)) + +## v0.12.0 (2022-07-13) +### Feature +* Add macro imports ([`e8ca750`](https://github.com/mcbeet/bolt/commit/e8ca750c6664ffa610b09c17d00cb7f9eed445c8)) + +## v0.11.0 (2022-07-10) +### Feature +* Turn module manager into a mapping ([`3c486eb`](https://github.com/mcbeet/bolt/commit/3c486eb6d9ce6c9a47ec16c14342fa98db6fbfd7)) +* Extract module manager from runtime ([`20e7499`](https://github.com/mcbeet/bolt/commit/20e74995ab2b44505a494e352f94d1c756ba0ec6)) +* Make it possible to run the lazy plugin multiple times ([`87df070`](https://github.com/mcbeet/bolt/commit/87df070d2f528a95c06b14fb2a8b3e019e80651c)) + +## v0.10.0 (2022-07-09) +### Feature +* Working macro and tests ([`a0ddfba`](https://github.com/mcbeet/bolt/commit/a0ddfbab5a46b173ec8a0bd46f8e78c76a098c85)) +* Start working on macro ([`e3bbb3e`](https://github.com/mcbeet/bolt/commit/e3bbb3e83dd2f60df2a4f8ca848782b7a83a2c84)) + +### Fix +* Update mecha ([`e6c77e9`](https://github.com/mcbeet/bolt/commit/e6c77e9d495e7de8dcc3326ea15c2edc767249f7)) + +## v0.9.1 (2022-07-02) +### Fix +* Allow ast node interpolation directly ([`ffa076e`](https://github.com/mcbeet/bolt/commit/ffa076efa635be32c735371e2761da954385abbf)) + +## v0.9.0 (2022-06-24) +### Feature +* Add bolt.contrib.lazy ([`c9f12f3`](https://github.com/mcbeet/bolt/commit/c9f12f360c70ca031acfe5f9b2f0213bc7c3fdff)) +* Add compiled module execution hooks ([`0a91936`](https://github.com/mcbeet/bolt/commit/0a91936a8172ad7b2b246c38b931cac8da6a27d2)) + +### Fix +* Minor visual changes ([`0ab3519`](https://github.com/mcbeet/bolt/commit/0ab35197e9d8f8532c39faf4b2a03caa938077bc)) + +## v0.8.2 (2022-06-24) +### Fix +* Remove leftover extern plugin ([`42cc659`](https://github.com/mcbeet/bolt/commit/42cc6590bfef34cc4573ed8d2fe8404cf26c9f34)) + +## v0.8.1 (2022-06-18) +### Fix +* Update deps for new snapshot settings ([`6739b2b`](https://github.com/mcbeet/bolt/commit/6739b2bb0d4521de90e6162f40033d37b2550fcd)) + +## v0.8.0 (2022-06-18) +### Feature +* Add context managers ([`38e614e`](https://github.com/mcbeet/bolt/commit/38e614e8d38d1c62af019f2b5f08a78cff22ed0b)) + +## v0.7.2 (2022-06-17) +### Fix +* Only restrict builtins when sandbox is active ([`8eefb0c`](https://github.com/mcbeet/bolt/commit/8eefb0cf1f5d6f3a6b0e472ffb1da0e6b60ba5e7)) + +## v0.7.1 (2022-06-15) +### Fix +* Track bolt version in cached ast ([`1e5d2d1`](https://github.com/mcbeet/bolt/commit/1e5d2d119872397dadac006c18b2c3909ccf6496)) + +## v0.7.0 (2022-06-15) +### Feature +* Support decorators ([`3e8f60b`](https://github.com/mcbeet/bolt/commit/3e8f60bf9b7e9c74b631bdf689f4370f76f37766)) + +### Fix +* Change prefix of codegen variables ([`f9cc901`](https://github.com/mcbeet/bolt/commit/f9cc9013ba1dd1333a2b5a199c29c84d054e15be)) + +## v0.6.0 (2022-05-27) +### Feature +* Add bolt.contrib.debug_codegen ([`d504b00`](https://github.com/mcbeet/bolt/commit/d504b00867bf22770708e6eeb6190ed790f0a483)) + +## v0.5.1 (2022-05-27) +### Fix +* Handle uppercase python imports ([`a5e50e0`](https://github.com/mcbeet/bolt/commit/a5e50e0173b47310db43b6915d31b707b7218c3a)) + +## v0.5.0 (2022-05-06) +### Feature +* Iterable/mapping unpacking in json/nbt interpolation ([`32acee0`](https://github.com/mcbeet/bolt/commit/32acee0e8bbf2c831fa48c30595b63ed61f1de40)) +* Unpack field for interpolation node ([`b2f6dd7`](https://github.com/mcbeet/bolt/commit/b2f6dd7017afe5c7ebc4c530f152b666c7557ba1)) + +### Fix +* Converter for json object ([`825f077`](https://github.com/mcbeet/bolt/commit/825f077e14bafdf719f4f488347714b5761886ac)) + +## v0.4.2 (2022-05-02) +### Fix +* Update mecha to fix indent bug ([`006a57a`](https://github.com/mcbeet/bolt/commit/006a57a4ff0f0e99af106e235440c5c7b06f1f71)) + +## v0.4.1 (2022-05-01) +### Fix +* Leftover print ([`3731f2c`](https://github.com/mcbeet/bolt/commit/3731f2c071942d0e131f42432bbd165bdf4e602e)) + +## v0.4.0 (2022-05-01) +### Feature +* Update beet to allow bolt files outside of the data pack ([`5c0d58c`](https://github.com/mcbeet/bolt/commit/5c0d58c181a053c2c0090d9af8b6eb96b6bc54f8)) + +## v0.3.4 (2022-04-30) +### Fix +* Update mecha to handle nbtlib instances properly for interpolation ([`fce1766`](https://github.com/mcbeet/bolt/commit/fce1766807e588d5a7c75b7e4e407926c0bc164d)) + +## v0.3.3 (2022-04-30) +### Fix +* Use next_token ([`3fb3aab`](https://github.com/mcbeet/bolt/commit/3fb3aabae017862b89d29c2124d523f2fd0c0e3a)) + +## v0.3.2 (2022-04-30) +### Fix +* Proper check for final expressions ([`57bb1f1`](https://github.com/mcbeet/bolt/commit/57bb1f18b09bc7b9ab04079651668341262a462b)) + +## v0.3.1 (2022-04-29) +### Fix +* Make message interpolation final ([`b46b165`](https://github.com/mcbeet/bolt/commit/b46b1652ee78a3224911a7a57173c9f3f236244e)) + +## v0.3.0 (2022-04-29) +### Feature +* Allow nbt compound interpolation ([`b6f964f`](https://github.com/mcbeet/bolt/commit/b6f964fc16805bedc1d3be94a7d042acf4112551)) + +## v0.2.1 (2022-04-26) +### Fix +* Use BubbleException ([`c8ce2fb`](https://github.com/mcbeet/bolt/commit/c8ce2fb478fa35aff0cf768cdb320302cf123378)) + +## v0.2.0 (2022-04-25) +### Feature +* Setup project ([`50b4bea`](https://github.com/mcbeet/bolt/commit/50b4beaf0faad49fad9785423378002d2bdd482a)) diff --git a/packages/bolt/LICENSE b/packages/bolt/LICENSE new file mode 100644 index 000000000..d74fc840b --- /dev/null +++ b/packages/bolt/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Beet + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/bolt/README.md b/packages/bolt/README.md new file mode 100644 index 000000000..aff6911bf --- /dev/null +++ b/packages/bolt/README.md @@ -0,0 +1,65 @@ +# Bolt + +[![GitHub Actions](https://github.com/mcbeet/beet/workflows/CI/badge.svg)](https://github.com/mcbeet/beet/actions) +[![PyPI](https://img.shields.io/pypi/v/bolt.svg)](https://pypi.org/project/bolt/) +[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/bolt.svg)](https://pypi.org/project/bolt/) +[![Discord](https://img.shields.io/discord/900530660677156924?color=7289DA&label=discord&logo=discord&logoColor=fff)](https://discord.gg/98MdSGMm8j) + +> Supercharge Minecraft commands with Python. + +```python +infinite_invisibility = { + Id: 14, + Duration: 999999, + Amplifier: 1, + ShowParticles: false, +} + +def summon_chicken_army(n): + for i in range(n): + summon chicken ~i ~ ~ { + Tags: [f"quack{i}"], + IsChickenJockey: true, + Passengers: [{ + id: zombie, + IsBaby: true, + ActiveEffects: [infinite_invisibility] + }] + } + +say Go forth, my minions! +summon_chicken_army(16) +``` + +## Installation + +The package can be installed with `pip`. + +```bash +$ pip install bolt +``` + +## Contributing + +Contributions are welcome. Make sure to first open an issue discussing the problem or the new feature before creating a pull request. The project uses [`uv`](https://github.com/astral-sh/uv). + +```console +$ uv sync +``` + +You can run the tests with `uv run pytest`. + +```console +$ uv run pytest +``` + +The code is formatted and checked with [`ruff`](https://github.com/astral-sh/ruff). + +```console +$ uv run ruff format +$ uv run ruff check +``` + +--- + +License - [MIT](https://github.com/mcbeet/beet/blob/main/packages/bolt/LICENSE) diff --git a/packages/bolt/examples/bolt_1_20/beet.yml b/packages/bolt/examples/bolt_1_20/beet.yml new file mode 100644 index 000000000..c8a8dd658 --- /dev/null +++ b/packages/bolt/examples/bolt_1_20/beet.yml @@ -0,0 +1,6 @@ +require: + - bolt +data_pack: + load: "src" +pipeline: + - mecha diff --git a/packages/bolt/examples/bolt_1_20/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_1_20/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..ae76e9c84 --- /dev/null +++ b/packages/bolt/examples/bolt_1_20/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,12 @@ +def f(): + execute if entity @e[type=pig] run return 123 + n = 77 + execute return n + return 99999 + +say f() + +execute function ./thing: + return 42 + +return 0 diff --git a/packages/bolt/examples/bolt_assets/beet.yml b/packages/bolt/examples/bolt_assets/beet.yml new file mode 100644 index 000000000..3f4787a97 --- /dev/null +++ b/packages/bolt/examples/bolt_assets/beet.yml @@ -0,0 +1,9 @@ +require: + - bolt +resource_pack: + load: "src" +pipeline: + - mecha +meta: + bolt: + entrypoint: "*" diff --git a/packages/bolt/examples/bolt_assets/src/assets/demo/modules/foo.bolt b/packages/bolt/examples/bolt_assets/src/assets/demo/modules/foo.bolt new file mode 100644 index 000000000..f56500fd6 --- /dev/null +++ b/packages/bolt/examples/bolt_assets/src/assets/demo/modules/foo.bolt @@ -0,0 +1 @@ +model minecraft:block/stone {"parent": "block/cube"} diff --git a/packages/bolt/examples/bolt_basic/beet.yml b/packages/bolt/examples/bolt_basic/beet.yml new file mode 100644 index 000000000..e15d7678c --- /dev/null +++ b/packages/bolt/examples/bolt_basic/beet.yml @@ -0,0 +1,6 @@ +require: + - bolt +data_pack: + load: [src] +pipeline: + - mecha diff --git a/packages/bolt/examples/bolt_basic/random_data.json b/packages/bolt/examples/bolt_basic/random_data.json new file mode 100644 index 000000000..d086709f6 --- /dev/null +++ b/packages/bolt/examples/bolt_basic/random_data.json @@ -0,0 +1,3 @@ +{ + "value": 42 +} diff --git a/packages/bolt/examples/bolt_basic/src/data/demo/functions/attribute_rebind.mcfunction b/packages/bolt/examples/bolt_basic/src/data/demo/functions/attribute_rebind.mcfunction new file mode 100644 index 000000000..b1ee022c2 --- /dev/null +++ b/packages/bolt/examples/bolt_basic/src/data/demo/functions/attribute_rebind.mcfunction @@ -0,0 +1,83 @@ +from dataclasses import dataclass + +@dataclass +class Var: + name: str + + def __add__(self, value): + result = Var(f"{self}_plus_{value}") + say f"{result} = {self} + {value}" + return result + + def __rebind__(self, value): + say f"{self.name} = {value}" + return self + + def __str__(self): + return self.name + +foo = Var("foo") +bar = Var("bar") +say f"{foo} {bar}" + +foo = 123 +foo += 456 +foo = bar + 789 +foo += bar + 999 + +del foo +del bar + +foo = 123 +bar = 456 +say f"{foo} {bar}" + +@dataclass +class A: + foo: Var + bar: Var + +a = A(Var("foo"), Var("bar")) +say a + +a.foo = 123 +a.foo += 456 +a.foo = a.bar + 789 +a.foo += a.bar + 999 + +del a.foo +del a.bar + +a.foo = 123 +a.bar = 456 +say a + +b = {ayy: Var("ayy"), yoo: Var("yoo")} +say b + +b.ayy = 123 +b.ayy += 456 +b.ayy = b.yoo + 789 +b.ayy += b.yoo + 999 + +del b.ayy +del b.yoo + +b["ayy"] = 123 +b["yoo"] = 456 +say b + +c = {ayy: Var("ayy"), yoo: Var("yoo")} +say c + +c["ayy"] = 123 +c["ayy"] += 456 +c["ayy"] = c["yoo"] + 789 +c["ayy"] += c["yoo"] + 999 + +del c.ayy +del c.yoo + +c["ayy"] = 123 +c["yoo"] = 456 +say c diff --git a/packages/bolt/examples/bolt_basic/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_basic/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..6341f0d12 --- /dev/null +++ b/packages/bolt/examples/bolt_basic/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,671 @@ +say hello +say this is a function file augmented with mecha + +if score @s tmp matches 1.. as @a: + for feature in [ + "multiline", + "nesting", + "implicit execute", + "relative location", + ]: + say (feature + " is automatically enabled") + message = "abc" * 5 + say message.upper() + +def you_can_make_functions(): + say you_can_make_functions.__name__.replace("_", " ").capitalize() + tellraw @a [ + "", + {"text": "yep"}, + ] + return "and return values" + +say you_can_make_functions() + +def fib(n): + if n <= 1: + return n + return fib(n - 1) + fib(n - 2) + +say fib(7) +say fib(8) +say fib(9) + +def default_params_are_neat( + number, + result=fib(number), + thing={ + "number": number, + "result": result, + }, +): + say Unlike in python default params are evaluated when the function is called + say number + say result + say thing + +default_params_are_neat(12) + +say this is basically a custom subset of python +say functions are first-class objects just like in python + +my_functions = [] + +for i in "abc": + def functions_in_loop(value): + def yo(): + say value.upper() + return yo + my_functions.append(functions_in_loop(i * 3)) + +my_functions[0]() +my_functions[1]() +my_functions[2]() + +should_break = False + +while True: + if should_break: + break + say just once + should_break = True + +something = '{"text": "Hello", "bold": true}' +as @a at @s if block ~ ~-1 ~ #wool give @s stone{ + display: { + Name: something + } +} + +with_tuples = ((((())))) + (1,2,3) + (4,) +say with_tuples +say (with_tuples) +say (with_tuples,) +a = f'\\' +say f"f-strings {"work".upper()!r} too {a}" +say f"{{{f"{{{f"{{{7:08}}}\\"}}}\""}}}" + +x = 8 +if score @s tmp matches (x, None) say wat +if score @s tmp matches f"{x}.." say wat + + +def copy_items(type, count): + for i in range(count): + item replace entity @a f"{type}.{i}" from entity @s f"{type}.{i}" + +copy_items('hotbar', 9) +copy_items('inventory', 26) + +def f(): + yield 1 + yield 2 + yield 3 + +for i in f(): + say i + +def wow(ok): + yield from ok() + yield from ok() + +say list(wow(f)) + +say ctx.generate.id("hello") + +import math +say math.cos(math.pi) + +import math as m +say (math is m) + +from ./thing import do_stuff + +say do_stuff(1, math.pi) + +import ./thing as thing + +say (thing.do_stuff is do_stuff) + +for i in range(6): + if i == 1: + say 1 + elif i == 2: + say 2 + elif i == 3: + say 3 + else: + say other + say done + +execute + as @a # For each "player", + at @s # start at their feet. + anchored eyes # Looking through their eyes, + facing 0 0 0 # face perfectly at the target + anchored feet # (go back to the feet) + positioned ^ ^ ^1 # and move one block forward. + rotated as @s # Face the direction the player + # is actually facing, + positioned ^ ^ ^-1 # and move one block back. + if entity @s[distance=..0.6] function ./abc: + say foo + say bar + +baz = "demo:xyz" +if predicate foo:bar function baz: + say foo + say bar + +for node in generate_tree("abcdefghijklmnopqrstuvwxyz0123456789"): + append function node.parent: + if node.partition(5): + if score @s thingy matches node.range function node.children + else: + if score @s thingy matches node.range say node.value + +data modify entity @e[type=armor_stand,limit=1] NoBasePlate set value 1b + +from ./thing import call_recursive +call_recursive() + +def try_coordinates(): + a = 1 + setblock a 2 3 stone + a = "1 2 3" + setblock a stone + a = 1 + if block ^ f"^{a}" ^ #planks say 42 + if block ("~", "~", "~") #planks say 42 + +try_coordinates() + +keyword_arguments = dict(foo=1, bar=2, **{"thing": 42}) +say keyword_arguments + +for node in generate_tree(range(8), name="small_tree"): + append function node.parent: + if node.partition(): + if score @s thingy matches node.range function node.children + else: + if score @s thingy matches node.range say node.value + +def try_unpacking(): + a = [1, *"abc", 2] + b = dict(zip(a, a)) + c = {**b, "b": "thing"} + say c + +try_unpacking() + +def try_set_item(): + try_set_item.data = {} + try_set_item.data[1] = {} + try_set_item.data[1][2] = "foo" + say try_set_item.data + +try_set_item() + +for node in generate_tree(range(10, 20), key=int): + append function node.parent: + if node.partition(): + if score @s thingy matches node.range function node.children + else: + if score @s thingy matches node.range say node.value + +scoreboard objectives setdisplay list some_score_name +color = "red" +scoreboard objectives setdisplay f"sidebar.team.{color}" some_score_name + +weapon = {1: "weapon.offhand", 2: "weapon.mainhand"} +item replace entity @s weapon.offhand from entity @s weapon.mainhand +my_weapon = weapon +item replace entity @s my_weapon.1 from entity @s my_weapon.2 + +numbers = list(range(12)) +say numbers[:] +say numbers[3:] +say numbers[:9] +say numbers[3:9] +say numbers[::] +say numbers[3::] +say numbers[:9:] +say numbers[3:9:] +say numbers[::-1] +say numbers[9::-1] +say numbers[:3:-1] +say numbers[9:3:-1] + +execute at @s if block ~ ~ ~ #minecraft:beds: + teleport @s ~ ~0.56250 ~ + +del numbers[3:] +say numbers + +for loop, value in loop_info("abcd"): + say f"==[{loop.current}]==" + say (value == loop.current) + say loop.before + say loop.after + say loop.first + say loop.last + say loop.cycle("foo", "bar") + +from ./thing import raw + +raw(f"say hello{'!' * 5}") + +execute function demo:bbb: + say 1 +append function demo:bbb: + say 2 +prepend function demo:bbb: + say 0 + +mykey1 = "foo" + +if data storage some:path/to/storage f"some.{mykey1}.path" +if data storage some:path/to/storage f"some.{mykey1}.path" run say hi +if data storage some:path/to/storage f'some.{mykey1}.path{{my: "compound"}}' +if data storage some:path/to/storage f'some.{mykey1}.path{{my: "compound"}}' run say hi + +mykey2 = "bar" + +mypath1 = f"some.{mykey2}.path" +if data storage some:path/to/storage (mypath1) +if data storage some:path/to/storage (mypath1) run say hi + +mypath2 = f'some.{mykey2}.path{{my: "compound"}}' +if data storage some:path/to/storage (mypath2) +if data storage some:path/to/storage (mypath2) run say hi + +mypath3 = f'some.{mykey2}.path[{{my: "subscript"}}]' +if data storage some:path/to/storage (mypath3) +if data storage some:path/to/storage (mypath3) run say hi + +myindex = 42 + +if data storage some:path/to/storage f'some.{mykey1}.path{{my: "compound"}}.stuff[{myindex}].beep.{mykey2}[{{my: "subscript"}}].boop' run say hi + +mypath4 = "foo.bar" +if data storage some:path/to/storage mypath4 + +if data storage some:path/to/storage "some.foo.path" + +import nbtlib +mypath5 = nbtlib.Path().something.cool[3].foo +if data storage some:path/to/storage mypath5 + +foo = nbtlib.Byte(23) +data merge storage some:path/to/storage {Count: foo} + +from uuid import UUID + +def try_entity_interpolation(x): + if score x some_objective matches 0 say yes + +try_entity_interpolation("some_fake_player") +try_entity_interpolation("0-0-0-0-1") +try_entity_interpolation(UUID("12345678-1234-5678-1234-567812345678")) + +entity_type = "creeper" +at @e[type=entity_type] run summon lightning_bolt + +language minecraft:aaaa { + "menu.singleplayer": "AAAA" +} + +some_translation = "bonjour" + +merge language minecraft:aaaa { + "something.else": some_translation +} + +merge language minecraft:aaaa: + yaml.key1: some_translation.upper() + yaml.key2: some_translation.title() + +x = 0 +y = 1 +z = 2 + +data merge storage demo:foo: + custom_data1: [x, y, z] + custom_data2: + - x + - y + - z + +def set_at_origin(block): + setblock 0 0 0 block + +set_at_origin(minecraft:stone) + +some_name = ./useless/../no_quotes_lol +function some_name: + say that's neat + +if ./a/b/c == ./x/../a/b/x/../c: + say same thing + +tellraw @a: + text: 'hello world' + +function_tag felix:howdy: + values: ['demo:foo', __name__] + +execute at @s run particle minecraft:dust 0 1 1 0.9 ~ ~1 ~ 0 0 0 0.01 1 force +execute at @s run particle minecraft:block yellow_concrete ~ ~1.62 ~ 0 0.4 0 0 30 force +execute at @s run particle minecraft:block minecraft:light_blue_concrete ~ ~1 ~ 0.05 0.1 0.05 0 3 + +particle_file minecraft:end_rod { + "textures": [ + "minecraft:glitter_7", + "minecraft:glitter_6", + "minecraft:glitter_5", + "minecraft:glitter_4", + "minecraft:glitter_3", + "minecraft:glitter_2", + "minecraft:glitter_1", + "minecraft:glitter_0" + ] +} + +for obj in ["foo", "bar"]: + as @a[scores={obj=1..}] scoreboard players remove @s obj 1 + +x = 8 +scoreboard players set @p tmp -x + +x = 12 +y = 23 +z = 34 + +setblock f"~{x} ~{y} ~{z}" stone +setblock f"~{x}" y f"~{z}" stone + +setblock x y z stone +setblock ~x ~y ~z stone +setblock ^x ^y ^z stone +setblock -x -y -z stone +setblock ~-x ~-y ~-z stone +setblock ^-x ^-y ^-z stone + +for i, x in enumerate("abc"): + say f"{i} {x}" + +major, minor, patch = "1.2.3".split(".") +say major +say minor +say patch + +def check_above(n): + if n > 0: + if block ~ ~n ~ air: + check_above(n - 1) + else: + say nothing above! + +check_above(6) + +math = 0 +say math +def wat(): + global math + import math +wat() +say math.ceil(0.1) + +say bolt implements a __rebind__(rhs) magic method that gets called when you reassign a value to a variable +say let's try it out by manually building a class + +def init_global_score(self, name): + self.name = name + +def add_global_score(self, rhs): + tmp = GlobalScore(generate_id("tmp.{incr}")) + scoreboard players operation tmp.name global = self.name global + tmp += rhs + return tmp + +def iadd_global_score(self, rhs): + if isinstance(rhs, GlobalScore): + scoreboard players operation self.name global += rhs.name global + else: + scoreboard players add self.name global rhs + return self + +def rebind_global_score(self, rhs): + if self is not rhs: + if isinstance(rhs, GlobalScore): + scoreboard players operation self.name global = rhs.name global + else: + scoreboard players set self.name global rhs + return self + +GlobalScore = type("GlobalScore", (), { + "__init__": init_global_score, + "__add__": add_global_score, + "__iadd__": iadd_global_score, + "__rebind__": rebind_global_score, +}) + +a = GlobalScore("a") +a = 123 +a = 456 + +b = GlobalScore("b") +b = 789 +b = a + +a += b + 6 + a +b += 1 + +say you can use nonlocal to create fake classes + +def Counter(x=0): + def incr(): + nonlocal x + x += 1 + return x + return {"incr": incr} + +counter = Counter() +say counter.incr() +say counter.incr() +say Counter(9).incr() + +def foo(msg): + say msg + +if score #temp abc matches 1..: + foo("foo") + +tag_name = ./yolo_funcs + +function_tag tag_name: + values: + - "demo:foo" + +as @p function #tag_name # this runs the tag + +def truth_table(): + for a in [False, True]: + for b in [False, True]: + for c in [False, True]: + for d in [False, True]: + yield (a and b or c and not d) in [True] not in [False] + +say list(truth_table()) + +def not_init(self, value): + self.value = value + +def not_not(self): + say self.value + return self + +NotPrint = type("NotPrint", (), {"__init__": not_init, "__not__": not_not}) +not not not not NotPrint("hello") + +def thing_within(self, container): + for item in container: + say (self == item) + return self + +def thing_contains(self, item): + say (self == item) + return self + +def thing_equal(self, item): + return f"thing == {item}" + +Thing = type("Thing", (), {"__within__": thing_within, "__contains__": thing_contains, "__eq__": thing_equal}) +(Thing() in [1, 2, 3]) in [99] +"world" in ("hello" in Thing()) + +from contextlib import contextmanager + +def dsl_init(self, score, inverted=False): + self.score = score + self.inverted = inverted + +def dsl_not(self): + return DSL(self.score, not self.inverted) + +def dsl_branch(self): + if score @s self.score matches int(not self.inverted): + yield True + +DSL = type("DSL", (), {"__init__": dsl_init, "__not__": dsl_not, "__branch__": contextmanager(dsl_branch)}) + +if DSL("foo"): + say yes + if DSL("bar"): + say with bar +else: + say no + +predicate ./check_scores [ + { + "condition": "minecraft:entity_scores", + "entity": "this", + "scores": { + "foo": 1 + } + }, + { + "condition": "minecraft:entity_scores", + "entity": "killer_player", + "scores": { + "bar": 1 + } + } +] + +def cond_init(self, name=generate_id("tmp{incr}")): + self.name = name + +def cond_str(self): + return self.name + +def cond_not(self): + result = Cond() + result = 1 + unless score global self matches 0: + result = 0 + return result + +def cond_dup(self): + result = Cond() + result = self + return result + +def cond_rebind(self, rhs): + if isinstance(rhs, Cond): + scoreboard players operation global self = global rhs + else: + scoreboard players set global self rhs + return self + +def cond_branch(self): + unless score global self matches 0: + yield True + +Cond = type("Cond", (), { + "__init__": cond_init, + "__str__": cond_str, + "__not__": cond_not, + "__dup__": cond_dup, + "__rebind__": cond_rebind, + "__branch__": contextmanager(cond_branch), +}) + +is_awesome = Cond("is_awesome") +is_awesome = Cond("is_cool") and Cond("is_nice") + +if is_awesome or Cond("force_awesomeness"): + say hello + +from ./utils import something +something() + +from ./utils import load_storage +load_storage(./random_data, "random_data.json") +if data storage ./random_data {value: 42} say json loaded successfully + +custom_model_data = {CustomModelData: 7} +give @s bow{**custom_model_data} + +stone_can_place_on = ["minecraft:dirt"] +give @s stone{CanPlaceOn: [*stone_can_place_on, "minecraft:gravel"], **custom_model_data} +red = {"color": "red"} +tellraw @p ["", {"text": "hey", **red}, *stone_can_place_on, *red.color] + +def testing_decorator(f): + if callable(f): + f("dummy") + else: + say f + return testing_decorator + +@testing_decorator +@testing_decorator(1) +@testing_decorator(2) +@testing_decorator([ + ./abc, + ./def, +]) or None +def f(x): + say f"inner {x}" + +from contextlib import suppress + +with suppress(ZeroDivisionError): + 1 / 0 + +from bolt import Runtime +runtime = ctx.inject(Runtime) + +with runtime.scope() as commands: + tellraw @p "hello" + +say repr(commands[0]) +say commands[0] + +from uuid import UUID +execute as UUID(fields=(1, 2, 3, 4, 5, 6)) function test:test + +from random import Random + +rd = Random() +rd.seed(42) +random_id = UUID(int=rd.getrandbits(128)) +whitelist add random_id + +other_id = "demo:double" +attribute @s minecraft:generic.attack_speed modifier add other_id 0.5 add_multiplied_total + +x = 1 +del x +x = 2 diff --git a/packages/bolt/examples/bolt_basic/src/data/demo/functions/import_a.mcfunction b/packages/bolt/examples/bolt_basic/src/data/demo/functions/import_a.mcfunction new file mode 100644 index 000000000..05054aabf --- /dev/null +++ b/packages/bolt/examples/bolt_basic/src/data/demo/functions/import_a.mcfunction @@ -0,0 +1,18 @@ +from bolt import Runtime + +runtime = ctx.inject(Runtime) + +foo = 42 + +def callback(): + return foo + +def loga(f): + runtime.modules.current.namespace["bar"] *= 2 + say __name__ + say runtime.modules.current_path + say f() + +from ./import_b import logb + +logb(callback) diff --git a/packages/bolt/examples/bolt_basic/src/data/demo/functions/import_b.mcfunction b/packages/bolt/examples/bolt_basic/src/data/demo/functions/import_b.mcfunction new file mode 100644 index 000000000..a45182afb --- /dev/null +++ b/packages/bolt/examples/bolt_basic/src/data/demo/functions/import_b.mcfunction @@ -0,0 +1,18 @@ +from bolt import Runtime + +runtime = ctx.inject(Runtime) + +bar = 7 + +def callback(): + return bar + +def logb(f): + runtime.modules.current.namespace["foo"] *= 2 + say __name__ + say runtime.modules.current_path + say f() + +from ./import_a import loga + +loga(callback) diff --git a/packages/bolt/examples/bolt_basic/src/data/demo/functions/thing.mcfunction b/packages/bolt/examples/bolt_basic/src/data/demo/functions/thing.mcfunction new file mode 100644 index 000000000..31069cc02 --- /dev/null +++ b/packages/bolt/examples/bolt_basic/src/data/demo/functions/thing.mcfunction @@ -0,0 +1,15 @@ +from mecha import Mecha +from bolt import Runtime + +mc = ctx.inject(Mecha) +runtime = ctx.inject(Runtime) + +def do_stuff(a, b): + import math + return a + math.cos(b) + +def call_recursive(): + if score @s loop_again matches 1 function runtime.modules.current_path + +def raw(cmd): + runtime.commands.append(mc.parse(cmd, using="command")) diff --git a/packages/bolt/examples/bolt_basic/src/data/demo/functions/utils.mcfunction b/packages/bolt/examples/bolt_basic/src/data/demo/functions/utils.mcfunction new file mode 100644 index 000000000..ed8bb9ae4 --- /dev/null +++ b/packages/bolt/examples/bolt_basic/src/data/demo/functions/utils.mcfunction @@ -0,0 +1 @@ +say this doesn't conflict with the demo:utils module diff --git a/packages/bolt/examples/bolt_basic/src/data/demo/modules/utils.bolt b/packages/bolt/examples/bolt_basic/src/data/demo/modules/utils.bolt new file mode 100644 index 000000000..3953beb33 --- /dev/null +++ b/packages/bolt/examples/bolt_basic/src/data/demo/modules/utils.bolt @@ -0,0 +1,11 @@ +import json + +def something(): + say f"this is from {__name__}" + +def load_storage(storage_id, filename): + path = ctx.directory / filename + merge function_tag minecraft:load {"values": [generate_path("load_storage")]} + append function generate_path("load_storage"): + data merge storage storage_id json.loads(path.read_text()) + say json loaded! diff --git a/packages/bolt/examples/bolt_chained_comp/beet.yml b/packages/bolt/examples/bolt_chained_comp/beet.yml new file mode 100644 index 000000000..c8a8dd658 --- /dev/null +++ b/packages/bolt/examples/bolt_chained_comp/beet.yml @@ -0,0 +1,6 @@ +require: + - bolt +data_pack: + load: "src" +pipeline: + - mecha diff --git a/packages/bolt/examples/bolt_chained_comp/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_chained_comp/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..49f9675ae --- /dev/null +++ b/packages/bolt/examples/bolt_chained_comp/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,80 @@ +from contextlib import contextmanager +from itertools import combinations + + +class Tmp: + def __init__(self, name = None): + if name is None: + name = ctx.generate.format("tmp{incr}") + self.name = name + + def __dup__(self): + result = Tmp() + result = self + return result + + def __rebind__(self, rhs): + if isinstance(rhs, Tmp): + scoreboard players operation global self = global rhs + else: + scoreboard players set global self int(rhs) + return self + + @contextmanager + def __branch__(self): + unless score global self matches 0: + yield True + + def __not__(self): + result = Tmp() + result = 1 + unless score global self matches 0: + result = 0 + return result + + def __eq__(self, rhs): + result = Tmp() + result = 0 + if isinstance(rhs, Tmp): + if score global self = global rhs: + result = 1 + else: + if score global self matches int(rhs): + result = 1 + return result + + def __str__(self): + return self.name + + +for s in [7, Tmp("seven")]: + if s == s and s == s and s == s: + say 1 + + if s == s == s == s: + say 2 + + +if 1 == (Tmp("foo") == Tmp("bar")) == Tmp("thing"): + say 3 + + +for a, b in combinations([123, 456, Tmp("foo"), Tmp("bar")], 2): + raw # + raw f"# check {a}, {b}" + say (a == a == a == a) + say (a == a == a == b) + say (a == a == b == a) + say (a == a == b == b) + say (a == b == a == a) + say (a == b == a == b) + say (a == b == b == a) + say (a == b == b == b) + say (b == a == a == a) + say (b == a == a == b) + say (b == a == b == a) + say (b == a == b == b) + say (b == b == a == a) + say (b == b == a == b) + say (b == b == b == a) + say (b == b == b == b) diff --git a/packages/bolt/examples/bolt_chicken/beet.yml b/packages/bolt/examples/bolt_chicken/beet.yml new file mode 100644 index 000000000..e15d7678c --- /dev/null +++ b/packages/bolt/examples/bolt_chicken/beet.yml @@ -0,0 +1,6 @@ +require: + - bolt +data_pack: + load: [src] +pipeline: + - mecha diff --git a/packages/bolt/examples/bolt_chicken/src/data/demo/functions/chicken.mcfunction b/packages/bolt/examples/bolt_chicken/src/data/demo/functions/chicken.mcfunction new file mode 100644 index 000000000..1103a5d22 --- /dev/null +++ b/packages/bolt/examples/bolt_chicken/src/data/demo/functions/chicken.mcfunction @@ -0,0 +1,4 @@ +from ./utils import summon_chicken_army + +say Go forth, my minions! +summon_chicken_army(16) diff --git a/packages/bolt/examples/bolt_chicken/src/data/demo/modules/utils.bolt b/packages/bolt/examples/bolt_chicken/src/data/demo/modules/utils.bolt new file mode 100644 index 000000000..0ec3cea6a --- /dev/null +++ b/packages/bolt/examples/bolt_chicken/src/data/demo/modules/utils.bolt @@ -0,0 +1,18 @@ +infinite_invisibility = { + Id: 14, + Duration: 999999, + Amplifier: 1, + ShowParticles: false, +} + +def summon_chicken_army(n): + for i in range(n): + summon chicken ~i ~ ~ { + Tags: [f"quack{i}"], + IsChickenJockey: true, + Passengers: [{ + id: zombie, + IsBaby: true, + ActiveEffects: [infinite_invisibility] + }] + } diff --git a/packages/bolt/examples/bolt_circular/beet.yml b/packages/bolt/examples/bolt_circular/beet.yml new file mode 100644 index 000000000..487cbcf89 --- /dev/null +++ b/packages/bolt/examples/bolt_circular/beet.yml @@ -0,0 +1,10 @@ +require: + - bolt +data_pack: + load: + data/demo/modules: src +pipeline: + - mecha +meta: + bolt: + entrypoint: "demo:main" diff --git a/packages/bolt/examples/bolt_circular/src/bar.bolt b/packages/bolt/examples/bolt_circular/src/bar.bolt new file mode 100644 index 000000000..a6568c5ba --- /dev/null +++ b/packages/bolt/examples/bolt_circular/src/bar.bolt @@ -0,0 +1,6 @@ +import ./foo as foo + +VALUE = 33 + +def b(): + return foo.VALUE diff --git a/packages/bolt/examples/bolt_circular/src/foo.bolt b/packages/bolt/examples/bolt_circular/src/foo.bolt new file mode 100644 index 000000000..16fab9389 --- /dev/null +++ b/packages/bolt/examples/bolt_circular/src/foo.bolt @@ -0,0 +1,6 @@ +import ./bar as bar + +VALUE = 42 + +def a(): + return bar.VALUE diff --git a/packages/bolt/examples/bolt_circular/src/main.bolt b/packages/bolt/examples/bolt_circular/src/main.bolt new file mode 100644 index 000000000..a8e7f18bf --- /dev/null +++ b/packages/bolt/examples/bolt_circular/src/main.bolt @@ -0,0 +1,6 @@ +from ./foo import a +from ./bar import b + +function ./thing: + say a() + say b() diff --git a/packages/bolt/examples/bolt_class/beet.yml b/packages/bolt/examples/bolt_class/beet.yml new file mode 100644 index 000000000..c8a8dd658 --- /dev/null +++ b/packages/bolt/examples/bolt_class/beet.yml @@ -0,0 +1,6 @@ +require: + - bolt +data_pack: + load: "src" +pipeline: + - mecha diff --git a/packages/bolt/examples/bolt_class/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_class/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..766c01387 --- /dev/null +++ b/packages/bolt/examples/bolt_class/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,90 @@ +from dataclasses import dataclass + +class Foo: + def __init__(self, name): + self.name = name + +say f"hello {Foo("steve").name}" + +class Bar(Foo): + def __init__(self): + super().__init__("alex") + +say f"hello {Bar().name}" + +class A: + pass +class B: + pass +class C(A, B): + pass + +say C.mro() + +things = [] + +@things.append +@str.upper +@str +@object.__new__ +class Thing1: + def __str__(self): + return "hello" + +@things.append +@str.upper +@str +@object.__new__ +class Thing2: + def __str__(self): + return "world" + +say things + +class Weird: + say hello + +class Ayy: + def bar(self): + say bar + +Ayy().bar() + +class GG: + def __init__(self): + Hmm().damn() + +class Hmm: + value = 42 + + def damn(self): + say value + say self.value + +GG() + +class A: + def b(self): + say b + return B() + +class B: + def a(self): + say a + return A() + +A().b().a() +B().a().b() + +@dataclass +class PreviouslyNotPossible: + text: str + data: str + + @staticmethod + def you_can_do_this(): + setblock = 0 + setblock setblock setblock setblock air + +say PreviouslyNotPossible("no", "conflict") +PreviouslyNotPossible.you_can_do_this() diff --git a/packages/bolt/examples/bolt_class/src/data/demo/functions/pydantic.mcfunction b/packages/bolt/examples/bolt_class/src/data/demo/functions/pydantic.mcfunction new file mode 100644 index 000000000..6c544ada4 --- /dev/null +++ b/packages/bolt/examples/bolt_class/src/data/demo/functions/pydantic.mcfunction @@ -0,0 +1,7 @@ +from pydantic import BaseModel + +class Model(BaseModel, frozen=True): + a: int + b: str + +say Model(a="123", b="123").model_dump_json() diff --git a/packages/bolt/examples/bolt_compile_cached/beet.yml b/packages/bolt/examples/bolt_compile_cached/beet.yml new file mode 100644 index 000000000..ba783af80 --- /dev/null +++ b/packages/bolt/examples/bolt_compile_cached/beet.yml @@ -0,0 +1,7 @@ +require: + - bolt +data_pack: + load: "src" +pipeline: + - mecha + - compile_cached diff --git a/packages/bolt/examples/bolt_compile_cached/cached/slow.mcfunction b/packages/bolt/examples/bolt_compile_cached/cached/slow.mcfunction new file mode 100644 index 000000000..587ef5b6a --- /dev/null +++ b/packages/bolt/examples/bolt_compile_cached/cached/slow.mcfunction @@ -0,0 +1,2 @@ +for i in range(10): + say f"something slow {i}" diff --git a/packages/bolt/examples/bolt_compile_cached/compile_cached.py b/packages/bolt/examples/bolt_compile_cached/compile_cached.py new file mode 100644 index 000000000..3cedb0c47 --- /dev/null +++ b/packages/bolt/examples/bolt_compile_cached/compile_cached.py @@ -0,0 +1,23 @@ +from beet import Context, sandbox, subproject + + +def beet_default(ctx: Context): + ctx.require(sandbox(compile_cached)) + + +def compile_cached(ctx: Context): + cached_data_pack = ctx.cache["compile_cached"].directory / "data_pack" + + if cached_data_pack.is_dir(): + ctx.data.load(cached_data_pack) + else: + ctx.require( + subproject( + { + "require": ["bolt"], + "data_pack": {"load": {"data/demo/functions/cached": "cached"}}, + "pipeline": ["mecha"], + } + ) + ) + ctx.data.save(path=cached_data_pack, overwrite=True) diff --git a/packages/bolt/examples/bolt_compile_cached/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_compile_cached/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..516a5d0c0 --- /dev/null +++ b/packages/bolt/examples/bolt_compile_cached/src/data/demo/functions/foo.mcfunction @@ -0,0 +1 @@ +say hello diff --git a/packages/bolt/examples/bolt_custom_provider/beet.yml b/packages/bolt/examples/bolt_custom_provider/beet.yml new file mode 100644 index 000000000..0186c3c15 --- /dev/null +++ b/packages/bolt/examples/bolt_custom_provider/beet.yml @@ -0,0 +1,7 @@ +require: + - custom_provider +data_pack: + load: "src" +pipeline: + - mecha + - custom_provider.remove_custom_modules diff --git a/packages/bolt/examples/bolt_custom_provider/custom_provider.py b/packages/bolt/examples/bolt_custom_provider/custom_provider.py new file mode 100644 index 000000000..a19c776f9 --- /dev/null +++ b/packages/bolt/examples/bolt_custom_provider/custom_provider.py @@ -0,0 +1,23 @@ +from typing import ClassVar + +from beet import Context, NamespaceFileScope, TextFile +from mecha import FileTypeCompilationUnitProvider, Mecha + + +class Custom(TextFile): + scope: ClassVar[NamespaceFileScope] = ("custom",) + extension: ClassVar[str] = ".bolt" + + +def beet_default(ctx: Context): + ctx.data.extend_namespace.append(Custom) + + ctx.require("bolt") + + mc = ctx.inject(Mecha) + mc.providers = [FileTypeCompilationUnitProvider([Custom])] + + +def remove_custom_modules(ctx: Context): + for pack in [ctx.data, *ctx.data.overlays.values()]: + pack[Custom].clear() diff --git a/packages/bolt/examples/bolt_custom_provider/src/data/demo/custom/stuff.bolt b/packages/bolt/examples/bolt_custom_provider/src/data/demo/custom/stuff.bolt new file mode 100644 index 000000000..c95cfeef9 --- /dev/null +++ b/packages/bolt/examples/bolt_custom_provider/src/data/demo/custom/stuff.bolt @@ -0,0 +1,2 @@ +function ~/abc: + say 123 diff --git a/packages/bolt/examples/bolt_custom_provider/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_custom_provider/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..cbf9ea46c --- /dev/null +++ b/packages/bolt/examples/bolt_custom_provider/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,3 @@ +value = 123 +print(value) +say this should be left untouched diff --git a/packages/bolt/examples/bolt_debug/beet.yml b/packages/bolt/examples/bolt_debug/beet.yml new file mode 100644 index 000000000..b17a0fd69 --- /dev/null +++ b/packages/bolt/examples/bolt_debug/beet.yml @@ -0,0 +1,6 @@ +require: + - bolt.contrib.debug_codegen +data_pack: + load: "src" +pipeline: + - mecha diff --git a/packages/bolt/examples/bolt_debug/src/data/demo/functions/bar.mcfunction b/packages/bolt/examples/bolt_debug/src/data/demo/functions/bar.mcfunction new file mode 100644 index 000000000..e8e9320c0 --- /dev/null +++ b/packages/bolt/examples/bolt_debug/src/data/demo/functions/bar.mcfunction @@ -0,0 +1 @@ +say hello without anything fancy diff --git a/packages/bolt/examples/bolt_debug/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_debug/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..8ed547d67 --- /dev/null +++ b/packages/bolt/examples/bolt_debug/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,4 @@ +def say_hello(): + say hello + +say_hello() diff --git a/packages/bolt/examples/bolt_defer/beet.yml b/packages/bolt/examples/bolt_defer/beet.yml new file mode 100644 index 000000000..c8a8dd658 --- /dev/null +++ b/packages/bolt/examples/bolt_defer/beet.yml @@ -0,0 +1,6 @@ +require: + - bolt +data_pack: + load: "src" +pipeline: + - mecha diff --git a/packages/bolt/examples/bolt_defer/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_defer/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..a4c374f1b --- /dev/null +++ b/packages/bolt/examples/bolt_defer/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,31 @@ +from mecha import AstString +from bolt.contrib.defer import Defer, AstDefer + +defer = ctx.inject(Defer) + +counter = 0 +def foo(): + global counter + say counter + counter += 1 + +say start +foo() +foo() +defer(foo) +defer(foo) +say end + +counter = 7 + +@defer +def bar(): + function ./bar: + defer(foo) + global counter + counter *= 2 + +def format_deferred_tag(): + return AstString(value=f"thing{counter}") + +tag @p add AstDefer(callback=format_deferred_tag) diff --git a/packages/bolt/examples/bolt_docstring/beet.yml b/packages/bolt/examples/bolt_docstring/beet.yml new file mode 100644 index 000000000..c8a8dd658 --- /dev/null +++ b/packages/bolt/examples/bolt_docstring/beet.yml @@ -0,0 +1,6 @@ +require: + - bolt +data_pack: + load: "src" +pipeline: + - mecha diff --git a/packages/bolt/examples/bolt_docstring/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_docstring/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..3fb45c14e --- /dev/null +++ b/packages/bolt/examples/bolt_docstring/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,34 @@ +value = """ + hello + world +""" + +say repr(value) + +def f(): + """My function""" + return 42 + +say f() +say f.__doc__ + +def g(a=123): + """My other function""" + return a + +say g() +say g.__doc__ + +class A: + """This is a class. + + It doesn't do anything. + """ + +say repr(A.__doc__) + +value = r""" + foo\n\nbar +""" + +say repr(value) diff --git a/packages/bolt/examples/bolt_flat/beet.yml b/packages/bolt/examples/bolt_flat/beet.yml new file mode 100644 index 000000000..a098ccb1c --- /dev/null +++ b/packages/bolt/examples/bolt_flat/beet.yml @@ -0,0 +1,10 @@ +require: + - bolt +data_pack: + load: + data/demo/modules: "@demo" +pipeline: + - mecha +meta: + bolt: + entrypoint: "demo:main" diff --git a/beet/contrib/__init__.py b/packages/bolt/examples/bolt_flat/demo/__init__.py similarity index 100% rename from beet/contrib/__init__.py rename to packages/bolt/examples/bolt_flat/demo/__init__.py diff --git a/packages/bolt/examples/bolt_flat/demo/main.bolt b/packages/bolt/examples/bolt_flat/demo/main.bolt new file mode 100644 index 000000000..0a04f4673 --- /dev/null +++ b/packages/bolt/examples/bolt_flat/demo/main.bolt @@ -0,0 +1,21 @@ +from demo.math import add + +function ./foo: + say add(2, 3) + +function ./oby: + stat_list = [ + "damage", + "attack_speed", + "multishot", + "piercing" + ] + + if data storage firework:temp data.item.tag.aftermath.stats: + + for stat in stat_list: + if data storage firework:temp f'data.item.tag.aftermath.stats.{stat}': + say f'{stat}, present' + + unless data storage firework:temp data.item.tag.aftermath.stats: + say 'hi' diff --git a/packages/bolt/examples/bolt_flat/demo/math.py b/packages/bolt/examples/bolt_flat/demo/math.py new file mode 100644 index 000000000..e1829c359 --- /dev/null +++ b/packages/bolt/examples/bolt_flat/demo/math.py @@ -0,0 +1,2 @@ +def add(a: int, b: int) -> int: + return a + b diff --git a/packages/bolt/examples/bolt_function_signature/beet.yml b/packages/bolt/examples/bolt_function_signature/beet.yml new file mode 100644 index 000000000..96d33d4a9 --- /dev/null +++ b/packages/bolt/examples/bolt_function_signature/beet.yml @@ -0,0 +1,7 @@ +require: + - bolt +data_pack: + load: + data/demo/functions/foo.mcfunction: "foo.mcfunction" +pipeline: + - mecha diff --git a/packages/bolt/examples/bolt_function_signature/foo.mcfunction b/packages/bolt/examples/bolt_function_signature/foo.mcfunction new file mode 100644 index 000000000..c5a866cd0 --- /dev/null +++ b/packages/bolt/examples/bolt_function_signature/foo.mcfunction @@ -0,0 +1,35 @@ +def f(): + pass + +def f(a): + pass + +def f(a=1): + pass + +def f(a=1,): + pass + +def f(a=1, /): + pass + +def f(a, /, b): + pass + +def f(a=1, /, b=2): + pass + +def f(a=1, /, b=2, *args): + pass + +def f(a, /, b, *, c): + pass + +def f(a=1, /, b=2, *, c=3): + pass + +def f(a=1, /, b=2, *, c=3, **kwargs): + pass + +def f(a=1, /, b=2, *args, c=3, **kwargs): + pass diff --git a/packages/bolt/examples/bolt_generated/beet.yml b/packages/bolt/examples/bolt_generated/beet.yml new file mode 100644 index 000000000..9d8b1ea5d --- /dev/null +++ b/packages/bolt/examples/bolt_generated/beet.yml @@ -0,0 +1,7 @@ +require: + - bolt +data_pack: + load: [src] +pipeline: + - demo + - mecha diff --git a/packages/bolt/examples/bolt_generated/demo.py b/packages/bolt/examples/bolt_generated/demo.py new file mode 100644 index 000000000..513680caa --- /dev/null +++ b/packages/bolt/examples/bolt_generated/demo.py @@ -0,0 +1,11 @@ +from beet import Context, Function + +generated_code = """ +execute if score #smithed.core load.status matches 1 function ./0: + data merge storage smithed:log {message:'["Could not find ",{"text":"#smithed.prevent_aggression 0.0.1","color":"red"}]',type:'ERROR'} + function #smithed:core/pub/technical/tools/log +""" + + +def beet_default(ctx: Context): + ctx.data["demo:generated"] = Function(generated_code) diff --git a/packages/bolt/examples/bolt_generated/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_generated/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..4f5df804d --- /dev/null +++ b/packages/bolt/examples/bolt_generated/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,2 @@ +a = 123 +say a diff --git a/packages/bolt/examples/bolt_json/beet.yml b/packages/bolt/examples/bolt_json/beet.yml new file mode 100644 index 000000000..df7152758 --- /dev/null +++ b/packages/bolt/examples/bolt_json/beet.yml @@ -0,0 +1,7 @@ +require: + - mecha.contrib.json_files + - bolt +data_pack: + load: "src" +pipeline: + - mecha diff --git a/packages/bolt/examples/bolt_json/src/data/minecraft/tags/functions/load.json b/packages/bolt/examples/bolt_json/src/data/minecraft/tags/functions/load.json new file mode 100644 index 000000000..17e4fdfc1 --- /dev/null +++ b/packages/bolt/examples/bolt_json/src/data/minecraft/tags/functions/load.json @@ -0,0 +1,8 @@ +name = demo:foo + +{ + "values": [name] +} + +function name: + say hello diff --git a/packages/bolt/examples/bolt_lazy/beet.yml b/packages/bolt/examples/bolt_lazy/beet.yml new file mode 100644 index 000000000..9e4a3a870 --- /dev/null +++ b/packages/bolt/examples/bolt_lazy/beet.yml @@ -0,0 +1,11 @@ +require: + - bolt +data_pack: + load: "src" +pipeline: + - mecha +meta: + bolt: + entrypoint: + - "*" + - "!*.lazy" diff --git a/packages/bolt/examples/bolt_lazy/src/data/demo/modules/foo.bolt b/packages/bolt/examples/bolt_lazy/src/data/demo/modules/foo.bolt new file mode 100644 index 000000000..905f2167f --- /dev/null +++ b/packages/bolt/examples/bolt_lazy/src/data/demo/modules/foo.bolt @@ -0,0 +1 @@ +import ./hello1.lazy as _ diff --git a/packages/bolt/examples/bolt_lazy/src/data/demo/modules/hello1.lazy.bolt b/packages/bolt/examples/bolt_lazy/src/data/demo/modules/hello1.lazy.bolt new file mode 100644 index 000000000..74ee6a307 --- /dev/null +++ b/packages/bolt/examples/bolt_lazy/src/data/demo/modules/hello1.lazy.bolt @@ -0,0 +1,2 @@ +function ./hello1: + say hello1 diff --git a/packages/bolt/examples/bolt_lazy/src/data/demo/modules/hello2.lazy.bolt b/packages/bolt/examples/bolt_lazy/src/data/demo/modules/hello2.lazy.bolt new file mode 100644 index 000000000..babb9d474 --- /dev/null +++ b/packages/bolt/examples/bolt_lazy/src/data/demo/modules/hello2.lazy.bolt @@ -0,0 +1,2 @@ +function ./hello2: + say hello2 diff --git a/packages/bolt/examples/bolt_lectern_prelude/beet.yml b/packages/bolt/examples/bolt_lectern_prelude/beet.yml new file mode 100644 index 000000000..fd613120e --- /dev/null +++ b/packages/bolt/examples/bolt_lectern_prelude/beet.yml @@ -0,0 +1,11 @@ +require: + - bolt +pipeline: + - lectern + - mecha + - beet.contrib.cleanup_overlays +meta: + lectern: + load: "source.txt" + bolt: + prelude: "demo:prelude" diff --git a/packages/bolt/examples/bolt_lectern_prelude/source.txt b/packages/bolt/examples/bolt_lectern_prelude/source.txt new file mode 100644 index 000000000..ce8fc5382 --- /dev/null +++ b/packages/bolt/examples/bolt_lectern_prelude/source.txt @@ -0,0 +1,12 @@ +@module demo:prelude +stuff = 123 + +@function demo:foo +say stuff + +@overlay dummy +@module demo:prelude +stuff = 456 + +@function demo:foo +say stuff diff --git a/packages/bolt/examples/bolt_loop/beet.yml b/packages/bolt/examples/bolt_loop/beet.yml new file mode 100644 index 000000000..c8a8dd658 --- /dev/null +++ b/packages/bolt/examples/bolt_loop/beet.yml @@ -0,0 +1,6 @@ +require: + - bolt +data_pack: + load: "src" +pipeline: + - mecha diff --git a/packages/bolt/examples/bolt_loop/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_loop/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..6ab134925 --- /dev/null +++ b/packages/bolt/examples/bolt_loop/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,90 @@ +from contextlib import contextmanager + + +class LoopAgainAtRuntime: + def __init__(self, name: str): + self.name = name + + def __bool__(self): + function self.name + return False + + +class Tmp: + def __init__(self, name: str = None): + if name is None: + name = ctx.generate.format("tmp{incr}") + self.name = name + + def __dup__(self): + result = Tmp() + result = self + return result + + def __rebind__(self, rhs: object): + if self is rhs: + return self + if isinstance(rhs, Tmp): + scoreboard players operation self.name global = rhs.name global + else: + scoreboard players set self.name global int(rhs) + return self + + @contextmanager + def __branch__(self): + unless score self.name global matches 0: + yield True + + @contextmanager + def __loop__(self): + name = ctx.generate.format(~/ + "/loop{incr}") + execute function name: + yield LoopAgainAtRuntime(name) + + def __not__(self): + result = Tmp() + result = 1 + unless score self.name global matches 0: + result = 0 + return result + + def __eq__(self, rhs: object): + result = Tmp() + result = 0 + if isinstance(rhs, Tmp): + if score self.name global = rhs.name global: + result = 1 + else: + if score self.name global matches int(rhs): + result = 1 + return result + + def __iadd__(self, rhs: object): + if isinstance(rhs, Tmp): + scoreboard players operation self.name global += rhs.name global + else: + scoreboard players add self.name global int(rhs) + return self + + def __str__(self): + return self.name + + +def display(value: object): + if isinstance(value, Tmp): + tellraw @a {"score": {"name": value.name, "objective": "global"}} + else: + tellraw @a {"text": f"{value}"} + + +def f(i): + while not i == 4: + display(i) + i += 1 + +a = 0 +f(a) + +b = Tmp("b") +b = 0 +f(b) diff --git a/packages/bolt/examples/bolt_loop/src/data/demo/functions/init.mcfunction b/packages/bolt/examples/bolt_loop/src/data/demo/functions/init.mcfunction new file mode 100644 index 000000000..ff3d1e3cc --- /dev/null +++ b/packages/bolt/examples/bolt_loop/src/data/demo/functions/init.mcfunction @@ -0,0 +1,5 @@ +append function_tag minecraft:load { + "values": [__name__] +} + +scoreboard objectives add global dummy diff --git a/packages/bolt/examples/bolt_macro/beet.yml b/packages/bolt/examples/bolt_macro/beet.yml new file mode 100644 index 000000000..c8a8dd658 --- /dev/null +++ b/packages/bolt/examples/bolt_macro/beet.yml @@ -0,0 +1,6 @@ +require: + - bolt +data_pack: + load: "src" +pipeline: + - mecha diff --git a/packages/bolt/examples/bolt_macro/src/data/demo/functions/bar.mcfunction b/packages/bolt/examples/bolt_macro/src/data/demo/functions/bar.mcfunction new file mode 100644 index 000000000..97d2732b7 --- /dev/null +++ b/packages/bolt/examples/bolt_macro/src/data/demo/functions/bar.mcfunction @@ -0,0 +1,29 @@ +from ./foo import foo, setblock, do_twice, >>, execute, tellraw, ! + +foo +foo 4 +foo 3 double +foo 3 hello @a[ + scores = { + x = 42 + } +] + +setblock +setblock stone +setblock 1 2 3 +setblock 1 2 3 stone + +do_twice: + say this is repeated twice + +>> Custom symbols work +as @a run >> Even inside execute +as @a >> Overload execute to allow implicit "run" + +tellraw @s from ./dummy_message + +! @e ! + +from ./foo import abc +abc 42 diff --git a/packages/bolt/examples/bolt_macro/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_macro/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..626aaedc8 --- /dev/null +++ b/packages/bolt/examples/bolt_macro/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,92 @@ +from mecha import AstCommand, AstChildren +from bolt import Runtime + +runtime = ctx.inject(Runtime) + +macro foo: + say macro foo + +foo + +macro foo bar=brigadier:integer: + if bar.value > 0: + say f"macro foo {bar.value}" + foo (bar.value - 1) + else: + foo + +foo 4 + +macro foo bar=brigadier:integer double: + foo (bar.value * 2) + +foo 3 double + +macro foo bar=brigadier:integer message=minecraft:message: + foo bar + say message + +foo 3 hello @a[ + scores = { + x = 42 + } +] + +macro setblock: + setblock air +macro setblock block=minecraft:block_state: + setblock ~ ~ ~ block +macro setblock pos=minecraft:block_pos: + setblock pos air + +setblock +setblock stone +setblock 1 2 3 +setblock 1 2 3 stone + +macro do_twice body=mecha:nested_root: + yield body + return body + +do_twice: + say this is repeated twice + +macro >> text=brigadier:string{"type": "greedy"}: + tellraw @p {"text": text.value} + +>> Custom symbols work +as @a run >> Even inside execute + +macro execute >> text=brigadier:string{"type": "greedy"}: + with runtime.scope() as subcommand: + >> text + return AstCommand(identifier="execute:run:subcommand", arguments=AstChildren(subcommand)) + +as @a >> Overload execute to allow implicit "run" + +macro tellraw + targets=minecraft:entity{ + "type": "players", + "amount": "multiple" + } + from + path=minecraft:resource_location: + tellraw targets {"text": f"from {path.get_canonical_value()}"} + +tellraw @s from ./dummy_message + +macro ! targets=minecraft:entity{"type": "entities", "amount": "multiple"} !: + macro rip: + kill targets + def do_it(): + rip + do_it() + +! @e ! + +macro abc num1=brigadier:integer: + macro def num2=brigadier:integer: + say num2 + def num1 + +abc 42 diff --git a/packages/bolt/examples/bolt_macro/src/data/demo/functions/redirect.mcfunction b/packages/bolt/examples/bolt_macro/src/data/demo/functions/redirect.mcfunction new file mode 100644 index 000000000..df761c5a7 --- /dev/null +++ b/packages/bolt/examples/bolt_macro/src/data/demo/functions/redirect.mcfunction @@ -0,0 +1,34 @@ +from mecha import AstCommand, AstChildren +from bolt import Runtime + +macro repeat n=brigadier:integer command=subcommand: + execute: + for _ in range(n.value): + yield command + +repeat 2 as @a run repeat 3 say ok +repeat 3 say world + +n = 1 +repeat n if score foo bar matches 1 say over! + +macro as at + targets=minecraft:entity{"type": "entities", "amount": "multiple"} + clause=subcommand{"redirect": ["execute"]}: + as targets at @s: + yield AstCommand(identifier="execute:subcommand", arguments=AstChildren([clause])) + +as at @e[type=pig] if block ~ ~ ~ water say blbllblb +as at @e[type=bat] setblock ~ ~ ~ lava +if score foo bar matches 1 run as at @e[type=chicken] kill @e[type=fox, distance=..4] + +macro execute as at + targets=minecraft:entity{"type": "entities", "amount": "multiple"} + clause=subcommand{"redirect": ["execute"]}: + runtime = ctx.inject(Runtime) + with runtime.scope() as commands: + as at targets: + yield AstCommand(identifier="execute:subcommand", arguments=AstChildren([clause])) + return commands[0].arguments[-1] + +if score foo bar matches 1 as at @e[type=chicken] kill @e[type=fox, distance=..4] diff --git a/packages/bolt/examples/bolt_macro_perf/beet.yml b/packages/bolt/examples/bolt_macro_perf/beet.yml new file mode 100644 index 000000000..c8a8dd658 --- /dev/null +++ b/packages/bolt/examples/bolt_macro_perf/beet.yml @@ -0,0 +1,6 @@ +require: + - bolt +data_pack: + load: "src" +pipeline: + - mecha diff --git a/packages/bolt/examples/bolt_macro_perf/src/data/demo/functions/export.mcfunction b/packages/bolt/examples/bolt_macro_perf/src/data/demo/functions/export.mcfunction new file mode 100644 index 000000000..9cf403809 --- /dev/null +++ b/packages/bolt/examples/bolt_macro_perf/src/data/demo/functions/export.mcfunction @@ -0,0 +1,4 @@ +# Importing macros doesn't cause a command tree update when there is no code below + +from ./lib import foo1, foo2 +from ./lib import foo3 diff --git a/packages/bolt/examples/bolt_macro_perf/src/data/demo/functions/func1.mcfunction b/packages/bolt/examples/bolt_macro_perf/src/data/demo/functions/func1.mcfunction new file mode 100644 index 000000000..7593c5e37 --- /dev/null +++ b/packages/bolt/examples/bolt_macro_perf/src/data/demo/functions/func1.mcfunction @@ -0,0 +1,8 @@ +# Importing multiple macros in a row will update the command tree in a single batch + +from ./lib import foo1, foo2 +from ./lib import foo3 + +foo1 +foo2 +foo3 diff --git a/packages/bolt/examples/bolt_macro_perf/src/data/demo/functions/func2.mcfunction b/packages/bolt/examples/bolt_macro_perf/src/data/demo/functions/func2.mcfunction new file mode 100644 index 000000000..1388fe44a --- /dev/null +++ b/packages/bolt/examples/bolt_macro_perf/src/data/demo/functions/func2.mcfunction @@ -0,0 +1,7 @@ +# If multiple scopes end up using the same set of macros they will share the same underlying command spec + +from ./export import foo1, foo2, foo3 + +foo1 +foo2 +foo3 diff --git a/packages/bolt/examples/bolt_macro_perf/src/data/demo/functions/lib.mcfunction b/packages/bolt/examples/bolt_macro_perf/src/data/demo/functions/lib.mcfunction new file mode 100644 index 000000000..887e5383c --- /dev/null +++ b/packages/bolt/examples/bolt_macro_perf/src/data/demo/functions/lib.mcfunction @@ -0,0 +1,13 @@ +# Defining multiple macros in a row will update the command tree in a single batch + +macro foo1: + say foo1 + +macro foo2: + say foo2 + +macro foo3: + say foo3 + +macro foo4: + say foo4 diff --git a/packages/bolt/examples/bolt_memo/beet.yml b/packages/bolt/examples/bolt_memo/beet.yml new file mode 100644 index 000000000..12028f24c --- /dev/null +++ b/packages/bolt/examples/bolt_memo/beet.yml @@ -0,0 +1,27 @@ +pipeline: + - require: + - bolt + data_pack: + load: "src" + pipeline: + - mecha + - beet.contrib.rename_files + - beet.contrib.find_replace + meta: + rename_files: + data_pack: + match: "demo:*" + find: "demo:([a-z_/]+)" + replace: 'demo:ref/\1' + find_replace: + data_pack: + match: "demo:*" + substitute: + find: "demo:([a-z_/]+)" + replace: 'demo:ref/\1' + - require: + - bolt + data_pack: + load: "src" + pipeline: + - mecha diff --git a/packages/bolt/examples/bolt_memo/src/data/demo/functions/bar.mcfunction b/packages/bolt/examples/bolt_memo/src/data/demo/functions/bar.mcfunction new file mode 100644 index 000000000..e393409df --- /dev/null +++ b/packages/bolt/examples/bolt_memo/src/data/demo/functions/bar.mcfunction @@ -0,0 +1,40 @@ +from beet import Function + +as @a: + memo: + say hello + +memo dope=1: + dope + +x = 42 +memo x: + for i in range(x): + as @e[type=pig, scores={foo=1.., bar=i}, tag=okok] at @s[] + anchored eyes align xyz positioned ~ ~1 ~ if block ^ ^ ^1 stone expand: + say i + setblock ~ ~ ~i air + +def make_tree(n=0): + memo count=42, n: + total = count + n + for node in generate_tree(range(total), name=f"tree_{total}"): + append function node.parent: + if node.partition(): + if score @s thingy matches node.range function node.children + else: + if score @s thingy matches node.range say node.value + +make_tree() + +say ok +say bye + +make_tree() +make_tree(15) + +memo: + ctx.generate("{hash}", Function(["say uwu"])) + function_tag demo:nothing { + "values": [__name__] + } diff --git a/packages/bolt/examples/bolt_memo/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_memo/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..d1990b4c5 --- /dev/null +++ b/packages/bolt/examples/bolt_memo/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,25 @@ +memo: + pass + +bop = 123 +memo bop, foo=bop, foo: + thing = foo + function ./ayy: + say thing + say foo + +a = (1, 2) +a = (a, a) +a = (a, a) + +memo a, a: + say a + +say wat + +a = 1 +b = 2 +memo a, b: + say (a + b) + memo b: + say b diff --git a/packages/bolt/examples/bolt_memo/src/data/demo/functions/vars.mcfunction b/packages/bolt/examples/bolt_memo/src/data/demo/functions/vars.mcfunction new file mode 100644 index 000000000..048c26ca1 --- /dev/null +++ b/packages/bolt/examples/bolt_memo/src/data/demo/functions/vars.mcfunction @@ -0,0 +1,23 @@ +g_uwu = 1 +memo: + g_uwu = 5 +say g_uwu + +def ok(): + memo: + value = 10 + global g_uwu + g_uwu = value + +ok() +say g_uwu + +def fib(n): + if n <= 1: + return n + memo n: + result = fib(n - 1) + fib(n - 2) + return result + +for i in range(50): + say f"fib({i}) = {fib(i)}" diff --git a/packages/bolt/examples/bolt_memo2/beet.yml b/packages/bolt/examples/bolt_memo2/beet.yml new file mode 100644 index 000000000..c8a8dd658 --- /dev/null +++ b/packages/bolt/examples/bolt_memo2/beet.yml @@ -0,0 +1,6 @@ +require: + - bolt +data_pack: + load: "src" +pipeline: + - mecha diff --git a/packages/bolt/examples/bolt_memo2/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_memo2/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..ef95b9b06 --- /dev/null +++ b/packages/bolt/examples/bolt_memo2/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,4 @@ +say ok +memo: + append function __name__: + say wat diff --git a/packages/bolt/examples/bolt_memo3/beet.yml b/packages/bolt/examples/bolt_memo3/beet.yml new file mode 100644 index 000000000..c8a8dd658 --- /dev/null +++ b/packages/bolt/examples/bolt_memo3/beet.yml @@ -0,0 +1,6 @@ +require: + - bolt +data_pack: + load: "src" +pipeline: + - mecha diff --git a/packages/bolt/examples/bolt_memo3/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_memo3/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..f66847337 --- /dev/null +++ b/packages/bolt/examples/bolt_memo3/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,7 @@ +n = 5 +execute function ~/{n}: + say 1 + memo n: + say 2 + append function ~/: + say 3 diff --git a/packages/bolt/examples/bolt_memo4/beet.yml b/packages/bolt/examples/bolt_memo4/beet.yml new file mode 100644 index 000000000..c8a8dd658 --- /dev/null +++ b/packages/bolt/examples/bolt_memo4/beet.yml @@ -0,0 +1,6 @@ +require: + - bolt +data_pack: + load: "src" +pipeline: + - mecha diff --git a/packages/bolt/examples/bolt_memo4/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_memo4/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..6cd6abb51 --- /dev/null +++ b/packages/bolt/examples/bolt_memo4/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,13 @@ +def stuff(): + memo: + append function __name__: + say something + +say before +stuff() +stuff() +say after + +function ./bar: + say bop + stuff() diff --git a/packages/bolt/examples/bolt_merge/beet.yml b/packages/bolt/examples/bolt_merge/beet.yml new file mode 100644 index 000000000..d99dcb326 --- /dev/null +++ b/packages/bolt/examples/bolt_merge/beet.yml @@ -0,0 +1,8 @@ +data_pack: + load: ["pack_1", "pack_2"] +require: + - bolt + - mecha.contrib.messages + - beet.contrib.auto_yaml +pipeline: + - mecha diff --git a/packages/bolt/examples/bolt_merge/pack_1/data/pack_1/functions/bob.mcfunction b/packages/bolt/examples/bolt_merge/pack_1/data/pack_1/functions/bob.mcfunction new file mode 100644 index 000000000..6ba9cd6d2 --- /dev/null +++ b/packages/bolt/examples/bolt_merge/pack_1/data/pack_1/functions/bob.mcfunction @@ -0,0 +1,2 @@ +from pack_1:utils import hello +hello("Bob") diff --git a/packages/bolt/examples/bolt_merge/pack_1/data/pack_1/functions/utils.mcfunction b/packages/bolt/examples/bolt_merge/pack_1/data/pack_1/functions/utils.mcfunction new file mode 100644 index 000000000..277812f17 --- /dev/null +++ b/packages/bolt/examples/bolt_merge/pack_1/data/pack_1/functions/utils.mcfunction @@ -0,0 +1,3 @@ +def hello(name): + say f"Hello, {name}!" +hello("Alice") diff --git a/packages/bolt/examples/bolt_merge/pack_2/data/pack_2/functions/carol.mcfunction b/packages/bolt/examples/bolt_merge/pack_2/data/pack_2/functions/carol.mcfunction new file mode 100644 index 000000000..6ecf74efb --- /dev/null +++ b/packages/bolt/examples/bolt_merge/pack_2/data/pack_2/functions/carol.mcfunction @@ -0,0 +1,2 @@ +from pack_1:utils import hello +hello("Carol") diff --git a/packages/bolt/examples/bolt_nested_location/beet.yml b/packages/bolt/examples/bolt_nested_location/beet.yml new file mode 100644 index 000000000..c8a8dd658 --- /dev/null +++ b/packages/bolt/examples/bolt_nested_location/beet.yml @@ -0,0 +1,6 @@ +require: + - bolt +data_pack: + load: "src" +pipeline: + - mecha diff --git a/packages/bolt/examples/bolt_nested_location/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_nested_location/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..5a201cdef --- /dev/null +++ b/packages/bolt/examples/bolt_nested_location/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,13 @@ +execute function ~/thing1: + as @a function ~/thing2: + function ~/thing{3:03}/hello + say ~/thing{123456789:x}/hello + + schedule function ~/blob 2s append: + say (~/, ~/../{~/[:3]}) + +function ./welp: + if predicate ~/ say yes + +function ~/{"":_<2} +function ~/foo{"bar":_^5} diff --git a/packages/bolt/examples/bolt_nested_location/src/data/ref/functions/foo.mcfunction b/packages/bolt/examples/bolt_nested_location/src/data/ref/functions/foo.mcfunction new file mode 100644 index 000000000..a2ad3133f --- /dev/null +++ b/packages/bolt/examples/bolt_nested_location/src/data/ref/functions/foo.mcfunction @@ -0,0 +1,39 @@ +x = "" + +function ~/{x} +function ~/thing{x} +function ~/../thing{x} + +function ref:bar: + function ~/{x} + function ~/thing{x} + function ~/../thing{x} + + append function ~/thing{x}: + function #~/aaa{x} + +function ./wat: + function ~/{x} + function ~/thing{x} + function ~/../thing{x} + + append function ~/thing{x}: + function #~/bbb{x} + +function ~/this{x}: + function ~/{x} + function ~/thing{x} + function ~/../thing{x} + + append function ~/thing{x}: + function #~/ccc{x} + +function ~/../upthis{x}: + function ~/{x} + function ~/thing{x} + function ~/../thing{x} + + append function ~/thing{x}: + function #~/ddd{x} + +execute if function ~/condition{x} run function ~/action{x} diff --git a/packages/bolt/examples/bolt_nesting/beet.yml b/packages/bolt/examples/bolt_nesting/beet.yml new file mode 100644 index 000000000..c8a8dd658 --- /dev/null +++ b/packages/bolt/examples/bolt_nesting/beet.yml @@ -0,0 +1,6 @@ +require: + - bolt +data_pack: + load: "src" +pipeline: + - mecha diff --git a/packages/bolt/examples/bolt_nesting/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_nesting/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..8063c2e32 --- /dev/null +++ b/packages/bolt/examples/bolt_nesting/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,21 @@ +from contextlib import contextmanager +from bolt import Runtime + +runtime = ctx.inject(Runtime) +say runtime.nesting + +@contextmanager +def stuff(): + if score foo temp matches 77: + yield + +as @s say runtime.nesting + +name = ./thing + +with stuff(): + function name: + say runtime.nesting + say runtime.nesting + +say runtime.nesting diff --git a/packages/bolt/examples/bolt_order/beet.yml b/packages/bolt/examples/bolt_order/beet.yml new file mode 100644 index 000000000..60c43158a --- /dev/null +++ b/packages/bolt/examples/bolt_order/beet.yml @@ -0,0 +1,9 @@ +require: + - bolt +data_pack: + load: "src" +pipeline: + - mecha +meta: + bolt: + entrypoint: hello:entry_point diff --git a/packages/bolt/examples/bolt_order/src/data/hello/modules/entry_point.bolt b/packages/bolt/examples/bolt_order/src/data/hello/modules/entry_point.bolt new file mode 100644 index 000000000..dfd085f5c --- /dev/null +++ b/packages/bolt/examples/bolt_order/src/data/hello/modules/entry_point.bolt @@ -0,0 +1,3 @@ +from hello:main import i_run_last + +i_run_last() diff --git a/packages/bolt/examples/bolt_order/src/data/hello/modules/main.bolt b/packages/bolt/examples/bolt_order/src/data/hello/modules/main.bolt new file mode 100644 index 000000000..eedb32a8e --- /dev/null +++ b/packages/bolt/examples/bolt_order/src/data/hello/modules/main.bolt @@ -0,0 +1,8 @@ +def i_run_last(): + print(2) + append function hello:world: + say second + +print(1) +append function hello:world: + say first diff --git a/packages/bolt/examples/bolt_overgen/beet.yml b/packages/bolt/examples/bolt_overgen/beet.yml new file mode 100644 index 000000000..223cac1e8 --- /dev/null +++ b/packages/bolt/examples/bolt_overgen/beet.yml @@ -0,0 +1,11 @@ +require: + - bolt.contrib.sandbox + - overgen.define_custom_resources + - overgen.define_compilation_unit_providers + - overgen.define_module_globals +data_pack: + load: "src" +pipeline: + - mecha + - overgen.remove_custom_resources + - beet.contrib.cleanup_overlays diff --git a/packages/bolt/examples/bolt_overgen/overgen.py b/packages/bolt/examples/bolt_overgen/overgen.py new file mode 100644 index 000000000..0a307646e --- /dev/null +++ b/packages/bolt/examples/bolt_overgen/overgen.py @@ -0,0 +1,68 @@ +from dataclasses import dataclass +from typing import Any, ClassVar, Iterable, List, Optional, Tuple, Union + +from beet import ( + Context, + DataPack, + NamespaceFileScope, + ResourcePack, + TextFile, + TextFileBase, +) +from beet.core.utils import normalize_string +from mecha import CompilationDatabase, CompilationUnit, Mecha + +from bolt import Runtime + + +class Overgen(TextFile): + scope: ClassVar[NamespaceFileScope] = ("overgen",) + extension: ClassVar[str] = ".bolt" + + +def define_custom_resources(ctx: Context): + ctx.data.extend_namespace.append(Overgen) + + +def provide_compilation_units( + pack: Union[ResourcePack, DataPack], + match: Optional[List[str]] = None, +) -> Iterable[Tuple[TextFileBase[Any], CompilationUnit]]: + for resource_location in pack[Overgen].match(*match or ["*"]): + file_instance = pack[Overgen][resource_location] + + yield ( + file_instance, + CompilationUnit( + resource_location=resource_location, + pack=pack.overlays[normalize_string(resource_location)], + ), + ) + + +def define_compilation_unit_providers(ctx: Context): + mc = ctx.inject(Mecha) + mc.providers = [provide_compilation_units] + + +@dataclass +class DeclareOverlayFormats: + database: CompilationDatabase + + def __call__(self, min_format: int, max_format: int): + pack = self.database[self.database.current].pack + if pack is not None: + pack.min_format = min_format + pack.max_format = max_format + + +def define_module_globals(ctx: Context): + mc = ctx.inject(Mecha) + declare_overlay_formats = DeclareOverlayFormats(mc.database) + + runtime = ctx.inject(Runtime) + runtime.expose("declare_overlay_formats", declare_overlay_formats) + + +def remove_custom_resources(ctx: Context): + ctx.data[Overgen].clear() diff --git a/packages/bolt/examples/bolt_overgen/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_overgen/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..5be7a2070 --- /dev/null +++ b/packages/bolt/examples/bolt_overgen/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,3 @@ +value = "abc" +print(value) +say this should be left untouched diff --git a/packages/bolt/examples/bolt_overgen/src/data/demo/overgen/stuff.bolt b/packages/bolt/examples/bolt_overgen/src/data/demo/overgen/stuff.bolt new file mode 100644 index 000000000..d7eb21cee --- /dev/null +++ b/packages/bolt/examples/bolt_overgen/src/data/demo/overgen/stuff.bolt @@ -0,0 +1,7 @@ +declare_overlay_formats( + min_format=[88, 0], + max_format=[88, 0], +) + +function ./foo: + say stuff diff --git a/packages/bolt/examples/bolt_overlay/beet.yml b/packages/bolt/examples/bolt_overlay/beet.yml new file mode 100644 index 000000000..158b77c0d --- /dev/null +++ b/packages/bolt/examples/bolt_overlay/beet.yml @@ -0,0 +1,7 @@ +require: + - bolt +data_pack: + load: "src" +pipeline: + - mecha + - beet.contrib.cleanup_overlays diff --git a/packages/bolt/examples/bolt_overlay/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_overlay/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..cae32b333 --- /dev/null +++ b/packages/bolt/examples/bolt_overlay/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,2 @@ +from ./vars import stuff +say f"my {stuff}" diff --git a/packages/bolt/examples/bolt_overlay/src/data/demo/modules/vars.bolt b/packages/bolt/examples/bolt_overlay/src/data/demo/modules/vars.bolt new file mode 100644 index 000000000..707568105 --- /dev/null +++ b/packages/bolt/examples/bolt_overlay/src/data/demo/modules/vars.bolt @@ -0,0 +1 @@ +stuff = 123 diff --git a/packages/bolt/examples/bolt_overlay/src/dummy_overlay/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_overlay/src/dummy_overlay/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..b63e1f25c --- /dev/null +++ b/packages/bolt/examples/bolt_overlay/src/dummy_overlay/data/demo/functions/foo.mcfunction @@ -0,0 +1,3 @@ +from ./vars import stuff +say (1 + 2) +say stuff diff --git a/packages/bolt/examples/bolt_patchers/beet.yml b/packages/bolt/examples/bolt_patchers/beet.yml new file mode 100644 index 000000000..671ddbc08 --- /dev/null +++ b/packages/bolt/examples/bolt_patchers/beet.yml @@ -0,0 +1,7 @@ +require: + - bolt.contrib.sandbox + - patchers.define_custom_resources +data_pack: + load: "src" +pipeline: + - patchers.process_files diff --git a/packages/bolt/examples/bolt_patchers/patchers.py b/packages/bolt/examples/bolt_patchers/patchers.py new file mode 100644 index 000000000..998f172d4 --- /dev/null +++ b/packages/bolt/examples/bolt_patchers/patchers.py @@ -0,0 +1,44 @@ +from typing import ClassVar + +from beet import Context, NamespaceFileScope, TextFile +from mecha import Mecha + +from bolt import Runtime + + +class Patcher(TextFile): + scope: ClassVar[NamespaceFileScope] = ("patchers",) + extension: ClassVar[str] = ".bolt" + + +def define_custom_resources(ctx: Context): + ctx.data.extend_namespace.append(Patcher) + + +def process_files(ctx: Context): + mc = ctx.inject(Mecha) + for patcher_name, patcher in ctx[Patcher]: + mc.compile( + patcher, + resource_location=patcher_name, + report=mc.diagnostics, + ) + + if mc.diagnostics.error: + return + + runtime = ctx.inject(Runtime) + for patcher_name, patcher in ctx[Patcher]: + with runtime.modules.error_handler( + message="Patcher raised an exception.", + resource_location=patcher_name, + ): + impl = runtime.modules[patcher].namespace + for entries in ctx.query(match=impl["targets"]).values(): + for name, file_instance in entries: + result = impl["patch"](name, file_instance.ensure_deserialized()) + if result is not None: + file_instance.set_content(result) + + for pack in [ctx.data, *ctx.data.overlays.values()]: + pack[Patcher].clear() diff --git a/packages/bolt/examples/bolt_patchers/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_patchers/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..155423677 --- /dev/null +++ b/packages/bolt/examples/bolt_patchers/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,8 @@ +say this is not compiled + +value = 123 +print(value) + +# As long as you don't mc.compile(ctx.data) or include "mecha" in the pipeline +# you can leverage mecha's compiler and the bolt runtime for other purposes +# without affecting function files in the output pack. diff --git a/packages/bolt/examples/bolt_patchers/src/data/demo/patchers/arrow_recipes.bolt b/packages/bolt/examples/bolt_patchers/src/data/demo/patchers/arrow_recipes.bolt new file mode 100644 index 000000000..9e8e1a2f3 --- /dev/null +++ b/packages/bolt/examples/bolt_patchers/src/data/demo/patchers/arrow_recipes.bolt @@ -0,0 +1,17 @@ +targets = { + recipes: "minecraft:*arrow" +} + +def patch(name, recipe): + if recipe.type == minecraft:crafting_shaped: + result = recipe.result + + recipe.key."#" = { + tag: minecraft:coals + } + + if result.item == minecraft:arrow: + del recipe.key["X"] + recipe.pattern[0] = "#" + + result["count"] += 2 + (10 - 9) * 3 diff --git a/packages/bolt/examples/bolt_patchers/src/data/minecraft/recipes/arrow.json b/packages/bolt/examples/bolt_patchers/src/data/minecraft/recipes/arrow.json new file mode 100644 index 000000000..7d8c54ce3 --- /dev/null +++ b/packages/bolt/examples/bolt_patchers/src/data/minecraft/recipes/arrow.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "equipment", + "key": { + "#": { + "item": "minecraft:stick" + }, + "X": { + "item": "minecraft:flint" + }, + "Y": { + "item": "minecraft:feather" + } + }, + "pattern": ["X", "#", "Y"], + "result": { + "count": 4, + "item": "minecraft:arrow" + }, + "show_notification": true +} diff --git a/packages/bolt/examples/bolt_patchers/src/data/minecraft/recipes/spectral_arrow.json b/packages/bolt/examples/bolt_patchers/src/data/minecraft/recipes/spectral_arrow.json new file mode 100644 index 000000000..0d0e45280 --- /dev/null +++ b/packages/bolt/examples/bolt_patchers/src/data/minecraft/recipes/spectral_arrow.json @@ -0,0 +1,18 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "equipment", + "key": { + "#": { + "item": "minecraft:glowstone_dust" + }, + "X": { + "item": "minecraft:arrow" + } + }, + "pattern": [" # ", "#X#", " # "], + "result": { + "count": 2, + "item": "minecraft:spectral_arrow" + }, + "show_notification": true +} diff --git a/packages/bolt/examples/bolt_patchers/src/data/minecraft/recipes/tipped_arrow.json b/packages/bolt/examples/bolt_patchers/src/data/minecraft/recipes/tipped_arrow.json new file mode 100644 index 000000000..26222e2c5 --- /dev/null +++ b/packages/bolt/examples/bolt_patchers/src/data/minecraft/recipes/tipped_arrow.json @@ -0,0 +1,4 @@ +{ + "type": "minecraft:crafting_special_tippedarrow", + "category": "misc" +} diff --git a/packages/bolt/examples/bolt_prelude/beet.yml b/packages/bolt/examples/bolt_prelude/beet.yml new file mode 100644 index 000000000..5af0bc5d6 --- /dev/null +++ b/packages/bolt/examples/bolt_prelude/beet.yml @@ -0,0 +1,61 @@ +pipeline: + - require: + - bolt + data_pack: + load: "src" + pipeline: + - mecha + - beet.contrib.rename_files + - beet.contrib.find_replace + meta: + bolt: + prelude: + - "demo:prelude" + - "no_idea:prelude" + rename_files: + data_pack: + match: "demo:*" + find: "demo:([a-z_/]+)" + replace: 'demo:ref/\1' + find_replace: + data_pack: + match: "demo:*" + substitute: + find: "demo:([a-z_/]+)" + replace: 'demo:ref/\1' + - require: + - bolt + - bolt.contrib.debug_codegen + data_pack: + load: "src" + pipeline: + - mecha + - beet.contrib.rename_files + - beet.contrib.find_replace + meta: + bolt: + prelude: + - "demo:prelude" + - "no_idea:prelude" + rename_files: + data_pack: + match: "demo:*" + find: "demo:([a-z_/]+)" + replace: 'demo:codegen/\1' + find_replace: + data_pack: + match: "demo:*" + substitute: + find: "demo:([a-z_/]+)" + replace: 'demo:codegen/\1' + - require: + - bolt + data_pack: + load: "src" + pipeline: + - mecha + meta: + bolt: + prelude: + - "demo:prelude" + - "no_idea:prelude" diff --git a/packages/bolt/examples/bolt_prelude/src/data/demo/functions/bar.mcfunction b/packages/bolt/examples/bolt_prelude/src/data/demo/functions/bar.mcfunction new file mode 100644 index 000000000..a57829285 --- /dev/null +++ b/packages/bolt/examples/bolt_prelude/src/data/demo/functions/bar.mcfunction @@ -0,0 +1 @@ +dump_ast() diff --git a/packages/bolt/examples/bolt_prelude/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_prelude/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..43ed3b8b6 --- /dev/null +++ b/packages/bolt/examples/bolt_prelude/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,11 @@ +!!! +greet anonymous() +summon CALAMAR +CALAMAR = None + +!!! +alright_then() +what about minecraft:becomeduck + +!!! +dump_ast() diff --git a/packages/bolt/examples/bolt_prelude/src/data/demo/modules/macros.bolt b/packages/bolt/examples/bolt_prelude/src/data/demo/modules/macros.bolt new file mode 100644 index 000000000..551ab18f2 --- /dev/null +++ b/packages/bolt/examples/bolt_prelude/src/data/demo/modules/macros.bolt @@ -0,0 +1,2 @@ +macro greet name=brigadier:string{"type": "word"}: + say f"hello {name.value}" diff --git a/packages/bolt/examples/bolt_prelude/src/data/demo/modules/other.bolt b/packages/bolt/examples/bolt_prelude/src/data/demo/modules/other.bolt new file mode 100644 index 000000000..f0334fe24 --- /dev/null +++ b/packages/bolt/examples/bolt_prelude/src/data/demo/modules/other.bolt @@ -0,0 +1,9 @@ +from bolt import Runtime + +CALAMAR = minecraft:squid + +def dump_ast(): + runtime = ctx.inject(Runtime) + function generate_path(): + for line in runtime.modules.current.ast.dump().splitlines(): + raw f"# {line}" diff --git a/packages/bolt/examples/bolt_prelude/src/data/demo/modules/prelude.bolt b/packages/bolt/examples/bolt_prelude/src/data/demo/modules/prelude.bolt new file mode 100644 index 000000000..4244fca4d --- /dev/null +++ b/packages/bolt/examples/bolt_prelude/src/data/demo/modules/prelude.bolt @@ -0,0 +1,8 @@ +from ./macros import greet +from ./other import CALAMAR, dump_ast + +macro !!!: + say !!! + +def anonymous() -> str: + return "anonymous" diff --git a/packages/bolt/examples/bolt_prelude/src/data/no_idea/modules/aaa.bolt b/packages/bolt/examples/bolt_prelude/src/data/no_idea/modules/aaa.bolt new file mode 100644 index 000000000..f0e69337a --- /dev/null +++ b/packages/bolt/examples/bolt_prelude/src/data/no_idea/modules/aaa.bolt @@ -0,0 +1,6 @@ +from ./bbb import okok +from demo:macros import greet + +def alright_then(): + okok() + greet steve diff --git a/packages/bolt/examples/bolt_prelude/src/data/no_idea/modules/bbb.bolt b/packages/bolt/examples/bolt_prelude/src/data/no_idea/modules/bbb.bolt new file mode 100644 index 000000000..608a8e98a --- /dev/null +++ b/packages/bolt/examples/bolt_prelude/src/data/no_idea/modules/bbb.bolt @@ -0,0 +1,2 @@ +def okok(): + say ok ok diff --git a/packages/bolt/examples/bolt_prelude/src/data/no_idea/modules/prelude.bolt b/packages/bolt/examples/bolt_prelude/src/data/no_idea/modules/prelude.bolt new file mode 100644 index 000000000..d29f46a8d --- /dev/null +++ b/packages/bolt/examples/bolt_prelude/src/data/no_idea/modules/prelude.bolt @@ -0,0 +1,6 @@ +from ./aaa import alright_then + +macro what about(stream): + from mecha import delegate + node = delegate("resource_location", stream) + say repr(node) diff --git a/packages/bolt/examples/bolt_proc_macro/beet.yml b/packages/bolt/examples/bolt_proc_macro/beet.yml new file mode 100644 index 000000000..c8a8dd658 --- /dev/null +++ b/packages/bolt/examples/bolt_proc_macro/beet.yml @@ -0,0 +1,6 @@ +require: + - bolt +data_pack: + load: "src" +pipeline: + - mecha diff --git a/packages/bolt/examples/bolt_proc_macro/src/data/demo/functions/foo.mcfunction b/packages/bolt/examples/bolt_proc_macro/src/data/demo/functions/foo.mcfunction new file mode 100644 index 000000000..84c46d3a4 --- /dev/null +++ b/packages/bolt/examples/bolt_proc_macro/src/data/demo/functions/foo.mcfunction @@ -0,0 +1,30 @@ +from ./util import repeat, lisp + +value = 3 + +repeat 7: + value += 1 + +repeat 3: + say value + +as @a run repeat 3: + say value + +repeat until value < 5: + say value + value -= 1 + +repeat until @a[scores={counter=10}]: + scoreboard players add @r counter 1 + +lisp foo +lisp 123 +lisp (foo 123) +lisp (foo 123 () (bar)) +lisp ( + (defun do_math (x y) + (add x y)) + (defun do_more_math (x y) + (mul (do_math x y) y)) +) diff --git a/packages/bolt/examples/bolt_proc_macro/src/data/demo/functions/util.mcfunction b/packages/bolt/examples/bolt_proc_macro/src/data/demo/functions/util.mcfunction new file mode 100644 index 000000000..c298a524b --- /dev/null +++ b/packages/bolt/examples/bolt_proc_macro/src/data/demo/functions/util.mcfunction @@ -0,0 +1,44 @@ +from mecha import delegate, AstCommand, AstChildren +from bolt import AstExpressionUnary + +macro repeat(stream): + with stream.syntax(count="[0-9]+"): + token = stream.expect("count") + body = delegate("nested_root", stream) + return int(token.value) * [body] + +macro repeat until(stream): + condition = delegate("bolt:expression", stream) + body = delegate("nested_root", stream) + return AstCommand( + identifier="while:condition:body", + arguments=AstChildren([ + AstExpressionUnary(operator="not", value=condition), + body, + ]) + ) + +macro repeat until(stream): + entity = delegate("selector", stream) + body = delegate("nested_root", stream) + path = generate_path() + execute function path: + yield body + unless entity entity function path + +def parse_sexp(stream): + with stream.syntax(brace=r"\(|\)", number=r"\d+", name=r"\w+"): + brace, number, name = stream.expect(("brace", "("), "number", "name") + if brace: + with stream.ignore("newline"): + result = [] + while not stream.get(("brace", ")")): + result.append(parse_sexp(stream)) + return result + elif number: + return int(number.value) + elif name: + return name.value + +macro lisp(stream): + say parse_sexp(stream) diff --git a/packages/bolt/examples/bolt_sample_ui/beet.yml b/packages/bolt/examples/bolt_sample_ui/beet.yml new file mode 100644 index 000000000..6bdcfff9a --- /dev/null +++ b/packages/bolt/examples/bolt_sample_ui/beet.yml @@ -0,0 +1,6 @@ +require: + - sample_ui.resources +data_pack: + load: "src" +pipeline: + - sample_ui.generate diff --git a/beet/core/__init__.py b/packages/bolt/examples/bolt_sample_ui/sample_ui/__init__.py similarity index 100% rename from beet/core/__init__.py rename to packages/bolt/examples/bolt_sample_ui/sample_ui/__init__.py diff --git a/packages/bolt/examples/bolt_sample_ui/sample_ui/generate.py b/packages/bolt/examples/bolt_sample_ui/sample_ui/generate.py new file mode 100644 index 000000000..13f8a7158 --- /dev/null +++ b/packages/bolt/examples/bolt_sample_ui/sample_ui/generate.py @@ -0,0 +1,24 @@ +from beet import Context, subproject + +from .resources import UiScreen + + +def beet_default(ctx: Context): + all_screens = { + name: screen_file.text for name, screen_file in ctx.data[UiScreen].items() + } + ctx.data[UiScreen].clear() + + ctx.require( + subproject( + { + "require": ["bolt"], + "data_pack": {"load": {"data/sample_ui/modules": "@sample_ui/modules"}}, + "pipeline": ["mecha"], + "meta": { + "bolt": {"entrypoint": "sample_ui:main"}, + "sample_ui": {"all_screens": all_screens}, + }, + } + ) + ) diff --git a/packages/bolt/examples/bolt_sample_ui/sample_ui/modules/main.bolt b/packages/bolt/examples/bolt_sample_ui/sample_ui/modules/main.bolt new file mode 100644 index 000000000..2ce7a8999 --- /dev/null +++ b/packages/bolt/examples/bolt_sample_ui/sample_ui/modules/main.bolt @@ -0,0 +1,9 @@ +import xml.etree.ElementTree as ET + +for name, content in ctx.meta.sample_ui.all_screens.items(): + parser = ET.XMLPullParser(["start", "end"]) + parser.feed(content) + + function name: + for event, elem in parser.read_events(): + say (event, elem.tag, elem.attrib) diff --git a/packages/bolt/examples/bolt_sample_ui/sample_ui/resources.py b/packages/bolt/examples/bolt_sample_ui/sample_ui/resources.py new file mode 100644 index 000000000..b705a0a26 --- /dev/null +++ b/packages/bolt/examples/bolt_sample_ui/sample_ui/resources.py @@ -0,0 +1,12 @@ +from typing import ClassVar + +from beet import Context, NamespaceFileScope, TextFileBase + + +class UiScreen(TextFileBase[str]): + scope: ClassVar[NamespaceFileScope] = ("ui_screens",) + extension: ClassVar[str] = ".xml" + + +def beet_default(ctx: Context): + ctx.data.extend_namespace.append(UiScreen) diff --git a/packages/bolt/examples/bolt_sample_ui/src/data/demo/ui_screens/foo.xml b/packages/bolt/examples/bolt_sample_ui/src/data/demo/ui_screens/foo.xml new file mode 100644 index 000000000..626df75c4 --- /dev/null +++ b/packages/bolt/examples/bolt_sample_ui/src/data/demo/ui_screens/foo.xml @@ -0,0 +1,12 @@ + + +