From d761a150d7800fd1a9a3ba8f2eea26a04e55dabd Mon Sep 17 00:00:00 2001 From: funman300 Date: Thu, 14 May 2026 19:23:49 -0700 Subject: [PATCH] chore: rename app from Solitaire Quest to Ferrous Solitaire MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updates all in-tree references: - Android package: com.solitairequest.app → com.ferrousapp.solitaire - APK name: solitaire-quest → ferrous-solitaire - Data dir: solitaire_quest → ferrous_solitaire (across all 6 data modules + engine) - Keyring service: solitaire_quest_server → ferrous_solitaire_server - Android Keystore key: solitaire_quest_token_key → ferrous_solitaire_token_key - Gitea repo: Rusty_Solitare → Ferrous-Solitaire (also fixes "Solitare" typo) - Renamed pkg/solitaire-quest* → pkg/ferrous-solitaire* - Updated ArgoCD, docker-compose, CI workflow, build script, all docs Co-Authored-By: Claude Sonnet 4.6 --- .gitea/workflows/android-release.yml | 6 ++--- ARCHITECTURE.md | 16 ++++++------- CHANGELOG.md | 8 +++---- CLAUDE.md | 2 +- README.md | 8 +++---- argocd/application.yaml | 2 +- docs/ANDROID.md | 16 ++++++------- docs/sync_test_runbook.md | 16 ++++++------- .../PKGBUILD | 16 ++++++------- .../ferrous-solitaire-server.service | 23 +++++++++++++++++++ .../server.env | 8 +++---- .../PKGBUILD | 4 ++-- .../solitaire-quest-server.service | 23 ------------------- scripts/build_android_apk.sh | 4 ++-- solitaire_app/Cargo.toml | 4 ++-- solitaire_app/android/AndroidManifest.xml | 2 +- solitaire_app/src/lib.rs | 2 +- solitaire_data/src/achievements.rs | 2 +- solitaire_data/src/android_keystore.rs | 2 +- solitaire_data/src/auth_tokens.rs | 4 ++-- solitaire_data/src/platform.rs | 14 +++++------ solitaire_data/src/progress.rs | 2 +- solitaire_data/src/replay.rs | 6 ++--- solitaire_data/src/settings.rs | 2 +- solitaire_data/src/storage.rs | 2 +- solitaire_engine/src/assets/user_dir.rs | 10 ++++---- solitaire_engine/src/game_plugin.rs | 4 ++-- solitaire_engine/src/replay_playback.rs | 2 +- solitaire_engine/src/stats_plugin.rs | 4 ++-- solitaire_engine/src/time_attack_plugin.rs | 2 +- solitaire_server/docker-compose.yml | 2 +- 31 files changed, 109 insertions(+), 109 deletions(-) rename pkg/{solitaire-quest-server => ferrous-solitaire-server}/PKGBUILD (72%) create mode 100644 pkg/ferrous-solitaire-server/ferrous-solitaire-server.service rename pkg/{solitaire-quest-server => ferrous-solitaire-server}/server.env (54%) rename pkg/{solitaire-quest => ferrous-solitaire}/PKGBUILD (93%) delete mode 100644 pkg/solitaire-quest-server/solitaire-quest-server.service diff --git a/.gitea/workflows/android-release.yml b/.gitea/workflows/android-release.yml index ac1ba40..45f9b3b 100644 --- a/.gitea/workflows/android-release.yml +++ b/.gitea/workflows/android-release.yml @@ -10,9 +10,9 @@ env: NDK_VERSION: 30.0.14904198 BUILD_TOOLS_VERSION: "36.1.0" PLATFORM: android-34 - APK_OUT: target/release/apk/solitaire-quest.apk + APK_OUT: target/release/apk/ferrous-solitaire.apk GITEA_URL: https://git.aleshym.co - REPO: funman300/Rusty_Solitare + REPO: funman300/Ferrous-Solitaire jobs: build-apk: @@ -106,7 +106,7 @@ jobs: -d "{ \"tag_name\": \"$TAG\", \"name\": \"$TAG\", - \"body\": \"## Android release $TAG\n\n**Install / update with Obtainium** — add this source URL:\n\`\`\`\nhttps://git.aleshym.co/funman300/Rusty_Solitare\n\`\`\`\n\nOr download \`solitaire-quest.apk\` below and sideload it directly.\", + \"body\": \"## Android release $TAG\n\n**Install / update with Obtainium** — add this source URL:\n\`\`\`\nhttps://git.aleshym.co/funman300/Ferrous-Solitaire\n\`\`\`\n\nOr download \`ferrous-solitaire.apk\` below and sideload it directly.\", \"draft\": false, \"prerelease\": false }" \ diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 1e1d91c..8f8b3b5 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -58,7 +58,7 @@ Pure-core, no-panics-in-game-logic, and UI-first-interaction constraints are enf ## 2. Workspace Structure ``` -solitaire_quest/ +ferrous_solitaire/ │ ├── Cargo.toml # Workspace manifest ├── .env.example # Server environment variable template @@ -366,12 +366,12 @@ Minimum window: 800×600. At this size cards are small but usable. ### Local Storage -All files stored under `dirs::data_dir() / "solitaire_quest"/`: +All files stored under `dirs::data_dir() / "ferrous_solitaire"/`: ``` -~/.local/share/solitaire_quest/ (Linux) -~/Library/Application Support/solitaire_quest/ (macOS) -%APPDATA%\solitaire_quest\ (Windows) +~/.local/share/ferrous_solitaire/ (Linux) +~/Library/Application Support/ferrous_solitaire/ (macOS) +%APPDATA%\ferrous_solitaire\ (Windows) │ ├── stats.json # StatsSnapshot ├── progress.json # PlayerProgress (XP, level, unlocks, daily challenge) @@ -426,7 +426,7 @@ pub enum SyncBackend { url: String, username: String, // JWT access + refresh tokens stored in OS keychain - // key: "solitaire_quest_server_{username}" + // key: "ferrous_solitaire_server_{username}" }, } ``` @@ -980,8 +980,8 @@ Add `--features bevy/dynamic_linking` during development to dramatically reduce ### Docker Compose (Recommended) ```bash -git clone https://github.com/yourname/solitaire_quest -cd solitaire_quest +git clone https://github.com/yourname/ferrous_solitaire +cd ferrous_solitaire cp .env.example .env # Edit .env — set JWT_SECRET and SERVER_PORT docker compose up -d diff --git a/CHANGELOG.md b/CHANGELOG.md index 8141cf1..5ae186e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1431,7 +1431,7 @@ candidate — the app-icon round — stays open. - **Android build target — first working APK** (`fb8b2ac`). `cargo apk build -p solitaire_app --target x86_64-linux-android` now produces a 54 MB debug-signed APK at - `target/debug/apk/solitaire-quest.apk`. Five gating points + `target/debug/apk/ferrous-solitaire.apk`. Five gating points resolved end-to-end: - **`solitaire_app` split into bin + lib.** cargo-apk needs a `cdylib` to bundle as `libmain.so`; pure-bin crates panic @@ -1548,7 +1548,7 @@ candidate — the app-icon round — stays open. achievements, replays, game-state, time-attack sessions, user themes). New `solitaire_data::platform::data_dir()` shim falls through to `dirs::data_dir()` on desktop and returns the per-app - sandbox at `/data/data/com.solitairequest.app/files` on Android + sandbox at `/data/data/com.ferrousapp.solitaire/files` on Android — no JNI needed, since the package id is pinned in `[package.metadata.android]`. Six call sites across `solitaire_data` plus `solitaire_engine/assets/user_dir.rs` @@ -1690,7 +1690,7 @@ fully reverted and is not part of this release. The test's single-frame `app.update()` was sensitive to first-frame `Time::delta_secs()` variance under heavy parallel cargo-test load, and to production-disk - `~/.local/share/solitaire_quest/game_state.json` state leaking + `~/.local/share/ferrous_solitaire/game_state.json` state leaking into the test world via `GamePlugin::build`'s load path. `test_app` now resets `PendingRestoredGame(None)` after plugin build (preventing the dev machine's saved-game state from @@ -2386,7 +2386,7 @@ the binary shipped with bundled artwork. patterns. - **Ambient audio loop** wired through the kira mixer. - **Arch Linux PKGBUILDs** for the game client and sync server (under - the separate `solitaire-quest-pkgbuild` directory). + the separate `ferrous-solitaire-pkgbuild` directory). - **Workspace README, CI workflow, migration guide.** ### Changed diff --git a/CLAUDE.md b/CLAUDE.md index 233cade..ceba491 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -447,7 +447,7 @@ raw `z_index` values — they drift and cause ordering bugs. ```bash cargo apk build --package solitaire_app --lib -adb install -r target/debug/apk/solitaire-quest.apk +adb install -r target/debug/apk/ferrous-solitaire.apk ``` ## 15.2 Coordinate system reminder diff --git a/README.md b/README.md index 74e42a3..592d796 100644 --- a/README.md +++ b/README.md @@ -38,16 +38,16 @@ optional self-hosted sync so your stats follow you across machines. 1. Install [Obtainium](https://github.com/ImranR98/Obtainium/releases) on your device 2. Tap the badge below (on your Android device) or add the URL manually: - [Get it on Obtainium](https://apps.obtainium.imranr.dev/redirect?r=obtainium://add/https://git.aleshym.co/funman300/Rusty_Solitare) + [Get it on Obtainium](https://apps.obtainium.imranr.dev/redirect?r=obtainium://add/https://git.aleshym.co/funman300/Ferrous-Solitaire) - Manual URL: `https://git.aleshym.co/funman300/Rusty_Solitare` + Manual URL: `https://git.aleshym.co/funman300/Ferrous-Solitaire` 3. Tap **Install** to download the current release — Obtainium will notify you when updates are available. ### Direct APK -Download the latest `solitaire-quest.apk` from the -[Releases](https://git.aleshym.co/funman300/Rusty_Solitare/releases) page, +Download the latest `ferrous-solitaire.apk` from the +[Releases](https://git.aleshym.co/funman300/Ferrous-Solitaire/releases) page, enable **Install from unknown sources** in your device settings, and open the file. ## Building diff --git a/argocd/application.yaml b/argocd/application.yaml index d62d673..b0a279f 100644 --- a/argocd/application.yaml +++ b/argocd/application.yaml @@ -6,7 +6,7 @@ metadata: spec: project: default source: - repoURL: https://git.aleshym.co/funman300/Rusty_Solitare.git + repoURL: https://git.aleshym.co/funman300/Ferrous-Solitaire.git targetRevision: master path: deploy destination: diff --git a/docs/ANDROID.md b/docs/ANDROID.md index 55ff451..91f0fb6 100644 --- a/docs/ANDROID.md +++ b/docs/ANDROID.md @@ -6,7 +6,7 @@ later sections document what's known to compile, what's stubbed, and the next milestones. > **Status (2026-05-07):** First working APK at `fb8b2ac`. 54 MB -> debug-signed `solitaire-quest.apk` for `x86_64-linux-android`. Has +> debug-signed `ferrous-solitaire.apk` for `x86_64-linux-android`. Has > NOT yet been verified to launch on a device or emulator — that's > the next milestone. @@ -121,7 +121,7 @@ cargo apk build -p solitaire_app --target x86_64-linux-android Output: ``` -target/debug/apk/solitaire-quest.apk +target/debug/apk/ferrous-solitaire.apk ``` Targets shipped via `[package.metadata.android].build_targets` in @@ -164,8 +164,8 @@ Physical device: ```bash adb devices # confirm connection -adb install target/debug/apk/solitaire-quest.apk -adb shell am start -n com.solitairequest.app/android.app.NativeActivity +adb install target/debug/apk/ferrous-solitaire.apk +adb shell am start -n com.ferrousapp.solitaire/android.app.NativeActivity adb logcat | grep -iE "RustStdoutStderr|solitaire|panic" ``` @@ -174,7 +174,7 @@ Emulator: ```bash emulator -avd bevy_test -no-window -gpu swiftshader_indirect & adb wait-for-device -adb install target/debug/apk/solitaire-quest.apk +adb install target/debug/apk/ferrous-solitaire.apk # ... same start + logcat steps as above. ``` @@ -203,7 +203,7 @@ What's NOT yet ported / not yet measured: - `dirs::data_dir()` returns `None` on Android. Callers in `solitaire_data/src/storage.rs`, `progress.rs`, `replay.rs`, `achievements.rs`, `settings.rs` all need an Android-aware - helper (likely `/data/data/com.solitairequest.app/files`). + helper (likely `/data/data/com.ferrousapp.solitaire/files`). - Touch UX pass — hit-target sizes, modal scaling on small screens, app lifecycle (suspend / resume), font scaling. - Android Keystore via JNI for `auth_tokens`. @@ -221,8 +221,8 @@ cargo build -p solitaire_app # desktop sanity cargo clippy --workspace --all-targets -- -D warnings # gate cargo test --workspace # gate cargo apk build -p solitaire_app --target x86_64-linux-android --lib -adb install -r target/debug/apk/solitaire-quest.apk # `-r` reinstalls -adb logcat -c && adb shell am start -n com.solitairequest.app/android.app.NativeActivity +adb install -r target/debug/apk/ferrous-solitaire.apk # `-r` reinstalls +adb logcat -c && adb shell am start -n com.ferrousapp.solitaire/android.app.NativeActivity adb logcat | grep -iE "RustStdoutStderr|solitaire" ``` diff --git a/docs/sync_test_runbook.md b/docs/sync_test_runbook.md index d828568..3e31ba7 100644 --- a/docs/sync_test_runbook.md +++ b/docs/sync_test_runbook.md @@ -39,13 +39,13 @@ Before starting, delete any existing local save files to ensure a clean state: ``` # Linux -rm -rf ~/.local/share/solitaire_quest/ +rm -rf ~/.local/share/ferrous_solitaire/ # macOS -rm -rf ~/Library/Application\ Support/solitaire_quest/ +rm -rf ~/Library/Application\ Support/ferrous_solitaire/ # Windows -rmdir /s %APPDATA%\solitaire_quest\ +rmdir /s %APPDATA%\ferrous_solitaire\ ``` --- @@ -130,10 +130,10 @@ On the machine where you want to test (Linux example): ```bash # List keychain entries (uses secret-tool on GNOME) -secret-tool search service solitaire_quest_server +secret-tool search service ferrous_solitaire_server # Overwrite alice's access token with a deliberately invalid value -secret-tool store --label="alice_access" service solitaire_quest_server account alice_access <<< "invalid.token.value" +secret-tool store --label="alice_access" service ferrous_solitaire_server account alice_access <<< "invalid.token.value" ``` ### Step 2 — Trigger a sync with the expired/invalid token @@ -148,7 +148,7 @@ secret-tool store --label="alice_access" service solitaire_quest_server account ```bash # Extract the new token from the keychain -secret-tool lookup service solitaire_quest_server account alice_access | head -c 50 +secret-tool lookup service ferrous_solitaire_server account alice_access | head -c 50 # Should look like a valid JWT (three base64 segments separated by dots) ``` @@ -157,8 +157,8 @@ secret-tool lookup service solitaire_quest_server account alice_access | head -c 1. Corrupt both the access token and the refresh token in the keychain: ```bash - secret-tool store --label="alice_access" service solitaire_quest_server account alice_access <<< "bad" - secret-tool store --label="alice_refresh" service solitaire_quest_server account alice_refresh <<< "bad" + secret-tool store --label="alice_access" service ferrous_solitaire_server account alice_access <<< "bad" + secret-tool store --label="alice_refresh" service ferrous_solitaire_server account alice_refresh <<< "bad" ``` 2. Launch the game and trigger a sync. diff --git a/pkg/solitaire-quest-server/PKGBUILD b/pkg/ferrous-solitaire-server/PKGBUILD similarity index 72% rename from pkg/solitaire-quest-server/PKGBUILD rename to pkg/ferrous-solitaire-server/PKGBUILD index 145876f..2c40491 100644 --- a/pkg/solitaire-quest-server/PKGBUILD +++ b/pkg/ferrous-solitaire-server/PKGBUILD @@ -1,10 +1,10 @@ # Maintainer: funman300 -pkgname=solitaire-quest-server +pkgname=ferrous-solitaire-server pkgver=0.1.0 pkgrel=1 -pkgdesc='Self-hosted sync server for Solitaire Quest (stats, achievements, leaderboards)' -url='https://github.com/funman300/solitaire-quest' +pkgdesc='Self-hosted sync server for Ferrous Solitaire (stats, achievements, leaderboards)' +url='https://github.com/funman300/ferrous-solitaire' license=('MIT') arch=('x86_64') makedepends=('cargo' 'rust') @@ -12,12 +12,12 @@ depends=( 'gcc-libs' 'glibc' ) -backup=('etc/solitaire-quest-server/server.env') +backup=('etc/ferrous-solitaire-server/server.env') # Build from the local workspace (two levels above this PKGBUILD). _srcdir="$startdir/../.." source=( - 'solitaire-quest-server.service' + 'ferrous-solitaire-server.service' 'server.env' ) b2sums=('SKIP' @@ -49,12 +49,12 @@ package() { install -Dm0755 -t "$pkgdir/usr/bin/" "target/release/solitaire_server" # systemd service - install -Dm0644 "$srcdir/solitaire-quest-server.service" \ - "$pkgdir/usr/lib/systemd/system/solitaire-quest-server.service" + install -Dm0644 "$srcdir/ferrous-solitaire-server.service" \ + "$pkgdir/usr/lib/systemd/system/ferrous-solitaire-server.service" # Environment file (contains JWT_SECRET, DATABASE_URL, SERVER_PORT) install -Dm0640 "$srcdir/server.env" \ - "$pkgdir/etc/solitaire-quest-server/server.env" + "$pkgdir/etc/ferrous-solitaire-server/server.env" # License and docs install -Dm0644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" diff --git a/pkg/ferrous-solitaire-server/ferrous-solitaire-server.service b/pkg/ferrous-solitaire-server/ferrous-solitaire-server.service new file mode 100644 index 0000000..30a8577 --- /dev/null +++ b/pkg/ferrous-solitaire-server/ferrous-solitaire-server.service @@ -0,0 +1,23 @@ +[Unit] +Description=Ferrous Solitaire Sync Server +Documentation=https://github.com/funman300/ferrous-solitaire/blob/main/README_SERVER.md +After=network.target + +[Service] +Type=simple +User=ferrous-solitaire +Group=ferrous-solitaire +EnvironmentFile=/etc/ferrous-solitaire-server/server.env +ExecStart=/usr/bin/solitaire_server +Restart=on-failure +RestartSec=5s + +# Harden the service +NoNewPrivileges=true +PrivateTmp=true +ProtectSystem=strict +ProtectHome=true +ReadWritePaths=/var/lib/ferrous-solitaire-server + +[Install] +WantedBy=multi-user.target diff --git a/pkg/solitaire-quest-server/server.env b/pkg/ferrous-solitaire-server/server.env similarity index 54% rename from pkg/solitaire-quest-server/server.env rename to pkg/ferrous-solitaire-server/server.env index 2b0afbe..f2fb3fa 100644 --- a/pkg/solitaire-quest-server/server.env +++ b/pkg/ferrous-solitaire-server/server.env @@ -1,10 +1,10 @@ -# Solitaire Quest Server — environment configuration -# This file is installed to /etc/solitaire-quest-server/server.env (mode 0640). +# Ferrous Solitaire Server — environment configuration +# This file is installed to /etc/ferrous-solitaire-server/server.env (mode 0640). # Edit these values before starting the service. # Path to the SQLite database file. -# The directory must be writable by the solitaire-quest service user. -DATABASE_URL=sqlite:///var/lib/solitaire-quest-server/solitaire.db +# The directory must be writable by the ferrous-solitaire service user. +DATABASE_URL=sqlite:///var/lib/ferrous-solitaire-server/solitaire.db # HS256 signing secret for JWT tokens. # Generate a strong secret with: openssl rand -hex 32 diff --git a/pkg/solitaire-quest/PKGBUILD b/pkg/ferrous-solitaire/PKGBUILD similarity index 93% rename from pkg/solitaire-quest/PKGBUILD rename to pkg/ferrous-solitaire/PKGBUILD index 43b3229..8cc34cb 100644 --- a/pkg/solitaire-quest/PKGBUILD +++ b/pkg/ferrous-solitaire/PKGBUILD @@ -1,10 +1,10 @@ # Maintainer: funman300 -pkgname=solitaire-quest +pkgname=ferrous-solitaire pkgver=0.1.0 pkgrel=1 pkgdesc='Cross-platform Klondike Solitaire with progression, achievements, and optional sync' -url='https://github.com/funman300/solitaire-quest' +url='https://github.com/funman300/ferrous-solitaire' license=('MIT') arch=('x86_64') makedepends=('cargo' 'rust') diff --git a/pkg/solitaire-quest-server/solitaire-quest-server.service b/pkg/solitaire-quest-server/solitaire-quest-server.service deleted file mode 100644 index 8f1c7c7..0000000 --- a/pkg/solitaire-quest-server/solitaire-quest-server.service +++ /dev/null @@ -1,23 +0,0 @@ -[Unit] -Description=Solitaire Quest Sync Server -Documentation=https://github.com/funman300/solitaire-quest/blob/main/README_SERVER.md -After=network.target - -[Service] -Type=simple -User=solitaire-quest -Group=solitaire-quest -EnvironmentFile=/etc/solitaire-quest-server/server.env -ExecStart=/usr/bin/solitaire_server -Restart=on-failure -RestartSec=5s - -# Harden the service -NoNewPrivileges=true -PrivateTmp=true -ProtectSystem=strict -ProtectHome=true -ReadWritePaths=/var/lib/solitaire-quest-server - -[Install] -WantedBy=multi-user.target diff --git a/scripts/build_android_apk.sh b/scripts/build_android_apk.sh index e375e50..f11ad78 100755 --- a/scripts/build_android_apk.sh +++ b/scripts/build_android_apk.sh @@ -18,7 +18,7 @@ # "arm64-v8a armeabi-v7a x86_64"). Reduce in CI to # fit the runner's disk budget — a full three-ABI # debug build can exceed 25 GB of target/ output. -# APK_OUT Output APK path (default: target/$PROFILE/apk/solitaire-quest.apk) +# APK_OUT Output APK path (default: target/$PROFILE/apk/ferrous-solitaire.apk) # KEYSTORE Path to keystore for signing (default: generates a debug keystore) # KEYSTORE_PASS Keystore password (default: "android" for the generated debug keystore) # KEY_ALIAS Key alias (default: "androiddebugkey") @@ -35,7 +35,7 @@ set -euo pipefail PROFILE="${PROFILE:-debug}" ABIS="${ABIS:-arm64-v8a armeabi-v7a x86_64}" -APK_OUT="${APK_OUT:-target/${PROFILE}/apk/solitaire-quest.apk}" +APK_OUT="${APK_OUT:-target/${PROFILE}/apk/ferrous-solitaire.apk}" REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$REPO_ROOT" diff --git a/solitaire_app/Cargo.toml b/solitaire_app/Cargo.toml index ed6e2bd..2556920 100644 --- a/solitaire_app/Cargo.toml +++ b/solitaire_app/Cargo.toml @@ -56,8 +56,8 @@ tiny-skia = { workspace = true } # already uses ships into the APK without copy-tree gymnastics. # `apk_name` keeps the output filename predictable across machines. [package.metadata.android] -package = "com.solitairequest.app" -apk_name = "solitaire-quest" +package = "com.ferrousapp.solitaire" +apk_name = "ferrous-solitaire" build_targets = ["aarch64-linux-android", "armv7-linux-androideabi", "x86_64-linux-android"] assets = "../assets" # Density-bucketed launcher icons. `aapt` processes `res/mipmap-*/` and diff --git a/solitaire_app/android/AndroidManifest.xml b/solitaire_app/android/AndroidManifest.xml index ceb25bc..f814f93 100644 --- a/solitaire_app/android/AndroidManifest.xml +++ b/solitaire_app/android/AndroidManifest.xml @@ -13,7 +13,7 @@ shared object name without the `lib` prefix or `.so` suffix. --> diff --git a/solitaire_app/src/lib.rs b/solitaire_app/src/lib.rs index 46d7178..16512a5 100644 --- a/solitaire_app/src/lib.rs +++ b/solitaire_app/src/lib.rs @@ -109,7 +109,7 @@ pub fn run() { title: "Ferrous Solitaire".into(), // X11/Wayland WM_CLASS so taskbar managers group // multiple windows of this app correctly. - name: Some("solitaire-quest".into()), + name: Some("ferrous-solitaire".into()), resolution: window_resolution, position: window_position, // AutoNoVsync prefers Mailbox (triple-buffered) and diff --git a/solitaire_data/src/achievements.rs b/solitaire_data/src/achievements.rs index b6aca92..40cdaa7 100644 --- a/solitaire_data/src/achievements.rs +++ b/solitaire_data/src/achievements.rs @@ -10,7 +10,7 @@ use std::path::{Path, PathBuf}; pub use solitaire_sync::AchievementRecord; -const APP_DIR_NAME: &str = "solitaire_quest"; +const APP_DIR_NAME: &str = "ferrous_solitaire"; const FILE_NAME: &str = "achievements.json"; /// Platform-specific default path for `achievements.json`. diff --git a/solitaire_data/src/android_keystore.rs b/solitaire_data/src/android_keystore.rs index 457ac40..46995dd 100644 --- a/solitaire_data/src/android_keystore.rs +++ b/solitaire_data/src/android_keystore.rs @@ -19,7 +19,7 @@ use std::path::PathBuf; use crate::auth_tokens::TokenError; -const KEY_ALIAS: &str = "solitaire_quest_token_key"; +const KEY_ALIAS: &str = "ferrous_solitaire_token_key"; #[derive(Serialize, Deserialize)] struct TokenBlob { diff --git a/solitaire_data/src/auth_tokens.rs b/solitaire_data/src/auth_tokens.rs index d638e29..b9ff173 100644 --- a/solitaire_data/src/auth_tokens.rs +++ b/solitaire_data/src/auth_tokens.rs @@ -1,6 +1,6 @@ //! Secure storage for JWT access and refresh tokens using the OS keychain. //! -//! Tokens are stored under service name `"solitaire_quest_server"` with entry +//! Tokens are stored under service name `"ferrous_solitaire_server"` with entry //! keys `"{username}_access"` and `"{username}_refresh"`. //! //! On Linux this requires a running secret service (GNOME Keyring / KWallet). @@ -46,7 +46,7 @@ pub enum TokenError { /// Service name used to namespace all keychain entries for this application. #[cfg(not(target_os = "android"))] -const SERVICE: &str = "solitaire_quest_server"; +const SERVICE: &str = "ferrous_solitaire_server"; /// Map a `keyring_core::Error` to the appropriate `TokenError`. #[cfg(not(target_os = "android"))] diff --git a/solitaire_data/src/platform.rs b/solitaire_data/src/platform.rs index a8bd36c..47777ef 100644 --- a/solitaire_data/src/platform.rs +++ b/solitaire_data/src/platform.rs @@ -3,7 +3,7 @@ //! The rest of `solitaire_data` (settings, stats, achievements, //! replays, progress, game state) and the engine's user-themes //! discovery all need a base path under which to nest -//! `solitaire_quest/`. On desktop the right answer is +//! `ferrous_solitaire/`. On desktop the right answer is //! `dirs::data_dir()` (which resolves to platform-appropriate //! locations: `~/.local/share` on Linux, `~/Library/Application //! Support` on macOS, `%APPDATA%` on Windows). On Android the @@ -12,9 +12,9 @@ //! //! [`data_dir`] is a thin shim that returns the right base path //! per target. Callers continue to append -//! `solitaire_quest/` themselves, so the on-disk layout is +//! `ferrous_solitaire/` themselves, so the on-disk layout is //! identical across platforms (the per-app Android sandbox makes -//! the extra `solitaire_quest/` segment harmless, and a `tar` +//! the extra `ferrous_solitaire/` segment harmless, and a `tar` //! export from one platform deserialises cleanly on another). //! //! # Why hardcode on Android? @@ -24,7 +24,7 @@ //! `AndroidApp` context through Bevy's startup hooks and a //! per-call JNI bridge — meaningfully more code than the //! sandbox-guaranteed `/data/data//files` path. The -//! package name `com.solitairequest.app` is fixed at compile +//! package name `com.ferrousapp.solitaire` is fixed at compile //! time in `solitaire_app/Cargo.toml`'s //! `[package.metadata.android]` block, so a hardcoded path is //! safe until that ever changes (at which point this constant @@ -40,14 +40,14 @@ use std::path::PathBuf; /// constant and the Cargo metadata together if the package id /// ever changes. #[cfg(target_os = "android")] -const ANDROID_APP_FILES_DIR: &str = "/data/data/com.solitairequest.app/files"; +const ANDROID_APP_FILES_DIR: &str = "/data/data/com.ferrousapp.solitaire/files"; /// Returns the per-user data directory for the current target, /// or `None` if the platform doesn't expose one (rare; usually /// indicates a broken `$HOME` or `$XDG_*` configuration on a /// minimal Linux container). /// -/// Callers append `solitaire_quest/` themselves. See the +/// Callers append `ferrous_solitaire/` themselves. See the /// module-level doc comment for the per-platform behaviour and /// why Android uses a hardcoded path. pub fn data_dir() -> Option { @@ -87,6 +87,6 @@ mod tests { #[test] fn data_dir_returns_sandbox_path_on_android() { let dir = data_dir().expect("android must report a data dir"); - assert_eq!(dir, PathBuf::from("/data/data/com.solitairequest.app/files")); + assert_eq!(dir, PathBuf::from("/data/data/com.ferrousapp.solitaire/files")); } } diff --git a/solitaire_data/src/progress.rs b/solitaire_data/src/progress.rs index a3cc95b..9dccdf3 100644 --- a/solitaire_data/src/progress.rs +++ b/solitaire_data/src/progress.rs @@ -14,7 +14,7 @@ use chrono::{Datelike, NaiveDate}; pub use solitaire_sync::progress::level_for_xp; pub use solitaire_sync::PlayerProgress; -const APP_DIR_NAME: &str = "solitaire_quest"; +const APP_DIR_NAME: &str = "ferrous_solitaire"; const FILE_NAME: &str = "progress.json"; /// Deterministic seed derived from a date, identical for all players globally. diff --git a/solitaire_data/src/replay.rs b/solitaire_data/src/replay.rs index 3b4e3bd..82d97a3 100644 --- a/solitaire_data/src/replay.rs +++ b/solitaire_data/src/replay.rs @@ -1,7 +1,7 @@ //! Win-game replay recording + storage. //! //! When a player wins, the engine freezes the in-memory recording into a -//! [`Replay`] and persists it to `/solitaire_quest/latest_replay.json` +//! [`Replay`] and persists it to `/ferrous_solitaire/latest_replay.json` //! via [`save_latest_replay_to`]. The Stats screen offers a "Watch replay" //! action that loads it via [`load_latest_replay_from`] so the player can //! revisit (or, in a future build, watch the engine re-execute) the path @@ -29,7 +29,7 @@ use serde::{Deserialize, Serialize}; use solitaire_core::game_state::{DrawMode, GameMode}; use solitaire_core::pile::PileType; -const APP_DIR_NAME: &str = "solitaire_quest"; +const APP_DIR_NAME: &str = "ferrous_solitaire"; const LATEST_REPLAY_FILE_NAME: &str = "latest_replay.json"; const REPLAY_HISTORY_FILE_NAME: &str = "replays.json"; @@ -221,7 +221,7 @@ impl Replay { /// Rolling history of the player's most recent winning replays. /// /// Stored as a single JSON file at -/// `/solitaire_quest/replays.json` (see +/// `/ferrous_solitaire/replays.json` (see /// [`replay_history_path`]). Capped at [`REPLAY_HISTORY_CAP`] entries — /// when [`append_replay_to_history`] pushes past the cap, the oldest /// entry is dropped so the file never grows unbounded. diff --git a/solitaire_data/src/settings.rs b/solitaire_data/src/settings.rs index c4beaa9..ab27a9b 100644 --- a/solitaire_data/src/settings.rs +++ b/solitaire_data/src/settings.rs @@ -11,7 +11,7 @@ use std::path::{Path, PathBuf}; use serde::{Deserialize, Serialize}; use solitaire_core::game_state::{DifficultyLevel, DrawMode}; -const APP_DIR_NAME: &str = "solitaire_quest"; +const APP_DIR_NAME: &str = "ferrous_solitaire"; const SETTINGS_FILE_NAME: &str = "settings.json"; /// Animation playback speed for card transitions. diff --git a/solitaire_data/src/storage.rs b/solitaire_data/src/storage.rs index 0da45f8..e2d7a14 100644 --- a/solitaire_data/src/storage.rs +++ b/solitaire_data/src/storage.rs @@ -13,7 +13,7 @@ use solitaire_core::game_state::{GameState, GAME_STATE_SCHEMA_VERSION}; use crate::stats::StatsSnapshot; -const APP_DIR_NAME: &str = "solitaire_quest"; +const APP_DIR_NAME: &str = "ferrous_solitaire"; const STATS_FILE_NAME: &str = "stats.json"; const GAME_STATE_FILE_NAME: &str = "game_state.json"; const TIME_ATTACK_SESSION_FILE_NAME: &str = "time_attack_session.json"; diff --git a/solitaire_engine/src/assets/user_dir.rs b/solitaire_engine/src/assets/user_dir.rs index d31dcaa..22e1a8f 100644 --- a/solitaire_engine/src/assets/user_dir.rs +++ b/solitaire_engine/src/assets/user_dir.rs @@ -29,7 +29,7 @@ static USER_THEME_DIR_OVERRIDE: OnceLock = OnceLock::new(); /// Sub-folder under `dirs::data_dir()` where the project keeps every /// per-user file. Matches the existing convention used by /// `solitaire_data` for `settings.json`, `stats.json`, etc. -const APP_DIR_NAME: &str = "solitaire_quest"; +const APP_DIR_NAME: &str = "ferrous_solitaire"; /// Sub-folder under [`APP_DIR_NAME`] dedicated to user themes. const THEME_DIR_NAME: &str = "themes"; @@ -97,19 +97,19 @@ mod tests { use super::*; #[test] - fn user_theme_dir_for_appends_solitaire_quest_themes() { + fn user_theme_dir_for_appends_ferrous_solitaire_themes() { let dir = user_theme_dir_for(PathBuf::from("/tmp/data")); assert_eq!( dir, - PathBuf::from("/tmp/data/solitaire_quest/themes"), - "user dir must nest under solitaire_quest/themes" + PathBuf::from("/tmp/data/ferrous_solitaire/themes"), + "user dir must nest under ferrous_solitaire/themes" ); } #[test] fn user_theme_dir_for_handles_empty_root() { let dir = user_theme_dir_for(PathBuf::new()); - assert_eq!(dir, PathBuf::from("solitaire_quest/themes")); + assert_eq!(dir, PathBuf::from("ferrous_solitaire/themes")); } #[test] diff --git a/solitaire_engine/src/game_plugin.rs b/solitaire_engine/src/game_plugin.rs index 3295aff..77ebb2a 100644 --- a/solitaire_engine/src/game_plugin.rs +++ b/solitaire_engine/src/game_plugin.rs @@ -1302,7 +1302,7 @@ mod tests { /// Build a minimal headless `App` with just `GamePlugin` installed. /// Disables persistence and overrides the seed so tests are deterministic - /// and don't touch `~/.local/share/solitaire_quest/game_state.json`. + /// and don't touch `~/.local/share/ferrous_solitaire/game_state.json`. fn test_app(seed: u64) -> App { let mut app = App::new(); app.add_plugins(MinimalPlugins).add_plugins(GamePlugin); @@ -1316,7 +1316,7 @@ mod tests { // can't leak into per-test world state and trip the // `pending.0.is_some()` guard in `auto_save_game_state` / // `save_game_state_on_exit`. Without this clear, an - // unrelated `~/.local/share/solitaire_quest/game_state.json` + // unrelated `~/.local/share/ferrous_solitaire/game_state.json` // would silently disable the auto-save path under test. app.insert_resource(PendingRestoredGame(None)); // Override the system-time seed with a known value. diff --git a/solitaire_engine/src/replay_playback.rs b/solitaire_engine/src/replay_playback.rs index 8ae4064..97924c5 100644 --- a/solitaire_engine/src/replay_playback.rs +++ b/solitaire_engine/src/replay_playback.rs @@ -552,7 +552,7 @@ mod tests { .add_plugins(GamePlugin::headless()) .add_plugins(ReplayPlaybackPlugin); // Disable game-state persistence so tests don't touch the - // real ~/.local/share/solitaire_quest/game_state.json. + // real ~/.local/share/ferrous_solitaire/game_state.json. app.insert_resource(crate::game_plugin::GameStatePath(None)); app.insert_resource(crate::game_plugin::ReplayPath(None)); // Tick once so any startup systems flush before the first diff --git a/solitaire_engine/src/stats_plugin.rs b/solitaire_engine/src/stats_plugin.rs index 2fc524f..f968624 100644 --- a/solitaire_engine/src/stats_plugin.rs +++ b/solitaire_engine/src/stats_plugin.rs @@ -64,7 +64,7 @@ pub struct StatsCell; /// Resource holding the rolling [`ReplayHistory`] of recent winning /// replays. /// -/// Populated from `/solitaire_quest/replays.json` at startup +/// Populated from `/ferrous_solitaire/replays.json` at startup /// and refreshed in-place whenever the engine writes a new winning /// replay so the Stats overlay's selector always reflects the current /// on-disk history. @@ -166,7 +166,7 @@ impl Default for StatsPlugin { impl StatsPlugin { /// Plugin configured with no persistence. Use in tests and headless apps - /// where touching `~/.local/share/solitaire_quest/stats.json` would be + /// where touching `~/.local/share/ferrous_solitaire/stats.json` would be /// incorrect. pub fn headless() -> Self { Self { storage_path: None } diff --git a/solitaire_engine/src/time_attack_plugin.rs b/solitaire_engine/src/time_attack_plugin.rs index ce3fbea..829fd5d 100644 --- a/solitaire_engine/src/time_attack_plugin.rs +++ b/solitaire_engine/src/time_attack_plugin.rs @@ -307,7 +307,7 @@ mod tests { .add_plugins(TimeAttackPlugin); app.init_resource::>(); // Disable session persistence — tests must not touch the real - // ~/.local/share/solitaire_quest/time_attack_session.json. + // ~/.local/share/ferrous_solitaire/time_attack_session.json. app.insert_resource(TimeAttackSessionPath(None)); // The plugin's startup-load hook may have populated TimeAttackResource // from a real on-disk session. Reset it so each test starts inactive. diff --git a/solitaire_server/docker-compose.yml b/solitaire_server/docker-compose.yml index 5779110..757a91c 100644 --- a/solitaire_server/docker-compose.yml +++ b/solitaire_server/docker-compose.yml @@ -3,7 +3,7 @@ services: build: context: .. dockerfile: solitaire_server/Dockerfile - image: solitaire-quest-server:latest + image: ferrous-solitaire-server:latest restart: unless-stopped ports: - "${SERVER_PORT:-8080}:8080"