import type { SCUMCommandResult, SCUMConfigPatch, SCUMConfigRead, SCUMFeatureAvailability, SCUMFeatureKey, SCUMGiftGrant, SCUMPlayerProfile, SCUMStatePatch, SCUMStateSnapshot, SCUMTrajectoryCollection } from "./contracts.js"; import { validateConfigPatch, validateStatePatch } from "./schemas.js"; export type PluginFeatureBridge = { dispatch(action: "game-client.command" | "game-client.snapshot.read", payload: Record): Promise<{ status: string; result?: Record; error?: { message: string } }> }; export type SCUMFeatureAPI = { availability(feature: SCUMFeatureKey): Promise; readConfig(version: string): Promise; patchConfig(patch: SCUMConfigPatch): Promise; playerProfile(playerId: string): Promise; stateSnapshot(playerId: string): Promise; requestStatePatch(patch: SCUMStatePatch): Promise; giftGrants(): Promise; trajectories(): Promise; }; export function createSCUMFeatureAPI(bridge: PluginFeatureBridge, serverVersion: string, availableFeatures: readonly SCUMFeatureAvailability[]): SCUMFeatureAPI { const availability = async (feature: SCUMFeatureKey) => availableFeatures.find((item) => item.feature === feature) ?? { feature, available: false, reason: "插件未声明此功能。", serverVersion }; return { availability, async readConfig(version) { const result = await bridge.dispatch("game-client.command", { type: "config.read", version }); return result.status === "ok" ? decode(result.result) : null; }, async patchConfig(patch) { const error = validateConfigPatch(patch); if (error) return { status: "validation-failed", summary: error }; return commandResult(await bridge.dispatch("game-client.command", { type: "config.patch", patch: JSON.stringify(patch) })); }, async playerProfile(playerId) { const result = await bridge.dispatch("game-client.snapshot.read", { type: "semantic.events", subjectId: playerId }); return result.status === "ok" ? decode(result.result) : null; }, async stateSnapshot(playerId) { const result = await bridge.dispatch("game-client.command", { type: "player.lookup", playerId }); return result.status === "ok" ? decode(result.result) : null; }, async requestStatePatch(patch) { const error = validateStatePatch(patch.gameVersion, patch.changes); if (error) return { status: "validation-failed", summary: error }; return commandResult(await bridge.dispatch("game-client.command", { type: "game-state.patch", patch: JSON.stringify(patch) })); }, async giftGrants() { const result = await bridge.dispatch("game-client.snapshot.read", { type: "semantic.events", projection: "gifts" }); return result.status === "ok" ? decode(result.result) ?? [] : []; }, async trajectories() { const result = await bridge.dispatch("game-client.snapshot.read", { type: "semantic.events", projection: "trajectories" }); return result.status === "ok" ? decode(result.result) ?? { available: false, reason: "没有已验证的位置事件源。", trajectories: [] } : { available: false, reason: result.error?.message ?? "没有已验证的位置事件源。", trajectories: [] }; } }; } function commandResult(result: { status: string; result?: Record; error?: { message: string } }): SCUMCommandResult { if (result.status === "queued") return { status: "queued", summary: result.result?.summary ?? "已进入受控队列。" }; if (result.status === "unsupported") return { status: "unsupported", summary: result.error?.message ?? "当前版本不支持此操作。" }; return { status: "failed", summary: result.error?.message ?? "受控操作未被接受。" }; } function decode(result: Record | undefined): T | null { const payload = result?.payload; if (!payload) return null; try { return JSON.parse(payload) as T; } catch { return null; } }