Commit Graph

5 Commits

Author SHA1 Message Date
funman300 b0306a9b1d feat(match): atomic exactly-once match-completion transaction
Add complete_match: one BEGIN/COMMIT that validates identity + result,
enforces a durable (profile_id, match_identity) uniqueness guard
(migration 0022 match_completions), persists match history, and grants
coins + XP/level-ups + W/D/L/DNF statistics + objectives + achievements
exactly once. Any failure rolls the whole match back (no compensating
cleanup). Handles sequential/restart/concurrent replay, conflicting
re-report (first result canonical), DNF (loss economics, own stat
bucket) and no-contest (zero economic effect).

Adds tx-scoped variants: statistics::record_match_tx/
record_position_goals_tx, objective::increment_metric_tx,
achievement::check_and_unlock_tx. New MatchResultKind/CompleteMatchRequest/
MatchCompletionResult models + POST /matches/complete route.
2026-08-20 16:38:16 +00:00
funman300 679f147c6a Phase 20: achievement system
CI / Build, lint & test (push) Failing after 1m19s
18 data-driven achievements (achievements.json) across 8 trigger categories:
matches_played, matches_won, goals_scored, packs_opened, sbcs_completed,
cards_owned, level, objectives_completed, drafts_completed. Rarities span
common → epic. Coin rewards range from 500 (first_match) to 6000 (win_50).

check_and_unlock() queries the relevant metric from existing tables, skips
already-earned achievements via INSERT OR IGNORE, grants coin rewards, and
fires a persistent notification per unlock. Trigger values are cached per
call to avoid redundant DB round-trips for same-trigger achievements.

Checks run automatically after every match result (all triggers), every
pack open (packs_opened), and every successful SBC submission (sbcs_completed).

GET /achievements returns all definitions annotated with unlocked/unlocked_at,
plus earned and total counts. POST /matches/result response gains an
achievements_unlocked array (empty when nothing new unlocked).

AppState gains achievement_defs (Arc<Vec<AchievementDefinition>>) loaded
from data/achievements/**/*.json at startup — same pattern as obj_defs.

5 new tests: list endpoint, first_match unlock, first_win unlock, no-dup
guard, coin reward verification. Core now at 82 tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 18:03:54 -07:00
funman300 d97695d414 Phase 17: profile level-up system
CI / Build, lint & test (push) Failing after 57s
XP thresholds (500→1200→2000→…→11000→+2500/level) drive automatic level
increases. add_xp_with_levelup() replaces bare add_xp() in match
processing: for each level gained it grants level×500 coins and milestone
packs (bronze@5, silver@10, gold@15, rare_gold@20, gold every 5 after).

GET /profile now returns computed level (recalculated from XP so it
stays consistent), xp_to_next_level, and xp_for_next_level so the
dashboard can render a progress bar without a second call.

POST /matches/result response gains level_ups array (empty when no
level-up occurred) with new_level, coins_granted, pack_granted per event.

Four new tests: profile level fields, level_for_xp boundary checks,
level-up event in match result, milestone pack unit test.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 17:41:13 -07:00
funman300 4ae1081aa9 feat: Phase 3 — polish & settings complete
Card pool:
- TOTW cards (5): overall 88-92, rarity "totw"
- Hero cards (5): overall 85-88, rarity "hero"
- Icon cards (5): overall 93-95, rarity "icon" (permanent, non-loan)

Packs:
- TOTW Pack (30,000 coins): 5 TOTW cards guaranteed
- Icon Pack (50,000 coins): 3 icon cards guaranteed
- Hero Pack (20,000 coins): 5 hero cards guaranteed

Objectives:
- Milestone objectives (6): win 10/50, score 100/500 goals, 10 SBCs, 25 packs

Formations:
- GET /formations returns 12 valid formation strings

Multiple named squads (#20):
- POST /squad with no squad_id always creates a new squad
- POST /squad with squad_id updates that specific squad
- GET /squads: list all squads for the club (metadata only)
- GET /squads/🆔 squad with players + chemistry
- DELETE /squads/🆔 remove a squad

Per-position goal stats (#41):
- Migration 0003: position_goals table
- POST /matches/result accepts optional goal_positions: ["ST","CAM",...]
- GET /statistics now includes position_goals map

Settings (#44-46):
- GET /settings: { difficulty, preferred_formation } with defaults
- PUT /settings: upsert any key-value combination

Draft mode skeleton (#38):
- GET /draft/squad?difficulty=... returns a randomly generated 11-player squad

Integration tests:
- 9 new tests covering: formations, pack buy/open, SBC submit, settings R/W,
  multiple squads, draft endpoint, goal position tracking, win streak

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 15:46:20 -07:00
funman300 1ffe0ffa9f Initial commit: OpenFUT Core
Offline Ultimate Team backend — game-independent REST API.

- 19 API endpoints: auth, profiles, clubs, cards, packs, squads,
  objectives, SBCs, match rewards, NPC market, statistics
- Axum + SQLite + SQLx with full migrations
- Weighted pack generator, SBC validation engine
- JSON-driven mod data (cards, packs, objectives, SBCs)
- 5 integration tests passing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 14:54:51 -07:00