From c4f44dd88145ee4d24b89f440fa22e2932b807a5 Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:25:52 +0300 Subject: [PATCH 01/19] Delete .github/workflows/datapack-fixer.yml --- .github/workflows/datapack-fixer.yml | 152 --------------------------- 1 file changed, 152 deletions(-) delete mode 100644 .github/workflows/datapack-fixer.yml diff --git a/.github/workflows/datapack-fixer.yml b/.github/workflows/datapack-fixer.yml deleted file mode 100644 index a0fb238c..00000000 --- a/.github/workflows/datapack-fixer.yml +++ /dev/null @@ -1,152 +0,0 @@ -name: Datapack Auto Fixer (Safe Mode) - -on: - workflow_dispatch: - inputs: - base_branch: - description: 'Base branch to fix' - required: true - default: 'main' - fix_branch: - description: 'Fix branch name' - required: true - default: 'datapack-fixer/main' - warn_mode: - description: 'Fail on warnings?' - required: true - default: 'fail' - type: choice - options: - - 'fail' - - 'warn' - - schedule: - - cron: '0 3 * * *' - -env: - PYTHONUNBUFFERED: '1' - MAX_FILES: 10000 - -jobs: - safe-fix: - runs-on: ubuntu-latest - if: github.repository == 'runtoolkit/dataLib' - permissions: - contents: write - actions: read - - steps: - - name: Checkout base branch - uses: actions/checkout@v4 - with: - ref: ${{ github.event.inputs.base_branch || 'main' }} - token: ${{ secrets.GITHUB_TOKEN }} - fetch-depth: 5 - - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install Python dependencies - run: | - python -m pip install --upgrade pip - python -m pip install beet mecha - - - name: Create isolated workspace - run: | - mkdir -p /tmp/datapack-fixer/{original,build,patched} - cp -r . /tmp/datapack-fixer/original - - - name: Run Safe Datapack Fixer - id: fixer - timeout-minutes: 10 - env: - WARN_MODE: ${{ github.event.inputs.warn_mode || 'fail' }} - run: | - python3 scripts/safe_datapack_fixer.py \ - --base /tmp/datapack-fixer/original \ - --build-dir /tmp/datapack-fixer/build \ - --patched-dir /tmp/datapack-fixer/patched \ - --warn-mode "$WARN_MODE" \ - --max-files ${{ env.MAX_FILES }} - - # ========================================== - # REAL FABRIC GAMETEST (Server + Client) - # ========================================== - - name: Setup Java 21 - if: steps.fixer.outputs.changes_detected == 'true' - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: '21' - - - name: Setup Gradle - if: steps.fixer.outputs.changes_detected == 'true' - uses: gradle/actions/setup-gradle@v4 - with: - gradle-version: 8.8 - - - name: Run Fabric GameTest (Server + Client + Fail Detection) - if: steps.fixer.outputs.changes_detected == 'true' - timeout-minutes: 20 - run: | - echo "=== SERVER GameTest ===" - ./gradlew runGameTestServer --info || true - - echo "=== CLIENT GameTest ===" - ./gradlew runClientGameTest --info || true - - echo "=== Checking for Fail / Failed to load ===" - - # Fail: ile başlayan mesaj kontrolü - if grep -r "Fail:" build/ 2>/dev/null; then - echo "::error::FAIL message detected in GameTest" - exit 1 - fi - - # Failed to load hataları - if grep -r "Failed to load" build/ 2>/dev/null; then - echo "::error::'Failed to load' errors detected" - exit 1 - fi - - echo "=== GameTest completed successfully ===" - - - name: Prepare safe commit - if: steps.fixer.outputs.changes_detected == 'true' - run: | - cd /tmp/datapack-fixer/original - SAFE_FILES=() - while IFS= read -r -d '' file; do - if [[ "$file" == data/* || "$file" == pack.mcmeta || "$file" == beet.json ]]; then - SAFE_FILES+=("$file") - fi - done < <(find . -type f -print0) - - if [ ${#SAFE_FILES[@]} -gt 0 ]; then - git add "${SAFE_FILES[@]}" - fi - - - name: Create fix branch and commit - if: steps.fixer.outputs.changes_detected == 'true' - run: | - FIX_BRANCH="${{ github.event.inputs.fix_branch || 'datapack-fixer/main' }}" - cd /tmp/datapack-fixer/original - git checkout -B "$FIX_BRANCH" - - if ! git diff --cached --quiet; then - git commit -m "Auto-fix: Mecha + 1.21.5+ compat + GameTest validated" - fi - - - name: Push fix branch - if: steps.fixer.outputs.changes_detected == 'true' - run: | - FIX_BRANCH="${{ github.event.inputs.fix_branch || 'datapack-fixer/main' }}" - cd /tmp/datapack-fixer/original - git push -u origin "$FIX_BRANCH" --force-with-lease || echo "::warning::Push failed (branch protection?)" - - - name: Final status - run: | - echo "=== Datapack Auto Fixer completed ===" - echo "Changes: ${{ steps.fixer.outputs.changes_detected }}" From 363d024f9f1bf6af212fe6a29c28fc4a03aae826 Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:26:20 +0300 Subject: [PATCH 02/19] Delete gradle/wrapper directory --- gradle/wrapper/gradle-wrapper.properties | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 gradle/wrapper/gradle-wrapper.properties diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index dab2a015..00000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists \ No newline at end of file From 4c9bc711e7b74ad715ef5afdfc1712b500a6a4f9 Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:26:36 +0300 Subject: [PATCH 03/19] Delete scripts directory --- scripts/datapack_fix_library.py | 72 -------- scripts/safe_datapack_fixer.py | 305 -------------------------------- 2 files changed, 377 deletions(-) delete mode 100644 scripts/datapack_fix_library.py delete mode 100644 scripts/safe_datapack_fixer.py diff --git a/scripts/datapack_fix_library.py b/scripts/datapack_fix_library.py deleted file mode 100644 index 29321c45..00000000 --- a/scripts/datapack_fix_library.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python3 -""" -dataLib Fix Library -Detects and fixes common datapack errors found during GameTest. -""" - -import re -from pathlib import Path -from typing import List, Tuple - -def detect_failed_to_load_errors(log_content: str) -> List[str]: - """Detect 'Failed to load' errors from Minecraft logs""" - errors = re.findall(r'Failed to load ([^\n]+)', log_content) - return errors - -def detect_fail_messages(log_content: str) -> List[str]: - """Detect messages starting with 'Fail:'""" - fails = re.findall(r'^Fail:.*$', log_content, re.MULTILINE) - return fails - -def fix_common_syntax_issues(content: str) -> Tuple[str, int]: - """Apply common fixes to mcfunction files""" - original = content - fixes = 0 - - # Fix 1: Score holder spacing - if re.search(r'[\w.-]+:\s+', content): - content = re.sub(r'([\w.-]+):\s+', r'\1:*', content) - fixes += 1 - - # Fix 2: Remove trailing spaces in commands - content = re.sub(r' +$', '', content, flags=re.MULTILINE) - if content != original: - fixes += 1 - - return content, fixes - -def run_fix_library(workdir: Path, log_content: str = "") -> int: - """Main entry point for the fix library""" - total_fixes = 0 - - # Check for critical errors - failed_loads = detect_failed_to_load_errors(log_content) - fail_messages = detect_fail_messages(log_content) - - if failed_loads: - print(f"[FixLibrary] Detected {len(failed_loads)} 'Failed to load' errors") - for err in failed_loads: - print(f" - {err}") - - if fail_messages: - print(f"[FixLibrary] Detected {len(fail_messages)} FAIL messages") - for msg in fail_messages: - print(f" - {msg}") - - # Apply fixes to all mcfunction files - for mcfile in workdir.rglob("*.mcfunction"): - try: - content = mcfile.read_text(encoding="utf-8") - new_content, fixes = fix_common_syntax_issues(content) - if fixes > 0: - mcfile.write_text(new_content, encoding="utf-8") - total_fixes += fixes - except Exception: - continue - - if total_fixes > 0: - print(f"[FixLibrary] Applied {total_fixes} fixes") - else: - print("[FixLibrary] No fixes needed") - - return total_fixes \ No newline at end of file diff --git a/scripts/safe_datapack_fixer.py b/scripts/safe_datapack_fixer.py deleted file mode 100644 index 1498af87..00000000 --- a/scripts/safe_datapack_fixer.py +++ /dev/null @@ -1,305 +0,0 @@ -#!/usr/bin/env python3 -""" -Safe Datapack Auto Fixer (v2 - No Deletion Mode) -- Runs entirely in /tmp -- Never deletes files -- Only applies known 1.21.5+ compatibility patches -- Uses Beet + Mecha for validation -- Supports warn-mode=fail -- Logs via simulated 'say' commands -- Designed for GitHub Actions + Fabric GameTest simulation -""" - -import argparse -import json -import os -import re -import shutil -import subprocess -import sys -import tempfile -from pathlib import Path -from typing import List, Tuple - -# Color codes for GitHub Actions -C_RESET = "\033[0m" -C_RED = "\033[31m" -C_GREEN = "\033[32m" -C_YELLOW = "\033[33m" -C_BLUE = "\033[34m" -C_CYAN = "\033[36m" -C_BOLD = "\033[1m" - -def log(msg: str): - print(f"{C_BLUE}[*]{C_RESET} {msg}") - -def ok(msg: str): - print(f"{C_GREEN}[OK]{C_RESET} {msg}") - -def warn(msg: str): - print(f"{C_YELLOW}[!]{C_RESET} {msg}") - -def err(msg: str): - print(f"{C_RED}[FAIL]{C_RESET} {msg}", file=sys.stderr) - -def step(msg: str): - print(f"\n{C_BOLD}{C_CYAN}== {msg} =={C_RESET}") - -def say_log(message: str): - """Simulate Minecraft /say command logging""" - print(f"say {message}") - -def run_cmd(cmd: List[str], cwd: Path, check: bool = True, capture: bool = False) -> subprocess.CompletedProcess: - """Safe command runner""" - try: - result = subprocess.run( - cmd, - cwd=cwd, - check=check, - capture_output=capture, - text=True, - timeout=120 - ) - return result - except subprocess.TimeoutExpired: - err(f"Command timed out: {' '.join(cmd)}") - raise - except subprocess.CalledProcessError as e: - if check: - err(f"Command failed: {' '.join(cmd)}") - if e.stdout: - print(e.stdout) - if e.stderr: - print(e.stderr, file=sys.stderr) - raise - -def apply_safe_patches(workdir: Path) -> int: - """Apply only known safe 1.21.5+ compatibility patches. NEVER delete.""" - patches_applied = 0 - - step("Applying safe compatibility patches") - - # Patch 1: Score holder spacing (foo: * -> foo:*) - patch_desc = "score holder shorthand spacing" - pattern = re.compile(r'([\w.-]+):\s+') - replacement = r'\1:*' - count = 0 - - for mcfile in workdir.rglob("*.mcfunction"): - if ".git" in str(mcfile) or "build" in str(mcfile): - continue - try: - content = mcfile.read_text(encoding="utf-8") - if pattern.search(content): - new_content = pattern.sub(replacement, content) - mcfile.write_text(new_content, encoding="utf-8") - count += 1 - except Exception: - continue - - if count > 0: - ok(f"{patch_desc} -> {count} file(s) patched") - patches_applied += count - else: - log(f"{patch_desc} -> no matches, skipped") - - # NOTE: time of is now the correct modern syntax (1.21.5+) - # We no longer patch "time of" or "time query daytime" as they are valid. - # Patch 2 and Patch 3 have been removed to avoid breaking current syntax. - - return patches_applied - -def validate_with_beet(workdir: Path, build_dir: Path) -> bool: - """Run Beet + Mecha validation (the real test)""" - step("Running Beet + Mecha validation") - - beet_json = workdir / "beet.json" - - if not beet_json.exists(): - log("No beet.json found — creating minimal safe version") - beet_json.write_text(json.dumps({ - "name": "dataLib", - "output": str(build_dir), - "pipeline": ["mecha"], - "data_pack": {"load": ["."]} - }, indent=2), encoding="utf-8") - - try: - # Run beet build in isolated directory - result = run_cmd( - ["beet", "build"], - cwd=workdir, - check=False, - capture=True - ) - - if result.returncode == 0: - ok("Beet + Mecha validation passed") - say_log("Beet + Mecha validation passed") - return True - else: - err("Beet build failed") - if result.stdout: - print(result.stdout) - if result.stderr: - print(result.stderr, file=sys.stderr) - say_log("Beet validation FAILED") - return False - - except Exception as e: - err(f"Beet execution error: {e}") - return False - -def simulate_fabric_gametest(workdir: Path) -> bool: - """ - Realistic Fabric GameTest API simulation. - This mimics the output format of actual Fabric GameTest runs. - In a real setup this would be replaced by a proper Fabric mod test. - """ - step("Fabric GameTest API + /say Logging (Realistic Simulation)") - - say_log("§a[GameTest] Starting datapack validation on Fabric server") - say_log("§e[GameTest] Environment: Minecraft 1.21.5 + Fabric API") - - func_files = list(workdir.rglob("*.mcfunction")) - func_count = len(func_files) - - say_log(f"§b[GameTest] Discovered {func_count} mcfunction files") - - # Simulate loading StringLib modules with realistic GameTest output - modules = [ - ("concat", "data/stringlib/function/zprivate/concat"), - ("find", "data/stringlib/function/zprivate/find"), - ("replace", "data/stringlib/function/zprivate/replace"), - ("split", "data/stringlib/function/zprivate/split"), - ("case", "data/stringlib/function/zprivate/to_lowercase"), - ] - - passed = 0 - failed = 0 - - for module_name, path in modules: - module_files = [f for f in func_files if path in str(f)] - if module_files: - say_log(f"§a[GameTest] ✓ Loaded module: {module_name} ({len(module_files)} functions)") - passed += 1 - else: - say_log(f"§c[GameTest] ✗ Module not found: {module_name}") - failed += 1 - - # Run actual function tests (simulated) - say_log("§6[GameTest] Running function tests...") - - test_cases = [ - ("stringlib:concat/main", "PASS"), - ("stringlib:find/main", "PASS"), - ("stringlib:replace/main", "PASS"), - ("stringlib:split/main", "PASS"), - ("stringlib:to_lowercase/main_fast", "PASS"), - ] - - for test_name, result in test_cases: - if result == "PASS": - say_log(f"§a[GameTest] {test_name} → §aPASS") - else: - say_log(f"§c[GameTest] {test_name} → §cFAIL") - - say_log("§a[GameTest] All tests completed successfully") - say_log("§b[GameTest] Summary: 5/5 tests passed") - - ok("Fabric GameTest API simulation completed successfully") - return True - -def main(): - parser = argparse.ArgumentParser(description="Safe Datapack Auto Fixer") - parser.add_argument("--base", required=True, type=Path, help="Original datapack directory") - parser.add_argument("--build-dir", required=True, type=Path, help="Build output directory") - parser.add_argument("--patched-dir", required=True, type=Path, help="Patched output directory") - parser.add_argument("--warn-mode", choices=["fail", "warn"], default="fail") - parser.add_argument("--max-files", type=int, default=10000) - - args = parser.parse_args() - - base_dir: Path = args.base.resolve() - build_dir: Path = args.build_dir.resolve() - patched_dir: Path = args.patched_dir.resolve() - warn_mode = args.warn_mode - - step("Safe Datapack Fixer starting") - say_log("Safe datapack fixer started") - - # Safety checks - if not base_dir.exists(): - err("Base directory does not exist") - sys.exit(1) - - # Count files (safety limit) - mcfunction_files = list(base_dir.rglob("*.mcfunction")) - if len(mcfunction_files) > args.max_files: - warn(f"Large datapack detected: {len(mcfunction_files)} mcfunction files (limit: {args.max_files})") - warn("Continuing anyway (limit increased for dataLib)") - # Do not exit — we want Fabric GameTest to run even on large packs - - # Create working copy in /tmp (never touch original) - workdir = Path(tempfile.mkdtemp(prefix="datapack-fix-")) - log(f"Working in isolated directory: {workdir}") - - try: - # Copy original to temp (safe copy) - shutil.copytree(base_dir, workdir, dirs_exist_ok=True) - ok("Isolated copy created") - - # Apply patches (never deletes) - patches = apply_safe_patches(workdir) - - # Validate with Beet/Mecha - beet_ok = validate_with_beet(workdir, build_dir) - - if not beet_ok: - if warn_mode == "fail": - err("Validation failed — failing workflow (warn-mode=fail)") - sys.exit(1) - else: - warn("Validation failed but continuing (warn-mode=warn)") - - # Run Fabric GameTest simulation - gametest_ok = simulate_fabric_gametest(workdir) - - if not gametest_ok: - if warn_mode == "fail": - sys.exit(1) - - # If everything passed, copy patched files back to patched-dir - if patches > 0 or beet_ok: - log("Copying patched files to output directory") - if patched_dir.exists(): - shutil.rmtree(patched_dir) - shutil.copytree(workdir, patched_dir, dirs_exist_ok=True) - - # Remove build artifacts from output - for build_path in patched_dir.rglob("build"): - if build_path.is_dir(): - shutil.rmtree(build_path) - - ok(f"Patched datapack ready in {patched_dir}") - - # GitHub Actions output - print(f"::set-output name=changes_detected::true") - print(f"::set-output name=patches_applied::{patches}") - else: - print("::set-output name=changes_detected::false") - ok("No changes needed") - - say_log("Safe datapack fixer completed successfully") - - except Exception as e: - err(f"Unexpected error: {e}") - if warn_mode == "fail": - sys.exit(1) - finally: - # Always clean up temp directory - if workdir.exists(): - shutil.rmtree(workdir, ignore_errors=True) - -if __name__ == "__main__": - main() From 2ba32d8c32ed7d1b66aee5908b901a2707466544 Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:26:45 +0300 Subject: [PATCH 04/19] Delete src/testmod directory --- .../gametest/DataLibClientGameTest.java | 38 -------- .../runtoolkit/gametest/DataLibGameTest.java | 97 ------------------- .../gametest/DataLibServerGameTest.java | 58 ----------- src/testmod/resources/fabric.mod.json | 16 --- 4 files changed, 209 deletions(-) delete mode 100644 src/testmod/java/com/runtoolkit/gametest/DataLibClientGameTest.java delete mode 100644 src/testmod/java/com/runtoolkit/gametest/DataLibGameTest.java delete mode 100644 src/testmod/java/com/runtoolkit/gametest/DataLibServerGameTest.java delete mode 100644 src/testmod/resources/fabric.mod.json diff --git a/src/testmod/java/com/runtoolkit/gametest/DataLibClientGameTest.java b/src/testmod/java/com/runtoolkit/gametest/DataLibClientGameTest.java deleted file mode 100644 index 26422e01..00000000 --- a/src/testmod/java/com/runtoolkit/gametest/DataLibClientGameTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.runtoolkit.gametest; - -import net.fabricmc.fabric.api.client.gametest.v1.FabricClientGameTest; -import net.minecraft.client.MinecraftClient; -import net.minecraft.text.Text; - -/** - * Client-side Fabric GameTest for dataLib - * Runs AFTER Server tests - */ -public class DataLibClientGameTest implements FabricClientGameTest { - - private void clientSay(String message) { - if (MinecraftClient.getInstance().player != null) { - MinecraftClient.getInstance().player.sendMessage(Text.literal(message), false); - } else { - System.out.println(message); - } - } - - @Override - public void runTest() { - clientSay("§a[GameTest] Starting CLIENT datapack validation"); - clientSay("§e[GameTest] Client Environment: Minecraft 1.21.5 + Fabric API"); - - clientSay("§a[GameTest] ✓ Client module: concat loaded"); - clientSay("§a[GameTest] ✓ Client module: find loaded"); - clientSay("§a[GameTest] ✓ Client module: replace loaded"); - - clientSay("§6[GameTest] Running client function tests..."); - - clientSay("§a[GameTest] stringlib:concat/client_test → §aPASS"); - clientSay("§a[GameTest] stringlib:find/client_test → §aPASS"); - - clientSay("§a[GameTest] Client tests completed successfully"); - clientSay("§b[GameTest] Client Summary: 2/2 tests passed"); - } -} \ No newline at end of file diff --git a/src/testmod/java/com/runtoolkit/gametest/DataLibGameTest.java b/src/testmod/java/com/runtoolkit/gametest/DataLibGameTest.java deleted file mode 100644 index 46e842fe..00000000 --- a/src/testmod/java/com/runtoolkit/gametest/DataLibGameTest.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.runtoolkit.gametest; - -import net.fabricmc.fabric.api.gametest.v1.FabricGameTest; -import net.minecraft.gametest.framework.GameTest; -import net.minecraft.gametest.framework.GameTestHelper; -import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.text.Text; -import net.minecraft.util.math.BlockPos; - -/** - * Real Fabric GameTest for dataLib datapack - * Uses actual GameTest API + /say logging + screenshots - */ -public class DataLibGameTest implements FabricGameTest { - - private static final String EMPTY_STRUCTURE = "empty"; - - @GameTest(structureName = EMPTY_STRUCTURE, timeoutTicks = 200) - public void testStringLibConcat(GameTestHelper helper) { - ServerCommandSource source = helper.getWorld().getServer().getCommandSource(); - - // Log via /say - source.sendFeedback(() -> Text.literal("§a[GameTest] Starting dataLib StringLib validation..."), false); - - // Execute datapack function - helper.runCommand("function stringlib:concat/main"); - - source.sendFeedback(() -> Text.literal("§a[GameTest] ✓ concat/main executed successfully"), false); - - // Take screenshot - helper.takeScreenshot("datalib_concat_test"); - - helper.succeed(); - } - - @GameTest(structureName = EMPTY_STRUCTURE, timeoutTicks = 200) - public void testStringLibFind(GameTestHelper helper) { - ServerCommandSource source = helper.getWorld().getServer().getCommandSource(); - - source.sendFeedback(() -> Text.literal("§b[GameTest] Running find module test..."), false); - - helper.runCommand("function stringlib:find/main"); - - source.sendFeedback(() -> Text.literal("§a[GameTest] ✓ find/main passed"), false); - helper.takeScreenshot("datalib_find_test"); - - helper.succeed(); - } - - @GameTest(structureName = EMPTY_STRUCTURE, timeoutTicks = 200) - public void testStringLibReplace(GameTestHelper helper) { - ServerCommandSource source = helper.getWorld().getServer().getCommandSource(); - - source.sendFeedback(() -> Text.literal("§6[GameTest] Testing replace module..."), false); - - helper.runCommand("function stringlib:replace/main"); - - source.sendFeedback(() -> Text.literal("§a[GameTest] ✓ replace/main passed"), false); - helper.takeScreenshot("datalib_replace_test"); - - helper.succeed(); - } - - @GameTest(structureName = EMPTY_STRUCTURE, timeoutTicks = 200) - public void testStringLibSplit(GameTestHelper helper) { - ServerCommandSource source = helper.getWorld().getServer().getCommandSource(); - - source.sendFeedback(() -> Text.literal("§d[GameTest] Testing split module..."), false); - - helper.runCommand("function stringlib:split/main"); - - source.sendFeedback(() -> Text.literal("§a[GameTest] ✓ split/main passed"), false); - helper.takeScreenshot("datalib_split_test"); - - helper.succeed(); - } - - @GameTest(structureName = EMPTY_STRUCTURE, timeoutTicks = 200) - public void testFullValidation(GameTestHelper helper) { - ServerCommandSource source = helper.getWorld().getServer().getCommandSource(); - - source.sendFeedback(() -> Text.literal("§a[GameTest] === FULL dataLib VALIDATION ==="), false); - - // Run all major modules - helper.runCommand("function stringlib:concat/main"); - helper.runCommand("function stringlib:find/main"); - helper.runCommand("function stringlib:replace/main"); - helper.runCommand("function stringlib:split/main"); - - source.sendFeedback(() -> Text.literal("§b[GameTest] Summary: 4/4 modules passed"), false); - source.sendFeedback(() -> Text.literal("§a[GameTest] All tests completed successfully"), false); - - helper.takeScreenshot("datalib_full_validation"); - - helper.succeed(); - } -} \ No newline at end of file diff --git a/src/testmod/java/com/runtoolkit/gametest/DataLibServerGameTest.java b/src/testmod/java/com/runtoolkit/gametest/DataLibServerGameTest.java deleted file mode 100644 index ea188204..00000000 --- a/src/testmod/java/com/runtoolkit/gametest/DataLibServerGameTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.runtoolkit.gametest; - -import net.fabricmc.fabric.api.gametest.v1.FabricGameTest; -import net.minecraft.gametest.framework.GameTest; -import net.minecraft.gametest.framework.GameTestHelper; -import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.text.Text; - -/** - * Server-side Fabric GameTest for dataLib - * Real /say logging + Fail detection - */ -public class DataLibServerGameTest implements FabricGameTest { - - private static final String EMPTY_STRUCTURE = "empty"; - - private void say(ServerCommandSource source, String message) { - source.sendFeedback(() -> Text.literal(message), false); - } - - @GameTest(structureName = EMPTY_STRUCTURE, timeoutTicks = 500) - public void testAllModulesServer(GameTestHelper helper) { - ServerCommandSource source = helper.getWorld().getServer().getCommandSource(); - - say(source, "§a[GameTest] Starting datapack validation on Fabric server"); - say(source, "§e[GameTest] Environment: Minecraft 1.21.5 + Fabric API"); - say(source, "§b[GameTest] Discovered 3254 mcfunction files"); - - say(source, "§a[GameTest] ✓ Loaded module: concat (203 functions)"); - say(source, "§a[GameTest] ✓ Loaded module: find (8 functions)"); - say(source, "§a[GameTest] ✓ Loaded module: replace (8 functions)"); - say(source, "§a[GameTest] ✓ Loaded module: split (9 functions)"); - say(source, "§a[GameTest] ✓ Loaded module: case (2 functions)"); - - say(source, "§6[GameTest] Running function tests..."); - - // Execute real datapack functions - helper.runCommand("function stringlib:concat/main"); - say(source, "§a[GameTest] stringlib:concat/main → §aPASS"); - - helper.runCommand("function stringlib:find/main"); - say(source, "§a[GameTest] stringlib:find/main → §aPASS"); - - helper.runCommand("function stringlib:replace/main"); - say(source, "§a[GameTest] stringlib:replace/main → §aPASS"); - - helper.runCommand("function stringlib:split/main"); - say(source, "§a[GameTest] stringlib:split/main → §aPASS"); - - helper.runCommand("function stringlib:to_lowercase/main_fast"); - say(source, "§a[GameTest] stringlib:to_lowercase/main_fast → §aPASS"); - - say(source, "§a[GameTest] All tests completed successfully"); - say(source, "§b[GameTest] Summary: 5/5 tests passed"); - - helper.succeed(); - } -} \ No newline at end of file diff --git a/src/testmod/resources/fabric.mod.json b/src/testmod/resources/fabric.mod.json deleted file mode 100644 index 04170d29..00000000 --- a/src/testmod/resources/fabric.mod.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "schemaVersion": 1, - "id": "datalib-gametest", - "version": "1.0.0", - "name": "dataLib GameTest", - "description": "Fabric GameTest for dataLib datapack validation", - "authors": ["runtoolkit"], - "license": "MIT", - "environment": "*", - "entrypoints": {}, - "depends": { - "fabricloader": ">=0.16.0", - "fabric-api": "*", - "minecraft": "1.21.5" - } -} \ No newline at end of file From 90f85c43bb13464141e6fd89f97abb427c7f3f5b Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:26:59 +0300 Subject: [PATCH 05/19] Delete build.gradle --- build.gradle | 48 ------------------------------------------------ 1 file changed, 48 deletions(-) delete mode 100644 build.gradle diff --git a/build.gradle b/build.gradle deleted file mode 100644 index ea30027b..00000000 --- a/build.gradle +++ /dev/null @@ -1,48 +0,0 @@ -plugins { - id 'fabric-loom' version '1.6-SNAPSHOT' -} - -version = '1.0.0' -group = 'com.runtoolkit' - -repositories { - mavenCentral() - maven { url 'https://maven.fabricmc.net/' } -} - -dependencies { - minecraft 'com.mojang:minecraft:1.21.5' - mappings 'net.fabricmc:yarn:1.21.5+build.1:v2' - - modImplementation 'net.fabricmc:fabric-loader:0.16.10' - modImplementation 'net.fabricmc.fabric-api:fabric-api:0.119.0+1.21.5' - - // GameTest support (included in fabric-api, but explicit for clarity) - modImplementation 'net.fabricmc.fabric-api:fabric-gametest-api-v1:0.119.0+1.21.5' -} - -loom { - runs { - gametest { - server() - name 'Game Test' - vmArgs '-Dfabric-api.gametest=true' - vmArgs '-Dfabric-api.gametest.report.file=build/test-report.xml' - } - } -} - -tasks.register('runGameTestServer', JavaExec) { - dependsOn 'classes' - group = 'fabric' - description = 'Runs the GameTest server' - - classpath = sourceSets.main.runtimeClasspath - main = 'net.fabricmc.loader.impl.launch.knot.KnotServer' - - args = ['--nogui'] - systemProperties = [ - 'fabric-api.gametest': 'true', - 'fabric-api.gametest.report.file': 'build/test-report.xml' - ] -} \ No newline at end of file From ffbdb50999d68a444afc2de1f652bdb51902e774 Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:27:17 +0300 Subject: [PATCH 06/19] Delete settings.gradle --- settings.gradle | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 settings.gradle diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index a6250782..00000000 --- a/settings.gradle +++ /dev/null @@ -1,8 +0,0 @@ -pluginManagement { - repositories { - maven { url 'https://maven.fabricmc.net/' } - gradlePluginPortal() - } -} - -rootProject.name = "dataLib" \ No newline at end of file From 9ff6125ce50e89c47fdbaf13d08c28ed0614f8e3 Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:27:27 +0300 Subject: [PATCH 07/19] Delete gradlew.bat --- gradlew.bat | 92 ----------------------------------------------------- 1 file changed, 92 deletions(-) delete mode 100644 gradlew.bat diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index 25da30db..00000000 --- a/gradlew.bat +++ /dev/null @@ -1,92 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega From eb6a404f43f0998171bf48f00655bfc6e4d9442f Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:27:49 +0300 Subject: [PATCH 08/19] Delete gradlew --- gradlew | 249 -------------------------------------------------------- 1 file changed, 249 deletions(-) delete mode 100755 gradlew diff --git a/gradlew b/gradlew deleted file mode 100755 index b740cf13..00000000 --- a/gradlew +++ /dev/null @@ -1,249 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" From 26d3e95be1c12b19baae1bc222811d21f8803bb7 Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:49:57 +0300 Subject: [PATCH 09/19] Delete .github directory --- .github/CODE_OF_CONDUCT.md | 136 --------------- .github/CONTRIBUTING.md | 196 ---------------------- .github/DISCUSSION_TEMPLATE/general.yml | 25 --- .github/DISCUSSION_TEMPLATE/ideas.yml | 9 - .github/DISCUSSION_TEMPLATE/q-a.yml | 15 -- .github/ISSUE_TEMPLATE/bug_report.yml | 67 -------- .github/ISSUE_TEMPLATE/compatibility.yml | 65 -------- .github/ISSUE_TEMPLATE/config.yml | 1 - .github/ISSUE_TEMPLATE/general.yml | 18 -- .github/PULL_REQUEST_TEMPLATE.md | 28 ---- .github/SECURITY.md | 89 ---------- .github/scripts/generate_buid-py.py | 97 ----------- .github/workflows/build.yml | 204 ----------------------- .github/workflows/dp-lint.yml | 77 --------- .github/workflows/generate-build-py.yml | 58 ------- .github/workflows/lint-and-build.yml | 88 ---------- .github/workflows/no-op-workflow.yml | 12 -- 17 files changed, 1185 deletions(-) delete mode 100644 .github/CODE_OF_CONDUCT.md delete mode 100644 .github/CONTRIBUTING.md delete mode 100644 .github/DISCUSSION_TEMPLATE/general.yml delete mode 100644 .github/DISCUSSION_TEMPLATE/ideas.yml delete mode 100644 .github/DISCUSSION_TEMPLATE/q-a.yml delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml delete mode 100644 .github/ISSUE_TEMPLATE/compatibility.yml delete mode 100644 .github/ISSUE_TEMPLATE/config.yml delete mode 100644 .github/ISSUE_TEMPLATE/general.yml delete mode 100644 .github/PULL_REQUEST_TEMPLATE.md delete mode 100644 .github/SECURITY.md delete mode 100644 .github/scripts/generate_buid-py.py delete mode 100644 .github/workflows/build.yml delete mode 100644 .github/workflows/dp-lint.yml delete mode 100644 .github/workflows/generate-build-py.yml delete mode 100644 .github/workflows/lint-and-build.yml delete mode 100644 .github/workflows/no-op-workflow.yml diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md deleted file mode 100644 index 392b8186..00000000 --- a/.github/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,136 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, testers, and maintainers pledge to make -participation in this Minecraft Java Edition datapack project a harassment-free -experience for everyone, regardless of age, body size, visible or invisible -disability, ethnicity, sex characteristics, gender identity and expression, -level of experience, education, socio-economic status, nationality, personal -appearance, race, religion, or sexual identity and orientation. - -We commit to fostering a community that is technically rigorous, respectful, -and collaborative. - ---- - -## Our Standards - -Examples of behavior that contributes to a positive environment include: - -- Providing constructive feedback on datapack functions, commands, and architecture -- Reporting bugs or exploits responsibly -- Respecting different implementation strategies (scoreboard logic, storage patterns, NBT handling, etc.) -- Reviewing pull requests objectively and based on technical merit -- Taking responsibility for mistakes in code, documentation, or testing - -Examples of unacceptable behavior include: - -- Trolling, flame wars, or hostile arguments about implementation style -- Publicly posting unpatched exploits without responsible disclosure -- Harassment in issues, pull requests, discussions, or external platforms -- Publishing private information without explicit permission -- Submitting intentionally malicious code (e.g., crash loops, backdoors, hidden command execution) - ---- - -## Enforcement Responsibilities - -Project maintainers are responsible for enforcing this Code of Conduct. - -Maintainers may: - -- Remove or edit comments -- Close issues or pull requests -- Reject contributions -- Temporarily or permanently restrict participation - -Moderation decisions will be based on maintaining a healthy technical -environment and protecting contributors. - ---- - -## Scope - -This Code of Conduct applies to: - -- GitHub issues and pull requests -- Discussions and documentation -- Datapack releases and testing environments -- Any public space where the project is officially represented - -This includes representation via official repository accounts, -release announcements, and community channels. - ---- - -## Reporting - -Instances of abusive, harassing, exploitative, or otherwise unacceptable -behavior may be reported privately to the project maintainers via: - -- GitHub private contact -- Security Advisory system -- Official project contact address (if available) - -Do not open public issues for Code of Conduct violations. - -All reports will be reviewed promptly and handled confidentially. - ---- - -## Enforcement Guidelines - -The following graduated response model may be applied: - -### 1. Correction - -**Impact**: Minor unprofessional conduct or inappropriate tone. - -**Consequence**: Private warning and clarification of expected behavior. - ---- - -### 2. Warning - -**Impact**: Repeated minor violations or a single disruptive incident. - -**Consequence**: Formal warning. Temporary restriction from discussions -or interactions with specific contributors. - ---- - -### 3. Temporary Ban - -**Impact**: Serious misconduct, sustained hostility, or exploit abuse. - -**Consequence**: Temporary removal from project participation -(issues, pull requests, discussions). - ---- - -### 4. Permanent Ban - -**Impact**: Pattern of violations, harassment, malicious code submission, -or intentional damage to the project or its community. - -**Consequence**: Permanent removal from all project spaces. - ---- - -## Additional Datapack-Specific Policy - -- Security exploits must be disclosed responsibly. -- Malicious pull requests (e.g., hidden `execute` abuse, infinite loops, - unauthorized storage writes) will result in immediate review and potential ban. -- Performance sabotage (intentional lag mechanisms) is treated as misconduct. - ---- - -## Attribution - -This Code of Conduct is adapted from Contributor Covenant version 2.0: -https://www.contributor-covenant.org/version/2/0/code_of_conduct.html - -Community Impact Guidelines were inspired by Mozilla’s enforcement model: -https://github.com/mozilla/diversity diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md deleted file mode 100644 index c3c7151d..00000000 --- a/.github/CONTRIBUTING.md +++ /dev/null @@ -1,196 +0,0 @@ -# Contributing Guide -Thank you for considering contributing to this Minecraft Java Edition datapack. -This document defines contribution standards, architectural constraints, and review criteria. -All contributions are evaluated based on stability, performance, security, and long-term maintainability. ---- -## Project Philosophy -This datapack prioritizes: -- Deterministic and predictable behavior -- Server-side performance optimization -- Secure and auditable command architecture -- Backward compatibility within supported versions -- Minimal unnecessary tick execution -Contributions that conflict with these principles may be rejected. ---- -## Before You Contribute -Ensure the following before opening a pull request: -- You are targeting a supported version (see `SECURITY.md`) -- All changes are tested in a clean world -- No debug scoreboards or temporary storage values remain -- No development-only functions are included -- No experimental snapshot-only features are used -- No performance regression is introduced ---- -## Development Standards -### 1. Namespacing -- All functions must use the project namespace. -- No pollution of the `minecraft:` namespace. -- Cross-namespace execution must be justified and documented. ---- -### 2. Function Design -- No infinite loops. -- No recursive `execute` chains without guaranteed termination. -- Minimize logic inside `tick` functions. -- Prefer `storage` over unnecessary scoreboards when appropriate. -- Use targeted selectors instead of broad selectors (`@a`, `@e`) whenever possible. ---- -### 3. Performance Rules -Contributions must avoid: -- Unbounded `@e` scans -- Nested `execute as @a at @a` patterns -- Heavy NBT checks every tick -- Excessive scoreboard writes -- Redundant function calls per tick -Any measurable performance regression may result in rejection. ---- -### 4. Security Expectations -Pull requests must NOT include: -- Hidden command execution -- Permission bypass mechanisms -- Backdoors or obfuscated logic -- Crash triggers or lag exploits -- Storage manipulation outside the defined schema -Security-sensitive changes must include a technical explanation. ---- -## Pull Request Process -1. Fork the repository. -2. Create a dedicated feature branch. -3. Keep commits atomic and clearly described. -4. Open a pull request including: - - Clear description of changes - - Technical explanation - - Performance impact analysis - - Testing steps - - Relevant logs or screenshots (if applicable) -Incomplete or low-effort pull requests may be closed without review. ---- -## Code Style -- Consistent indentation -- No trailing whitespace -- Clear and descriptive function naming -- Comment complex logic -- Avoid redundant or duplicated commands -### Example Function Structure -```mcfunction -# Initialize scoreboard -scoreboard objectives add example dummy -# Controlled execution -execute as @a[scores={example=1..}] run function namespace:feature/run -``` ---- -## Bug Reports -When opening an issue, include: -* Datapack version -* Minecraft version -* Steps to reproduce -* Expected result -* Actual result -* Relevant logs -Vague or incomplete reports may be closed. ---- -## Feature Requests -Feature requests must include: -* Clear use case -* Performance consideration -* Compatibility analysis -* Explanation of alignment with project philosophy ---- -## Known Pitfalls & Decisions - -This section documents recurring mistakes and deliberate decisions to prevent regressions -and reduce contributor confusion. - -### pack_format -Always use the exact `pack_format` value for the target version range. -Do not increment version numbers without a meaningful content change — this breaks -semver-aware compatibility checks and downstream dependency guards. -For multi-version support, use `supported_formats` (≤1.21.4) or `min_format`/`max_format` -at root level (1.21.5+ / pack format 61+). Do not mix both forms. - -### Text Component Syntax (`clickEvent` vs `click_event`) -- `clickEvent` — legacy camelCase key, valid in all versions up to and including current. -- `click_event` — new snake_case key introduced in 1.21.5. -Both keys are currently accepted for backward compatibility, but **all new files must use -`click_event`** to remain forward-compatible with future deprecation of the camelCase form. -Do not silently convert one to the other without verifying the target version range. - -### Macros and Storage -- `$` prefix is required only on lines that contain a `$(var)` substitution. - Do not apply it globally. -- `$(var)` accepts strings only — full JSON text components cannot be passed as macro arguments. - Pre-resolve storage values via `function … with storage` before passing to macros. -- Use `set` (not `append`) on shared storage paths to avoid unintended accumulation. -- Always guard storage initialization with `execute unless data`. - -### Fake Files and Unverified Links -Do not reference file paths, download links, or external URLs that have not been verified -to exist. Placeholder domains (e.g. `example.com`, `files.examplecdn.com`) are not -acceptable in documentation or code comments. - -### No Version Inflation -Version numbers must reflect actual changes: -- Patch (`x.x.Z`): bug fixes only. -- Minor (`x.Y.0`): new features, backward-compatible. -- Major (`X.0.0`): breaking changes. -Incrementing a version number without a corresponding changelog entry will be rejected. - ---- -## AI-Assisted Contributions - -Using AI tools (ChatGPT, Claude, etc.) to help write code or documentation is allowed, -but contributions must meet the same standards as hand-written code. -AI output is not a substitute for understanding — **you are responsible for everything you submit.** - -### Common AI Mistakes to Verify Before Submitting - -**pack_format / version numbers** -AI models frequently output incorrect or inconsistent `pack_format` values. -Always verify against the [official wiki](https://minecraft.wiki/w/Pack_format) before submitting. - -**Hallucinated links and file references** -AI tools may produce plausible-looking but entirely fake URLs, CDN paths, or file names. -Never include a link or file reference you have not personally verified exists. - -**Syntax versioning (`clickEvent` vs `click_event`, camelCase vs snake_case)** -AI models often mix old and new key formats within the same output. -Cross-check all JSON text component keys, NBT keys, and advancement event names -against the target version. See [Known Pitfalls & Decisions](#known-pitfalls--decisions). - -**Macro usage** -AI-generated macro functions frequently apply the `$` prefix to non-substitution lines, -or attempt to pass full JSON text components as `$(var)` arguments (not supported). -Review every macro file manually. - -**Fabricated wait times and progress claims** -Disregard any AI output that claims a file "is being prepared" or will be ready after a delay. -If the content is not in the response, it does not exist. - -**Version inflation** -AI tools tend to increment version numbers across iterations without actual changes. -Use your own judgment for versioning — do not copy version strings from AI output blindly. - -### Checklist for AI-Assisted PRs -Before opening a pull request that involved AI assistance: -- [ ] All `pack_format` values manually verified -- [ ] No unverified external links or file paths -- [ ] All text component keys match target version syntax -- [ ] Macro `$` prefix applied only to substitution lines -- [ ] Version number reflects actual change scope -- [ ] Logic understood and tested — not submitted as-is from AI output - ---- -## What Will Be Rejected -* Snapshot-only features -* Performance-heavy mechanics without justification -* Overengineered abstractions -* Features outside project scope -* Low-effort or poorly tested contributions ---- -## Final Review Criteria -All contributions are evaluated based on: -* Stability -* Security -* Performance -* Maintainability -* Architectural consistency -Quality over quantity. diff --git a/.github/DISCUSSION_TEMPLATE/general.yml b/.github/DISCUSSION_TEMPLATE/general.yml deleted file mode 100644 index c78adb70..00000000 --- a/.github/DISCUSSION_TEMPLATE/general.yml +++ /dev/null @@ -1,25 +0,0 @@ -title: "[Question]" -body: - - type: textarea - id: summary - attributes: - label: Summary - description: Provide a short summary of your question about the datapack. - placeholder: "What's your question about (function, macro, GUI, etc.)?" - - type: textarea - id: details - attributes: - label: Details - description: Give detailed information, including Minecraft version and datapack version. - placeholder: "Include relevant context, errors, screenshots, or example commands." - - type: dropdown - id: category - attributes: - label: Category - description: Select the category that best fits your question. - options: - - Function / Macro - - Custom Items - - GUI / Admin Panel - - NPC / Entity - - Other diff --git a/.github/DISCUSSION_TEMPLATE/ideas.yml b/.github/DISCUSSION_TEMPLATE/ideas.yml deleted file mode 100644 index 638d654f..00000000 --- a/.github/DISCUSSION_TEMPLATE/ideas.yml +++ /dev/null @@ -1,9 +0,0 @@ -title: "[Idea]" -labels: ["idea"] -body: - - type: textarea - id: idea - attributes: - label: "Your Idea" - description: "Share your idea for the datapack." - placeholder: "Explain what you think could improve the datapack." diff --git a/.github/DISCUSSION_TEMPLATE/q-a.yml b/.github/DISCUSSION_TEMPLATE/q-a.yml deleted file mode 100644 index 526e73ca..00000000 --- a/.github/DISCUSSION_TEMPLATE/q-a.yml +++ /dev/null @@ -1,15 +0,0 @@ -title: "[Q&A]" -labels: ["question"] -body: - - type: textarea - id: question - attributes: - label: "Question" - description: "Ask your question about DataLib." - placeholder: "For example: How do I create a macro that…?" - - type: input - id: mc_version - attributes: - label: "Minecraft Version" - description: "Which Minecraft version are you using?" - placeholder: "1.21.8" diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml deleted file mode 100644 index c5556558..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Bug Report -description: Report a bug or unexpected behavior -title: "[BUG] " -labels: [ bug ] -body: - - type: markdown - attributes: - value: | - Please check for existing issues before reporting. - - type: input - id: ame_version - attributes: - label: D.L. Version - placeholder: "e.g.: v1.0.6-pre4" - validations: - required: true - - type: dropdown - id: mc_version - attributes: - label: Minecraft Version - options: - - "1.21.1" - - "1.21.2 / 1.21.3" - - "1.21.4" - - "1.21.5" - - "1.21.6+" - validations: - required: true - - type: dropdown - id: environment - attributes: - label: Environment - options: - - Singleplayer - - Multiplayer (vanilla server) - - Multiplayer (Paper/Spigot) - - Multiplayer (Fabric) - validations: - required: true - - type: textarea - id: description - attributes: - label: Description of the Bug - description: What happened? What did you expect to happen? - validations: - required: true - - type: textarea - id: reproduction - attributes: - label: Steps to Reproduce - placeholder: | - 1. Run `/function datalib:...` - 2. ... - validations: - required: true - - type: textarea - id: affected_functions - attributes: - label: Affected Functions - description: Which `datalib:` function(s) are affected? - placeholder: "e.g.: datalib:flag/toggle_system, datalib:lib/tick_guard" - - type: textarea - id: logs - attributes: - label: Logs / Error Output - description: In-game error messages or content from `.minecraft/logs/latest.txt` - render: text diff --git a/.github/ISSUE_TEMPLATE/compatibility.yml b/.github/ISSUE_TEMPLATE/compatibility.yml deleted file mode 100644 index 6423ed38..00000000 --- a/.github/ISSUE_TEMPLATE/compatibility.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: Version Compatibility Issue -description: Report something that is not working in a specific Minecraft version -title: "[COMPAT] " -labels: [ compatibility ] -body: - - type: input - id: ame_version - attributes: - label: D.L. Version - placeholder: "e.g.: v1.0.6-pre4" - validations: - required: true - - type: dropdown - id: broken_version - attributes: - label: Minecraft Version Where the Issue Occurs - options: - - "1.20.3" - - "1.20.4" - - "1.21" - - "1.21.1" - - "1.21.2 / 1.21.3" - - "1.21.4" - - "1.21.5" - - "1.21.6" - - "1.21.7" - - "1.21.8" - - "1.21.9" - - "1.21.10" - - "1.21.11" - - "26.1" - - "26.1.1" - - "26.1.2" - validations: - required: true - - type: dropdown - id: working_version - attributes: - label: Working Minecraft Version (if known) - options: - - I don't know - - "1.21.1" - - "1.21.2 / 1.21.3" - - "1.21.4" - - "1.21.5" - - "1.21.6+" - - type: input - id: pack_format - attributes: - label: pack_format Value - description: From F3 screen or pack.mcmeta - placeholder: "e.g.: 61" - - type: textarea - id: description - attributes: - label: Description of the Issue - description: Which command/function is not working? What error does it give? - validations: - required: true - - type: textarea - id: error - attributes: - label: Error Message - render: text - placeholder: "Unknown function / Invalid variable for command / ..." diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 3ba13e0c..00000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1 +0,0 @@ -blank_issues_enabled: false diff --git a/.github/ISSUE_TEMPLATE/general.yml b/.github/ISSUE_TEMPLATE/general.yml deleted file mode 100644 index 0ac3f984..00000000 --- a/.github/ISSUE_TEMPLATE/general.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: General -description: For anything that doesn't fit into the other categories -title: "[General] " -labels: - - general -body: - - type: textarea - id: description - attributes: - label: Description - validations: - required: true - - type: textarea - id: extra - attributes: - label: Additional Context / Information - validations: - required: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 299c0da5..00000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,28 +0,0 @@ -## Summary - - -## Type -- [ ] Bug fix -- [ ] New module / function -- [ ] Breaking change -- [ ] Documentation -- [ ] Other - -## Module(s) Affected - - -## Testing - -- Minecraft version: -- pack_format: - -## Checklist -- [ ] Functions are commented with module path and context (`# @context`) -- [ ] No hardcoded player selectors where macro input is expected -- [ ] `pack.mcmeta` `supported_formats` uses object syntax (not array) -- [ ] `min_format` / `max_format` present and consistent with `formats` ranges -- [ ] Overlay `formats` ranges do not overlap -- [ ] Overlay added if version-specific behavior changed - -## Related Issues - diff --git a/.github/SECURITY.md b/.github/SECURITY.md deleted file mode 100644 index b7e98fa5..00000000 --- a/.github/SECURITY.md +++ /dev/null @@ -1,89 +0,0 @@ -# Security Policy - -## Supported Versions - -This project is a Minecraft Java Edition datapack. -Security updates are only provided for the following versions: - -| Version | Supported | -| ------- | --------- | -| 5.0.x | :white_check_mark: | -| 3.0.x | :x: | -| < 3.0 | :x: | - -### Notes - -- Only **critical vulnerabilities** are patched. -- No backports for unsupported versions. -- Minecraft snapshot versions are not officially supported. -- Performance issues are not considered security issues unless they cause server crashes or denial-of-service behavior. - ---- - -## Reporting a Vulnerability - -If you discover a security vulnerability, **do NOT open a public issue.** - -### What qualifies as a vulnerability? - -Examples include: - -- Permission bypass via `execute` -- Scoreboard manipulation exploits -- NBT injection -- Storage tampering -- Unauthorized admin function execution -- Infinite loop / server crash triggers -- Selector abuse (`@a`, `@e`) causing unintended execution - ---- - -## How to Report - -Report privately using one of the following methods: - -1. GitHub Security Advisory (preferred) -2. Direct contact with the maintainer - ---- - -## Required Report Information - -Please include: - -- Affected datapack version -- Minecraft version -- Technical explanation of the exploit -- Steps to reproduce -- Expected behavior -- Actual behavior -- Proof-of-concept (minimal test world or commands, if possible) - -Incomplete reports may not be processed. - ---- - -## Response Policy - -- Initial response: within 72 hours -- Status update: within 7 days -- Target fix time for critical issues: 14 days - -If the vulnerability is confirmed: - -- A patched version will be released -- The advisory will be published after the fix -- The reporter may be credited (optional) - -If declined: - -- A technical explanation will be provided -- No further action will be taken - ---- - -## Scope - -This security policy applies only to the official release versions of this datapack. - -Modifications, forks, unofficial builds, or altered distributions are not covered. diff --git a/.github/scripts/generate_buid-py.py b/.github/scripts/generate_buid-py.py deleted file mode 100644 index e834bc1d..00000000 --- a/.github/scripts/generate_buid-py.py +++ /dev/null @@ -1,97 +0,0 @@ -import os -import json - -OUTPUT = "build.py" -ROOT = "./data" -MCMETA = "./pack.mcmeta" -EXTS = (".mcfunction", ".json", ".mcmeta") - -FILES = [] -SEEN = set() - -def collect(directory): - for root, dirs, files in os.walk(directory): - dirs.sort() - for name in sorted(files): - if not name.endswith(EXTS): - continue - path = os.path.join(root, name) - if path in SEEN: - continue - SEEN.add(path) - try: - with open(path, "r", encoding="utf-8") as f: - content = f.read() - except Exception as e: - print(f"[SKIP] {path}: {e}") - continue - FILES.append((path, content)) - -# ------------------------- -# Add pack.mcmeta -# ------------------------- -if os.path.isfile(MCMETA): - try: - with open(MCMETA, "r", encoding="utf-8") as f: - FILES.append((MCMETA, f.read())) - SEEN.add(MCMETA) - print("[INFO] pack.mcmeta found.") - except Exception as e: - print(f"[SKIP] pack.mcmeta: {e}") -else: - print("[WARN] pack.mcmeta missing — no overlay scan.") - -# ------------------------- -# Main data -# ------------------------- -collect(ROOT) - -# ------------------------- -# Overlay parsing -# ------------------------- -overlays = [] -if os.path.isfile(MCMETA): - try: - with open(MCMETA, "r", encoding="utf-8") as f: - meta = json.load(f) - entries = meta.get("overlays", {}).get("entries", []) - overlays = [e["directory"] for e in entries if "directory" in e] - except Exception as e: - print(f"[WARN] overlay parse error: {e}") - -# ------------------------- -# Overlay data scan -# ------------------------- -for overlay in overlays: - overlay_data = os.path.join(".", overlay, "data") - if os.path.isdir(overlay_data): - before = len(FILES) - collect(overlay_data) - print(f"[INFO] Overlay '{overlay}': {len(FILES) - before} files.") - else: - print(f"[WARN] Overlay not found: {overlay_data}") - -print(f"[INFO] Total {len(FILES)} files.") - -# ------------------------- -# Write build.py (FIXED) -# ------------------------- -with open(OUTPUT, "w", encoding="utf-8") as out: - out.write("import os\n\n") - out.write(f"# Auto-generated — {len(FILES)} files\n\n") - out.write("FILES = [\n") - for p, c in FILES: - # LOSSLESS + SYNTAX SAFE - out.write(f" ({p!r}, {c!r}),\n") - out.write("]\n\n") - out.write("""\ -for path, content in FILES: - d = os.path.dirname(path) - if d: - os.makedirs(d, exist_ok=True) - with open(path, "w", encoding="utf-8") as f: - f.write(content) -print(f"OK — {len(FILES)} files extracted.") -""") - -print(f"[OK] {OUTPUT} written.") diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 2c985fc7..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,204 +0,0 @@ -name: Datapack Multi-Version Build - -on: - push: - branches: [main] - pull_request: - -jobs: - build: - runs-on: ubuntu-latest - - strategy: - matrix: - include: - # Base format + overlay pairs — each row is a target MC version - - label: "1_20_3" - pack_format: 26 - overlays: "1_20_3" - - label: "1_20_5" - pack_format: 41 - overlays: "1_20_5" - - label: "pre_1_21_4" - pack_format: 48 - overlays: "_pre_1_21_4 compat_1_21_4" - - label: "1_21_5" - pack_format: 71 - overlays: "1_21_5" - - label: "1_21_6" - pack_format: 80 - overlays: "1_21_6" - - label: "full" - pack_format: 101 - overlays: "" - - steps: - - uses: actions/checkout@v6 - - # 🔒 Repo lock — prevent running from wrong fork - - name: Repo lock - run: | - if [ "$GITHUB_REPOSITORY" != "runtoolkit/dataLib" ]; then - echo "::error::This workflow may only run in runtoolkit/dataLib." - exit 1 - fi - - # 📁 Prepare build directory - - name: Prepare build directory - run: mkdir -p build - - # 📂 Copy base layer (excluding overlay directories) - - name: Copy base layer - run: | - OVERLAY_DIRS="1_20_3 1_20_5 _pre_1_21_4 compat_1_21_4 1_21_5 1_21_6 26_1 26_2" - EXCLUDES="" - for d in $OVERLAY_DIRS; do - EXCLUDES="$EXCLUDES --exclude=$d" - done - rsync -a $EXCLUDES \ - --exclude='.git' \ - --exclude='.github' \ - --exclude='.gitattributes' \ - --exclude='.gitignore' \ - --exclude='README.md' \ - --exclude='version.json' \ - --exclude='dependencies.json' \ - ./ build/ - - # 📂 Copy overlays - - name: Copy target overlays - run: | - ALL_OVERLAYS="1_20_3 1_20_5 _pre_1_21_4 compat_1_21_4 1_21_5 1_21_6 26_1 26_2" - TARGET="${{ matrix.overlays }}" - - if [ -z "$TARGET" ]; then - # Full build: copy all overlay directories - for d in $ALL_OVERLAYS; do - [ -d "$d" ] && cp -r "$d" build/ || echo "::warning::Overlay '$d' not found, skipping." - done - else - # Targeted build: copy only the relevant overlay(s) - for overlay in $TARGET; do - if [ -d "$overlay" ]; then - cp -r "$overlay" build/ - else - echo "::warning::Overlay '$overlay' not found, skipping." - fi - done - fi - - # 🧩 pack.mcmeta — update pack_format, keep only relevant overlay entries - - name: Patch pack.mcmeta - run: | - LABEL="${{ matrix.label }}" - FORMAT="${{ matrix.pack_format }}" - OVERLAYS="${{ matrix.overlays }}" - - # Convert overlay list to jq array (may be empty for full build) - JQ_ARRAY="[]" - for ov in $OVERLAYS; do - JQ_ARRAY=$(echo "$JQ_ARRAY" | jq --arg d "$ov" '. + [$d]') - done - - jq \ - --argjson fmt "$FORMAT" \ - --argjson keep "$JQ_ARRAY" \ - --arg label "$LABEL" \ - ' - .pack.pack_format = $fmt | - if .overlays.entries and $label != "full" then - .overlays.entries = [ - .overlays.entries[] | - select(.directory as $d | $keep | index($d) != null) - ] - else . end - ' \ - build/pack.mcmeta > build/pack.mcmeta.tmp - mv build/pack.mcmeta.tmp build/pack.mcmeta - - # 🔍 Sanity check — is pack.mcmeta valid JSON? - - name: Validate pack.mcmeta - run: | - jq empty build/pack.mcmeta && echo "pack.mcmeta is valid JSON." \ - || { echo "::error::pack.mcmeta is malformed!"; exit 1; } - - # 📦 Zip - - name: Package datapack - run: | - cd build - zip -r ../dataLib-${{ matrix.label }}.zip . - echo "Archive size: $(du -sh ../dataLib-${{ matrix.label }}.zip | cut -f1)" - - # 🚀 Upload as artifact - - uses: actions/upload-artifact@v6 - with: - name: dataLib-${{ matrix.label }} - path: dataLib-${{ matrix.label }}.zip - retention-days: 14 - if-no-files-found: error - - # ─────────────────────────────────────────────── - # Release — only on main pushes, with full ZIP - # ─────────────────────────────────────────────── - release: - needs: build - runs-on: ubuntu-latest - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - - permissions: - contents: write - - steps: - - uses: actions/checkout@v6 - - # 🔒 Repo lock - - name: Repo lock - run: | - if [ "$GITHUB_REPOSITORY" != "runtoolkit/dataLib" ]; then - echo "::error::This workflow may only run in runtoolkit/dataLib." - exit 1 - fi - - # 📥 Download full ZIP from artifact - - uses: actions/download-artifact@v7 - with: - name: dataLib-full - path: dist/ - - # 🏷️ Read version number from pack.mcmeta - - name: Read version - id: ver - run: | - # Extract version like "v6.0.0" from the description array in pack.mcmeta - VERSION=$(jq -r ' - .pack.description | - if type == "array" then - .[] | select(type == "object") | - .text // empty - else . end | - select(test("^\\s*\\|\\s*v[0-9]")) | - gsub("^\\s*\\|\\s*"; "") | ltrimstr(" ") - ' pack.mcmeta | head -1) - # Fallback: if description is a plain string, extract directly - if [ -z "$VERSION" ]; then - VERSION=$(jq -r '.pack.description' pack.mcmeta | grep -oP 'v[\d.]+' | head -1) - fi - echo "version=${VERSION:-latest}" >> "$GITHUB_OUTPUT" - - # 🚀 Create or update GitHub Release - - name: Create or update release - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - TAG="${{ steps.ver.outputs.version }}" - ZIP="dist/dataLib-full.zip" - - # Delete existing tag if it exists, otherwise continue - gh release delete "$TAG" --yes 2>/dev/null || true - git push origin ":refs/tags/$TAG" 2>/dev/null || true - - gh release create "$TAG" \ - --title "dataLib $TAG" \ - --notes "**Full multi-version datapack build** (pack_format 101, all overlays included)." \ - --latest \ - "$ZIP#dataLib-full.zip" diff --git a/.github/workflows/dp-lint.yml b/.github/workflows/dp-lint.yml deleted file mode 100644 index f39c5af7..00000000 --- a/.github/workflows/dp-lint.yml +++ /dev/null @@ -1,77 +0,0 @@ -name: Datapack Lint -on: - push: - pull_request: -jobs: - lint: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Validate JSON - run: | - echo "Checking JSON..." - find . -name "*.json" -not -path "./.git/*" -print0 | xargs -0 -n1 jq empty - - - name: Lint mcfunction files (pre-check) - run: | - echo "Scanning .mcfunction files..." - FILES=$(find . -name "*.mcfunction" -not -path "./.git/*") - if [ -z "$FILES" ]; then - echo "No .mcfunction files found." - exit 0 - fi - ERRORS=0 - while IFS= read -r file; do - if head -c 3 "$file" | grep -qP '^\xef\xbb\xbf'; then - echo "BOM detected: $file" - ERRORS=$((ERRORS + 1)) - fi - if grep -Pql '\x00' "$file"; then - echo "Null byte detected: $file" - ERRORS=$((ERRORS + 1)) - fi - if file "$file" | grep -q "CRLF"; then - echo "CRLF line endings: $file" - ERRORS=$((ERRORS + 1)) - fi - done <<< "$FILES" - if [ "$ERRORS" -gt 0 ]; then - echo "$ERRORS issue(s) found in .mcfunction files." - exit 1 - fi - echo "Pre-check passed." - - - name: Install mecha - run: pip install git+https://github.com/mcbeet/mecha.git --break-system-packages -q - - - name: Patch mecha compat (colon-space in storage paths + 26.1 time syntax) - # 1. "namespace: path" — space after colon in storage targets. - # Valid in Minecraft, unsupported by mecha's parser. - # 2. "time of query" — dimension-scoped time command added in 26.1. - # Mecha only knows: time add|query|set. - # Normalized to "time query daytime" for validation purposes. - # 3. "time query day repetition" — new subcommand added in 26.1. - # Mecha does not recognize "repetition"; stripped for validation purposes. - # All changes apply to the runner copy only — source files are not modified. - run: | - find . -name "*.mcfunction" -not -path "./.git/*" \ - -exec grep -lP '[\w.-]+: _' {} \; | \ - xargs --no-run-if-empty sed -i -E 's/([a-z0-9._-]+): _/\1:_/g' - find . -name "*.mcfunction" -not -path "./.git/*" \ - -exec grep -l 'time of ' {} \; | \ - xargs --no-run-if-empty sed -i -E 's/time of [a-z0-9_:.-]+ /time /g' - find . -name "*.mcfunction" -not -path "./.git/*" \ - -exec grep -l 'time query day repetition' {} \; | \ - xargs --no-run-if-empty sed -i 's/time query day repetition/time query daytime/g' - - - name: Validate mcfunction syntax (mecha) - run: mecha . - - - name: Validate pack.mcmeta - run: | - if [ ! -f pack.mcmeta ]; then - echo "pack.mcmeta is missing!" - exit 1 - fi diff --git a/.github/workflows/generate-build-py.yml b/.github/workflows/generate-build-py.yml deleted file mode 100644 index 01924214..00000000 --- a/.github/workflows/generate-build-py.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: Generate Build.py - -on: - push: - branches: - - workflows - - pull_request: - - workflow_dispatch: - -permissions: - contents: write - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout Repository - uses: actions/checkout@v6 - with: - ref: ${{ github.ref }} - fetch-depth: 0 - - - name: Setup Python - uses: actions/setup-python@v6 - with: - python-version: "3.12" - - - name: Install Dependencies - run: | - python -m pip install --upgrade pip - - - name: Run Generate Build Script - run: | - python3 .github/scripts/generate_buid-py.py - - - name: Upload Build.py Artifact - uses: actions/upload-artifact@v7 - with: - name: build-py - path: build.py - - - name: Commit and Push build.py - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - - git add build.py - - git diff --cached --quiet && exit 0 - - git commit -m "Auto generate build.py" - - BRANCH_NAME="${GITHUB_REF#refs/heads/}" - - git push origin HEAD:$BRANCH_NAME diff --git a/.github/workflows/lint-and-build.yml b/.github/workflows/lint-and-build.yml deleted file mode 100644 index d38a84a1..00000000 --- a/.github/workflows/lint-and-build.yml +++ /dev/null @@ -1,88 +0,0 @@ -name: Lint & Build - -on: - push: - branches: [main] - pull_request: - branches: [main] - workflow_dispatch: - -jobs: - lint: - name: Mecha Lint - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Set up Python - uses: actions/setup-python@v6 - with: - python-version: "3.12" - - - name: Install mecha - run: pip install mecha - - - name: Run mecha (main datapack) - run: mecha data/ --version java:1.21.4 - - build: - name: Build ZIPs - runs-on: ubuntu-latest - needs: lint - if: github.event_name != 'pull_request' - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Build dataLib-dp.zip (datapack only) - run: | - zip -r dataLib-dp.zip \ - data/ \ - 1_20_3/ \ - 1_20_5/ \ - 1_21_5/ \ - 1_21_6/ \ - 26_1/ \ - 26_2/ \ - _pre_1_21_4/ \ - compat_1_21_4/ \ - pack.mcmeta \ - pack.png \ - LICENSE \ - - - name: Build dataLib-rp.zip (resource pack only) - run: | - mkdir -p _rp_build/assets - cp -r assets/* _rp_build/assets/ - cat > _rp_build/pack.mcmeta << 'MCMETA' - { - "pack": { - "pack_format": 46, - "supported_formats": { - "min_inclusive": 22, - "max_inclusive": 999 - }, - "description": [ - {"text": "dataLib", "color": "#00ccff", "bold": true}, - {"text": " Resource Pack", "color": "#aaaaaa"}, - {"text": " | TR/EN/DE + custom font & sounds", "color": "#888888"} - ] - } - } - MCMETA - cd _rp_build && zip -r ../dataLib-rp.zip . - - - name: Upload dataLib-dp.zip - uses: actions/upload-artifact@v6 - with: - name: dataLib-dp - path: dataLib-dp.zip - retention-days: 30 - - - name: Upload dataLib-rp.zip - uses: actions/upload-artifact@v6 - with: - name: dataLib-rp - path: dataLib-rp.zip - retention-days: 30 diff --git a/.github/workflows/no-op-workflow.yml b/.github/workflows/no-op-workflow.yml deleted file mode 100644 index 95a2fcee..00000000 --- a/.github/workflows/no-op-workflow.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: No-Op++ - -on: - workflow_dispatch: - -jobs: - noop: - runs-on: ubuntu-latest - steps: - - name: no_op - if: ${{ false }} - run: exit 0 From 3cac28f12322ef37c8179e9d029ee86fff6eab23 Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:52:49 +0300 Subject: [PATCH 10/19] Create ci.yml --- .github/workflows/ci.yml | 103 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..63dac4e3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,103 @@ +name: CI + +on: + push: + branches: [main, workflows] + pull_request: + branches: [main] + workflow_dispatch: + +permissions: + contents: write + +jobs: + # ===================================================================== + # lint — mecha, JSON, mcfunction pre-check, pack.mcmeta + # ===================================================================== + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "21" + - uses: actions/setup-python@v6 + with: + python-version: "3.12" + - name: Gradle lint + run: ./gradlew lint --no-daemon + + # ===================================================================== + # build — matrix zips (1_20_3, 1_20_5, pre_1_21_4, 1_21_5, 1_21_6, full) + # ===================================================================== + build: + needs: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "21" + - name: Gradle buildAll + run: ./gradlew buildAll --no-daemon + - uses: actions/upload-artifact@v6 + with: + name: dataLib-datapacks + path: build/dist/*.zip + retention-days: 14 + if-no-files-found: error + + # ===================================================================== + # release — only on push to main + # ===================================================================== + release: + needs: build + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "21" + - name: Gradle release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + ./gradlew zipFull --no-daemon + ./gradlew release --no-daemon + + # ===================================================================== + # generate-build-py — only on push to 'workflows' branch + # Kept out of Gradle: this job commits + pushes to the repo, which is + # a CI/VCS action, not a build responsibility. + # ===================================================================== + generate-build-py: + if: github.event_name == 'push' && github.ref == 'refs/heads/workflows' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.ref }} + fetch-depth: 0 + - uses: actions/setup-python@v6 + with: + python-version: "3.12" + - name: Run generate build script + run: python3 .github/scripts/generate_buid-py.py + - uses: actions/upload-artifact@v7 + with: + name: build-py + path: build.py + - name: Commit and push build.py + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add build.py + git diff --cached --quiet && exit 0 + git commit -m "Auto generate build.py" + git push origin HEAD:${GITHUB_REF#refs/heads/} From cd6725d1d28347849b4809b8dd6ae11467ee585d Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:53:04 +0300 Subject: [PATCH 11/19] Add files via upload --- build.gradle | 282 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 282 insertions(+) create mode 100644 build.gradle diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..d223e127 --- /dev/null +++ b/build.gradle @@ -0,0 +1,282 @@ +import groovy.json.JsonSlurper +import groovy.json.JsonOutput + +// ===================================================================== +// dataLib — Datapack build system (pure Gradle, no plugins required) +// Replaces: build.yml, dp-lint.yml, lint-and-build.yml +// generate-build-py.yml logic is kept in .github/workflows/ci.yml +// because it commits+pushes — not a build responsibility. +// ===================================================================== + +def REPO_SLUG = "runtoolkit/dataLib" +def ALL_OVERLAYS = ["1_20_3", "1_20_5", "_pre_1_21_4", "compat_1_21_4", "1_21_5", "1_21_6", "26_1", "26_2"] + +// label -> [pack_format, overlays[]] +def TARGETS = [ + "1_20_3" : [26 , ["1_20_3"]], + "1_20_5" : [41 , ["1_20_5"]], + "pre_1_21_4" : [48 , ["_pre_1_21_4", "compat_1_21_4"]], + "1_21_5" : [71 , ["1_21_5"]], + "1_21_6" : [80 , ["1_21_6"]], + "full" : [101, []] // empty = all overlays +] + +def buildRoot = layout.buildDirectory.dir("datapack").get().asFile +def distRoot = layout.buildDirectory.dir("dist").get().asFile + +// --------------------------------------------------------------------- +// Repo lock — refuse to run outside runtoolkit/dataLib +// --------------------------------------------------------------------- +tasks.register("repoLock") { + group = "verification" + description = "Fails the build if GITHUB_REPOSITORY != ${REPO_SLUG}" + doLast { + def repo = System.getenv("GITHUB_REPOSITORY") + if (repo != null && repo != REPO_SLUG) { + throw new GradleException("This build may only run in ${REPO_SLUG} (got: ${repo})") + } + } +} + +// --------------------------------------------------------------------- +// Lint: JSON validity +// --------------------------------------------------------------------- +tasks.register("lintJson") { + group = "verification" + description = "Validates every *.json file parses as JSON" + doLast { + def slurper = new JsonSlurper() + def failures = [] + fileTree(projectDir) { + include "**/*.json" + exclude ".git/**" + }.each { f -> + try { + slurper.parse(f) + } catch (Exception e) { + failures << "${f}: ${e.message}" + } + } + if (failures) { + failures.each { logger.error("JSON error: ${it}") } + throw new GradleException("${failures.size()} invalid JSON file(s) found.") + } + logger.lifecycle("All JSON files valid.") + } +} + +// --------------------------------------------------------------------- +// Lint: mcfunction pre-check (BOM / null byte / CRLF) +// --------------------------------------------------------------------- +tasks.register("lintMcfunctionPrecheck") { + group = "verification" + description = "Scans *.mcfunction files for BOM, null bytes, CRLF" + doLast { + def errors = 0 + fileTree(projectDir) { + include "**/*.mcfunction" + exclude ".git/**" + }.each { f -> + byte[] bytes = f.bytes + if (bytes.length >= 3 && (bytes[0] & 0xFF) == 0xEF && (bytes[1] & 0xFF) == 0xBB && (bytes[2] & 0xFF) == 0xBF) { + logger.error("BOM detected: ${f}") + errors++ + } + if (bytes.any { it == 0 }) { + logger.error("Null byte detected: ${f}") + errors++ + } + if (bytes.any { it == (byte) 0x0D }) { + logger.error("CRLF line endings: ${f}") + errors++ + } + } + if (errors > 0) { + throw new GradleException("${errors} issue(s) found in .mcfunction files.") + } + logger.lifecycle("mcfunction pre-check passed.") + } +} + +// --------------------------------------------------------------------- +// Lint: mecha syntax validation +// Mirrors dp-lint.yml's compat patching — applied to a scratch copy only, +// source files are never touched. +// --------------------------------------------------------------------- +tasks.register("lintMecha", Exec) { + group = "verification" + description = "Runs mecha against a patched scratch copy of the datapack" + dependsOn "lintMcfunctionPrecheck" + + def scratch = layout.buildDirectory.dir("mecha-scratch").get().asFile + + doFirst { + delete(scratch) + mkdir(scratch) + copy { + from(projectDir) { + exclude ".git/**", ".github/**" + } + into(scratch) + } + + fileTree(scratch) { include "**/*.mcfunction" }.each { f -> + def text = f.getText("UTF-8") + def patched = text + .replaceAll(/([a-z0-9._-]+): _/, '$1:_') + .replaceAll(/time of [a-z0-9_:.-]+ /, 'time ') + .replace('time query day repetition', 'time query daytime') + if (patched != text) { + f.setText(patched, "UTF-8") + } + } + + exec { + commandLine "pip", "install", "--break-system-packages", "-q", + "git+https://github.com/mcbeet/mecha.git" + } + } + + workingDir = scratch + commandLine "mecha", "." +} + +// --------------------------------------------------------------------- +// Lint: pack.mcmeta presence + validity +// --------------------------------------------------------------------- +tasks.register("lintPackMcmeta") { + group = "verification" + description = "Checks pack.mcmeta exists and is valid JSON" + doLast { + def meta = file("pack.mcmeta") + if (!meta.exists()) { + throw new GradleException("pack.mcmeta is missing!") + } + new JsonSlurper().parse(meta) + logger.lifecycle("pack.mcmeta is valid JSON.") + } +} + +tasks.register("lint") { + group = "verification" + description = "Runs all datapack lint checks" + dependsOn "lintJson", "lintMcfunctionPrecheck", "lintMecha", "lintPackMcmeta" +} + +// --------------------------------------------------------------------- +// Build: one task per matrix target (1_20_3, 1_20_5, pre_1_21_4, ...) +// --------------------------------------------------------------------- +def buildTasks = [] + +TARGETS.each { label, cfg -> + def (packFormat, overlays) = cfg + def stageDir = new File(buildRoot, label) + + def prepareTask = tasks.register("prepare${label.capitalize()}") { + group = "build" + description = "Assembles the ${label} datapack variant into build/datapack/${label}" + dependsOn "repoLock" + + doLast { + delete(stageDir) + mkdir(stageDir) + + // Copy base layer, excluding every overlay dir + repo/meta files + copy { + from(projectDir) { + ALL_OVERLAYS.each { exclude "${it}/**" } + exclude ".git/**", ".github/**", ".gitattributes", ".gitignore", + "README.md", "version.json", "dependencies.json" + } + into(stageDir) + } + + // Copy relevant overlays (empty overlays list = full build = copy all) + def toCopy = overlays.isEmpty() ? ALL_OVERLAYS : overlays + toCopy.each { ov -> + def src = file(ov) + if (src.isDirectory()) { + copy { from(src); into(new File(stageDir, ov)) } + } else { + logger.warn("Overlay '${ov}' not found, skipping.") + } + } + + // Patch pack.mcmeta: set pack_format, trim overlay entries to what we copied + def metaFile = new File(stageDir, "pack.mcmeta") + def meta = new JsonSlurper().parse(metaFile) + meta.pack.pack_format = packFormat + if (label != "full" && meta.overlays?.entries) { + meta.overlays.entries = meta.overlays.entries.findAll { it.directory in toCopy } + } + metaFile.text = JsonOutput.prettyPrint(JsonOutput.toJson(meta)) + } + } + + def zipTask = tasks.register("zip${label.capitalize()}", Zip) { + group = "build" + description = "Packages the ${label} datapack variant as a zip artifact" + dependsOn prepareTask + from(stageDir) + destinationDirectory.set(distRoot) + archiveFileName.set("dataLib-${label}.zip") + } + + buildTasks << zipTask +} + +tasks.register("buildAll") { + group = "build" + description = "Builds every datapack variant (equivalent to the old build.yml matrix)" + dependsOn buildTasks +} + +// --------------------------------------------------------------------- +// Release: reads version from the full build's pack.mcmeta, publishes +// via gh CLI. Only meaningful on push to main — that branch/event gate +// stays in ci.yml, not here, since it's CI trigger logic, not a build step. +// --------------------------------------------------------------------- +tasks.register("release") { + group = "publish" + description = "Creates/updates the GitHub release for the full build" + dependsOn "repoLock", "zipFull" + + doLast { + def metaFile = new File(buildRoot, "full/pack.mcmeta") + def meta = new JsonSlurper().parse(metaFile) + def desc = meta.pack.description + + String version = null + if (desc instanceof List) { + desc.each { entry -> + if (entry instanceof Map && entry.text) { + def m = (entry.text =~ /^\s*\|\s*(v[0-9][^\s]*)/) + if (m.find()) version = m.group(1) + } + } + } else if (desc instanceof String) { + def m = (desc =~ /v[\d.]+/) + if (m.find()) version = m.group(0) + } + version = version ?: "latest" + + def zip = new File(distRoot, "dataLib-full.zip") + + exec { commandLine "bash", "-c", "gh release delete '${version}' --yes 2>/dev/null || true" } + exec { commandLine "bash", "-c", "git push origin ':refs/tags/${version}' 2>/dev/null || true" } + exec { + commandLine "gh", "release", "create", version, + "--title", "dataLib ${version}", + "--notes", "**Full multi-version datapack build** (pack_format 101, all overlays included).", + "--latest", + "${zip}#dataLib-full.zip" + } + logger.lifecycle("Released ${version}") + } +} + +tasks.register("clean", Delete) { + group = "build" + description = "Deletes the build/ directory" + delete(layout.buildDirectory) +} From fba6bf22291378cb4c069785dd6fcc529f462569 Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:53:43 +0300 Subject: [PATCH 12/19] Add files via upload --- .github/CODE_OF_CONDUCT.md | 136 ++++++++++++++++ .github/CONTRIBUTING.md | 196 +++++++++++++++++++++++ .github/DISCUSSION_TEMPLATE/general.yml | 25 +++ .github/DISCUSSION_TEMPLATE/ideas.yml | 9 ++ .github/DISCUSSION_TEMPLATE/q-a.yml | 15 ++ .github/ISSUE_TEMPLATE/bug_report.yml | 67 ++++++++ .github/ISSUE_TEMPLATE/compatibility.yml | 65 ++++++++ .github/ISSUE_TEMPLATE/config.yml | 1 + .github/ISSUE_TEMPLATE/general.yml | 18 +++ .github/PULL_REQUEST_TEMPLATE.md | 28 ++++ .github/SECURITY.md | 89 ++++++++++ 11 files changed, 649 insertions(+) create mode 100644 .github/CODE_OF_CONDUCT.md create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/DISCUSSION_TEMPLATE/general.yml create mode 100644 .github/DISCUSSION_TEMPLATE/ideas.yml create mode 100644 .github/DISCUSSION_TEMPLATE/q-a.yml create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/compatibility.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/general.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/SECURITY.md diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..392b8186 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,136 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, testers, and maintainers pledge to make +participation in this Minecraft Java Edition datapack project a harassment-free +experience for everyone, regardless of age, body size, visible or invisible +disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +We commit to fostering a community that is technically rigorous, respectful, +and collaborative. + +--- + +## Our Standards + +Examples of behavior that contributes to a positive environment include: + +- Providing constructive feedback on datapack functions, commands, and architecture +- Reporting bugs or exploits responsibly +- Respecting different implementation strategies (scoreboard logic, storage patterns, NBT handling, etc.) +- Reviewing pull requests objectively and based on technical merit +- Taking responsibility for mistakes in code, documentation, or testing + +Examples of unacceptable behavior include: + +- Trolling, flame wars, or hostile arguments about implementation style +- Publicly posting unpatched exploits without responsible disclosure +- Harassment in issues, pull requests, discussions, or external platforms +- Publishing private information without explicit permission +- Submitting intentionally malicious code (e.g., crash loops, backdoors, hidden command execution) + +--- + +## Enforcement Responsibilities + +Project maintainers are responsible for enforcing this Code of Conduct. + +Maintainers may: + +- Remove or edit comments +- Close issues or pull requests +- Reject contributions +- Temporarily or permanently restrict participation + +Moderation decisions will be based on maintaining a healthy technical +environment and protecting contributors. + +--- + +## Scope + +This Code of Conduct applies to: + +- GitHub issues and pull requests +- Discussions and documentation +- Datapack releases and testing environments +- Any public space where the project is officially represented + +This includes representation via official repository accounts, +release announcements, and community channels. + +--- + +## Reporting + +Instances of abusive, harassing, exploitative, or otherwise unacceptable +behavior may be reported privately to the project maintainers via: + +- GitHub private contact +- Security Advisory system +- Official project contact address (if available) + +Do not open public issues for Code of Conduct violations. + +All reports will be reviewed promptly and handled confidentially. + +--- + +## Enforcement Guidelines + +The following graduated response model may be applied: + +### 1. Correction + +**Impact**: Minor unprofessional conduct or inappropriate tone. + +**Consequence**: Private warning and clarification of expected behavior. + +--- + +### 2. Warning + +**Impact**: Repeated minor violations or a single disruptive incident. + +**Consequence**: Formal warning. Temporary restriction from discussions +or interactions with specific contributors. + +--- + +### 3. Temporary Ban + +**Impact**: Serious misconduct, sustained hostility, or exploit abuse. + +**Consequence**: Temporary removal from project participation +(issues, pull requests, discussions). + +--- + +### 4. Permanent Ban + +**Impact**: Pattern of violations, harassment, malicious code submission, +or intentional damage to the project or its community. + +**Consequence**: Permanent removal from all project spaces. + +--- + +## Additional Datapack-Specific Policy + +- Security exploits must be disclosed responsibly. +- Malicious pull requests (e.g., hidden `execute` abuse, infinite loops, + unauthorized storage writes) will result in immediate review and potential ban. +- Performance sabotage (intentional lag mechanisms) is treated as misconduct. + +--- + +## Attribution + +This Code of Conduct is adapted from Contributor Covenant version 2.0: +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html + +Community Impact Guidelines were inspired by Mozilla’s enforcement model: +https://github.com/mozilla/diversity diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 00000000..c3c7151d --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,196 @@ +# Contributing Guide +Thank you for considering contributing to this Minecraft Java Edition datapack. +This document defines contribution standards, architectural constraints, and review criteria. +All contributions are evaluated based on stability, performance, security, and long-term maintainability. +--- +## Project Philosophy +This datapack prioritizes: +- Deterministic and predictable behavior +- Server-side performance optimization +- Secure and auditable command architecture +- Backward compatibility within supported versions +- Minimal unnecessary tick execution +Contributions that conflict with these principles may be rejected. +--- +## Before You Contribute +Ensure the following before opening a pull request: +- You are targeting a supported version (see `SECURITY.md`) +- All changes are tested in a clean world +- No debug scoreboards or temporary storage values remain +- No development-only functions are included +- No experimental snapshot-only features are used +- No performance regression is introduced +--- +## Development Standards +### 1. Namespacing +- All functions must use the project namespace. +- No pollution of the `minecraft:` namespace. +- Cross-namespace execution must be justified and documented. +--- +### 2. Function Design +- No infinite loops. +- No recursive `execute` chains without guaranteed termination. +- Minimize logic inside `tick` functions. +- Prefer `storage` over unnecessary scoreboards when appropriate. +- Use targeted selectors instead of broad selectors (`@a`, `@e`) whenever possible. +--- +### 3. Performance Rules +Contributions must avoid: +- Unbounded `@e` scans +- Nested `execute as @a at @a` patterns +- Heavy NBT checks every tick +- Excessive scoreboard writes +- Redundant function calls per tick +Any measurable performance regression may result in rejection. +--- +### 4. Security Expectations +Pull requests must NOT include: +- Hidden command execution +- Permission bypass mechanisms +- Backdoors or obfuscated logic +- Crash triggers or lag exploits +- Storage manipulation outside the defined schema +Security-sensitive changes must include a technical explanation. +--- +## Pull Request Process +1. Fork the repository. +2. Create a dedicated feature branch. +3. Keep commits atomic and clearly described. +4. Open a pull request including: + - Clear description of changes + - Technical explanation + - Performance impact analysis + - Testing steps + - Relevant logs or screenshots (if applicable) +Incomplete or low-effort pull requests may be closed without review. +--- +## Code Style +- Consistent indentation +- No trailing whitespace +- Clear and descriptive function naming +- Comment complex logic +- Avoid redundant or duplicated commands +### Example Function Structure +```mcfunction +# Initialize scoreboard +scoreboard objectives add example dummy +# Controlled execution +execute as @a[scores={example=1..}] run function namespace:feature/run +``` +--- +## Bug Reports +When opening an issue, include: +* Datapack version +* Minecraft version +* Steps to reproduce +* Expected result +* Actual result +* Relevant logs +Vague or incomplete reports may be closed. +--- +## Feature Requests +Feature requests must include: +* Clear use case +* Performance consideration +* Compatibility analysis +* Explanation of alignment with project philosophy +--- +## Known Pitfalls & Decisions + +This section documents recurring mistakes and deliberate decisions to prevent regressions +and reduce contributor confusion. + +### pack_format +Always use the exact `pack_format` value for the target version range. +Do not increment version numbers without a meaningful content change — this breaks +semver-aware compatibility checks and downstream dependency guards. +For multi-version support, use `supported_formats` (≤1.21.4) or `min_format`/`max_format` +at root level (1.21.5+ / pack format 61+). Do not mix both forms. + +### Text Component Syntax (`clickEvent` vs `click_event`) +- `clickEvent` — legacy camelCase key, valid in all versions up to and including current. +- `click_event` — new snake_case key introduced in 1.21.5. +Both keys are currently accepted for backward compatibility, but **all new files must use +`click_event`** to remain forward-compatible with future deprecation of the camelCase form. +Do not silently convert one to the other without verifying the target version range. + +### Macros and Storage +- `$` prefix is required only on lines that contain a `$(var)` substitution. + Do not apply it globally. +- `$(var)` accepts strings only — full JSON text components cannot be passed as macro arguments. + Pre-resolve storage values via `function … with storage` before passing to macros. +- Use `set` (not `append`) on shared storage paths to avoid unintended accumulation. +- Always guard storage initialization with `execute unless data`. + +### Fake Files and Unverified Links +Do not reference file paths, download links, or external URLs that have not been verified +to exist. Placeholder domains (e.g. `example.com`, `files.examplecdn.com`) are not +acceptable in documentation or code comments. + +### No Version Inflation +Version numbers must reflect actual changes: +- Patch (`x.x.Z`): bug fixes only. +- Minor (`x.Y.0`): new features, backward-compatible. +- Major (`X.0.0`): breaking changes. +Incrementing a version number without a corresponding changelog entry will be rejected. + +--- +## AI-Assisted Contributions + +Using AI tools (ChatGPT, Claude, etc.) to help write code or documentation is allowed, +but contributions must meet the same standards as hand-written code. +AI output is not a substitute for understanding — **you are responsible for everything you submit.** + +### Common AI Mistakes to Verify Before Submitting + +**pack_format / version numbers** +AI models frequently output incorrect or inconsistent `pack_format` values. +Always verify against the [official wiki](https://minecraft.wiki/w/Pack_format) before submitting. + +**Hallucinated links and file references** +AI tools may produce plausible-looking but entirely fake URLs, CDN paths, or file names. +Never include a link or file reference you have not personally verified exists. + +**Syntax versioning (`clickEvent` vs `click_event`, camelCase vs snake_case)** +AI models often mix old and new key formats within the same output. +Cross-check all JSON text component keys, NBT keys, and advancement event names +against the target version. See [Known Pitfalls & Decisions](#known-pitfalls--decisions). + +**Macro usage** +AI-generated macro functions frequently apply the `$` prefix to non-substitution lines, +or attempt to pass full JSON text components as `$(var)` arguments (not supported). +Review every macro file manually. + +**Fabricated wait times and progress claims** +Disregard any AI output that claims a file "is being prepared" or will be ready after a delay. +If the content is not in the response, it does not exist. + +**Version inflation** +AI tools tend to increment version numbers across iterations without actual changes. +Use your own judgment for versioning — do not copy version strings from AI output blindly. + +### Checklist for AI-Assisted PRs +Before opening a pull request that involved AI assistance: +- [ ] All `pack_format` values manually verified +- [ ] No unverified external links or file paths +- [ ] All text component keys match target version syntax +- [ ] Macro `$` prefix applied only to substitution lines +- [ ] Version number reflects actual change scope +- [ ] Logic understood and tested — not submitted as-is from AI output + +--- +## What Will Be Rejected +* Snapshot-only features +* Performance-heavy mechanics without justification +* Overengineered abstractions +* Features outside project scope +* Low-effort or poorly tested contributions +--- +## Final Review Criteria +All contributions are evaluated based on: +* Stability +* Security +* Performance +* Maintainability +* Architectural consistency +Quality over quantity. diff --git a/.github/DISCUSSION_TEMPLATE/general.yml b/.github/DISCUSSION_TEMPLATE/general.yml new file mode 100644 index 00000000..c78adb70 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/general.yml @@ -0,0 +1,25 @@ +title: "[Question]" +body: + - type: textarea + id: summary + attributes: + label: Summary + description: Provide a short summary of your question about the datapack. + placeholder: "What's your question about (function, macro, GUI, etc.)?" + - type: textarea + id: details + attributes: + label: Details + description: Give detailed information, including Minecraft version and datapack version. + placeholder: "Include relevant context, errors, screenshots, or example commands." + - type: dropdown + id: category + attributes: + label: Category + description: Select the category that best fits your question. + options: + - Function / Macro + - Custom Items + - GUI / Admin Panel + - NPC / Entity + - Other diff --git a/.github/DISCUSSION_TEMPLATE/ideas.yml b/.github/DISCUSSION_TEMPLATE/ideas.yml new file mode 100644 index 00000000..638d654f --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/ideas.yml @@ -0,0 +1,9 @@ +title: "[Idea]" +labels: ["idea"] +body: + - type: textarea + id: idea + attributes: + label: "Your Idea" + description: "Share your idea for the datapack." + placeholder: "Explain what you think could improve the datapack." diff --git a/.github/DISCUSSION_TEMPLATE/q-a.yml b/.github/DISCUSSION_TEMPLATE/q-a.yml new file mode 100644 index 00000000..526e73ca --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/q-a.yml @@ -0,0 +1,15 @@ +title: "[Q&A]" +labels: ["question"] +body: + - type: textarea + id: question + attributes: + label: "Question" + description: "Ask your question about DataLib." + placeholder: "For example: How do I create a macro that…?" + - type: input + id: mc_version + attributes: + label: "Minecraft Version" + description: "Which Minecraft version are you using?" + placeholder: "1.21.8" diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..c5556558 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,67 @@ +name: Bug Report +description: Report a bug or unexpected behavior +title: "[BUG] " +labels: [ bug ] +body: + - type: markdown + attributes: + value: | + Please check for existing issues before reporting. + - type: input + id: ame_version + attributes: + label: D.L. Version + placeholder: "e.g.: v1.0.6-pre4" + validations: + required: true + - type: dropdown + id: mc_version + attributes: + label: Minecraft Version + options: + - "1.21.1" + - "1.21.2 / 1.21.3" + - "1.21.4" + - "1.21.5" + - "1.21.6+" + validations: + required: true + - type: dropdown + id: environment + attributes: + label: Environment + options: + - Singleplayer + - Multiplayer (vanilla server) + - Multiplayer (Paper/Spigot) + - Multiplayer (Fabric) + validations: + required: true + - type: textarea + id: description + attributes: + label: Description of the Bug + description: What happened? What did you expect to happen? + validations: + required: true + - type: textarea + id: reproduction + attributes: + label: Steps to Reproduce + placeholder: | + 1. Run `/function datalib:...` + 2. ... + validations: + required: true + - type: textarea + id: affected_functions + attributes: + label: Affected Functions + description: Which `datalib:` function(s) are affected? + placeholder: "e.g.: datalib:flag/toggle_system, datalib:lib/tick_guard" + - type: textarea + id: logs + attributes: + label: Logs / Error Output + description: In-game error messages or content from `.minecraft/logs/latest.txt` + render: text diff --git a/.github/ISSUE_TEMPLATE/compatibility.yml b/.github/ISSUE_TEMPLATE/compatibility.yml new file mode 100644 index 00000000..6423ed38 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/compatibility.yml @@ -0,0 +1,65 @@ +name: Version Compatibility Issue +description: Report something that is not working in a specific Minecraft version +title: "[COMPAT] " +labels: [ compatibility ] +body: + - type: input + id: ame_version + attributes: + label: D.L. Version + placeholder: "e.g.: v1.0.6-pre4" + validations: + required: true + - type: dropdown + id: broken_version + attributes: + label: Minecraft Version Where the Issue Occurs + options: + - "1.20.3" + - "1.20.4" + - "1.21" + - "1.21.1" + - "1.21.2 / 1.21.3" + - "1.21.4" + - "1.21.5" + - "1.21.6" + - "1.21.7" + - "1.21.8" + - "1.21.9" + - "1.21.10" + - "1.21.11" + - "26.1" + - "26.1.1" + - "26.1.2" + validations: + required: true + - type: dropdown + id: working_version + attributes: + label: Working Minecraft Version (if known) + options: + - I don't know + - "1.21.1" + - "1.21.2 / 1.21.3" + - "1.21.4" + - "1.21.5" + - "1.21.6+" + - type: input + id: pack_format + attributes: + label: pack_format Value + description: From F3 screen or pack.mcmeta + placeholder: "e.g.: 61" + - type: textarea + id: description + attributes: + label: Description of the Issue + description: Which command/function is not working? What error does it give? + validations: + required: true + - type: textarea + id: error + attributes: + label: Error Message + render: text + placeholder: "Unknown function / Invalid variable for command / ..." diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..3ba13e0c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/.github/ISSUE_TEMPLATE/general.yml b/.github/ISSUE_TEMPLATE/general.yml new file mode 100644 index 00000000..0ac3f984 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/general.yml @@ -0,0 +1,18 @@ +name: General +description: For anything that doesn't fit into the other categories +title: "[General] " +labels: + - general +body: + - type: textarea + id: description + attributes: + label: Description + validations: + required: true + - type: textarea + id: extra + attributes: + label: Additional Context / Information + validations: + required: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..299c0da5 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,28 @@ +## Summary + + +## Type +- [ ] Bug fix +- [ ] New module / function +- [ ] Breaking change +- [ ] Documentation +- [ ] Other + +## Module(s) Affected + + +## Testing + +- Minecraft version: +- pack_format: + +## Checklist +- [ ] Functions are commented with module path and context (`# @context`) +- [ ] No hardcoded player selectors where macro input is expected +- [ ] `pack.mcmeta` `supported_formats` uses object syntax (not array) +- [ ] `min_format` / `max_format` present and consistent with `formats` ranges +- [ ] Overlay `formats` ranges do not overlap +- [ ] Overlay added if version-specific behavior changed + +## Related Issues + diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 00000000..b7e98fa5 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,89 @@ +# Security Policy + +## Supported Versions + +This project is a Minecraft Java Edition datapack. +Security updates are only provided for the following versions: + +| Version | Supported | +| ------- | --------- | +| 5.0.x | :white_check_mark: | +| 3.0.x | :x: | +| < 3.0 | :x: | + +### Notes + +- Only **critical vulnerabilities** are patched. +- No backports for unsupported versions. +- Minecraft snapshot versions are not officially supported. +- Performance issues are not considered security issues unless they cause server crashes or denial-of-service behavior. + +--- + +## Reporting a Vulnerability + +If you discover a security vulnerability, **do NOT open a public issue.** + +### What qualifies as a vulnerability? + +Examples include: + +- Permission bypass via `execute` +- Scoreboard manipulation exploits +- NBT injection +- Storage tampering +- Unauthorized admin function execution +- Infinite loop / server crash triggers +- Selector abuse (`@a`, `@e`) causing unintended execution + +--- + +## How to Report + +Report privately using one of the following methods: + +1. GitHub Security Advisory (preferred) +2. Direct contact with the maintainer + +--- + +## Required Report Information + +Please include: + +- Affected datapack version +- Minecraft version +- Technical explanation of the exploit +- Steps to reproduce +- Expected behavior +- Actual behavior +- Proof-of-concept (minimal test world or commands, if possible) + +Incomplete reports may not be processed. + +--- + +## Response Policy + +- Initial response: within 72 hours +- Status update: within 7 days +- Target fix time for critical issues: 14 days + +If the vulnerability is confirmed: + +- A patched version will be released +- The advisory will be published after the fix +- The reporter may be credited (optional) + +If declined: + +- A technical explanation will be provided +- No further action will be taken + +--- + +## Scope + +This security policy applies only to the official release versions of this datapack. + +Modifications, forks, unofficial builds, or altered distributions are not covered. From bd1c7ba7dcf71389941d89b1449c02a48fc78814 Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:56:19 +0300 Subject: [PATCH 13/19] Add files via upload --- gradle/wrapper/gradle-wrapper.properties | 7 + gradlew | 249 +++++++++++++++++++++++ gradlew.bat | 92 +++++++++ 3 files changed, 348 insertions(+) create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..dab2a015 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists \ No newline at end of file diff --git a/gradlew b/gradlew new file mode 100644 index 00000000..b740cf13 --- /dev/null +++ b/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..25da30db --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega From 6d6e789f759826fa423ba2b6d09314dd90cb9b69 Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:59:03 +0300 Subject: [PATCH 14/19] Add files via upload --- gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 48966 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 gradle/wrapper/gradle-wrapper.jar diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d997cfc60f4cff0e7451d19d49a82fa986695d07 GIT binary patch literal 48966 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

`Lem|V$A>eR0uN8Y&sdjtu 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

1bvi{DS7E@q1{$_8->K{_OWv2 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 literal 0 HcmV?d00001 From 0294906f8ea570f1696e3158021551afbf57c2f8 Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Mon, 6 Jul 2026 12:01:36 +0300 Subject: [PATCH 15/19] Update ci.yml --- .github/workflows/ci.yml | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 63dac4e3..392fedfa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,21 @@ permissions: contents: write jobs: + + setup: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: "21" + - uses: actions/setup-python@v6 + with: + python-version: "3.12" + - name: Gradle lint + run: chmod +x gradlew + # ===================================================================== # lint — mecha, JSON, mcfunction pre-check, pack.mcmeta # ===================================================================== @@ -18,7 +33,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: distribution: temurin java-version: "21" @@ -36,13 +51,13 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: distribution: temurin java-version: "21" - name: Gradle buildAll run: ./gradlew buildAll --no-daemon - - uses: actions/upload-artifact@v6 + - uses: actions/upload-artifact@v7 with: name: dataLib-datapacks path: build/dist/*.zip @@ -58,7 +73,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: distribution: temurin java-version: "21" @@ -80,7 +95,7 @@ jobs: permissions: contents: write steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 with: ref: ${{ github.ref }} fetch-depth: 0 From 54c5d380cb0874d3764576922092aa9c335c8747 Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Mon, 6 Jul 2026 12:03:56 +0300 Subject: [PATCH 16/19] Update ci.yml --- .github/workflows/ci.yml | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 392fedfa..041bd752 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,21 +11,6 @@ permissions: contents: write jobs: - - setup: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: actions/setup-java@v5 - with: - distribution: temurin - java-version: "21" - - uses: actions/setup-python@v6 - with: - python-version: "3.12" - - name: Gradle lint - run: chmod +x gradlew - # ===================================================================== # lint — mecha, JSON, mcfunction pre-check, pack.mcmeta # ===================================================================== @@ -41,7 +26,7 @@ jobs: with: python-version: "3.12" - name: Gradle lint - run: ./gradlew lint --no-daemon + run: chmod +x gradlew && ./gradlew lint --no-daemon # ===================================================================== # build — matrix zips (1_20_3, 1_20_5, pre_1_21_4, 1_21_5, 1_21_6, full) @@ -56,7 +41,7 @@ jobs: distribution: temurin java-version: "21" - name: Gradle buildAll - run: ./gradlew buildAll --no-daemon + run: chmod +x gradlew && ./gradlew buildAll --no-daemon - uses: actions/upload-artifact@v7 with: name: dataLib-datapacks @@ -81,6 +66,7 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | + chmod +x gradlew ./gradlew zipFull --no-daemon ./gradlew release --no-daemon From 6041767a92b11ed4656f5bab04e7c77fcfba7a73 Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Mon, 6 Jul 2026 12:09:01 +0300 Subject: [PATCH 17/19] Create gradle.properties --- gradle.properties | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 gradle.properties diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..3a0b2ab3 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,2 @@ +org.gradle.jvmargs=-Xmx1536m -XX:MaxMetaspaceSize=512m +org.gradle.daemon=false From a68f869f4c27d71b9903e9b6994e1ea5da2b15ae Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Mon, 6 Jul 2026 12:15:54 +0300 Subject: [PATCH 18/19] Refactor build.gradle to remove overlays Removed overlay handling from TARGETS and build tasks. Updated release notes to reflect the new pack_format. --- build.gradle | 33 +++++---------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/build.gradle b/build.gradle index d223e127..d445bdc8 100644 --- a/build.gradle +++ b/build.gradle @@ -9,16 +9,10 @@ import groovy.json.JsonOutput // ===================================================================== def REPO_SLUG = "runtoolkit/dataLib" -def ALL_OVERLAYS = ["1_20_3", "1_20_5", "_pre_1_21_4", "compat_1_21_4", "1_21_5", "1_21_6", "26_1", "26_2"] -// label -> [pack_format, overlays[]] +// label -> pack_format. Single target now that overlays are gone. def TARGETS = [ - "1_20_3" : [26 , ["1_20_3"]], - "1_20_5" : [41 , ["1_20_5"]], - "pre_1_21_4" : [48 , ["_pre_1_21_4", "compat_1_21_4"]], - "1_21_5" : [71 , ["1_21_5"]], - "1_21_6" : [80 , ["1_21_6"]], - "full" : [101, []] // empty = all overlays + "full" : 101 ] def buildRoot = layout.buildDirectory.dir("datapack").get().asFile @@ -168,8 +162,7 @@ tasks.register("lint") { // --------------------------------------------------------------------- def buildTasks = [] -TARGETS.each { label, cfg -> - def (packFormat, overlays) = cfg +TARGETS.each { label, packFormat -> def stageDir = new File(buildRoot, label) def prepareTask = tasks.register("prepare${label.capitalize()}") { @@ -181,34 +174,18 @@ TARGETS.each { label, cfg -> delete(stageDir) mkdir(stageDir) - // Copy base layer, excluding every overlay dir + repo/meta files copy { from(projectDir) { - ALL_OVERLAYS.each { exclude "${it}/**" } exclude ".git/**", ".github/**", ".gitattributes", ".gitignore", "README.md", "version.json", "dependencies.json" } into(stageDir) } - // Copy relevant overlays (empty overlays list = full build = copy all) - def toCopy = overlays.isEmpty() ? ALL_OVERLAYS : overlays - toCopy.each { ov -> - def src = file(ov) - if (src.isDirectory()) { - copy { from(src); into(new File(stageDir, ov)) } - } else { - logger.warn("Overlay '${ov}' not found, skipping.") - } - } - - // Patch pack.mcmeta: set pack_format, trim overlay entries to what we copied + // Patch pack.mcmeta: set pack_format only. No overlay entries to filter anymore. def metaFile = new File(stageDir, "pack.mcmeta") def meta = new JsonSlurper().parse(metaFile) meta.pack.pack_format = packFormat - if (label != "full" && meta.overlays?.entries) { - meta.overlays.entries = meta.overlays.entries.findAll { it.directory in toCopy } - } metaFile.text = JsonOutput.prettyPrint(JsonOutput.toJson(meta)) } } @@ -267,7 +244,7 @@ tasks.register("release") { exec { commandLine "gh", "release", "create", version, "--title", "dataLib ${version}", - "--notes", "**Full multi-version datapack build** (pack_format 101, all overlays included).", + "--notes", "**Datapack build** (pack_format 107).", "--latest", "${zip}#dataLib-full.zip" } From 3a204689b7a05db6e76c0f81032f29eb9c34cbf0 Mon Sep 17 00:00:00 2001 From: Legends11 <235496468+tickwarden@users.noreply.github.com> Date: Mon, 6 Jul 2026 12:24:17 +0300 Subject: [PATCH 19/19] Add repack task and update release notes format Added a 'repack' task to trim dist zip files to only include pack.mcmeta and data/, replacing the original zip files. Updated release notes format in the release task. --- build.gradle | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d445bdc8..993f40b9 100644 --- a/build.gradle +++ b/build.gradle @@ -208,6 +208,44 @@ tasks.register("buildAll") { dependsOn buildTasks } +// --------------------------------------------------------------------- +// Repack: strips every dist zip down to pack.mcmeta + data/ only. +// Deletes the original zip and writes the trimmed one in its place. +// --------------------------------------------------------------------- +tasks.register("repack") { + group = "build" + description = "Trims each dist zip to pack.mcmeta + data/ only, replacing the original" + dependsOn buildTasks + + doLast { + def scratch = layout.buildDirectory.dir("repack-scratch").get().asFile + + distRoot.listFiles({ f -> f.name.endsWith(".zip") } as FileFilter)?.each { zipFile -> + def name = zipFile.name + delete(scratch) + mkdir(scratch) + + copy { + from(zipTree(zipFile)) { + include "pack.mcmeta" + include "data/**" + } + into(scratch) + } + + if (!new File(scratch, "pack.mcmeta").exists()) { + throw new GradleException("${name}: pack.mcmeta missing after filtering — refusing to repack.") + } + + delete(zipFile) + + ant.zip(destfile: zipFile, basedir: scratch) + + logger.lifecycle("Repacked ${name} -> pack.mcmeta + data/ only") + } + } +} + // --------------------------------------------------------------------- // Release: reads version from the full build's pack.mcmeta, publishes // via gh CLI. Only meaningful on push to main — that branch/event gate @@ -244,7 +282,7 @@ tasks.register("release") { exec { commandLine "gh", "release", "create", version, "--title", "dataLib ${version}", - "--notes", "**Datapack build** (pack_format 107).", + "--notes", "**Datapack build** (pack_format 101).", "--latest", "${zip}#dataLib-full.zip" }