diff --git a/packages/frontend/src/pages/room.vue b/packages/frontend/src/pages/room.vue index a2d22a4c6f..659de04a98 100644 --- a/packages/frontend/src/pages/room.vue +++ b/packages/frontend/src/pages/room.vue @@ -71,7 +71,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ selectedObjectDef.name }} - +
@@ -265,6 +265,7 @@ const attachments = { } as RoomAttachments; function addFileAttachment(file: Misskey.entities.DriveFile) { + // TODO: clean unused attachment attachments.files.push(file); } @@ -460,6 +461,11 @@ function showSnappingMenu(ev: PointerEvent) { }], ev.currentTarget ?? ev.target); } +function updateObjectOption(k: string, v: any) { + // TODO: podtMrssageのコスト削減のためattachmentsは更新がある場合のみ送る + controller.updateObjectOption(controller.selected.value.objectId, k, v, attachments); +} + async function addObject(ev: PointerEvent) { // 重いので止める controller.pauseRender(); diff --git a/packages/frontend/src/world/room/engine.ts b/packages/frontend/src/world/room/engine.ts index 2e7d4872ad..8d9d3d83a9 100644 --- a/packages/frontend/src/world/room/engine.ts +++ b/packages/frontend/src/world/room/engine.ts @@ -19,7 +19,7 @@ import { findMaterial, GRAPHICS_QUALITY, ModelManager, SYSTEM_HEYA_MESH_NAMES, S import { JapaneseEnvManager, MuseumEnvManager, SimpleEnvManager } from './env.js'; import { convertRawOptions } from './object.js'; import type { RoomAttachments } from './utility.js'; -import type { ConvertedOptions, ObjectDef, RoomObjectInstance, RoomStateObject } from './object.js'; +import type { ConvertedOptions, ObjectDef, RawOptions, RoomObjectInstance, RoomStateObject } from './object.js'; import type { GridMaterial } from '@babylonjs/materials'; import type { EnvManager, JapaneseEnvOptions, SimpleEnvOptions } from './env.js'; import { genId } from '@/utility/id.js'; @@ -42,6 +42,22 @@ export type RoomState = { worldScale: number; }; +export function collectAttachmentFileIds(roomState: RoomState) { + const fileIds = new Set(); + for (const o of roomState.installedObjects) { + const def = getObjectDef(o.type); + for (const schemaRecord of Object.entries(def.options.schema)) { + if (schemaRecord[1].type === 'file') { + const optionValue = o.options[schemaRecord[0]]; + if (optionValue != null && optionValue !== '') { + fileIds.add(optionValue); + } + } + } + } + return fileIds; +} + function mergeMeshes(meshes: BABYLON.Mesh[], root: BABYLON.Mesh, hasTexture: boolean) { const excludeMeshes = root.getChildMeshes().filter(m => SYSTEM_MESH_NAMES.some(s => m.name.includes(s))); @@ -641,7 +657,7 @@ export class RoomEngine extends EventEmitter { id: string; position: BABYLON.Vector3; rotation: BABYLON.Vector3; - options: any; + options: RawOptions; }) { const def = getObjectDef(args.type); @@ -1448,7 +1464,7 @@ export class RoomEngine extends EventEmitter { return root; } - public async addObject(type: string, _options?: any, attachments?: RoomAttachments) { + public async addObject(type: string, _options?: RawOptions, attachments?: RoomAttachments) { if (!this.isEditMode) return; if (this.grabbingCtx != null) return; diff --git a/packages/frontend/src/world/room/object-defs.ts b/packages/frontend/src/world/room/object-defs.ts index 0a0ac77e5b..1b1e965c83 100644 --- a/packages/frontend/src/world/room/object-defs.ts +++ b/packages/frontend/src/world/room/object-defs.ts @@ -114,6 +114,7 @@ import { wireNet } from './objects/wireNet.js'; import { woodRingFloorLamp } from './objects/woodRingFloorLamp.js'; import { woodRingsPendantLight } from './objects/woodRingsPendantLight.js'; import { woodSoundAbsorbingPanel } from './objects/woodSoundAbsorbingPanel.js'; +import type { ObjectDef } from './object.js'; export const OBJECT_DEFS = [ a4Case, @@ -229,8 +230,8 @@ export const OBJECT_DEFS = [ wireBasket, ]; -export function getObjectDef(type: string): typeof OBJECT_DEFS[number] { - const def = OBJECT_DEFS.find(x => x.id === type); +export function getObjectDef(type: string): ObjectDef { + const def = OBJECT_DEFS.find(x => x.id === type) as ObjectDef | undefined; if (def == null) { throw new Error(`Unrecognized object type: ${type}`); } diff --git a/packages/frontend/src/world/room/object.ts b/packages/frontend/src/world/room/object.ts index b2d80841f5..9b247eb7aa 100644 --- a/packages/frontend/src/world/room/object.ts +++ b/packages/frontend/src/world/room/object.ts @@ -115,13 +115,13 @@ type GetConvertedOptionsSchemaValues = { never; }; -export type ObjectDef = { +export type ObjectDef = { id: string; name: string; path?: string; options: { - schema: OpSc; - default: GetRawOptionsSchemaValues; + schema: OpSc extends undefined ? OptionsSchema : NonNullable; + default: OpSc extends undefined ? RawOptions : GetRawOptionsSchemaValues>; }; placement: 'top' | 'side' | 'bottom' | 'wall' | 'ceiling' | 'floor'; hasCollisions?: boolean; @@ -141,13 +141,13 @@ export type ObjectDef = { }; lc: BABYLON.ClusteredLightContainer | null; root: BABYLON.Mesh; - options: Readonly>; + options: OpSc extends undefined ? ConvertedOptions : Readonly>>; model: ModelManager; id: string; timer: Timer; graphicsQuality: number; stickyMarkerMeshUpdated?: (mesh: BABYLON.Mesh) => void; - }) => RoomObjectInstance> | Promise>>; // TODO: createInstanceをasyncにするのではなく、別にreadyみたいなものを返させる + }) => RoomObjectInstance>> | Promise>>>; // TODO: createInstanceをasyncにするのではなく、別にreadyみたいなものを返させる }; export function defineObject(def: ObjectDef): ObjectDef { @@ -162,8 +162,8 @@ export function defineObjectClass(baseDef: Par }; } -export function convertRawOptions(schema: OpSc, raw: RawOptions, attachments: RoomAttachments): GetConvertedOptionsSchemaValues { - const converted = {} as GetConvertedOptionsSchemaValues; +export function convertRawOptions(schema: OpSc, raw: RawOptions, attachments: RoomAttachments): ConvertedOptions { + const converted = {} as ConvertedOptions; for (const record of Object.entries(schema)) { const k = record[0]; const v = raw[k];