Commit Graph

806 Commits

Author SHA1 Message Date
gamer147
b5e13551cd Merge branch 'feat/decomp-only-followups' 2026-06-13 10:07:12 -04:00
gamer147
43dee9d00b feat(card): /card/create_foil_card Seer's Globe conversion (1 orb + 1 base → 1 foil) 2026-06-13 09:58:55 -04:00
gamer147
13ca7d118e feat(config): persist is_foil_preferred + is_prize_preferred (stubs → real) 2026-06-13 09:54:51 -04:00
gamer147
911374106f feat(emblem): /emblem/update_emblem sets viewer.SelectedEmblem (ownership-validated) 2026-06-13 08:16:19 -04:00
gamer147
bc110e2274 feat(emblem): /emblem/favorite accept-only (persistence deferred) 2026-06-13 08:15:29 -04:00
gamer147
921297227e feat(emblem): /emblem/emblem_list returns owned ids 2026-06-13 08:14:36 -04:00
gamer147
c5c61b6e9d feat(degree): /degree/update_degree sets viewer.SelectedDegree (ownership-validated) 2026-06-13 08:13:49 -04:00
gamer147
356e58ba25 feat(degree): /degree/favorite accept-only (persistence deferred) 2026-06-13 08:12:02 -04:00
gamer147
68741b2980 feat(degree): /degree/degree_list returns owned ids 2026-06-13 08:11:13 -04:00
gamer147
025c2a8c56 feat(mission): /mission/receive_reward stub (refresh-only, no decomp)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 23:33:50 -04:00
gamer147
e54e63a762 feat(mission): /mission/buy_additional_right stub (refresh-only, no decomp)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 23:29:55 -04:00
gamer147
30b8af2d16 feat(pack): /pack/get_leader_skin_owned_status (empty per-class buckets) 2026-06-12 23:26:29 -04:00
gamer147
c870b95be9 feat(sleeve): /sleeve/favorite accept-only (persistence deferred) 2026-06-12 23:22:25 -04:00
gamer147
f879c05dca feat(sleeve): /sleeve/sleeve_list returns owned ids 2026-06-12 23:17:10 -04:00
gamer147
e2f5786b60 feat(mypage): /mypage/get_special_crystal_info (empty — no offers) 2026-06-12 23:12:49 -04:00
gamer147
43e4c06a2c feat(mypage): /mypage/finish_battle (returns check=0 — no in-flight state yet) 2026-06-12 23:08:42 -04:00
gamer147
b4a87792dc feat(account): /account/update_region_code (accept-only) 2026-06-12 23:01:53 -04:00
gamer147
56ef994275 style(profile): drop file-path header comments from quick-wins task 1 2026-06-12 22:59:35 -04:00
gamer147
398764a4cc feat(profile): /profile/update_official_mark_display
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 22:54:52 -04:00
gamer147
37cb8b5eeb test(free-battle): literal-prod-JSON parity for /do_matching 2026-06-12 21:30:19 -04:00
gamer147
55ec279f73 test(free-battle): force_finish no-op returns empty object 2026-06-12 21:29:52 -04:00
gamer147
31ecda95f7 test(free-battle): consistency battle_result echoes on rotation URL 2026-06-12 21:29:26 -04:00
gamer147
523ac605a1 test(free-battle): /finish echoes battle_result with no rank fields 2026-06-12 21:29:02 -04:00
gamer147
487a86fbbb test(free-battle): 3001 when viewer has no deck for format 2026-06-12 21:28:35 -04:00
gamer147
db298a0652 test(free-battle): two-viewer PvP pairing flow 2026-06-12 21:28:00 -04:00
gamer147
4a09b03820 test(free-battle): DoMatching first poll returns 3002 2026-06-12 21:27:33 -04:00
gamer147
3e58fbb650 feat(free-battle): FreeBattleController + routing smoke tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 21:24:02 -04:00
gamer147
84760490e7 feat(free-battle): register PvpOnly ModePolicy for rotation+unlimited 2026-06-12 21:17:24 -04:00
gamer147
1054c86038 feat(free-battle): add FreeBattleFinishResponseDto 2026-06-12 21:17:04 -04:00
gamer147
36eb25da44 feat(free-battle): add FreeBattleFinishRequestDto 2026-06-12 21:16:47 -04:00
gamer147
1a55561c5d feat(free-battle): add DoMatchingResponseDto 2026-06-12 21:16:20 -04:00
gamer147
fe9ec1fc68 feat(free-battle): add DoMatchingRequestDto 2026-06-12 21:15:59 -04:00
gamer147
d0d6a6916c fix(card-master): wire EnableServing kill switch in controller + add to config-section test 2026-06-12 12:52:06 -04:00
gamer147
b4a279ef0c feat(load-index): emit data.card_master_hash on mismatch (tier-1 freshness gate) 2026-06-12 12:27:40 -04:00
gamer147
92a21e7a7a test(routing): cover /immutable_data/card_master routing 2026-06-12 12:20:46 -04:00
gamer147
6eb4547866 fix(card-master): 500 not 503 when blob missing; explicit test-project copy link 2026-06-12 12:19:37 -04:00
gamer147
d74bae9ba0 feat(card-master): implement POST /immutable_data/card_master serving captured blob 2026-06-12 12:11:15 -04:00
gamer147
29f1cdd7d5 fix(card-master): make CardMasterRequest.CardMasterHash non-nullable to match wire spec 2026-06-12 12:09:13 -04:00
gamer147
dc39ed4146 feat(card-master): add request/response DTOs for /immutable_data/card_master 2026-06-12 12:05:14 -04:00
gamer147
014cd62729 fix(card-master): degrade payload provider gracefully on IO error or empty file 2026-06-12 12:03:32 -04:00
gamer147
02cc8537f2 feat(card-master): add ICardMasterPayloadProvider singleton, load blob at startup 2026-06-12 11:59:35 -04:00
gamer147
c65ec14544 feat(config): add CardMasterConfig section (CurrentHash + EnableServing) 2026-06-12 11:54:32 -04:00
gamer147
8dc21b8f27 feat(card-master): stash captured prod blob as static asset for tier-1 serving 2026-06-12 11:48:20 -04:00
gamer147
4acc38b4af feat(content-server): scaffold SVSim.ContentServer
New ASP.NET Core 8 web project, standalone (no project references). Serves
the CDN-mirror tree from a configured root via UseStaticFiles mounted at
/dl/, with ServeUnknownFileTypes=true + DefaultContentType=octet-stream
since blob filenames are bare MD5 hashes with no extension and asset
extensions (.unity3d/.acb/.usm) aren't in the default MIME map.

Configuration: SVSIM_CONTENT_ROOT env var (or Content:Root in appsettings,
or --Content:Root cli arg) points at the asset root containing the dl/
subdirectory. The asset root is populated by data_dumps/scripts/
content_cdn_mirror.py in the outer repo (~22 GB Eng+Jpn).

Listens on port 5149 (distinct from EmulatedEntrypoint's 5148). Lightweight
request logging via inline middleware emits status/method/path/ms for every
request. /health returns "ok".

End-to-end smoke verified against the populated working copy at
4670rPsPMVlRTd2:
- Both lang tier-1s served (Eng 1013b / Jpn 955b), MD5s match live CDN.
- A category manifest (bg_assetmanifest Eng) served + verified.
- A 4.5 MB font asset bundle served + MD5-verified.
- A Jpn voice from Sound/Jpn/ served + MD5-verified.
- Hardlinked content (master_ai_ally_common.unity3d, hash shared across
  langs) served identically from both Resource/Eng/ and Resource/Jpn/.
- 404 for missing blobs (correct).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 10:29:37 -04:00
gamer147
1960e28298 refactor(auth): decouple Steam handler from request DTO shape
Translation middleware now extracts viewer_id/steam_id/steam_session_ticket
from the decrypted msgpack dict into HttpContext.Items before the typed
DTO deserialize. The Steam handler reads from there instead of re-parsing
Request.Body — so authed action DTOs no longer need to inherit BaseRequest
to keep the auth fields alive through the msgpack→DTO→JSON pivot.

Retires the recurring footgun documented in
docs/superpowers/specs/2026-06-02-baseRequest-auth-footgun-improvement.md
(2026-05-25 basic-puzzle, 2026-05-28 deck-code, 2026-06-02 Phase 3 Bot,
2026-06-10 profile/index + item_acquire_history/info + user_mypage/update).

Pinned by AuthDecouplingTests — posts an encrypted msgpack body to
/profile/index (DTO does not inherit BaseRequest) through the real
translation middleware + auth handler and asserts 200. Adds an
EncryptedMsgpackHelper + useRealAuthHandler factory flag, reusable for
future wire-shape tests.

ProfileIndexRequest, ItemAcquireHistoryInfoRequest, and
UserMyPageUpdateRequest revert to the naked shape — the per-DTO
workarounds become vestigial under the new architecture.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 12:29:10 -04:00
gamer147
b18b24b085 test(ranking): literal-prod-JSON wire-shape parity
Per feedback_wire_shape_tests: controller round-trip tests using the
same DTO can't catch wire-key/wire-type drift. Asserts parsing of and
snake_case emission for the period list + monthly ranking shapes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 10:48:51 -04:00
gamer147
f743b27696 feat(ranking): stub /ranking/* (6 endpoints)
Rankings menu opens. Period picker shows deterministic monthly schedule.
Every leaderboard returns { period, ranking: [] }.

Endpoints:
- /ranking/get_viewable_ranking_period_list
- /ranking/master_point_{rotation,unlimited}_info
- /ranking/rank_match_class_win_{rotation,unlimited}_info
- /ranking/two_pick_win_info

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 10:46:00 -04:00
gamer147
80f249f8a2 feat(ranking): add RankingPeriodSchedule helper
Pure deterministic monthly period generator for the four ranking
families. Anchor dates derived from prod capture (2026-06-09): id=1 is
each family's launch month in JST; id=N is anchor + N-1 months. Used
by /ranking/get_viewable_ranking_period_list to render the period
picker and by per-family leaderboard endpoints to echo the requested
period back.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 10:36:51 -04:00
gamer147
b4aa07577f fix(friend): add BaseRequest body param to 6 body-less actions
ShadowverseTranslationMiddleware throws InvalidOperationException when
a Unity client posts an encrypted msgpack body to an action with zero
[FromBody] parameters — it has no target type for the deserializer.
Tests pass because they post JSON directly with no UnityPlayer UA and
the middleware short-circuits. Same defect already fixed on /replay/info
in 216dcab; this catches up the friend system shipped 2026-06-09.

Fixed actions: info, receive_apply_info, send_apply_info,
played_together_info, reject_apply_all, cancel_apply_all.

Tests updated to post the BaseRequest auth fields so [ApiController]
model validation passes (BaseRequest.ViewerId is non-nullable string).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 08:54:50 -04:00
gamer147
b54a47f333 test(replay): literal-prod-JSON wire-shape parity for /replay/info
Per feedback_wire_shape_tests (2026-05-28): controller round-trip tests
using the same DTO can't catch wire-key/wire-type drift. Asserts
parsing of, snake_case emission for, and MessagePack round-trip
through the captured prod frame.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 08:24:24 -04:00