From 2749e33fded159313b477f8b6fc71c732768a09b Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Wed, 1 Jul 2026 15:02:43 +0000 Subject: [PATCH 1/3] Refactor manager requests and selector namespace --- README.md | 237 +++--- RTWrapper-manager.patch | 708 ++++++++++++++++++ .../core/function/selector/detect.mcfunction | 41 + .../private/accept_limited_all.mcfunction | 5 + .../accept_limited_entity_player.mcfunction | 5 + .../private/accept_player_name.mcfunction | 5 + .../selector/private/accept_self.mcfunction | 4 + .../api/batch/enqueue_next.mcfunction | 9 + .../function/api/enqueue_many.mcfunction | 11 + .../rtwrapper/function/api/run.mcfunction | 15 +- .../function/api/run_many.mcfunction | 5 + .../function/api/run_next.mcfunction | 2 + .../function/api/testmode/off.mcfunction | 2 - .../function/api/testmode/on.mcfunction | 3 - .../function/condition/check/0.mcfunction | 2 + .../function/condition/check/1.mcfunction | 2 + .../function/condition/check/2.mcfunction | 2 + .../function/condition/check/3.mcfunction | 2 + .../function/condition/check/4.mcfunction | 2 + .../function/condition/check/5.mcfunction | 2 + .../function/condition/check/6.mcfunction | 2 + .../function/condition/check/7.mcfunction | 2 + .../condition/check_current.mcfunction | 13 + .../function/condition/predicate.mcfunction | 1 + .../function/condition/score_alias.mcfunction | 1 + .../condition/score_objective.mcfunction | 1 + .../rtwrapper/function/core/load.mcfunction | 5 +- .../rtwrapper/function/core/tick.mcfunction | 5 - .../core/wrappers/handler/proc.mcfunction | 20 +- .../string/detect_selector.mcfunction | 9 - .../string/detect_selector/run.mcfunction | 25 - .../function/trigger/handle.mcfunction | 14 - .../function/trigger/run_request.mcfunction | 7 - .../function/ui/list_commands.mcfunction | 14 - .../rtwrapper/function/ui/open.mcfunction | 2 - .../function/ui/selector_help.mcfunction | 4 - .../rtwrapper/function/ui/settings.mcfunction | 3 - .../function/ui/settings/compact.mcfunction | 2 - .../function/ui/settings/wide.mcfunction | 2 - .../data/runtoolkit/function/dpman.mcfunction | 11 +- .../dpman/disable_rtwrapper.mcfunction | 2 - .../dpman/enable_rtwrapper.mcfunction | 2 - .../dpman/reload_rtwrapper.mcfunction | 2 - datapack/RTWrapper-Datapack/pack.png | Bin 1835 -> 0 bytes docs/API.md | 314 ++------ scripts/validate_datapack.py | 63 +- 46 files changed, 1071 insertions(+), 519 deletions(-) create mode 100644 RTWrapper-manager.patch create mode 100644 datapack/RTWrapper-Datapack/data/core/function/selector/detect.mcfunction create mode 100644 datapack/RTWrapper-Datapack/data/core/function/selector/private/accept_limited_all.mcfunction create mode 100644 datapack/RTWrapper-Datapack/data/core/function/selector/private/accept_limited_entity_player.mcfunction create mode 100644 datapack/RTWrapper-Datapack/data/core/function/selector/private/accept_player_name.mcfunction create mode 100644 datapack/RTWrapper-Datapack/data/core/function/selector/private/accept_self.mcfunction create mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/api/batch/enqueue_next.mcfunction create mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/api/enqueue_many.mcfunction create mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/api/run_many.mcfunction delete mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/api/testmode/off.mcfunction delete mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/api/testmode/on.mcfunction create mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/0.mcfunction create mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/1.mcfunction create mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/2.mcfunction create mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/3.mcfunction create mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/4.mcfunction create mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/5.mcfunction create mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/6.mcfunction create mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/7.mcfunction create mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check_current.mcfunction create mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/predicate.mcfunction create mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/score_alias.mcfunction create mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/score_objective.mcfunction delete mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/string/detect_selector.mcfunction delete mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/string/detect_selector/run.mcfunction delete mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/trigger/handle.mcfunction delete mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/trigger/run_request.mcfunction delete mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/list_commands.mcfunction delete mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/open.mcfunction delete mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/selector_help.mcfunction delete mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/settings.mcfunction delete mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/settings/compact.mcfunction delete mode 100644 datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/settings/wide.mcfunction delete mode 100644 datapack/RTWrapper-Datapack/data/runtoolkit/function/dpman/disable_rtwrapper.mcfunction delete mode 100644 datapack/RTWrapper-Datapack/data/runtoolkit/function/dpman/enable_rtwrapper.mcfunction delete mode 100644 datapack/RTWrapper-Datapack/data/runtoolkit/function/dpman/reload_rtwrapper.mcfunction delete mode 100644 datapack/RTWrapper-Datapack/pack.png diff --git a/README.md b/README.md index f1fd00c..8f4c01b 100644 --- a/README.md +++ b/README.md @@ -1,184 +1,136 @@ # RTWrapper -RTWrapper is a Fabric mod + standalone datapack for Java Edition 26.2. The mod embeds the same datapack data tree, while `datapack/RTWrapper-Datapack/` can be zipped and installed separately. +RTWrapper is a Fabric mod + standalone datapack for Minecraft Java Edition 26.2. The mod embeds the same datapack data tree, while `datapack/RTWrapper-Datapack/` can be zipped and installed separately. ## Requirements | Component | Version | | --- | --- | -| Minecraft | 26.2 ("Chaos Cubed") | -| Java | 25 (JDK 25 toolchain required) | -| Fabric Loader | matches `fabric.mod.json` — verify against `build/libs` manifest before release | -| Fabric API | matches `fabric.mod.json` | +| Minecraft | 26.2 | +| Java | 25 | +| Fabric Loader | see `fabric.mod.json` | +| Fabric API | see `gradle.properties` | | Gradle | 9.5+ | -| Pack format | see `pack.mcmeta` in the datapack root — bump this in lockstep with `PACK_FORMAT` in `scripts/generate_wrappers.py` | - -RTWrapper is built and tested against 26.2 only. It has not been validated against 26.1 or 26.3 — see "Updating command wrappers" below before attempting cross-version use. - -Optional-but-managed dependency: [StringLib](https://github.com/CMDred/StringLib). RTWrapper's selector-detection helpers and dependency manager probe `stringlib:util/find`; install StringLib if you want those helpers and a clean dependency status. - -## Layout - -```text -src/main/ Fabric mod skeleton, Mojang official names -datapack/RTWrapper-Datapack/ Standalone datapack root -datapack/commands-26.2.json Command wrapper manifest + command parameter names -docs/API.md API/storage protocol -scripts/validate_datapack.py Datapack/static wrapper validator -src/gametest/ Fabric server GameTests -``` +| Optional dependency | [StringLib](https://github.com/CMDred/StringLib) for CRE player-name selector validation | ## Installation -### As a Fabric mod +### Fabric mod -1. Install Fabric Loader for Minecraft 26.2. -2. Install Fabric API matching your Loader version. -3. Download or build `rtwrapper-.jar` (see Build below). -4. Drop the jar into your `mods/` folder. -5. Launch the game or server. The mod embeds its datapack automatically — no separate install step needed. -6. In-world, run `/reload` once to ensure the embedded datapack functions are registered, then verify with: +1. Build or download `rtwrapper-.jar`. +2. Put it in `mods/` with Fabric API. +3. Run `/reload` once after the server starts. +4. Check status: - ```mcfunction - function runtoolkit:api/status - ``` +```mcfunction +function runtoolkit:api/status +``` -### As a standalone datapack (no mod) +### Standalone datapack -1. Download or build `RTWrapper-Datapack-.zip` (see Build below). -2. Place the zip into your world's `datapacks/` folder (`/datapacks/RTWrapper-Datapack-.zip`), or into `datapacks/` under your server root for a server. -3. If the world is already running, run `/reload`. Otherwise this loads automatically on world start. -4. Confirm the datapack is active — check **Advancements > Runtoolkit** in-game, or run: +1. Put `RTWrapper-Datapack-.zip` in `/datapacks/`. +2. Optional but recommended: put StringLib in the same datapacks folder. +3. Run: - ```mcfunction - function runtoolkit:api/status - ``` +```mcfunction +reload +function runtoolkit:api/status +``` -Do not install both the mod and the standalone datapack zip on the same instance — they embed the same `rtwrapper:` and `runtoolkit:` namespaces and will conflict on `/reload` (duplicate function/advancement registration). Pick one. +Do not install both the mod and standalone datapack at the same time; both provide the same namespaces. -## Datapack API quick start +## Basic request API Generated command wrappers use meaningful command-specific parameter names. There is no generated catch-all or generic numeric parameter API. -Provide the named parameters in the order listed for that command in `datapack/commands-26.2.json`. The dispatcher calls the exact `_` variant and does not append unused params. - ```mcfunction -# /tp @s 0 80 0 via queued handler -data modify storage rtwrapper:api request set value {cmd:"tp",params:{target:"@s",x:"0",y:"80",z:"0"}} +# /tp @s 0 80 0 +data modify storage rtwrapper:api request set value {cmd:"tp",params:{target:"@s",x:"0",y:"80",z:"0"},runSafeMode:1b} function rtwrapper:api/run -# /give @s minecraft:stone 1 via direct API +# /give @s minecraft:stone 1 +data modify storage rtwrapper:api request set value {cmd:"give",params:{target:"@s",item:"minecraft:stone",count:"1"},runSafeMode:1b} +function rtwrapper:api/run + +# direct command API data modify storage rtwrapper:api params set value {target:"@s",item:"minecraft:stone",count:"1"} function rtwrapper:api/commands/give - -# /scoreboard players set #smoke rtw.test 1 -data modify storage rtwrapper:api request set value {cmd:"scoreboard",params:{category:"players",action:"set",subject:"#smoke",objective:"rtw.test",value:"1"}} -function rtwrapper:api/run ``` -## In-game trigger UI (testMode) +Parameter order and names are in `datapack/commands-26.2.json` under `command_params`. -RTWrapper creates these objectives on load: +## runSafeMode -```mcfunction -scoreboard objectives add rtw.temp dummy -scoreboard objectives add RTWrapper trigger -``` +Single request `runSafeMode` defaults to `0b`. -`rtw.temp` is used by temporary UI/trigger systems: `1` means accepted/success, `0` means canceled/error. In-game trigger UI is gated behind the `rtwrapper.testMode` tag. Enable it per player: +- `runSafeMode:0b`: immediate full queue drain. +- `runSafeMode:1b`: safe one-step execution via `run_next`. -```mcfunction -function rtwrapper:api/testmode/on -``` +Prefer `runSafeMode:1b` for player/user-generated requests. -Then use: +## Batch requests -```mcfunction -trigger RTWrapper set 1 # Open RTWrapper dialog -trigger RTWrapper set 2 # Run current rtwrapper:api request via rtwrapper:api/run -trigger RTWrapper set 3 # List command wrappers in chat -trigger RTWrapper set 4 # Open runtoolkit:dpman -``` - -Disable test mode: +Use `rtwrapper:api/run_many` for conditional multi-request execution. ```mcfunction -function rtwrapper:api/testmode/off +data modify storage rtwrapper:api batch set value {runSafeMode:1b,requests:[{req:{cmd:"give",params:{target:"@s",item:"minecraft:stone",count:"1"}},args:[{type:"score",name:"#allow",objective:"myScore",value:"1"}]},{req:{cmd:"function",params:{function_id:"my_pack:do_thing"}},args:[{type:"predicate",id:"my_pack:my_predicate"}]}]} +function rtwrapper:api/run_many ``` -Named convenience wrapper example: +Each item is: -```mcfunction -# $give $(target) $(item)$(components) $(count) -data modify storage rtwrapper:api params set value {target:"@s",item:"minecraft:stone",components:"",count:"1"} -function rtwrapper:api/commands/give_item +```snbt +{req:{...normal RTWrapper request...},args:[...conditions...]} ``` -### `run` vs `enqueue` / autotick - -Two distinct execution paths exist — they are not interchangeable: +Supported conditions: -- **`rtwrapper:api/run`** executes the request in `rtwrapper:api request` immediately and drains the queue synchronously in the current tick. Use this for one-off calls where you need the result before your function continues. -- **`rtwrapper:api/enqueue`** pushes the request onto a queue. With `rtwrapper:api/autotick/on`, the queue is processed one action per tick. Use this when issuing many commands in a burst where spreading load across ticks avoids a single-tick lag spike. +```snbt +{type:"predicate",id:"my_pack:my_predicate"} +{type:"score",name:"#allow",objective:"myScore",value:"1"} +{type:"score",name:"#allow",score:"myScore",value:"1"} +``` -Autotick usage: +## Queue / autotick ```mcfunction function rtwrapper:api/autotick/on -data modify storage rtwrapper:api request set value {cmd:"say",params:{message:"queued hello"}} +data modify storage rtwrapper:api request set value {cmd:"say",params:{message:"queued hello"},runSafeMode:1b} function rtwrapper:api/enqueue ``` Autotick processes one queued action per tick. Use `function rtwrapper:api/run` only for immediate full queue drain. -See [`docs/API.md`](docs/API.md) for the full protocol and debug/silent controls. - -## Runtoolkit manager / loaded-pack discovery - -RTWrapper installs the shared `runtoolkit` namespace so Runtoolkit packs can be managed globally: +## Core selector detection -```text -data/runtoolkit/function/ -data/runtoolkit/tags/function/ -data/runtoolkit/advancement/root.json -data/runtoolkit/advancement/packs/rtwrapper.json -``` - -After `/reload`, players get a visible **Advancements > Runtoolkit** tab via the `minecraft:tick` advancement trigger. This is intentionally not revoked, so it can be used as a stable visual list of loaded Runtoolkit datapacks without reading a long `/datapack list` output. - -Dynamic list/status: +Selector detection lives in `core:selector/detect`. ```mcfunction -function runtoolkit:api/status -function runtoolkit:api/list -function runtoolkit:api/dump_registry +data modify storage core:selector input.value set value "@s" +function core:selector/detect +data get storage core:selector result ``` -Manager controls for one pack: +Allowed: -```mcfunction -data modify storage runtoolkit:api request set value {id:"rtwrapper"} -function runtoolkit:api/disable +- player names, validated with StringLib as no `@` and length 3..16 +- `@s` +- restricted `@a[limit=1]` / nearest variants +- restricted `@e[type=player,limit=1]` / nearest variants -data modify storage runtoolkit:api request set value {id:"rtwrapper"} -function runtoolkit:api/enable - -data modify storage runtoolkit:api request set value {id:"rtwrapper"} -function runtoolkit:api/reload -``` +Rejected: -Bulk manager hooks: +- `@p` +- `@r` +- unrestricted `@a` +- unrestricted or non-player `@e` -```mcfunction -function runtoolkit:api/disable_all -function runtoolkit:api/enable_all -function runtoolkit:api/reload_all -``` +The detector reads storage first and returns fail when `storage core:selector input.value` is missing. -This manager does not call vanilla `/datapack disable|enable`; it controls Runtoolkit-managed registration/load/tick hooks. Functions still exist when a pack is manager-disabled, but the global manager stops running that pack's managed hooks. +## Runtoolkit manager -Other Runtoolkit projects can join the system by adding functions to these tags: +RTWrapper installs the shared `runtoolkit` namespace and manager tags: ```text #runtoolkit:register @@ -190,19 +142,31 @@ Other Runtoolkit projects can join the system by adding functions to these tags: #runtoolkit:reload ``` -## `commands-26.2.json` +Useful commands: -This file is the single source of truth for what `generate_wrappers.py` emits: +```mcfunction +function runtoolkit:api/list +function runtoolkit:api/status +function runtoolkit:api/dump_registry +function runtoolkit:dpman +``` -- Top-level command lists define supported vanilla commands (`tp`, `give`, `scoreboard`, etc.). -- `command_params` lists ordered meaningful parameter names for each command. -- Named convenience wrappers (like `give_item`) are generated alongside the vanilla command wrappers. +Manager enable/disable/reload controls Runtoolkit hooks, not vanilla `/datapack enable|disable`: -If you add or change a command's parameters, edit `scripts/generate_wrappers.py` first, regenerate, then validate. Hand-editing generated function files under `data/rtwrapper/function/api/commands/` directly will be overwritten on next generation. +```mcfunction +data modify storage runtoolkit:api request set value {id:"rtwrapper"} +function runtoolkit:api/disable -## Build +data modify storage runtoolkit:api request set value {id:"rtwrapper"} +function runtoolkit:api/enable + +data modify storage runtoolkit:api request set value {id:"rtwrapper"} +function runtoolkit:api/reload +``` -Minecraft 26.2 targets Java 25. Use a JDK 25 toolchain and Gradle 9.5+. For 26.1+ Fabric, the build uses `net.fabricmc.fabric-loom` with Mojang official names directly, so there is intentionally no `mappings loom.officialMojangMappings()` dependency. +There is no RTWrapper dialog UI, no `rtwrapper.testMode` tag, and no `RTWrapper` trigger objective. + +## Build ```bash python3 scripts/validate_datapack.py @@ -211,20 +175,13 @@ python3 scripts/validate_datapack.py Build outputs: -- Fabric mod jar: `build/libs/rtwrapper-.jar` -- Standalone datapack zip: `build/libs/RTWrapper-Datapack-.zip` - -The `build` task also runs server GameTests through Fabric API's GameTest integration. These spin up a headless test server as part of the Gradle build and fail the build on test failure. To run only the GameTests without a full build, use: - -```bash -./gradlew runGametest -``` +- `build/libs/rtwrapper-.jar` +- `build/libs/RTWrapper-Datapack-.zip` -## Updating command wrappers for 26.3+ +## Updating command wrappers -1. Update `COMMANDS` / `DEBUG_COMMANDS` and `COMMAND_PARAMS` in `scripts/generate_wrappers.py`. -2. Update `minecraft_version`, `fabric_version`, and possibly `PACK_FORMAT`. -3. Run: +1. Edit `COMMANDS`, `DEBUG_COMMANDS`, and `COMMAND_PARAMS` in `scripts/generate_wrappers.py`. +2. Run: ```bash python3 scripts/generate_wrappers.py @@ -232,12 +189,10 @@ python3 scripts/validate_datapack.py ./gradlew build ``` -Bumping `PACK_FORMAT` without updating every consuming datapack (including any `runtoolkit:` namespace dependents) can break `/reload` behavior on older packs. Verify with `function runtoolkit:api/status` after any pack format bump; don't assume silence means success. - ## License MIT. See [`LICENSE`](LICENSE). ## Safety -RTWrapper intentionally exposes privileged macro-command execution for trusted datapacks/admins. Do not copy untrusted player-controlled text into `rtwrapper:api request` or `rtwrapper:api params`. +RTWrapper exposes privileged macro-command execution for trusted datapacks/admins. Do not copy untrusted player-controlled text into `rtwrapper:api request` or `rtwrapper:api params`. diff --git a/RTWrapper-manager.patch b/RTWrapper-manager.patch new file mode 100644 index 0000000..2bd8264 --- /dev/null +++ b/RTWrapper-manager.patch @@ -0,0 +1,708 @@ +diff --git a/README.md b/README.md +index 0886332..ebf32f9 100644 +--- a/README.md ++++ b/README.md +@@ -4,14 +4,14 @@ RTWrapper is a Fabric mod + standalone datapack for Java Edition 26.2. The mod e + + ## Requirements + +-| Component | Version | +-|-------------------|-----------------------------------| +-| Minecraft | 26.2 ("Chaos Cubed") | +-| Java | 25 (JDK 25 toolchain required) | +-| Fabric Loader | matches `fabric.mod.json` — verify against `build/libs` manifest before release | +-| Fabric API | matches `fabric.mod.json` | +-| Gradle | 9.5+ | +-| Pack format | see `pack.mcmeta` in the datapack root — bump this in lockstep with `PACK_FORMAT` in `scripts/generate_wrappers.py` | ++| Component | Version | ++| --- | --- | ++| Minecraft | 26.2 ("Chaos Cubed") | ++| Java | 25 (JDK 25 toolchain required) | ++| Fabric Loader | matches `fabric.mod.json` — verify against `build/libs` manifest before release | ++| Fabric API | matches `fabric.mod.json` | ++| Gradle | 9.5+ | ++| Pack format | see `pack.mcmeta` in the datapack root — bump this in lockstep with `PACK_FORMAT` in `scripts/generate_wrappers.py` | + + RTWrapper is built and tested against 26.2 only. It has not been validated against 26.1 or 26.3 — see "Updating command wrappers" below before attempting cross-version use. + +@@ -36,6 +36,7 @@ src/gametest/ Fabric server GameTests + 4. Drop the jar into your `mods/` folder. + 5. Launch the game or server. The mod embeds its datapack automatically — no separate install step needed. + 6. In-world, run `/reload` once to ensure the embedded datapack functions are registered, then verify with: ++ + ```mcfunction + function runtoolkit:api/status + ``` +@@ -46,6 +47,7 @@ src/gametest/ Fabric server GameTests + 2. Place the zip into your world's `datapacks/` folder (`/datapacks/RTWrapper-Datapack-.zip`), or into `datapacks/` under your server root for a server. + 3. If the world is already running, run `/reload`. Otherwise this loads automatically on world start. + 4. Confirm the datapack is active — check **Advancements > Runtoolkit** in-game, or run: ++ + ```mcfunction + function runtoolkit:api/status + ``` +@@ -80,19 +82,18 @@ data modify storage rtwrapper:api params set value {target:"@s",item:"minecraft: + function rtwrapper:api/commands/give_item + ``` + +-### `run` vs `enqueue`/autotick ++### `run` vs `enqueue` / autotick + + Two distinct execution paths exist — they are not interchangeable: + +-- **`rtwrapper:api/run`** executes the request in `rtwrapper:api request` (or `rtwrapper:api params` for direct command wrappers) immediately, synchronously, in the current tick. Use this for one-off calls where you need the result available before your function continues. +-- **`rtwrapper:api/enqueue`** + autotick pushes the request onto a queue that is drained one action per tick by `rtwrapper:api/autotick/on`. Use this when issuing many commands in a burst (e.g. spawning many entities, batch scoreboard writes) where spreading load across ticks avoids a single-tick lag spike. The tradeoff is latency: an enqueued action is not guaranteed to execute in the same tick it was queued. +- +-Calling `rtwrapper:api/run` while autotick is on and the queue is non-empty performs a full immediate drain of the queue — see below. ++- **`rtwrapper:api/run`** executes the request in `rtwrapper:api request` immediately and drains the queue synchronously in the current tick. Use this for one-off calls where you need the result before your function continues. ++- **`rtwrapper:api/enqueue`** pushes the request onto a queue. With `rtwrapper:api/autotick/on`, the queue is processed one action per tick. Use this when issuing many commands in a burst where spreading load across ticks avoids a single-tick lag spike. + + Autotick usage: + + ```mcfunction + function rtwrapper:api/autotick/on ++ + data modify storage rtwrapper:api request set value {cmd:"say",params:{message:"queued hello"}} + function rtwrapper:api/enqueue + ``` +@@ -101,15 +102,71 @@ Autotick processes one queued action per tick. Use `function rtwrapper:api/run` + + See [`docs/API.md`](docs/API.md) for the full protocol and debug/silent controls. + ++## Runtoolkit manager / loaded-pack discovery ++ ++RTWrapper installs the shared `runtoolkit` namespace so Runtoolkit packs can be managed globally: ++ ++```text ++data/runtoolkit/function/ ++data/runtoolkit/tags/function/ ++data/runtoolkit/advancement/root.json ++data/runtoolkit/advancement/packs/rtwrapper.json ++``` ++ ++After `/reload`, players get a visible **Advancements > Runtoolkit** tab via the `minecraft:tick` advancement trigger. This is intentionally not revoked, so it can be used as a stable visual list of loaded Runtoolkit datapacks without reading a long `/datapack list` output. ++ ++Dynamic list/status: ++ ++```mcfunction ++function runtoolkit:api/status ++function runtoolkit:api/list ++function runtoolkit:api/dump_registry ++``` ++ ++Manager controls for one pack: ++ ++```mcfunction ++data modify storage runtoolkit:api request set value {id:"rtwrapper"} ++function runtoolkit:api/disable ++ ++data modify storage runtoolkit:api request set value {id:"rtwrapper"} ++function runtoolkit:api/enable ++ ++data modify storage runtoolkit:api request set value {id:"rtwrapper"} ++function runtoolkit:api/reload ++``` ++ ++Bulk manager hooks: ++ ++```mcfunction ++function runtoolkit:api/disable_all ++function runtoolkit:api/enable_all ++function runtoolkit:api/reload_all ++``` ++ ++This manager does not call vanilla `/datapack disable|enable`; it controls Runtoolkit-managed registration/load/tick hooks. Functions still exist when a pack is manager-disabled, but the global manager stops running that pack's managed hooks. ++ ++Other Runtoolkit projects can join the system by adding functions to these tags: ++ ++```text ++#runtoolkit:register ++#runtoolkit:load ++#runtoolkit:tick ++#runtoolkit:list ++#runtoolkit:enable ++#runtoolkit:disable ++#runtoolkit:reload ++``` ++ + ## `commands-26.2.json` + +-This file is the single source of truth for what `generate_wrappers.py` emits — it is not just a manifest, it defines every wrapper function that exists under `rtwrapper:api/commands/`. Structure: ++This file is the single source of truth for what `generate_wrappers.py` emits: + +-- Top-level: one entry per supported vanilla command (`tp`, `give`, `scoreboard`, etc.) +-- Each entry lists ordered parameter names for that command's positional wrapper variant(s) — this is what determines the exact param order you must supply in `params:{...}` when calling that command's `_` variant. +-- Named convenience wrappers (like `give_item`) are derived from this file per the macro syntax comment above their definition (e.g. `$give $(target) $(item)$(components) $(count)`). ++- Top-level command lists define supported vanilla commands (`tp`, `give`, `scoreboard`, etc.). ++- `command_params` lists ordered meaningful parameter names for each command. ++- Named convenience wrappers (like `give_item`) are generated alongside the vanilla command wrappers. + +-If you add or change a command's parameters, edit this file first, then regenerate (see "Updating command wrappers" below) — hand-editing generated function files under `data/rtwrapper/function/api/commands/` directly will be overwritten on next generation. ++If you add or change a command's parameters, edit `scripts/generate_wrappers.py` first, regenerate, then validate. Hand-editing generated function files under `data/rtwrapper/function/api/commands/` directly will be overwritten on next generation. + + ## Build + +@@ -121,10 +178,11 @@ python3 scripts/validate_datapack.py + ``` + + Build outputs: ++ + - Fabric mod jar: `build/libs/rtwrapper-.jar` + - Standalone datapack zip: `build/libs/RTWrapper-Datapack-.zip` + +-The `build` task also runs server GameTests through Fabric API's GameTest integration — these spin up a headless test server as part of the Gradle build itself (not a separate CI-only step) and will fail the build on test failure. Running `./gradlew build` locally runs the same GameTests that run in CI; there is no separate local/CI test split. To run only the GameTests without a full build, use: ++The `build` task also runs server GameTests through Fabric API's GameTest integration. These spin up a headless test server as part of the Gradle build and fail the build on test failure. To run only the GameTests without a full build, use: + + ```bash + ./gradlew runGametest +@@ -142,11 +200,11 @@ python3 scripts/validate_datapack.py + ./gradlew build + ``` + +-Bumping `PACK_FORMAT` without updating every consuming datapack (including any `runtoolkit:` namespace dependents) will silently break `/reload` behavior on older packs — Minecraft does not always hard-error on a pack_format mismatch, it may just warn and load anyway with undefined wrapper behavior. Verify with `function runtoolkit:api/status` after any pack_format bump, don't assume silence means success. ++Bumping `PACK_FORMAT` without updating every consuming datapack (including any `runtoolkit:` namespace dependents) can break `/reload` behavior on older packs. Verify with `function runtoolkit:api/status` after any pack format bump; don't assume silence means success. + + ## License + +-No license file is currently present in this repository. Until one is added, default copyright applies and no reuse, modification, or redistribution rights are granted to third parties beyond what's needed to view the source. If this repo is intended to be public or to accept external contributions, add a `LICENSE` file before relying on that assumption. ++MIT. See [`LICENSE`](LICENSE). + + ## Safety + +diff --git a/datapack/RTWrapper-Datapack/data/minecraft/tags/function/load.json b/datapack/RTWrapper-Datapack/data/minecraft/tags/function/load.json +index 6b11790..6ba7ba2 100644 +--- a/datapack/RTWrapper-Datapack/data/minecraft/tags/function/load.json ++++ b/datapack/RTWrapper-Datapack/data/minecraft/tags/function/load.json +@@ -1,6 +1,5 @@ + { + "values": [ +- "rtwrapper:core/load", + "runtoolkit:core/load" + ] + } +diff --git a/datapack/RTWrapper-Datapack/data/minecraft/tags/function/tick.json b/datapack/RTWrapper-Datapack/data/minecraft/tags/function/tick.json +index 4cda2d7..4ba576f 100644 +--- a/datapack/RTWrapper-Datapack/data/minecraft/tags/function/tick.json ++++ b/datapack/RTWrapper-Datapack/data/minecraft/tags/function/tick.json +@@ -1,6 +1,5 @@ + { + "values": [ +- "rtwrapper:core/tick", + "runtoolkit:core/tick" + ] + } +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/disable.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/disable.mcfunction +new file mode 100644 +index 0000000..5f19c7d +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/disable.mcfunction +@@ -0,0 +1,4 @@ ++# Disable one managed Runtoolkit pack. ++# Usage: data modify storage runtoolkit:api request set value {id:"rtwrapper"} ++# function runtoolkit:api/disable ++function runtoolkit:manager/disable with storage runtoolkit:api request +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/disable_all.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/disable_all.mcfunction +new file mode 100644 +index 0000000..d6f44b9 +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/disable_all.mcfunction +@@ -0,0 +1,2 @@ ++# Disable all registered Runtoolkit packs through their managed hooks. ++function #runtoolkit:disable +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/dump_registry.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/dump_registry.mcfunction +new file mode 100644 +index 0000000..9838ce1 +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/dump_registry.mcfunction +@@ -0,0 +1,2 @@ ++# Console-friendly raw registry dump. ++data get storage runtoolkit:registry packs +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/enable.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/enable.mcfunction +new file mode 100644 +index 0000000..7e403f0 +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/enable.mcfunction +@@ -0,0 +1,4 @@ ++# Enable one managed Runtoolkit pack. ++# Usage: data modify storage runtoolkit:api request set value {id:"rtwrapper"} ++# function runtoolkit:api/enable ++function runtoolkit:manager/enable with storage runtoolkit:api request +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/enable_all.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/enable_all.mcfunction +new file mode 100644 +index 0000000..a381174 +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/enable_all.mcfunction +@@ -0,0 +1,2 @@ ++# Enable all registered Runtoolkit packs through their managed hooks. ++function #runtoolkit:enable +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/list.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/list.mcfunction +new file mode 100644 +index 0000000..5e00960 +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/list.mcfunction +@@ -0,0 +1,3 @@ ++# Dynamic list of Runtoolkit-managed packs. ++tellraw @s [{"text":"[Runtoolkit] Managed packs","color":"gold","bold":true}] ++function #runtoolkit:list +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/reload.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/reload.mcfunction +new file mode 100644 +index 0000000..c4b06e6 +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/reload.mcfunction +@@ -0,0 +1,4 @@ ++# Reload one managed Runtoolkit pack. ++# Usage: data modify storage runtoolkit:api request set value {id:"rtwrapper"} ++# function runtoolkit:api/reload ++function runtoolkit:manager/reload with storage runtoolkit:api request +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/reload_all.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/reload_all.mcfunction +new file mode 100644 +index 0000000..7ac00f6 +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/reload_all.mcfunction +@@ -0,0 +1,7 @@ ++# Re-register and reload all enabled Runtoolkit packs without using /reload. ++data modify storage runtoolkit:registry packs set value {} ++data modify storage runtoolkit:runtime missing_dependencies set value [] ++scoreboard players set #dependency_errors rtk.status 0 ++scoreboard players add #manager_reloads rtk.status 1 ++function #runtoolkit:register ++function #runtoolkit:reload +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/require.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/require.mcfunction +new file mode 100644 +index 0000000..bf2f380 +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/require.mcfunction +@@ -0,0 +1,4 @@ ++# Dependency assertion helper for pack check hooks. ++# Usage: data modify storage runtoolkit:api request set value {id:"dependency_pack_id"} ++# function runtoolkit:api/require ++function runtoolkit:manager/require with storage runtoolkit:api request +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/status.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/status.mcfunction +index 4c7042e..99a925c 100644 +--- a/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/status.mcfunction ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/api/status.mcfunction +@@ -1,2 +1,2 @@ +-# Lightweight status helper for Runtoolkit packs. +-tellraw @s [{"text":"[Runtoolkit] Loaded registry: ","color":"gold"},{"text":"RTWrapper","color":"green"},{"text":". Open Advancements > Runtoolkit for the visual loaded-pack list.","color":"gray"}] ++# Alias for the dynamic list view. ++function runtoolkit:api/list +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/core/load.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/core/load.mcfunction +index 25b260c..23eac05 100644 +--- a/datapack/RTWrapper-Datapack/data/runtoolkit/function/core/load.mcfunction ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/core/load.mcfunction +@@ -1,5 +1,15 @@ +-# Runtoolkit common registry bootstrap. +-# This namespace is shared by Runtoolkit datapacks so loaded modules can be +-# discovered without relying on /datapack list. ++# Runtoolkit global datapack manager bootstrap. ++# All Runtoolkit datapacks register through #runtoolkit:register, then the ++# manager runs enabled pack load hooks through #runtoolkit:load. ++scoreboard objectives add rtk.registered dummy ++scoreboard objectives add rtk.enabled dummy + scoreboard objectives add rtk.loaded dummy +-function runtoolkit:packs/rtwrapper/register ++scoreboard objectives add rtk.status dummy ++ ++data modify storage runtoolkit:registry packs set value {} ++data modify storage runtoolkit:runtime missing_dependencies set value [] ++scoreboard players set #dependency_errors rtk.status 0 ++scoreboard players add #reloads rtk.status 1 ++ ++function #runtoolkit:register ++function #runtoolkit:load +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/core/tick.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/core/tick.mcfunction +index 3bda22e..6a9c701 100644 +--- a/datapack/RTWrapper-Datapack/data/runtoolkit/function/core/tick.mcfunction ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/core/tick.mcfunction +@@ -1,4 +1,4 @@ +-# Runtoolkit common tick hook. +-# Advancement completion is handled by advancement trigger minecraft:tick. +-# Keep this function lightweight and do not revoke advancements here. +-execute if score #rtwrapper rtk.loaded matches 1 run scoreboard players add #rtwrapper_ticks rtk.loaded 0 ++# Runtoolkit global datapack manager tick. ++# Managed packs should add a lightweight function to #runtoolkit:tick and gate ++# their own tick work behind their rtk.enabled score. ++function #runtoolkit:tick +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/manager/check.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/manager/check.mcfunction +new file mode 100644 +index 0000000..7b7892f +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/manager/check.mcfunction +@@ -0,0 +1,2 @@ ++# Macro dispatch to a pack dependency check hook. ++$function runtoolkit:packs/$(id)/check_dependencies +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/manager/disable.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/manager/disable.mcfunction +new file mode 100644 +index 0000000..fff3bfd +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/manager/disable.mcfunction +@@ -0,0 +1,2 @@ ++# Macro dispatch to a pack disable hook. ++$function runtoolkit:packs/$(id)/disable +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/manager/enable.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/manager/enable.mcfunction +new file mode 100644 +index 0000000..2046b72 +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/manager/enable.mcfunction +@@ -0,0 +1,2 @@ ++# Macro dispatch to a pack enable hook. ++$function runtoolkit:packs/$(id)/enable +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/manager/reload.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/manager/reload.mcfunction +new file mode 100644 +index 0000000..9222ffa +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/manager/reload.mcfunction +@@ -0,0 +1,2 @@ ++# Macro dispatch to a pack reload hook. ++$function runtoolkit:packs/$(id)/reload +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/manager/require.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/manager/require.mcfunction +new file mode 100644 +index 0000000..04f17e7 +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/manager/require.mcfunction +@@ -0,0 +1,4 @@ ++# Macro dependency check: a dependency is satisfied only when its rtk.enabled ++# score is 1 or greater. Missing dependencies are recorded in runtime storage. ++$execute unless score $(id) rtk.enabled matches 1.. run scoreboard players add #dependency_errors rtk.status 1 ++$execute unless score $(id) rtk.enabled matches 1.. run data modify storage runtoolkit:runtime missing_dependencies append value "$(id)" +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/check_dependencies.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/check_dependencies.mcfunction +new file mode 100644 +index 0000000..a1bf7ff +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/check_dependencies.mcfunction +@@ -0,0 +1,4 @@ ++# RTWrapper has no Runtoolkit dependencies. ++data modify storage runtoolkit:runtime missing_dependencies set value [] ++scoreboard players set #dependency_errors rtk.status 0 ++scoreboard players set rtwrapper rtk.loaded 0 +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/disable.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/disable.mcfunction +new file mode 100644 +index 0000000..ff5a6a7 +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/disable.mcfunction +@@ -0,0 +1,5 @@ ++# Disable RTWrapper manager hooks. Functions remain present, but the global ++# manager will stop running RTWrapper load/tick hooks. ++scoreboard players set rtwrapper rtk.enabled 0 ++scoreboard players set rtwrapper rtk.loaded 0 ++tellraw @a [{"text":"[Runtoolkit] Disabled RTWrapper manager hooks","color":"yellow"}] +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/enable.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/enable.mcfunction +new file mode 100644 +index 0000000..5c5b30b +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/enable.mcfunction +@@ -0,0 +1,5 @@ ++# Enable RTWrapper manager hooks. This does not run /datapack enable; it controls ++# Runtoolkit-managed execution hooks. ++scoreboard players set rtwrapper rtk.enabled 1 ++function runtoolkit:packs/rtwrapper/load ++tellraw @a [{"text":"[Runtoolkit] Enabled RTWrapper","color":"green"}] +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/list.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/list.mcfunction +new file mode 100644 +index 0000000..51162d5 +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/list.mcfunction +@@ -0,0 +1,3 @@ ++# Dynamic list row for RTWrapper. ++execute if score rtwrapper rtk.enabled matches 1.. run tellraw @s [{"text":" - RTWrapper ","color":"aqua"},{"text":"enabled","color":"green"},{"text":" v0.1.1+26.2","color":"gray"}] ++execute unless score rtwrapper rtk.enabled matches 1.. run tellraw @s [{"text":" - RTWrapper ","color":"aqua"},{"text":"disabled","color":"red"},{"text":" v0.1.1+26.2","color":"gray"}] +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/load.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/load.mcfunction +new file mode 100644 +index 0000000..f409cef +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/load.mcfunction +@@ -0,0 +1,5 @@ ++# Load RTWrapper only when enabled and dependency checks pass. ++execute if score rtwrapper rtk.enabled matches 1.. run function runtoolkit:packs/rtwrapper/check_dependencies ++execute if score rtwrapper rtk.enabled matches 1.. if score #dependency_errors rtk.status matches 0 run function rtwrapper:core/load ++execute if score rtwrapper rtk.enabled matches 1.. if score #dependency_errors rtk.status matches 0 run scoreboard players set rtwrapper rtk.loaded 1 ++execute if score rtwrapper rtk.enabled matches 1.. unless score #dependency_errors rtk.status matches 0 run tellraw @a [{"text":"[Runtoolkit] RTWrapper dependency check failed: ","color":"red"},{"nbt":"missing_dependencies","storage":"runtoolkit:runtime","color":"yellow"}] +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/register.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/register.mcfunction +index 1a95289..0e96585 100644 +--- a/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/register.mcfunction ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/register.mcfunction +@@ -1,4 +1,10 @@ +-# Registration hook for RTWrapper. Other Runtoolkit datapacks can mirror this pattern. ++# Register RTWrapper with the global Runtoolkit manager. ++scoreboard objectives add rtk.registered dummy ++scoreboard objectives add rtk.enabled dummy + scoreboard objectives add rtk.loaded dummy +-scoreboard players set #rtwrapper rtk.loaded 1 +-data modify storage runtoolkit:registry packs.rtwrapper set value {id:"rtwrapper",name:"RTWrapper",version:"0.1.1+26.2",kind:"datapack",advancement:"runtoolkit:packs/rtwrapper"} ++scoreboard objectives add rtk.status dummy ++ ++scoreboard players set rtwrapper rtk.registered 1 ++execute unless score rtwrapper rtk.enabled matches 0.. run scoreboard players set rtwrapper rtk.enabled 1 ++ ++data modify storage runtoolkit:registry packs.rtwrapper set value {id:"rtwrapper",name:"RTWrapper",version:"0.1.1+26.2",namespace:"rtwrapper",kind:"datapack",advancement:"runtoolkit:packs/rtwrapper",dependencies:[],hooks:{register:"runtoolkit:packs/rtwrapper/register",load:"runtoolkit:packs/rtwrapper/load",tick:"runtoolkit:packs/rtwrapper/tick",list:"runtoolkit:packs/rtwrapper/list",enable:"runtoolkit:packs/rtwrapper/enable",disable:"runtoolkit:packs/rtwrapper/disable",reload:"runtoolkit:packs/rtwrapper/reload",check_dependencies:"runtoolkit:packs/rtwrapper/check_dependencies"}} +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/reload.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/reload.mcfunction +new file mode 100644 +index 0000000..40a9fce +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/reload.mcfunction +@@ -0,0 +1,4 @@ ++# Reload RTWrapper through the manager. ++function runtoolkit:packs/rtwrapper/register ++function runtoolkit:packs/rtwrapper/load ++tellraw @a [{"text":"[Runtoolkit] Reloaded RTWrapper","color":"aqua"}] +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/tick.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/tick.mcfunction +new file mode 100644 +index 0000000..512eeb5 +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/packs/rtwrapper/tick.mcfunction +@@ -0,0 +1,2 @@ ++# Managed tick hook for RTWrapper. ++execute if score rtwrapper rtk.enabled matches 1.. run function rtwrapper:core/tick +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/tags/function/disable.json b/datapack/RTWrapper-Datapack/data/runtoolkit/tags/function/disable.json +new file mode 100644 +index 0000000..ca05425 +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/tags/function/disable.json +@@ -0,0 +1,5 @@ ++{ ++ "values": [ ++ "runtoolkit:packs/rtwrapper/disable" ++ ] ++} +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/tags/function/enable.json b/datapack/RTWrapper-Datapack/data/runtoolkit/tags/function/enable.json +new file mode 100644 +index 0000000..022dc73 +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/tags/function/enable.json +@@ -0,0 +1,5 @@ ++{ ++ "values": [ ++ "runtoolkit:packs/rtwrapper/enable" ++ ] ++} +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/tags/function/list.json b/datapack/RTWrapper-Datapack/data/runtoolkit/tags/function/list.json +new file mode 100644 +index 0000000..edc84d2 +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/tags/function/list.json +@@ -0,0 +1,5 @@ ++{ ++ "values": [ ++ "runtoolkit:packs/rtwrapper/list" ++ ] ++} +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/tags/function/load.json b/datapack/RTWrapper-Datapack/data/runtoolkit/tags/function/load.json +new file mode 100644 +index 0000000..8a064db +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/tags/function/load.json +@@ -0,0 +1,5 @@ ++{ ++ "values": [ ++ "runtoolkit:packs/rtwrapper/load" ++ ] ++} +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/tags/function/register.json b/datapack/RTWrapper-Datapack/data/runtoolkit/tags/function/register.json +new file mode 100644 +index 0000000..1df6417 +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/tags/function/register.json +@@ -0,0 +1,5 @@ ++{ ++ "values": [ ++ "runtoolkit:packs/rtwrapper/register" ++ ] ++} +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/tags/function/reload.json b/datapack/RTWrapper-Datapack/data/runtoolkit/tags/function/reload.json +new file mode 100644 +index 0000000..1d2a982 +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/tags/function/reload.json +@@ -0,0 +1,5 @@ ++{ ++ "values": [ ++ "runtoolkit:packs/rtwrapper/reload" ++ ] ++} +diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/tags/function/tick.json b/datapack/RTWrapper-Datapack/data/runtoolkit/tags/function/tick.json +new file mode 100644 +index 0000000..6e510ee +--- /dev/null ++++ b/datapack/RTWrapper-Datapack/data/runtoolkit/tags/function/tick.json +@@ -0,0 +1,5 @@ ++{ ++ "values": [ ++ "runtoolkit:packs/rtwrapper/tick" ++ ] ++} +diff --git a/docs/API.md b/docs/API.md +index b9c37b7..e25c35e 100644 +--- a/docs/API.md ++++ b/docs/API.md +@@ -99,33 +99,123 @@ function rtwrapper:api/commands/give_item + + For `give_item`, `components:""` is allowed because it is concatenated directly to `item`, not appended as a separate trailing token. + +-## Runtoolkit loaded-pack registry ++## Runtoolkit global datapack manager + +-RTWrapper includes a shared `runtoolkit` namespace for Runtoolkit datapack discovery: ++RTWrapper includes a shared `runtoolkit` namespace that behaves like a lightweight manager for Runtoolkit datapacks/modules. It does not run vanilla `/datapack enable|disable`; instead it manages Runtoolkit pack registration, dependency checks, load/tick hooks, dynamic listing, and manager-level enable/disable/reload. ++ ++Core files: + + ```text + data/runtoolkit/function/core/load.mcfunction + data/runtoolkit/function/core/tick.mcfunction +-data/runtoolkit/function/api/status.mcfunction ++data/runtoolkit/function/api/*.mcfunction ++data/runtoolkit/function/manager/*.mcfunction + data/runtoolkit/advancement/root.json + data/runtoolkit/advancement/packs/rtwrapper.json + ``` + +-The visual registry uses advancements with the `minecraft:tick` trigger and does not revoke them. After `/reload`, open **Advancements > Runtoolkit** to see loaded Runtoolkit packs/modules. ++Function tags used by the manager: ++ ++```text ++#runtoolkit:register ++#runtoolkit:load ++#runtoolkit:tick ++#runtoolkit:list ++#runtoolkit:enable ++#runtoolkit:disable ++#runtoolkit:reload ++``` ++ ++The Minecraft `load` and `tick` tags call only the Runtoolkit manager entrypoints. The manager then dispatches the registered pack hooks. + +-Status helper: ++### Visual loaded-pack list ++ ++The visual registry uses advancements with the `minecraft:tick` trigger and does not revoke them. After `/reload`, open **Advancements > Runtoolkit** to see loaded Runtoolkit packs/modules. + + ```mcfunction + function runtoolkit:api/status ++function runtoolkit:api/list ++``` ++ ++For console-friendly raw storage output: ++ ++```mcfunction ++function runtoolkit:api/dump_registry ++``` ++ ++### Manager enable / disable / reload ++ ++```mcfunction ++# Disable RTWrapper manager hooks ++data modify storage runtoolkit:api request set value {id:"rtwrapper"} ++function runtoolkit:api/disable ++ ++# Enable RTWrapper manager hooks ++data modify storage runtoolkit:api request set value {id:"rtwrapper"} ++function runtoolkit:api/enable ++ ++# Reload RTWrapper through the manager ++data modify storage runtoolkit:api request set value {id:"rtwrapper"} ++function runtoolkit:api/reload ++``` ++ ++Bulk hooks: ++ ++```mcfunction ++function runtoolkit:api/disable_all ++function runtoolkit:api/enable_all ++function runtoolkit:api/reload_all ++``` ++ ++### Dependency management ++ ++Packs define their metadata in their register hook and can provide a `check_dependencies` hook. Dependency checks should call `runtoolkit:api/require` for each dependency: ++ ++```mcfunction ++data modify storage runtoolkit:api request set value {id:"required_pack_id"} ++function runtoolkit:api/require ++``` ++ ++`api/require` increments `#dependency_errors rtk.status` and appends to `storage runtoolkit:runtime missing_dependencies` when the dependency is not enabled. ++ ++### How another Runtoolkit pack registers ++ ++A Runtoolkit pack should add functions to the manager tags, for example: ++ ++```json ++{ ++ "values": [ ++ "runtoolkit:packs/my_pack/register" ++ ] ++} ++``` ++ ++under: ++ ++```text ++data/runtoolkit/tags/function/register.json ++``` ++ ++Recommended pack hook layout: ++ ++```text ++data/runtoolkit/function/packs/my_pack/register.mcfunction ++data/runtoolkit/function/packs/my_pack/load.mcfunction ++data/runtoolkit/function/packs/my_pack/tick.mcfunction ++data/runtoolkit/function/packs/my_pack/list.mcfunction ++data/runtoolkit/function/packs/my_pack/enable.mcfunction ++data/runtoolkit/function/packs/my_pack/disable.mcfunction ++data/runtoolkit/function/packs/my_pack/reload.mcfunction ++data/runtoolkit/function/packs/my_pack/check_dependencies.mcfunction + ``` + +-Other Runtoolkit datapacks can add themselves by including a child advancement under the shared namespace, for example: ++The pack should also add a child advancement under the shared namespace: + + ```text + data/runtoolkit/advancement/packs/my_pack.json + ``` + +-Use this pattern: ++Use `minecraft:tick` as the trigger: + + ```json + { +diff --git a/scripts/validate_datapack.py b/scripts/validate_datapack.py +index b510fc8..ce01276 100644 +--- a/scripts/validate_datapack.py ++++ b/scripts/validate_datapack.py +@@ -49,8 +49,8 @@ def main() -> None: + fail('pack.mcmeta must declare min_format=[107, 1] and max_format=[107, 1] for the 26.2 target') + + required_tag_values = { +- 'load': {'rtwrapper:core/load', 'runtoolkit:core/load'}, +- 'tick': {'rtwrapper:core/tick', 'runtoolkit:core/tick'}, ++ 'load': {'runtoolkit:core/load'}, ++ 'tick': {'runtoolkit:core/tick'}, + } + for tag, required_values in required_tag_values.items(): + data = load_json(PACK / 'data' / 'minecraft' / 'tags' / 'function' / f'{tag}.json') +@@ -58,6 +58,25 @@ def main() -> None: + if not required_values.issubset(values): + fail(f'{tag}.json missing values: {sorted(required_values - values)}') + ++ for tag in ['register', 'load', 'tick', 'list', 'enable', 'disable', 'reload']: ++ tag_path = PACK / 'data' / 'runtoolkit' / 'tags' / 'function' / f'{tag}.json' ++ if not tag_path.exists(): ++ fail(f'missing runtoolkit function tag {tag_path.relative_to(ROOT)}') ++ values = set(load_json(tag_path).get('values', [])) ++ expected_value = f'runtoolkit:packs/rtwrapper/{tag}' ++ if expected_value not in values: ++ fail(f'{tag_path.relative_to(ROOT)} missing {expected_value}') ++ ++ for function in [ ++ PACK / 'data' / 'runtoolkit' / 'function' / 'api' / 'enable.mcfunction', ++ PACK / 'data' / 'runtoolkit' / 'function' / 'api' / 'disable.mcfunction', ++ PACK / 'data' / 'runtoolkit' / 'function' / 'api' / 'reload.mcfunction', ++ PACK / 'data' / 'runtoolkit' / 'function' / 'api' / 'list.mcfunction', ++ PACK / 'data' / 'runtoolkit' / 'function' / 'manager' / 'require.mcfunction', ++ ]: ++ if not function.exists(): ++ fail(f'missing Runtoolkit manager function {function.relative_to(ROOT)}') ++ + for advancement in [ + PACK / 'data' / 'runtoolkit' / 'advancement' / 'root.json', + PACK / 'data' / 'runtoolkit' / 'advancement' / 'packs' / 'rtwrapper.json', diff --git a/datapack/RTWrapper-Datapack/data/core/function/selector/detect.mcfunction b/datapack/RTWrapper-Datapack/data/core/function/selector/detect.mcfunction new file mode 100644 index 0000000..9995723 --- /dev/null +++ b/datapack/RTWrapper-Datapack/data/core/function/selector/detect.mcfunction @@ -0,0 +1,41 @@ +# Core selector detector. +# Input: storage core:selector input.value = "" +# Output: storage core:selector result = {valid:,kind:"...",value:"..."} +# Return: success if accepted, fail if missing/rejected. +# Allowed: player names (StringLib-checked no @), @s, restricted @a, restricted @e[type=player,...]. +# Rejected: @p, @r, unrestricted @a, unrestricted @e, any string containing @ that is not exact-allowlisted. +scoreboard objectives add core.selector dummy +scoreboard objectives add rtw.temp dummy +scoreboard players set #valid core.selector 0 +scoreboard players set #has_at core.selector 0 +scoreboard players set #length core.selector 0 +scoreboard players set #selector_valid rtw.temp 0 +data modify storage core:selector result set value {valid:0b,kind:"missing",reason:"missing input.value"} +execute unless data storage core:selector input.value run return fail + +data modify storage core:selector result set value {valid:0b,kind:"rejected",reason:"not an allowed selector/player name"} +execute if entity @s run scoreboard players set @s rtw.temp 0 + +# Exact allowed selector forms. +execute if data storage core:selector input {value:"@s"} run function core:selector/private/accept_self +execute if data storage core:selector input {value:"@a[limit=1]"} run function core:selector/private/accept_limited_all +execute if data storage core:selector input {value:"@a[sort=nearest,limit=1]"} run function core:selector/private/accept_limited_all +execute if data storage core:selector input {value:"@a[limit=1,sort=nearest]"} run function core:selector/private/accept_limited_all +execute if data storage core:selector input {value:"@e[type=player,limit=1]"} run function core:selector/private/accept_limited_entity_player +execute if data storage core:selector input {value:"@e[type=player,sort=nearest,limit=1]"} run function core:selector/private/accept_limited_entity_player +execute if data storage core:selector input {value:"@e[type=player,limit=1,sort=nearest]"} run function core:selector/private/accept_limited_entity_player +execute if score #valid core.selector matches 1.. run return 1 + +# Player-name validation needs StringLib. If StringLib is not detected, fail safely. +execute unless score stringlib rtk.enabled matches 1.. run data modify storage core:selector result set value {valid:0b,kind:"missing_dependency",reason:"StringLib required for player-name validation"} +execute unless score stringlib rtk.enabled matches 1.. run return fail + +# Reject any string containing @. If no @ and length is 3..16, accept as player name. +data modify storage stringlib:input find.String set from storage core:selector input.value +data modify storage stringlib:input find.Find set value "@" +data modify storage stringlib:input find.n set value 1 +execute store success score #has_at core.selector run function stringlib:util/find +execute store result score #length core.selector run data get storage core:selector input.value +execute if score #has_at core.selector matches 0 if score #length core.selector matches 3..16 run function core:selector/private/accept_player_name +execute if score #valid core.selector matches 1.. run return 1 +return fail diff --git a/datapack/RTWrapper-Datapack/data/core/function/selector/private/accept_limited_all.mcfunction b/datapack/RTWrapper-Datapack/data/core/function/selector/private/accept_limited_all.mcfunction new file mode 100644 index 0000000..603f592 --- /dev/null +++ b/datapack/RTWrapper-Datapack/data/core/function/selector/private/accept_limited_all.mcfunction @@ -0,0 +1,5 @@ +scoreboard players set #valid core.selector 1 +scoreboard players set #selector_valid rtw.temp 1 +execute if entity @s run scoreboard players set @s rtw.temp 1 +data modify storage core:selector result set value {valid:1b,kind:"limited_all"} +data modify storage core:selector result.value set from storage core:selector input.value diff --git a/datapack/RTWrapper-Datapack/data/core/function/selector/private/accept_limited_entity_player.mcfunction b/datapack/RTWrapper-Datapack/data/core/function/selector/private/accept_limited_entity_player.mcfunction new file mode 100644 index 0000000..ae2e384 --- /dev/null +++ b/datapack/RTWrapper-Datapack/data/core/function/selector/private/accept_limited_entity_player.mcfunction @@ -0,0 +1,5 @@ +scoreboard players set #valid core.selector 1 +scoreboard players set #selector_valid rtw.temp 1 +execute if entity @s run scoreboard players set @s rtw.temp 1 +data modify storage core:selector result set value {valid:1b,kind:"limited_entity_player"} +data modify storage core:selector result.value set from storage core:selector input.value diff --git a/datapack/RTWrapper-Datapack/data/core/function/selector/private/accept_player_name.mcfunction b/datapack/RTWrapper-Datapack/data/core/function/selector/private/accept_player_name.mcfunction new file mode 100644 index 0000000..8091aa7 --- /dev/null +++ b/datapack/RTWrapper-Datapack/data/core/function/selector/private/accept_player_name.mcfunction @@ -0,0 +1,5 @@ +scoreboard players set #valid core.selector 1 +scoreboard players set #selector_valid rtw.temp 1 +execute if entity @s run scoreboard players set @s rtw.temp 1 +data modify storage core:selector result set value {valid:1b,kind:"player_name"} +data modify storage core:selector result.value set from storage core:selector input.value diff --git a/datapack/RTWrapper-Datapack/data/core/function/selector/private/accept_self.mcfunction b/datapack/RTWrapper-Datapack/data/core/function/selector/private/accept_self.mcfunction new file mode 100644 index 0000000..990c59d --- /dev/null +++ b/datapack/RTWrapper-Datapack/data/core/function/selector/private/accept_self.mcfunction @@ -0,0 +1,4 @@ +scoreboard players set #valid core.selector 1 +scoreboard players set #selector_valid rtw.temp 1 +execute if entity @s run scoreboard players set @s rtw.temp 1 +data modify storage core:selector result set value {valid:1b,kind:"self",value:"@s"} diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/batch/enqueue_next.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/batch/enqueue_next.mcfunction new file mode 100644 index 0000000..5732a81 --- /dev/null +++ b/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/batch/enqueue_next.mcfunction @@ -0,0 +1,9 @@ +data modify storage rtwrapper:runtime batch.current set from storage rtwrapper:runtime batch.requests[0] +data remove storage rtwrapper:runtime batch.requests[0] +data modify storage rtwrapper:runtime batch.request set from storage rtwrapper:runtime batch.current.req +execute if data storage rtwrapper:runtime batch.current.args run data modify storage rtwrapper:runtime batch.request.conditions set from storage rtwrapper:runtime batch.current.args +execute unless data storage rtwrapper:runtime batch.request.runSafeMode run data modify storage rtwrapper:runtime batch.request.runSafeMode set from storage rtwrapper:runtime batch.runSafeMode +data modify storage rtwrapper:runtime queue append from storage rtwrapper:runtime batch.request +data remove storage rtwrapper:runtime batch.current +data remove storage rtwrapper:runtime batch.request +execute if data storage rtwrapper:runtime batch.requests[0] run function rtwrapper:api/batch/enqueue_next diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/enqueue_many.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/enqueue_many.mcfunction new file mode 100644 index 0000000..4035d1b --- /dev/null +++ b/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/enqueue_many.mcfunction @@ -0,0 +1,11 @@ +# Enqueue many conditional requests. +# Supported storage shapes: +# rtwrapper:api batch = {requests:[{req:{cmd:"...",params:{...}},args:[...]}],runSafeMode:1b} +# rtwrapper:api requests = [{req:{...},args:[...]}] plus optional rtwrapper:api runSafeMode +# Each item args[] is copied to req.conditions[]. +data remove storage rtwrapper:runtime batch +execute if data storage rtwrapper:api batch.requests[0] run data modify storage rtwrapper:runtime batch set from storage rtwrapper:api batch +execute unless data storage rtwrapper:runtime batch.requests[0] if data storage rtwrapper:api requests[0] run data modify storage rtwrapper:runtime batch.requests set from storage rtwrapper:api requests +execute if data storage rtwrapper:api runSafeMode run data modify storage rtwrapper:runtime batch.runSafeMode set from storage rtwrapper:api runSafeMode +execute unless data storage rtwrapper:runtime batch.runSafeMode run data modify storage rtwrapper:runtime batch.runSafeMode set value 0b +execute if data storage rtwrapper:runtime batch.requests[0] run function rtwrapper:api/batch/enqueue_next diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/run.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/run.mcfunction index 26f4052..746bc6c 100644 --- a/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/run.mcfunction +++ b/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/run.mcfunction @@ -1,4 +1,11 @@ -# Consume rtwrapper:api request, then drain the queue. -execute if data storage rtwrapper:api request.cmd run function rtwrapper:api/enqueue -execute unless data storage rtwrapper:api request.cmd if data storage rtwrapper:api request.type run function rtwrapper:api/enqueue -function rtwrapper:core/run/run_actions +# Consume rtwrapper:api request. +# Default runSafeMode is 0b: immediate drain of current request + queue. +# runSafeMode:1b: process at most one action with run_next. +scoreboard players set #run_safe rtw.temp 0 +execute if data storage rtwrapper:api request.cmd unless data storage rtwrapper:api request.runSafeMode run data modify storage rtwrapper:api request.runSafeMode set value 0b +execute unless data storage rtwrapper:api request.cmd if data storage rtwrapper:api request.type unless data storage rtwrapper:api request.runSafeMode run data modify storage rtwrapper:api request.runSafeMode set value 0b +execute if data storage rtwrapper:api request{runSafeMode:1b} run scoreboard players set #run_safe rtw.temp 1 +execute if score #run_safe rtw.temp matches 1 run function rtwrapper:core/run/run_next +execute unless score #run_safe rtw.temp matches 1 if data storage rtwrapper:api request.cmd run function rtwrapper:api/enqueue +execute unless score #run_safe rtw.temp matches 1 unless data storage rtwrapper:api request.cmd if data storage rtwrapper:api request.type run function rtwrapper:api/enqueue +execute unless score #run_safe rtw.temp matches 1 run function rtwrapper:core/run/run_actions diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/run_many.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/run_many.mcfunction new file mode 100644 index 0000000..965e95c --- /dev/null +++ b/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/run_many.mcfunction @@ -0,0 +1,5 @@ +# Enqueue many conditional requests and run according to runSafeMode. +# runSafeMode:0b => immediate full drain. runSafeMode:1b => run one safe step; remaining queue waits for autotick/manual run_next. +function rtwrapper:api/enqueue_many +execute if data storage rtwrapper:runtime batch{runSafeMode:1b} run function rtwrapper:core/run/run_next +execute unless data storage rtwrapper:runtime batch{runSafeMode:1b} run function rtwrapper:core/run/run_actions diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/run_next.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/run_next.mcfunction index 65608fe..4ed76fb 100644 --- a/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/run_next.mcfunction +++ b/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/run_next.mcfunction @@ -1,4 +1,6 @@ # Consume one queued/direct action only. +execute if data storage rtwrapper:api request.cmd unless data storage rtwrapper:api request.runSafeMode run data modify storage rtwrapper:api request.runSafeMode set value 1b +execute unless data storage rtwrapper:api request.cmd if data storage rtwrapper:api request.type unless data storage rtwrapper:api request.runSafeMode run data modify storage rtwrapper:api request.runSafeMode set value 1b execute if data storage rtwrapper:api request.cmd run function rtwrapper:api/enqueue execute unless data storage rtwrapper:api request.cmd if data storage rtwrapper:api request.type run function rtwrapper:api/enqueue function rtwrapper:core/run/run_next diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/testmode/off.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/testmode/off.mcfunction deleted file mode 100644 index 1eb3030..0000000 --- a/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/testmode/off.mcfunction +++ /dev/null @@ -1,2 +0,0 @@ -tag @s remove rtwrapper.testMode -tellraw @s [{"text":"[RTWrapper] testMode disabled","color":"gold"}] diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/testmode/on.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/testmode/on.mcfunction deleted file mode 100644 index c6c04ef..0000000 --- a/datapack/RTWrapper-Datapack/data/rtwrapper/function/api/testmode/on.mcfunction +++ /dev/null @@ -1,3 +0,0 @@ -tag @s add rtwrapper.testMode -scoreboard players enable @s RTWrapper -tellraw @s [{"text":"[RTWrapper] testMode enabled. Use ","color":"gold"},{"text":"/trigger RTWrapper set 1","color":"yellow"},{"text":" for UI.","color":"gold"}] diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/0.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/0.mcfunction new file mode 100644 index 0000000..b6066d9 --- /dev/null +++ b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/0.mcfunction @@ -0,0 +1,2 @@ +data modify storage rtwrapper:runtime condition.current set from storage rtwrapper:runtime current.conditions[0] +function rtwrapper:condition/check_current diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/1.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/1.mcfunction new file mode 100644 index 0000000..8cee683 --- /dev/null +++ b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/1.mcfunction @@ -0,0 +1,2 @@ +data modify storage rtwrapper:runtime condition.current set from storage rtwrapper:runtime current.conditions[1] +function rtwrapper:condition/check_current diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/2.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/2.mcfunction new file mode 100644 index 0000000..89a6c5b --- /dev/null +++ b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/2.mcfunction @@ -0,0 +1,2 @@ +data modify storage rtwrapper:runtime condition.current set from storage rtwrapper:runtime current.conditions[2] +function rtwrapper:condition/check_current diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/3.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/3.mcfunction new file mode 100644 index 0000000..944f4d7 --- /dev/null +++ b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/3.mcfunction @@ -0,0 +1,2 @@ +data modify storage rtwrapper:runtime condition.current set from storage rtwrapper:runtime current.conditions[3] +function rtwrapper:condition/check_current diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/4.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/4.mcfunction new file mode 100644 index 0000000..e98272c --- /dev/null +++ b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/4.mcfunction @@ -0,0 +1,2 @@ +data modify storage rtwrapper:runtime condition.current set from storage rtwrapper:runtime current.conditions[4] +function rtwrapper:condition/check_current diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/5.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/5.mcfunction new file mode 100644 index 0000000..e42a421 --- /dev/null +++ b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/5.mcfunction @@ -0,0 +1,2 @@ +data modify storage rtwrapper:runtime condition.current set from storage rtwrapper:runtime current.conditions[5] +function rtwrapper:condition/check_current diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/6.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/6.mcfunction new file mode 100644 index 0000000..dbae0f8 --- /dev/null +++ b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/6.mcfunction @@ -0,0 +1,2 @@ +data modify storage rtwrapper:runtime condition.current set from storage rtwrapper:runtime current.conditions[6] +function rtwrapper:condition/check_current diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/7.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/7.mcfunction new file mode 100644 index 0000000..07c2dbb --- /dev/null +++ b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check/7.mcfunction @@ -0,0 +1,2 @@ +data modify storage rtwrapper:runtime condition.current set from storage rtwrapper:runtime current.conditions[7] +function rtwrapper:condition/check_current diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check_current.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check_current.mcfunction new file mode 100644 index 0000000..697dcf9 --- /dev/null +++ b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/check_current.mcfunction @@ -0,0 +1,13 @@ +# Condition object forms: +# {type:"predicate",id:"my_pack:my_predicate"} +# {type:"score",name:"#foo",objective:"myScore",value:"1"} +# {type:"score",name:"#foo",score:"myScore",value:"1"} +execute unless data storage rtwrapper:runtime condition.current.type run scoreboard players set #conditions rtw.temp 0 +execute if data storage rtwrapper:runtime condition.current{type:"predicate"} unless data storage rtwrapper:runtime condition.current.id run scoreboard players set #conditions rtw.temp 0 +execute if data storage rtwrapper:runtime condition.current{type:"predicate"} if data storage rtwrapper:runtime condition.current.id run function rtwrapper:condition/predicate with storage rtwrapper:runtime condition.current +execute if data storage rtwrapper:runtime condition.current{type:"score"} unless data storage rtwrapper:runtime condition.current.name run scoreboard players set #conditions rtw.temp 0 +execute if data storage rtwrapper:runtime condition.current{type:"score"} unless data storage rtwrapper:runtime condition.current.value run scoreboard players set #conditions rtw.temp 0 +execute if data storage rtwrapper:runtime condition.current{type:"score"} if data storage rtwrapper:runtime condition.current.objective run function rtwrapper:condition/score_objective with storage rtwrapper:runtime condition.current +execute if data storage rtwrapper:runtime condition.current{type:"score"} unless data storage rtwrapper:runtime condition.current.objective if data storage rtwrapper:runtime condition.current.score run function rtwrapper:condition/score_alias with storage rtwrapper:runtime condition.current +execute unless data storage rtwrapper:runtime condition.current{type:"predicate"} unless data storage rtwrapper:runtime condition.current{type:"score"} run scoreboard players set #conditions rtw.temp 0 +data remove storage rtwrapper:runtime condition.current diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/predicate.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/predicate.mcfunction new file mode 100644 index 0000000..99c0356 --- /dev/null +++ b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/predicate.mcfunction @@ -0,0 +1 @@ +$execute unless predicate $(id) run scoreboard players set #conditions rtw.temp 0 diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/score_alias.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/score_alias.mcfunction new file mode 100644 index 0000000..345c3d0 --- /dev/null +++ b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/score_alias.mcfunction @@ -0,0 +1 @@ +$execute unless score $(name) $(score) matches $(value) run scoreboard players set #conditions rtw.temp 0 diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/score_objective.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/score_objective.mcfunction new file mode 100644 index 0000000..09acd1c --- /dev/null +++ b/datapack/RTWrapper-Datapack/data/rtwrapper/function/condition/score_objective.mcfunction @@ -0,0 +1 @@ +$execute unless score $(name) $(objective) matches $(value) run scoreboard players set #conditions rtw.temp 0 diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/core/load.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/core/load.mcfunction index 6effc83..66094f5 100644 --- a/datapack/RTWrapper-Datapack/data/rtwrapper/function/core/load.mcfunction +++ b/datapack/RTWrapper-Datapack/data/rtwrapper/function/core/load.mcfunction @@ -4,13 +4,13 @@ scoreboard objectives add rtw.config dummy scoreboard objectives add rtw.status dummy scoreboard objectives add rtw.test dummy scoreboard objectives add rtw.temp dummy -scoreboard objectives add RTWrapper trigger execute unless score #debug rtw.config matches 0.. run scoreboard players set #debug rtw.config 0 execute unless score #silent rtw.config matches 0.. run scoreboard players set #silent rtw.config 1 execute unless score #auto_tick rtw.config matches 0.. run scoreboard players set #auto_tick rtw.config 0 execute unless score #processed rtw.status matches 0.. run scoreboard players set #processed rtw.status 0 execute unless score #errors rtw.status matches 0.. run scoreboard players set #errors rtw.status 0 +execute unless score #skipped rtw.status matches 0.. run scoreboard players set #skipped rtw.status 0 execute unless score #selector_match rtw.temp matches 0.. run scoreboard players set #selector_match rtw.temp 0 execute unless score #selector_found rtw.temp matches 0.. run scoreboard players set #selector_found rtw.temp 0 @@ -19,9 +19,8 @@ execute unless data storage rtwrapper:runtime config run data modify storage rtw execute unless data storage rtwrapper:runtime queue run data modify storage rtwrapper:runtime queue set value [] execute unless data storage rtwrapper:runtime current run data modify storage rtwrapper:runtime current set value {} execute unless data storage rtwrapper:runtime selector run data modify storage rtwrapper:runtime selector set value {found:0b} -execute unless data storage rtwrapper:settings dialog run data modify storage rtwrapper:settings dialog set value {mode:"default",columns:2,datapack:{manager:"runtoolkit"}} +execute unless data storage rtwrapper:settings datapack run data modify storage rtwrapper:settings datapack set value {manager:"runtoolkit"} execute unless data storage rtwrapper:api request run data modify storage rtwrapper:api request set value {} execute unless data storage rtwrapper:api params run data modify storage rtwrapper:api params set value {} -scoreboard players enable @a[tag=rtwrapper.debug] RTWrapper execute if score #debug rtw.config matches 1.. run tellraw @a[tag=rtwrapper.debug] [{"text":"[RTWrapper] load complete","color":"gold"}] diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/core/tick.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/core/tick.mcfunction index bd45de9..cb60be0 100644 --- a/datapack/RTWrapper-Datapack/data/rtwrapper/function/core/tick.mcfunction +++ b/datapack/RTWrapper-Datapack/data/rtwrapper/function/core/tick.mcfunction @@ -1,8 +1,3 @@ -# Trigger objective is available to every player, but handler actions are gated -# behind rtwrapper.testMode for in-game experimental UI features. -scoreboard players enable @a RTWrapper -execute as @a[scores={RTWrapper=1..}] run function rtwrapper:trigger/handle - # Disabled by default. Enable with: function rtwrapper:api/autotick/on # Autotick intentionally processes only one action per tick. Use rtwrapper:api/run # when you explicitly want to drain the whole queue immediately. diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/core/wrappers/handler/proc.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/core/wrappers/handler/proc.mcfunction index d671ea0..e324a82 100644 --- a/datapack/RTWrapper-Datapack/data/rtwrapper/function/core/wrappers/handler/proc.mcfunction +++ b/datapack/RTWrapper-Datapack/data/rtwrapper/function/core/wrappers/handler/proc.mcfunction @@ -1,13 +1,25 @@ # Normalise request shape, then prepare dispatch. # Supported request shapes: -# {cmd:"tp", params:{target:"@s", x:"0", y:"80", z:"0"}} -# {type:"tp", params:{target:"@s", x:"0", y:"80", z:"0"}} # type is accepted as an alias. +# {cmd:"tp", params:{target:"@s", x:"0", y:"80", z:"0"}, runSafeMode:1b} +# {type:"tp", params:{target:"@s", x:"0", y:"80", z:"0"}, conditions:[...]} # Parameter rule: provide contiguous named parameters in the documented command order. execute unless data storage rtwrapper:runtime current.cmd if data storage rtwrapper:runtime current.type run data modify storage rtwrapper:runtime current.cmd set from storage rtwrapper:runtime current.type execute unless data storage rtwrapper:runtime current.params if data storage rtwrapper:runtime current.args run data modify storage rtwrapper:runtime current.params set from storage rtwrapper:runtime current.args execute unless data storage rtwrapper:runtime current.params run data modify storage rtwrapper:runtime current.params set value {} +execute unless data storage rtwrapper:runtime current.runSafeMode run data modify storage rtwrapper:runtime current.runSafeMode set value 0b -execute if data storage rtwrapper:runtime current.cmd run function rtwrapper:core/wrappers/handler/dispatch -execute unless data storage rtwrapper:runtime current.cmd run function rtwrapper:core/wrappers/handler/error_missing_cmd +scoreboard players set #conditions rtw.temp 1 +execute if data storage rtwrapper:runtime current.conditions[0] run function rtwrapper:condition/check/0 +execute if data storage rtwrapper:runtime current.conditions[1] run function rtwrapper:condition/check/1 +execute if data storage rtwrapper:runtime current.conditions[2] run function rtwrapper:condition/check/2 +execute if data storage rtwrapper:runtime current.conditions[3] run function rtwrapper:condition/check/3 +execute if data storage rtwrapper:runtime current.conditions[4] run function rtwrapper:condition/check/4 +execute if data storage rtwrapper:runtime current.conditions[5] run function rtwrapper:condition/check/5 +execute if data storage rtwrapper:runtime current.conditions[6] run function rtwrapper:condition/check/6 +execute if data storage rtwrapper:runtime current.conditions[7] run function rtwrapper:condition/check/7 + +execute if score #conditions rtw.temp matches 1.. if data storage rtwrapper:runtime current.cmd run function rtwrapper:core/wrappers/handler/dispatch +execute if score #conditions rtw.temp matches 1.. unless data storage rtwrapper:runtime current.cmd run function rtwrapper:core/wrappers/handler/error_missing_cmd +execute unless score #conditions rtw.temp matches 1.. run scoreboard players add #skipped rtw.status 1 data remove storage rtwrapper:runtime current diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/string/detect_selector.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/string/detect_selector.mcfunction deleted file mode 100644 index 867d968..0000000 --- a/datapack/RTWrapper-Datapack/data/rtwrapper/function/string/detect_selector.mcfunction +++ /dev/null @@ -1,9 +0,0 @@ -# Detect @p/@a/@s/@r/@e in storage rtwrapper:api string.value using StringLib. -# Output: @s rtw.temp = 1 if a selector token is found, 0 otherwise. -scoreboard players set @s rtw.temp 0 -scoreboard players set #selector_match rtw.temp 0 -scoreboard players set #selector_found rtw.temp 0 -execute unless data storage rtwrapper:api string.value run tellraw @s [{"text":"[RTWrapper] selector detect canceled: storage rtwrapper:api string.value missing","color":"red"}] -execute if data storage rtwrapper:api string.value run function rtwrapper:string/detect_selector/run -execute if score @s rtw.temp matches 1 run data modify storage rtwrapper:runtime selector set value {found:1b} -execute unless score @s rtw.temp matches 1 run data modify storage rtwrapper:runtime selector set value {found:0b} diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/string/detect_selector/run.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/string/detect_selector/run.mcfunction deleted file mode 100644 index 442cfbf..0000000 --- a/datapack/RTWrapper-Datapack/data/rtwrapper/function/string/detect_selector/run.mcfunction +++ /dev/null @@ -1,25 +0,0 @@ -data modify storage stringlib:input find.String set from storage rtwrapper:api string.value - -data modify storage stringlib:input find.Find set value "@p" -data modify storage stringlib:input find.n set value 1 -execute store success score #selector_match rtw.temp run function stringlib:util/find -execute if score #selector_match rtw.temp matches 1.. run scoreboard players set @s rtw.temp 1 - -data modify storage stringlib:input find.Find set value "@a" -execute store success score #selector_match rtw.temp run function stringlib:util/find -execute if score #selector_match rtw.temp matches 1.. run scoreboard players set @s rtw.temp 1 - -data modify storage stringlib:input find.Find set value "@s" -execute store success score #selector_match rtw.temp run function stringlib:util/find -execute if score #selector_match rtw.temp matches 1.. run scoreboard players set @s rtw.temp 1 - -data modify storage stringlib:input find.Find set value "@r" -execute store success score #selector_match rtw.temp run function stringlib:util/find -execute if score #selector_match rtw.temp matches 1.. run scoreboard players set @s rtw.temp 1 - -data modify storage stringlib:input find.Find set value "@e" -execute store success score #selector_match rtw.temp run function stringlib:util/find -execute if score #selector_match rtw.temp matches 1.. run scoreboard players set @s rtw.temp 1 - -execute if score @s rtw.temp matches 1 run tellraw @s [{"text":"[RTWrapper] Selector token detected by StringLib","color":"green"}] -execute unless score @s rtw.temp matches 1 run tellraw @s [{"text":"[RTWrapper] No selector token detected by StringLib","color":"gray"}] diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/trigger/handle.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/trigger/handle.mcfunction deleted file mode 100644 index 276735c..0000000 --- a/datapack/RTWrapper-Datapack/data/rtwrapper/function/trigger/handle.mcfunction +++ /dev/null @@ -1,14 +0,0 @@ -# RTWrapper trigger dispatcher. -# rtw.temp: 1 means accepted/success, 0 means canceled/error. -scoreboard players set @s rtw.temp 0 -execute unless entity @s[tag=rtwrapper.testMode] run tellraw @s [{"text":"[RTWrapper] Trigger canceled: ","color":"red"},{"text":"rtwrapper.testMode tag required. Run function rtwrapper:api/testmode/on","color":"yellow"}] - -execute if entity @s[tag=rtwrapper.testMode] if score @s RTWrapper matches 1 run function rtwrapper:ui/open -execute if entity @s[tag=rtwrapper.testMode] if score @s RTWrapper matches 2 run function rtwrapper:trigger/run_request -execute if entity @s[tag=rtwrapper.testMode] if score @s RTWrapper matches 3 run function rtwrapper:ui/list_commands -execute if entity @s[tag=rtwrapper.testMode] if score @s RTWrapper matches 4 run function runtoolkit:dpman -execute if entity @s[tag=rtwrapper.testMode] unless score @s RTWrapper matches 1..4 run tellraw @s [{"text":"[RTWrapper] Unknown trigger value. Use 1=dialog, 2=run, 3=list, 4=dpman.","color":"red"}] - -execute if score @s rtw.temp matches 0 run tellraw @s [{"text":"[RTWrapper] Operation ended with rtw.temp=0 (canceled/error).","color":"red"}] -scoreboard players set @s RTWrapper 0 -scoreboard players enable @s RTWrapper diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/trigger/run_request.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/trigger/run_request.mcfunction deleted file mode 100644 index 6b63216..0000000 --- a/datapack/RTWrapper-Datapack/data/rtwrapper/function/trigger/run_request.mcfunction +++ /dev/null @@ -1,7 +0,0 @@ -# Trigger value 2: run the current RTWrapper API request. -execute if data storage rtwrapper:api request.cmd run scoreboard players set @s rtw.temp 1 -execute unless data storage rtwrapper:api request.cmd if data storage rtwrapper:api request.type run scoreboard players set @s rtw.temp 1 -execute if data storage rtwrapper:api request.params.target run data modify storage rtwrapper:api string.value set from storage rtwrapper:api request.params.target -execute if data storage rtwrapper:api request.params.target run function rtwrapper:string/detect_selector -execute if score @s rtw.temp matches 1 run function rtwrapper:api/run -execute unless score @s rtw.temp matches 1 run tellraw @s [{"text":"[RTWrapper] No rtwrapper:api request.cmd/type found.","color":"red"}] diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/list_commands.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/list_commands.mcfunction deleted file mode 100644 index 2f6b986..0000000 --- a/datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/list_commands.mcfunction +++ /dev/null @@ -1,14 +0,0 @@ -# Trigger value 3: list command wrapper categories in chat. -scoreboard players set @s rtw.temp 1 -tellraw @s [{"text":"[RTWrapper] Command wrappers (26.2)","color":"gold","bold":true}] -tellraw @s [{"text":" - advancement, attribute, bossbar, clear, clone, damage, data, datapack","color":"yellow"}] -tellraw @s [{"text":" - debug, defaultgamemode, dialog, difficulty, effect, enchant, execute, experience","color":"yellow"}] -tellraw @s [{"text":" - fetchprofile, fill, fillbiome, forceload, function, gamemode, gamerule, give","color":"yellow"}] -tellraw @s [{"text":" - help, item, jfr, kick, kill, list, locate, loot","color":"yellow"}] -tellraw @s [{"text":" - me/msg/tell/w, op/deop, pardon/ban, particle, perf, place, playsound, publish/unpublish","color":"yellow"}] -tellraw @s [{"text":" - random, recipe, reload, return, ride, rotate, save-all/save-on/save-off, say","color":"yellow"}] -tellraw @s [{"text":" - schedule, scoreboard, seed, setblock, setidletimeout, setworldspawn, spawnpoint, spectate","color":"yellow"}] -tellraw @s [{"text":" - spreadplayers, stop, stopsound, stopwatch, summon, swing, tag, team/teammsg/tm","color":"yellow"}] -tellraw @s [{"text":" - teleport/tp, tellraw, test, tick, time, title, transfer, trigger","color":"yellow"}] -tellraw @s [{"text":" - version, waypoint, weather, whitelist, worldborder, xp","color":"yellow"}] -tellraw @s [{"text":"Use datapack/commands-26.2.json for meaningful params. Use function rtwrapper:ui/open for dialog UI.","color":"gray"}] diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/open.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/open.mcfunction deleted file mode 100644 index 73fc27f..0000000 --- a/datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/open.mcfunction +++ /dev/null @@ -1,2 +0,0 @@ -# Trigger value 1: open RTWrapper inline dialog UI. -execute store success score @s rtw.temp run dialog show @s {type:"minecraft:multi_action",title:{text:"RTWrapper Test UI",color:"aqua",bold:true},body:[{type:"minecraft:plain_message",contents:{text:"Experimental in-game UI. Requires rtwrapper.testMode. Buttons use trigger/function commands."}}],columns:2,actions:[{label:{text:"Run current request",color:"green"},tooltip:{text:"Runs function rtwrapper:api/run via trigger value 2"},action:{type:"run_command",command:"trigger RTWrapper set 2"}},{label:{text:"List commands",color:"yellow"},action:{type:"run_command",command:"trigger RTWrapper set 3"}},{label:{text:"Runtoolkit DP Manager",color:"gold"},action:{type:"run_command",command:"trigger RTWrapper set 4"}},{label:{text:"Settings",color:"aqua"},action:{type:"run_command",command:"function rtwrapper:ui/settings"}},{label:{text:"Suggest /give wrapper",color:"white"},action:{type:"suggest_command",command:'data modify storage rtwrapper:api request set value {cmd:"give",params:{target:"@s",item:"minecraft:stone",count:"1"}}'}},{label:{text:"Suggest /tp wrapper",color:"white"},action:{type:"suggest_command",command:'data modify storage rtwrapper:api request set value {cmd:"tp",params:{target:"@s",x:"0",y:"80",z:"0"}}'}},{label:{text:"Selector check helper",color:"light_purple"},action:{type:"run_command",command:"function rtwrapper:ui/selector_help"}},{label:{text:"Copy docs hint",color:"gray"},action:{type:"copy_to_clipboard",value:"Use /trigger RTWrapper set 1..4 after function rtwrapper:api/testmode/on"}}],exit_action:{label:{text:"Close",color:"red"}}} diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/selector_help.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/selector_help.mcfunction deleted file mode 100644 index bbb1e34..0000000 --- a/datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/selector_help.mcfunction +++ /dev/null @@ -1,4 +0,0 @@ -tellraw @s [{"text":"[RTWrapper] Selector detection uses StringLib. Example:","color":"gold"}] -tellraw @s [{"text":"data modify storage rtwrapper:api string.value set value \"@p\"","color":"yellow"}] -tellraw @s [{"text":"function rtwrapper:string/detect_selector","color":"yellow"}] -scoreboard players set @s rtw.temp 1 diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/settings.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/settings.mcfunction deleted file mode 100644 index 262f3cb..0000000 --- a/datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/settings.mcfunction +++ /dev/null @@ -1,3 +0,0 @@ -# RTWrapper settings dialog. -dialog show @s {type:"minecraft:multi_action",title:{text:"RTWrapper Settings",color:"gold",bold:true},body:[{type:"minecraft:plain_message",contents:{text:"Dialog/datapack customization helpers. These are testMode-only in-game controls."}}],columns:2,actions:[{label:{text:"Debug ON",color:"green"},action:{type:"run_command",command:"function rtwrapper:api/debug/on"}},{label:{text:"Debug OFF",color:"red"},action:{type:"run_command",command:"function rtwrapper:api/debug/off"}},{label:{text:"Silent ON",color:"green"},action:{type:"run_command",command:"function rtwrapper:api/silent/on"}},{label:{text:"Silent OFF",color:"red"},action:{type:"run_command",command:"function rtwrapper:api/silent/off"}},{label:{text:"Autotick ON",color:"green"},action:{type:"run_command",command:"function rtwrapper:api/autotick/on"}},{label:{text:"Autotick OFF",color:"red"},action:{type:"run_command",command:"function rtwrapper:api/autotick/off"}},{label:{text:"Compact dialog",color:"yellow"},action:{type:"run_command",command:"function rtwrapper:ui/settings/compact"}},{label:{text:"Wide dialog",color:"yellow"},action:{type:"run_command",command:"function rtwrapper:ui/settings/wide"}},{label:{text:"Disable testMode",color:"dark_red"},action:{type:"run_command",command:"function rtwrapper:api/testmode/off"}},{label:{text:"Back",color:"aqua"},action:{type:"run_command",command:"trigger RTWrapper set 1"}}],exit_action:{label:{text:"Close"}}} -scoreboard players set @s rtw.temp 1 diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/settings/compact.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/settings/compact.mcfunction deleted file mode 100644 index ea1bb92..0000000 --- a/datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/settings/compact.mcfunction +++ /dev/null @@ -1,2 +0,0 @@ -data modify storage rtwrapper:settings dialog set value {mode:"compact",columns:1} -tellraw @s [{"text":"[RTWrapper] Dialog mode set to compact","color":"gold"}] diff --git a/datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/settings/wide.mcfunction b/datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/settings/wide.mcfunction deleted file mode 100644 index c4e0f3a..0000000 --- a/datapack/RTWrapper-Datapack/data/rtwrapper/function/ui/settings/wide.mcfunction +++ /dev/null @@ -1,2 +0,0 @@ -data modify storage rtwrapper:settings dialog set value {mode:"wide",columns:3} -tellraw @s [{"text":"[RTWrapper] Dialog mode set to wide","color":"gold"}] diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/dpman.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/dpman.mcfunction index 9be61fa..a55c88c 100644 --- a/datapack/RTWrapper-Datapack/data/runtoolkit/function/dpman.mcfunction +++ b/datapack/RTWrapper-Datapack/data/runtoolkit/function/dpman.mcfunction @@ -1,4 +1,7 @@ -# Runtoolkit datapack manager dialog. In-game dialog is testMode-only. -execute unless entity @s[tag=rtwrapper.testMode] run function runtoolkit:api/list -execute if entity @s[tag=rtwrapper.testMode] run dialog show @s {type:"minecraft:multi_action",title:{text:"Runtoolkit Datapack Manager",color:"gold",bold:true},body:[{type:"minecraft:plain_message",contents:{text:"Manage Runtoolkit-registered datapacks. This does not run vanilla /datapack enable|disable; it controls manager hooks."}}],columns:2,actions:[{label:{text:"List packs",color:"yellow"},action:{type:"run_command",command:"function runtoolkit:api/list"}},{label:{text:"Dump registry",color:"gray"},action:{type:"run_command",command:"function runtoolkit:api/dump_registry"}},{label:{text:"Enable RTWrapper",color:"green"},action:{type:"run_command",command:"function runtoolkit:dpman/enable_rtwrapper"}},{label:{text:"Disable RTWrapper",color:"red"},action:{type:"run_command",command:"function runtoolkit:dpman/disable_rtwrapper"}},{label:{text:"Reload RTWrapper",color:"aqua"},action:{type:"run_command",command:"function runtoolkit:dpman/reload_rtwrapper"}},{label:{text:"Reload all",color:"light_purple"},action:{type:"run_command",command:"function runtoolkit:api/reload_all"}},{label:{text:"Back to RTWrapper",color:"aqua"},action:{type:"run_command",command:"trigger RTWrapper set 1"}}],exit_action:{label:{text:"Close"}}} -execute if entity @s[tag=rtwrapper.testMode] run scoreboard players set @s rtw.temp 1 +# Dialog-free Runtoolkit datapack manager entrypoint. +tellraw @s [{"text":"[Runtoolkit] Datapack Manager","color":"gold","bold":true}] +function runtoolkit:api/list +tellraw @s [{"text":"Commands:","color":"yellow"}] +tellraw @s [{"text":" data modify storage runtoolkit:api request set value {id:\"rtwrapper\"}","color":"gray"}] +tellraw @s [{"text":" function runtoolkit:api/enable | disable | reload","color":"gray"}] +tellraw @s [{"text":" function runtoolkit:api/reload_all / enable_all / disable_all / dump_registry","color":"gray"}] diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/dpman/disable_rtwrapper.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/dpman/disable_rtwrapper.mcfunction deleted file mode 100644 index 08ee5de..0000000 --- a/datapack/RTWrapper-Datapack/data/runtoolkit/function/dpman/disable_rtwrapper.mcfunction +++ /dev/null @@ -1,2 +0,0 @@ -data modify storage runtoolkit:api request set value {id:"rtwrapper"} -function runtoolkit:api/disable diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/dpman/enable_rtwrapper.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/dpman/enable_rtwrapper.mcfunction deleted file mode 100644 index 7a86cd7..0000000 --- a/datapack/RTWrapper-Datapack/data/runtoolkit/function/dpman/enable_rtwrapper.mcfunction +++ /dev/null @@ -1,2 +0,0 @@ -data modify storage runtoolkit:api request set value {id:"rtwrapper"} -function runtoolkit:api/enable diff --git a/datapack/RTWrapper-Datapack/data/runtoolkit/function/dpman/reload_rtwrapper.mcfunction b/datapack/RTWrapper-Datapack/data/runtoolkit/function/dpman/reload_rtwrapper.mcfunction deleted file mode 100644 index 0679d0b..0000000 --- a/datapack/RTWrapper-Datapack/data/runtoolkit/function/dpman/reload_rtwrapper.mcfunction +++ /dev/null @@ -1,2 +0,0 @@ -data modify storage runtoolkit:api request set value {id:"rtwrapper"} -function runtoolkit:api/reload diff --git a/datapack/RTWrapper-Datapack/pack.png b/datapack/RTWrapper-Datapack/pack.png deleted file mode 100644 index fe9d7137933be896a21fb0ed52dc0f86349de150..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1835 zcmV+`2h{k9P) zzi%Wp6vyAJQr}6rBdo+#k+RZ86k8uUilc*q`)epcP~Kk?3h1B!L7n21NQrdSAq`8i z(%nBW6v2zfYma|C&pf~J`&QXaKDNj2jXg8|#?!3Uo9^cNvVi~{KUsF>cTZ22aJOwk zaI1vR&+qQrpm+iRGyniYNni_lNF!n-{CK=C85RHlH`kZVYP~rKbV%?40PeOeJUv;4 z{HgF0^S&7NvO|pop@BD5@|hsKeKa`uVzu6MzDAs$ou{-C(eP_6Tz1R3jp_(A}06%0A1HX(*)m=3ZIGBW96E*Rm>NzLUiz-zyFGEgc8?I zzr6SudWGfcL&}PKo{3DLHGyRo){h^ zfa~nf)d=CI=T$6E&(05$AZi4_G(z}tcs&V{5+Fw-gfAlr7MN8i>Vps-p9DuL6H-^9 zR3lQaZd3SIKmV}*`QpoOz5J)&yxRZ#=JkcQezW``bzL`DzE2eWr)vR#Uyq(Lll9=J z&qKT(9NTBJep~rN3Mt z0#|TcN#MIm4=vwSp88yB|Cu^i3^E@Db6o%tzR#-#A=4n@y$0Zt$-<}cK^K9gAB6CJ z-lgEXfD#Tu`22S_;s>aR2RL*FML!3iJ_zCSnnI2+jj+uvzyJAba5*oYzf7r~d;x_! zuP*>5B|wfw0N`$Ms5>z?AA92DN&@i%RLtLPTUc;d1r&Zf{s^EyC#2}NKYmGW06=-e zF$EtZbGmr({AF_e+57-lQJ)FaJXS^c0kEPD0yU3S5qE90 ze!qk{nUcpUrhaDva5{)N@V1jg@iET`V(TBA0=Nxl!pHNY`vCyTw3l(>G4jQ1bg->(Ifk_4_1#kWyIjBNk!$8GH(7e6aM0CoKw`PumasO#s(PvQrN)!0kX zzD=OG4(BQSX!`?T#l3zm{H*-|)b(@VXYU8Vs)Qh7;@*Do17r~Q_KP1NgSfX}`~d%nd;2jGKfnxl0PzC| zkKkp0fXew<`2nWnZBG|DwO?s=et@Zyz)S>A?FSg?eF0c;>qO2;Q2qV@xx9v|9Az+} zUO)=vXYB`|Xum!!P`6+F0Dyl^k*b_YfRV5MD)tAUk{S)D+dq^f2Df3}p^lsstM}jF7@~&aZ_M6)RXMbNn>AXH2 zV1i)YKKcT$tE42T)(?<#IF1Sa81<(}CBadSSI4J{^GE{l|Eud$Rl-Ohet<;YeBw8C z{kp^tAiVeigcm=6@Ztv$Ui<*UNAa`WA3*ppUi<)+^Hul(xbqnK>Swb*z})$i*TTfq z@6sfG0N26AyiAHi5Xa=l(xiR>tV$S;sUQ1dMZP}(R@6bD=CLYbe*mnggFww=r6)m> zAq1d5r|ENf%*71nIhm4I{f((#_6HC?pC5p8C)Ca#0UR0yQyal?I4y<${If4e>d*GR z0M&@t7e$*q=OA+Av-$z3hcn>|@B?t{gqrzL`~ciKp=N$GKY;K&_)+};!k5pF?gtRQ zY`%*90fd+L1ys+wRNejnHS+)k_yL5cUjQQa0|;ME63G4lmGa^T5MKNM!e`nafchYW zPv;Nz2Y7pX1poji%V(1{Lillc6-yOcT?c*u842p*7rXnmN8D{&`0#!Q0P&+hY9oXn zj~_bwclT{$y$#fzL6QV~`H api/enqueue when request exists - -> core/run/run_actions - -> core/wrappers/handler/main - -> proc - -> dispatch - -> exec - -> core/wrappers/internal/ - -> core/wrappers/internal/variants/_ -``` - -A generated variant looks like: +`runSafeMode` defaults to `0b`: -```mcfunction -$ $(meaningful_name_a) $(meaningful_name_b) ... -``` - -Put one token or one already-formatted Brigadier group in each parameter. For example, JSON/text messages can still be stored as one parameter value if Minecraft expects that parameter as one continuous component. - -## Direct API command wrappers +- `0b`: immediate full drain. +- `1b`: safe one-step execution. -You can also place parameters in `storage rtwrapper:api params` and call a direct API function: +## Direct command API ```mcfunction -# /give @s minecraft:stone 1 data modify storage rtwrapper:api params set value {target:"@s",item:"minecraft:stone",count:"1"} function rtwrapper:api/commands/give ``` -The direct API copies `rtwrapper:api params` to runtime storage and invokes `core/wrappers/internal/`, which chooses the correct `_` variant. +Parameter names are defined in `datapack/commands-26.2.json`. -## Convenience named wrappers - -The datapack still includes named examples for common macro use: +## Batch requests ```mcfunction -# $tp $(target) $(x) $(y) $(z) -data modify storage rtwrapper:api params set value {target:"@s",x:"0",y:"80",z:"0"} -function rtwrapper:api/commands/tp_pos - -# $tp $(target) $(x) $(y) $(z) $(rotation) -data modify storage rtwrapper:api params set value {target:"@s",x:"0",y:"80",z:"0",rotation:"0 0"} -function rtwrapper:api/commands/tp_pos_rot - -# $give $(target) $(item)$(components) $(count) -data modify storage rtwrapper:api params set value {target:"@s",item:"minecraft:stone",components:"",count:"1"} -function rtwrapper:api/commands/give_item +data modify storage rtwrapper:api batch set value {runSafeMode:1b,requests:[{req:{cmd:"give",params:{target:"@s",item:"minecraft:stone",count:"1"}},args:[{type:"score",name:"#allow",objective:"myScore",value:"1"}]},{req:{cmd:"function",params:{function_id:"my_pack:do_thing"}},args:[{type:"predicate",id:"my_pack:my_predicate"}]}]} +function rtwrapper:api/run_many ``` -For `give_item`, `components:""` is allowed because it is concatenated directly to `item`, not appended as a separate trailing token. +Item shape: -## Trigger and dialog UI - -RTWrapper creates two trigger-related objectives: - -```mcfunction -scoreboard objectives add rtw.temp dummy -scoreboard objectives add RTWrapper trigger +```snbt +{req:{...normal RTWrapper request...},args:[...conditions...]} ``` -`rtw.temp` is the temporary success/cancel flag used by UI and trigger functions: - -- `1` = accepted/success -- `0` = canceled/error +Supported conditions: -The in-game UI is intentionally gated behind the `rtwrapper.testMode` tag. This keeps experimental dialog features out of normal gameplay. - -Enable test mode for yourself: - -```mcfunction -function rtwrapper:api/testmode/on +```snbt +{type:"predicate",id:"my_pack:my_predicate"} +{type:"score",name:"#allow",objective:"myScore",value:"1"} +{type:"score",name:"#allow",score:"myScore",value:"1"} ``` -Trigger values: +Internally `args[]` is copied to `conditions[]` on the queued request. -```mcfunction -trigger RTWrapper set 1 # Open inline dialog UI -trigger RTWrapper set 2 # Run current rtwrapper:api request with rtwrapper:api/run -trigger RTWrapper set 3 # List command wrappers in chat -trigger RTWrapper set 4 # Open runtoolkit:dpman -``` - -Disable test mode: +## Queue and autotick ```mcfunction -function rtwrapper:api/testmode/off -``` - -The dialog UI uses inline SNBT with `/dialog show @s {...}`; no dialog JSON file is required for the generated menus. It uses `minecraft:multi_action` dialogs and button actions such as `run_command`, `suggest_command`, `copy_to_clipboard`, and nested menu functions. +function rtwrapper:api/autotick/on -## StringLib selector detection +data modify storage rtwrapper:api request set value {cmd:"say",params:{message:"queued hello"},runSafeMode:1b} +function rtwrapper:api/enqueue +``` -RTWrapper declares a managed dependency on [StringLib](https://github.com/CMDred/StringLib) and probes it with `stringlib:util/find`. Selector detection helpers search a string for vanilla selector tokens like `@p`, `@a`, `@s`, `@r`, and `@e`. +Autotick runs one action per tick. -Example: +## Core selector detection ```mcfunction -data modify storage rtwrapper:api string.value set value "@p" -function rtwrapper:string/detect_selector +data modify storage core:selector input.value set value "@s" +function core:selector/detect +data get storage core:selector result ``` -Output is stored in `@s rtw.temp` and `storage rtwrapper:runtime selector.found`: +Allowed: -- `rtw.temp = 1`, `found:1b` means a selector token was detected. -- `rtw.temp = 0`, `found:0b` means no selector token was detected or StringLib was unavailable. +- player names, StringLib-validated as no `@` and length 3..16 +- `@s` +- restricted `@a[limit=1]` forms +- restricted `@e[type=player,limit=1]` forms -## Runtoolkit global datapack manager +Rejected: -RTWrapper includes a shared `runtoolkit` namespace that behaves like a lightweight manager for Runtoolkit datapacks/modules. It does not run vanilla `/datapack enable|disable`; instead it manages Runtoolkit pack registration, dependency checks, load/tick hooks, dynamic listing, and manager-level enable/disable/reload. +- `@p` +- `@r` +- unrestricted `@a` +- unrestricted or non-player `@e` -Core files: +If input storage is missing, the function returns fail before doing detection. -```text -data/runtoolkit/function/core/load.mcfunction -data/runtoolkit/function/core/tick.mcfunction -data/runtoolkit/function/api/*.mcfunction -data/runtoolkit/function/manager/*.mcfunction -data/runtoolkit/advancement/root.json -data/runtoolkit/advancement/packs/rtwrapper.json +## Runtoolkit manager + +```mcfunction +function runtoolkit:api/list +function runtoolkit:api/status +function runtoolkit:api/dump_registry +function runtoolkit:dpman ``` -Function tags used by the manager: +Manager hooks: ```text #runtoolkit:register @@ -181,154 +105,28 @@ Function tags used by the manager: #runtoolkit:reload ``` -The Minecraft `load` and `tick` tags call only the Runtoolkit manager entrypoints. The manager then dispatches the registered pack hooks. - -### Visual loaded-pack list - -The visual registry uses advancements with the `minecraft:tick` trigger and does not revoke them. After `/reload`, open **Advancements > Runtoolkit** to see loaded Runtoolkit packs/modules. +Enable/disable/reload one managed pack: ```mcfunction -function runtoolkit:api/status -function runtoolkit:api/list -``` - -For console-friendly raw storage output: - -```mcfunction -function runtoolkit:api/dump_registry -``` - -### Manager enable / disable / reload - -```mcfunction -# Disable RTWrapper manager hooks data modify storage runtoolkit:api request set value {id:"rtwrapper"} function runtoolkit:api/disable -# Enable RTWrapper manager hooks data modify storage runtoolkit:api request set value {id:"rtwrapper"} function runtoolkit:api/enable -# Reload RTWrapper through the manager data modify storage runtoolkit:api request set value {id:"rtwrapper"} function runtoolkit:api/reload ``` -Bulk hooks: +Dependency helper: ```mcfunction -function runtoolkit:api/disable_all -function runtoolkit:api/enable_all -function runtoolkit:api/reload_all -``` - -### Dependency management - -Packs define their metadata in their register hook and can provide a `check_dependencies` hook. Dependency checks should call `runtoolkit:api/require` for each dependency: - -```mcfunction -data modify storage runtoolkit:api request set value {id:"required_pack_id"} +data modify storage runtoolkit:api request set value {id:"required_pack"} function runtoolkit:api/require ``` -`api/require` increments `#dependency_errors rtk.status` and appends to `storage runtoolkit:runtime missing_dependencies` when the dependency is not enabled. - -### How another Runtoolkit pack registers - -A Runtoolkit pack should add functions to the manager tags, for example: - -```json -{ - "values": [ - "runtoolkit:packs/my_pack/register" - ] -} -``` - -under: - -```text -data/runtoolkit/tags/function/register.json -``` - -Recommended pack hook layout: - -```text -data/runtoolkit/function/packs/my_pack/register.mcfunction -data/runtoolkit/function/packs/my_pack/load.mcfunction -data/runtoolkit/function/packs/my_pack/tick.mcfunction -data/runtoolkit/function/packs/my_pack/list.mcfunction -data/runtoolkit/function/packs/my_pack/enable.mcfunction -data/runtoolkit/function/packs/my_pack/disable.mcfunction -data/runtoolkit/function/packs/my_pack/reload.mcfunction -data/runtoolkit/function/packs/my_pack/check_dependencies.mcfunction -``` - -The pack should also add a child advancement under the shared namespace: - -```text -data/runtoolkit/advancement/packs/my_pack.json -``` - -Use `minecraft:tick` as the trigger: - -```json -{ - "parent": "runtoolkit:root", - "display": { - "icon": {"id": "minecraft:knowledge_book"}, - "title": {"text": "My Pack", "color": "green"}, - "description": {"text": "Loaded: My Pack"}, - "show_toast": false, - "announce_to_chat": false, - "hidden": false - }, - "criteria": { - "loaded": {"trigger": "minecraft:tick"} - } -} -``` - -Do not add revoke functions for this registry; the goal is a persistent loaded-pack marker. - -## Debug / silent - -```mcfunction -function rtwrapper:api/debug/listen -function rtwrapper:api/debug/on -function rtwrapper:api/silent/off -function rtwrapper:api/status -``` - -`silent` suppresses RTWrapper's own debug/status tellraw output. It intentionally does not permanently toggle vanilla gamerules. - -## Auto tick - -Auto tick is disabled by default. - -```mcfunction -function rtwrapper:api/autotick/on -function rtwrapper:api/autotick/off -``` - -Current behavior: autotick processes **one** action per tick by calling `core/run/run_next`. It does not recursively drain the whole queue. This avoids tick spikes and runaway recursion. - -Recommended autotick usage for multiple actions: - -```mcfunction -function rtwrapper:api/autotick/on - -data modify storage rtwrapper:api request set value {cmd:"say",params:{message:"first queued action"}} -function rtwrapper:api/enqueue - -data modify storage rtwrapper:api request set value {cmd:"say",params:{message:"second queued action"}} -function rtwrapper:api/enqueue -``` - -Each queued action will be processed on a later tick, one per tick. If you write a single `rtwrapper:api request` and do not enqueue it, autotick will still consume that one direct request on the next tick; however, multiple producers should use `api/enqueue` to avoid overwriting the shared request storage. - -Use `function rtwrapper:api/run` only when you want immediate full drain. +The Runtoolkit advancement registry uses `minecraft:tick` and is never revoked. -## Security note +## Removed UI systems -Every macro command runs with the permission/context of the function caller. Only write trusted strings into `rtwrapper:api request` or `rtwrapper:api params`. +RTWrapper no longer provides the experimental dialog UI, `rtwrapper.testMode`, or the `RTWrapper` trigger objective. Use command functions directly. diff --git a/scripts/validate_datapack.py b/scripts/validate_datapack.py index ce01276..1402f7f 100644 --- a/scripts/validate_datapack.py +++ b/scripts/validate_datapack.py @@ -13,6 +13,8 @@ PARAM_RE = re.compile(r'^[a-z][a-z0-9_]*$') GENERIC_RE = re.compile(r'\b(?:arg|p)\d+\b') ADV_REVOKE_RE = re.compile(r'(^|\s)advancement\s+revoke(\s|$)') +MISSING_STORAGE_MODIFY_PATH_RE = re.compile(r'(^|\s)data\s+modify\s+storage\s+\S+\s+set\s+value(\s|$)') +MISSING_STORAGE_TEST_PATH_RE = re.compile(r'(^|\s)data\s+storage\s+\S+\s+run(\s|$)') def fail(message: str) -> None: @@ -23,7 +25,7 @@ def fail(message: str) -> None: def load_json(path: Path): try: return json.loads(path.read_text(encoding='utf-8')) - except Exception as exc: # noqa: BLE001 - validator should print concise failures + except Exception as exc: fail(f'{path.relative_to(ROOT)} is not valid JSON: {exc}') @@ -39,7 +41,6 @@ def main() -> None: if not PACK.exists(): fail('datapack/RTWrapper-Datapack is missing') - # Validate every datapack JSON file early, including advancements and tags. for path in PACK.rglob('*.json'): load_json(path) @@ -67,15 +68,59 @@ def main() -> None: if expected_value not in values: fail(f'{tag_path.relative_to(ROOT)} missing {expected_value}') - for function in [ + required_functions = [ PACK / 'data' / 'runtoolkit' / 'function' / 'api' / 'enable.mcfunction', PACK / 'data' / 'runtoolkit' / 'function' / 'api' / 'disable.mcfunction', PACK / 'data' / 'runtoolkit' / 'function' / 'api' / 'reload.mcfunction', PACK / 'data' / 'runtoolkit' / 'function' / 'api' / 'list.mcfunction', PACK / 'data' / 'runtoolkit' / 'function' / 'manager' / 'require.mcfunction', - ]: + PACK / 'data' / 'runtoolkit' / 'function' / 'dpman.mcfunction', + PACK / 'data' / 'core' / 'function' / 'selector' / 'detect.mcfunction', + PACK / 'data' / 'rtwrapper' / 'function' / 'api' / 'run_many.mcfunction', + PACK / 'data' / 'rtwrapper' / 'function' / 'api' / 'enqueue_many.mcfunction', + PACK / 'data' / 'rtwrapper' / 'function' / 'condition' / 'check_current.mcfunction', + ] + for function in required_functions: if not function.exists(): - fail(f'missing Runtoolkit manager function {function.relative_to(ROOT)}') + fail(f'missing required manager/selector/batch function {function.relative_to(ROOT)}') + + core_load = (PACK / 'data' / 'rtwrapper' / 'function' / 'core' / 'load.mcfunction').read_text(encoding='utf-8') + if 'scoreboard objectives add rtw.temp dummy' not in core_load: + fail('rtwrapper:core/load must create rtw.temp') + if 'scoreboard objectives add RTWrapper trigger' in core_load: + fail('RTWrapper trigger objective must not be present after removing in-game trigger UI') + + core_tick = (PACK / 'data' / 'rtwrapper' / 'function' / 'core' / 'tick.mcfunction').read_text(encoding='utf-8') + if 'rtwrapper:trigger/' in core_tick or 'scores={RTWrapper=' in core_tick: + fail('rtwrapper:core/tick must not process removed RTWrapper trigger UI') + + for forbidden in ['dialog show', 'rtwrapper.testMode', 'trigger RTWrapper', 'RTWrapper trigger']: + for path in PACK.rglob('*.mcfunction'): + if forbidden in path.read_text(encoding='utf-8'): + fail(f'forbidden removed UI/testMode token {forbidden!r} in {path.relative_to(ROOT)}') + + selector_detect = (PACK / 'data' / 'core' / 'function' / 'selector' / 'detect.mcfunction').read_text(encoding='utf-8') + for forbidden_selector in ['"@p"', '"@r"']: + if forbidden_selector in selector_detect: + fail(f'core selector detect must not allow {forbidden_selector}') + for required_selector in ['input.value', 'stringlib:util/find', '@s', '@a[limit=1]', '@e[type=player,limit=1]']: + if required_selector not in selector_detect: + fail(f'core selector detect missing {required_selector}') + + rtw_register = (PACK / 'data' / 'runtoolkit' / 'function' / 'packs' / 'rtwrapper' / 'register.mcfunction').read_text(encoding='utf-8') + if 'dependencies:["stringlib"]' not in rtw_register: + fail('RTWrapper manager registration must declare StringLib dependency') + + rtw_check = (PACK / 'data' / 'runtoolkit' / 'function' / 'packs' / 'rtwrapper' / 'check_dependencies.mcfunction').read_text(encoding='utf-8') + if 'stringlib:util/find' not in rtw_check: + fail('RTWrapper dependency check must probe StringLib') + + api_run = (PACK / 'data' / 'rtwrapper' / 'function' / 'api' / 'run.mcfunction').read_text(encoding='utf-8') + if 'runSafeMode' not in api_run or 'rtwrapper:core/run/run_next' not in api_run: + fail('rtwrapper:api/run must support runSafeMode safe execution') + batch_run = (PACK / 'data' / 'rtwrapper' / 'function' / 'api' / 'run_many.mcfunction').read_text(encoding='utf-8') + if 'runSafeMode' not in batch_run or 'rtwrapper:api/enqueue_many' not in batch_run: + fail('rtwrapper:api/run_many must support batch runSafeMode') for advancement in [ PACK / 'data' / 'runtoolkit' / 'advancement' / 'root.json', @@ -134,7 +179,7 @@ def main() -> None: names = command_params[cmd] if 'scoreboard players set #pc rtw.status 0' not in text: fail(f'{wrapper.relative_to(ROOT)} does not compute parameter count') - for count, name in enumerate(names, start=1): + for _, name in enumerate(names, start=1): if f'current.params.{name}' not in text: fail(f'{wrapper.relative_to(ROOT)} does not detect parameter {name}') @@ -164,8 +209,12 @@ def main() -> None: fail(f'mcfunction lines must not start with / at {rel}:{i}') if ADV_REVOKE_RE.search(line): fail(f'advancement revoke is not allowed for loaded-pack registry at {rel}:{i}') + if MISSING_STORAGE_MODIFY_PATH_RE.search(line): + fail(f'missing storage path before set value at {rel}:{i}') + if MISSING_STORAGE_TEST_PATH_RE.search(line): + fail(f'missing storage path before run at {rel}:{i}') - print(f'[validateDatapack] OK: {len(expected)} wrappers, {variant_count} named variants, runtoolkit advancements use minecraft:tick, format=107') + print(f'[validateDatapack] OK: {len(expected)} wrappers, {variant_count} named variants, no dialog/testMode, format=107') if __name__ == '__main__': From b838b6aef78159807e300d244ac6e8616f844fe4 Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Wed, 1 Jul 2026 15:09:11 +0000 Subject: [PATCH 2/3] . --- .devcontainer/devcontainer.json | 3 ++- .devcontainer/setup.sh | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 .devcontainer/setup.sh diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 33f9f34..708663c 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -47,5 +47,6 @@ "mounts": [ "source=gradle-cache,target=/root/.gradle,type=volume" - ] + ], + "onCreateCommand": "setup.sh" } \ No newline at end of file diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh new file mode 100644 index 0000000..8fde66d --- /dev/null +++ b/.devcontainer/setup.sh @@ -0,0 +1,7 @@ +echo "Starting..." +cd /workspaces/RTWrapper +sudo apt update +sdk install java 21.0.2-tem +sdk use java 21.0.2-tem +chmod +x gradlew +./gradlew vscode \ No newline at end of file From fb53bcf755cbd6c29b6e90bda40b2edf63f3ab57 Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Wed, 1 Jul 2026 15:14:47 +0000 Subject: [PATCH 3/3] Update .gitignore --- .gitignore | 9 +++++++++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 48462 bytes 2 files changed, 9 insertions(+) create mode 100644 gradle/wrapper/gradle-wrapper.jar diff --git a/.gitignore b/.gitignore index 8a3727a..f88370b 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,12 @@ run-*/ # OS .DS_Store Thumbs.db + +# Minecraft / Datapack files +!Datapack/ +!datapack/RTWrapper-Datapack/ +!datapack/commands-26.2.json +!src/main/java/com/runtoolkit/rtwrapper/run/ +!src/main/resources/data/*/function/**/run/ +!scripts/run/ +!gradle/wrapper/gradle-wrapper.jar \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..b1b8ef56b44f16b14dc800fa8103a6d89abb526f GIT binary patch literal 48462 zcma&NV{|3jwk;gnwr$(CRk3Z`Sy9Ed?Nn^ruGlsztklcC=e7I2x9>aqJFB(1eyu-q z%|3b`eLzVT6buar3JMAc2#EOW{C^)LAZQ?YaW!FjX$1*JIcZUG1yyl%HEd!6f#E+}*Jo*NafvM<-FbE0;-_L#rp}qdn%JEoAVNlEB#J^Oq`mU_#*ev4HLmc> zjXz_hFft^><#omb;Zer-%wm4hxo!wjuX3hBldg(^-RiOleKin`>KHfL3P*{k?(rji(#j2Cc0K509#>qu=-T&B!-5EBi(+ zIuTD-qfcAYgS@`Fb2^-p)4#o6A3z0&fp?~cV=CRsAeCmO4ZQ5kKgC%0el=Q&Rhd#k zaGmAbUW8uKC}-C0s~2);d{;mpsNBx9rn__66W{AhaSvJEK+c0b6ARO+l(CI7E|S5x zhaYP--@F<|99X&)9`q^2(^-Zu^Tzfm)v|gkTJHQ!G*zIg5hzoygeXZoYUEJ;iFkE# zq^r$*c|>Hmn3GapzcDYnjgSFiO^NFyTR5AH#mh%zRToMpEi(r)1$5)h455DuV}0al z!*psWuL@Ke-2gvftfMEGf9YEi^<{B@qru zINgo+YsE&LN?)1qItJoNhISp-fZ86`XR#*6xcvM~_7=JHUX;K9*=Gu5X~ zix|O2d=&C#u_w{=B$eCpJ4L*6i7={j+{Og~`Emz@&98}6s<-p^)`0fXE4cJBP{>)Ltb>JwcqI>yz z0-r-SEhC@p)XOoh|1|XgjFaREHfsu4dAGVz*k#m+V<4 zHqvlud6=;#QWHUoTR_a8Y8+heN?M%n1@0YLiaN@GuOPNd26tik7eKulTx?mM-R!1H znB6+H{^krFXg_b{y=QeCT~qR3T4}l+b!Oz9;~|3*6F<3?#|DYYW&1RtFE)ILZ!`85 zVmvrZkLTzf31unH7Cc5E0iFShqlBE9hgEnRJH1juII*vyp&xd!g`q}X_6WT6E$hhQ`Vdp9k^<)VS?lj!cTh z7FQcQAVA@jL^cXod8cnhKG2TS9+;QU6Kq>}UOY3&TL9gXbl{Fv8@WsF=z7>X0To@$ zY@Oi1uc|MdJ$>Kn{@!g_e`-I&Tpwfg9cr>(iakDX1qciCG_1y!Di#4_)lE!bWJbrp z5aUonb6m-?tiQyR_`P#~SOu+tb_ev6JO>EbEhHK@KbeT0_FDo>dl9bMg)>xmCNB*g zG5NC8ABavuTEZVGW6jP*nAqRt3W?7Iigc-EE~zpNJXRAE z>`~RO9$892j&I1kV;9U)xT8^}IeV`n{}QDtj2o-RBt`DGZUOO;O*lFCb_vpyGh*;95PfeGu!dyrmZ9VJ3Z*upg z6R-3Lr%_55$Hw1^{+KWx0#z`T7O6sXo1h;m?B_ur`X2bFz-SzDrL zpk^@B<+I6imc@7vip za%1jMB7q@1j# zz{u?YojZMW{5j$@h=v4iu2mTu7IzI|)Sxn!74=*J>1a&?Xjt z2%JhSi#4huEcD9qdR9Lj4vwmfnL{%+vQ{f-KgYeqin(OPd8+(g*Uq#TLxQjD4 zLCL%ul(V&PAPlAx8D`@K8Rc`{GPecQ<)d=KWel0ejFeeXGQ6o7601B!!I@RY&eDriADD6wP6DcFKDLZ|lO#YwnrNCZ)zRJpdxX_nPZa4j#$j6v!h|6p!dH}MY6#B`@%6=) z-HigguDACKBULnon^FKzazF|Y1{t(U5rUGnEU|}djVsWT-F>@@mNx?_$kF51QF4C5 zStKR$^3(fw85(4HGs9{mUTtn1)3PwxTN?6}j;32&vJ^BiPHfndLkdU5sOemXKGyCZ z@<7j(k>DNeo~QXyJkFWk!7(y1SB%nA3{v~P2c8ooKa4auM!el!Q_=;lJ$c5ADqE+^ zX8*|A99v;jWPrm(8=h;2ZAj|(vVbx~wQ{N%v;eYLD_BB2LAEWCs@xauyBDl(_HIBvA(XJ7B1E;O zJYCJ8xFJh7f5sr;Y#Wp_`$4Z_H4e9bGiBp?Qu&2!@%Bl2dT5evfFO*^hLDiBu2%Jl z*WAlL5PaQ7skJa(qVysky}DQquZ8U?2@UyJ8zB#=U_E>MgE%XA$CtfL31m$rATJvC zs@!crc0=128PM=Zp zW_5Czv9))n_8Ru?{pxM2F8^r%*O41}RnONbSj*piG%`nyF>6ky=|;B&k8iot(J=kyoU3p<_zaAX(1ijzf*uXA zZ_5jeC{Lks+&QeFIlmzZi3+fsF4fNW^~kvC4Q*T-vrNP!x9xnen12lZQM=1_MdW76LKX(GuW`%T~dM^YX6+ras|Xy4Qhfcq=D+z-P-ea z`T;^gj3+grr3^hwqcNTJErl$z+k>{bYFm6QV%7Opth?9+>|Dn)O@`7F@=j-XSqGPW zjUAu%b3Er@;j1%RZxVDhI3sakg-gvTLOSV7;FV6ED=(5;UG??=WADZw^=$4AyFh#}VMe3afM^pF zFa}-nM8X=K?Jy02*o02@6k{ z%O!hBhjXlXKdhy3A{xGB<##e|j3^dFv~~%v2_H{t(mN7NVeS~51?D&Ozbxa`qwZ_4 z;C#Q#fL1sua%ggucgIEHZtcY=Ag&GgE|h7Q{77D!WUq`;SSGEE0pU;aoj<7-JCAvf zduN=(tx3Mb+EUXKoax|v;8b@#HJ&Q|!g4ryrl|R>WlAv?IH`bk)I24;eE4NIq@SLK31LD4+w~#3iN{=<`<1R!t^$@K5>U6%W=%8_ANuR5 zs(IDuI18ftirTDARnGmF%;iz+4{MlMihJw_l!0Y)NttXC_t+s)V<EY>=Xin*nGX79k6vQ?beRk zy_J>@YSC_gMIG$yjO-y&o>S6xtfT27aSs>e|`x(f2R1bM}*518~%x>1Yct=18b&Z>GiS*>VB$+i2876zL)1cT zN33g=g|>xWE2)dds5m2+8Vy)m-u@NHOlGYxxjam21r1;xWtT0TgqKZrl}*LSkqFt4 zNTI1=3o%C*!-i;iWnlca$stRdwITA1?#fD~5OIqIQAM18BwO_u>hqL&OAANiF|8rG z_IZ9mp?FA-{Gq9+Ky<#NgL1gWJixfO0ziP$4T4G>vsvqC-NQh+A64F4! z-(t<=AbPSG%`mTl6BJtH~3RmvPhQlE-EUkEoBIP(_WMN zK~Fe!siee{M*ns1hkp5(2}vX#%u+T!Abh=<_gEx_QW?h4V@B>uOCEetEe01tl)^`V z(=cOLmuOB;8&&m%_6pcyrt83UXkJ`f9I&0KxY09}RTTs!l^_7~8$tPA%Hm#&$k0;# zF;O0zCGo0IN)X~SyKDoY1DW{Ulce|V9w=ld;U`z$t$>8U!Gu8V?_LAJAudt3eI#*! z2i9~F=kP5m>!bmb%1e~b1!1gz01Py(Yw5gOsFN#o1a&d|=PpgN(#UVreY9^99I0iG zaYE@>(C^V7pnoB~#w$2C1_TIb1N5Je&iao?S2A*TF>@vpHg`31{uk<9{zf_}s&z%dL-Fo)C$yl$%pAdqU!HJgp zh_{m1imk{&{ScyeuziqZHu5cto0{S}^BlXu% z0~;>_yHGd#?Kt8ErxK)z6ojj5SacQobw)-8`c!$HOI*V6eyqou{1Upm%_p!BY^t(D zDtn(oQ!jff`ddGSD;P8Hes!v)OKW-*>mS&#i0ow87;h>(=Cu0>b4)|=EegbN5=Xkh z9Ge13=3z#sk+fT<)PuUUf_%Nx@l!P?t*mni^94p^Ax6b2SVL5U>9dHH!H4DL4}@?@ z?Gpq$C**OmWliYA{5s<|EZ@QI2{-K#brFxfA~AIqq&-WSALHWQ8}%mvaNFasrtnE{ zg=sB4-RF!?)nf{>Wo~kNFgYefoFHBcSr*;iF9B!R=5Np|jv>Uf+mcarG-XGy*kP{z zISVyoPcl_9cOg-@613Qx16OGF#sH&2NTHDa_}vyidmxS~pMfY#AeQvu?AXpWNzi7A z*6&7a7!C9HRU+N{>WYTh0GXoBnXw{lQby^XShgDOw@e8TP}9Y*oFV4MVF#@Ds2A+A zXBEt3a@-IIl)TOcXx;0P;|ihR%Tq@DXeG5p-O{!T7Sg$s1 z8OA4iOx-!>6eK^x{jU-0SvByimK|nZik5zKIvvWVGE)4=x^&5Nx%Qgje!k3VoizaB zip#?$u(R8u{wUFC>tVR8oA%7fs?xEu(gYn>y6BB%vwPR9&RoZE%%RK! zl#Qnkl^+Y*Y4L{Xk(YX&aGj|zSpqO_;C3CTepA!L#4EXO|(eA`Fi+2EQ3!C zo^SpVP?{chQ3uaxu7y>w213e22cdA#l-M2kStPE%sq6vE4M*?3At!S7tIp(tQg(Ml zECjeJw8)*#LYYk_+Txv3rxsH9jJZBRrHp29yJ(^;_PEdn%#U1q`r89}38;XeF{ee& zsZEsUbJ{LtwOjU{vjL(Wvs2!Bx;#^Mzld&TjS@oo3kk=0P36MC-Ie6eHNN&{8b^s z0@jcbdejrrj!>r#Wu=3H1dgjeOI}NkhmE}K+UK&M>%7b!n&{0Zixk%^)6#@=V~IZN zxG>9kl&STQth}qScidfg58d2dF|v_U<@+V^eE@$4x;7oS3)MvWusA?9+%rN>aY#eA_6 zic@S(@e9$9tQM-&-7>X8~#n{5G}nuOu=dSyN+b~jA;_SExZ1H9Q1A}}Rz;XtXUIOP0~ zZzS|~T+%de-nGI$s?wxaJoe+99vmo%xm8o8SNEsAqAE)4LNvHc-1AX24C4k4u3vZmov^_VcxgGxapV(8)_K(^8= z2d{xCrmk(x&514Ly?e{Mf6}h3=oeP7+ZE{%B^c-kK8g0W{tYw3q%zty_Rd@1nbnyHMwabNp-sSyzpV4v>QsnKcQjF67%g~n&3t^1MesVxCzfJ5b=SOI#YfPP^^JGQw=9L1RCMFbrU{8O0LWOUdBK#j&{`tzXX zpe2_{+-8$a+o#%8MUlL4$yK`*--z&3{@Y?jP!m{g5nM+Ht=bD3o}Ok~sBQ_!^!->! z?NDVtyLXzmGYCEmjSCDK*q?Aq1;8fz9l9|z@~l{)R6GfKELc^(nV+TjjI^n0M+S0i z@YOu*Tk>|M6a0_n$(E;#^1Zgif<-CpYiMvyT+Y*9Z?&~IKSwsLa5Q#p_?FqK3lKIw zlp6Hk%lio6)yq>m-`QT2Nj-q!aX7~Hlm^Xh6FNbw z$#ri(Kk*GUHXORu@`aYQU@ zB~S-oIO^~abRPocemkm!W73dbb!j^_xgo_@#W#6p12>w^{){VfeX?U71Xyn9&E zHa1#*!4c;?r}jv7dMN`g#&R_S215)dccDOJr=uz%LIz@zia+LIFjRakROr?P zQ|Xw0Pa8o7&W=fw17`+SqepsQ-Os5v3ncD5|N?N(AHH&`>hLY+CLOluJ z_ErpaT49zK(UcdNmQ%iA-`jS`A_1c|$W86{d_T_T2V-HH3xUqpX0QJSH%i>1i>#vK z&y{;5)^pMB=u;&_DEWakQU>j&+opIrBf~2GUh{`kG{|Z&2Z}5dwG}>Y{W_uQHaR$_ zYH%}$c`CGC-FGCetRdQ@RZ2-%ucC_|R?mHzYEnqC%u9zRBH8wx7po`=EVPMpq+hL2 zTdjVhQn$)++17^cn;<3=bxJy0Z$U;i3AqJMPJO&SuieU&0eVX?eLEEI7Av@#PV_ZQ zsa>I>B5HE996O$z6HyJfhEt^aC><@AnzeN`xs@lv>^pPFtcodrcGyqPSB?#C`Piu0 zh5=hAW|OtT9hs*G?7}@*mG_f7ae@-Nz4{qvne66kco^uD$(JbCo2ttqUm-SMy@kx% z!eDt?5>w5)M!E#C!b#Iu9GqyhUs|QoYWHtR{4espRS-LUt=viY2iygF=-j3kcU#uF z{ka2=zsOuLR}s;&PbbrB`zty&NfZpV*Y;~i*W$EH0JOGS&FMS%VK@)f*%OOrcU3P9 zq4zjhMpx}oc`PWtP!o5Bdlp=(A***TZwVwuZbuB1Pibv5uiHvW{PsE-k5IfCgUz~l z0nMeZU0R>(ajoQ0G%Il)z0BgRR*bsdz5NcqJ<)niF6|PUO0i}<4)q>6wx4K(5>Y_I z4$WMkbCOQFs(krBnl zx85i0*7%Zm(&nKNP?AQ}d~6@?D9dO%@}ouN2paSR;zyUqJuw)1SRy=g%o;g(BD|Bh ztnKV(4fcBgDJ~M@%}n-6ow3xOhnC>C^d?PbS(9=TnO)k5p+W;pu2F4eiG7ts zJVL4M(NiZPQDy*9`H>-P0GWY#=UTnh8feiNF}hCs`8^ZDKy;XIL^9K4Ps&y^#DQSE z-?J z@YOQ9NQi>ZP>^ix5K`R07kWj?`R(B?E*OyR1$Vd;8p%2Y2zEYt4CJM~gVX%MO(E1B zzXhsHn~R1ifq9~dtzuH!*3&W;r`D(Sjrc)m#EI%`Car;CMWcU0c+0r?O!)HpjEvyP zb^;pO-Bn6e-+>dS^o{q&8yEH9v}vuXX`W;NPRlwJdX|59`z?~z{pFE!^u{3k{KkJ55^ zD;F0ldy9W*`d5YP|0(E6|K%}9|D^SIq>wO)4^cJ+yCa&xl*3}hpvcQ1eP_k;@>tz= zOZnw)#fxHc81jPcTM#)jgy|0?n0(jd3IPu-lJ&Tm`#F1)o$GTwYp@dlqy-qiHFCHS zKgikMUx|%x=_%B)>n_y^+HvD2=nP`}-G_0A7)I$yc4`tXS-On8qOkNp>Q^$|Ew%Jm zYx34*(*Z3SF}xw$CA?nG9O3ZH7l)@Dp4EyH>8eXDb}AFz)k*T53iA~gRu&e15u@|% z9Rw?69nQOeJhv^^unjd-VGFwbDzf9K{i(U{xxHyM@-aI+0qP{TU0G~w+Fs>taL#Ik z4+92(Z7n%+okd478;__0GkE`&(C`k8h@?UNnM=F%A~2|TKo)q9F<5`s)KwxJRw~k; z4giS~|8AIVG;rde6I^W6m9fliR^7YT*>&x7wv^?xu(5p45n{|2F>x%?9Jq+~Tqo9# zChbeGm@9!(s;uIKae_4h@`~yIj`Tqct+-M>d>~2PCiQ?UmFUioyy&~h_DTBQ--W|q zqA^UaJMTz4tEggQ*_cQ_LA7j7bLyz8#cpGggy;YBVk!%oSdufoh5-FYAQ)v=d$Bi`G$^~ zm!O;En#M9uCykPzLZ5SHa%?hDHP5P;T4HN0L6J*r9DAvC1WWPOrd{*obfr3yJ?Kl3 z^_6dnXRoi4<$Tr!=4mhHg6ig~BatHR zv%ZMJr-`8w_JyFEzUSQdp0HT>|9QQG?IXj$7Rbx4E)%HauDyY!tedHP ztIbq;D)ckd-eirAHOG7icBH23*ApHA@nG*Jdh}~G?L5C^Xw^+nLWG+>hRi&(fnpY5 z?^hj4si6I{m1u^%i_yk$tco}28X8|}g5*tAEZYF37$f(+xT%XvO^`i^Ig}%cydrwF zlpL!xdO->&@q|8MiJrAxt;z2CP*a+EvV`_2& z<1=p{zjhmmYVkpx#RV=#zuy&7^2Trn=H$nT{OBVF*0z|QH!NxBF%gbqT!BEx zKB!SsSUwSo1Zr?kMM%N)@hG=&m`vRQ6QK6=oIvnUI+|C)dGKM@jNwqG2Xi8;YCUHYRh? zbl@DN-za)+0F9kw>Yv=ioL)01uFp7@AVEB0AH-nmB%j$RC_totFy4BKd;OPCMUMBb zu3oUUK`|{AvkM+@KPZD4Tn$(VlQi&aWV*Uf@DO|FQjLOoVw&C@z~Um*h%Ka-C=n4H z@(Lf&MDJXNS{3Hs@J)11(zo9tGp>wS^b9{Q1WN=Ktn>ZieRZS?k`gb7P4n?cl^7^* zG5-oARAG#i<*z`J0ski%;QCLD-T$AbOHq<{KxIb4=QJRn@MGj=ns0WhZX+uX z=oTjz`o-VviMt1mB0W1vA*7oq1ENz{<*-EU)U;r*ODfV!G-?hdnzhM@rRZ=|qaFTN zX*t~$gc-)M7GS{#34R-n`B)eAPfebN46~61R?j^(Pg3TXR1PyQrO7Mf@xf<3VL0`4 zh(i?-SktJu8Oj?KIy4p@%5ZH;P&p5LB8 z^}7P)9h}vUP+1Hd3nNzNcbR`%1>dSZbWhiXe-CcB+s9e)_w<{bypZ(@cQT`P@ch=d zSOPhExgI31MVFPsClEXe>$~qYQ+d}7(!BE*9y%AjQ47BMDt=#>`1ie)|ES{pFFdHa zI)CK`f3x>)DtZnm!f5=e@g;3iK^jf!RU6hpjYu^V#q0uWLuJ-6={Ua3gDi9#*P7;- z`rm*5)n{2QE{UZ01PVy@_9(amogzzOwYcVgp2>LsJ(}hKbX_!ayZ7=U{!p{BHussVj(W z2z3$zu7h$KK<%}P0YBJ+)0unV*xD&6GusXqs=M=Cl&fP@Ttzfq?>H9TW#qDId+C7? zhD;;HOxDJR4dc_xI7-b6N6nZ@bUWueDk<_9Rju2I*o(i)M0&~%C^ zc)a<25M<^NrsjAccydV2HJu_-1W>b;xrB~Mi@c7FrW-94$-GnKXvF7( zA68!d!gkIo8(URS{(u{zRtrF}B$9@*)KH9POqOW-B$za4Sg-A&PM*on$>$o#L7pH~ z&YW8oJX3T!!@2r4Rr6ac0ZDbtB1b5yc$5}7oZSDvGF0FWTpZ#r7@GfM^MmC-p{9Qj z_JmmlTxO(^(NHqBc$ECU$jQp^;)%xnyr$qvNTd`R@j$8JppDCGQAHQ7?fja9McCUZ^;``VW$1+G#=<;K{_OfH- z_$fp~S3K`;jPNNZnkB@=DFQy3{6+Bq9nOf3~dr4q8zD_t{P4-^%<4kj!U z0aj`=#@G*w?!4fpM? z8Pwb15(Ka*TtDN-2aWK>*hh{R_C}*e*vSTkHdM(ETM!JrJ=1h?(_WL}2p#QXjrKZ_ z0k_yu^;~)#*r>sQP7d_4VBRvWJCzw#TxA{*hktwQI3ST{8{>3$KHJIgMGK6I!d}Q zinmfq&RLRxX8P)_@@vVr0gPu7*)uU<%xS{|Eg;*w1}2=C&?7B zSX?OLt-gZO+<4@tLeF+K0~*|xwMD__KxWgGfsUpj)KyeCM3J-f*uxe|xk;Dlqq%1< zL(PaY@U(>Z#k!C!B45JlmE^~wHSH;r1c^kWTG9_VT~1LN6$a6Yg@kNF?&b0hs+5Dw=0j zR(wcEYmdfgojx+Hzu89*C}4$I7^?^vYKhF(`>=MC)VeeFR}}?j#XeLnp8OhW9%9ND zt6utD8DHnQj5@YJv+$USdN{8apQir2)Z{8_s!BABmG2O#pz5lSh|gf#CI8X4I|U4g zhQwk=VEV+j+-KNxuIk96Bi%^(Sf9}A7o$zHJ5mV~)qP))QQY&^>9}z9z9)PWpw>8T z7#NWNEtnUoUl{DP5(lmy<3;tpLJ3hG|;CGB`3**uH0tf9>;7w;Aq9SRVg1FDpI5y~rY#B|eCNpAXD z9692@_%$t2^nu&4lU~(~_iVf|Cs|mXs-xKlY$-~FZB$!oDK#)JgHZCG)ySDURM=@(i zCpd{Er89|l&)(&5>L6LuWY3yC6)`jPz(Po8pY=AYIBnx3y2Qx6*sT42mpR$zwx!!< zHHCc~tbF^-bje?bo#~Q59Dmw_-VcliCn^FfI*EV)U1NkNA`6Cm=^%j`%M?1Zxa=1U zn#DPNc32&XHHfUfmPx*J+3_GA&g-_pd#wO=Q^5bdhzmm)>s@yO0q|>ROV(hkhJWf@ zqWjI#+9Wx%C+!kp&kxX|XPS5m9CBC&3r>}SwdFd#YF_W78A*CN6mFC)qzOjM);Z&v z#MjdXXMw63v*tbvY+$tDmuHNFunOlRM#qe|eV&|$98!xy{n)-=N?lrkr0_}U^sz|x zs0y);(2Dooa;(9zHzRi=I{GSVcv!6jl%ck@)>JODfR? z%aI)0HvbhzY9K7eYsntq#JvWzj$WCuoyGoPY7;LSPfZlFiWU)X?(-p}s4FXQcpIp00;%Jv;k0t@2vBu4i;rh-?{z}cHTLL9Rz zT8r(1Ws*H~EyH+adP$cGv|7HkeS9p6eOEI*`idH3twkEJ*72|ey4JgISglGV0Vo@qe#)f-=|g%l$S&Onwl@mmdn|sjXXYaQ4MlfzjiK1* zY&hWQyc9?G2}2s1fYnQ}LXpq{!&Kr97d?=a?_xXAU0SXrZE?T+=9os2*v9%Csph*M zW{}m4+PIRmHEI;<=c5$PMrfg#MTs);4Tb_0**o}*cimSWRcxo(;G&&NV+-?W7v*%4ACG#t5J zQP=$g-(mN*;B6s)d9JNkF0#Zz_WA>J;{=2a!IJsiqCV!YLjJ(wUJ`3b$>qcZ!HjDT z2xm;fMSbtJ|3o~tc!jJ+U8a)vX@NcxU8y#u!Puq%R~{sps0msRFO2!GM4}786S7* zxgNmf{q@|Sdnf6_he>gEGX7Hn)uih5nL&&t4`O{?V;;bdl1U~9RAnjNmt~1UPC3mh zrR8ZtHzz1(yOYSK$OjKf;InJ+7mH$WfqI^OG3dhA+S!YmIgRv>2H78?<6A=~%E{ug^P+^b*+f=j32&Nv&Ypq?DcH&Busg^AUDE|p; z8(tQxZs1+0gUX<5~Ah zT0cGckI5%nM~d`uaMJ$o%2bt^##I0UdaQ2>-bpsP4P1Vk8r7EOSr+a!D*Z4shiKFL z35Lvs^i;#;G{%ksUUo8(Nj2DY?u5->J8kqS_#{B`HqS(UkzR|K5&6XI_#FH4?$ znMXeTb$nmr1`|{n*#5H1T%vtU4-H)vrtAchme!ZG#@c+Hrf4uxx$;VU(Dr~N-ich4 zMKpdwot^bPY#kBILFgi?i3W_kV%vn2J+%R5x}TL8I?B~o#VXlmr?i=y`yJi-><;X* zPCDrsU51x;mkr+t18lPs=6)r^gEh2$saaA!qv_< zKQP13J}ptHaUjT_(*x+P}wfV-}57aU3rp#3AB&~e3%y}0ju#22u5@mUIT!GA{* zd%-e2DTmr#$(P6^$&N0oCgR)F9IPR~!Q!x6YI*7dx6LR6n8tj(#1~!0rofeMtT#g* zW%-p@V09>&o>iz0j66K^soJWg(o9#T(8Xx-P3?;J|t~nIDSGPq(?-B zOoNnc5HZhsW(m6!J+yj~kjmjV6GKvhO>%^v5`O2I@4B$Z!~DgelYWdC4P>YfmI$TR zq`atDEhIt5ua)PS;Yz1`FX@3Na6j^uBx_rNKTmgboWGwE6O5;iQiN6Q8>ZX%ApVJS zTEf6oj=@?7klS(JaijG|(gO@dTgxB3#H)4&?+@VWkTc)dl;qK|uv;WRI*cG2`6PiF z4+svy+Bfn&Fs57Jz6i!C(w$w@VWPAbRGak~oN>3vUg|Mmk0NpfURt0*DSJ_e*Gi8I zqshW4F}L&aS8x~4*#{4vOc`gKW99cx*L^69fgPj#?++q9LidItd}<@&#E{ZGz7g|c zFX$uKJ;Qv^NpN*e&EL;l@1br8j8oxO3e`g<911L_jr~Xb0)t$x$A~dFay9(}gt4&L zyb=1<`|)_7(!^xJ14xLBGKXO3`R^_;F01 zG70TiF<5(=pRsJYj!^XjLl_vFJOQPhN#Pkr#G0-m#xG>q)GAHjE4WFhe7Zi83;gte zdDv6+)qrgh3F0}$gPmtb9-Ff1m|xDD$6jX)Dcd5Ms-(@nKM_3)2+hfh6@Cs@-=%Z_ zIinf|ck6rN{EOadGmJ-rzvxZnAL)(mf108HL2v&m)%=a*?3CnX2ZfOQY?ha_11m@UzRqlkhrVbQ@0M(tSSTerx}IH@Dn2={w$iGqU#`v}PuV7I&A9JYNP%sqMn z1bTq*Ok{V>SlVH8H*4X-lO?VzaDQzAaLvc1tTL+To)YOuj^V8mQ?)K-FT(s_!ds-O zeb$rKRR-~g^+_aiGtH6kbJ)!K^ie;ipJ8e;>iy2}73i(1RY-~!(tk2zPj;pwB4k1a zVa~7lF^EE`UH=#eb**88zBH%!WkO0S?_Zu0KpRtXN+XMsAwfT56IZI}&cs+R5N~p3 zlQH7o$(zsQQBPIRmD)i>TfdcgCSKbVVD;VCmO3l1VNbV&rWc9o>Pk>ex!)Nap%NtP z&kKIFMm@k9-HeXj2$((SmG+a-dXvl7q(7n=8)cELHf!@Le+X)=++(}pKC*dcns?>G zVa*fV{2FDIJNaK_jq)WE9MvxiTm6sI%YUn|S=oP0Z`vE#GMZa`4V5byxmv0@8@Zb~ zyBOJuTAG>Im^uIL@!ZrWJy6xL{%n;pEwY87Y^xYSfmmgRcgcEDfz4TJ#{;n|g>8(> zv$(RLnp4oD1Mj>H@ar|0RCy}E{GwvuKOf1FS}O&z-Q)MmCVEK{p~b2xFj@lTn}#s4xg7h+r;n$TZDlT2AXAv z7R^$J?R|*xL^>7HI}e>7{HszA#Y_e8=~8*3zy_J$ejuhByeI0I!w-&%MW7Q-FGMKU z8qPm&IdU3w#^#`d%Vcn&q^w;EEr|w2F@ax^`R;a@p>l`U-T%~f&^`#zG}qdSV)A<0 z^*U=#=#o&gd{o+*s#j$xf+2y^t1Wj9_h}(DNi^aK#jI}z)v1rk-H)gocbgc`wB*?$ zfg~22r!^VEN+n>U8|3{Ebe#!9k|dF8lV*9c&9H~&g|$Ymc-2O^j9w$Q^I)ldd}5zv zQkBFDS2TxDn`p}-{-`br?tUCgyfr0Wbf3QeATbp=9sN|e90U^eVOu0~VT$1A5))@C zPcwzUn7bP^Gd~hLA@8EwiklMmlc^(;uPE%tLecC-iZ$_~jNJnZYn1A%r}=VE(-LG; znh6Q+b;zKz_N7)0SH7t~u#)e>Pr194w7xp;V&CpmJw5j6zBO%yB zjVf*iveYaWlrE~+p8YYym=-QmTd_F!`)ATishn6(oD}hTE2AqnVPF_os`ca^ET@@Z zoo~4YJASOBn<;8#(#3G>n1E)&@JA^3LV7mK^kaJ$((~ASWup3G(%#8O%xFX8XSiN~ zUF0&gDyT`FzIjtA`<-+9RXEKbwu%RtcrG!#-aoN0aj)i z(G|=#b_!z{o1}cIyw#n=j~Ac|NnR@<-CW$c%JFBFTi5JW0BX#4k2o2w{L0EglSN7E zFUcmFVF&U6NBA7!t`Lut>faDk>pW>Lz9BSzsqWvnI<+L#wg=zw+aeL6=70S773#Rq zG@fVM9=1ZibB`>L>hKz>rHG}`pX;dZD>I!_x~u>jsx3;0d$`Q%t7d<8^lkl8w0WZ3 z(HGiok6h^#G2EzIH}G*;!U8FW>@|C+wE+z{@e{wwWEkzUEiT0aDJo2JwZR{zcX$Bz ze2pzE&vKCc6@vE*GIv1LZ=qSg~HR)Jf|ljt#^m2hZF4z|32*7{hd|u`C7{C zjG>}`{SC3Dnc~5%D4yBa!V@}xSBtQ$ZWY^qs3)9jTuIXYMgPF5E0*&A0B(=JEntcVgC%ZO4UKHyuzuSblKNHWJ}OzVpeS z?8|{P8FtkJ=~%YMf1h*@o-YsZkLVQU!43cY~nWEmBt#&Ar%7WClZK8 zSe-!M)B8((tj^wSIm3?e5oe&mQs6BAE#Y7K*^boU^Z#aITL%-H zul5Gx*FKM}n~RnE*Ko3}nXrk8nTw0Ok-d?{|KMda<$n9cFHzkfb4wa&Dp0x>XjayP zg-KZ^Ayey*gb`NecHls@$a-2|Z!Xe^@P`uYYo`Q*jKzDQGPFf^GDQ5rd(-X3n)&f|bD>?`-DktKL<0hWK!cPS>L^@|VH6## zG*0#NtGfzpZpt+e{yL@K$|Lg*JfO%I+hp&kR;NxOJ+y2H49xZA7=^RKObPZi6 zL&R70!l_{PTFcxI#h+WsO^Y<`hE*z1vg9n7nG-6n0xBU8F8yDd}=?${Kl$qim3(S98@^W*vvSs{l zU}!oUIXap-i#nT`er(?avm4Q4-snuM&-cwu#-M{K8n;l1gP$ z3sw?`ls1z%eb%&mNBvLuEci8}-Q`|kUw6;F0-pHb?+A)+BLSn7_@my}6u%J=Ub~(* zU1n~wcfO|73IBZF;|Bhy$0FeO^>lmmZz?ZuZC8$p6<>B{Lsp-*mS05IVU00ergKWv z(LIsLS=?(>QLLQQ?bdTpyO?iiEL`;>(XJw^lA*7FCd|$g@c3VRy#tUf-Lfs*_HNs@ zZQC|>+qT`k+qP}nwz1o`ZNC1_y*J{2=fCentcZ%LwW?M`<;L&dcdwa@4GT@LCkltq=Xfy+OasOLT!lXrqy` zEW9YuDcfQtJ$oJ|Ln|b|q*_a|YPgCbBBfQ|5;-1(P3R`sK~3T`TtVV6yrtDbioJKI zPDV1BAaj#O~V^ll>$# zNC?nv_r5RiH^A2t<)qzcvns9Qd$_UU$`jN;KUSNqMCQiCFCi3A$*D#(v=FXCqz$SB zyC8vjHyJhMy$5kCi}FBy0NdSCJa6{q(|*9I^zwX1NHX*dHOIDB8bsI3_{(*-kkQV@ng|lWd*nWx!(xQ1stGMcRDjH=YUQvY2^uCZuO%-0Jw5az*F1nW_|h zR~z5DT4j&Z7527|#z9b}pmRW}p^|OrU(TWox^&Kn>YUn%%JlZJ^16vzy|O|GnZsf3 zSXEMjOhuYZlh*ikE0&zHt5va@6&GI{1&D+NPop@Tss&f!V4;}nqX@iOvdonoDa}J_ zE-u%qrrUpYVYSGU5NeXJr?#B#3dkObD8uk*U|u*zS;T2YgAk;_kdF0s4A6A*YGO4)#dKwYLQi+*i=C3N85d93 zAe#Lng7EX?@}-FPvIdp0y!`J@^1tg|IHwZ=C-i6LW7u!d>#==7<(?=6?caFCo;)AM zwwV6XHIU7}%D3 z75#&7SiVq=f6k4N*gy{?o~K9`+fsId8Co*62ksPHLm=SB>G)@44I(Fbs1stfE==|e z5WM)k7Hs~OwT#*$%<~0|BEb_6HV0F0=kYy;P zdAZbN(@{*9FL}4bSi-&#J^2;N`G{J?KFD@i^8BEXQq3$Q#~shvw_cx5r%ZlgHz2&Y z*cU<9UD1(G6qg=Yx{LRix``xh^Yi7@j|r7hm00t{(0ei78ZQbt`JV={$XlXvX91YH zxbI<;-YQG@9xrY>Ar~yWklR>hQ-X6TUxD-S!;~b9lu;Tu@f59S=euifnkTO2C*G;S z@TJZ5{$VG<^ThBbq_74=9q9r7DxC6VBngr@olJ}~W87-NEagn(;M*)7Oj2!(TG+}U zsLu!TV4B7DH{}gtanAHawLkpH5_$jk$0~;0`rM1Hjkl;4D-KsjXTl<*z|E`_8Nlb6 zroi&vNu(socja8wZ}9J>;D}esqgs4BR?_u7ZyELz2k%GQjtG%Vx+yeS&QI*AK1Q~e z;1-8)WjT?WqB>et(n%42u5UPI+!F^B7Hx#oW{i;??}{9#vpvk}lwvHPB$=-+pnIAL zGBd3sTO%TRGFw?`Nh>DzU#VeO7C?`w!-QT4ZgBE!WsS1clJ&i=m$ zHn^;?BNx^_wESMCsSKfxi542WFvUJUh%GpT-JP-b+D|wh`H$h4?*AT6uKyK)=>%&^oOXr5Al10+ld z9x<66pEk?hlV|$s!otJ~_Kz3DcB~XFzWq<@HMwvNFc2}VQuS$6g{U$+nN4G0`E zua0)-H1D8k;mm6E{(!pNomCz*qxv$pI3NvG>(+Q4AcJvK#K8 zb9SOKS@GC!pN|JW#<}*37GFj>D1wi~_)k#-N5izNy0%(q7hMm?oL_Ju8jMFGA9bKb zv$!gbC9lC0>Unx?+*3GF(6ZZH<(4j|5-Om02Y2z2IG_&xn+2Z`6;N1An(~^lQwwUQ zOiKj)?fuj7EGlb8nv@wDs4us&o=Bt%l*TAhB{h=R+Pddpm83-ms{V0T&ofYt=D7dS=Kr=V{~wzR|1=j_+3Fh+3mcp0J6k#Z&$+yVt*OJ$s$BYK zRx!5u|IH#%N;9@dV#r@$o(;Dy3GBon{2-)SK+R!>`0yL(nq~lFeelQy_)_BZt2i}m z8rSXb0|MpaMQpG<_IaUCD@=+=`KtLmC}H1)-vV;8Y!fw&`K2B6oou$QOj%XL`Ye$dX*5~GV? zjoCc8{4m*B_lFn=K@#mp@(*Vga>;sjA3Ds|(a_aGGbuFi)9-z>)&hY^h=PM>jvvAt z$Q7Zfbr%lPeu2OFHW3uNyavs`ezAXnB`OuCGx+U1e%!gwF?S3T3XLaG+BzOfiLB-f zLsTI!R2nT{#3)Z+EHpqiKXE$CK-~2S!*Tvgi)l{*o7SZiuHQf&N=jK$gt6|+nF)`Gm z!Txq?dNfctW^}=z-436nDud8w974=Iuf~cqED93ykXqf1w8FZK9fiO>iyHhGH6`Xa zy99CYP)x3@)FSqPdVt-Br1$H%x6;EwpuBzZ?#_D^RUI0KPMzf^_Q2rPhK)0jFB8Xm zlV*;2seylEHqM|s4!E5>k-zx$17R0R2*LcwM(ea^%K>Rf92id$mc6SChy+Lhh?+zh zvO6({dx7GOFjsuW1#TIks9C3Y1NS^K;IL#Bmt5WRAnNcc>QhlO{Vj2vmon)s*asQd z33&IEDekAAXHibwHHW4Kjin6FB;UgbL))#+*%fRgjq!Uy)J$xt^A4P* z=wpGU$DPMXW)DL%DW!nu39E+G5tKB@YM$r#?rOf~PwEaIWOZ?-rZteokPGZsqWYS4;B z|0LjjIbp)2Q9#;HApIi0rAAv&MKYgXU3KhsoOYe|YT)zr{({<}EXL67@nFgE$g8n) zlwsHK7H3m?1l)9j7MVEeKIFU&$Urel=||l_I+%2%vpEWGJ4%Ae=4~9emV-GN((dey zu%{X&7)-JZ@$2L0Yqtni7;-H%fWs%8= z=kT2S6oOA<-_q!hTShh=6tYB`my{cf^+Lx>yzS~3hAy^=8Fn4^M9*a;F$7-pPb`5WTTi>BH<(hQt<2d>L}bEO@qeR~R5CV6M#}U~hOs$t?sI z7o&N-naKA!$TJ z>&^XTo(>zGjv|b*XTI$ut5?7&&KtRH*Xif1`>gBEp7*Joo(B{{&6%EYr?;2euFLC6 zyxINGDCvA&Z9Ke6+p?I9Q!BMcUI`b0h}(?yqWH@VsM zQOR!?^5j*fLK3_B=$34i3+r{u7IgD)M~W2q7y3L-307k;BupXtBuqlRxD3=-rhwa9 z?bS^@iS*Hnd^;p2cOp}nC~VDSN?;3$3z!yI^$)`1W?UAhtCjjqn>M&ph0;8EaiL{z zu|C4KQm1Ko&6~iXk*x&^ph_a+*qDsevtmcT;T0k>1Tvc@2_|YU#phijBjGm~(FAS> zlUlF>J!lV+cX^mbgNt|q+%c)}o#I2L8tL)BII4PpHABevx1oqq4Fk=enLf)lPJppehzt;iO9UQ2qK{ycJZ}25$Em8#QCj@IGeY)Ih;t1C_j5#Indn9> z?q%Mr*&t<`FGYDnXUw!Q9F(&(vc=j2NyA|}`{O%(aBk4&ic|F*CyG^zcJTh7Jbkku znj-MdZ0aPz3?=kXncCW=-<;dP;J9T1y-C;{aJj^)J(P2N6H-0wO?ZvS=U!GHKVCK< z=aWv?u%5>H&8MwXa49`eLmGW<%;nt}*#2=)K*`axE(dLvH|fGa6F34#8tRY?cr_y0 ze3Ys0rp;JgADiP65s|!r+v;Bhhv}`Vm{n>M24Hc%zOJ&UhG2A;(vSJbsM4>fU{u2_ z-6VIhEcV`qxROML_k8tmxBr)-{ z0Nki4Ka!>@`U^UZ)eJ*+dVEKh%hU52puWKbEG44AD>zWsBPQobQCa)OTlz41wS`U5 zA(_e!#MIkQ_D?<^L@2G~TpSiQGc{2i*D?M}9=ed6<%52)rPN_&_Zz}kJyQ*xrss+n z+*}R)Uzw_8MN}8>Nin$jkrHrz;R3n*HT*JD&M9fIRS?wRHq#A#i(f4q5+z;_5Ij)k z55fi>(u^$A=GCiS!o_k6hWVWf;@9>(C^LB-^lw%JYn+7v`}UC04jw=#dbI?>PxGb< z^hYM;a|^$Xv8HwRyEFBlC0EGDeVFD zsI=F15ChE=aHP6tL~Ao9#WHh`H@ZcicgWiJi5Wg12JkaFg6%fLuw^#2^+FGSBYJC) zcLQaBfXhJJeIf<*h>U>kVP9*cRCfKc<$@qO~wd*)<>-)SK6P zJ@I^4#us1Hf$yt#&=?VaIkhDY^^W;!&OFd#L5S3wEK(42b#OVRSI3Yn=DLC>djb3m zOx*FMX7ymI4;B56>=L7Cv?Opmx_j#kUAIX{b-S2c8Z$v=gOMvo?-ij^Qg7+-IsiMdRFM)v7G{O9O zb{zD!lmDA*H)}70ZFQ4xTkLM$F*jknM@CK!9fA;1rEyA1T;kT|rRhl7MQ@3Z8K3<$ zthbXo^c6w1sy3usEhrD|+wtJ{DqW>!SzzMAYG&n5P_48!FI7^!mt^UsJ=Ii%VFz|f zC`{_0n8zVxPB%8P&U9wpG3=awF3lq(pY)ZY+X0iPX>u?nXvOVKqHlZ!kPr!p?==9sB_~DS`Wz) z-C{l?ZU7>v`xhem*b=STWhZXwe7a@WUN>CeYu(sj2^yMe+X__p(O0XKfx z%AXEQxVFsfTzy)ozm#eCQhr*;4iF$jVCn@40VgXeH%1E z29UQ3y$aVZ3TOp-E~*g`Gz^slv`Lf|RO$MFBa@P)tKRuI=cc?XxIqzmXgmw~OWv_3 z79M~sk*g{jtNxD4ShkFGO@d3`N{)-(L`+B$P3o{T)|L%BE`c71nj=koezdtBY4~a%t^5r3-m!3Kj%V`9dB?v%w?BxOI$&~!jUNWa z@o8Q~I6n%f3*aDLLYK<|4FU2X@*``7jnlDRq5+VebLwb4vJVL_1XDYFTUc;$dW3relP0}p?81NZ&{!uRJU{&9)O%uEL4Mkts~ z&T=;)Kjl_c^Tc3YX*8y9Lb`*cpyU^wFHkn{Z--k1SA~|n0bO2_YwyEVv91paW(>>D z5A?fn$`0!!94mEWTUFmE5+yocu&wZDj;aE3+jOFJ95*T%`pKWaqKNiaixt!T^#`@p zHlA$6Fj^5&7!Hb19 zHyE9zQWe<12XmH)8IDIOtwPeM zHRd&LKn-qMRQRtyy5LYzR9#*8JDBD2K-E^^INa=#S{XA+rW5XKtg>7Nn^Of&Vhir! z+P>KycTUF|e~Hw_vAX%ap<+u9o9)jcAVaw~|4zkmS zZa8>nl~i|D8zjQ^%<{;ZR6cbVD>%?nlBzUD&(9h}VOpBkVW!AuVW!MGuz;OfTWE_| z{yi!0mE#74$DH%4$iv357s-5PS(g3aXJUS?=I-+Jz4Y{Czu2{VMepL1!wV0l8b0k) zSH~&|HJ~YYm{WKY&gKO*WNzB=l|JE3C?T`VIh$Fi$wHFx68QWYRy%ziF%z4Zc<{>B zjkGSyv*i{+F*O@tKQ!EDM%7xw!z{Yx)~Woo$kr{Z7+t7ve;X$MoE{R-LVe22TZY;% zOIFYRqSw}4;Mcno^z?O*G8Q`&wbgNV%>E*DX{fnqK*lP#K0dvcU3endLW%GugLOH< z>Y{oG#ECe$UPvO#$t@?@GA5JFE*6oY@?+$jRxnx(BiZ8q{AuRkwymR+;{*D6-bh*) z-5@PC8lo`?K**Ec9*n$U>OJRjK0H$J@vnMoQZa4ti zMegzJ2oft=1Y+aEG$4JE9{t_I{tH*SwKVixk$IyL|hvQq*qu&_4C6X zp>36)v+qAXl|OfXL8koN-RrhNjjA36)N;pjmTkOO>jg}c>35j<2gH)fb7QYv#8VV2-AXJ1-O{Vpi$uIz3lMp3dl`?Wwpp>|6_$}|ROmbQ- z+O3VID2pdMNR%dc(_#%+-P-%bNIb5Irk&d>rOY(_mq8%P;dkWuH0mR4vhl=r?rV5g z%=n2Yz2%@f5#I6!(KxF>D%1-3IyJU|VW-!(l$}cWBQtobb>#9D+>HlD>@kp+qgiCj zU_Y+2nP+9m^gw~vIRygs?R~aXBZ*Vk8cFZj_&b8(pTaY{Y}cTT z*fRuKeL3=89rk16#2TNQ%KL}Ryx)%5M0MHy=A(uL9M*f_;^wBL-FO~J+@|(7I)GQF zGxu8y$fzRDE)xoI0MCR3S^FKd3Mzir$&35HZu)9V$~5*Kk^r{%vt!7ISD#%fswRS1 z7x8ugQ&u(usOPXbN5Z5URhEFc|NLc;g}f4JzVjlUxu&$T#yH-Omy4s=$~b=B<)v}= z;R7RHY}oe#TExRVjM2_)jF*Q3%G{)3ZZqgSTa^}wnjk_InITrx)tW> zN_A5pLZ9CogVv`5^1_9Jm_n4I&Od-1kC6YSPp-Oxyt0!D zIplg&zC_?4NKvoQui_?BUY3EYOP5n0W0#hYf21a%4Fg1xeEs;w-CE2d_X6pd9A`2e zuiIRY)}Lqe0J(eXdpq{`UG}5w@h=I2qwDlnybY&n3-F)3(mWK*z~Y1=sqQ352UCF4 zQlI=T^y5Lp>gG~>1T94`()}Z4=w<|*zIWTL=+#(!PT$k6nPOoI-RVk#s?iWB=$tTc z;v`#9_oLoCy7W1j8Mn^hfr?}kDKcERb3jxH4>hafqve(?N%m6{o48;*Aj`VQb5)Ul zHK-31_Fm*+OH8EXSzh8{$7fljqN=ahTv<75(Rp-SR$Zz#EMGFOcXfT5%J^HHx8x@r zP2)nIWHes~>%OVy%4>O3(0{X?N*ukyQv5>kKb>M|32-D&p%1(V8j7s?3w|Lp63nOV z937ts^a~AioVI92W$?353}~XMK~{A}5JkKH5b=n9Ciq@IDBAB;Z!IUAV+ciiDvH*j zMD^3Dk+a${QM5$azio{#f^OHOx>LnJ+5kbRm4^N`5ii4(4>XD|b?3s1jrWv1Z}MFy zT9v+!?Ds9SiLUpcRnr?JG+C=^SKkC=BwXt~F8Tyir)=)czcAl$Z)2R5pR!H;e=OVl z8*}D=$~ONscK(|=^G~^sSitaqkw<2U?vov$hY7)fa=I8~62|7IuK10w(qZq9BnSjK zt$S9yI^QU{77(-&cteiu27n8-8*tNC&-dMPS#upD2hi$Q=J$O0#Os?xwTN{WtSzZC zp0+5nsTrDO-C3RykP7Y)6z8U{uiQ@973Pg|STBrbPO4R4VU>jA3ZJD%OK)mD`u%Bq zjUA|-$B9L(11X}nY*naJ%@8ESe`WsFWU8vR= z2;2}9@)$?_zbc_riw26%Kg!e8Kd<=z-OEDxpIr0*^LqcyFQ+uzy_6rD_)MF*+Au)L zK+sV!gc8RX!}1A93BeHY86igj>{s@tCS@2Inb@Wg|3Ir$G(TxPHZ`*>y-_zsskEEv zlcqu`YL%;Yn6XuOyEIg6vQ;HLymz>grb&Gw(*q#A5?6USh=@|D2=%(`I*cmsk7f^9^}}P? z?OW5EW$5ivagZURMyiQ!)dSTd0?Cq6Pu{r&OKRfiuu+&nj(M|bhppFk4ze_}sSz1;);PvKNiaE=q^G|5w^Vy2SN zBs0Xts91C^d0dq<=JmXesd8D;1K5UvF9?WTYl6d%lJqXxN`Pj}5LxPgSRE$%)Se9Nn;^;MLmXCiH$)23AiNRlj3 zB5S`@U11=y{xj(rqgS3zSUD^dhUILAwb|IZt>UN#gv=Rm63ig{MK*6HQPQQC{?1ODO*flB7}Q(AO3hFI}(g&O+0tS_v* zssss=fjAF6c7M%h{bJFcbm>-<=R>Xa4X{qGb3|a97zk+R8pO+p(k2^QM<;%(sz0y~ zRB?%#!Lct8vXEtAzqvF2#xo$NsieLB9TCSs^E_?X{@2BD7<@uv#vvJzQhJD^v3!dT zl|$vIA|g+p5nMz|Au5{UAyp|$2kfI)S~hhN0%yOnr(#(o-&bKg$Y+VeF{*sx3Du~N znZWwrE{QHx{GA?2J*uLTQ+AKA)Nbt+N2AXvftlF`pev3SOJ$4`MSDf=HiGkA5i0UO zd~$T7PLbVXMt2^U57wmD5}@X1U>&QO#B&jZ0J18_+exP+Z@5Me9xd0Jbq&L^e7(>X zNNZ(5fx4(0i?cEE=!j+2!b@EfJXIo&j};GwfS*019h#N=Yt|*|0J4`!D5 zN_q7;3^d-)FNmK&7&H^rwGK+yh}q{Hpt?|PFC?Fm#mlG5xknmlrQ>IgB05c3KF~=a zh6K*nAvP~CiOXlXY$wlxYQ8_)WN;>NeiQS5Mb-&Nuox?GER-8$-`li(QhmzUy}Keq zW@+_RPM`C|bx|r{2{VLpv4kQKehI>QOprT%3zknCxVb_F`5u!3W#trOn>06Z6D*XH z=M)M2!jWK4RGLfuttE%E2P@F6hVZljI&jmjn43^ zPJ~{D)br75_H1XB8(ej-Emk3-$#Qk8x9>hEB<9vjxJQ=EG&)&*v=3TD&pvVnxeR-) z?Lb+YlOky39f%jYERz8;%h7@zQH?O%8>!r^nUZ(>IPqq+lbCHA8Ax24#IZ@dwzGe_ zNr{+ocSoD-L2*Xdg%@t^OiJbgq#@1W&4(>T_SLJKpM5HrJSQaRRfbG&uyI9+T~>My zyWR{C12~~%bhg$$vJk%xRx<*^v~v)B^3%hV33i~-tUvA5Sfb|5i=rmc9n>)2!GqKa z^P&<_F>DtK$|77CJ5xuKX-Q%!OtxP3n%EsDQrn82M%6F*?l55XtzSVcMPQG0ZuQjl zmq*Ic&aackwk$S6PqbQ!TT;VJDSX~x&h0RoXfrD8&a{@qUZfVn6$ilU9V(GVzCpk^ zP$Zf;Ui%dnVGK2;ueF6kZ zFhW{mY7j^Tftei%owFtP`AO&4M?tOT( z;Htw$hS6rDA9#f<0l{2DA~U)NOfScqg!^m^q#5Caibizsnh)JfGIIAiSiC=S%J|_X-AWeS|ich7A5v3!>zaS0qG@+}6 zF+61ADkXR}zFbZ1mX?PdOp=@C9DI^|;2Tz^0qedK3>_4z?WYMY85qL(rt=Zq14q`G zmX)L~hGa0K_F1zeK5O`YjYkt&x-#C=rX%}-v%xC}Z95zssU#Mk{YR8Je z@U4Wha=tl!xo6aPg=VsfWT-Uw*s!bATd!Jrcam6JES#?b>09?3j3HtW9zjdZo{@vm z;Qsw!K~TU*LK!uvRJbS;OkNH2Wt%Y^x3I4&v!zodO!!r6#`%hm7yl~tBXG|sE%(t= zztYj^vC$ivB^+7S$l7s@do8-L_omu&g;hi4Q7^#p%DB);DAqKLC_yf{M--fbVCW4Q zpLSAJpyR=Jw|FpZ7!OY9&`o&H;FE5C-006%H7z?V^+c?EUl19l4m+%pxM%W-d$e~- zt(|&Ex@CFK^ihfbnmM|@OUuO+x=YOaa6Up`MZSv=z+ zj&v;Xfs>|(JoZyyf*n#2H&qEvkEBqz1th01TIY?cy1siJEZd%upf04|88q_e^UcqIJI$qO^tX{0Q=;ytn*d0;d>W zpbMg2hvsXQ_P18QOkwPq?4dM+V|(uRBPZ<<$bpw08v0vS$9$VUpbm=Fv(IMqMe~ij zM>0rOq>iZMoC}d%y?jB;97(AMLyv&6Zzi(5LIvB?<#Ywf0)mZ_~Rdangdl z&@8jcCHuwoEo63_;{rqY2HFx=n@YZylX9a} zl&P9Yv{)Lgc|b3Q1o2l|SANshLidoYfmF5?I`bsF`E$9kGP};}K?$qva#L^~CH` z!TFGfb4WF(Bq_ENC#V_OREgx>tR!Qa(Jg2?b%7g;M5AE-&>&(JHfZkcmN2s4eJeN!nCrcl9Way`gTk=o|nGo|BD1pGHLvB0ih$H-WM^@K##RBrgEQ`4$CSNzg z8QjInTy|bpvXE2PqeM9*$mGvZ!Ps7Fn?$@*V_0OIlsGq$7xq#m0A&oC)8WX5OB{I{& z&m4D92ULj=J&5P>4A>lRn(KPS@|aiq-&TfHnOC`uYpkgbZ!za!sgrKX&HmC&DR$Qw znLUwmqe#(ab!;OBsne)NG--Cm>qV#<+25uf(vCyt?AGIMoJse#4t}n3bFn42(girok)X zsLlF0m3f3uPV@^VjN3J zs7vW$dREOUH=t;vnxK-_6qp*ejG&zM*m*>v9wu&xniWe@+eJ-67VZtoVET-b0X5{6 zr(c*Y=7z@KB`=B#zMR8)M_(&sn@t?LtNkyD`lrk0nJapT+`Ued`PVEyOY{v7f2Alh zxP{mY>C3kmqt~@Sx9=weAH3PUD&9e;-4Z?DM%u2JrA~7?nOo3Fg!@?ilHRb~Q9Vh0 zS~k)vttP$Xy9A>{?$-j{oKIM^!~^qOk9nFfO9U;uX<{Z}MGPU&T0}pPw4d7EHF*^c z(1Qo888T#p5hW(|Q-(yg#r6vVzhg0gpd>56bb9oH0wu}%3M)p2fxFLEy>QG4R_-h8 zU+Al?!eBv?3%sHzLA?4>j0E@%7$S|RYf_S$ylY+ z4n%*ot_mG#p83HvVERPUjJRH!Ay-9T%yQe2biJr+b%|?XeE(`??bZyWEqp{h5`F<$ z|26&q>X&o$0crC>TI-zNN~}*w7-kFnefLs z2fQs{{%-wM-9ryBgJ*Iuv&{5yuKy+Eoc^si>??Jju|gyAn_Uf`ajXB1%g`EBtwiQ1 zx^awk%lc*V?-yf2mx&<2oHk?3d{TaxpMu&Sc>d+t2h>+*DNg;iw%P+Pbq56MHt1{8 zuC!j;1YlpBL2hXi-rks7|L=db0Mz7?nWiEF08stMZRP$Sn6!kAqm#as74d%`|J5u1 zZ`hY{-1iNNl z1=2bj@r1^~3~TeQTAAId%fY2ha|!FRU6VMpiAkkk@VViqVwhBxz8SBI0v70InyyD6 z3Bn|Jj3nVomoatTh{xa7jx;yvi_UnW_#l*M<|9E)rOc4j#iVycL>cKHTtp3#k-nKL z+7?|mS#aSINetxl?nE8)%Zyk>!C1k`<{`huyPwZD2`YbK4!99|Okznl56^r1}88nU&cpyn*~f zRP2FGaX0@#FpvKuii!WfqnQ6~#DBA2l_uoxjK6W&?wmdns)%IKg2?m;9KE4d3H+J4 z{P-@21_oU4WQ76zv4`7rf2c8VBqkLlTWX8sn;VP7*r9$|Zvr<123Vyh&st-dNnOt) zxtL4AjW-w3bde9fPrdt&)f0toUJ2&UdD?Duy5Ap7dEF=0V82i93p+Kxkri{*^!QAa z`)V#?MO?Egc}EaN?0rV`N9>*U}noU~6E-WouZiR;Mgh z;i}OVBurvrDpRj7!i%ICbMj)VT&(w5JB7dEWs8$MSfbZaa1D^jw$rlh41JSI!*+g5 zc`HjldKt~dEdKiq-t`OW#SHiFi#h4kU3|pR`S;CF5SvpIp|Cl8#>|qEO zL6o_yj`uN0$wSqXQfj)_qWIKrnS3$j-u8y`GrF8k5xy*m3E_xC>4xG+3@28lsi2dl zG->G?bNPxG)$u+RlKOK*4722EnDvKFTfCP}MVn#i1AP7T_HVVXeMTs4JO zpT_!OPG@)cEQ+es9a7Q~8ZJxuwg`RN6PqI_ZGrR{=g#vc28nWQy+I8dcb5dFR^-u; z&&P%sTVJJ;F`R;9s*$hDbF31St>mkHWdp=P*}5fF!x?lQhPw$TMi}e=#xDm^PWJok zBklIX+F!cN8)z!@No~Er@9ywmEwj?-&7I}xh?Aw0SPtK(3EQ+5LHqwwu+}k1p;#vH zrvh`dw3QgL-4@kIQ!Av--?{@#~s8|+dQ;(;Mo#ndpY6spn{3TJBv8{Ee0%vgX2)N zCCV1=Y(p9TH+hpYR^mG9QF6nF>tHb9wDPpXRlL7F+QvVV*IK(W=+D|wiR-*I;elS7 zY`O=x^{a5b-2CDtug6c%+y!Jb>;Y$1|5k+KbP-$ndnLz+PK~0IJ6_kenCmP!NG!nT z0oX@l4sD#DBU$@kjnc{sh4baeOf!mqY{x0?+@X-P%tFTkGt+fK8Xnl}SW!g#bX7&^ z+2;eo?q}&im*rirs}E*eubvzp8ZZ##(eDL0O^$sfaX!0;rmj^d#vG<0v5$vbadqkM z;c@S>jXq)Rz%lvuo_XtEk0U!0-X%0LG%_Oo&y;sC!y!Vzbv!1e%gjo7+E(!P5CXQg zglw~&%zv|GAITU4^EUXYL*ba5L|+fG{n2f#<$P`;XXQzw!rFG>1xIQtjYXPCx$0Tg z_y1H9*k8*NMu;cG(T9I5k|_z+!6-KvLctWLG?awCF`Wto6>5{_B*kX_J!#TlRfW|Q zTxT2;H#0}=YR;55U1N;$dTp5H%;k}GCmbbyfA00QK5!SnK;wWT_=y7G3YX(F_2ej zekKG-;-FFYlnsInfBS-ue-l(=JyzlnCV;dv+bFa!pd>$1xZyr37BgGGzr|0+^O~0j z15^}t&e-E6dU|#)QNVmuka5beLq1^$=n5hx6Mg@fLV!rjf(f07zjUyE!{MRr^$O81 z9c&-SdtEZ{pn(T}h6ZnUS7wPMBn?d!5HMe!BHRBbb05=@24O?2h_`+1 zSkky=Y6p<;hK&MFs_UV3Pi4-ZFlQ5qOdAaJ4>=1O04Q<~*!bCF?FPS~o{er4?b z@BAktYAQF=_~SF#TF%vAsN~HdgBetV+7Sn}tl<@KS7SOg0f&fC(;da%oL1YWSL+*m zGM#5P_te#*^#`lcd2E#Bzrd<*Ozyihcs6GM{UIN@;iOnS-MRs~qr?3IfIIow<-ibm z1axfeXk3WdOtrvL9~RrkL@RPE27Wm{vO5xg=Y{Si6xRMyB}nHWVL(7VUs(tiyCf+=eFX z^v*e{k1Tj6MkZdZ0LiaYY^zFpCUo+Dxx=bBlNeU*IS#VeeOAzI)Vt^$zh$j^EZMHM z**h+Kz~xZ6N@mz-#ETTbxO`K|Nr-N;@=2jQ#7ZgkFx(W;GWygjB|Jx@jU+qS`t!IrL_@Mh#X_TZx%@ z^4p_*L+-*ol_Bw(5gpCY^}j0qLkVl4eKqJivQEuSwK~_wQU=a?(Pr}B&EB% zySux)K|s1&x?55}O1is2>5>k~O$h(?yyyFj*W>Z~9|mI&_Fz2Mnsd!nbFSyU4NmP* zk_r34gxePNOJ$h6cykvyCw$qW0>}3|r&9U*AFcQWu@^Z90;YM#zVCO^+rx zNH@pXoqevqr|SqP@$wvXr8J@&d_JP>=uXmMSW8G@sN0shx}NXhJ^U;k3^P3*Y9*{X zT_){Q>`WUL%w79gi?=u4Dq=QB^rnC>Qexc!1mCKET58qi_4>ylhJterN@VVP&{9R} zf`VGjgzL=<92XlYXsi4V{!C1%tpasaKFas6LJV)K-=vfm;P_v(pq!FX4Y?&YsVKhO zR%%faHzRDbQ!M3E;64T2WnRzcuczPxKYjJ4E?oK+r6|}!&xa}zY4)CB2A?|sZ9Z0a z|7}5bo3I!eu5axh5J}j*49lzaa_Zc8rw3g>pdb(cSDK@($H8DyJ~4-_*`cwZ$s? ze5h6-?o%Yb`5-tXa|0?FF6Y2tk6?PhbB~VSfa6cTW01)6;9^4dE+jka44m<(+qOx| zS7+%A4{cV1vYAlL_6DE@7TAVxXLfPEJy)0APHnPc=nL6sYxCkc(#=FY#J=VU)@bgA z0_~_L;7&Dz1PtGWxfn&<4}Ma94p>_udw=f*7k4kv58VQ0lC!J^kehlmGtWV4Mi6UiYHz1L*lE`k@;g5_yK$-= zZtu<-NFGqxlm4JpB#T7g%Ex-iNmQO!&y7g$cHfwbO|=&7md}4l4Mn9|n24rEQ^>Ux zYO+gTedMAD(2~_1Q6k*FOpy38A*yn7gLcbXj?+s+U;2tl$BG4xn$@hHmfNzSfuA*V zDR8OI{FbT?yi6r34Q}@hSTAGKo2ggB19-#DmV2x|Zadz2|rHCQV8f=qYq3S-XQKr)V!L{fbjC(JB{i1oZ ziF#JsGKmxT>@0|5a3}*}b2#dWUIr!i`8n>4;r7E*)&qvB!SvEbZkC%_T$i>HF_iTK znSw(apn9nYdcK)KaXd!E__$?es}T}>(H*ztldjGo3~FxJOQHIwDEbA;V7L2u0y+iR zI z`Ta|+1SVzj1fro-ACvhOxw!`lkeVnt+5zUv+2Q>l6W3DEHS!?GkLeUc=jF=*DYi;4 zgAmXvqwtL98S&@oBP*(OL2;6Q!{jJ!x!SIzc(UKP=n25KVnzea3MJKb=3u8Cm>iLlc zo>?@$-95+WQf~)EAZt_5R=Kx&-+eesXf5(h%iWVsgV-k<5sR4Bt?SzA!_Si!Vs17{ z{6tvfF)5Sptk|88Zta~Yi^wNgFB3D>72<4rA$j}O^elvaJgTjo4ShF~YmiNpHeGbr zyKXGp)-!&Ibd!z^zbI+4QbF?)fGbwcwDyLFza9Z}=ghoEC1>_-5DRf*_-4`0`D_3% z-j$9^NUELnMfu|?&hgFGHu3n@;Oi!chfyGFC1tj zysM2L<;pVB&eZILeivP-DG6^E!_0P@Pv$*0)yMcNP8S ztipdgy#t~iDVyOeruzZb?;xzt0NZ53utk9^3ZvN}(iFQco`XI5+!2~Bt*g7s$UI9V zqTk}E=N|5KTZK~u!6+3ngR++0rc2UcL~b2^1ySOpH^5EkBa;19dk^IoLT_D(^eYV? zh)u!~KjQmm97L8GO!T6q$6zM-+4)P@I(QCal||#8B$YWzh+EnD6~{;lGD;KM(2Z~x zbfm^>#(c>3<`9QS(Mb$0_NoT37Om8`p*ft5u4+)-eY&scXqIdG8ph(=r%k3w~PVLOXd zvY%SJgzTUS)}20bSmIE#Ku2ArE#^+hFkz~5s)Jq}y~;DcyBxahE*PlD`+}A(u^rn<&8zczVDn%^A5dk-Vy_mr0qL*uM z+kH(G>dhnCDc>o`r?(AIs+^*rfe)ECTkV3CYD3Q#19fXQhe<>BD4P`WFJ{4fglrGp zMC#o(hLNzR_6BG%EOWFS0kBYlhLR^aX`ly0}L;y&ATq9Kgir+g(JSTR7eC^Kd70rtk@Qwh@u3M8?jc zvgkQ+ER2q@6iY?Es?2yUOPXy52HHmmw09OlCy8i1JSX$cFQ?Kz?WxLaD*;xXXdOZ= zBkjariS2=U=4{ztOD4WdLby%7@-N=%81G7r_onmAC}*~wh&dH`ElcXAaT1YCg!*3c zydPyIQxoLY1}B)t!AYV-sVm|=v@yqXQI~?W4Le?d1`+uZEGOQ|ee*VGf zrT|&74wW?}lFB{`V02N9RseY6=RHwR+vczuOFPU6KW$IutXl`cwNkIGa12qG zrJ%bP3TNk7J?}yS3x6XEWxoN1EKl;n-Jr)OR82@8A-lLcqJ0m!DhivFnJu)P!CIZozRj3Dupfu>UuxP6njtRWN0x(t)#GPjJ(W*QX;@KZebajIc;dm zCW~hL0jRsrD=aVq-P|3Oy{?-lW2lzd!ihrjVFr)oLbOS5oQOiE*S-!;?Lbx&bB@wB zIBCNkoH#5Y8I#5PlHx>EpLUEIfBnTV;pU3R%nfkZ z!YFhE-!>M@7lKEDX})s?nHWmd;*DDNM6GEm7PaY{ePtQ7vU*E6^Yo7t_xmKXg?pIw zLetbL($kGYR?TwDFJ{6?y@??DP->A;k*WI-u5h`r_Fj=a1?c8CaYv_fx+w3Y&sz)# z5l!Eerg8T>?FtY$ym)%@xf}a@V)bx@rCghzp-=;#(K|s@NOO*IZA)NzB23n8Oyp`N z6Y_)!pjq5GpOl;|9mspLVAjuk4Swf>dB>Z+oWGfksTiJHt6LL8{)`TN&}5mlo&S@f zn?k$j;4E88b8ms}U06xznINvR%znonws$*X0nXu~KR;D&0=; zq1MxLBj~1VFmZ3_rpJ&0B|edG0LL4z$TA%JtOE-~IHfCXompV+wy z8-&6rt-RaR;6BG2HZ5IoYkQ!W1K80!*5H1C5|T&@US7!VmLWU9nG%2IR0sf%g(q;p zir%R2#OCiM-FRbfu?u|_l)-Q7I{}F_K#B)nXF9wXSLm-9xO`&}clEL58GaMK6`1Uo zQKob~3zs=o{h-kD;27bhfCkdw{8=X?mD$rB(iIfJLV2z}Inma$btemM>{3VY_dH`c zRmH*W_;0{4Bi*0y!=kq3gCg}!KzsqQv(?<&2%Y|52_E_JZZE7axCF6;pWKz-h9;(1 zFEg|lBDp{TkLtU9pc8X{8!)$h;lT}wYiX`cFvH{sCC$IJ1nrkGsX1R-c54t zLc9jBHVaK(PZqQAK)*w|rQxaCi@4yDsR;BKp_0+QMY4^V@oQdty=y?g5jigp7$EqZ zjDUR~x@7qfAlguTFi<0JZx{E(?05$3ZrE!(`+7JwC(6-O)0zPfL-;9#k~GMZLtGy?nM#)>2+T`kNj ze-Cd%!Vd{3rx0cOIo+1L-plN7F!@)*0?vWum?{xsvwILKF<=UycOWzqNrt^1DAHo{ z&>l4+Ab^}}aY{#leq4;cq6#<-V$Ho7UKVZ81@Wh+CFOY)SxBEZUOMd5^n&4mJBI5y zhiL&%RP$EK=dU%dsx>v_%dKWSAnH{~OU>To6_twC8@+RTFwOV zjN#5sZh{G`WWFrn$+vV8xa_EdxGegTh$iG5fdf8|IkR2eF_u{^F!2%tv7EYty{ytY zfTzxF4)ngPoP_WTG|Fer08u&Q$%>o}_7yWw_VUke{^I-nDIPLL`#{~ep5)0hW*8ez z$=vvIc7ys0bTt^Z4cC$pSAr8jP+)*}S0n5;J4~41b{%cIM*fv_$1_a{7~CzEGF*%a zmo!~DyV(mH=a!>N6aTXY|l>8fd_G+w#(nF|q5jcLBA z13?#dl>PPCA}RNzqD6oVO(@OKym{I-Pa5JmLRwqW$FBiUBnL+P2)@~J(ec|s_sm!R2@$OKicGYN*2GqU(J&T z{Lqn)*=vxuAX1Gv0Dk!C`pCTtlDrGq_gKcHI?^jian>rS^UL?G0{-ilaNK#DTyw56 z{Mo5FbQ?Hew~5Kllovle5o!-n7?EA%~9 z%jQnBip8H@%a9KGo;gZW59-6s%P>_Y62@fk&z9tt_3vec<8wZNl}y-DPVJOG|Iin_ z626Fx(_8z21@R?Y6h3=m$wyZ(m0~u^gGm$C_>_E9bIWd}w}}Fi6`vO0&SEgSdVWB! z70oGSTwI5)%Dq)n3w0Upp_=|g;_;3OZw=}>WJUsdX*M=A4EsAwYD>0ZPrKc^Y`%(P zR4QJgyJNu4aNup&3279U6_ zdbsfLmw#jb+-(ai0SJf=$M4ESh--^XS307Zgwt`pJ8{}aNm%u@LRcdGx zw~H)F7#NIpX{7#kW5V(1H5 zz5AdL#5;!Xs~elu2h{fX{pR6_V=3+&^ruJ{iTx$`s^O_)RYD@?{ol+}(o43PDCFcy z>6@z&ig(9lnQ&Je#^YG*qG0nV5izc-nDi1Oya!vptC5L&xq!LbWas62!Jk9@Hgg$u zcf|NzytpAfC_?Eo)ZG&ywyD+)KyrtAk@F|5=o#Mda4t2W8yW1la)U@5zE9jn2t8L( zX81%5B2%>F4iIQQ*!=|^;t?PSN?@8gFwrSJ@S3$#y8xt&xUbuD-u=7}9#eLWR72-qTT@xu+BTcA6}iClYMq3D|3PS&w~_olnHK zbbUG}X3XIIUV2VpcbYSqR^lWK`E;G4pb|N_JYdhO-P9g;3Pq zx#XGZHE!5Xc?m~}&3$AbIXJZLI=xQV><&VT5CXbQ&*Kz10ue(bo$2A61QOcN*>`p;EOKRNXLPtn*{8w3F-Cleb(>;Dq;Q;C(4 zd?J7xq=(1C&}V+H(IjuWE!QWIPhSF^7YZk!fUfOIo+QzqwU^5k7P>3Y8U%-;?GA!O zHYcntF5ohIP^By2K2uO|W-gA~czK@O*61M(U{K*rXX`j+=FR!L5*bC z8%ZNoC}V;XL!Kpb>sP)JkSj_sf;rwMx2$<+g%bK77T7~8tSw-VD@GV=JA)2g5Hs@& zN(X^2sMAj;J;5fpbBvQ$s%Wr@mKo`t|+60qbQv%_fRc(1N8*2fDS zc~Y)?i3pyo`Y`?2GK=TmHMB1Sk?@)-KhzR}Oj=qWo(Ut-uUx}_lC%xNatZzBfmEBJ zSB2ILfPtS-VxP5RivoeD?|F1}MKFC}S2DXwe+>&i*)@^(pNc<0Ylm@t;ENoizkQkG z#jnpbKyf#qNVcsT*VPwT{GWW9AfDFmg(z^eN2;&JR3~wRYIg?8~`b z6w+Q}ETeZ#j>1Z?z5425VK$AnXI=J;)o?YW1AC@*n=7rc0xy8rmLo~Jcb!bgn3ceG zv1@S2g~rpP*}ia;hD~CRV%Kn2XA_Ux$o_4-22CZ*sM5r!eGy6Peeyw==5WHgAUBr! zfvRYibkq^Pj~pB0`BIi)Xx#xu3H)+%OM`sS+HY@3+2tFUh{#~*CgyA#2A6>lqfn z6S5O{6{Wk3D3`MS+HG^VfwulGBaN;h`#huNIg<4%zjQE;0edb^GBt_26eM9Eg~2<= z%x&8wNd;sz2J(b`T`Vn+b%GZu!pg_&@u44I_b|jc_M^Ast*GX% z~cER`C{E`DzN*%y4r>@ti4A$Le2~6EEK|BE&%nFopIQQ zN!-D9pX<=ija}?3M}Wur)SnR4!Q^=N{TZI>K-5OX+PuZ@ecEdP)O|3 z;Z49IgbEtgSJg(*(Aa^$Aoi=5ZV6^_E4HzP)mn?bbRzqSk-Q@}P! zU^@l7uS{R0FQ1#*uh%#!jP+VDBI7|deK+xz-o;cMwsFQa_N6oU`m|HL^uTLD=QXI? zqFiDND9*>fT!W9Zuh{5;R})jH-(6Au;dQ~kD`bIM)20??E{+DjC_(m7K9a=~L+3%m zmtNX7LSUw(wb78YdD4gQYKDwb0w6BK=Xyc%RRPAvWSvJs>w0h2R385!%w)PxhWr&M01bMie zx>a1ez2u_4;Q$qR#^a%(z`bD;W}PcbW;gZp$;XJ(jj16;20aY3xp5(V_)^EWM`}Gr zK#ADYB0DVWY&9JP_oH)FDL~K(Y0HNT%jo5+7MAC6`q*B*BqP)IfOA zSs1}p4ht#5?g87B?XYTl`HxLvWh($kg4e|Fz2Zvohr;hXR?n)(=s&V%ugp%$J_YTVFooJk<#&j9b704}aM+b!QM* zY2B{6NUDF@2GpzM?B-{6Ghg#rk|qw*Qr=FO%CA^HN`cxwni?*?^I8;o%^2I|#b!@H z!~kFZVrVLm*xR}zG$0!nJB)j{!+gufR3EieNl0$mvb9e%%PXc-huMH^XTw*p?1 zYyBDhW(uaF%N2hMyCTWakzvUi@hY_+R8p{u`b*vcrP^U z_*g|+yWK|d2olI`sQ^ThBwo*25*7;P@yH3tB(f9HU$-isz0RnuWHIEzUyNIb?n@Re zv$Du(b|ul3b3Fq0U>?6%DxBrqHZ@M!(Q9Sr<$XXSD&RZR=lmi8#WaVOpR03FJ!gJX7}xq)vi!L65L~h`COI7w7PQN!xMG^TmKZsOTAK%u z#7EYSymBa>Y&`4@Ffm&lxog|JGhG>BPx$u;Ig zhanra)@5TBV{@8(le)od=MZScTHK2=8cikHIuNW>^0PQLiQ-@U95r?P0sc?spnX8XB-Fwp8ZN9nk*gQNY==j2)0kCP> zDS3wH9LV%ani_3bU2|xy#zAU$rwL<`uAe~6y>{(&G8kQVUiZh>m`rur~bZ0XVL~QQ(q<_ClM)5o8+`+95hA?X0lOj&2f6?i%}xEm~y3R zZA1w3h^*;MJ*GFdRrP9o(a}EeSy$0MRB1H>ND#EI?o(ILX|D1yXsML7Jz;PiQelZ+ zp!i9t0BZQ}Y0c!zH|4A21GdDR7i)Cpg{XY}^=@lm1vWb9>y^p4F^Fj{5|XH~U(`1y zf0U&kUb4c0uQ(#`!MNRwE;%*DP}`saRhM}Q@8)WSInEKkDq_N)ih@A^4cDIuzpTR1 zg1^TRqQx;vVRq~}7XnA(a3&`_p-X}Rp+M!R82&a9yRuU2)qbcH!*(OuBG-ZxL$7^3 zk&b$I^~5I@OdQRRR`nvwa|Z8Ax*#R#RSH|9#$u7?>1oDhG*RHFDlwSr4bi&61QLwz zDLzl|vh{cbR+{+2Riced&uLkYy9`dK_ScE8u`N&ueqg2cUruA%=)P)#35CF58vwV> zIFPBlmMmvWShXzwjAC;X9Q9dnE`&F@@U8Utn=nx1ySEfLX(0((;LiiMhO*{o z332vyIVs;A+_1A?y(oW|?Fl2oUa(^_iON_+oYqiYgd}-iq2eyFl8e*2C7b|Q$7#)w zm1s2=sH^Fdv2u>d+BWU{?4KqFr-5CP>KbEH1xpYDVVij6M-c8AG=ym^@?d!I(P`9u z(W@77VDq{wy0<#R`)C@Tr;x*YPD61$^u=U&KnFrtLk+}c7XYQ}!}&%5t49-o8#I6j z8$BWc@|_PmISg)MZFq}`=(Tu&Y0*gn=!zUT%R6}HnzGC1I3zr#o#GHqMQG@>OzQj7okNAF z(psjhjkl6sE-6TI^GhnVg0K&Qnd~;28l$D{!$=pSZL9m)_hz5f__8{k;McQxsl7yL zoV4+ZL@DetHhsB+u&|Sr*#=j%+t!eitu!F$RMK>tLL_&GeKR_!oe^eQ=FnS3U9fs4 zI?FrCXlH>RT``+eW}G!(+Yec7JR&Y?WJi( zmoa%r*|6?kWI2MyMWFR&UR94W?=gsTJxJ}_*g_YkdUWL!owBrj-lX=Hx;)8+BIbFr zftcCqOWQ7{96mH7cGBrD==xgg7+$j^gyKT_a)O9QZ?{T>TX!jrkd>J#Cm|;2;tO2| z=43{SY5NJhTQKQ*&oeNy$u#WO!de&b$r+usOzH|f+vA&o_9PCcYXVad((7s>b=O!Z zxvTY)LL%1i&SDV@+C7(o`!I)3_ln}{m?q?=Y~@fKh>zj!lY5>N_O3$Ml2U5KPx+(7 zN0LYrf4JaN?NRvbXSVht{+PCc8`(XyfG??_f2D8e;jKH>`WI|T!;WbjqP9zrm*ZR7KW`bM%aMZ4>;lijsSslVlc+pT}&WfxFuQSMv0}uM1%mqJA$7GWa z6pIIode$f6LrBHlm1tMmunGE`=P4W`HIGYvT#t8kYINF0AA{{c=jGrCMA7YO`<&7m znPRW=3T+R(iyAEZD5LAgt+0a^)JQ95Y} zArV<65fxQBr;(Bl?f2HlYs0 ziGdJ%;O|#epl^W;RG_kRG@~>7OHhi=$l8MLJ1b@ZM>7{2pdviba?Qm47dPlXx4gn5 zAS((u#k2^#&-gl#^es}6f5-WyC+g41pS(8g(F7)M06uYiwe0*BfoQ)={+9!*<1+zM zpe4zFKtG#={Y zWh|VWfPQ@cp#n$BpCHi$Fq3A1NJ*f0`j5@bc=iX#zgcbujwXNJ%$8|Sv|Ql8_W^R* zf9Tq6-~sy2ga7Yw^MCDC&}KYbVj#*CIDmc}rk9j|j8g*IG1;2^%l?~tkPAUa! zoPSK-#rj{#|LUpVSk(V~Fn@15{M8crTjX;6d-DGbxPRIH@BK7?9A#=eKOijruWrUa zH|Ben#;-;|-(p?xH>CfwTj$T*@7>LQyk=br|G@pFquD<@LjKJ8-uCLNSK7B=k^Fbg zA3CS~4E^4B>8qpGw|FJ}1N48^U;fBn>u1XM)-XTrI(OM$QvTNt=KtpC^fUK+i;S=aQLge^)V~~G-zzMBoxuDS=O(|*`v;1gKX3c@GJ`*k za60qfF#ev4`Df+EpE=)Gb$=Bt{1(v`f5!Qj&icO6_{Yu)@%|;?4@$*8G>EADkeqE{m7WL`BO#91q`=2-V`_;N1uP(+}zs&l(<<*~)e?RN~b;0jj z5a;|l`5!F*{S5hjw(!SY+EDOI$ls&#chmVlGroU@`a19UEsRQj$M}a?NO>s;-~$;5 R2np~f1o-$>Q}y+){|A@R9n$~+ literal 0 HcmV?d00001