import * as THREE from 'three'; import type {Controller, ControllerEvent} from '../../input/Controller'; import type { GlobalInteractionHook, GlobalInteractionEvent, InteractionCallbackDispatch, InteractionSourceType, TargetedInteractionHook, } from '../../interaction/InteractionTypes'; import {getInteractionSource} from '../../interaction/InteractionTypes'; import type {ManipulationEvent} from '../../interaction/manipulation/ManipulationTypes'; import {getSemanticControl} from '../../interaction/SemanticControl'; import { KeyEvent, type HoverEvent, type LongSelectEvent, type ObjectGrabEvent, type ObjectTouchEvent, type ObjectTouchStartEvent, Script, SelectEvent, type SelectEndEvent, } from '../Script'; import {isDefaultScriptMethod} from '../ScriptHooks'; type MaybeScript = THREE.Object3D & {isXRScript?: boolean}; export enum ScriptsManagerEventType { EXCEPTION = 'exception', } export type ScriptsManagerEventMap = THREE.Object3DEventMap & { [ScriptsManagerEventType.EXCEPTION]: { scriptName: string; context: string; error: Error; timestamp: number; }; }; type GlobalScriptHook = | 'update' | 'physicsStep' | 'onSelectStart' | 'onSelectEnd' | 'onSelect' | 'onSelecting' | 'onLongSelect' | 'onSqueezeStart' | 'onSqueezeEnd' | 'onSqueeze' | 'onSqueezing' | 'onKeyDown' | 'onKeyUp' | 'onXRSessionStarted' | 'onXRSessionEnded' | 'onSimulatorStarted'; type IndexedScriptHook = | TargetedInteractionHook | GlobalScriptHook | 'onObjectManipulate'; interface PendingInitialization { readonly script: Script; promise: Promise; connection: 'connected' | 'disconnected' | 'reconnected'; } type TargetDispatch = { [Hook in TargetedInteractionHook]: ( script: Script, argument: unknown ) => void; }; const TARGET_DISPATCH: TargetDispatch = { onObjectSelectStart: (script, argument) => script.onObjectSelectStart(argument as SelectEvent), onObjectSelectEnd: (script, argument) => script.onObjectSelectEnd(argument as SelectEndEvent), onObjectLongSelect: (script, argument) => script.onObjectLongSelect(argument as LongSelectEvent), onObjectTouchStart: (script, argument) => script.onObjectTouchStart(argument as ObjectTouchStartEvent), onObjectTouching: (script, argument) => script.onObjectTouching(argument as ObjectTouchEvent), onObjectTouchEnd: (script, argument) => script.onObjectTouchEnd(argument as ObjectTouchEvent), onObjectGrabStart: (script, argument) => script.onObjectGrabStart(argument as ObjectGrabEvent), onObjectGrabbing: (script, argument) => script.onObjectGrabbing(argument as ObjectGrabEvent), onObjectGrabEnd: (script, argument) => script.onObjectGrabEnd(argument as ObjectGrabEvent), onHoverEnter: (script, argument) => script.onHoverEnter(argument as HoverEvent), onHovering: (script, argument) => script.onHovering(argument as HoverEvent), onHoverExit: (script, argument) => script.onHoverExit(argument as HoverEvent), }; const TARGETED_HOOKS = Object.freeze( Object.keys(TARGET_DISPATCH) as TargetedInteractionHook[] ); const GLOBAL_HOOKS = Object.freeze([ 'update', 'physicsStep', 'onSelectStart', 'onSelectEnd', 'onSelect', 'onSelecting', 'onLongSelect', 'onSqueezeStart', 'onSqueezeEnd', 'onSqueeze', 'onSqueezing', 'onKeyDown', 'onKeyUp', 'onXRSessionStarted', 'onXRSessionEnded', 'onSimulatorStarted', ] as const satisfies readonly GlobalScriptHook[]); const INDEXED_HOOKS = Object.freeze([ ...TARGETED_HOOKS, ...GLOBAL_HOOKS, 'onObjectManipulate', ] as const satisfies readonly IndexedScriptHook[]); const RAY_TARGET_HOOKS = Object.freeze([ 'onObjectSelectStart', 'onObjectSelectEnd', 'onObjectLongSelect', 'onHoverEnter', 'onHovering', 'onHoverExit', ] as const satisfies readonly TargetedInteractionHook[]); const DIRECT_TOUCH_TARGET_HOOKS = Object.freeze([ 'onObjectSelectStart', 'onObjectSelectEnd', 'onObjectLongSelect', 'onObjectTouchStart', 'onObjectTouching', 'onObjectTouchEnd', 'onObjectGrabStart', 'onObjectGrabbing', 'onObjectGrabEnd', ] as const satisfies readonly TargetedInteractionHook[]); const SCRIPT_READY = Promise.resolve(); const NO_SCRIPT_CHANGES = Promise.resolve[]>([]); export class ScriptsManager extends THREE.EventDispatcher implements InteractionCallbackDispatch { private readonly activeScripts = new Set