feat: unify recommend anonymous runtime guest auth

- Route recommended runtime launches through shared runtime guest token handling
- Extend recommend-page anonymous play beyond jump-hop
- Add regression coverage for runtime guest launch clients
- Update docs to reflect the full anonymous-play matrix
This commit is contained in:
kdletters
2026-05-25 14:03:38 +08:00
parent 9a0bc6b129
commit c1dcf074bb
23 changed files with 820 additions and 236 deletions

View File

@@ -117,6 +117,7 @@ import {
BACKGROUND_AUTH_REQUEST_OPTIONS,
} from '../../services/apiClient';
import {
ensureRuntimeGuestToken,
getPublicAuthUserByCode,
getPublicAuthUserById,
} from '../../services/authService';
@@ -127,6 +128,7 @@ import {
publishBarkBattleWork,
updateBarkBattleDraftConfig,
} from '../../services/bark-battle-creation';
import { startBarkBattleRun } from '../../services/bark-battle-runtime';
import {
createBigFishCreationSession,
executeBigFishCreationAction,
@@ -550,8 +552,13 @@ const AGENT_RESULT_STRUCTURAL_BLOCKER_CODES = new Set([
]);
const RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS =
BACKGROUND_AUTH_REQUEST_OPTIONS;
const PUBLIC_PUZZLE_RUNTIME_AUTH_OPTIONS =
RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS;
async function buildRecommendRuntimeGuestOptions() {
const { token } = await ensureRuntimeGuestToken();
return {
...RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS,
runtimeGuestToken: token,
};
}
const PUZZLE_DRAFT_GENERATION_POINT_COST = 2;
const MATCH3D_DRAFT_GENERATION_POINT_COST = 10;
const BARK_BATTLE_DRAFT_GENERATION_POINT_COST = 3;
@@ -3253,6 +3260,11 @@ export function PlatformEntryFlowShellImpl({
resolveRpgCreationErrorMessage(error, fallback),
[],
);
const resolveBarkBattleErrorMessage = useCallback(
(error: unknown, fallback: string) =>
resolveRpgCreationErrorMessage(error, fallback),
[],
);
const refreshBigFishShelf = useCallback(async () => {
setIsBigFishLoadingLibrary(true);
@@ -7135,11 +7147,14 @@ export function PlatformEntryFlowShellImpl({
profileId: targetProfileId,
mode: 'play' as const,
};
const runtimeGuestOptions = options.embedded
? await buildRecommendRuntimeGuestOptions()
: {};
const { run } = options.embedded
? await startVisualNovelRun(
targetProfileId,
startRunPayload,
RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS,
runtimeGuestOptions,
)
: await startVisualNovelRun(targetProfileId, startRunPayload);
setVisualNovelWork(workDetail);
@@ -7186,9 +7201,14 @@ export function PlatformEntryFlowShellImpl({
setVisualNovelError(null);
setIsVisualNovelBusy(true);
try {
const runtimeGuestOptions =
activeRecommendRuntimeKind === 'visual-novel'
? await buildRecommendRuntimeGuestOptions()
: {};
const nextRun = await streamVisualNovelRuntimeAction(
visualNovelRun.runId,
payload,
runtimeGuestOptions,
);
setVisualNovelRun(nextRun);
} catch (error) {
@@ -7200,6 +7220,7 @@ export function PlatformEntryFlowShellImpl({
}
},
[
activeRecommendRuntimeKind,
isVisualNovelBusy,
resolvePuzzleErrorMessage,
setIsVisualNovelBusy,
@@ -7608,12 +7629,12 @@ export function PlatformEntryFlowShellImpl({
setJumpHopError(null);
setJumpHopRuntimeReturnStage(options.returnStage ?? 'work-detail');
try {
const runtimeGuestOptions = options.embedded
? await buildRecommendRuntimeGuestOptions()
: {};
const [detail, runResponse] = await Promise.all([
jumpHopClient.getWorkDetail(normalizedProfileId).catch(() => null),
jumpHopClient.startRun(
normalizedProfileId,
options.embedded ? RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS : {},
),
jumpHopClient.startRun(normalizedProfileId, runtimeGuestOptions),
]);
if (detail?.item) {
setJumpHopWork(detail.item);
@@ -7902,9 +7923,14 @@ export function PlatformEntryFlowShellImpl({
setWoodenFishError(null);
setWoodenFishRuntimeReturnStage(options.returnStage ?? 'work-detail');
try {
const runtimeGuestOptions = options.embedded
? await buildRecommendRuntimeGuestOptions()
: {};
const [detail, runResponse] = await Promise.all([
woodenFishClient.getWorkDetail(normalizedProfileId).catch(() => null),
woodenFishClient.startRun(normalizedProfileId),
options.embedded
? woodenFishClient.startRun(normalizedProfileId, runtimeGuestOptions)
: woodenFishClient.startRun(normalizedProfileId),
]);
if (detail?.item) {
setWoodenFishWork(detail.item);
@@ -8384,15 +8410,15 @@ export function PlatformEntryFlowShellImpl({
profileId: item.profileId,
levelId: levelId ?? null,
};
const runtimeGuestOptions = options.embedded
? await buildRecommendRuntimeGuestOptions()
: {};
const authMode = options.embedded
? 'isolated'
: (options.authMode ?? 'default');
const { run } =
authMode === 'isolated'
? await startPuzzleRun(
startRunPayload,
PUBLIC_PUZZLE_RUNTIME_AUTH_OPTIONS,
)
? await startPuzzleRun(startRunPayload, runtimeGuestOptions)
: await startPuzzleRun(startRunPayload);
setSelectedPuzzleDetail(item);
setPuzzleRun(run);
@@ -8488,10 +8514,11 @@ export function PlatformEntryFlowShellImpl({
runtimeProfile.generatedBackgroundAsset,
{ expireSeconds: 300 },
);
const runtimeGuestOptions = options.embedded
? await buildRecommendRuntimeGuestOptions()
: {};
const runtimeOptions = {
...(options.embedded
? RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS
: {}),
...runtimeGuestOptions,
...(typeof options.itemTypeCountOverride === 'number'
? { itemTypeCountOverride: options.itemTypeCountOverride }
: {}),
@@ -8559,11 +8586,11 @@ export function PlatformEntryFlowShellImpl({
setSquareHoleError(null);
try {
const runtimeGuestOptions = options.embedded
? await buildRecommendRuntimeGuestOptions()
: {};
const { run } = options.embedded
? await startSquareHoleRun(
profile.profileId,
RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS,
)
? await startSquareHoleRun(profile.profileId, runtimeGuestOptions)
: await startSquareHoleRun(profile.profileId);
setSquareHoleRun(run);
setSquareHoleRuntimeReturnStage(returnStage);
@@ -8715,9 +8742,14 @@ export function PlatformEntryFlowShellImpl({
bigFishInputInFlightRef.current = true;
try {
const runtimeGuestOptions =
activeRecommendRuntimeKind === 'big-fish'
? await buildRecommendRuntimeGuestOptions()
: {};
const { run } = await submitBigFishRuntimeInput(
bigFishRun.runId,
payload,
runtimeGuestOptions,
);
setBigFishRun(run);
} catch (error) {
@@ -8728,7 +8760,12 @@ export function PlatformEntryFlowShellImpl({
bigFishInputInFlightRef.current = false;
}
},
[bigFishRun, resolveBigFishErrorMessage, setBigFishError],
[
activeRecommendRuntimeKind,
bigFishRun,
resolveBigFishErrorMessage,
setBigFishError,
],
);
const reportBigFishObservedPlayTime = useCallback(() => {
@@ -8929,12 +8966,13 @@ export function PlatformEntryFlowShellImpl({
profileId: currentLevel.profileId,
levelId: resolvePuzzleRestartLevelId(currentRun, detailItem),
};
const runtimeGuestOptions =
puzzleRuntimeAuthMode === 'isolated'
? await buildRecommendRuntimeGuestOptions()
: {};
const { run } =
puzzleRuntimeAuthMode === 'isolated'
? await startPuzzleRun(
startRunPayload,
PUBLIC_PUZZLE_RUNTIME_AUTH_OPTIONS,
)
? await startPuzzleRun(startRunPayload, runtimeGuestOptions)
: await startPuzzleRun(startRunPayload);
setSelectedPuzzleDetail(detailItem);
puzzleRunRef.current = run;
@@ -9057,10 +9095,8 @@ export function PlatformEntryFlowShellImpl({
const submitLeaderboardPromise =
puzzleRuntimeAuthMode === 'isolated'
? submitPuzzleLeaderboard(
puzzleRun.runId,
payload,
PUBLIC_PUZZLE_RUNTIME_AUTH_OPTIONS,
? buildRecommendRuntimeGuestOptions().then((runtimeGuestOptions) =>
submitPuzzleLeaderboard(puzzleRun.runId, payload, runtimeGuestOptions),
)
: submitPuzzleLeaderboard(puzzleRun.runId, payload);
@@ -9117,6 +9153,10 @@ export function PlatformEntryFlowShellImpl({
return;
}
const runtimeGuestOptions =
puzzleRuntimeAuthMode === 'isolated'
? await buildRecommendRuntimeGuestOptions()
: {};
const targetProfileId = _target?.profileId?.trim() ?? '';
if (puzzleRun.nextLevelMode === 'similarWorks' && targetProfileId) {
const itemPromise =
@@ -9132,7 +9172,7 @@ export function PlatformEntryFlowShellImpl({
{
targetProfileId,
},
PUBLIC_PUZZLE_RUNTIME_AUTH_OPTIONS,
runtimeGuestOptions,
)
: advancePuzzleNextLevel(puzzleRun.runId, {
targetProfileId,
@@ -9157,7 +9197,7 @@ export function PlatformEntryFlowShellImpl({
? await advancePuzzleNextLevel(
puzzleRun.runId,
{},
PUBLIC_PUZZLE_RUNTIME_AUTH_OPTIONS,
runtimeGuestOptions,
)
: await advancePuzzleNextLevel(puzzleRun.runId);
setPuzzleRun(run);
@@ -10993,11 +11033,11 @@ export function PlatformEntryFlowShellImpl({
setBigFishRuntimeReturnStage(returnStage);
setBigFishRun(null);
try {
const runtimeGuestOptions = options.embedded
? await buildRecommendRuntimeGuestOptions()
: {};
const { run } = options.embedded
? await startBigFishRuntimeRun(
sessionId,
RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS,
)
? await startBigFishRuntimeRun(sessionId, runtimeGuestOptions)
: await startBigFishRuntimeRun(sessionId);
setBigFishRuntimeStartedAt(Date.now());
setBigFishRun(run);
@@ -11008,11 +11048,7 @@ export function PlatformEntryFlowShellImpl({
);
}
const recordPlayPromise = options.embedded
? recordBigFishPlay(
sessionId,
{ elapsedMs: 0 },
RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS,
)
? recordBigFishPlay(sessionId, { elapsedMs: 0 }, runtimeGuestOptions)
: recordBigFishPlay(sessionId, { elapsedMs: 0 });
void recordPlayPromise.catch((error) => {
setBigFishError(
@@ -11031,9 +11067,10 @@ export function PlatformEntryFlowShellImpl({
);
const startBarkBattleRunFromWork = useCallback(
(
async (
item: BarkBattleWorkSummary,
returnStage: BarkBattleRuntimeReturnStage = 'work-detail',
options: { embedded?: boolean } = {},
) => {
if (item.status !== 'published') {
setBarkBattleError('汪汪声浪作品发布后才能进入正式玩法。');
@@ -11045,17 +11082,33 @@ export function PlatformEntryFlowShellImpl({
setBarkBattleRuntimeMode('published');
setBarkBattlePublishedConfig(mapBarkBattleWorkToPublishedConfig(item));
setBarkBattleRuntimeReturnStage(returnStage);
selectionStageRef.current = 'bark-battle-runtime';
setSelectionStage('bark-battle-runtime');
pushAppHistoryPath(
buildPublicWorkStagePath(
'bark-battle-runtime',
buildBarkBattlePublicWorkCode(item.workId),
),
);
return true;
try {
const runtimeGuestOptions = options.embedded
? await buildRecommendRuntimeGuestOptions()
: {};
const runResponse = options.embedded
? await startBarkBattleRun(item.workId, {}, runtimeGuestOptions)
: await startBarkBattleRun(item.workId);
void runResponse;
selectionStageRef.current = 'bark-battle-runtime';
if (!options.embedded) {
setSelectionStage('bark-battle-runtime');
pushAppHistoryPath(
buildPublicWorkStagePath(
'bark-battle-runtime',
buildBarkBattlePublicWorkCode(item.workId),
),
);
}
return true;
} catch (error) {
setBarkBattleError(
resolveBarkBattleErrorMessage(error, '启动汪汪声浪玩法失败。'),
);
return false;
}
},
[setSelectionStage],
[resolveBarkBattleErrorMessage, setSelectionStage],
);
const startSelectedPublicWork = useCallback(() => {
@@ -11327,7 +11380,9 @@ export function PlatformEntryFlowShellImpl({
'当前汪汪声浪作品信息不完整,暂时无法进入玩法。',
);
} else {
started = startBarkBattleRunFromWork(work, 'platform');
started = await startBarkBattleRunFromWork(work, 'platform', {
embedded: true,
});
}
} else if (isEdutainmentGalleryEntry(entry)) {
started = await startBabyObjectMatchRuntimeFromEntry(