#!/usr/bin/env bash # Exporta o Learn to Code RPG (freeCodeCamp.org, BSD 3-Clause, github.com/freeCodeCamp/LearnToCodeRPG, Ren'Py) # para a web com o Ren'Py 8.5.3 oficial (MIT, com partes LGPL): o SDK e o modulo web vem do site do Ren'Py e sao # conferidos pelo SHA-256 do checksums.txt assinado do release. Ficam em artifacts/tools/renpy-8.5.3. # # src/FredGames.Bundler/Builds/build-learn-to-code-rpg.sh # # Mudancas no jogo (tudo reproduzido aqui; o texto vai no learn-to-code-rpg/game.patch): # - a fonte saxMono (sem licenca) vira Share Tech Mono (OFL, google/fonts); # - sem links, doacao, itch.io/GitHub/Twitter/YouTube, sem a checagem de versao no itch.io, sem os logos da # freeCodeCamp e do jogo (a chama e marca registrada): o credito a freeCodeCamp.org fica no texto; # - sem traducoes (so a pasta tl/ vazia de conteudo), sem a fonte chinesa nem arquivos de demonstracao do icomoon; # - imagens do jogo (exceto gui/) em WebP q85; trilhas em mp3 80 kbps; efeitos wav grandes em mp3. # O Ren'Py web baixa imagens e musicas sob demanda (progressive_download.txt); efeitos sonoros ficam no game.zip. # Depois do export: sem PWA (manifest, service worker), sem o link "Powered by Ren'Py", wasm em .gz (a pagina # descomprime com DecompressionStream; o CloudFront nao comprime acima de 10 MB) e CSP que so permite esta origem. # # Ferramentas de sistema: lame 4.0 e cwebp 1.5.0 (Homebrew); o script aborta se a versao for outra. # # Saida: artifacts/build/learn-to-code-rpg-9ccf18c/{learn-to-code-rpg-web.tar.gz (a pasta web/), renpy-8.5.3-lgpl-source.tar.gz}, # conferidos pelo bundle.yaml. ATENCAO: o game.zip nao e reprodutivel byte a byte (ver o fim do script). set -euo pipefail COMMIT=9ccf18c86e962ed25b946bb2d28aa6ac8c1ae9c1 SHA256=5beb485acabe7814c85251ae3383cd8f06ee71158347de82cfc1d259d8a27a28 RENPY=8.5.3 RENPY_URL=https://www.renpy.org/dl/$RENPY RENPY_SDK_SHA256=eb0a9be7f0fb13632fe25ceade9a8bed5a1b4d6b6e83bd19eeeb29e1a1bb4a45 RENPY_WEB_SHA256=954db897e65f51ea63cb2fb7b203d02be0447f4e22069514020bbe6c6691fdfc RENPY_SRC_SHA256=4fd3d7bca7349b0d182830d3cdcf65940fa32e66be1aea0b3690e048be6282a5 RB_TAG=renpy-8.5.3.26051504 RB_SHA256=cee7b853c82b28f3d69586cda1aaf3bfb55cec723171b5cec06784c8e3bc5ce6 GFONTS=8c3ea249848f15b7ff6092b3e16d44ef91771cad STM_TTF_SHA256=9ceab1f87414829af259c0f537573ae03ef7dd3147c0b27a36a1a0beb6732677 STM_OFL_SHA256=9d96f445b6e9c701428811d0177f894874f8d6f07ecc30d568c506542368f3ff HACK_LICENSE_SHA256=1f61bb7c790c59b4b0ecdf304628b94e42ae4c8020094a8c3da73381ab212623 FEATHER_LICENSE_SHA256=308028e93fcf84972523cdf6e616f73168546b4953895f516d01287f16fe7bee here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" root="$(git -C "$here" rev-parse --show-toplevel 2>/dev/null || pwd)" out="$root/artifacts/build/learn-to-code-rpg-${COMMIT:0:7}" cache="$root/artifacts/build/cache" tools="$root/artifacts/tools" sdk="$tools/renpy-$RENPY/renpy-$RENPY-sdk" mkdir -p "$cache" fetch() { if [ ! -f "$cache/$2" ]; then curl -fsSL --retry 4 "$1" -o "$cache/$2.partial" && mv "$cache/$2.partial" "$cache/$2" fi echo "$3 $cache/$2" | shasum -a 256 -c - } install_renpy() { [ -d "$sdk/web" ] && return 0 fetch "$RENPY_URL/renpy-$RENPY-sdk.tar.bz2" "renpy-$RENPY-sdk.tar.bz2" "$RENPY_SDK_SHA256" fetch "$RENPY_URL/renpy-$RENPY-web.zip" "renpy-$RENPY-web.zip" "$RENPY_WEB_SHA256" rm -rf "$tools/renpy-$RENPY.tmp" mkdir -p "$tools/renpy-$RENPY.tmp" tar -xjf "$cache/renpy-$RENPY-sdk.tar.bz2" -C "$tools/renpy-$RENPY.tmp" unzip -q "$cache/renpy-$RENPY-web.zip" -d "$tools/renpy-$RENPY.tmp/renpy-$RENPY-sdk" rm -rf "$tools/renpy-$RENPY" mv "$tools/renpy-$RENPY.tmp" "$tools/renpy-$RENPY" } if [ "${1:-}" = "--install" ]; then install_renpy; exit 0; fi lame --version | head -1 | grep -q "version 4.0" || { echo "lame 4.0 necessario" >&2; exit 1; } cwebp -version | head -1 | grep -q "^1.5.0" || { echo "cwebp 1.5.0 necessario" >&2; exit 1; } fetch "https://github.com/freeCodeCamp/LearnToCodeRPG/archive/$COMMIT.tar.gz" "ltcrpg-${COMMIT:0:7}.tar.gz" "$SHA256" fetch "$RENPY_URL/renpy-$RENPY-source.tar.bz2" "renpy-$RENPY-source.tar.bz2" "$RENPY_SRC_SHA256" fetch "https://github.com/renpy/renpy-build/archive/refs/tags/$RB_TAG.tar.gz" "renpy-build-$RB_TAG.tar.gz" "$RB_SHA256" fetch "https://raw.githubusercontent.com/google/fonts/$GFONTS/ofl/sharetechmono/ShareTechMono-Regular.ttf" stm-ShareTechMono-Regular.ttf "$STM_TTF_SHA256" fetch "https://raw.githubusercontent.com/google/fonts/$GFONTS/ofl/sharetechmono/OFL.txt" stm-OFL.txt "$STM_OFL_SHA256" fetch "https://raw.githubusercontent.com/source-foundry/Hack/v3.003/LICENSE.md" hack-LICENSE.md "$HACK_LICENSE_SHA256" fetch "https://raw.githubusercontent.com/feathericons/feather/v4.29.2/LICENSE" feather-LICENSE "$FEATHER_LICENSE_SHA256" if [ ! -d "$sdk/web" ]; then lockf -k "$root/artifacts/renpy-$RENPY.lock" bash "${BASH_SOURCE[0]}" --install fi rm -rf "$out" mkdir -p "$out/project" "$out/web" tar -xzf "$cache/ltcrpg-${COMMIT:0:7}.tar.gz" -C "$out/project" --strip-components 1 cd "$out/project" rm -rf android-* badge-bw.png icon.* presplash.png project.json utils README.md LICENSE patch -s -p1 < "$here/learn-to-code-rpg/game.patch" cd game # Arquivos que saem: traducoes (so andaimes do Ren'Py), fonte chinesa, demos do icomoon, logos e imagens de # divulgacao (itch.io, GitHub, freeCodeCamp), icone da janela (a chama). rm -rf tl fonts/simplified_chinese fonts/saxmono.ttf fonts/icomoon/demo.html fonts/icomoon/demo-files \ fonts/icomoon/selection.json fonts/icomoon/style.css "fonts/icomoon/Read Me.txt" \ fonts/icomoon/fonts/icomoon.eot fonts/icomoon/fonts/icomoon.svg fonts/icomoon/fonts/icomoon.woff \ images/others/fcc_logo.png images/others/learn_to_code_rpg_logo.png images/others/itch_rate.png \ images/others/github_star.png gui/window_icon.png mkdir -p fonts/sharetechmono cp "$cache/stm-ShareTechMono-Regular.ttf" fonts/sharetechmono/ShareTechMono-Regular.ttf # Tela de abertura da pagina: a arte do proprio jogo no lugar do logo do Ren'Py. cwebp -quiet -q 80 -resize 1280 720 gui/main_menu.png -o ../web-presplash.webp # Imagens: WebP (o game.patch ja aponta os scripts para .webp); em gui/ so as duas telas grandes, o resto fica em PNG. find images -type f -iname '*.png' | LC_ALL=C sort > "$out/images.txt" printf '%s\n' gui/main_menu.png gui/main_menu_v1.png >> "$out/images.txt" tr '\n' '\0' < "$out/images.txt" | xargs -0 -n 1 -P 4 sh -c 'cwebp -quiet -q 85 -alpha_q 100 -m 4 "$0" -o "${0%.*}.webp" && rm "$0"' [ -z "$(find images -type f -iname '*.png')" ] # Audio: trilhas de fundo 80 kbps; efeitos wav acima de 100 kB viram mp3; o jogo do ritmo fica como esta. find audio/bgm -type f -name '*.mp3' | LC_ALL=C sort > "$out/bgm.txt" tr '\n' '\0' < "$out/bgm.txt" | xargs -0 -n 1 -P 4 sh -c 'lame --quiet -b 80 -m j -q 2 "$0" "$0.tmp" && mv "$0.tmp" "$0"' find audio/sfx -type f -name '*.wav' -size +100000c | LC_ALL=C sort > "$out/sfx.txt" while IFS= read -r f; do lame --quiet -V 6 -m j "$f" "${f%.wav}.mp3" rm "$f" n="$(basename "${f%.wav}")" { grep -rl --include='*.rpy' "audio/sfx/$n\.wav" . || true; } | while IFS= read -r r; do sed -i '' "s#audio/sfx/$n\\.wav#audio/sfx/$n.mp3#g" "$r" done done < "$out/sfx.txt" # Toda referencia a audio/imagem tem de existir. grep -rhoE --include='*.rpy' "audio/(sfx|bgm|rhythm_game)/[A-Za-z0-9_ .'-]+\.(wav|mp3|ogg|opus)" . | LC_ALL=C sort -u | while IFS= read -r f; do [ -f "$f" ] || { echo "faltando: $f" >&2; exit 1; }; done # Progressive download: imagens (menos gui/) e musicas; os efeitos sonoros (curtos) ficam no game.zip. cat > ../progressive_download.txt <<'EOF' # RenPyWeb progressive download rules - first match applies - image game/gui/** + image game/** - music game/audio/sfx/** + music game/audio/** EOF cd .. # Datas fixas (o game.zip leva a data de cada arquivo). TZ=UTC find . -exec touch -t 202001010000 {} + export HOME="$out/home" TZ=UTC LC_ALL=C PYTHONHASHSEED=0 mkdir -p "$HOME" ( cd "$sdk" && ./renpy.sh launcher web_build "$out/project" --destination "$out/webbuild" ) > "$out/renpy.log" 2>&1 || { tail -40 "$out/renpy.log" >&2; exit 1; } ls -l "$out/webbuild" > /dev/null # --- pos-processamento --- w="$out/webbuild" rm -rf "$w/icons" "$w/manifest.json" "$w/service-worker.js" "$w/pwa_catalog.json" "$w/index.html.symbols" rm -f "$w/web-icon.png" "$tools/venv/bin/python" -I "$here/learn-to-code-rpg/post-web.py" "$w" gzip -n -9 -c "$w/renpy.wasm" > "$w/renpy.wasm.gz" rm "$w/renpy.wasm" mkdir -p "$out/web" cp -R "$w/." "$out/web/" TZ=UTC find "$out/web" -exec touch -t 202001010000 {} + ( cd "$out" && find web -type f | LC_ALL=C sort > "$out/web-files.txt" ) ( cd "$out" && COPYFILE_DISABLE=1 tar --format=ustar --uid 0 --gid 0 --uname root --gname root -n \ -cf - -T "$out/web-files.txt" ) | gzip -n -9 > "$out/learn-to-code-rpg-web.tar.gz" # --- fonte LGPL: Ren'Py + renpy-build (ffmpeg, fribidi e as receitas de build) --- lg="$out/lgpl" mkdir -p "$lg/renpy-$RENPY" "$lg/renpy-build" tar -xjf "$cache/renpy-$RENPY-source.tar.bz2" -C "$lg/renpy-$RENPY" --strip-components 1 tar -xzf "$cache/renpy-build-$RB_TAG.tar.gz" -C "$lg/renpy-build" --strip-components 1 ( cd "$lg/renpy-$RENPY" && rm -rf doc sphinx the_question tutorial sdk-fonts ) ( cd "$lg/renpy-build" && rm -rf prebuilt rapt renios steamapi nvlib && cd source && find . -mindepth 1 -maxdepth 1 ! -name 'ffmpeg-4.3.1.tar.gz' ! -name 'fribidi-1.0.7.tar.bz2' -exec rm -rf {} + ) TZ=UTC find "$lg" -exec touch -t 202001010000 {} + ( cd "$lg" && find . -type f | sed 's|^\./||' | LC_ALL=C sort > "$out/lgpl-files.txt" ) ( cd "$lg" && COPYFILE_DISABLE=1 tar --format=ustar --uid 0 --gid 0 --uname root --gname root -n \ -cf - -T "$out/lgpl-files.txt" ) | gzip -n -9 > "$out/renpy-$RENPY-lgpl-source.tar.gz" rm -rf "$lg" ( cd "$out" && shasum -a 256 renpy-$RENPY-lgpl-source.tar.gz learn-to-code-rpg-web.tar.gz && cd web && find . -type f -print0 | LC_ALL=C sort -z | xargs -0 shasum -a 256 ) > "$out/SHA256SUMS" du -sh "$out/web" "$out/renpy-$RENPY-lgpl-source.tar.gz" "$out/learn-to-code-rpg-web.tar.gz" # Nota: o game.zip NAO sai igual entre duas execucoes (os .rpyc/.rpyb que o Ren'Py gera mudam de bytes; todo o resto # sai igual). Por isso o sha256 do tar.gz no bundle.yaml e o da execucao publicada: nao rode de novo sem atualizar.