Commit Graph

5 Commits

Author SHA1 Message Date
funman300 ceb9c950a1 chore: add pedantic workspace lints (#90)
Add [workspace.lints.rust] and wire each member crate up with
[lints] workspace = true:

  unsafe_code = "deny"        (forbid would break the Android JNI build)
  single_use_lifetimes = "warn"
  trivial_casts = "warn"
  unused_lifetimes = "warn"
  unused_qualifications = "warn"
  variant_size_differences = "warn"
  unexpected_cfgs = "warn"

unsafe_code is "deny" rather than the issue's "forbid" so the three
Android JNI FFI modules (android_keystore, android_clipboard, safe_area)
can opt back in with a scoped #![allow(unsafe_code)] — forbid cannot be
locally overridden. Pure crates carry no unsafe and stay clean.

Clean up the warnings the new lints surface:
- 150ish unused_qualifications removed via `cargo fix` (purely syntactic
  redundant-path-prefix removals).
- table_plugin: the TABLE_COLOUR import was #[cfg(test)]-gated while the
  camera clear-colour used the fully-qualified path; unqualifying it left
  a non-test build with no import. Made the import unconditional instead.
- assets/sources: the `as &[u8]` casts in embed_*_svg! coerce each
  fixed-size &[u8; N] to a uniform slice so the tuples fit the
  &[(&str, &[u8])] arrays — load-bearing, so scoped #[allow(trivial_casts)].

Workspace clippy -D warnings and the full test suite pass. Android build
not compiled here (needs the NDK; built separately per CLAUDE.md §15) —
the deny + scoped-allow keeps the JNI unsafe blocks legal.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 13:05:28 -07:00
funman300 6e407a3ea7 fix(engine,server): safe area clamp, analytics batch, achievement save order, daily rollover, replay validation, leaderboard opt-in (#56, #60, #61, #62, #66, #68)
Build and Deploy / build-and-push (push) Successful in 3m54s
- #66: Clamp safe-area insets to 25% of window height with warn!() on excess
- #68: Move fire_flush outside per-event loop in analytics (batch flush once)
- #56: Persist progress before marking reward_granted to prevent XP loss on crash
- #60: Add DateRolloverTimer + check_date_rollover system for midnight seed refresh
- #62: Add validate_header() in replay upload with mode/draw_mode allowlists
- #61: Restore two-query leaderboard opt-in check (SELECT then UPDATE); original
       queries already in .sqlx cache; EXISTS variant would require sqlx prepare

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-28 13:07:22 -07:00
funman300 d5d869a6c8 fix(multi): resolve 16 bugs from comprehensive rules and code review
Build and Deploy / build-and-push (push) Successful in 4m12s
Core (solitaire_core):
- fix(core): auto-complete now requires waste empty to prevent deadlock
- fix(core): reject multi-card moves from waste pile (Klondike rule)
- fix(core): reject foundation-to-foundation moves (score farming exploit)
- fix(core): undo restores score from snapshot baseline, not live score
- feat(scoring): add +5 flip bonus when face-down tableau card is exposed
- feat(scoring): add recycle penalty (Draw-1: -100/pass, Draw-3: -20/pass)

Engine (solitaire_engine):
- fix(engine): remove TokioRuntimeResource::default() panic; degrade gracefully
- fix(engine): add ModalScrim guard to handle_new_game spawn site
- fix(engine): add ModalScrim guard to spawn_restore_prompt spawn site
- fix(engine): add ModalScrim guard to check_no_moves spawn site

Server / Web (solitaire_server):
- fix(web): correct draw_mode casing in replay submission (DrawOne/DrawThree)
- fix(web): correct mode casing in replay submission (Classic) for leaderboard
- fix(web): trim recorded_at to YYYY-MM-DD for NaiveDate deserialization
- fix(server): move /avatars route outside auth middleware (was always 401)

Data / Sync (solitaire_data, solitaire_sync):
- fix(data): namespace Android token file under APP_DIR_NAME with migration
- fix(data): Android token store now multi-user (HashMap); no silent overwrite
- fix(sync): draw_one_wins + draw_three_wins invariant preserved after merge

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 16:27:09 -07:00
funman300 ffed6b27e9 perf(engine): share Tokio runtime across all network tasks (M-16)
Replace per-call new_current_thread() runtimes with a single
TokioRuntimeResource(Arc<Runtime>) built once at startup using
new_multi_thread(worker_threads(2)). The Arc is cloned cheaply into
each AsyncComputeTaskPool closure, eliminating repeated OS thread
allocation on every sync pull/push, auth, avatar fetch, and analytics
flush. Using a multi-threaded runtime ensures concurrent block_on calls
from different worker threads are safe.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 20:58:51 -07:00
funman300 677999a51e feat(engine): wire avatar download and display into profile modal
Build and Deploy / build-and-push (push) Successful in 4m15s
- Add avatar_plugin: AvatarPlugin, AvatarResource, AvatarFetchEvent
  - After AvatarFetchEvent fires, spawns an async reqwest download task
  - On completion, decodes image bytes via image::load_from_memory →
    Image::from_dynamic and inserts into Assets<Image>
- Expand auth task to also call fetch_me_with_token immediately after
  login/register so avatar_url is available without a second round-trip
- poll_auth_task fires AvatarFetchEvent when avatar_url is Some, building
  the full URL from base_url + relative avatar path
- Profile modal shows 48px circular avatar ImageNode when AvatarResource
  is populated, or an initials disc (first letter of username) as fallback
- Add image = "0.25" and reqwest to solitaire_engine deps
- Add fetch_me_with_token helper to SolitaireServerClient for use when
  the access token hasn't been persisted to keychain yet

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 17:27:27 -07:00