diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml new file mode 100644 index 000000000..f8a85030a --- /dev/null +++ b/.github/workflows/appimage.yml @@ -0,0 +1,60 @@ +name: Build Arch Package & AppImage (Git) + +on: + workflow_dispatch: + +jobs: + build: + name: Build with makepkg + runs-on: ubuntu-latest + + container: + image: archlinux:latest + + steps: + - name: Install base dependencies + run: | + pacman -Syu --noconfirm archlinux-keyring + pacman -Syu --noconfirm base-devel git sudo wget zsync xorg-server-xvfb patchelf npm + + # Enable multilib. Recent archlinux:latest containers no longer ship a + # commented #[multilib] block, so append the section if absent. + if ! grep -q '^\[multilib\]' /etc/pacman.conf; then + cat >> /etc/pacman.conf <<'EOF' + + [multilib] + Include = /etc/pacman.d/mirrorlist + EOF + fi + + sudo pacman -Syu --noconfirm + + - name: Create build user + run: | + useradd -m builder + echo "builder ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Fix permissions + run: chown -R builder:builder . + + - name: Build package (makepkg -si) + run: | + sudo -u builder bash <<'EOF' + set -e + cd pkg/git + makepkg -si --noconfirm + cd ../AppImage/ + chmod +x AppImage.sh + HEADLESS=1 ./AppImage.sh + EOF + + - name: Upload package artifact + uses: actions/upload-artifact@v7 + with: + name: arch-package + path: | + pkg/git/*.pkg.tar.zst + pkg/AppImage/dist/*.AppImage diff --git a/.gitignore b/.gitignore index fd445f0f8..557716a07 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,28 @@ Data .DS_Store secrets.json + +*.7z +*.7z.part +*.so +*.deb +*.kate-swp +quick-sharun + +# git dir +pkg/git/*.tar.zst +pkg/git/pkg +pkg/git/src +pkg/git/launcher + +# AppImage dir +pkg/AppImage/*.tar.zst +pkg/AppImage/.MTREE +pkg/AppImage/.PKGINFO +pkg/AppImage/.BUILDINFO +pkg/AppImage/usr/ +pkg/AppImage/AppDir/ +pkg/AppImage/quick-sharun + +# dist dir +pkg/AppImage/dist diff --git a/pkg/AppImage/AppImage.sh b/pkg/AppImage/AppImage.sh new file mode 100755 index 000000000..752d48997 --- /dev/null +++ b/pkg/AppImage/AppImage.sh @@ -0,0 +1,111 @@ +#!/bin/sh + +# You might need to restart your pc if sharun doesn't create `AppDir` in this directory (It should create dirs on its own) + +set -eu + +# Always build in this script's directory regardless of the caller's cwd +cd "$(dirname "$0")" +unset APPDIR + +ARCH="$(uname -m)" +SHARUN="https://raw.githubusercontent.com/pkgforge-dev/Anylinux-AppImages/main/useful-tools/quick-sharun.sh" + +export MAIN_BIN=$HOME/Games/Flashpoint/Launcher/flashpoint-launcher +#export UPINFO="gh-releases-zsync|${GITHUB_REPOSITORY%/*}|${GITHUB_REPOSITORY#*/}|latest|*$ARCH.AppImage.zsync" +export OUTNAME=flashpoint-launcher-anylinux-"$ARCH".AppImage +export DESKTOP=/usr/share/applications/flashpoint-archive.desktop +export ICON=$HOME/Games/Flashpoint/Launcher/icon.svg +export OUTPATH=./dist + +#Remove leftovers +rm -rf AppDir dist appinfo + +# ADD LIBRARIES +wget --retry-connrefused --tries=30 "$SHARUN" -O ./quick-sharun +chmod +x ./quick-sharun + +# Download the static 7zz used to seed ~/Games/Flashpoint from the bundled +# archive on first run. 7zzs is statically linked, so no library collection +# is needed for it. +wget --retry-connrefused --tries=30 "https://www.7-zip.org/a/7z2602-linux-x64.tar.xz" -O /tmp/7zz.tar.xz +tar -xf /tmp/7zz.tar.xz -C /tmp 7zzs 2>/dev/null || tar -xf /tmp/7zz.tar.xz -C /tmp 7zz + +# The standalone Ruffle binary is not shipped in the fp archive; the core-ruffle +# extension downloads it at runtime. When HEADLESS=1, fetch the latest Linux +# build here so quick-sharun can bundle it, matching a machine that has run once. +RUFFLE_DIR="$HOME/Games/Flashpoint/Data/Ruffle/standalone/latest" +if [ "$HEADLESS" = 1 ] && [ ! -x "$RUFFLE_DIR/ruffle" ]; then + echo "Standalone Ruffle missing; downloading latest Linux build..." + mkdir -p "$RUFFLE_DIR" + RUFFLE_URL=$(wget -qO- "https://api.github.com/repos/ruffle-rs/ruffle/releases?per_page=1" | grep -oE 'https://[^"]*linux-x86_64\.tar\.gz' | head -n1) + wget --retry-connrefused --tries=30 -O /tmp/ruffle.tar.gz "$RUFFLE_URL" + tar -xzf /tmp/ruffle.tar.gz -C "$RUFFLE_DIR" + chmod +x "$RUFFLE_DIR/ruffle" + rm -f /tmp/ruffle.tar.gz + echo "Standalone Ruffle downloaded." +fi + +mkdir -p AppDir +cp AppRun.sh AppDir/AppRun.sh +chmod +x AppDir/AppRun.sh + +# Pre-create the desktop entry so the AppImage does not bundle a stale copy of +# the system-installed one. Exec routes through the seeded data root's +# start-flashpoint.sh (the .desktop spec cannot expand $HOME itself). +cp ../git/flashpoint-archive.desktop AppDir/flashpoint-archive.desktop + +# Point to binaries +./quick-sharun \ + $HOME/Games/Flashpoint/Launcher/flashpoint-launcher \ + $HOME/Games/Flashpoint/Server/FlashpointGameServer \ + $HOME/Games/Flashpoint/Data/Ruffle/standalone/latest/ruffle \ + $HOME/Games/Flashpoint/FPSoftware/Wine/bin/wine \ + $HOME/Games/Flashpoint/FPSoftware/Wine/bin/wineserver + +cp -a $HOME/Games/Flashpoint/Launcher/. AppDir/shared/bin/ + +# Copy the launcher's data files next to the sharun wrapper (Electron's DIR_EXE). +# Do NOT overwrite the `flashpoint-launcher` wrapper hardlink in bin/. +for f in icudtl.dat chrome_100_percent.pak chrome_200_percent.pak resources.pak \ + snapshot_blob.bin v8_context_snapshot.bin vk_swiftshader_icd.json \ + config.json ormconfig.json secret.dat LICENSE.electron.txt LICENSES.chromium.html \ + chrome_crashpad_handler chrome-sandbox \ + libEGL.so libGLESv2.so libvk_swiftshader.so libvulkan.so.1 libffmpeg.so; do + cp -a "$HOME/Games/Flashpoint/Launcher/$f" AppDir/bin/ +done +for d in locales resources lang licenses extern; do + cp -a "$HOME/Games/Flashpoint/Launcher/$d" AppDir/bin/ +done + +# The launcher resolves config.flashpointPath relative to the working directory +# (e.g. path.join('..', 'Data') -> ../Data). Bake a per-user data root so the +# AppImage works no matter where it is launched from. `` is resolved by a +# patched parseVarStr (see the asar patch below). +sed -i 's|"flashpointPath": ".."|"flashpointPath": "/Games/Flashpoint"|' AppDir/bin/config.json + +# On FUSE-mount runs $APPDIR is read-only, and the launcher always writes its log +# next to the exe (dirname(app.getPath('exe'))/launcher.log). A symlink redirects +# that write to a writable location so file logging survives mount runs. +ln -sf $HOME/Games/Flashpoint/Launcher/launcher.log AppDir/bin/launcher.log + +# Remove this part once a new release is done that has the 1 line edit in src/shared/Util.ts +# Patch the bundled asar so parseVarStr understands `` (used by the baked +# flashpointPath above). Without this the literal `` is left in the path. +npx --yes asar extract AppDir/bin/resources/app.asar /tmp/asar-patch +sed -i "/case 'cwd':/a\\ case 'home': return process.env.HOME || '';" /tmp/asar-patch/build/shared/Util.js +npx --yes asar pack /tmp/asar-patch AppDir/bin/resources/app.asar +rm -rf /tmp/asar-patch + +# Bundle the static 7zz and the fp archive so AppRun.sh can seed +# ~/Games/Flashpoint on a fresh system. +mkdir -p AppDir/gamesdata +cp /tmp/7zzs AppDir/bin/7zz 2>/dev/null || cp /tmp/7zz AppDir/bin/7zz +FP_ARCHIVE=$(ls ../git/fp*_lin_*.7z | head -n 1) +[ -n "$FP_ARCHIVE" ] || { echo "ERROR: no fp archive found in ../git/" >&2; exit 1; } +cp "$FP_ARCHIVE" AppDir/gamesdata/flashpoint.7z + +# Make AppImage +./quick-sharun --make-appimage + +echo "All Done!" diff --git a/pkg/AppImage/AppRun.sh b/pkg/AppImage/AppRun.sh new file mode 100755 index 000000000..3e9482295 --- /dev/null +++ b/pkg/AppImage/AppRun.sh @@ -0,0 +1,53 @@ +#!/bin/sh + +# AppRun for the Flashpoint Archive AppImage (executed by sharun). +# On first run the bundled fp archive is extracted into ~/Games/Flashpoint, +# then the launcher is started with the environment start-flashpoint.sh would set. + +if [ "$APPRUN_DEBUG" = 1 ]; then + set -x +fi + +set -e + +FP_DATA="${FLASHPOINT_PATH:-$HOME/Games/Flashpoint}" +if [ ! -f "$FP_DATA/.preferences.defaults.json" ]; then + echo "Flashpoint Archive: first run detected, setting up data in '$FP_DATA'..." + echo " This may take a minute or two." + mkdir -p "$FP_DATA" + "$APPDIR/bin/7zz" x -y "$APPDIR/gamesdata/flashpoint.7z" -o"$FP_DATA" >/dev/null || { + echo "ERROR: failed to extract Flashpoint data to '$FP_DATA'" >&2 + exit 1 + } + echo "Flashpoint data ready." +fi + +# flash32 has no RPATH and needs the bundled GTK2/NSS/X11 libs, which are no +# longer exported globally (they break native GPU detection). Re-add them in its +# wrapper. Idempotent; also fixes data roots seeded by older AppImages. +if [ -d "$FP_DATA/Libraries/lib/x86_64-linux-gnu" ] && \ + [ -f "$FP_DATA/FPSoftware/Flash/flash32.sh" ] && \ + ! grep -q 'Libraries/lib/x86_64-linux-gnu' "$FP_DATA/FPSoftware/Flash/flash32.sh"; then + sed -i '/^\.\/flash32 /i\ +if [ -d "$FP_PATH/Libraries/lib/x86_64-linux-gnu" ]; then\ + export LD_LIBRARY_PATH="$FP_PATH/Libraries/lib/x86_64-linux-gnu"\ + export LIBGL_DRIVERS_PATH="$FP_PATH/Libraries/lib/x86_64-linux-gnu/dri"\ +fi' "$FP_DATA/FPSoftware/Flash/flash32.sh" +fi + +export WINEPREFIX="$FP_DATA/FPSoftware/Wine" +export FP_PATH="$FP_DATA" +if [ -f "$FP_DATA/Libraries/lib/x86_64-linux-gnu/libgtk-3.so.0" ]; then + export GALLIUM_DRIVER="i915,i965,iris,nouveau,nouveau_vieux,r200,r300,r600,radeon,radeonsi,zink,swrast" \ + GDK_PIXBUF_MODULE_FILE="$FP_DATA/Libraries/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders.cache" \ + GSETTINGS_SCHEMA_DIR="$FP_DATA/Libraries/share/glib-2.0/schemas" \ + GTK_MODULES= + # Do NOT export LD_LIBRARY_PATH / LIBGL_DRIVERS_PATH here: the launcher + # copies them into every spawned game, and the bundled libraries break + # native GPU detection (e.g. Ruffle's Vulkan/EGL panic). Binaries that + # need the bundled libs (flash32) set them in their own wrapper. +fi + +export PATH=$APPDIR/bin:$PATH + +exec "$APPDIR/bin/flashpoint-launcher" "$@" diff --git a/pkg/git/PKGBUILD b/pkg/git/PKGBUILD new file mode 100644 index 000000000..3a82e824a --- /dev/null +++ b/pkg/git/PKGBUILD @@ -0,0 +1,73 @@ +# Maintainer: hmarcelino +# Contributor: bbaster + +# This is used to make an AppImage out of the latest release + +pkgname=flashpoint-launcher-bin +pkgver=14.0.3_20251201 +_launcherver=${pkgver%_*} +_timestamp=${pkgver#*_} + +pkgrel=2 +pkgdesc="An archive for games and animations from the web." +arch=('x86_64') +url="https://flashpointarchive.org/" +license=('MIT') + +provides=(flashpoint-launcher) +conflicts=(flashpoint-launcher) + +depends=( + 'pulse-native-provider' + 'lib32-libxcomposite' + 'lib32-libpulse' + 'gtk3' + 'nss' + 'php' + '7zip' + 'bash' +) + +optdepends=( + 'gtk2: native Flash support' + 'libxt: native Flash support' +) + +options=( + '!strip' + '!emptydirs' +) + +source=("https://download.flashpointarchive.org/upload/fp${_launcherver}_lin_${_timestamp}.7z") +sha256sums=('f393a98c5c35e229a744c102b0cb53270b1b4f1b3ebd40d604f98323444a4b1f') + +prepare() { + patch -p1 -i ../setup-desktop-entry.patch -d "${srcdir}" + patch -p1 -i ../start-flashpoint.patch -d "${srcdir}" +} + +package() { + echo "Copying data files ..." + mkdir -vp "${pkgdir}$HOME/Games/Flashpoint/" + chmod 700 "${pkgdir}${HOME}" + find "${srcdir}/" -maxdepth 1 -not -name "fp${_launcherver}_lin_${_timestamp}.7z" -exec cp -rp {} "${pkgdir}$HOME/Games/Flashpoint/" \; + + echo "Installing icon and desktop file..." + mkdir -vp "${pkgdir}/usr/share/licenses" + cp -rp "${pkgdir}$HOME/Games/Flashpoint/Launcher/licenses/" "${pkgdir}/usr/share/licenses/flashpoint" + install -Dm644 "${srcdir}/Launcher/icon.svg" "${pkgdir}/usr/share/icons/hicolor/scalable/apps/flashpoint-launcher.svg" + install -Dm644 ../flashpoint-archive.desktop "${pkgdir}/usr/share/applications/flashpoint-archive.desktop" + + echo "Removing unneeded src folder..." + rm -rf "${pkgdir}$HOME/Games/Flashpoint/src" + + echo "Removing unneeded Libraries folder..." + rm -rf "${pkgdir}$HOME/Games/Flashpoint/Libraries" + + # makepkg builds under fakeroot, so everything would be installed as + # root:root and the launcher (run as the user) could not write + # preferences.json, secret.dat or logs. Restore user ownership on the + # per-user data root so the launcher can run unprivileged. + echo "Setting user ownership on the data root..." + chown -R "$USER:$USER" "${pkgdir}${HOME}/Games" +} diff --git a/pkg/git/flashpoint-archive.desktop b/pkg/git/flashpoint-archive.desktop new file mode 100644 index 000000000..c4bb538bb --- /dev/null +++ b/pkg/git/flashpoint-archive.desktop @@ -0,0 +1,11 @@ +#!/usr/bin/env xdg-open +[Desktop Entry] +Type=Application +Name=Flashpoint Archive +Comment=An archive for games and animations from the web. +Icon=flashpoint-launcher +StartupWMClass=flashpoint-launcher +Exec=sh -c "$HOME/Games/Flashpoint/start-flashpoint.sh %f" +Terminal=false +Categories=Archiving;Game; +SingleMainWindow=true diff --git a/pkg/git/setup-desktop-entry.patch b/pkg/git/setup-desktop-entry.patch new file mode 100644 index 000000000..bce6d1079 --- /dev/null +++ b/pkg/git/setup-desktop-entry.patch @@ -0,0 +1,43 @@ +diff --git a/setup-desktop-entry.sh b/setup-desktop-entry.sh +old mode 100755 +new mode 100644 +index b13a1e6..9d5a91e +--- a/setup-desktop-entry.sh ++++ b/setup-desktop-entry.sh +@@ -1,7 +1,7 @@ + #!/usr/bin/env sh + # Generate a desktop entry for Flashpoint Archive. + +-[ `id -u` -ne 0 ] && cd -P -- "`dirname "$0"`" && [ -f Launcher/flashpoint-launcher ] || exit 1 ++[ $(id -u) -eq 0 ] && cd -P -- "`dirname "$0"`" && [ -f "$(realpath "$(dirname "${BASH_SOURCE[0]}")")/Launcher/flashpoint-launcher" ] || exit 1 + + [ "$ENTRY" ] || ENTRY="$HOME/.local/share/applications/flashpoint-archive.desktop" + [ -e "${ENTRY%/*}" ] || mkdir -p "${ENTRY%/*}" +@@ -11,22 +11,12 @@ + Type=Application + Name=Flashpoint Archive + Comment=An archive for games and animations from the web. +-Icon=$PWD/Launcher/icon.svg ++Icon=flashpoint-launcher + StartupWMClass=flashpoint-launcher +-Exec=/usr/bin/env -S " > "$ENTRY" +-if [ "`file -b --mime-type Libraries/lib/x86_64-linux-gnu/libgtk-3.so.0`" = 'application/x-sharedlib' ]; then +- printf "GALLIUM_DRIVER=\"i915,i965,iris,nouveau,nouveau_vieux,r200,r300,r600,radeon,radeonsi,zink,swrast\" GDK_PIXBUF_MODULE_FILE=\"$PWD/Libraries/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders.cache\" GSETTINGS_SCHEMA_DIR=\"$PWD/Libraries/share/glib-2.0/schemas\" GTK_MODULES= LD_LIBRARY_PATH=\"$PWD/Libraries/lib/x86_64-linux-gnu\" LIBGL_DRIVERS_PATH=\"$PWD/Libraries/lib/x86_64-linux-gnu/dri\" PATH=\"$PWD/Libraries/bin" >> "$ENTRY" +- if ! [ -d FPSoftware/Wine/bin ]; then +- CMD="`command -v wine`" +- [ "$CMD" ] && printf ":${CMD%/*}" >> "$ENTRY" +- fi +- printf '" ' >> "$ENTRY" +-fi +-printf "WINEPREFIX=\"$PWD/FPSoftware/Wine\" ./flashpoint-launcher --js-flags=--lite_mode --ozone-platform-hint=auto +-Path=$PWD/Launcher ++Exec=sh -c "$HOME/Games/Flashpoint/start-flashpoint.sh %f" + Terminal=false + Categories=Archiving;Game; +-SingleMainWindow=true" >> "$ENTRY" ++SingleMainWindow=true" > "$ENTRY" + + # This is for backwards compatibility, and will be removed in the future. +-ln "$ENTRY" Flashpoint.desktop +\ No newline at end of file ++ln -f "$ENTRY" Flashpoint.desktop diff --git a/pkg/git/start-flashpoint.patch b/pkg/git/start-flashpoint.patch new file mode 100644 index 000000000..61406d634 --- /dev/null +++ b/pkg/git/start-flashpoint.patch @@ -0,0 +1,36 @@ +diff --git a/start-flashpoint.sh.orig b/start-flashpoint.sh +index 1eff7b3..90947de 100755 +--- a/start-flashpoint.sh.orig ++++ b/start-flashpoint.sh +@@ -2,17 +2,27 @@ + + [ `id -u` -ne 0 ] && cd -P -- "`dirname "$0"`" || exit 1 + ++# Set configurable Flashpoint root directory. ++# Default: absolute path to script's directory ++[ "$FP_DIR" ] || FP_DIR="$(realpath "$(dirname "${BASH_SOURCE[0]}")")" ++ + if [ "`file -b --mime-type Libraries/lib/x86_64-linux-gnu/libgtk-3.so.0`" = 'application/x-sharedlib' ]; then +- export GALLIUM_DRIVER="i915,i965,iris,nouveau,nouveau_vieux,r200,r300,r600,radeon,radeonsi,zink,swrast" GDK_PIXBUF_MODULE_FILE="$PWD/Libraries/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders.cache" GSETTINGS_SCHEMA_DIR="$PWD/Libraries/share/glib-2.0/schemas" GTK_MODULES= LD_LIBRARY_PATH="$PWD/Libraries/lib/x86_64-linux-gnu" LIBGL_DRIVERS_PATH="$PWD/Libraries/lib/x86_64-linux-gnu/dri" ++ export GALLIUM_DRIVER="i915,i965,iris,nouveau,nouveau_vieux,r200,r300,r600,radeon,radeonsi,zink,swrast" GDK_PIXBUF_MODULE_FILE="$FP_DIR/Libraries/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders.cache" GSETTINGS_SCHEMA_DIR="$FP_DIR/Libraries/share/glib-2.0/schemas" GTK_MODULES= LD_LIBRARY_PATH="$FP_DIR/Libraries/lib/x86_64-linux-gnu" LIBGL_DRIVERS_PATH="$FP_DIR/Libraries/lib/x86_64-linux-gnu/dri" + if [ ! -d FPSoftware/Wine/bin ]; then + CMD="`command -v wine`" +- [ "$CMD" ] && export PATH="$PWD/Libraries/bin:${CMD%/*}" ++ [ "$CMD" ] && export PATH="$FP_DIR/Libraries/bin:${CMD%/*}" + else +- export PATH="$PWD/Libraries/bin" ++ export PATH="$FP_DIR/Libraries/bin" + fi + fi + +-export WINEPREFIX="$PWD/FPSoftware/Wine" ++export WINEPREFIX="$FP_DIR/FPSoftware/Wine" ++ ++# Check for correct ownership ++if [[ "$(stat -c %U "$FP_DIR/Data/flashpoint.sqlite")" != "$USER" ]]; then ++ echo -e "WARNING: Flashpoint directory is not owned by current user! Expect issues. ++ Run \"sudo USER=\$USER chown -R \$USER $FP_DIR\" to correct this." ++fi + + [ $# -eq 0 ] && FP_FLAGS="--js-flags=--lite_mode --ozone-platform-hint=auto" || FP_FLAGS="$@" + cd Launcher diff --git a/src/shared/Util.ts b/src/shared/Util.ts index f4ff5708a..18f647318 100644 --- a/src/shared/Util.ts +++ b/src/shared/Util.ts @@ -328,6 +328,7 @@ export function parseVarStr(str: string, config?: AppConfigData) { case 'os': return process.platform; case 'arch': return process.arch; case 'cwd': return fixSlashes(process.cwd()); + case 'home': return process.env.HOME || ''; case 'fpPath': return config ? fixSlashes(config.flashpointPath) : ''; default: return ''; }