Merge branch 'master' into codex/puzzle-clear-template-runtime-fixes
# Conflicts: # .hermes/shared-memory/decision-log.md # .hermes/shared-memory/project-overview.md # docs/【开发运维】本地开发验证与生产运维-2026-05-15.md # scripts/dev.test.ts # server-rs/crates/api-server/src/creation_entry_config.rs # server-rs/crates/api-server/src/wooden_fish.rs # server-rs/crates/module-auth/src/lib.rs # server-rs/crates/spacetime-client/src/wooden_fish.rs # server-rs/crates/spacetime-module/src/auth/procedures.rs # src/components/custom-world-home/creationWorkShelf.ts # src/components/platform-entry/PlatformEntryFlowShellImpl.tsx # src/components/rpg-entry/rpgEntryWorldPresentation.ts # src/services/miniGameDraftGenerationProgress.test.ts # src/services/miniGameDraftGenerationProgress.ts
This commit is contained in:
@@ -226,7 +226,10 @@ function resolvePuzzleBackendProgressPercent(
|
||||
state: MiniGameDraftGenerationState,
|
||||
) {
|
||||
const progressPercent = state.metadata?.puzzleProgressPercent;
|
||||
if (typeof progressPercent !== 'number' || !Number.isFinite(progressPercent)) {
|
||||
if (
|
||||
typeof progressPercent !== 'number' ||
|
||||
!Number.isFinite(progressPercent)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -512,6 +515,24 @@ function clampProgress(value: number) {
|
||||
return Math.max(0, Math.min(100, Math.round(value)));
|
||||
}
|
||||
|
||||
export function resolveMiniGameDraftGenerationStartedAtMs(
|
||||
startedAt: string | number | null | undefined,
|
||||
fallbackMs = Date.now(),
|
||||
) {
|
||||
if (typeof startedAt === 'number' && Number.isFinite(startedAt)) {
|
||||
return startedAt;
|
||||
}
|
||||
|
||||
if (typeof startedAt === 'string') {
|
||||
const parsed = Date.parse(startedAt);
|
||||
if (Number.isFinite(parsed)) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
|
||||
return fallbackMs;
|
||||
}
|
||||
|
||||
function getStepDefinitions(kind: MiniGameDraftGenerationKind) {
|
||||
if (kind === 'puzzle') {
|
||||
return buildPuzzleSteps(createMiniGameDraftGenerationState('puzzle'));
|
||||
@@ -590,6 +611,7 @@ function buildMiniGameProgressSteps(
|
||||
|
||||
export function createMiniGameDraftGenerationState(
|
||||
kind: MiniGameDraftGenerationKind,
|
||||
startedAtMs = Date.now(),
|
||||
): MiniGameDraftGenerationState {
|
||||
return {
|
||||
kind,
|
||||
@@ -600,16 +622,16 @@ export function createMiniGameDraftGenerationState(
|
||||
? 'square-hole-draft'
|
||||
: kind === 'match3d'
|
||||
? 'match3d-work-title'
|
||||
: kind === 'baby-object-match'
|
||||
? 'baby-object-draft'
|
||||
: kind === 'jump-hop'
|
||||
? 'jump-hop-draft'
|
||||
: kind === 'puzzle-clear'
|
||||
? 'puzzle-clear-draft'
|
||||
: kind === 'wooden-fish'
|
||||
? 'wooden-fish-draft'
|
||||
: 'compile',
|
||||
startedAtMs: Date.now(),
|
||||
: kind === 'baby-object-match'
|
||||
? 'baby-object-draft'
|
||||
: kind === 'jump-hop'
|
||||
? 'jump-hop-draft'
|
||||
: kind === 'puzzle-clear'
|
||||
? 'puzzle-clear-draft'
|
||||
: kind === 'wooden-fish'
|
||||
? 'wooden-fish-draft'
|
||||
: 'compile',
|
||||
startedAtMs,
|
||||
completedAssetCount: 0,
|
||||
totalAssetCount: 0,
|
||||
error: null,
|
||||
@@ -833,10 +855,7 @@ function resolvePuzzleActiveStepElapsedProgressRatio(
|
||||
// 中文注释:未收到后端真实里程碑时,跨步骤必须卡住;
|
||||
// 但当前步骤内的假进度要按整段等待时间继续向前走,避免短步骤几秒后停死。
|
||||
const fallbackDurationMs = Math.max(1, resolvePuzzleEstimatedWaitMs(state));
|
||||
return Math.max(
|
||||
0,
|
||||
Math.min(0.98, elapsedMs / fallbackDurationMs),
|
||||
);
|
||||
return Math.max(0, Math.min(0.98, elapsedMs / fallbackDurationMs));
|
||||
}
|
||||
|
||||
function resolveElapsedActiveStepProgressRatio(
|
||||
@@ -866,6 +885,7 @@ function resolveElapsedActiveStepProgressRatio(
|
||||
);
|
||||
}
|
||||
|
||||
/** 计算拼图生成总进度,后端里程碑决定跨步骤,当前步骤内使用平滑假进度。 */
|
||||
function resolvePuzzleOverallProgress(
|
||||
state: MiniGameDraftGenerationState,
|
||||
activeStepProgressRatio: number,
|
||||
@@ -918,7 +938,8 @@ export function buildMiniGameDraftGenerationProgress(
|
||||
}
|
||||
|
||||
const effectiveNowMs =
|
||||
typeof state.finishedAtMs === 'number' && Number.isFinite(state.finishedAtMs)
|
||||
typeof state.finishedAtMs === 'number' &&
|
||||
Number.isFinite(state.finishedAtMs)
|
||||
? state.finishedAtMs
|
||||
: nowMs;
|
||||
const elapsedMs = Math.max(0, effectiveNowMs - state.startedAtMs);
|
||||
@@ -945,34 +966,34 @@ export function buildMiniGameDraftGenerationProgress(
|
||||
...state,
|
||||
phase: woodenFishTimeline.phase,
|
||||
}
|
||||
: state.kind === 'big-fish' &&
|
||||
state.phase !== 'failed' &&
|
||||
state.phase !== 'ready'
|
||||
? {
|
||||
...state,
|
||||
phase: resolveBigFishPhaseByElapsedMs(elapsedMs),
|
||||
}
|
||||
: state.kind === 'square-hole' &&
|
||||
: state.kind === 'big-fish' &&
|
||||
state.phase !== 'failed' &&
|
||||
state.phase !== 'ready'
|
||||
? {
|
||||
...state,
|
||||
phase: resolveSquareHolePhaseByElapsedMs(elapsedMs),
|
||||
phase: resolveBigFishPhaseByElapsedMs(elapsedMs),
|
||||
}
|
||||
: state.kind === 'match3d' &&
|
||||
: state.kind === 'square-hole' &&
|
||||
state.phase !== 'failed' &&
|
||||
state.phase !== 'ready'
|
||||
? {
|
||||
...state,
|
||||
phase: resolveMatch3DPhaseByElapsedMs(elapsedMs, state.phase),
|
||||
phase: resolveSquareHolePhaseByElapsedMs(elapsedMs),
|
||||
}
|
||||
: state.kind === 'baby-object-match' &&
|
||||
: state.kind === 'match3d' &&
|
||||
state.phase !== 'failed' &&
|
||||
state.phase !== 'ready'
|
||||
? {
|
||||
...state,
|
||||
phase: resolveBabyObjectMatchPhaseByElapsedMs(elapsedMs),
|
||||
phase: resolveMatch3DPhaseByElapsedMs(elapsedMs, state.phase),
|
||||
}
|
||||
: state.kind === 'baby-object-match' &&
|
||||
state.phase !== 'failed' &&
|
||||
state.phase !== 'ready'
|
||||
? {
|
||||
...state,
|
||||
phase: resolveBabyObjectMatchPhaseByElapsedMs(elapsedMs),
|
||||
}
|
||||
: state.kind === 'jump-hop' &&
|
||||
state.phase !== 'failed' &&
|
||||
state.phase !== 'ready'
|
||||
@@ -980,14 +1001,14 @@ export function buildMiniGameDraftGenerationProgress(
|
||||
...state,
|
||||
phase: resolveJumpHopPhaseByElapsedMs(elapsedMs),
|
||||
}
|
||||
: state.kind === 'puzzle-clear' &&
|
||||
state.phase !== 'failed' &&
|
||||
state.phase !== 'ready'
|
||||
? {
|
||||
...state,
|
||||
phase: resolvePuzzleClearPhaseByElapsedMs(elapsedMs),
|
||||
}
|
||||
: state;
|
||||
: state.kind === 'puzzle-clear' &&
|
||||
state.phase !== 'failed' &&
|
||||
state.phase !== 'ready'
|
||||
? {
|
||||
...state,
|
||||
phase: resolvePuzzleClearPhaseByElapsedMs(elapsedMs),
|
||||
}
|
||||
: state;
|
||||
|
||||
const puzzleTimedSteps =
|
||||
normalizedState.kind === 'puzzle'
|
||||
@@ -1002,22 +1023,23 @@ export function buildMiniGameDraftGenerationProgress(
|
||||
const activeStepIndex = getActiveStepIndex(steps, normalizedState.phase);
|
||||
const activeStep = steps[activeStepIndex] ?? steps[0];
|
||||
const activeStepProgressRatio =
|
||||
normalizedState.kind === 'puzzle'
|
||||
normalizedState.phase === 'failed'
|
||||
? 0
|
||||
: normalizedState.kind === 'puzzle'
|
||||
? normalizedState.phase === 'ready'
|
||||
? 1
|
||||
: normalizedState.phase === 'failed'
|
||||
? 0
|
||||
: resolvePuzzleActiveStepElapsedProgressRatio(
|
||||
normalizedState,
|
||||
puzzleTimedSteps ?? buildPuzzleTimedSteps(normalizedState),
|
||||
activeStepIndex,
|
||||
elapsedMs,
|
||||
effectiveNowMs,
|
||||
)
|
||||
: resolvePuzzleActiveStepElapsedProgressRatio(
|
||||
normalizedState,
|
||||
puzzleTimedSteps ?? buildPuzzleTimedSteps(normalizedState),
|
||||
activeStepIndex,
|
||||
elapsedMs,
|
||||
effectiveNowMs,
|
||||
)
|
||||
: normalizedState.totalAssetCount > 0
|
||||
? Math.min(
|
||||
1,
|
||||
normalizedState.completedAssetCount / normalizedState.totalAssetCount,
|
||||
normalizedState.completedAssetCount /
|
||||
normalizedState.totalAssetCount,
|
||||
)
|
||||
: normalizedState.phase === 'ready'
|
||||
? 1
|
||||
@@ -1026,16 +1048,16 @@ export function buildMiniGameDraftGenerationProgress(
|
||||
normalizedState.kind,
|
||||
elapsedMs,
|
||||
)
|
||||
: normalizedState.kind === 'square-hole'
|
||||
: normalizedState.kind === 'square-hole'
|
||||
? resolveElapsedActiveStepProgressRatio(
|
||||
normalizedState.kind,
|
||||
elapsedMs,
|
||||
)
|
||||
: normalizedState.kind === 'match3d'
|
||||
? resolveElapsedActiveStepProgressRatio(
|
||||
normalizedState.kind,
|
||||
elapsedMs,
|
||||
)
|
||||
: normalizedState.kind === 'match3d'
|
||||
? resolveElapsedActiveStepProgressRatio(
|
||||
normalizedState.kind,
|
||||
elapsedMs,
|
||||
)
|
||||
: normalizedState.kind === 'baby-object-match'
|
||||
? resolveElapsedActiveStepProgressRatio(
|
||||
normalizedState.kind,
|
||||
@@ -1065,20 +1087,11 @@ export function buildMiniGameDraftGenerationProgress(
|
||||
? Math.max(1, completedWeight)
|
||||
: normalizedState.phase === 'ready'
|
||||
? 100
|
||||
: normalizedState.kind === 'puzzle'
|
||||
? resolvePuzzleOverallProgress(
|
||||
normalizedState,
|
||||
activeStepProgressRatio,
|
||||
)
|
||||
: completedWeight + activeStep.weight * activeStepProgressRatio;
|
||||
: completedWeight + activeStep.weight * activeStepProgressRatio;
|
||||
const cappedOverallProgress =
|
||||
normalizedState.phase === 'ready' || normalizedState.phase === 'failed'
|
||||
? overallProgress
|
||||
: normalizedState.kind === 'puzzle'
|
||||
? Math.min(PUZZLE_NON_READY_MAX_PROGRESS, overallProgress)
|
||||
: normalizedState.kind === 'wooden-fish'
|
||||
? Math.min(PUZZLE_NON_READY_MAX_PROGRESS, overallProgress)
|
||||
: overallProgress;
|
||||
: Math.min(PUZZLE_NON_READY_MAX_PROGRESS, overallProgress);
|
||||
|
||||
return {
|
||||
phaseId: normalizedState.phase,
|
||||
@@ -1099,11 +1112,11 @@ export function buildMiniGameDraftGenerationProgress(
|
||||
? '宝贝识物草稿已准备完成,可进入结果页继续发布。'
|
||||
: normalizedState.kind === 'jump-hop'
|
||||
? '跳一跳草稿已准备完成,可进入结果页试玩或发布。'
|
||||
: normalizedState.kind === 'puzzle-clear'
|
||||
? '拼消消草稿已准备完成,可进入结果页试玩或发布。'
|
||||
: normalizedState.kind === 'wooden-fish'
|
||||
? '敲木鱼草稿已准备完成,可进入结果页试玩或发布。'
|
||||
: '首关草稿与正式图已准备完成,可进入结果页补作品信息。'
|
||||
: normalizedState.kind === 'puzzle-clear'
|
||||
? '拼消消草稿已准备完成,可进入结果页试玩或发布。'
|
||||
: normalizedState.kind === 'wooden-fish'
|
||||
? '敲木鱼草稿已准备完成,可进入结果页试玩或发布。'
|
||||
: '首关草稿与正式图已准备完成,可进入结果页补作品信息。'
|
||||
: activeStep.detail),
|
||||
batchLabel: activeStep.label,
|
||||
overallProgress: clampProgress(cappedOverallProgress),
|
||||
@@ -1111,10 +1124,13 @@ export function buildMiniGameDraftGenerationProgress(
|
||||
totalWeight: 100,
|
||||
elapsedMs,
|
||||
estimatedRemainingMs:
|
||||
normalizedState.phase === 'ready'
|
||||
normalizedState.phase === 'ready' || normalizedState.phase === 'failed'
|
||||
? 0
|
||||
: normalizedState.kind === 'puzzle'
|
||||
? Math.max(0, resolvePuzzleEstimatedWaitMs(normalizedState) - elapsedMs)
|
||||
? Math.max(
|
||||
0,
|
||||
resolvePuzzleEstimatedWaitMs(normalizedState) - elapsedMs,
|
||||
)
|
||||
: normalizedState.kind === 'big-fish'
|
||||
? Math.max(0, 7_000 - elapsedMs)
|
||||
: normalizedState.kind === 'square-hole'
|
||||
@@ -1122,17 +1138,17 @@ export function buildMiniGameDraftGenerationProgress(
|
||||
: normalizedState.kind === 'match3d'
|
||||
? Math.max(0, MATCH3D_ESTIMATED_WAIT_MS - elapsedMs)
|
||||
: normalizedState.kind === 'baby-object-match'
|
||||
? Math.max(
|
||||
0,
|
||||
BABY_OBJECT_MATCH_ESTIMATED_WAIT_MS - elapsedMs,
|
||||
)
|
||||
? Math.max(0, BABY_OBJECT_MATCH_ESTIMATED_WAIT_MS - elapsedMs)
|
||||
: normalizedState.kind === 'jump-hop'
|
||||
? Math.max(0, JUMP_HOP_ESTIMATED_WAIT_MS - elapsedMs)
|
||||
: normalizedState.kind === 'puzzle-clear'
|
||||
? Math.max(0, PUZZLE_CLEAR_ESTIMATED_WAIT_MS - elapsedMs)
|
||||
: normalizedState.kind === 'wooden-fish'
|
||||
? Math.max(0, WOODEN_FISH_ESTIMATED_WAIT_MS - elapsedMs)
|
||||
: null,
|
||||
: normalizedState.kind === 'puzzle-clear'
|
||||
? Math.max(
|
||||
0,
|
||||
PUZZLE_CLEAR_ESTIMATED_WAIT_MS - elapsedMs,
|
||||
)
|
||||
: normalizedState.kind === 'wooden-fish'
|
||||
? Math.max(0, WOODEN_FISH_ESTIMATED_WAIT_MS - elapsedMs)
|
||||
: null,
|
||||
activeStepIndex,
|
||||
steps: buildMiniGameProgressSteps(
|
||||
steps,
|
||||
|
||||
Reference in New Issue
Block a user