fix(check): emit rewrite_viewer_id when UDID and Steam viewers disagree
Wipe-and-resignup left the client stuck with the blank V_new's id in Certification.ViewerId. /tool/signup is anonymous, so it can't see the Steam ticket and creates a fresh anonymous viewer keyed on the new UDID; the Steam handler on the next request resolves to V_old and serves its data, but no normal-response hook overwrites Certification.ViewerId. GameStart now compares the UDID-keyed viewer to the auth-resolved one and emits rewrite_viewer_id when they differ, which Cute/GameStartCheckTask writes back into Certification.ViewerId. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -38,12 +38,32 @@ public class CheckController : SVSimController
|
||||
?? throw new InvalidOperationException("Auth handler must set viewer in context.");
|
||||
Viewer fullViewer = await _viewerRepository.GetViewerWithSocials(viewer.Id) ?? viewer;
|
||||
|
||||
// Wipe-and-resignup reconciliation: /tool/signup is anonymous on the wire and can't see
|
||||
// the Steam ticket, so a freshly-wiped client lands a blank V_new keyed on its new UDID
|
||||
// while the Steam handler on this very request resolves to the original V_old. The client
|
||||
// has already written V_new.Id into Certification.ViewerId from the signup response; left
|
||||
// alone, it stays wrong forever (NormalTask.Parse never reads data_headers.viewer_id —
|
||||
// only SignUpTask / GameStartCheckTask.rewrite_viewer_id / the social-chain tasks do).
|
||||
// Detect the mismatch by re-looking-up the UDID-keyed viewer and emit rewrite_viewer_id
|
||||
// when it disagrees with the auth-resolved one.
|
||||
long? rewriteViewerId = null;
|
||||
Guid? udid = HttpContext.GetUdid();
|
||||
if (udid is Guid u && u != Guid.Empty)
|
||||
{
|
||||
Viewer? udidViewer = await _viewerRepository.GetViewerByUdid(u);
|
||||
if (udidViewer is not null && udidViewer.Id != fullViewer.Id)
|
||||
{
|
||||
rewriteViewerId = fullViewer.Id;
|
||||
}
|
||||
}
|
||||
|
||||
return new GameStartResponse
|
||||
{
|
||||
NowViewerId = fullViewer.Id,
|
||||
NowName = fullViewer.DisplayName,
|
||||
NowTutorialStep = fullViewer.MissionData.TutorialState.ToString(),
|
||||
IsSetTransitionPassword = true,
|
||||
RewriteViewerId = rewriteViewerId,
|
||||
// Stub rank map until per-format ranks are persisted (prod observed: "1"/"2"/"4"
|
||||
// keys mapping to RankName_010 / RankName_017). Empty dict here may be safe but
|
||||
// we don't yet know which client paths read this — match prod stub.
|
||||
|
||||
Reference in New Issue
Block a user