This commit is contained in:
2026-04-10 15:37:02 +08:00
parent 161cd32277
commit f19e482c8f
233 changed files with 43987 additions and 5127 deletions

View File

@@ -1,78 +1,131 @@
import type {
SavedGameSnapshot,
BasicOkResult,
CustomWorldLibraryResponse,
RuntimeSettings,
} from '../../packages/shared/src/contracts/runtime';
import type {
SavedGameSnapshotInput,
} from '../persistence/gameSaveStorage';
import type { SavedGameSettings } from '../persistence/gameSettingsStorage';
import type { HydratedSavedGameSnapshot } from '../persistence/runtimeSnapshotTypes';
import type { CustomWorldProfile } from '../types';
import { requestJson } from './apiClient';
import { type ApiRetryOptions,requestJson } from './apiClient';
const RUNTIME_API_BASE = '/api/runtime';
type CustomWorldLibraryResponse = {
profiles?: CustomWorldProfile[];
const RUNTIME_READ_RETRY: ApiRetryOptions = {
maxRetries: 1,
baseDelayMs: 180,
maxDelayMs: 480,
};
const RUNTIME_WRITE_RETRY: ApiRetryOptions = {
maxRetries: 1,
baseDelayMs: 240,
maxDelayMs: 640,
retryUnsafeMethods: true,
};
export async function getSaveSnapshot() {
return requestJson<SavedGameSnapshot | null>(
`${RUNTIME_API_BASE}/save/snapshot`,
{ method: 'GET' },
'读取存档失败',
export type RuntimeRequestOptions = {
signal?: AbortSignal;
retry?: ApiRetryOptions;
};
function requestRuntimeJson<T>(
path: string,
init: RequestInit,
fallbackMessage: string,
options: RuntimeRequestOptions = {},
) {
const method = (init.method ?? 'GET').toUpperCase();
const retry =
options.retry ??
(method === 'GET' ? RUNTIME_READ_RETRY : RUNTIME_WRITE_RETRY);
return requestJson<T>(
`${RUNTIME_API_BASE}${path}`,
{
...init,
signal: options.signal,
},
fallbackMessage,
{ retry },
);
}
export async function putSaveSnapshot(snapshot: SavedGameSnapshotInput) {
return requestJson<SavedGameSnapshot>(
`${RUNTIME_API_BASE}/save/snapshot`,
export async function getSaveSnapshot(options: RuntimeRequestOptions = {}) {
return requestRuntimeJson<HydratedSavedGameSnapshot | null>(
'/save/snapshot',
{ method: 'GET' },
'读取存档失败',
options,
);
}
export async function putSaveSnapshot(
snapshot: SavedGameSnapshotInput,
options: RuntimeRequestOptions = {},
) {
return requestRuntimeJson<HydratedSavedGameSnapshot>(
'/save/snapshot',
{
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(snapshot),
},
'保存存档失败',
options,
);
}
export async function deleteSaveSnapshot() {
return requestJson<{ ok: true }>(
`${RUNTIME_API_BASE}/save/snapshot`,
export async function deleteSaveSnapshot(options: RuntimeRequestOptions = {}) {
return requestRuntimeJson<BasicOkResult>(
'/save/snapshot',
{ method: 'DELETE' },
'删除存档失败',
options,
);
}
export async function getSettings() {
return requestJson<SavedGameSettings>(
`${RUNTIME_API_BASE}/settings`,
export async function getSettings(options: RuntimeRequestOptions = {}) {
return requestRuntimeJson<RuntimeSettings>(
'/settings',
{ method: 'GET' },
'读取设置失败',
options,
);
}
export async function putSettings(settings: SavedGameSettings) {
return requestJson<SavedGameSettings>(
`${RUNTIME_API_BASE}/settings`,
export async function putSettings(
settings: RuntimeSettings,
options: RuntimeRequestOptions = {},
) {
return requestRuntimeJson<RuntimeSettings>(
'/settings',
{
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(settings),
},
'保存设置失败',
options,
);
}
export async function listCustomWorldLibrary() {
const response = await requestJson<CustomWorldLibraryResponse>(
`${RUNTIME_API_BASE}/custom-world-library`,
export async function listCustomWorldLibrary(options: RuntimeRequestOptions = {}) {
const response = await requestRuntimeJson<CustomWorldLibraryResponse<CustomWorldProfile>>(
'/custom-world-library',
{ method: 'GET' },
'读取自定义世界库失败',
options,
);
return Array.isArray(response?.profiles) ? response.profiles : [];
}
export async function upsertCustomWorldProfile(profile: CustomWorldProfile) {
const response = await requestJson<CustomWorldLibraryResponse>(
`${RUNTIME_API_BASE}/custom-world-library/${encodeURIComponent(profile.id)}`,
export async function upsertCustomWorldProfile(
profile: CustomWorldProfile,
options: RuntimeRequestOptions = {},
) {
const response = await requestRuntimeJson<CustomWorldLibraryResponse<CustomWorldProfile>>(
`/custom-world-library/${encodeURIComponent(profile.id)}`,
{
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
@@ -81,17 +134,33 @@ export async function upsertCustomWorldProfile(profile: CustomWorldProfile) {
}),
},
'保存自定义世界失败',
options,
);
return Array.isArray(response?.profiles) ? response.profiles : [];
}
export async function deleteCustomWorldProfile(profileId: string) {
const response = await requestJson<CustomWorldLibraryResponse>(
`${RUNTIME_API_BASE}/custom-world-library/${encodeURIComponent(profileId)}`,
export async function deleteCustomWorldProfile(
profileId: string,
options: RuntimeRequestOptions = {},
) {
const response = await requestRuntimeJson<CustomWorldLibraryResponse<CustomWorldProfile>>(
`/custom-world-library/${encodeURIComponent(profileId)}`,
{ method: 'DELETE' },
'删除自定义世界失败',
options,
);
return Array.isArray(response?.profiles) ? response.profiles : [];
}
export const runtimeStorageClient = {
getSaveSnapshot,
putSaveSnapshot,
deleteSaveSnapshot,
getSettings,
putSettings,
listCustomWorldLibrary,
upsertCustomWorldProfile,
deleteCustomWorldProfile,
};