feat: add official game audio generation client
This commit is contained in:
@@ -48,6 +48,10 @@ import {
|
||||
import { createDataServicePluginAdapter } from '../coding-plugins/adapters/data-service';
|
||||
import { createGameResourcePluginAdapter } from '../coding-plugins/adapters/game-resource';
|
||||
import { GameResourceClient } from '../services/game-resource-client';
|
||||
import { GameAudioClient } from '../services/game-audio-client';
|
||||
import { GameAudioDeliveryCoordinator, GameAudioReceiptStore } from '../services/game-audio-delivery';
|
||||
import { GameAudioPluginAdapter } from '../coding-plugins/adapters/game-audio';
|
||||
import { MarketplaceHostedAdmissionResolver } from '../coding-plugins/hosted-admission';
|
||||
import {
|
||||
GameResourceDeliveryCoordinator,
|
||||
GameResourceDeliveryReceiptStore,
|
||||
@@ -339,6 +343,27 @@ export function createCodingComposition(
|
||||
bundledReleases: CODE_OWNED_OPTIONAL_BUNDLED_RELEASES,
|
||||
});
|
||||
const policyClient = options.policyClient ?? new PluginPolicyClient();
|
||||
const gameAudioClient = new GameAudioClient();
|
||||
const gameAudioDelivery = new GameAudioDeliveryCoordinator({
|
||||
client: gameAudioClient,
|
||||
receipts: new GameAudioReceiptStore(path.join(options.paths.userDataDir, 'coding-runtime', 'game-audio', 'receipts.json')),
|
||||
leases: projectWriteLeases,
|
||||
recordTouchedPaths: (conversationId, runId, paths) => productTools.recordTouchedPaths(conversationId, runId, paths),
|
||||
...(options.acquireBackgroundLease ? { acquireBackgroundLease: options.acquireBackgroundLease } : {}),
|
||||
});
|
||||
const gameAudioAdapter = new GameAudioPluginAdapter({
|
||||
client: gameAudioClient,
|
||||
delivery: gameAudioDelivery,
|
||||
admission: new MarketplaceHostedAdmissionResolver({
|
||||
marketplace: marketplaceClient, packageStore,
|
||||
makeloreVersion: options.clientVersion ?? '2.0.1',
|
||||
bundledReleases: CODE_OWNED_OPTIONAL_BUNDLED_RELEASES,
|
||||
}),
|
||||
getPricingVersion: () => {
|
||||
const state = policyClient.getState();
|
||||
return state.status === 'current' ? state.catalog?.pricing_version?.version_id ?? null : null;
|
||||
},
|
||||
});
|
||||
const knownPluginIds = new Set(pluginDefinitions.map(({ id }) => id));
|
||||
// Existing user Releases are discovered from the device index at startup;
|
||||
// unlike the bundled catalog they cannot be enumerated synchronously. Keep
|
||||
@@ -373,7 +398,7 @@ export function createCodingComposition(
|
||||
const capabilityRegistry = createCodingCapabilityRegistry({
|
||||
policyClient,
|
||||
projectPlugins,
|
||||
adapters: [dataServiceAdapter, gameResourceAdapter],
|
||||
adapters: [dataServiceAdapter, gameResourceAdapter, gameAudioAdapter],
|
||||
definitions: pluginDefinitions,
|
||||
effectiveResolver,
|
||||
getDurableProjectId: async (projectPath, localProjectId) => {
|
||||
@@ -389,7 +414,7 @@ export function createCodingComposition(
|
||||
projects,
|
||||
projectPlugins,
|
||||
policyClient,
|
||||
adapters: [dataServiceAdapter, gameResourceAdapter],
|
||||
adapters: [dataServiceAdapter, gameResourceAdapter, gameAudioAdapter],
|
||||
definitions: pluginDefinitions,
|
||||
effectiveResolver,
|
||||
getDefinitions: async () => {
|
||||
@@ -505,9 +530,11 @@ export function createCodingComposition(
|
||||
},
|
||||
});
|
||||
void gameResourceDelivery.resumePending().catch(() => undefined);
|
||||
void gameAudioDelivery.resumePending().catch(() => undefined);
|
||||
const unsubscribeMarketplaceSession = subscribeWorksSquareSession(() => {
|
||||
void invalidateManagedResources();
|
||||
void gameResourceDelivery.resumePending().catch(() => undefined);
|
||||
void gameAudioDelivery.resumePending().catch(() => undefined);
|
||||
});
|
||||
const host = createCodingProductHost({
|
||||
projects,
|
||||
@@ -526,6 +553,7 @@ export function createCodingComposition(
|
||||
})
|
||||
: () => undefined;
|
||||
return {
|
||||
gameAudio: gameAudioDelivery,
|
||||
attachments,
|
||||
dataService,
|
||||
devicePackages: devicePackageManager,
|
||||
@@ -557,6 +585,7 @@ export function createCodingComposition(
|
||||
async shutdown() {
|
||||
conversations.dispose();
|
||||
gameResourceDelivery.dispose();
|
||||
gameAudioDelivery.dispose();
|
||||
previewDataSession?.dispose();
|
||||
if (typeof options.browser.configurePreviewDataSession === 'function') {
|
||||
options.browser.configurePreviewDataSession(undefined);
|
||||
|
||||
@@ -48,6 +48,7 @@ import type {
|
||||
import type { MarketplaceLibrarySnapshot } from '../coding-plugins/account-plugin-cache';
|
||||
import type { EffectivePluginResolver } from '../coding-plugins/effective-resolver';
|
||||
import type { DevicePackageManager } from '../coding-packages/device-package-manager';
|
||||
import type { GameAudioDeliveryCoordinator } from '../services/game-audio-delivery';
|
||||
|
||||
export interface ActiveCodingProject {
|
||||
id: string;
|
||||
@@ -65,6 +66,7 @@ export interface CodingProductHost {
|
||||
}
|
||||
|
||||
export interface CodingProductComposition {
|
||||
gameAudio?: Pick<GameAudioDeliveryCoordinator, 'readSavedOutput'>;
|
||||
attachments: CodingAttachmentStore;
|
||||
dataService: DataServiceOperations;
|
||||
devicePackages: DevicePackageManager;
|
||||
|
||||
@@ -4,6 +4,7 @@ import { CodingProjectServiceError } from '../../coding-projects/project-service
|
||||
import type { HostApiContext } from '../context';
|
||||
import { parseJsonBody, sendJson } from '../route-utils';
|
||||
import { CodingProjectPluginServiceError } from '../coding-product-services';
|
||||
import { GameAudioClientError } from '../../services/game-audio-client';
|
||||
|
||||
const PLUGIN_ROUTE = /^\/api\/coding\/plugins\/([^/]+)$/u;
|
||||
const PLUGIN_ID_PATTERN = /^[a-z][a-z0-9.-]{0,127}$/u;
|
||||
@@ -96,6 +97,20 @@ export async function handleCodingPluginRoutes(
|
||||
url: URL,
|
||||
ctx: HostApiContext,
|
||||
): Promise<boolean> {
|
||||
const savedAudio = /^\/api\/coding\/game-audio\/([0-9a-f-]{36})\/outputs\/([0-9])$/u.exec(url.pathname);
|
||||
if (savedAudio && req.method === 'GET') {
|
||||
try {
|
||||
const delivery = ctx.codingProducts?.gameAudio;
|
||||
if (!delivery) throw new CodingPluginRouteError(503, 'game_audio_unavailable', 'Audio delivery is unavailable');
|
||||
if (url.search) requestError('Saved audio lookup does not accept query parameters');
|
||||
sendJson(res, 200, await delivery.readSavedOutput(savedAudio[1], Number(savedAudio[2])));
|
||||
} catch (error) {
|
||||
if (error instanceof GameAudioClientError) {
|
||||
sendJson(res, error.status, { code: error.code, error: error.message });
|
||||
} else sendError(res, error);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (url.pathname !== '/api/coding/plugins' && !PLUGIN_ROUTE.test(url.pathname)) return false;
|
||||
const plugins = ctx.codingProducts?.plugins;
|
||||
if (!plugins) {
|
||||
|
||||
Reference in New Issue
Block a user