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/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..041bd752 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,104 @@ +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@v5 + with: + distribution: temurin + java-version: "21" + - uses: actions/setup-python@v6 + with: + python-version: "3.12" + - name: Gradle lint + 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) + # ===================================================================== + build: + needs: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: "21" + - name: Gradle buildAll + run: chmod +x gradlew && ./gradlew buildAll --no-daemon + - uses: actions/upload-artifact@v7 + 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@v5 + with: + distribution: temurin + java-version: "21" + - name: Gradle release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + chmod +x gradlew + ./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@v7 + 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/} 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 }}" 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 diff --git a/build.gradle b/build.gradle index ea30027b..993f40b9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,48 +1,297 @@ -plugins { - id 'fabric-loom' version '1.6-SNAPSHOT' +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" + +// label -> pack_format. Single target now that overlays are gone. +def TARGETS = [ + "full" : 101 +] + +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", "." } -version = '1.0.0' -group = 'com.runtoolkit' +// --------------------------------------------------------------------- +// 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.") + } +} -repositories { - mavenCentral() - maven { url 'https://maven.fabricmc.net/' } +tasks.register("lint") { + group = "verification" + description = "Runs all datapack lint checks" + dependsOn "lintJson", "lintMcfunctionPrecheck", "lintMecha", "lintPackMcmeta" } -dependencies { - minecraft 'com.mojang:minecraft:1.21.5' - mappings 'net.fabricmc:yarn:1.21.5+build.1:v2' +// --------------------------------------------------------------------- +// Build: one task per matrix target (1_20_3, 1_20_5, pre_1_21_4, ...) +// --------------------------------------------------------------------- +def buildTasks = [] + +TARGETS.each { label, packFormat -> + 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 { + from(projectDir) { + exclude ".git/**", ".github/**", ".gitattributes", ".gitignore", + "README.md", "version.json", "dependencies.json" + } + into(stageDir) + } - modImplementation 'net.fabricmc:fabric-loader:0.16.10' - modImplementation 'net.fabricmc.fabric-api:fabric-api:0.119.0+1.21.5' + // 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 + 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") + } - // 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' + buildTasks << zipTask } -loom { - runs { - gametest { - server() - name 'Game Test' - vmArgs '-Dfabric-api.gametest=true' - vmArgs '-Dfabric-api.gametest.report.file=build/test-report.xml' +tasks.register("buildAll") { + group = "build" + description = "Builds every datapack variant (equivalent to the old build.yml matrix)" + 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") } } } -tasks.register('runGameTestServer', JavaExec) { - dependsOn 'classes' - group = 'fabric' - description = 'Runs the GameTest server' +// --------------------------------------------------------------------- +// 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 - classpath = sourceSets.main.runtimeClasspath - main = 'net.fabricmc.loader.impl.launch.knot.KnotServer' + 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") - args = ['--nogui'] - systemProperties = [ - 'fabric-api.gametest': 'true', - 'fabric-api.gametest.report.file': 'build/test-report.xml' - ] -} \ No newline at end of file + 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", "**Datapack build** (pack_format 101).", + "--latest", + "${zip}#dataLib-full.zip" + } + logger.lifecycle("Released ${version}") + } +} + +tasks.register("clean", Delete) { + group = "build" + description = "Deletes the build/ directory" + delete(layout.buildDirectory) +} 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 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..d997cfc6 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradlew b/gradlew old mode 100755 new mode 100644 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() 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 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