Files
Genarrative/src/components/rpg-entry/useRpgCreationEnterWorld.ts
2026-05-22 03:14:11 +08:00

154 lines
5.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useCallback } from 'react';
import type { RpgCreationResultView } from '../../../packages/shared/src/contracts/rpgCreationResultView';
import type { CustomWorldAgentSessionSnapshot } from '../../../packages/shared/src/contracts/customWorldAgent';
import type { CustomWorldRuntimeLaunchOptions } from '../platform-entry/platformEntryTypes';
import { rpgCreationPreviewAdapter } from '../../services/rpg-creation/rpgCreationPreviewAdapter';
import type { CustomWorldProfile } from '../../types';
import { chooseMoreCompleteCustomWorldProfile } from './rpgProfileCompleteness';
type UseRpgCreationEnterWorldParams = {
isAgentDraftResultView: boolean;
activeAgentSessionId: string | null;
currentAgentSessionStage?: CustomWorldAgentSessionSnapshot['stage'] | null;
generatedCustomWorldProfile: CustomWorldProfile | null;
handleCustomWorldSelect: (
customWorldProfile: CustomWorldProfile,
options?: CustomWorldRuntimeLaunchOptions,
) => void;
syncAgentDraftResultProfile: (
profile: CustomWorldProfile,
) => Promise<{ profile: CustomWorldProfile | null; view?: RpgCreationResultView | null }>;
executePublishWorld: () => Promise<unknown>;
syncAgentCreationResultView: (
sessionId: string,
) => Promise<RpgCreationResultView | null>;
setGeneratedCustomWorldProfile: (profile: CustomWorldProfile | null) => void;
};
/**
* 统一“进入世界”前的最终同步策略。
* Agent 草稿结果进入游戏时只读当前结果页 profile不再静默回退到会话侧旧快照。
*/
export function useRpgCreationEnterWorld(
params: UseRpgCreationEnterWorldParams,
) {
const {
isAgentDraftResultView,
activeAgentSessionId,
currentAgentSessionStage,
generatedCustomWorldProfile,
handleCustomWorldSelect,
syncAgentDraftResultProfile,
executePublishWorld,
syncAgentCreationResultView,
setGeneratedCustomWorldProfile,
} = params;
const enterWorldForTestFromCurrentResult = useCallback(async () => {
if (!generatedCustomWorldProfile) {
return;
}
// 中文注释:作品测试必须复用“结果页当前真相源”。
// 用户在结果页看到并可能继续编辑的是 generatedCustomWorldProfile
// 如果这里又回退成 session 里的旧 preview就会出现
// “结果页看起来已经是新版,但作品测试实际进入的是旧版快照”的错位。
if (isAgentDraftResultView && activeAgentSessionId) {
setGeneratedCustomWorldProfile(generatedCustomWorldProfile);
}
handleCustomWorldSelect(generatedCustomWorldProfile, {
// 中文注释:作品测试现在复用正式 play 运行链,只保留
// “返回结果页 + 禁止写正式持久化”的入口语义。
mode: 'play',
disablePersistence: true,
returnStage: 'custom-world-result',
});
}, [
activeAgentSessionId,
generatedCustomWorldProfile,
handleCustomWorldSelect,
isAgentDraftResultView,
setGeneratedCustomWorldProfile,
]);
const publishCurrentResult = useCallback(async () => {
if (!generatedCustomWorldProfile) {
return null;
}
if (!isAgentDraftResultView || !activeAgentSessionId) {
return generatedCustomWorldProfile;
}
if (currentAgentSessionStage === 'published') {
const latestView = await syncAgentCreationResultView(activeAgentSessionId);
const publishedProfile = chooseMoreCompleteCustomWorldProfile(
generatedCustomWorldProfile,
rpgCreationPreviewAdapter.buildPreviewFromResultView(latestView),
);
// 中文注释:已发布会话的“进入世界”只读取后端结果页真相,
// 不能再同步草稿或重复发送 publish_world否则会被发布阶段门槛拒绝。
setGeneratedCustomWorldProfile(publishedProfile);
return publishedProfile;
}
const syncedResult = await syncAgentDraftResultProfile(
generatedCustomWorldProfile,
);
const latestProfile =
syncedResult.profile ??
rpgCreationPreviewAdapter.buildPreviewFromResultView(syncedResult.view);
if (!latestProfile) {
return null;
}
setGeneratedCustomWorldProfile(latestProfile);
const canEnterPublishedWorld =
syncedResult.view?.session.stage === 'published' &&
syncedResult.view.canEnterWorld;
if (canEnterPublishedWorld) {
const latestView = await syncAgentCreationResultView(activeAgentSessionId);
return chooseMoreCompleteCustomWorldProfile(
latestProfile,
rpgCreationPreviewAdapter.buildPreviewFromResultView(latestView),
);
}
await executePublishWorld();
const latestView = await syncAgentCreationResultView(activeAgentSessionId);
const publishedProfile = chooseMoreCompleteCustomWorldProfile(
latestProfile,
rpgCreationPreviewAdapter.buildPreviewFromResultView(latestView),
);
setGeneratedCustomWorldProfile(publishedProfile);
return publishedProfile;
}, [
activeAgentSessionId,
currentAgentSessionStage,
executePublishWorld,
generatedCustomWorldProfile,
isAgentDraftResultView,
setGeneratedCustomWorldProfile,
syncAgentDraftResultProfile,
syncAgentCreationResultView,
]);
const enterWorldFromCurrentResult = useCallback(async () => {
const publishedProfile = await publishCurrentResult();
if (publishedProfile) {
handleCustomWorldSelect(publishedProfile);
}
}, [handleCustomWorldSelect, publishCurrentResult]);
return {
enterWorldFromCurrentResult,
enterWorldForTestFromCurrentResult,
publishCurrentResult,
};
}