fix: remove fixed match3d demo

This commit is contained in:
kdletters
2026-05-27 00:57:28 +08:00
parent 6b9c0fb3db
commit 2411eb6513
112 changed files with 15 additions and 293 deletions

View File

@@ -108,12 +108,6 @@ import type {
VisualNovelWorkSummary,
} from '../../../packages/shared/src/contracts/visualNovel';
import { buildCustomWorldPlayableCharacters } from '../../data/characterPresets';
import {
MATCH3D_DEMO_GALLERY_CARD,
MATCH3D_DEMO_PROFILE_ID,
MATCH3D_DEMO_WORK_PROFILE,
isMatch3DDemoProfileId,
} from '../../data/match3dDemoGalleryCard';
import {
buildPublicWorkStagePath,
pushAppHistoryPath,
@@ -198,10 +192,7 @@ import {
JumpHopWorkspaceCreateRequest,
} from '../../services/jump-hop/jumpHopClient';
import { match3dCreationClient } from '../../services/match3d-creation';
import {
createLocalMatch3DRuntimeAdapter,
createServerMatch3DRuntimeAdapter,
} from '../../services/match3d-runtime';
import { createServerMatch3DRuntimeAdapter } from '../../services/match3d-runtime';
import {
deleteMatch3DWork,
getMatch3DWorkDetail,
@@ -4332,9 +4323,6 @@ export function PlatformEntryFlowShellImpl({
}
return '服务端预览';
}, [agentResultPreview]);
const match3dDemoProfile = MATCH3D_DEMO_WORK_PROFILE;
const match3dDemoGalleryCard = MATCH3D_DEMO_GALLERY_CARD;
const featuredGalleryEntries = useMemo(() => {
const bigFishPublicEntries = isBigFishCreationVisible
? bigFishGalleryEntries.map(mapBigFishWorkToPlatformGalleryCard)
@@ -4373,7 +4361,6 @@ export function PlatformEntryFlowShellImpl({
[
...bigFishPublicEntries,
...match3dPublicEntries,
match3dDemoGalleryCard,
...puzzlePublicEntries,
...barkBattlePublicEntries,
...squareHolePublicEntries,
@@ -4398,7 +4385,6 @@ export function PlatformEntryFlowShellImpl({
squareHoleGalleryEntries,
visualNovelGalleryEntries,
woodenFishGalleryEntries,
match3dDemoGalleryCard,
]);
const latestGalleryEntries = useMemo(
() =>
@@ -4409,7 +4395,6 @@ export function PlatformEntryFlowShellImpl({
? bigFishGalleryEntries.map(mapBigFishWorkToPlatformGalleryCard)
: []),
...match3dGalleryEntries.map(mapMatch3DWorkToPublicWorkDetail),
match3dDemoGalleryCard,
...puzzleGalleryEntries.map(mapPuzzleWorkToPlatformGalleryCard),
...barkBattleGalleryEntries.map(mapBarkBattleWorkToPlatformGalleryCard),
...jumpHopGalleryEntries.map(mapJumpHopWorkToPlatformGalleryCard),
@@ -4451,7 +4436,6 @@ export function PlatformEntryFlowShellImpl({
barkBattleGalleryEntries,
barkBattleWorks,
woodenFishGalleryEntries,
match3dDemoGalleryCard,
],
);
const recommendRuntimeEntries = useMemo(() => {
@@ -4459,9 +4443,7 @@ export function PlatformEntryFlowShellImpl({
filterGeneralPublicWorks([
...featuredGalleryEntries,
...latestGalleryEntries,
])
.filter((entry) => !isMatch3DDemoProfileId(entry.profileId))
.forEach((entry) => {
]).forEach((entry) => {
entryMap.set(getPlatformPublicGalleryEntryKey(entry), entry);
});
return Array.from(entryMap.values());
@@ -5005,20 +4987,9 @@ export function PlatformEntryFlowShellImpl({
() => createServerMatch3DRuntimeAdapter(),
[],
);
const match3dDemoRuntimeAdapter = useMemo(
() =>
createLocalMatch3DRuntimeAdapter({
clearCount: 21,
profileId: MATCH3D_DEMO_PROFILE_ID,
}),
[],
);
const resolveMatch3DRuntimeAdapter = useCallback(
(profileId: string | null | undefined) =>
isMatch3DDemoProfileId(profileId)
? match3dDemoRuntimeAdapter
: match3dRuntimeAdapter,
[match3dDemoRuntimeAdapter, match3dRuntimeAdapter],
(_profileId: string | null | undefined) => match3dRuntimeAdapter,
[match3dRuntimeAdapter],
);
const match3dFlow = usePlatformCreationAgentFlowController<
Match3DAgentSessionSnapshot,
@@ -9690,11 +9661,9 @@ export function PlatformEntryFlowShellImpl({
setMatch3DError(null);
try {
const isDemoProfile = isMatch3DDemoProfileId(profile.profileId);
let runtimeProfile: Match3DWorkProfile | Match3DWorkSummary =
isDemoProfile ? match3dDemoProfile : profile;
profile;
if (
!isDemoProfile &&
(!hasMatch3DRuntimeAsset(profile.generatedItemAssets) ||
!hasMatch3DRuntimeBackgroundAsset(profile))
) {
@@ -9775,7 +9744,6 @@ export function PlatformEntryFlowShellImpl({
},
[
isMatch3DBusy,
match3dDemoProfile,
authUi,
match3dFlow,
resolveMatch3DErrorMessage,
@@ -11556,11 +11524,8 @@ export function PlatformEntryFlowShellImpl({
try {
const entries =
match3dGalleryEntries.length > 0
? [...match3dGalleryEntries, match3dDemoProfile]
: await refreshMatch3DGallery().then((items) => [
...items,
match3dDemoProfile,
]);
? match3dGalleryEntries
: await refreshMatch3DGallery();
const matchedEntry = entries.find(
(entry) => entry.profileId === profileId,
);
@@ -11580,7 +11545,6 @@ export function PlatformEntryFlowShellImpl({
},
[
match3dGalleryEntries,
match3dDemoProfile,
openPublicWorkDetail,
refreshMatch3DGallery,
resolveMatch3DErrorMessage,
@@ -14138,11 +14102,8 @@ export function PlatformEntryFlowShellImpl({
const tryOpenMatch3DGalleryEntry = async () => {
const entries =
match3dGalleryEntries.length > 0
? [...match3dGalleryEntries, match3dDemoProfile]
: await refreshMatch3DGallery().then((items) => [
...items,
match3dDemoProfile,
]);
? match3dGalleryEntries
: await refreshMatch3DGallery();
const matchedEntry = entries.find((entry) => {
const detailEntry = mapMatch3DWorkToPublicWorkDetail(entry);
return (
@@ -14559,7 +14520,6 @@ export function PlatformEntryFlowShellImpl({
refreshBigFishGallery,
resolveBigFishErrorMessage,
setBigFishError,
match3dDemoProfile,
],
);

View File

@@ -84,7 +84,6 @@ import {
} from '../../services/edutainment-baby-object';
import { match3dCreationClient } from '../../services/match3d-creation';
import {
createLocalMatch3DRuntimeAdapter,
createServerMatch3DRuntimeAdapter,
} from '../../services/match3d-runtime';
import {
@@ -674,7 +673,6 @@ vi.mock('../../services/match3dGeneratedModelCache', () => ({
}));
const match3dRuntimeServiceMocks = vi.hoisted(() => ({
createLocalMatch3DRuntimeAdapter: vi.fn(),
createServerMatch3DRuntimeAdapter: vi.fn(),
}));
@@ -687,15 +685,6 @@ const match3dServerRuntimeAdapterMock = vi.hoisted(() => ({
stopRun: vi.fn(),
}));
const match3dLocalRuntimeAdapterMock = vi.hoisted(() => ({
clickItem: vi.fn(),
finishTimeUp: vi.fn(),
getRun: vi.fn(),
restartRun: vi.fn(),
startRun: vi.fn(),
stopRun: vi.fn(),
}));
vi.mock('../../services/match3d-runtime', async () => {
const actual = await vi.importActual<
typeof import('../../services/match3d-runtime')
@@ -2405,9 +2394,6 @@ beforeEach(() => {
vi.mocked(createServerMatch3DRuntimeAdapter).mockReturnValue(
match3dServerRuntimeAdapterMock,
);
vi.mocked(createLocalMatch3DRuntimeAdapter).mockReturnValue(
match3dLocalRuntimeAdapterMock,
);
match3dServerRuntimeAdapterMock.startRun.mockRejectedValue(
new Error('未启动抓大鹅运行态'),
);
@@ -2423,21 +2409,6 @@ beforeEach(() => {
match3dServerRuntimeAdapterMock.stopRun.mockResolvedValue({
run: buildMockMatch3DRun('match3d-profile-stopped'),
});
match3dLocalRuntimeAdapterMock.startRun.mockResolvedValue({
run: buildMockMatch3DRun('match3d-demo-20260525'),
});
match3dLocalRuntimeAdapterMock.clickItem.mockRejectedValue(
new Error('未执行本地抓大鹅点击'),
);
match3dLocalRuntimeAdapterMock.restartRun.mockResolvedValue({
run: buildMockMatch3DRun('match3d-demo-20260525'),
});
match3dLocalRuntimeAdapterMock.finishTimeUp.mockResolvedValue({
run: buildMockMatch3DRun('match3d-demo-20260525'),
});
match3dLocalRuntimeAdapterMock.stopRun.mockResolvedValue({
run: buildMockMatch3DRun('match3d-demo-20260525'),
});
window.history.replaceState(null, '', '/');
window.sessionStorage.clear();
window.localStorage.clear();
@@ -8390,38 +8361,6 @@ test('public code search opens a published Match3D work by M3 code and starts ru
expect(getRpgEntryWorldGalleryDetailByCode).not.toHaveBeenCalled();
});
test('public code search opens the local Match3D demo and starts local runtime', async () => {
const user = userEvent.setup();
vi.mocked(listMatch3DGallery).mockResolvedValue({ items: [] });
render(<TestWrapper withAuth />);
await openDiscoverHub(user);
const searchInput =
await screen.findByPlaceholderText('搜索作品号、名称、作者、描述');
await user.type(searchInput, 'M3-20260525');
await user.click(screen.getByRole('button', { name: '搜索' }));
expect(await screen.findByText('详情')).toBeTruthy();
expect(screen.getByText('海底糖果集市')).toBeTruthy();
await user.click(screen.getByRole('button', { name: '启动' }));
await waitFor(() => {
expect(match3dLocalRuntimeAdapterMock.startRun).toHaveBeenCalledWith(
'match3d-demo-20260525',
{},
);
});
expect(match3dServerRuntimeAdapterMock.startRun).not.toHaveBeenCalled();
expect(getMatch3DWorkDetail).not.toHaveBeenCalledWith(
'match3d-demo-20260525',
);
expect(
await screen.findByText('抓大鹅运行态match3d-run-match3d-demo-20260525'),
).toBeTruthy();
});
test('published Match3D runtime receives persisted generated models', async () => {
const user = userEvent.setup();
const match3dWork: Match3DWorkSummary = {

View File

@@ -76,7 +76,6 @@ import type {
WechatMiniProgramPayParams,
WechatNativePayment,
} from '../../../packages/shared/src/contracts/runtime';
import { isMatch3DDemoProfileId } from '../../data/match3dDemoGalleryCard';
import type { HydratedSavedGameSnapshot } from '../../persistence/runtimeSnapshotTypes';
import { buildPublicWorkDetailUrl } from '../../routing/appPageRoutes';
import type { AuthUser } from '../../services/authService';
@@ -4296,19 +4295,13 @@ export function RpgEntryHomeView({
return;
}
const firstCategoryGroup =
categoryGroups.find((group) =>
group.entries.some((entry) => !isMatch3DDemoProfileId(entry.profileId)),
) ?? categoryGroups[0];
const firstCategoryGroup = categoryGroups[0];
const selectedCategoryGroup =
categoryGroups.find((group) => group.tag === selectedCategoryTag) ?? null;
if (
firstCategoryGroup &&
(!selectedCategoryGroup ||
(!hasManualCategoryTagSelectionRef.current &&
selectedCategoryGroup.entries.every((entry) =>
isMatch3DDemoProfileId(entry.profileId),
) &&
firstCategoryGroup.tag !== selectedCategoryGroup.tag))
) {
setSelectedCategoryTag(firstCategoryGroup.tag);

View File

@@ -10,7 +10,6 @@ import {
formatPlatformWorldTime,
isBarkBattleGalleryEntry,
isEdutainmentGalleryEntry,
isMatch3DGalleryEntry,
isVisualNovelGalleryEntry,
isWoodenFishGalleryEntry,
mapBabyObjectMatchDraftToPlatformGalleryCard,
@@ -22,7 +21,6 @@ import {
resolvePlatformPublicWorkCode,
resolvePlatformWorldFallbackCoverImage,
} from './rpgEntryWorldPresentation';
import { buildMatch3DDemoGalleryCard } from '../../data/match3dDemoGalleryCard';
test('formatPlatformWorldTime formats backend seconds timestamp text as date', () => {
expect(formatPlatformWorldTime('1777110165.990127Z')).toBe('2026-04-25');
@@ -80,24 +78,6 @@ test('platform public cards use play type reference images as cover fallback', (
);
});
test('builds local Match3D demo gallery card with generated runtime assets intact', () => {
const card = buildMatch3DDemoGalleryCard();
expect(isMatch3DGalleryEntry(card)).toBe(true);
expect(card.publicWorkCode).toBe('M3-20260525');
expect(resolvePlatformPublicWorkCode(card)).toBe('M3-20260525');
expect(card.coverImageSrc).toBe(
'/match3d-demo/undersea-candy-market/level-scene.png',
);
expect(card.generatedBackgroundAsset?.uiSpritesheetImageSrc).toBe(
'/match3d-demo/undersea-candy-market/ui-spritesheet.png',
);
expect(card.generatedBackgroundAsset?.containerImageSrc).toBeNull();
expect(card.generatedItemAssets?.[0]?.imageViews?.[0]?.imageSrc).toBe(
'/match3d-demo/undersea-candy-market/item-slices/item-01/view-01.png',
);
});
test('buildPuzzleWorkCoverSlides prefers each level formal image', () => {
const slides = buildPuzzleWorkCoverSlides({
workId: 'work-1',

View File

@@ -1,141 +0,0 @@
import type {
Match3DGeneratedBackgroundAsset,
Match3DGeneratedItemAsset,
Match3DWorkProfile,
} from '../../packages/shared/src/contracts/match3dWorks';
import { buildMatch3DPublicWorkCode } from '../services/publicWorkCode';
import type { PlatformMatch3DGalleryCard } from '../components/rpg-entry/rpgEntryWorldPresentation';
export const MATCH3D_DEMO_PROFILE_ID = 'match3d-demo-20260525';
export const MATCH3D_DEMO_WORK_ID = 'match3d-demo-undersea-candy-market';
export const MATCH3D_DEMO_PUBLIC_WORK_CODE =
buildMatch3DPublicWorkCode(MATCH3D_DEMO_PROFILE_ID);
const MATCH3D_DEMO_ASSET_BASE = '/match3d-demo/undersea-candy-market';
const MATCH3D_DEMO_PUBLISHED_AT = '2026-05-25T12:04:17.000+08:00';
const MATCH3D_DEMO_ITEM_NAMES = [
'海星糖',
'贝壳糖',
'珊瑚软糖',
'珍珠泡泡糖',
'海马棒棒糖',
'鱼尾果冻',
'水母棉花糖',
'螺旋饼干',
'海螺巧克力',
'贝珠马卡龙',
'珊瑚杯糕',
'星砂软糖',
'小鱼糖块',
'海草曲奇',
'泡泡杯',
'蓝莓珊瑚糖',
'迷你糖罐',
'珍珠饼',
'海浪甜甜圈',
'贝壳蛋糕',
] as const;
export const MATCH3D_DEMO_BACKGROUND_ASSET: Match3DGeneratedBackgroundAsset = {
prompt: '海底糖果集市抓大鹅关卡背景',
levelScenePrompt: '海底糖果集市完整关卡画面',
levelSceneImageSrc: `${MATCH3D_DEMO_ASSET_BASE}/level-scene.png`,
imageSrc: `${MATCH3D_DEMO_ASSET_BASE}/background.png`,
uiSpritesheetPrompt: '海底糖果集市 UI 透明 spritesheet',
uiSpritesheetImageSrc: `${MATCH3D_DEMO_ASSET_BASE}/ui-spritesheet.png`,
itemSpritesheetPrompt: '海底糖果集市物品 10x10 透明 spritesheet',
itemSpritesheetImageSrc: `${MATCH3D_DEMO_ASSET_BASE}/item-spritesheet.png`,
containerImageSrc: null,
status: 'image_ready',
};
export function buildMatch3DDemoGeneratedItemAssets() {
return MATCH3D_DEMO_ITEM_NAMES.map<Match3DGeneratedItemAsset>(
(itemName, itemIndex) => {
const itemNumber = itemIndex + 1;
const paddedItemNumber = String(itemNumber).padStart(2, '0');
return {
itemId: `match3d-item-${itemNumber}`,
itemName,
itemSize:
itemIndex < 4 ? '大' : itemIndex < 14 ? '中' : '小',
imageViews: Array.from({ length: 5 }, (_, viewIndex) => {
const viewNumber = viewIndex + 1;
return {
viewId: `view-${String(viewNumber).padStart(2, '0')}`,
viewIndex: viewNumber,
imageSrc: `${MATCH3D_DEMO_ASSET_BASE}/item-slices/item-${paddedItemNumber}/view-${String(viewNumber).padStart(2, '0')}.png`,
};
}),
backgroundAsset:
itemIndex === 0 ? MATCH3D_DEMO_BACKGROUND_ASSET : null,
status: 'image_ready',
};
},
);
}
export function buildMatch3DDemoWorkProfile(): Match3DWorkProfile {
return {
workId: MATCH3D_DEMO_WORK_ID,
profileId: MATCH3D_DEMO_PROFILE_ID,
ownerUserId: 'official-match3d-demo',
sourceSessionId: 'match3d-demo-session-20260525',
gameName: '海底糖果集市',
themeText: '海底糖果集市',
summary: '在海底糖果集市里把同款甜点抓成三消。',
tags: ['抓大鹅', '海底糖果', '官方示例'],
coverImageSrc: `${MATCH3D_DEMO_ASSET_BASE}/level-scene.png`,
referenceImageSrc: null,
clearCount: 21,
difficulty: 8,
publicationStatus: 'published',
playCount: 0,
updatedAt: MATCH3D_DEMO_PUBLISHED_AT,
publishedAt: MATCH3D_DEMO_PUBLISHED_AT,
publishReady: true,
generationStatus: 'ready',
backgroundPrompt: MATCH3D_DEMO_BACKGROUND_ASSET.prompt,
backgroundImageSrc: MATCH3D_DEMO_BACKGROUND_ASSET.imageSrc,
backgroundImageObjectKey: null,
generatedBackgroundAsset: MATCH3D_DEMO_BACKGROUND_ASSET,
generatedItemAssets: buildMatch3DDemoGeneratedItemAssets(),
};
}
export function buildMatch3DDemoGalleryCard(): PlatformMatch3DGalleryCard {
const profile = buildMatch3DDemoWorkProfile();
return {
sourceType: 'match3d',
workId: profile.workId,
profileId: profile.profileId,
sourceSessionId: profile.sourceSessionId,
publicWorkCode: MATCH3D_DEMO_PUBLIC_WORK_CODE,
ownerUserId: profile.ownerUserId,
authorDisplayName: '官方示例',
worldName: profile.gameName,
subtitle: '抓大鹅 · 资源管线示例',
summaryText: profile.summary,
coverImageSrc: profile.coverImageSrc ?? null,
themeTags: profile.tags,
playCount: profile.playCount,
remixCount: 0,
likeCount: 0,
recentPlayCount7d: 0,
visibility: 'published',
publishedAt: profile.publishedAt ?? null,
updatedAt: profile.updatedAt,
backgroundPrompt: profile.backgroundPrompt ?? null,
backgroundImageSrc: profile.backgroundImageSrc ?? null,
backgroundImageObjectKey: profile.backgroundImageObjectKey ?? null,
generatedBackgroundAsset: profile.generatedBackgroundAsset ?? null,
generatedItemAssets: profile.generatedItemAssets ?? [],
};
}
export const MATCH3D_DEMO_WORK_PROFILE = buildMatch3DDemoWorkProfile();
export const MATCH3D_DEMO_GALLERY_CARD = buildMatch3DDemoGalleryCard();
export function isMatch3DDemoProfileId(profileId: string | null | undefined) {
return profileId?.trim() === MATCH3D_DEMO_PROFILE_ID;
}

View File

@@ -117,15 +117,6 @@ test('local Match3D runtime adapter exposes the same runtime seam as the server
expect(stopped.run.status).toBe('Stopped');
});
test('local Match3D runtime adapter keeps the requested profile id on restart', async () => {
const adapter = createLocalMatch3DRuntimeAdapter({ clearCount: 1 });
const started = await adapter.startRun('match3d-demo-20260525');
const restarted = await adapter.restartRun(started.run.runId);
expect(started.run.profileId).toBe('match3d-demo-20260525');
expect(restarted.run.profileId).toBe('match3d-demo-20260525');
});
test('local Match3D runtime adapter keeps authority run local to the adapter', async () => {
const adapter = createLocalMatch3DRuntimeAdapter({ initialRun: startLocalMatch3DRun(1) });
const first = await adapter.getRun('unused-run-id');