chore: rename app from Solitaire Quest to Ferrous Solitaire
Build and Deploy / build-and-push (push) Successful in 4m40s
Build and Deploy / build-and-push (push) Successful in 4m40s
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 <noreply@anthropic.com>
This commit is contained in:
@@ -10,9 +10,9 @@ env:
|
|||||||
NDK_VERSION: 30.0.14904198
|
NDK_VERSION: 30.0.14904198
|
||||||
BUILD_TOOLS_VERSION: "36.1.0"
|
BUILD_TOOLS_VERSION: "36.1.0"
|
||||||
PLATFORM: android-34
|
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
|
GITEA_URL: https://git.aleshym.co
|
||||||
REPO: funman300/Rusty_Solitare
|
REPO: funman300/Ferrous-Solitaire
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-apk:
|
build-apk:
|
||||||
@@ -106,7 +106,7 @@ jobs:
|
|||||||
-d "{
|
-d "{
|
||||||
\"tag_name\": \"$TAG\",
|
\"tag_name\": \"$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,
|
\"draft\": false,
|
||||||
\"prerelease\": false
|
\"prerelease\": false
|
||||||
}" \
|
}" \
|
||||||
|
|||||||
+8
-8
@@ -58,7 +58,7 @@ Pure-core, no-panics-in-game-logic, and UI-first-interaction constraints are enf
|
|||||||
## 2. Workspace Structure
|
## 2. Workspace Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
solitaire_quest/
|
ferrous_solitaire/
|
||||||
│
|
│
|
||||||
├── Cargo.toml # Workspace manifest
|
├── Cargo.toml # Workspace manifest
|
||||||
├── .env.example # Server environment variable template
|
├── .env.example # Server environment variable template
|
||||||
@@ -366,12 +366,12 @@ Minimum window: 800×600. At this size cards are small but usable.
|
|||||||
|
|
||||||
### Local Storage
|
### 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)
|
~/.local/share/ferrous_solitaire/ (Linux)
|
||||||
~/Library/Application Support/solitaire_quest/ (macOS)
|
~/Library/Application Support/ferrous_solitaire/ (macOS)
|
||||||
%APPDATA%\solitaire_quest\ (Windows)
|
%APPDATA%\ferrous_solitaire\ (Windows)
|
||||||
│
|
│
|
||||||
├── stats.json # StatsSnapshot
|
├── stats.json # StatsSnapshot
|
||||||
├── progress.json # PlayerProgress (XP, level, unlocks, daily challenge)
|
├── progress.json # PlayerProgress (XP, level, unlocks, daily challenge)
|
||||||
@@ -426,7 +426,7 @@ pub enum SyncBackend {
|
|||||||
url: String,
|
url: String,
|
||||||
username: String,
|
username: String,
|
||||||
// JWT access + refresh tokens stored in OS keychain
|
// 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)
|
### Docker Compose (Recommended)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/yourname/solitaire_quest
|
git clone https://github.com/yourname/ferrous_solitaire
|
||||||
cd solitaire_quest
|
cd ferrous_solitaire
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
# Edit .env — set JWT_SECRET and SERVER_PORT
|
# Edit .env — set JWT_SECRET and SERVER_PORT
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
|
|||||||
+4
-4
@@ -1431,7 +1431,7 @@ candidate — the app-icon round — stays open.
|
|||||||
- **Android build target — first working APK** (`fb8b2ac`).
|
- **Android build target — first working APK** (`fb8b2ac`).
|
||||||
`cargo apk build -p solitaire_app --target x86_64-linux-android`
|
`cargo apk build -p solitaire_app --target x86_64-linux-android`
|
||||||
now produces a 54 MB debug-signed APK at
|
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:
|
resolved end-to-end:
|
||||||
- **`solitaire_app` split into bin + lib.** cargo-apk needs a
|
- **`solitaire_app` split into bin + lib.** cargo-apk needs a
|
||||||
`cdylib` to bundle as `libmain.so`; pure-bin crates panic
|
`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
|
achievements, replays, game-state, time-attack sessions, user
|
||||||
themes). New `solitaire_data::platform::data_dir()` shim falls
|
themes). New `solitaire_data::platform::data_dir()` shim falls
|
||||||
through to `dirs::data_dir()` on desktop and returns the per-app
|
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
|
— no JNI needed, since the package id is pinned in
|
||||||
`[package.metadata.android]`. Six call sites across
|
`[package.metadata.android]`. Six call sites across
|
||||||
`solitaire_data` plus `solitaire_engine/assets/user_dir.rs`
|
`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
|
The test's single-frame `app.update()` was sensitive to
|
||||||
first-frame `Time::delta_secs()` variance under heavy parallel
|
first-frame `Time::delta_secs()` variance under heavy parallel
|
||||||
cargo-test load, and to production-disk
|
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.
|
into the test world via `GamePlugin::build`'s load path.
|
||||||
`test_app` now resets `PendingRestoredGame(None)` after plugin
|
`test_app` now resets `PendingRestoredGame(None)` after plugin
|
||||||
build (preventing the dev machine's saved-game state from
|
build (preventing the dev machine's saved-game state from
|
||||||
@@ -2386,7 +2386,7 @@ the binary shipped with bundled artwork.
|
|||||||
patterns.
|
patterns.
|
||||||
- **Ambient audio loop** wired through the kira mixer.
|
- **Ambient audio loop** wired through the kira mixer.
|
||||||
- **Arch Linux PKGBUILDs** for the game client and sync server (under
|
- **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.**
|
- **Workspace README, CI workflow, migration guide.**
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
@@ -447,7 +447,7 @@ raw `z_index` values — they drift and cause ordering bugs.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo apk build --package solitaire_app --lib
|
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
|
## 15.2 Coordinate system reminder
|
||||||
|
|||||||
@@ -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
|
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:
|
2. Tap the badge below (on your Android device) or add the URL manually:
|
||||||
|
|
||||||
[<img src="https://raw.githubusercontent.com/ImranR98/Obtainium/main/assets/graphics/badge_obtainium.png" alt="Get it on Obtainium" height="40">](https://apps.obtainium.imranr.dev/redirect?r=obtainium://add/https://git.aleshym.co/funman300/Rusty_Solitare)
|
[<img src="https://raw.githubusercontent.com/ImranR98/Obtainium/main/assets/graphics/badge_obtainium.png" alt="Get it on Obtainium" height="40">](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.
|
3. Tap **Install** to download the current release — Obtainium will notify you when updates are available.
|
||||||
|
|
||||||
### Direct APK
|
### Direct APK
|
||||||
|
|
||||||
Download the latest `solitaire-quest.apk` from the
|
Download the latest `ferrous-solitaire.apk` from the
|
||||||
[Releases](https://git.aleshym.co/funman300/Rusty_Solitare/releases) page,
|
[Releases](https://git.aleshym.co/funman300/Ferrous-Solitaire/releases) page,
|
||||||
enable **Install from unknown sources** in your device settings, and open the file.
|
enable **Install from unknown sources** in your device settings, and open the file.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
project: default
|
project: default
|
||||||
source:
|
source:
|
||||||
repoURL: https://git.aleshym.co/funman300/Rusty_Solitare.git
|
repoURL: https://git.aleshym.co/funman300/Ferrous-Solitaire.git
|
||||||
targetRevision: master
|
targetRevision: master
|
||||||
path: deploy
|
path: deploy
|
||||||
destination:
|
destination:
|
||||||
|
|||||||
+8
-8
@@ -6,7 +6,7 @@ later sections document what's known to compile, what's stubbed, and
|
|||||||
the next milestones.
|
the next milestones.
|
||||||
|
|
||||||
> **Status (2026-05-07):** First working APK at `fb8b2ac`. 54 MB
|
> **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
|
> NOT yet been verified to launch on a device or emulator — that's
|
||||||
> the next milestone.
|
> the next milestone.
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ cargo apk build -p solitaire_app --target x86_64-linux-android
|
|||||||
Output:
|
Output:
|
||||||
|
|
||||||
```
|
```
|
||||||
target/debug/apk/solitaire-quest.apk
|
target/debug/apk/ferrous-solitaire.apk
|
||||||
```
|
```
|
||||||
|
|
||||||
Targets shipped via `[package.metadata.android].build_targets` in
|
Targets shipped via `[package.metadata.android].build_targets` in
|
||||||
@@ -164,8 +164,8 @@ Physical device:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
adb devices # confirm connection
|
adb devices # confirm connection
|
||||||
adb install target/debug/apk/solitaire-quest.apk
|
adb install target/debug/apk/ferrous-solitaire.apk
|
||||||
adb shell am start -n com.solitairequest.app/android.app.NativeActivity
|
adb shell am start -n com.ferrousapp.solitaire/android.app.NativeActivity
|
||||||
adb logcat | grep -iE "RustStdoutStderr|solitaire|panic"
|
adb logcat | grep -iE "RustStdoutStderr|solitaire|panic"
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -174,7 +174,7 @@ Emulator:
|
|||||||
```bash
|
```bash
|
||||||
emulator -avd bevy_test -no-window -gpu swiftshader_indirect &
|
emulator -avd bevy_test -no-window -gpu swiftshader_indirect &
|
||||||
adb wait-for-device
|
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.
|
# ... 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
|
- `dirs::data_dir()` returns `None` on Android. Callers in
|
||||||
`solitaire_data/src/storage.rs`, `progress.rs`, `replay.rs`,
|
`solitaire_data/src/storage.rs`, `progress.rs`, `replay.rs`,
|
||||||
`achievements.rs`, `settings.rs` all need an Android-aware
|
`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,
|
- Touch UX pass — hit-target sizes, modal scaling on small screens,
|
||||||
app lifecycle (suspend / resume), font scaling.
|
app lifecycle (suspend / resume), font scaling.
|
||||||
- Android Keystore via JNI for `auth_tokens`.
|
- 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 clippy --workspace --all-targets -- -D warnings # gate
|
||||||
cargo test --workspace # gate
|
cargo test --workspace # gate
|
||||||
cargo apk build -p solitaire_app --target x86_64-linux-android --lib
|
cargo apk build -p solitaire_app --target x86_64-linux-android --lib
|
||||||
adb install -r target/debug/apk/solitaire-quest.apk # `-r` reinstalls
|
adb install -r target/debug/apk/ferrous-solitaire.apk # `-r` reinstalls
|
||||||
adb logcat -c && adb shell am start -n com.solitairequest.app/android.app.NativeActivity
|
adb logcat -c && adb shell am start -n com.ferrousapp.solitaire/android.app.NativeActivity
|
||||||
adb logcat | grep -iE "RustStdoutStderr|solitaire"
|
adb logcat | grep -iE "RustStdoutStderr|solitaire"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -39,13 +39,13 @@ Before starting, delete any existing local save files to ensure a clean state:
|
|||||||
|
|
||||||
```
|
```
|
||||||
# Linux
|
# Linux
|
||||||
rm -rf ~/.local/share/solitaire_quest/
|
rm -rf ~/.local/share/ferrous_solitaire/
|
||||||
|
|
||||||
# macOS
|
# macOS
|
||||||
rm -rf ~/Library/Application\ Support/solitaire_quest/
|
rm -rf ~/Library/Application\ Support/ferrous_solitaire/
|
||||||
|
|
||||||
# Windows
|
# 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
|
```bash
|
||||||
# List keychain entries (uses secret-tool on GNOME)
|
# 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
|
# 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
|
### 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
|
```bash
|
||||||
# Extract the new token from the keychain
|
# 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)
|
# 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:
|
1. Corrupt both the access token and the refresh token in the keychain:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
secret-tool store --label="alice_access" service solitaire_quest_server account alice_access <<< "bad"
|
secret-tool store --label="alice_access" service ferrous_solitaire_server account alice_access <<< "bad"
|
||||||
secret-tool store --label="alice_refresh" service solitaire_quest_server account alice_refresh <<< "bad"
|
secret-tool store --label="alice_refresh" service ferrous_solitaire_server account alice_refresh <<< "bad"
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Launch the game and trigger a sync.
|
2. Launch the game and trigger a sync.
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# Maintainer: funman300 <funman300@gmail.com>
|
# Maintainer: funman300 <funman300@gmail.com>
|
||||||
|
|
||||||
pkgname=solitaire-quest-server
|
pkgname=ferrous-solitaire-server
|
||||||
pkgver=0.1.0
|
pkgver=0.1.0
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc='Self-hosted sync server for Solitaire Quest (stats, achievements, leaderboards)'
|
pkgdesc='Self-hosted sync server for Ferrous Solitaire (stats, achievements, leaderboards)'
|
||||||
url='https://github.com/funman300/solitaire-quest'
|
url='https://github.com/funman300/ferrous-solitaire'
|
||||||
license=('MIT')
|
license=('MIT')
|
||||||
arch=('x86_64')
|
arch=('x86_64')
|
||||||
makedepends=('cargo' 'rust')
|
makedepends=('cargo' 'rust')
|
||||||
@@ -12,12 +12,12 @@ depends=(
|
|||||||
'gcc-libs'
|
'gcc-libs'
|
||||||
'glibc'
|
'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).
|
# Build from the local workspace (two levels above this PKGBUILD).
|
||||||
_srcdir="$startdir/../.."
|
_srcdir="$startdir/../.."
|
||||||
source=(
|
source=(
|
||||||
'solitaire-quest-server.service'
|
'ferrous-solitaire-server.service'
|
||||||
'server.env'
|
'server.env'
|
||||||
)
|
)
|
||||||
b2sums=('SKIP'
|
b2sums=('SKIP'
|
||||||
@@ -49,12 +49,12 @@ package() {
|
|||||||
install -Dm0755 -t "$pkgdir/usr/bin/" "target/release/solitaire_server"
|
install -Dm0755 -t "$pkgdir/usr/bin/" "target/release/solitaire_server"
|
||||||
|
|
||||||
# systemd service
|
# systemd service
|
||||||
install -Dm0644 "$srcdir/solitaire-quest-server.service" \
|
install -Dm0644 "$srcdir/ferrous-solitaire-server.service" \
|
||||||
"$pkgdir/usr/lib/systemd/system/solitaire-quest-server.service"
|
"$pkgdir/usr/lib/systemd/system/ferrous-solitaire-server.service"
|
||||||
|
|
||||||
# Environment file (contains JWT_SECRET, DATABASE_URL, SERVER_PORT)
|
# Environment file (contains JWT_SECRET, DATABASE_URL, SERVER_PORT)
|
||||||
install -Dm0640 "$srcdir/server.env" \
|
install -Dm0640 "$srcdir/server.env" \
|
||||||
"$pkgdir/etc/solitaire-quest-server/server.env"
|
"$pkgdir/etc/ferrous-solitaire-server/server.env"
|
||||||
|
|
||||||
# License and docs
|
# License and docs
|
||||||
install -Dm0644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
|
install -Dm0644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
|
||||||
@@ -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
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
# Solitaire Quest Server — environment configuration
|
# Ferrous Solitaire Server — environment configuration
|
||||||
# This file is installed to /etc/solitaire-quest-server/server.env (mode 0640).
|
# This file is installed to /etc/ferrous-solitaire-server/server.env (mode 0640).
|
||||||
# Edit these values before starting the service.
|
# Edit these values before starting the service.
|
||||||
|
|
||||||
# Path to the SQLite database file.
|
# Path to the SQLite database file.
|
||||||
# The directory must be writable by the solitaire-quest service user.
|
# The directory must be writable by the ferrous-solitaire service user.
|
||||||
DATABASE_URL=sqlite:///var/lib/solitaire-quest-server/solitaire.db
|
DATABASE_URL=sqlite:///var/lib/ferrous-solitaire-server/solitaire.db
|
||||||
|
|
||||||
# HS256 signing secret for JWT tokens.
|
# HS256 signing secret for JWT tokens.
|
||||||
# Generate a strong secret with: openssl rand -hex 32
|
# Generate a strong secret with: openssl rand -hex 32
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
# Maintainer: funman300 <funman300@gmail.com>
|
# Maintainer: funman300 <funman300@gmail.com>
|
||||||
|
|
||||||
pkgname=solitaire-quest
|
pkgname=ferrous-solitaire
|
||||||
pkgver=0.1.0
|
pkgver=0.1.0
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc='Cross-platform Klondike Solitaire with progression, achievements, and optional sync'
|
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')
|
license=('MIT')
|
||||||
arch=('x86_64')
|
arch=('x86_64')
|
||||||
makedepends=('cargo' 'rust')
|
makedepends=('cargo' 'rust')
|
||||||
@@ -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
|
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
# "arm64-v8a armeabi-v7a x86_64"). Reduce in CI to
|
# "arm64-v8a armeabi-v7a x86_64"). Reduce in CI to
|
||||||
# fit the runner's disk budget — a full three-ABI
|
# fit the runner's disk budget — a full three-ABI
|
||||||
# debug build can exceed 25 GB of target/ output.
|
# 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 Path to keystore for signing (default: generates a debug keystore)
|
||||||
# KEYSTORE_PASS Keystore password (default: "android" for the generated debug keystore)
|
# KEYSTORE_PASS Keystore password (default: "android" for the generated debug keystore)
|
||||||
# KEY_ALIAS Key alias (default: "androiddebugkey")
|
# KEY_ALIAS Key alias (default: "androiddebugkey")
|
||||||
@@ -35,7 +35,7 @@ set -euo pipefail
|
|||||||
|
|
||||||
PROFILE="${PROFILE:-debug}"
|
PROFILE="${PROFILE:-debug}"
|
||||||
ABIS="${ABIS:-arm64-v8a armeabi-v7a x86_64}"
|
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)"
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
cd "$REPO_ROOT"
|
cd "$REPO_ROOT"
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ tiny-skia = { workspace = true }
|
|||||||
# already uses ships into the APK without copy-tree gymnastics.
|
# already uses ships into the APK without copy-tree gymnastics.
|
||||||
# `apk_name` keeps the output filename predictable across machines.
|
# `apk_name` keeps the output filename predictable across machines.
|
||||||
[package.metadata.android]
|
[package.metadata.android]
|
||||||
package = "com.solitairequest.app"
|
package = "com.ferrousapp.solitaire"
|
||||||
apk_name = "solitaire-quest"
|
apk_name = "ferrous-solitaire"
|
||||||
build_targets = ["aarch64-linux-android", "armv7-linux-androideabi", "x86_64-linux-android"]
|
build_targets = ["aarch64-linux-android", "armv7-linux-androideabi", "x86_64-linux-android"]
|
||||||
assets = "../assets"
|
assets = "../assets"
|
||||||
# Density-bucketed launcher icons. `aapt` processes `res/mipmap-*/` and
|
# Density-bucketed launcher icons. `aapt` processes `res/mipmap-*/` and
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
shared object name without the `lib` prefix or `.so` suffix.
|
shared object name without the `lib` prefix or `.so` suffix.
|
||||||
-->
|
-->
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.solitairequest.app"
|
package="com.ferrousapp.solitaire"
|
||||||
android:versionCode="1"
|
android:versionCode="1"
|
||||||
android:versionName="1.0">
|
android:versionName="1.0">
|
||||||
|
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ pub fn run() {
|
|||||||
title: "Ferrous Solitaire".into(),
|
title: "Ferrous Solitaire".into(),
|
||||||
// X11/Wayland WM_CLASS so taskbar managers group
|
// X11/Wayland WM_CLASS so taskbar managers group
|
||||||
// multiple windows of this app correctly.
|
// multiple windows of this app correctly.
|
||||||
name: Some("solitaire-quest".into()),
|
name: Some("ferrous-solitaire".into()),
|
||||||
resolution: window_resolution,
|
resolution: window_resolution,
|
||||||
position: window_position,
|
position: window_position,
|
||||||
// AutoNoVsync prefers Mailbox (triple-buffered) and
|
// AutoNoVsync prefers Mailbox (triple-buffered) and
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use std::path::{Path, PathBuf};
|
|||||||
|
|
||||||
pub use solitaire_sync::AchievementRecord;
|
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";
|
const FILE_NAME: &str = "achievements.json";
|
||||||
|
|
||||||
/// Platform-specific default path for `achievements.json`.
|
/// Platform-specific default path for `achievements.json`.
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
use crate::auth_tokens::TokenError;
|
use crate::auth_tokens::TokenError;
|
||||||
|
|
||||||
const KEY_ALIAS: &str = "solitaire_quest_token_key";
|
const KEY_ALIAS: &str = "ferrous_solitaire_token_key";
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct TokenBlob {
|
struct TokenBlob {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Secure storage for JWT access and refresh tokens using the OS keychain.
|
//! 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"`.
|
//! keys `"{username}_access"` and `"{username}_refresh"`.
|
||||||
//!
|
//!
|
||||||
//! On Linux this requires a running secret service (GNOME Keyring / KWallet).
|
//! 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.
|
/// Service name used to namespace all keychain entries for this application.
|
||||||
#[cfg(not(target_os = "android"))]
|
#[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`.
|
/// Map a `keyring_core::Error` to the appropriate `TokenError`.
|
||||||
#[cfg(not(target_os = "android"))]
|
#[cfg(not(target_os = "android"))]
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//! The rest of `solitaire_data` (settings, stats, achievements,
|
//! The rest of `solitaire_data` (settings, stats, achievements,
|
||||||
//! replays, progress, game state) and the engine's user-themes
|
//! replays, progress, game state) and the engine's user-themes
|
||||||
//! discovery all need a base path under which to nest
|
//! discovery all need a base path under which to nest
|
||||||
//! `solitaire_quest/<file>`. On desktop the right answer is
|
//! `ferrous_solitaire/<file>`. On desktop the right answer is
|
||||||
//! `dirs::data_dir()` (which resolves to platform-appropriate
|
//! `dirs::data_dir()` (which resolves to platform-appropriate
|
||||||
//! locations: `~/.local/share` on Linux, `~/Library/Application
|
//! locations: `~/.local/share` on Linux, `~/Library/Application
|
||||||
//! Support` on macOS, `%APPDATA%` on Windows). On Android the
|
//! 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
|
//! [`data_dir`] is a thin shim that returns the right base path
|
||||||
//! per target. Callers continue to append
|
//! per target. Callers continue to append
|
||||||
//! `solitaire_quest/<file>` themselves, so the on-disk layout is
|
//! `ferrous_solitaire/<file>` themselves, so the on-disk layout is
|
||||||
//! identical across platforms (the per-app Android sandbox makes
|
//! 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).
|
//! export from one platform deserialises cleanly on another).
|
||||||
//!
|
//!
|
||||||
//! # Why hardcode on Android?
|
//! # Why hardcode on Android?
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
//! `AndroidApp` context through Bevy's startup hooks and a
|
//! `AndroidApp` context through Bevy's startup hooks and a
|
||||||
//! per-call JNI bridge — meaningfully more code than the
|
//! per-call JNI bridge — meaningfully more code than the
|
||||||
//! sandbox-guaranteed `/data/data/<package>/files` path. The
|
//! sandbox-guaranteed `/data/data/<package>/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
|
//! time in `solitaire_app/Cargo.toml`'s
|
||||||
//! `[package.metadata.android]` block, so a hardcoded path is
|
//! `[package.metadata.android]` block, so a hardcoded path is
|
||||||
//! safe until that ever changes (at which point this constant
|
//! 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
|
/// constant and the Cargo metadata together if the package id
|
||||||
/// ever changes.
|
/// ever changes.
|
||||||
#[cfg(target_os = "android")]
|
#[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,
|
/// Returns the per-user data directory for the current target,
|
||||||
/// or `None` if the platform doesn't expose one (rare; usually
|
/// or `None` if the platform doesn't expose one (rare; usually
|
||||||
/// indicates a broken `$HOME` or `$XDG_*` configuration on a
|
/// indicates a broken `$HOME` or `$XDG_*` configuration on a
|
||||||
/// minimal Linux container).
|
/// minimal Linux container).
|
||||||
///
|
///
|
||||||
/// Callers append `solitaire_quest/<file>` themselves. See the
|
/// Callers append `ferrous_solitaire/<file>` themselves. See the
|
||||||
/// module-level doc comment for the per-platform behaviour and
|
/// module-level doc comment for the per-platform behaviour and
|
||||||
/// why Android uses a hardcoded path.
|
/// why Android uses a hardcoded path.
|
||||||
pub fn data_dir() -> Option<PathBuf> {
|
pub fn data_dir() -> Option<PathBuf> {
|
||||||
@@ -87,6 +87,6 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn data_dir_returns_sandbox_path_on_android() {
|
fn data_dir_returns_sandbox_path_on_android() {
|
||||||
let dir = data_dir().expect("android must report a data dir");
|
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"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use chrono::{Datelike, NaiveDate};
|
|||||||
pub use solitaire_sync::progress::level_for_xp;
|
pub use solitaire_sync::progress::level_for_xp;
|
||||||
pub use solitaire_sync::PlayerProgress;
|
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";
|
const FILE_NAME: &str = "progress.json";
|
||||||
|
|
||||||
/// Deterministic seed derived from a date, identical for all players globally.
|
/// Deterministic seed derived from a date, identical for all players globally.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! Win-game replay recording + storage.
|
//! Win-game replay recording + storage.
|
||||||
//!
|
//!
|
||||||
//! When a player wins, the engine freezes the in-memory recording into a
|
//! When a player wins, the engine freezes the in-memory recording into a
|
||||||
//! [`Replay`] and persists it to `<data_dir>/solitaire_quest/latest_replay.json`
|
//! [`Replay`] and persists it to `<data_dir>/ferrous_solitaire/latest_replay.json`
|
||||||
//! via [`save_latest_replay_to`]. The Stats screen offers a "Watch replay"
|
//! 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
|
//! 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
|
//! 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::game_state::{DrawMode, GameMode};
|
||||||
use solitaire_core::pile::PileType;
|
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 LATEST_REPLAY_FILE_NAME: &str = "latest_replay.json";
|
||||||
const REPLAY_HISTORY_FILE_NAME: &str = "replays.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.
|
/// Rolling history of the player's most recent winning replays.
|
||||||
///
|
///
|
||||||
/// Stored as a single JSON file at
|
/// Stored as a single JSON file at
|
||||||
/// `<data_dir>/solitaire_quest/replays.json` (see
|
/// `<data_dir>/ferrous_solitaire/replays.json` (see
|
||||||
/// [`replay_history_path`]). Capped at [`REPLAY_HISTORY_CAP`] entries —
|
/// [`replay_history_path`]). Capped at [`REPLAY_HISTORY_CAP`] entries —
|
||||||
/// when [`append_replay_to_history`] pushes past the cap, the oldest
|
/// when [`append_replay_to_history`] pushes past the cap, the oldest
|
||||||
/// entry is dropped so the file never grows unbounded.
|
/// entry is dropped so the file never grows unbounded.
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use std::path::{Path, PathBuf};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use solitaire_core::game_state::{DifficultyLevel, DrawMode};
|
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";
|
const SETTINGS_FILE_NAME: &str = "settings.json";
|
||||||
|
|
||||||
/// Animation playback speed for card transitions.
|
/// Animation playback speed for card transitions.
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use solitaire_core::game_state::{GameState, GAME_STATE_SCHEMA_VERSION};
|
|||||||
|
|
||||||
use crate::stats::StatsSnapshot;
|
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 STATS_FILE_NAME: &str = "stats.json";
|
||||||
const GAME_STATE_FILE_NAME: &str = "game_state.json";
|
const GAME_STATE_FILE_NAME: &str = "game_state.json";
|
||||||
const TIME_ATTACK_SESSION_FILE_NAME: &str = "time_attack_session.json";
|
const TIME_ATTACK_SESSION_FILE_NAME: &str = "time_attack_session.json";
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ static USER_THEME_DIR_OVERRIDE: OnceLock<PathBuf> = OnceLock::new();
|
|||||||
/// Sub-folder under `dirs::data_dir()` where the project keeps every
|
/// Sub-folder under `dirs::data_dir()` where the project keeps every
|
||||||
/// per-user file. Matches the existing convention used by
|
/// per-user file. Matches the existing convention used by
|
||||||
/// `solitaire_data` for `settings.json`, `stats.json`, etc.
|
/// `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.
|
/// Sub-folder under [`APP_DIR_NAME`] dedicated to user themes.
|
||||||
const THEME_DIR_NAME: &str = "themes";
|
const THEME_DIR_NAME: &str = "themes";
|
||||||
@@ -97,19 +97,19 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[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"));
|
let dir = user_theme_dir_for(PathBuf::from("/tmp/data"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dir,
|
dir,
|
||||||
PathBuf::from("/tmp/data/solitaire_quest/themes"),
|
PathBuf::from("/tmp/data/ferrous_solitaire/themes"),
|
||||||
"user dir must nest under solitaire_quest/themes"
|
"user dir must nest under ferrous_solitaire/themes"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn user_theme_dir_for_handles_empty_root() {
|
fn user_theme_dir_for_handles_empty_root() {
|
||||||
let dir = user_theme_dir_for(PathBuf::new());
|
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]
|
#[test]
|
||||||
|
|||||||
@@ -1302,7 +1302,7 @@ mod tests {
|
|||||||
|
|
||||||
/// Build a minimal headless `App` with just `GamePlugin` installed.
|
/// Build a minimal headless `App` with just `GamePlugin` installed.
|
||||||
/// Disables persistence and overrides the seed so tests are deterministic
|
/// 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 {
|
fn test_app(seed: u64) -> App {
|
||||||
let mut app = App::new();
|
let mut app = App::new();
|
||||||
app.add_plugins(MinimalPlugins).add_plugins(GamePlugin);
|
app.add_plugins(MinimalPlugins).add_plugins(GamePlugin);
|
||||||
@@ -1316,7 +1316,7 @@ mod tests {
|
|||||||
// can't leak into per-test world state and trip the
|
// can't leak into per-test world state and trip the
|
||||||
// `pending.0.is_some()` guard in `auto_save_game_state` /
|
// `pending.0.is_some()` guard in `auto_save_game_state` /
|
||||||
// `save_game_state_on_exit`. Without this clear, an
|
// `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.
|
// would silently disable the auto-save path under test.
|
||||||
app.insert_resource(PendingRestoredGame(None));
|
app.insert_resource(PendingRestoredGame(None));
|
||||||
// Override the system-time seed with a known value.
|
// Override the system-time seed with a known value.
|
||||||
|
|||||||
@@ -552,7 +552,7 @@ mod tests {
|
|||||||
.add_plugins(GamePlugin::headless())
|
.add_plugins(GamePlugin::headless())
|
||||||
.add_plugins(ReplayPlaybackPlugin);
|
.add_plugins(ReplayPlaybackPlugin);
|
||||||
// Disable game-state persistence so tests don't touch the
|
// 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::GameStatePath(None));
|
||||||
app.insert_resource(crate::game_plugin::ReplayPath(None));
|
app.insert_resource(crate::game_plugin::ReplayPath(None));
|
||||||
// Tick once so any startup systems flush before the first
|
// Tick once so any startup systems flush before the first
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ pub struct StatsCell;
|
|||||||
/// Resource holding the rolling [`ReplayHistory`] of recent winning
|
/// Resource holding the rolling [`ReplayHistory`] of recent winning
|
||||||
/// replays.
|
/// replays.
|
||||||
///
|
///
|
||||||
/// Populated from `<data_dir>/solitaire_quest/replays.json` at startup
|
/// Populated from `<data_dir>/ferrous_solitaire/replays.json` at startup
|
||||||
/// and refreshed in-place whenever the engine writes a new winning
|
/// and refreshed in-place whenever the engine writes a new winning
|
||||||
/// replay so the Stats overlay's selector always reflects the current
|
/// replay so the Stats overlay's selector always reflects the current
|
||||||
/// on-disk history.
|
/// on-disk history.
|
||||||
@@ -166,7 +166,7 @@ impl Default for StatsPlugin {
|
|||||||
|
|
||||||
impl StatsPlugin {
|
impl StatsPlugin {
|
||||||
/// Plugin configured with no persistence. Use in tests and headless apps
|
/// 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.
|
/// incorrect.
|
||||||
pub fn headless() -> Self {
|
pub fn headless() -> Self {
|
||||||
Self { storage_path: None }
|
Self { storage_path: None }
|
||||||
|
|||||||
@@ -307,7 +307,7 @@ mod tests {
|
|||||||
.add_plugins(TimeAttackPlugin);
|
.add_plugins(TimeAttackPlugin);
|
||||||
app.init_resource::<ButtonInput<KeyCode>>();
|
app.init_resource::<ButtonInput<KeyCode>>();
|
||||||
// Disable session persistence — tests must not touch the real
|
// 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));
|
app.insert_resource(TimeAttackSessionPath(None));
|
||||||
// The plugin's startup-load hook may have populated TimeAttackResource
|
// The plugin's startup-load hook may have populated TimeAttackResource
|
||||||
// from a real on-disk session. Reset it so each test starts inactive.
|
// from a real on-disk session. Reset it so each test starts inactive.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: ..
|
context: ..
|
||||||
dockerfile: solitaire_server/Dockerfile
|
dockerfile: solitaire_server/Dockerfile
|
||||||
image: solitaire-quest-server:latest
|
image: ferrous-solitaire-server:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "${SERVER_PORT:-8080}:8080"
|
- "${SERVER_PORT:-8080}:8080"
|
||||||
|
|||||||
Reference in New Issue
Block a user