Repository navigation
Wiki
The consolidated monorepo for the vortacraftmc ecosystem: Fabric mods, datapacks, resource packs and the tooling that keeps them honest. Original project by Runtoolkit (
runtoolkit/suite) · maintained by vortacraftmc.
- Overview
- Getting Started
- Repository Structure
- Build System
- CI and DevContainer
- ⭐ macroEngine (framework datapack)
- macroEngine Resource Pack
- Fabric Mods
- GUIKit — in-game GUI editor
- Utility Packs
- Archived Datapacks
- Examples
- Scripts and Tools
- Merge Manifest & mergeDatapacks
- Pack Formats and Versions
- Contributing
- Security
- Troubleshooting
- FAQ
- Glossary
vortacraftmc/core is a monorepo consolidating what used to be a dozen
separate repositories under the retired runtoolkit organization:
| Directory | Contents | Built by Gradle? |
|---|---|---|
mods/ |
Fabric mods (Java, Loom) | ✅ yes |
packs/ |
Datapacks & resource packs | 📦 packaged/validated only |
examples/ |
Templates & sample projects | ✅ yes (mod example) |
scripts/ |
Helper tooling (Python/Node) | 🔧 standalone |
.github/ |
CI workflows, issue templates, CODEOWNERS | ⚙️ CI |
build-logic/ |
shared Gradle script for Fabric subprojects | ⚙️ build |
.devcontainer/ |
one-click dev environment | ⚙️ DX |
archived/ |
reserved for frozen projects (empty today) | — |
other/ |
reserved for uncategorized content (empty today) | — |
Important
Datapacks are in maintenance mode. Bug fixes, security hardening and
compatibility fixes are in scope; new feature work belongs in Fabric mods.
Every existing pack is additionally locked by
packs/.datapack-lock.json
and enforced in CI — see §16.3 the lock protocol.
| Project | Kind | Target | Status |
|---|---|---|---|
| macroEngine v26.4 | framework datapack | MC 26.4-snapshot-1 | ✅ current |
| macroEngine v26.3 | framework datapack | MC 26.3 | 🗄️ archived |
| macroEngine resource pack | resource pack | MC 26.4-snapshot-1 | ✅ required by macroEngine |
| guikit-datapack | in-world GUI editor | MC 26.3 | 🔒 locked, maintained |
| BorderPing / DailyBonus / QuickShare | utility packs | format [122, 1]
|
🔒 locked, maintained |
| datapack-blocker | Fabric mod | MC 1.21.1 | ✅ maintained |
| datapack-fixer | Fabric mod (standalone build) | MC 1.21.4 | ✅ maintained |
| rtwrapper-mod | Fabric mod | MC 1.21.1 | ✅ maintained |
| opmod-example (LevelOp) | Fabric mod | MC 26.3 | ✅ example |
| TEMPLATE-MOD | Fabric template | MC 26.x | 📄 template |
| itemExampleMod | example Fabric mod | MC 26.1.2 | 📄 example |
| TunnelScript · cmdTunnel · inv_gui · click-detection packs · RTWrapper (datapack) | datapacks | 1.19.2 – 26.3 | 🗄️ archived |
Legend: ✅ active · 🔒 locked in CI · 🗄️ archived · 📄 template/example
- Originated as
runtoolkit/suiteby Runtoolkit, consolidating theruntoolkitorganization's repositories: TunnelScript, LeftClickDetection, dp-depman, RTWrapper, datapack-fixer, itemExampleMod, template-datapack, InteractionClickDetection, cmdTunnel-datapack, dpgen, TEMPLATE-MOD, inv_gui, macroEngine, guigen. - The
runtoolkitorganization was retired in favour ofvortacraftmc; the old repositories are archived and point here. - Original copyright notices are preserved in each
LICENSE/NOTICEfile. - Skipped during consolidation (empty/inconsistent):
FunctionPP(2 files),DataLibFabric(placeholder), the old.githubrepo (2 files).
| Requirement | Version | Notes |
|---|---|---|
| JDK | 25 | Required — the wrapper will not build with older JDKs. Temurin recommended[^1]. |
| Git | any recent | |
| Gradle | (none) | The Gradle wrapper (./gradlew) is committed — do not install Gradle separately. |
| Disk space | ~2 GB free | Gradle cache + Minecraft jars via Fabric Loom. |
| RAM | 2 GB+ for Gradle | Root gradle.properties sets -Xmx4G; see §18.4 OOM for small machines. |
[^1]: CI uses actions/setup-java with the temurin distribution — matching it locally avoids "works on CI" surprises.
Tip
On a 2 GB VPS or Codespace, override the heap instead of editing files:
./gradlew buildAll --max-workers=1 -Dorg.gradle.jvmargs="-Xmx1500m -XX:MaxMetaspaceSize=512m"git clone https://github.com/vortacraftmc/core.git
cd core./gradlew buildAllWhat that actually runs:
flowchart LR
A[buildAll] --> B[":mods:*:build\n(Fabric Loom)"]
A --> C[":examples:*:build"]
A --> D[zipPacks]
D --> E[checkPacks\npack.mcmeta + JSON + refs]
D --> F["package packs/ → build/packs-dist/"]
-
Fabric subprojects (
mods/,examples/itemExampleMod) are compiled and remapped by Fabric Loom; JARs land in each subproject'sbuild/libs/. -
zipPacksvalidates every pack (checkPacks) and packages them intobuild/packs-dist/. It demands confirmation — pass-PconfirmZip=yesnon-interactively (CI does exactly this). -
lintis separate:./gradlew lint.
Other useful entry points:
| Command | What it does |
|---|---|
./gradlew build |
root lifecycle: mergeDatapacks → build/dist/
|
./gradlew verifyMod |
fabric.mod.json sanity, mixin refs, duplicate mod ids |
./gradlew checkPacks |
pack.mcmeta / JSON validation only (no zipping) |
./gradlew printPackList / printPackFormats / printModList / printModVersions
|
inventory reports |
./gradlew mergeDatapacks |
merge packs per §14 |
./gradlew showOriginWatermarks / checkOriginWatermarks
|
provenance watermark audit |
./gradlew :mods:datapack-blocker:build
./gradlew :mods:rtwrapper-mod:build
./gradlew :examples:itemExampleMod:buildWarning
Scoped builds are fine while iterating, but always run the full
./gradlew buildAll and ./gradlew lint before opening a PR — a shared
dependency bump can silently break a sibling subproject. This is the #1
CI failure mode called out in NOTICE.md.
Datapacks are not compiled. To use one:
- Copy the pack folder (or the zip produced by
zipPacks) into your world'sdatapacks/directory — or configure a server-side pack source. - Run
/reloador restart. - For macroEngine you must also install the companion resource pack — messages, sounds and trim assets live there.
Version compatibility is per-pack: see §15.
The repo ships a devcontainer definition
(.devcontainer/) with GitHub CLI, zsh, and postCreateCommand /
postStartCommand hooks (setup.sh / start.sh) that provision the
toolchain. VS Code extensions are pre-declared:
-
misode.vscode-datapack-helper&SPGoding.datapack-language-server— datapack DX -
redhat.java+vscjava.vscode-java-pack— mod development -
vscjava.vscode-gradle— Gradle integration
Open the repo via Reopen in Container (or GitHub Codespaces) and run
./gradlew buildAll. Details: §5.5.
-
IntelliJ IDEA — import the root
build.gradle; each Fabric subproject gets Loom run configurations (Minecraft Client/Minecraft Server). -
VS Code — the devcontainer extensions above cover both Java and datapack
languages;
datapack-language-serverunderstandsmcfunction, SNBT andpack.mcmeta. - Datapack authors: keep an eye on the
pack-format matrix —
min_format/max_formatreplacedpack_formatfor modern releases.
core/
├── mods/ Fabric mods (Gradle subprojects)
│ ├── TEMPLATE-MOD/ MC 26.x template, split client/main sourcesets
│ ├── datapack-blocker/ MC 1.21.1 — locks the world datapacks/ folder
│ ├── datapack-fixer/ MC 1.21.4 — standalone build, excluded from shared build
│ ├── opmod-example/ MC 26.3 — "LevelOp", /op <player> <level>
│ └── rtwrapper-mod/ MC 1.21.1 — Fabric successor of RTWrapper
├── packs/ Datapacks & resource packs (NOT compiled)
│ ├── macroEngine-Datapack-v26.3 / -v26.4
│ ├── macroEngine-Resourcepack-v26.3 / -v26.4
│ ├── guikit-datapack/ guikit-demo/
│ ├── BorderPing/ DailyBonus/ QuickShare/
│ ├── TunnelScript/ cmdTunnel-datapack/ inv_gui/
│ ├── LeftClickDetection/ InteractionClickDetection/
│ ├── RTWrapper/ RTWrapper-1.21.1/ RTWrapper-v1.0.1/
│ ├── merge-manifest.json input for the mergeDatapacks task
│ └── .datapack-lock.json CI immutability locks
├── examples/
│ ├── itemExampleMod/ example Fabric mod (MC 26.1.2, Gradle subproject)
│ └── template-datapack/ minimal datapack skeleton
├── scripts/ standalone tooling (Python / Node)
│ ├── datapack_risk_scan.py read-only security audit → Markdown report
│ ├── datapack_lock/check_lock.py
│ ├── dp-depman/ datapack dependency manager (Python, stdlib only)
│ ├── dpgen/ config-driven datapack generator (Node)
│ └── gui_generator/ "guigenmc" — JSON → GUI datapack (Python, web UI)
├── build-logic/
│ └── shared-fabric-mod.gradle common processResources/jar/publishing blocks
├── .github/
│ ├── workflows/ build.yml, datapack-immutability.yml, …
│ ├── ISSUE_TEMPLATE/ structured bug/security reports
│ └── CODEOWNERS
├── .devcontainer/ devcontainer.json + setup.sh + start.sh
├── settings.gradle subproject auto-discovery (see §3.2)
├── build.gradle root aggregate tasks (~2,200 lines)
├── gradle.properties daemon JVM args (-Xmx4G), parallel, caching
├── README.md · NOTICE.md · CONTRIBUTING.md · SECURITY.md · LICENSE
└── banner.png · index.html
settings.gradle
auto-discovers subprojects — you normally never edit it when adding a project:
- Scans
SCAN_ROOTS = ['mods', 'packs', 'examples']recursively for anybuild.gradle/build.gradle.kts. - Includes each hit as
:<root>:<relative-dir>(e.g.:mods:datapack-blocker). - Skips anything under a
build/or.gradle/output directory. - Skips explicit exclusions, each with a recorded reason:
| Excluded path | Reason |
|---|---|
mods/datapack-fixer |
legacy tool, not yet migrated to the shared build — has its own wrapper + GameTests |
packs/RTWrapper/rtwrapper-fabric |
superseded by mods/rtwrapper-mod; kept for historical reference |
Add -PdebugSettings=1 to any Gradle invocation for verbose per-directory
discovery logs. Every build prints a summary:
[settings] subproject discovery summary:
[settings] mods/ -> 5 subproject(s)
[settings] packs/ -> 1 subproject(s)
[settings] examples/ -> 1 subproject(s)
[settings] total: 7 subproject(s), 2 excluded
build-logic/shared-fabric-mod.gradle
is applied via apply from: (not as a precompiled plugin — deliberately, so
each subproject can pin its own Fabric Loom version). It factors out only:
- the
processResourcesblock that expands${version}intofabric.mod.json withSourcesJar()- the basic
mavenJavapublication
Intentionally not shared (because they differ per project): the plugins{}
block, version/group, dependencies{}, Java release levels (21 vs 25), and
loom{} special cases like TEMPLATE-MOD's splitEnvironmentSourceSets().
Every datapack under packs/ carries a _vc_origin.mcfunction file under
data/<namespace>/function/:
-
zipPacksfails the build if a watermark is missing. -
showOriginWatermarks/checkOriginWatermarksaudit them (cleanOriginWatermarksexists but is disabled). - Contributors must not remove, rename or hand-edit watermarks in
packs/— this is a conduct expectation, not a license restriction (the Unlicense still governs copies you obtain). - Some packs additionally keep an older
_rt_origin.mcfunction(Runtoolkit-era) tag entry; both coexist without conflict.
packs/.datapack-lock.json
marks every existing top-level pack as locked: true:
BorderPing · DailyBonus · InteractionClickDetection · LeftClickDetection · QuickShare · RTWrapper · RTWrapper-1.21.1 · RTWrapper-v1.0.1 · TunnelScript · cmdTunnel-datapack · inv_gui · macroEngine-Datapack-v26.3/-v26.4 · macroEngine-Resourcepack-v26.3/-v26.4 · guikit-datapack · guikit-demo
The datapack-immutability.yml workflow fails any PR touching a file under a
locked pack. Editing one requires the three-PR protocol —
§16.3. New top-level pack directories are
allowed through (that's how packs get added) but must be added to the lock
file, locked, in a prompt follow-up PR.
| Component | Version | Source of truth |
|---|---|---|
| Gradle | 9.4.0 | gradle/wrapper/gradle-wrapper.properties |
| JDK | 25 | CI (temurin) and mod toolchains |
| Fabric Loom | per-subproject (see §4.3) | each build.gradle plugins{} block |
| Fabric Maven | https://maven.fabricmc.net/ |
settings.gradle pluginManagement |
flowchart TD
buildAll["buildAll (aggregate)"] --> modBuilds[":mods:*:build · :examples:*:build"]
buildAll --> zipPacks
zipPacks --> checkPacks
zipPacks --> dist["build/packs-dist/*.zip"]
build["build (root lifecycle)"] --> mergeDatapacks
mergeDatapacks --> merged["build/merged-datapack/ + .zip"]
build --> distRoot["build/dist/"]
check["check (root lifecycle)"] --> verifyMod
lint["lint (aggregate)"] --> modLints["per-subproject lint hooks"]
Gradle plugin versions are per-subproject in this monorepo, by design:
| Subproject | MC target | Loom | Mappings | Java |
|---|---|---|---|---|
mods/datapack-blocker |
1.21.1 | 1.15.5 | Yarn 1.21.1+build.3
|
21 |
mods/rtwrapper-mod |
1.21.1 | 1.15.5 | Yarn 1.21.1+build.3
|
21 |
mods/opmod-example |
26.3 | 1.15.5 | none (unobfuscated) | 25 |
mods/TEMPLATE-MOD |
26.1.2 | 1.16-SNAPSHOT | none (unobfuscated) | 25 |
examples/itemExampleMod |
26.1.2 | 1.15.5 | none (unobfuscated) | 25 |
Key rules that follow from this:
Important
MC 26.1+ ships unobfuscated. For those targets:
- there is no
mappingsdependency — Mojang no longer publishes mappings for 26.x; -
fabric.loom.disableObfuscation=trueis set ingradle.properties; - dependencies use plain
implementation/compileOnly— themod*configurations are never created when obfuscation is disabled.
mods/opmod-example is the reference implementation of this pattern.
Caution
Fabric publishes Loom per-patch only: 1.17.21, 1.18.2, … A bare
1.17 does not exist on maven.fabricmc.net and fails with
"Plugin … was not found". See §18.2.
The root gradle.properties
sets the daemon args for the whole multi-project build (subproject-local
gradle.properties jvmargs only apply when that subproject is built as its
own root):
org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=1g
org.gradle.parallel=true
org.gradle.caching=trueHistory: 512 MiB (Gradle default) used to OOM once several Loom subprojects
ran remapJar in one daemon; 4G is the conservative fix. On small machines,
override on the CLI instead of editing the file (see §2.1).
| Task | Description |
|---|---|
buildAll |
builds every Fabric subproject, then runs zipPacks
|
lint |
aggregate lint across subprojects |
build (root)
|
mergeDatapacks → copies output to build/dist/
|
check (root)
|
runs verifyMod
|
| Task | Flags | Description |
|---|---|---|
checkPacks |
-PcheckPacksReport=<path> writes JSON summary |
validates pack.mcmeta, JSON files and cross-references for every pack |
zipPacks |
-PconfirmZip=yes (required in CI) |
packages each pack into build/packs-dist/; fails on missing _vc_origin watermark |
mergeDatapacks |
-PmergeManifest=<path>, -PmergeReport=<path>
|
merges packs listed in packs/merge-manifest.json into one datapack — see §14
|
printPackList / printPackFormats
|
— | inventory of packs and their formats |
printModList / printModVersions
|
— | inventory of mods and MC/Loom/loader versions |
| Task | Description |
|---|---|
showOriginWatermarks |
prints every _vc_origin.mcfunction found |
checkOriginWatermarks |
fails if a pack lacks its watermark (also detects missing ones) |
cleanOriginWatermarks |
exists but is disabled |
verifyMod |
fabric.mod.json required fields, duplicate mod ids, entrypoint/mixin source existence, mavenLocal() smell |
| Task | Notes |
|---|---|
clean |
extended to also wipe build/packs-dist, build/packs-remap, build/merged-datapack(.zip), build/dist
|
Note
The root build.gradle is deliberately Gradle-10-ready: every task action
captures configuration-time values into locals instead of touching the
Project API at execution time.
| Workflow | Trigger | Job |
|---|---|---|
build.yml |
push / PR to main·master, manual |
build all subprojects, lint, artifacts, release packs |
datapack-immutability.yml |
PR to main
|
block edits to locked packs |
codeowners-sync.yml |
repo metadata events | keep .github/CODEOWNERS aligned |
get-source-code.yml |
manual | source snapshot helper |
test-guigenmc.yml |
push / PR | unit-test the guigenmc generator |
All workflows cancel superseded runs on the same ref
(concurrency: cancel-in-progress: true).
sequenceDiagram
participant G as GitHub runner
participant J as JDK 25 (temurin)
participant W as ./gradlew
G->>J: actions/setup-java@v5
G->>W: buildAll -PconfirmZip=yes
W-->>G: subproject JARs + packs-dist zips
G->>W: root build (mergeDatapacks → build/dist/)
G->>G: upload-artifact (if: always())
G->>G: release-packs job (needs: build)
Notable engineering decisions (documented inline in the workflow):
-
-PconfirmZip=yes—zipPacksrefuses to run without explicit confirmation; this flag is the sanctioned non-interactive path for CI. - The root
buildtask runs in addition tobuildAll, becausebuildAllhistorically never invokedmergeDatapacks(they are independent aggregates). Without this the merged datapack never reached artifacts. - Artifacts upload with
if: always()so partial outputs remain inspectable on failure;release-packsusesneeds: buildand is skipped on genuine failure.
Blocks PRs that modify files under packs/<name>/ when that pack is
"locked": true in packs/.datapack-lock.json on the base branch.
Security properties, by design:
-
No
pull_request_target— read-only diff check, no secrets, no elevated base-branch token against untrusted PR content. - Both the lock file and the checker script are loaded from the base branch, so a PR cannot unlock a pack or neuter the checker in the same change.
-
No
paths:filter — as a required status check it must report on every PR, otherwise path-filtered silence would block merges. The diff step itself handles "nothing changed under packs/".
Editing a locked pack anyway → §16.3.
-
codeowners-sync.yml — regenerates
.github/CODEOWNERSfrom a canonical mapping so review routing never drifts from directory ownership. -
test-guigenmc.yml — installs
scripts/gui_generatorand runs its test suite, guarding the guigenmc generator. - get-source-code.yml — utility workflow producing a source snapshot.
.devcontainer/devcontainer.json:
Works with VS Code Remote-Containers and GitHub Codespaces. After
create, ./gradlew buildAll is all you need. The datapack-language-server
extension gives completion/diagnostics for mcfunction, SNBT and pack.mcmeta.
| Template | Use for |
|---|---|
bug_report_mod.yml |
Fabric mod defects (mods/) |
bug_report_pack.yml |
Datapack/resource-pack defects (packs/) |
feature_request.yml |
New features (remember: packs are in maintenance mode — expect mod suggestions) |
security_report.yml |
Pointer to private vulnerability reporting — see §17 |
macroEngine is a macro/module framework datapack for Minecraft Java Edition: a large library of reusable command-based systems — math, string, NBT, geo, permissions, UUID cache, hooks, rate limiting and more — plus a multi-source text/value input system (dialogs, writable books, signs, lecterns, name tags, command-block minecarts).
Owner: runtoolkit (original) · Maintainer: vortacraftmc · License: Unlicense · Namespace:
macroengine
| Build | Minecraft | Pack format | Status |
|---|---|---|---|
macroEngine-Datapack-v26.4 |
26.4-snapshot-1 | [122, 0] |
✅ current |
macroEngine-Datapack-v26.3 |
26.3 | 121 |
🗄️ archived — see §6.14 |
Scale of the pack (v26.4): 1,533 files, of which ~1,000 are
mcfunction files — core/ 558 · api/ 220 · systems/ 157 · input/ 36
· experimental/ 16 · events/ 11.
- Drop the datapack folder into your world's
datapacks/directory (or add it as a server-side pack source). - Install the companion resource pack — without it, messages fall back to raw translation keys and players get a recurring join warning.
-
/reloador restart. macroEngine initialises automatically via#macroengine:events/on_load.
Server-side enforcement (recommended):
# server.properties
require-resource-pack=true
resource-pack=<url to macroEngine-Resourcepack-v26.4.zip>
resource-pack-prompt=macroEngine resource pack is requiredDefault tick configuration lives in data/macroengine/function/config/
(see §6.13).
flowchart TB
subgraph public["api/ — stable public entry points (220 functions)"]
api[cmd · cb · dialog · perm · title\ninteraction · item · macro · toggle\ntrigger · wand · color · gamerule]
end
subgraph internal["systems/ — internal modules (157 functions)"]
sys[math · string · nbt · logic · geo · flag\nhook · log · rate_limit · sound · uuid · color · trim]
end
subgraph io["input/ — player value capture (36 functions)"]
inp[book · sign · lectern · name tag\ndialog · anvil · command-block minecart\n+ input/validate]
end
subgraph triggers["event sources"]
adv["advancement/ (core · hidden · systems)"] --> hook
tags["function tags\n#macroengine:events/on_load · loop"] --> core["core/ (558 functions)"]
end
api --> sys
api --> io
hook[systems/hook] --> sys
exp["experimental/ (16 functions)\ngated by toggle/experimental"] --> sys
core --> api
Content registries shipped by the pack: advancement/, damage_type/,
dialog/, enchantment/, function/, item_modifier/, loot_table/,
predicate/, recipe/, tags/, trim_material/, trim_pattern/.
Lifecycle function tags (tags/function/events/): on_load · on_join ·
on_death · on_respawn · on_jump · on_lc (left click) · on_rc
(right click) · on_enchant · on_open_gui.
Stable public entry points. Anything under systems/ may change without
notice; the API surface is what pack authors should call.
Typed wrappers around vanilla commands, grouped by domain:
| Domain | Wrappers |
|---|---|
| Chat & titles |
actionbar, msg, msg_raw, title, title_all, title_bold, title_sub, title_times, title_clear, title_reset, subtitle
|
| Teleport & movement |
tp, tp_facing, tp_to_coords, tp_to_entity, tp_to_player, spreadplayers, spectate, spectate_stop, ride, ride_dismount
|
| Items & inventory |
give, give_one, clear, clear_item, item_modify, item_rename, loot_drop, loot_give
|
| Entities & effects |
summon, kill, damage, damage_typed, heal, effect_give, effect_give_all, effect_clear, effect_clear_one, enchant, freeze, unfreeze, swing
|
| World |
setblock, fill, clone, clone_masked, place_feature, place_structure, locate_structure, forceload_add, forceload_remove, time_set, time_add, time_of, weather, particle
|
| Scoreboard & storage |
scoreboard_add, scoreboard_get, scoreboard_set, storage_set, data_remove_block, data_remove_entity, data_remove_storage, trigger_set
|
| Advancements |
advancement_check, advancement_grant, advancement_revoke
|
| Attributes |
attribute_get, attribute_set, attribute_add_modifier, attribute_get_modifier, attribute_remove_modifier
|
| Boss bars |
bossbar_add, bossbar_remove, bossbar_set_max, bossbar_set_players, bossbar_set_value
|
| Players & admin |
op, deop, ban, ban_ip, kick, whitelist, gamemode, gamerule, difficulty, xp_add, xp_get, xp_set, tag_add, tag_remove, publish, datapack, console_say, console_me, display_name, as_player
|
| Sound |
sound, sound_all, stopsound
|
| Dispatch helpers |
function, execute_if_pred, run_with_cb, run_cb_reset, set_clock_rate, disable
|
Schedule work to run later: run · run_delayed · run_sequence ·
cancel · list · queue_size.
grant · revoke · has · check · exec · run · list · clear ·
trigger/ (permission-aware /trigger handling).
give · give_custom · give_lore · register · register_cmd ·
register_fn · unregister · unregister_all · has · list ·
cooldown_set · cooldown_check.
show · open · show_dialog_exec · notify_admins — backed by
dialog/input_prompt.json.
spawn · bind_attack · bind_use · unbind_attack · unbind_use ·
remove · list.
combo_marker · durability_overlay · hidden_flag · owner_tag ·
use_cooldown · use_cooldown_check.
bind · bind_cmd · enable · disable · unbind · unbind_all · list.
resolve · validate · lerp · from_score · palette API
(palette_set/get/list/delete) · gradient API (gradient_set/delete).
-
title:show·show_raw·actionbar·broadcast -
gamerule:get·set·reset -
macro:with_function·with_score·with_tag·with_predicate·with_advancement -
toggle: per-module dirs +list·show— §6.8
Internal utility modules used by the API layer (advanced integrations may call them directly).
abs · atan2 · average · ceil_div · clamp · cos · distance2d ·
distance3d · divmod · factorial · gcd · is_between · lcm ·
lerp · lerp_clamped · log2 · map · max · min · minmax · …
announce · announce_prefix · format_coords · format_number ·
format_ticks · header · hover_text · ordinal · pad_left ·
player_raw · …
append · clear · copy · count · exists · first · merge ·
move · pop · swap
in_region · in_region_unless · in_sphere · near_entity ·
near_entity_unless · as_nearby_player · region_watch/
store · recall · recall_array · from_entity · from_array ·
match · has · forget · cache_clear
set · set_if · get · get_or_default · unset · clear · copy ·
any · toggle · list · list_systems · toggle_system — boolean
flags in storage, plus per-system toggles.
if_eq · if_not_eq · if_gt · if_gte · if_lt · if_lte · if_and ·
if_or
Levelled logging: debug · info · warn · error · add · show ·
clear · set_level · get_level
play · play_at · stop — uses the sounds from the resource pack.
Dedicated sections: §6.6 · §6.7 · §6.12.
Capture player-provided values through whichever UI fits the situation — all
sources normalise into one pipeline with shared validation
(input/validate/check) for int / float / bool / tag-safe strings.
| Source | Entry points |
|---|---|
| ✍️ Writable book |
input/writable_book, input/give_writable_book
|
| 🪧 Sign | input/sign |
| 📖 Lectern | input/lectern |
| 🏷️ Name tag |
input/name_tag, input/give_name_tag
|
| 💬 Dialog | input/dialog |
| ⚒️ Anvil |
input/anvil, input/give_anvil, input/give_anvil_custom
|
| 🚃 Command-block minecart |
input/command_block_minecart, input/summon_cbm
|
Bind arbitrary functions to gameplay events. Bindings live in storage; use
bind / unbind / unbind_all / list, plus raycast/ helpers.
Fired events (on_*):
| Category | Events |
|---|---|
| Blocks |
on_break_block, on_placed_block
|
| Movement |
on_dimension_change, on_jump
|
| Combat |
on_entity_kill, on_killed_by_arrow, on_target_hit
|
| Interaction |
on_item_use, on_using_item, on_interact_anvil, on_interact_shulker_box, on_open_chest, on_drop, on_trade
|
| Misc |
on_eat, on_fish_caught, on_hero_of_the_village, on_advancement_fire
|
Advancement-driven hooks live under advancement/systems/hook/
(break_block, dimension_change, eat_food, entity_kill, fish_caught,
hero_of_the_village, interact_anvil, interact_shulker_box, item_use,
killed_by_arrow, placed_block, trade), while advancement/core/internal/player/
contains ~30 interaction advancements (anvil, barrel, beacon, blast furnace,
brewing stand, cake, campfire, cartography table, cauldron, chest, crafting
table, dispenser, dropper, ender chest, flower pot, furnace, grindstone,
hopper, jukebox, lectern, loom, noteblock, shulker box, smithing table,
smoker, stonecutter, trapped chest, vault, click/attack entity).
# bind a handler to an event, then let the engine fire it
data modify storage macroengine:input event set value "macroengine:trim_matched"
data modify storage macroengine:input func set value "macroengine:systems/trim/on_matched_example"
function macroengine:systems/hook/bind with storage macroengine:inputsystems/rate_limit throttles requests with sliding windows at three
scopes. Rules live in macroengine:engine storage and survive reloads.
| Scope | Key convention | Guards against |
|---|---|---|
| Global | global:<name> |
whole-server floods |
| Per-player | player:<name>:<player> |
one player spamming |
| Per-channel | channel:<id> |
one tick system starving others |
Register/update a rule (macro function):
function macroengine:systems/rate_limit/config {key:"global:my_event",limit:5,window:100}
function macroengine:systems/rate_limit/config {key:"player:shop:$(player)",limit:3,window:600}
function macroengine:systems/rate_limit/config {key:"channel:admin_systems",limit:10,window:20}Fields: key (unique rule id), limit (max hits inside the window),
window (sliding window width in ticks — 20 = 1 second). Manage with
rate_limit/check, rate_limit/delete {key:"..."} and rate_limit/list.
This is one of the features that illustrate why datapacks are "hard to scale" (see §19): a few dozen lines of Java equivalent becomes hundreds of lines of
mcfunctionhere.
Runtime enable/disable per module — api/toggle/<module>/true|false,
inspect with api/toggle/list and api/toggle/show.
Toggleable modules: cb · perm · geo · wand · interaction · hook ·
experimental.
Storage-backed permission model: grant / revoke mutate, has / check
test (silent vs messaging), exec / run gate function execution on a
permission, list / clear inspect/reset, and trigger/ wraps
permission-aware /trigger flows.
Dispatch helpers that build macro ($(param)) calls from different value
sources — the heart of the framework's name:
with_advancement · with_function · with_predicate · with_score · with_tag
Custom enchantments (enchantment/):
| Enchantment | Effect |
|---|---|
steady_hand |
utility |
xp_thrift |
XP economy |
momentum_surge |
movement speed on boots |
overload_ward |
protects against macroEngine's own damage types |
Custom damage types: overload, purge, sanctioned — grouped under
#macroengine:core in tags/damage_type/. overload_ward exists
specifically so players can be shielded from the engine's own sanction
mechanics. Enchanted books are craftable (recipe/momentum_surge_book.json,
recipe/overload_ward_book.json).
(v26.4+) A fully custom trim pipeline:
-
trim_pattern/macroengine:circuit— custom pattern -
trim_material/macroengine:overload— custom material - 1.21.5+ registry shape: no
template_item/ingredient, onlyoverride_armor_assets - Apply with any vanilla smithing template + the crafted ingot
(
recipe/util/overload_trim_ingot.json, pattern reciperecipe/circuit_armor_trim.json) - Matching assets (overlay textures, colour palette,
armor_trims.jsonatlas extension viareplace:false) ship in the companion resource pack -
Processing:
systems/trim/scans an entity's armor for the circuit+overload combination and fires amacroengine:trim_matchedhook event per matching slot;on_matched_example.mcfunctionis a bindable handler example.
-
config/tick_cfg.mcfunction— idempotent tick defaults (only sets keys that are missing; safe to re-run on/reload). Score keys under#vortacraftmc.packs.macroengine.config.tick.*on themacroengine.metascoreboard:-
tick.rate— ticks between dispatch runs (default 1 = every tick) -
tick.pause—0running /1paused - System dispatch is fixed-rate with 5 systems (time / player / queue /
hud / admin), toggled via
systems/flag/toggle_system— the old dynamic channel registry (tick.max_channels) was removed. - Policy note: tick config changes are reviewed via PR and checked by CI; runtime macro key generation is not allowed in this file.
-
-
setup.mcfunction/disable.mcfunction— install/removal entry points -
core/— internal engine (558 functions): loader, tick dispatch, unsupported gate -
player/,world/— per-player and per-world state
Warning
v26.3 is archived. On every /reload it prints an unsupported warning,
and the internal engine (macro:input / macro:output / macro:engine)
will not initialize unless you explicitly opt in:
/data modify storage macroengine:engine config.allow_unsupported_load set value 1bStatic content (recipes, loot tables, advancements, predicates, item
modifiers, enchantments) is unaffected either way.
See core/internal/load/unsupported_gate.mcfunction. Development continues
in v26.4 — a separate, supported pack.
-
item_modifier/(23):breakable,custom_model_data_clear,damage_random,enchant_randomly,enchant_with_levels_5/30,glint_add/remove,hide_tooltip(_reset),lore_add_custom,lore_clear,name_reset,provide_overload_trim,rename_to_custom,repair_full,set_count_1/16/64,set_custom_model_data,strip_enchants,unbreakable, plusinput/ -
predicate/(30+): state predicates such asis_sneaking,is_sprinting,is_swimming,is_gliding,is_flying,is_burning,is_freezing,is_hungry,is_full_health,is_baby,is_daytime,is_raining,is_thundering,weather_clear,in_overworld/nether/end,in_desert_biome,in_ocean_biome,in_the_end_highlands,is_creative,is_survival,is_on_ground,is_in_water,is_holding_bow/shield/sword/trident,has_empty_mainhand/offhand,health_below_half,near_village_structure, pluscore/ -
loot_table/:player/,util/ -
recipe/:circuit_armor_trim,momentum_surge_book,overload_ward_book,util/ -
dialog/:input_prompt.json
Gated behind api/toggle/experimental/true. Not guaranteed stable between
versions. Currently included (16 functions):
| Module | Idea |
|---|---|
combat_tag |
combat tagging |
crafting_ui |
crafted-UI experiments |
hologram |
hologram helpers |
particle_trail |
particle trails |
scoreboard_hud |
scoreboard HUD |
waypoint |
waypoints |
Schedule a delayed callback
function macroengine:api/cb/run_delayed # queue a function to run later
function macroengine:api/cb/list # inspect what's queuedGate an action on a permission
function macroengine:api/perm/grant # once, for the target player
function macroengine:api/perm/exec # run gated workAsk the player for a value (sign input + validation)
function macroengine:input/sign # open the capture surface
function macroengine:input/validate/check # validate before useReact to block breaks
data modify storage macroengine:input event set value "macroengine:on_break_block"
data modify storage macroengine:input func set value "mypack:handlers/on_break"
function macroengine:systems/hook/bind with storage macroengine:inputThrottle a player-facing action
function macroengine:systems/rate_limit/config {key:"player:shop:$(player)",limit:3,window:600}
function macroengine:systems/rate_limit/check # fails the caller when over limitImportant
This resource pack is REQUIRED for
macroEngine (datapack). All
user-facing tellraw/title messages use translation keys in the
macroengine.* namespace, and the custom sounds, death messages and trim
visuals live here.
| v26.4 | v26.3 | |
|---|---|---|
| Folder | macroEngine-Resourcepack-v26.4 |
macroEngine-Resourcepack-v26.3 |
| Pack format | [98, 0] |
v26.3-era |
| Status | ✅ current | 🗄️ archived with its datapack |
pack.mcmeta description says it plainly:
"macroEngine | 26.4-snapshot-1 — resource pack (REQUIRED: i18n, sounds, trim assets)".
assets/macroengine/
├── lang/ localized language files (macroengine.* keys)
├── sounds/ custom sound files
├── sounds.json sound registry
└── textures/
├── palettes/trim/ trim colour palettes
└── trims/entity/ armor trim overlays
├── humanoid/
└── humanoid_leggings/
Plus pack.mcmeta and pack.png at the root.
macroEngine never hard-codes chat text — every message is a macroengine.*
translation key served from the lang/ files. Players see messages in their
client language; other languages fall back per the rules below.
Players: place the zip (or folder) in resourcepacks/ and enable it in
Options → Resource Packs.
Servers (recommended — force the pack):
# server.properties
require-resource-pack=true
resource-pack=<url to macroEngine-Resourcepack-v26.4.zip>
resource-pack-prompt=macroEngine resource pack is requiredAlternatives: ship it as a world resource pack, or host the zip and let players opt in.
| Feature | Behaviour without the pack |
|---|---|
tellraw / title messages |
raw translation keys, or English defaults where fallback is used |
| Custom sounds | silent / vanilla fallback |
| Death messages | raw keys |
| Trim visuals | vanilla trims only |
| Join experience | recurring warning on join |
Everything under mods/ is a Fabric mod built through the shared root Gradle
build (./gradlew buildAll) — except datapack-fixer, which keeps its own
standalone build. See §4.3 for the version matrix.
| Mod | Mod id | MC | Java | What it does |
|---|---|---|---|---|
| datapack-blocker | datapack-blocker |
1.21.1 | 21 | locks the world's datapacks/ folder; quarantines newcomers |
| datapack-fixer | datapack_fixer |
1.21.4 | 21 | server-safe datapack syntax diagnostics + repairs |
| rtwrapper-mod | rtwrapper |
1.21.1 | 21 | Fabric successor of the RTWrapper datapack |
| opmod-example | levelop |
26.3 | 25 |
/op <targets> <level> — permission-level aware op |
| TEMPLATE-MOD | (template) | 26.1.2 | 25 | copy-paste starting point for new mods |
📁
mods/datapack-blocker· MC 1.21.1 · Loom 1.15.5 · Yarn1.21.1+build.3· Java 21
Locks a world's datapacks/ folder instead of leaving it open to arbitrary
drop-in changes.
Lifecycle:
-
First run on a world — every top-level entry in
datapacks/(folder or.zip) is recorded as that world's allowlist. Nothing is blocked yet. -
Every run after — the folder is compared against the allowlist:
- anything not allowlisted is moved to
<world>/datapack_blocker/quarantine/<timestamp>/(never deleted); - everything allowlisted has write permission stripped recursively.
- anything not allowlisted is moved to
Commands — all under /datapackblocker, op-only (permission level 4):
| Command | Effect |
|---|---|
status |
list what's currently in quarantine |
approve <name> |
move a quarantined pack back, allowlist it, lock it (then /reload) |
unlock |
temporarily restore write access for maintenance |
lock |
re-lock after maintenance |
Scope and limitations — this is a tripwire for the common case (new pack dropped in, server restarts), not a hard security boundary:
- enforcement runs at server start; a pack picked up by a live
/reloadmid-session is not caught until the next restart; - "read-only" is best-effort via
File#setWritable— POSIX strips the directory write bit, Windows maps to the read-only attribute; - comparison is by name only, not content hashing — a pack edited before the mod's first run gets allowlisted as-is;
- anyone with shell access to the host is out of scope by definition.
Why it exists: it complements the packs/ maintenance-mode policy —
servers that want to keep running existing datapacks but stop accepting new
ones without review get that boundary enforced automatically instead of
relying on an operator remembering to check the folder by hand.
📁
mods/datapack-fixer· MC 1.21.4 · Loader ≥ 0.19.3 · Fabric API 0.119.4+1.21.4 · Java 21 ·⚠️ excluded from the shared Gradle build — has its own wrapper, GameTests and CI
A server-safe diagnostic mod for datapack syntax migration. It does not intercept datapack loading, patch packets, or change gameplay. On startup it reads unpacked datapacks and emits diagnostics to the log; the only file writes are the explicit operator-triggered repair, which always backs up first.
Supported diagnostics (deliberately narrow, syntax-oriented):
- malformed JSON;
- unbalanced quote / SNBT / item-component delimiters in
.mcfunction; - legacy plural data directories from before 1.21;
-
minecraft:type_specific/slime→minecraft:type_specific/cube_mob(26.2); -
minecraft:alternative→minecraft:any_of; - likely pre-1.21.2 recipe ingredient object syntax.
Commands (op-only, permission level 4):
/datapackfixer scan # diagnostics only
/datapackfixer fix # repair + automatic backup
/reloadRepairs and backups (v1.2.0): before any change, fix copies every
directory datapack to world/datapack_fixer_backups/<UTC timestamp>/ and
writes an audit.txt report there. It never processes ZIP/JAR datapacks.
The repair allowlist covers only deterministic transformations — singular
data directory names, the slime/alternative renames, and a missing 1.21.4
pack_format in otherwise valid pack.mcmeta. Invalid JSON, ambiguous
recipe conversions, legacy NBT/item-component rewrites and gameplay-semantic
migrations are reported but deliberately not rewritten.
Multiplayer safety: no mixins, no packets, no reload hooks, no client
entrypoints. Scanning is bounded to files < 2 MB. No automatic pack.mcmeta
format conversion — changing a compatibility declaration is not a
syntax-only repair.
26.2 migration policy: the runnable artifact is pinned to 1.21.4 because the 26.2 official Minecraft mappings were not resolvable by Loom in this build environment. A future 26.2 release must update Minecraft, mappings, Fabric API and test APIs together after official artifacts are available.
Build & test (standalone):
cd mods/datapack-fixer
./gradlew build # JUnit + Fabric server GameTests
./gradlew runClientGameTest
./gradlew runProductionClientGameTest📁
mods/rtwrapper-mod· MC 1.21.1 · Loom 1.15.5 · Yarn1.21.1+build.3· Java 21
The Fabric successor of the archived
RTWrapper datapack. Same mission — execute commands
described as data — implemented as a proper mod instead of a command-block
pipeline. The datapack API/storage protocol is documented in
packs/RTWrapper/docs/API.md
(storage rtwrapper:api request + named parameters, immediate run vs
queued enqueue).
📁
mods/opmod-example· MC 26.3 · Loom 1.15.5 · no mappings (unobfuscated) · Java 25
Extends vanilla /op with an explicit permission level:
| Command | Behaviour |
|---|---|
/op <targets> |
unchanged vanilla (level from op-permission-level) |
/op <targets> <level> |
new — grants op at level 1–4 regardless of server default |
Both require permission level 3, same as vanilla.
Tip
This subproject is the repo's reference implementation for MC 26.x mods:
Loom pinned to 1.15.5, fabric.loom.disableObfuscation=true, and plain
implementation dependencies (the mod* configurations don't exist with
obfuscation disabled). Its README also documents what to do if a future MC
drop needs a newer Loom than the rest of the monorepo — bump this
subproject's plugin version independently.
📁
mods/TEMPLATE-MOD· MC 26.1.2 · Loom 1.16-SNAPSHOT · Java 25
Copy-paste starting point for new mods targeting MC 26.x:
-
splitEnvironmentSourceSets()— separatemainandclientsource sets - named mod registration (
template-mod) wiring both source sets - no
mappingsdependency (unobfuscated target)
./gradlew verifyMod (also part of root check) validates every
fabric.mod.json under mods/ and examples/ (skipping the two
settings-excluded dirs):
- ✅ required fields present (
schemaVersion,id,version,name); - ✅ duplicate mod id detection;
⚠️ entrypoint classes exist as source files (warning);⚠️ declared mixin configs exist (warning);⚠️ rootbuild.gradledoesn't usemavenLocal()(reproducibility smell).
Hard failure only on real errors (invalid JSON / missing required fields).
📁
packs/guikit-datapack· MC Java 26.3 (min_format/max_format121) · 🔒 locked in CI
Menus are built in the world, not by writing .mcfunction files.
A chest minecart is the screen. Left-click is the click. Definitions live in
storage guikit:lib (saved with the world) and survive /reload.
Companions: demo pack
packs/guikit-demo
and the standalone JSON→GUI generator
guigenmc.
-
/trigger guikit.last— reopens the most recent menu you opened (survives/reload; auto-cleared if that menu is deleted). -
/trigger guikit.close— closes your own open menu immediately (no op, no waiting for timeout). Map makers also getfunction guikit:api/close_all. - Two new
guikit:condbutton conditions:dimensionandweather.
The cart / click / redraw engine underneath is the classic guikit runtime;
authoring moved off the old #guikit:fill / #guikit:probe framework into
the editor.
Install: drop the pack in datapacks/, /reload. On first load it seeds a
published Welcome menu so there's something to open.
Anyone (no operator permission):
/trigger guikit.open # opens the list of PUBLISHED menus
/trigger guikit.last # reopen your most recent menu
/trigger guikit.close # close your own open menu nowJoining players are told about guikit.open once. guikit.last remembers the
last menu opened through the list, a “go to another menu” button, or a preview.
/function requires permission level 2, so the editor is operator-only:
/function guikit:editor/open
/function guikit:help- Menu list — emerald creates a menu, a book opens it.
- Editor chest — slots 0–25 are the menu. The bottom-right book is Tools (not part of the menu). Slot 26 of a 27-slot menu is edited via Tools → Edit slot 26.
- Hold the desired icon item, left-click a slot, pick an action in the dialog.
- Tools: pages, rename, container, timeout, publish/hide, preview, delete.
Preview runs the real menu; chat suggests
/function guikit:editor/resume to return to the editor.
| Action | Effect |
|---|---|
| Decoration | icon only, not clickable |
| Give held item | gives that item; optional fixed cost (diamond / emerald / gold / iron) |
| Message | plain tellraw (interpret:false) |
| Close | closes the menu |
| Go to page | pages 0–8
|
| Open another menu | picker, no typed ids |
| Toggle | per-player on/off, gray dye when off |
| Counter +1 | per-player score, clamped, value printed in chat |
| Sound | one of six built-in sounds, only the clicking player hears it |
| Web link | clickable https link in chat; menu closes so chat is visible |
| Command | stored command — see §9.7 |
Set on the slot dialog after the action is placed; a button can carry more than one rule. Keep leaves a rule unchanged.
| Rule | Behaviour |
|---|---|
| Confirm | second click within 2 seconds |
| Cooldown | per-player 3 / 10 / 30 s (barrier icon while running) |
| Required tag |
vip / member / staff — granted by an operator via /tag <player> add vip (tags are never typed by players) |
Hand-written button packs (storage guikit:btn defs) can gate a button on a
condition. function guikit:cond/check reads storage guikit:cond and sets
#cond guikit.tmp:
| Type | Keys | Passes when |
|---|---|---|
score |
obj, [min], [max]
|
player's score is in range (unset = fail) |
item_count |
item, [min]
|
player carries at least min of the item (default 1) |
tag |
tag |
player has the tag |
dimension (v5)
|
dimension id | player is in that dimension |
weather (v5)
|
weather state | current weather matches |
Caution
A Command widget runs at datapack function permission (default 2),
as the clicking player, for any player who can open the menu — the same
trust as a command block behind a button. Do not put player-typed text in
it. The command dialog rejects nothing by itself: avoid apostrophes (')
in commands, or the save line will not parse.
Storage map (all world-persistent):
| Storage | Purpose |
|---|---|
storage guikit:lib |
menu definitions + order (/data get storage guikit:lib; never data remove it — menus re-register from it via guikit:runtime/register) |
storage guikit:mem |
small per-player memories, e.g. last.m<pid> for guikit.last
|
storage guikit:ed |
per-player editor state (p<pid>), survives close so dialogs can resume the editor |
storage guikit:btn |
hand-written button packs (defs) |
storage guikit:cond |
condition inputs for guikit:cond/check
|
Generated menu ids are visible in /data get storage guikit:lib order
(e.g. m1).
Small, single-purpose datapacks targeting modern Minecraft
(min_format/max_format [122, 1]). All are locked in CI and
maintained in lock-protocol mode: bug
fixes and compatibility only.
Copyright (c) 2026 Runtoolkit · MIT licensed (see each pack's
LICENSE/NOTICE) · each pack carries its_vc_origin.mcfunctionprovenance watermark.
📁
packs/BorderPing· "warns players approaching the world border" · namespaceborderping
Tiny tick-driven proximity detector for the world border:
data/borderping/function/
├── _vc_origin.mcfunction # provenance watermark
├── load.mcfunction # #minecraft:load
├── tick.mcfunction # #minecraft:tick
└── warn.mcfunction # proximity warning
No commands, no configuration — it hooks #minecraft:load and
#minecraft:tick via the vanilla tags in data/minecraft/tags/.
📁
packs/DailyBonus· "once-per-in-game-day login reward" · namespacedailybonus
Grants players a reward once per in-game day, driven by a loot table so
server owners can re-roll the reward pool without touching functions:
load.mcfunction · tick.mcfunction · grant.mcfunction + loot_table/.
📁
packs/QuickShare· "per-player item sharing helper" · namespacequickshare
Per-player item sharing between players, wired through
#minecraft:load/#minecraft:tick function tags.
Warning
The packs in this section are archived: existing worlds keep working, but no new features or fixes are planned. The repo's direction is Fabric mods — see §19.1.
📁
packs/TunnelScript· MC 1.19.2 (pack_format 10) · namespacets· ships its ownCHANGELOG.md,CONTRIBUTING.md,SECURITY.md
A small command-running library: instead of copy-pasting
execute/function boilerplate, you hand TunnelScript a command (or a
batch) and it runs them with:
- ✅ permission gating
- ✅ confirmation step for dangerous actions
- ✅ safety rails: cooldowns, action cap, dry-run preview
Because 1.19.2 predates function macros (1.20.2+), there is no dynamic
$function $(...) dispatch — commands are captured and run through a
command-block-based runner.
Quick start: install → /reload → /function ts:version → /function ts:help.
Executes commands stored in storage using a temporary command-block
pipeline: storage-driven execution, function-tag dispatch hooks, lightweight
and fully datapack-based, designed for advanced automation. Requires operator
permissions and enable-command-block=true.
📁
packs/inv_gui· MC 1.20.3–1.20.4 (pack_format 26) · CC0-1.0 · v1.0.0
Inventory-based GUI menu library: 27-slot menus (Chest Minecart or Ender
Chest), button / normal / variable item types, click & drop event detection,
listener-based callbacks, mid-session menu swap (set_menu), plus
util/map + util/array helpers.
Caution
Licensing caveat: inv_gui entered the monorepo as a renamed fork of
rarula/Sketch whose licensing relative
to upstream was never cleared. Use is not recommended until that is
resolved; it is kept for reference and possible reimplementation only.
1.21+ support was planned via the overlay system but never shipped.
📁
packs/LeftClickDetection· MC 1.20.3+ · namespaceleft_click_detection
function left_click_detection:api/enable # enable for the executing player📁
packs/InteractionClickDetection· MC 1.20+
Lightweight, macro-free click detection on minecraft:interaction
entities using vanilla advancements:
- ✅ left click (
player_hurt_entity) - ✅ right click (
player_interacted_with_entity) - ✅ no macros, very lightweight (advancements + tag functions)
- ✅ easy to embed into other datapacks; multiplayer-safe
Three historical snapshots of RTWrapper ("Runtoolkit Wrapper") — execute commands described as data:
| Folder | Era | Notes |
|---|---|---|
packs/RTWrapper |
MC 26.3 | datapack + rtwrapper-fabric/ (excluded from the build) + docs/API.md
|
packs/RTWrapper-1.21.1 |
MC 1.21.1 | versioned snapshot |
packs/RTWrapper-v1.0.1 |
v1.0.1 | versioned snapshot |
Namespace rtwrapper; immediate execution uses
storage rtwrapper:api request + rtwrapper:api/run:
# /tp @s 0 80 0 immediately
data modify storage rtwrapper:api request set value \
{cmd:"tp",params:{target:"@s",x:"0",y:"80",z:"0"}}
function rtwrapper:api/run
# queued / autotick execution instead
data modify storage rtwrapper:api request set value \
{cmd:"say",params:{message:"queued hello"}}
function rtwrapper:api/enqueue-
typeis accepted as an alias forcmd. - Generated wrappers use meaningful command-specific parameter names
(no
$(tail)catch-all); parameter order is recorded per command indatapack/commands-26.3.jsonundercommand_params— e.g.tp → [target, x, y, z, rotation, facing_mode, facing_target, facing_anchor]. - The dispatcher calls the exact
<command>_<N>variant and doesn't append unused params.
Successor: mods/rtwrapper-mod — the same idea as a
Fabric mod; packs/RTWrapper/rtwrapper-fabric is kept only for historical
reference and excluded from the Gradle build.
📁
examples/itemExampleMod· MC 26.1.2 · Loader ≥ 0.18.4 · Fabric API0.148.2+26.1.2· Loom 1.15.5 · Java 25 · CC0
The runnable example Fabric mod — also the template the Fabric docs point to. Highlights:
-
splitEnvironmentSourceSets()with a named mod (customcmd) wiringmain+clientsource sets; - archives name
custom-cmd; - unobfuscated target: no
mappingsdependency, plainimplementationdependencies; - ships its own wrapper,
.github/andREADME.md(CC0 — learn from it and incorporate it in your own projects).
Build like any subproject: ./gradlew :examples:itemExampleMod:build.
A minimal datapack skeleton (data/, pack.mcmeta, .gitignore, CI
stubs) to copy when starting a new pack. Not covered by the same security
posture as mods//packs/ — it is a starting point, not a shipped artifact.
Standalone tooling under scripts/.
These are not part of the Gradle build — each has its own runtime.
Note
Per SECURITY.md: treat scripts/ as lower trust by default — review
before running locally.
| Tool | Language | Purpose |
|---|---|---|
| dp-depman | Python 3.12+ (stdlib only) | datapack dependency manager |
| dpgen | Node.js | config-driven datapack generator |
| guigenmc | Python 3.9+ | JSON → GUI datapack generator (CLI + web UI) |
| datapack_risk_scan.py | Python 3 | read-only security audit of datapacks |
| datapack_lock | Python 3 | CI checker behind .datapack-lock.json
|
Dependency management for Minecraft Java Edition datapacks: declare
dependencies in datapack_depends.json, resolve from GitHub Releases or
git submodules, and produce installable ZIPs — locally or in GitHub
Actions.
Requirements: Python 3.12+ (stdlib only — no pip install), Git for
submodule support, and a GITHUB_TOKEN env var for Releases resolution.
// datapack_depends.json
{
"id": "my-pack",
"version": "1.0.0",
"dependencies": {
"dataLib": {
"source": "github",
"repo": "vortacraftmc/core",
"version": ">=26.2.0",
"asset": "dataLib.zip"
}
}
}A lockfile (datapack_depends.lock) pins resolved versions; example-pack/
and docs/ show complete usage.
📁
scripts/dpgen· Node.js (builder.js,schema.js,templates.js)
Config-driven datapack generator for the CLI. Point it at a YAML/JSON file
describing namespaces, functions, advancements, recipes, loot tables,
predicates and item modifiers — it builds a ready-to-use datapack .zip.
Targets modern Minecraft (26.x, min_format/max_format pack.mcmeta).
Why not a browser generator?
- ✅ validation before build — invalid namespaces, duplicate function
paths, dangling predicate refs, inconsistent
min_format/max_formatfail loudly atvalidate/buildtime; - ✅ multiple namespaces, multiple tick/load functions — no
single-function ceiling; every declared
tick/loadfunction is merged into the right tag file; - ✅ macro function support — generates the
$(param)skeleton; - ✅ configs are scriptable, diffable, version-controllable.
cd scripts/dpgen
npm ci
node bin/dpgen.js build path/to/config.yaml📁
scripts/gui_generator· Python 3.9+ · tested in CI bytest-guigenmc.yml
JSON → Minecraft GUI datapack generator with two frontends:
- 🖥️ CLI — generate from a config file in the terminal
- 🌐 Web UI — visual editor in your browser
Works on Linux, macOS, GitHub Codespaces and GitHub Actions. No extra packages beyond Python 3.9+.
cd scripts/gui_generator
pip install -e .
guigenmc ui # visual editor (browser)
guigenmc generate my_menu.json # from JSON
guigenmc generate my_menu.json -z # …as a .zipPairs naturally with GUIKit: guigenmc produces the datapack side, guikit runs menus in-world.
A read-only auditing tool for datapacks. Scans .mcfunction (and
function-tag .json) files for command patterns known to cause real-world
problems — server/client lag, world/data corruption, griefing potential — and
produces a Markdown report with:
- file + line number
- the offending line
- why it's risky
- a concrete, safer alternative snippet you can paste in instead
python3 scripts/datapack_risk_scan.py path/to/datapack --out report.mdIt never modifies, blocks or disables anything — it exists to save you reading
every .mcfunction by hand before merging or trusting a third-party pack.
The checker executed by the datapack-immutability.yml workflow: loads
packs/.datapack-lock.json and itself from the PR's base branch, then
fails the PR if it touches files under any locked: true pack. Design
rationale (no pull_request_target, no paths: filter) is documented in
§5.3.
The root Gradle task mergeDatapacks fuses several datapacks into one
datapack — with a gated load/tick system, a kill-switch, toggleable debug and
load-error checking. Its behaviour is driven by
packs/merge-manifest.json.
./gradlew mergeDatapacks # uses packs/merge-manifest.json
./gradlew mergeDatapacks -PmergeManifest=path/to/other.json
./gradlew mergeDatapacks -PmergeReport=build/merge-report.json
./gradlew build # root build = merge + copy to build/dist/Only output + include are required; everything else shows its default:
{
"output": {
"name": "merged-datapack",
"description": "Merged vortacraftmc datapack",
"min_format": 121,
"max_format": 121
},
"include": ["guikit-datapack", "macroEngine-Datapack-v26.4"],
"exclude": [],
"allowMismatchedFormats": false,
"gatedLoad": true,
"namespace": "vcm_core",
"debugDefault": false,
"killSwitch": true,
"loadErrorHandling": true,
"strictLoadRefs": true,
"zip": true
}| Key | Default | Meaning |
|---|---|---|
output.name / .description
|
— | merged pack identity; also the zip name |
output.min_format / .max_format
|
— |
required for format 82+ (25w31a+); pack_format is legacy only |
include |
— | packs to merge (directories under packs/) |
exclude |
[] |
explicit exclusions |
allowMismatchedFormats |
false |
fail when included packs declare different formats |
gatedLoad |
true |
route every pack's minecraft:load/tick hooks through a generated gatekeeper; false = legacy plain union-merge of the load/tick tags |
namespace |
vcm_core |
namespace for generated gatekeeper files; lowercase [a-z0-9_.-]+, never minecraft
|
debugDefault |
false |
initial debug-message state (runtime-toggleable) |
killSwitch |
true |
also generate the global enable/disable kill-switch (needs gatedLoad) |
loadErrorHandling |
true |
build-time load/tick reference validation + runtime error log + per-pack load markers |
strictLoadRefs |
true |
load/tick hooks that resolve to nothing fail the build; false = warn only |
zip |
true |
also write build/<output.name>.zip next to build/merged-datapack/
|
With gatedLoad: true (default), each included pack's #minecraft:load and
#minecraft:tick hooks are re-wired through a generated <namespace>:load /
<namespace>:tick gatekeeper:
flowchart LR
V["#minecraft:load / #minecraft:tick"] --> GK["vcm_core gatekeeper"]
GK -->|"per-pack toggle ON"| P1[pack A load/tick]
GK -->|"toggle OFF"| X1((skipped))
GK --> P2[pack B load/tick]
GK -. debug + status .-> LOG[debug log / status]
Result: per-pack toggles, centralized debug output and status, and a single place to observe what loaded.
Generated runtime controls (defaults killSwitch: true, debugDefault: false):
/function vcm_core:enable_all # global kill-switch: everything on
/function vcm_core:disable_all # everything off
/function vcm_core:debug/on # debug messages on
/function vcm_core:debug/off
/function vcm_core:debug/toggleWith loadErrorHandling: true:
-
build-time — every
load/tickreference is validated; dangling refs fail the build whenstrictLoadRefs: true(warn otherwise); -
runtime —
api/report_error,errors,errors_clearentry points record and inspect load errors; - per-pack load markers feed the status view, so you can see which packs actually initialized.
| Path | Content |
|---|---|
build/merged-datapack/ |
exploded merged datapack |
build/<output.name>.zip |
zipped merged datapack (when zip: true) |
build/dist/ |
root build task copies both here so they survive clean
|
-PmergeReport=<path> |
JSON run summary (machine-readable) |
Tip
Merging macroEngine with other packs? Keep the resource pack installed — merging cannot bundle resource-pack assets into a datapack.
-
pack_format— the classic single-number pack format (legacy-only for modern releases). -
min_format/max_format— the pair-based declaration introduced for modern releases; required from format 82+ (25w31a+). Values may be plain numbers or[major, minor]pairs. -
Datapack vs resource pack formats differ — the same MC release has
different numbers for each (e.g. macroEngine v26.4: datapack
[122, 0], resource pack[98, 0]).
// datapack pack.mcmeta (macroEngine v26.4)
{ "pack": { "min_format": [122, 0], "max_format": [122, 0] } }| Pack | Kind | MC target | Format |
|---|---|---|---|
| macroEngine-Datapack-v26.4 | datapack | 26.4-snapshot-1 | [122, 0] |
| macroEngine-Datapack-v26.3 | datapack | 26.3 | 121 |
| macroEngine-Resourcepack-v26.4 | resource pack | 26.4-snapshot-1 | [98, 0] |
| guikit-datapack / guikit-demo | datapack | 26.3 | 121 |
| BorderPing / DailyBonus / QuickShare | datapack | modern 26.x | [122, 1] |
| RTWrapper (datapack) | datapack | 26.3 | format 104 (as documented in its API.md)
|
| TunnelScript | datapack | 1.19.2 | 10 |
| cmdTunnel-datapack | datapack | classic | — |
| inv_gui | datapack | 1.20.3–1.20.4 | 26 |
| LeftClickDetection | datapack | 1.20.3+ | — |
| InteractionClickDetection | datapack | 1.20+ | — |
| Mod | MC | Fabric Loader | Fabric API | Java | Loom |
|---|---|---|---|---|---|
| datapack-blocker | 1.21.1 | ≥ 0.16.14 | any | 21 | 1.15.5 + Yarn |
| datapack-fixer | 1.21.4 | ≥ 0.19.3 | 0.119.4+1.21.4 | 21 | standalone build |
| rtwrapper-mod | 1.21.1 | ≥ 0.16.14 | any | 21 | 1.15.5 + Yarn |
| opmod-example (LevelOp) | 26.3 | ≥ 0.19.5 | 26.3 line | 25 | 1.15.5, no mappings |
| TEMPLATE-MOD | 26.1.2 | 26.x line | 26.x line | 25 | 1.16-SNAPSHOT |
| examples/itemExampleMod | 26.1.2 | ≥ 0.18.4 | 0.148.2+26.1.2 | 25 | 1.15.5 |
Important
MC 26.1+ is unobfuscated. Mods targeting 26.x must not declare a
mappings dependency and should set
fabric.loom.disableObfuscation=true — full explanation in
§4.3 and §18.
-
./gradlew checkPacksvalidates everypack.mcmeta, JSON file and cross-reference before anything is packaged (zipPacksdepends on it). -
./gradlew printPackFormatsprints the declared formats of every pack — handy when bumping a pack to a new MC release. -
mergeDatapacksrefuses to merge packs with mismatched formats unlessallowMismatchedFormats: true(§14).
Read NOTICE.md
before opening a PR. This section summarizes the mechanics.
| Area | Rule |
|---|---|
mods/ |
Must build cleanly across all subprojects — a shared dependency bump can silently break a sibling mod |
packs/ |
Not part of the Gradle build; a green buildAll says nothing about a datapack change. Maintenance mode: fixes only |
scripts/ |
Lower trust by default — review before running |
examples/ |
Templates/samples; not shipped, lighter security posture |
| cross-cutting changes | If a change spans a mod and a pack, say so explicitly in the PR description — CI only covers the Gradle side |
-
Fork or clone, create a branch (
git checkout -b fix-gui-crash). No strict naming convention — describe the change. -
Make your changes; keep commit messages short and descriptive.
-
Build and lint before opening the PR:
./gradlew buildAll ./gradlew lint
-
Open the PR against
main; CI must pass before requesting review. -
Describe what changed and why, and whether it touches
mods/,packs/, or both. -
Work through the pre-PR checklist (below).
Before opening a PR — checklist from NOTICE.md:
-
./gradlew buildAllpasses -
./gradlew lintpasses - If you touched
packs/, you've validated the pack loads correctly (Gradle passing does not cover this) - PR description explains what changed and why, and says whether the change touches mods, packs, or both
- No unexplained dependencies, obfuscation, or behavior not covered by the description
Every existing top-level directory under packs/ is locked: true in
packs/.datapack-lock.json, enforced by CI. To change a locked pack you need
three PRs:
sequenceDiagram
participant A as Author
participant CI as CI (immutability)
participant M as Maintainers
A->>M: PR 1: set pack "locked": false
M-->>A: reviewed & merged
A->>CI: PR 2: the actual fix
CI-->>M: passes (pack unlocked)
M-->>A: reviewed & merged
A->>M: PR 3: set pack "locked": true again
M-->>A: merged — pack frozen again
Notes:
- The lock file and the checker script are read from the base branch, so PR 1 must be merged first — you cannot unlock and fix in one PR.
- New top-level pack directories pass through (that's how packs are added) but must be added to the lock file, locked, in a prompt follow-up PR.
Every datapack carries _vc_origin.mcfunction under
data/<namespace>/function/:
-
zipPackstreats a missing watermark as a build failure. - Do not remove, rename or hand-edit watermarks in
packs/in your PR. - Only
build.gradle(via itsbuild/packs-remap/working copy, neverpacks/) and maintainer tooling are expected to touch them. - Conduct expectation, not a license restriction — the Unlicense still governs copies once obtained.
- Java: clear and documented; complex logic needs intent-explaining comments.
- Avoid unnecessary dependencies — every dependency is shared build surface for the whole monorepo.
- Fabric mod code must remain tick-safe (project TPS guidelines).
- Datapacks: respect namespace isolation — don't introduce function/tag names that can collide with another pack in this repo or common third-party packs.
- Macro-heavy or command-generation changes get closer review — call them out explicitly in the PR.
From NOTICE.md — explicitly prohibited:
- ❌ building griefing tools, cheat clients/mods, exploit kits, or anything designed to bypass another server's protections without authorization;
- ❌ PRs adding obfuscated code, hidden network calls, backdoors, or any behavior not clearly described in the PR (closed without further discussion, regardless of technical quality);
- ❌ using this repo's macro/command-generation systems to inject unsafe or attacker-controlled input into commands on a target server;
- ❌ posting working exploits in public PR descriptions — use the private
process in
SECURITY.mdfirst.
Repeat or clearly malicious cases get blocked from the repo. Feature work against archived packs is also out of scope.
This project follows a security-first development philosophy, especially around datapack macro-injection risks and namespace isolation.
Report privately — never as a public issue:
- Preferred: GitHub private vulnerability reporting
- Otherwise open an issue with minimal detail ("possible command injection in X, details sent privately") and maintainers will follow up.
Include: affected project/path · reproduction steps or minimal example · impact (what an attacker could actually do) · Minecraft/Fabric/Loom version if relevant.
Reports are acknowledged as soon as possible, with a fix or mitigation aimed for a timeframe matching severity. Low-risk findings in archived/frozen projects may be documented rather than fixed.
| Area | Status |
|---|---|
mods/ |
Actively maintained Fabric mods. Compiled, type-safe. |
packs/ |
Datapacks — some actively maintained, some archived and frozen |
scripts/ |
Helper tooling. Lower trust; review before running. |
examples/ |
Templates and samples. Not production-intended. |
archived/ |
Reserved for no-longer-maintained projects — reference only (currently empty) |
Where practical, security-sensitive logic lives in a Fabric mod rather than a datapack.
Note
"Fabric is a hacker tool" — false. Fabric is a standard mod loader in the same category as Forge/NeoForge/Quilt. Tools are neutral; judge a specific mod by its source and behavior.
Note
"Datapacks are safe, mods are unsafe" — a false framing. The one real
technical difference: datapacks cannot execute arbitrary Java bytecode —
declarative JSON plus constrained mcfunction, interpreted by the vanilla
command engine. Mods are compiled Java running with server-process
privileges. That changes the shape of the attack surface, not the safety:
- Datapacks can still degrade or crash a server: unbounded
functionrecursion, runaway entity/marker spawning, unboundeddata storagegrowth — exactly the failure classes macroEngine's own cleanup work (_item_tmp/rl_workscratch-storage fixes) exists to prevent. - Datapacks can corrupt gameplay state (scoreboards, NBT, player data) as easily as badly written mods — both mutate the same world data.
- "Cannot run arbitrary Java" is a ceiling on what kind of damage is possible, not a guarantee that none is.
Note
Mod safety is a function of source, not mechanism. An unsigned mod from
an unmoderated site is high-risk — same as any unreviewed binary. A mod on
CurseForge/Modrinth with real download counts and scrutiny has had far more
eyes on it than the average privately-shared datapack. And a .zip of
obfuscated mcfunction can hide destructive logic just as well.
| Tool | Role |
|---|---|
| datapack-blocker | tripwire for unapproved datapacks dropped into a world |
| datapack-fixer | server-safe syntax diagnostics; backups before any write |
| datapack_risk_scan.py | read-only audit of .mcfunction risk patterns |
.datapack-lock.json + immutability CI
|
frozen packs can't be silently edited |
_vc_origin.mcfunction watermarks |
provenance tracking for every pack |
| macroEngine rate limiting | throttles in-engine request floods |
Known failure modes and their fixes, ordered by how often they bite.
A problem occurred configuring project ':mods:<name>'.
> Failed to setup Minecraft, java.lang.RuntimeException:
Failed to find official mojang mappings for 26.3
Cause: Minecraft 26.1+ ships unobfuscated. Mojang no longer publishes
official mappings for those versions, so loom.officialMojangMappings() can
never resolve. This is not a Loom bug and not a network problem.
Fix (the pattern used by mods/opmod-example):
dependencies {
minecraft "com.mojang:minecraft:${project.minecraft_version}"
- mappings loom.officialMojangMappings()
- modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
- modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
+ implementation "net.fabricmc:fabric-loader:${project.loader_version}"
+ implementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
}# <subproject>/gradle.properties
fabric.loom.disableObfuscation=trueWarning
Do not try to fix this by changing the Loom version — it won't help.
Loom 1.15.5 is the minimum version with unobfuscated-jar support
(MinecraftJarProcessor.isUnobfuscated()), which is why the monorepo pins it.
Also: MC 26.x renamed several classes. If compilation then fails with
cannot find symbol, check the merged jar — e.g. ResourceLocation →
Identifier, LoadingOverlay.LOGO → MOJANG_STUDIOS_LOGO_LOCATION.
Plugin [id: 'fabric-loom', version: '1.17'] was not found in any of the
following sources: … could not resolve plugin artifact
'fabric-loom:fabric-loom.gradle.plugin:1.17'
Cause: Fabric publishes Loom per-patch only. A bare 1.17 has never
existed on maven.fabricmc.net — the real series is 1.17.0 … 1.17.21,
then 1.18.x.
Fix: pin the nearest real version, keeping consistency with sibling subprojects where possible:
-id 'fabric-loom' version '1.17'
+id 'fabric-loom' version '1.17.21'Browse what actually exists: https://maven.fabricmc.net/fabric-loom/fabric-loom.gradle.plugin/maven-metadata.xml
Previous process has disowned the lock due to abrupt termination.
Found existing cache lock file (ACQUIRED_PREVIOUS_OWNER_DISOWNED),
rebuilding loom cache.
Not fatal — Loom rebuilds its cache automatically. If it keeps reappearing:
- Stop all Gradle daemons:
./gradlew --stop(and kill strayGradleDaemonJVMs). - Delete the stale cache for that MC version:
rm -rf ~/.gradle/caches/fabric-loom/<minecraft-version>(or yourGRADLE_USER_HOME). - Rebuild. Avoid Ctrl-C'ing Loom mid-setup; it's the usual source of the disowned lock.
Symptoms: OutOfMemoryError, Java heap space, GC overhead limit exceeded
— typically during remapJar when several Loom subprojects share one daemon.
Fixes:
-
The root
gradle.propertiesalready sets-Xmx4G -XX:MaxMetaspaceSize=1g; bump further if you add more Loom subprojects. -
On small machines (< 4 GB RAM), override instead:
./gradlew buildAll --max-workers=1 \ -Dorg.gradle.jvmargs="-Xmx1500m -XX:MaxMetaspaceSize=512m" -
Beware subproject-local
gradle.propertiesjvmargs: they only apply when that subproject is built as its own root; in the monorepo the root file wins.
zipPacks requires explicit confirmation …
Cause: packaging is gated on purpose. Fix: pass -PconfirmZip=yes
(the sanctioned non-interactive path; CI uses it), or answer the interactive
prompt. A missing _vc_origin.mcfunction watermark also fails zipPacks —
restore the watermark rather than bypassing the check.
By design — v26.3 is archived and its engine refuses to load unless you opt in:
/data modify storage macroengine:engine config.allow_unsupported_load set value 1bStatic content (recipes, loot tables, advancements, predicates, item modifiers, enchantments) works either way. The real fix: move to macroEngine v26.4 (§6).
You're missing the companion resource pack: messages fall back to raw
macroengine.* translation keys and players get a recurring join warning.
Install/enforce it per §7.3.
With fabric.loom.disableObfuscation=true, Loom's
RemapConfigurations.setupForSourceSet returns early and never creates the
mod* configurations — modImplementation silently no-ops or fails to
resolve. Use plain implementation / compileOnly / runtimeOnly
(§18.1).
Some forks of this repo ship an optional companion script
(scripts/ai-fixer/fixer.py) that automates exactly the fixes above: a
deterministic rule engine handles the known failure modes (mappings-for-26.x,
nonexistent Loom versions, missing yarn/fabric-api/loader versions, OOM), and
a local LLM (Ollama / any OpenAI-compatible server) is consulted only for
anything unknown. It is dry-run by default, validates every proposed edit,
and backs up files before writing. If your copy of the repo doesn't include
it, the manual fixes in this section are canonical.
The project no longer ships or accepts new vanilla datapacks; development moved to Fabric mods (Java, Gradle). Reasons:
- Datapacks run inside the vanilla command system — they can't execute native
code or touch the filesystem/network directly, but they can still cause
real problems: lag from unbounded loops, world/data corruption via
data merge, griefing via entity/inventory manipulation — all hard to review at scale and easy to hide in large command trees. - Fabric provides compiled, versioned, statically checkable code, proper dependency management, and CI tooling (tests, static analysis, dependency scanning) that datapacks don't support.
Contributors: new features belong in a Fabric mod under mods/. To port an
existing datapack, open an issue and maintainers can help convert the logic.
That framing is false — see the full risk model in §17.3. The one real technical difference is that datapacks can't run arbitrary Java bytecode, which caps what kind of damage is possible — but datapacks can still lag, crash and corrupt a server through purely in-game mechanisms. Actual safety depends overwhelmingly on the trustworthiness of the source, not the format.
Partly true, overstated:
- ✅ Getting started is easier with datapacks: no language, no build
chain, edit →
/reload→ see result. - ❌ Scaling is where it flips.
mcfunctionhas no first-class functions, no real data structures beyond NBT, and control flow built fromexecute if/unlesschains. Features that are dozens of lines of Java become hundreds of lines ofmcfunctionspread across many files — macroEngine's rate limiter, cooldown system and cleanup routines are the repo's own evidence. - ❌ Debugging: a mod stack trace tells you the failing line; a silently
failing
executechain just does nothing. - ❌ State management is manual in
mcfunction(scoreboards/storage paths), versus scoped, garbage-collected locals in Java.
Datapacks are easier to start and read small; mods scale far better once you're past the entry cost.
- ✅ In scope: bug fixes, security hardening, compatibility fixes for new Minecraft versions (pack_format bumps, syntax migrations), small QoL updates to existing modules.
- ❌ Out of scope: new modules, new systems, or feature work that expands what a pack does. If a change would need a new top-level system comparable to macroEngine's rate-limit or hidden-flag modules, it doesn't belong here.
- Rationale: the packs (especially macroEngine) have grown large enough that further feature growth trades off against maintainability.
Applies to packs/ only — mods/, scripts/, etc. are unaffected.
Not as new functionality — see maintenance mode. Fixes are welcome but must go through the lock protocol because every pack is locked.
Because the packs are frozen artifacts with many downstream users. Silent edits to a locked pack would change behavior for every world using it. The three-PR protocol makes every change deliberate, reviewed and auditable. New packs being added aren't blocked — only modifications to existing locked trees.
- Targeting MC 1.21.x (obfuscated, Yarn): pin 1.15.5 like
datapack-blocker/rtwrapper-mod. - Targeting MC 26.x (unobfuscated): 1.15.5 is the minimum with
unobfuscated-jar support; check
opmod-example/TEMPLATE-MODfor current pins, and never use a bare series number like1.17— only per-patch versions exist (§18.2).
The runtoolkit organization was retired in favour of vortacraftmc.
Its repos are archived and point to this monorepo. The original project was
runtoolkit/suite by Runtoolkit; copyright notices are preserved in each
LICENSE/NOTICE (§1).
Not recommended. Licensing relative to upstream (rarula/Sketch) was never cleared. Kept for reference and possible reimplementation only — see §11.3.
Privately — GitHub's private vulnerability reporting, never a public issue. Full policy: §17.
| Term | Meaning in this repo |
|---|---|
| Allowlist | The set of datapacks recorded by datapack-blocker on first run; anything else is quarantined |
| Gatekeeper | Generated <namespace>:load/tick layer that mergeDatapacks inserts between vanilla tags and each merged pack |
| Hook | A function bound to a gameplay event via macroengine:systems/hook/bind
|
| Kill-switch |
vcm_core:enable_all / disable_all — global on/off for a merged datapack |
| Loom | Fabric's Gradle plugin; compiles, remaps and runs Minecraft mods |
| Macro (mcfunction) | A .mcfunction file with $(param) placeholders, invoked with arguments (MC 1.20.2+) |
| Maintenance mode |
packs/ policy: fixes only, no new features |
| Namespace isolation | Keeping each pack's function/tag names inside its own namespace to avoid collisions |
| Provenance watermark |
_vc_origin.mcfunction file identifying a pack's origin; required by zipPacks
|
| Quarantine |
datapack_blocker's holding area for unapproved packs (never deleted) |
| Sliding window | macroEngine rate-limit mechanism: max N hits within the last W ticks |
| Tripwire | A control that detects/alerts rather than fully preventing — datapack-blocker's stated scope |
| Unobfuscated (MC 26.1+) | Minecraft ships final class/field/method names; no Yarn/Mojang mapping layer exists |
| Watermark check |
showOriginWatermarks / checkOriginWatermarks Gradle tasks |
📚 vortacraftmc/core wiki — content licensed under the
Unlicense unless
noted otherwise in a subproject's own LICENSE/NOTICE (some components are
MIT or CC0). Original work by Runtoolkit · maintained by vortacraftmc.
Repository ·
Issues ·
Report a security issue
{ "name": "vortacraftmc-core", "features": { "ghcr.io/devcontainers/features/github-cli:1": {}, "ghcr.io/devcontainers/features/common-utils:2": { "configureZshAsDefaultShell": true } }, "postCreateCommand": "bash setup.sh", // provisions the toolchain "postStartCommand": "bash start.sh", "customizations": { "vscode": { "extensions": [ "misode.vscode-datapack-helper", "SPGoding.datapack-language-server", "redhat.java", "vscjava.vscode-java-pack", "vscjava.vscode-gradle" ]}} }