From 9b460d2f75ff00aabdb0e77846c227362851f598 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Sun, 22 Mar 2026 17:44:47 +0100 Subject: [PATCH] Android: adapt CI to bundle the assets --- .github/workflows/ci.yml | 28 ++++++++++------ scripts/build-graphics-dat | 23 ++++++++++++++ scripts/prepare-android-assets | 41 ++++++++++++++++++++++++ src/openrct2-android/app/build.gradle | 46 +++++++++++++++++++++++++++ 4 files changed, 129 insertions(+), 9 deletions(-) create mode 100755 scripts/build-graphics-dat create mode 100755 scripts/prepare-android-assets diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ca650df78..37abf07008 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -176,10 +176,7 @@ jobs: echo "$GITHUB_WORKSPACE/bin" >> $GITHUB_PATH - name: Build graphics .dat files run: | - gxc build g2.dat OpenRCT2/resources/g2/sprites.json - gxc build palettes.dat OpenRCT2/resources/palettes/sprites.json - gxc build fonts.dat OpenRCT2/resources/fonts/sprites.json - gxc build tracks.dat OpenRCT2/resources/tracks/sprites.json + OpenRCT2/scripts/build-graphics-dat "$PWD" - name: Upload graphics .dat files uses: actions/upload-artifact@v6 with: @@ -648,14 +645,18 @@ jobs: # The gradle cache is by default ~/.gradle/caches, but ~ gets resolved by GitHub Actions to the runner's # home directory (/home/github), not the user inside the container (/root). Name the path explicitly. path: /root/.gradle/caches - key: gradle-${{ hashFiles('src/openrct2-android/gradle.properties') }} + # The cache key needs to occasionally be updated as the cache itself is immutable, otherwise it can grow stale. The easiest way to force this is to include the tag, which changes every month. + # Keep in sync with the cache key used in the `android` job. + key: gradle-${{ runner.os }}-${{ needs.build_variables.outputs.tag }}-android-${{ hashFiles('src/openrct2-android/build.gradle', 'src/openrct2-android/settings.gradle', 'src/openrct2-android/app/build.gradle', 'src/openrct2-android/gradle.properties', 'src/openrct2-android/gradle/wrapper/gradle-wrapper.properties') }} + restore-keys: | + gradle-${{ runner.os }}-${{ needs.build_variables.outputs.tag }}-android- - name: Install GCC problem matcher uses: ammaraskar/gcc-problem-matcher@master - name: Build Native Libs run: | . scripts/setenv pushd src/openrct2-android - ./gradlew app:externalNativeBuildRelease -PabiFilter=${{ matrix.arch }} + ./gradlew app:externalNativeBuildRelease -PabiFilter=${{ matrix.arch }} -PskipAssets -PskipJava popd - name: Upload Native Libs uses: actions/upload-artifact@v6 @@ -667,8 +668,8 @@ jobs: android: name: Android runs-on: ubuntu-latest - needs: [android-libs, build_variables] - container: openrct2/openrct2-build:25-android + needs: [android-libs, build_variables, g2dat] + container: ghcr.io/openrct2/openrct2-build:26-android steps: - name: Checkout uses: actions/checkout@v6 @@ -678,7 +679,10 @@ jobs: # The gradle cache is by default ~/.gradle/caches, but ~ gets resolved by GitHub Actions to the runner's # home directory (/home/github), not the user inside the container (/root). Name the path explicitly. path: /root/.gradle/caches - key: gradle-${{ hashFiles('src/openrct2-android/gradle.properties') }} + # Keep in sync with the cache key used in the `android-libs` job. + key: gradle-${{ runner.os }}-${{ needs.build_variables.outputs.tag }}-android-${{ hashFiles('src/openrct2-android/build.gradle', 'src/openrct2-android/settings.gradle', 'src/openrct2-android/app/build.gradle', 'src/openrct2-android/gradle.properties', 'src/openrct2-android/gradle/wrapper/gradle-wrapper.properties') }} + restore-keys: | + gradle-${{ runner.os }}-${{ needs.build_variables.outputs.tag }}-android- - name: Setup keystore run: | echo "${{ secrets.OPENRCT2_KEYSTORE_CONTENTS }}" | base64 -d > keystore.jks @@ -700,9 +704,15 @@ jobs: echo "::warning title=Missing libraries::Warning: No libs found for $arch" fi done + - name: Download graphics .dat files + uses: actions/download-artifact@v7 + with: + name: graphics-${{ needs.build_variables.outputs.name }} + path: bin/data - name: Build OpenRCT2 run: | . scripts/setenv + export OPENRCT2_DAT_DIR="$GITHUB_WORKSPACE/bin/data" pushd src/openrct2-android ./gradlew app:assembleRelease -PskipNativeBuild popd diff --git a/scripts/build-graphics-dat b/scripts/build-graphics-dat new file mode 100755 index 0000000000..e3536727fb --- /dev/null +++ b/scripts/build-graphics-dat @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +OUTPUT_DIR="${1:-}" + +if [[ -z "$OUTPUT_DIR" ]]; then + echo "Usage: $(basename "$0") " >&2 + exit 2 +fi + +if ! command -v gxc >/dev/null 2>&1; then + echo "ERROR: gxc not found in PATH." >&2 + echo "Download gxc from https://github.com/OpenRCT2/libsawyer/releases" >&2 + exit 1 +fi + +mkdir -p "$OUTPUT_DIR" + +gxc build "$OUTPUT_DIR/g2.dat" "$ROOT_DIR/resources/g2/sprites.json" +gxc build "$OUTPUT_DIR/palettes.dat" "$ROOT_DIR/resources/palettes/sprites.json" +gxc build "$OUTPUT_DIR/fonts.dat" "$ROOT_DIR/resources/fonts/sprites.json" +gxc build "$OUTPUT_DIR/tracks.dat" "$ROOT_DIR/resources/tracks/sprites.json" diff --git a/scripts/prepare-android-assets b/scripts/prepare-android-assets new file mode 100755 index 0000000000..0a283dc821 --- /dev/null +++ b/scripts/prepare-android-assets @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +ASSETS_DIR="$ROOT_DIR/src/openrct2-android/app/src/main/assets/openrct2" +DATA_DIR="$ASSETS_DIR/data" + +mkdir -p "$DATA_DIR" + +DAT_DIR="${OPENRCT2_DAT_DIR:-}" +if [[ -n "$DAT_DIR" && -f "$DAT_DIR/g2.dat" ]]; then + cp "$DAT_DIR"/*.dat "$DATA_DIR/" +else + TMP_DIR="$(mktemp -d)" + "$ROOT_DIR/scripts/build-graphics-dat" "$TMP_DIR" + cp "$TMP_DIR"/*.dat "$DATA_DIR/" + rm -rf "$TMP_DIR" +fi + +cp -r "$ROOT_DIR/data/language" "$DATA_DIR/" +cp -r "$ROOT_DIR/data/shaders" "$DATA_DIR/" +cp -r "$ROOT_DIR/data/scenario_patches" "$DATA_DIR/" +cp "$ROOT_DIR/distribution/changelog.txt" "$DATA_DIR/" +cp "$ROOT_DIR/contributors.md" "$DATA_DIR/" + +OBJECTS_URL=$(jq -r '.objects.url' "$ROOT_DIR/assets.json") +TS_URL=$(jq -r '."title-sequences".url' "$ROOT_DIR/assets.json") + +curl -fL "$OBJECTS_URL" -o "$DATA_DIR/objects.zip" +mkdir -p "$DATA_DIR/object" +unzip -q "$DATA_DIR/objects.zip" -d "$DATA_DIR/object" +rm -f "$DATA_DIR/objects.zip" + +curl -fL "$TS_URL" -o "$DATA_DIR/title-sequences.zip" +mkdir -p "$DATA_DIR/sequence" +unzip -q "$DATA_DIR/title-sequences.zip" -d "$DATA_DIR/sequence" +rm -f "$DATA_DIR/title-sequences.zip" + +pushd "$ROOT_DIR/src/openrct2-android/app/src/main/assets" >/dev/null +find openrct2 -type f ! -name manifest.txt -printf "%p|%s\n" > openrct2/manifest.txt +popd >/dev/null diff --git a/src/openrct2-android/app/build.gradle b/src/openrct2-android/app/build.gradle index aca80ecc6a..4e85f705e2 100644 --- a/src/openrct2-android/app/build.gradle +++ b/src/openrct2-android/app/build.gradle @@ -68,6 +68,52 @@ android { lintOptions { abortOnError = false } + + androidResources { + noCompress 'parkseq', 'zip', 'dat', 'parkobj' + } +} + +def prepareAndroidAssetsScript = "${rootProject.projectDir}/../../scripts/prepare-android-assets" +tasks.register('prepareOpenRCT2Assets', Exec) { + workingDir rootProject.projectDir + commandLine 'bash', prepareAndroidAssetsScript + inputs.file(prepareAndroidAssetsScript) + inputs.file("${rootProject.projectDir}/../../assets.json") + inputs.file("${rootProject.projectDir}/../../contributors.md") + inputs.file("${rootProject.projectDir}/../../distribution/changelog.txt") + inputs.file("${rootProject.projectDir}/../../resources/fonts/sprites.json") + inputs.file("${rootProject.projectDir}/../../resources/g2/sprites.json") + inputs.file("${rootProject.projectDir}/../../resources/palettes/sprites.json") + inputs.file("${rootProject.projectDir}/../../resources/tracks/sprites.json") + inputs.dir("${rootProject.projectDir}/../../data/language") + inputs.dir("${rootProject.projectDir}/../../data/shaders") + inputs.dir("${rootProject.projectDir}/../../data/scenario_patches") + outputs.dir("${projectDir}/src/main/assets/openrct2") +} + +if (!project.hasProperty('skipAssets')) { + preBuild.dependsOn(tasks.named('prepareOpenRCT2Assets')) +} + +// When the `skipJava` property is set, skip all Java-related tasks. +// This is used in CI so native libraries can be built in separate jobs for each ABI to speed up our builds. +if (project.hasProperty('skipJava')) { + tasks.withType(JavaCompile).configureEach { + enabled = false + } + def skippablePrefixes = ["process", "merge", "generate", "compile", "lint", "check"] + def skippableKeywords = ["Java", "Resources", "Manifest", "Asset", "Lint", "Runtime"] + tasks.configureEach { task -> + def name = task.name + def hasPrefix = skippablePrefixes.any { name.startsWith(it) } + def hasKeyword = skippableKeywords.any { name.contains(it) } + if (hasPrefix && hasKeyword) { + if (!name.contains("externalNativeBuild") && !name.contains("CMake") && !name.contains("Ninja")) { + task.enabled = false + } + } + } } dependencies {