Move piano glue behind behavior registry
This commit is contained in:
@@ -19,8 +19,8 @@ type KeyboardControllerDeps = {
|
||||
handleModeInput: (input: ModeInput) => void;
|
||||
canOpenCommandPaletteInMode: (mode: GameMode) => boolean;
|
||||
openCommandPalette: () => void;
|
||||
shouldForwardModeKeyUp: (mode: GameMode) => boolean;
|
||||
onModeKeyUp: (input: Pick<ModeInput, 'code' | 'shiftKey'>) => void;
|
||||
getModeKeyUpTarget: (activeMode: GameMode) => GameMode | null;
|
||||
onModeKeyUp: (mode: GameMode, input: Pick<ModeInput, 'code' | 'shiftKey'>) => void;
|
||||
pasteIntoActiveTextInput: (text: string) => boolean;
|
||||
updateStatus: (message: string) => void;
|
||||
setReplaceTextOnNextType: (value: boolean) => void;
|
||||
@@ -157,8 +157,9 @@ export function setupKeyboardInputHandlers(deps: KeyboardControllerDeps): void {
|
||||
|
||||
document.addEventListener('keyup', (event) => {
|
||||
const code = normalizeInputCode(event);
|
||||
if (code && deps.shouldForwardModeKeyUp(deps.state.mode)) {
|
||||
deps.onModeKeyUp({
|
||||
const keyUpMode = deps.getModeKeyUpTarget(deps.state.mode);
|
||||
if (code && keyUpMode) {
|
||||
deps.onModeKeyUp(keyUpMode, {
|
||||
code,
|
||||
shiftKey: event.shiftKey,
|
||||
});
|
||||
|
||||
@@ -77,6 +77,27 @@ export class ItemBehaviorRegistry {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns whether any item-owned mode supports opening the command palette. */
|
||||
canOpenModeCommandPalette(mode: GameMode): boolean {
|
||||
for (const behavior of this.behaviors) {
|
||||
if (behavior.canOpenModeCommandPalette?.(mode)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Resolves an optional suspended mode that still wants key-up events while another overlay is active. */
|
||||
getModeKeyUpTarget(activeMode: GameMode, returnMode: GameMode): GameMode | null {
|
||||
for (const behavior of this.behaviors) {
|
||||
const target = behavior.getModeKeyUpTarget?.(activeMode, returnMode);
|
||||
if (target) {
|
||||
return target;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Returns palette-visible commands for the active item-owned mode, if any. */
|
||||
getModeCommands(mode: GameMode): CommandDescriptor[] {
|
||||
const commands: CommandDescriptor[] = [];
|
||||
@@ -99,24 +120,20 @@ export class ItemBehaviorRegistry {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Routes incoming item-piano-note packets to the item behavior owning that protocol. */
|
||||
onRemotePianoNote(message: Extract<IncomingMessage, { type: 'item_piano_note' }>): void {
|
||||
/** Gives item behaviors a chance to consume custom incoming packets. */
|
||||
onIncomingMessage(message: IncomingMessage): boolean {
|
||||
for (const behavior of this.behaviors) {
|
||||
behavior.onRemotePianoNote?.(message);
|
||||
if (behavior.onIncomingMessage?.(message)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Routes incoming item-piano-status packets to behavior modules that track piano runtime state. */
|
||||
onPianoStatus(message: Extract<IncomingMessage, { type: 'item_piano_status' }>): void {
|
||||
/** Notifies behaviors that a peer left so they can release sender-owned runtime state. */
|
||||
onPeerLeft(senderId: string): void {
|
||||
for (const behavior of this.behaviors) {
|
||||
behavior.onPianoStatus?.(message);
|
||||
}
|
||||
}
|
||||
|
||||
/** Stops all remote notes for one sender across behavior modules that own remote note runtimes. */
|
||||
stopAllRemoteNotesForSender(senderId: string): void {
|
||||
for (const behavior of this.behaviors) {
|
||||
behavior.onStopAllRemoteNotesForSender?.(senderId);
|
||||
behavior.onPeerLeft?.(senderId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +31,6 @@ export function createPianoBehavior(deps: ItemBehaviorDeps): ItemBehavior {
|
||||
}
|
||||
return true;
|
||||
},
|
||||
onPianoStatus: (message) => {
|
||||
controller.onPianoStatus(message);
|
||||
},
|
||||
onPropertyPreviewChange: (item, key, value) => {
|
||||
controller.onPreviewPropertyChange(item, key, value);
|
||||
},
|
||||
@@ -50,6 +47,12 @@ export function createPianoBehavior(deps: ItemBehaviorDeps): ItemBehavior {
|
||||
controller.handleModeKeyUp(input);
|
||||
return true;
|
||||
},
|
||||
canOpenModeCommandPalette: (mode) => mode === 'pianoUse',
|
||||
getModeKeyUpTarget: (activeMode, returnMode) => {
|
||||
if (activeMode === 'pianoUse') return 'pianoUse';
|
||||
if (activeMode === 'commandPalette' && returnMode === 'pianoUse') return 'pianoUse';
|
||||
return null;
|
||||
},
|
||||
getModeCommands: (mode) => {
|
||||
if (mode !== 'pianoUse') return [];
|
||||
return controller.getModeCommands();
|
||||
@@ -58,7 +61,8 @@ export function createPianoBehavior(deps: ItemBehaviorDeps): ItemBehavior {
|
||||
if (mode !== 'pianoUse') return false;
|
||||
return controller.runModeCommand(commandId);
|
||||
},
|
||||
onRemotePianoNote: (message) => {
|
||||
onIncomingMessage: (message) => {
|
||||
if (message.type === 'item_piano_note') {
|
||||
if (message.on) {
|
||||
controller.playRemoteNote({
|
||||
itemId: message.itemId,
|
||||
@@ -79,8 +83,15 @@ export function createPianoBehavior(deps: ItemBehaviorDeps): ItemBehavior {
|
||||
} else {
|
||||
controller.stopRemoteNote(message.senderId, message.keyId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (message.type === 'item_piano_status') {
|
||||
controller.onPianoStatus(message);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
onStopAllRemoteNotesForSender: (senderId) => {
|
||||
onPeerLeft: (senderId) => {
|
||||
controller.stopAllRemoteNotesForSender(senderId);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -32,9 +32,10 @@ export type ItemBehavior = {
|
||||
onWorldUpdate?: () => void;
|
||||
handleModeInput?: (mode: GameMode, input: ModeInput) => boolean;
|
||||
handleModeKeyUp?: (mode: GameMode, input: Pick<ModeInput, 'code' | 'shiftKey'>) => boolean;
|
||||
canOpenModeCommandPalette?: (mode: GameMode) => boolean;
|
||||
getModeKeyUpTarget?: (activeMode: GameMode, returnMode: GameMode) => GameMode | null;
|
||||
getModeCommands?: (mode: GameMode) => CommandDescriptor[];
|
||||
runModeCommand?: (mode: GameMode, commandId: string) => boolean;
|
||||
onRemotePianoNote?: (message: Extract<IncomingMessage, { type: 'item_piano_note' }>) => void;
|
||||
onPianoStatus?: (message: Extract<IncomingMessage, { type: 'item_piano_status' }>) => void;
|
||||
onStopAllRemoteNotesForSender?: (senderId: string) => void;
|
||||
onIncomingMessage?: (message: IncomingMessage) => boolean;
|
||||
onPeerLeft?: (senderId: string) => void;
|
||||
};
|
||||
|
||||
@@ -1483,9 +1483,8 @@ const onAppMessage = createOnMessageHandler({
|
||||
);
|
||||
},
|
||||
handleItemActionResultStatus: (message) => itemBehaviorRegistry.onActionResultStatus(message),
|
||||
handleRemotePianoNote: (message) => itemBehaviorRegistry.onRemotePianoNote(message),
|
||||
handlePianoStatus: (message) => itemBehaviorRegistry.onPianoStatus(message),
|
||||
stopAllRemoteNotesForSender: (senderId) => itemBehaviorRegistry.stopAllRemoteNotesForSender(senderId),
|
||||
handleItemBehaviorIncomingMessage: (message) => itemBehaviorRegistry.onIncomingMessage(message),
|
||||
handleItemBehaviorPeerLeft: (senderId) => itemBehaviorRegistry.onPeerLeft(senderId),
|
||||
TELEPORT_SOUND_URL,
|
||||
TELEPORT_START_SOUND_URL,
|
||||
getAudioLayers: () => audioLayers,
|
||||
@@ -2034,7 +2033,7 @@ function getAvailableCommandPaletteEntriesForMode(mode: GameMode): Array<Command
|
||||
run: mainModeCommandHandlers[descriptor.id],
|
||||
}));
|
||||
}
|
||||
if (mode === 'pianoUse') {
|
||||
if (itemBehaviorRegistry.canOpenModeCommandPalette(mode)) {
|
||||
return itemBehaviorRegistry.getModeCommands(mode).map((descriptor) => ({
|
||||
...descriptor,
|
||||
run: () => {
|
||||
@@ -2046,11 +2045,7 @@ function getAvailableCommandPaletteEntriesForMode(mode: GameMode): Array<Command
|
||||
}
|
||||
|
||||
function canOpenCommandPaletteInMode(mode: GameMode): boolean {
|
||||
return mode === 'normal' || mode === 'pianoUse' || mode === 'commandPalette';
|
||||
}
|
||||
|
||||
function shouldForwardPianoKeyUp(currentMode: GameMode): boolean {
|
||||
return currentMode === 'pianoUse' || (currentMode === 'commandPalette' && commandPaletteReturnMode === 'pianoUse');
|
||||
return mode === 'normal' || mode === 'commandPalette' || itemBehaviorRegistry.canOpenModeCommandPalette(mode);
|
||||
}
|
||||
|
||||
function openCommandPalette(): void {
|
||||
@@ -2536,6 +2531,9 @@ function handleNicknameModeInput(code: string, key: string, ctrlKey: boolean): v
|
||||
}
|
||||
|
||||
function handleModeInput(input: ModeInput): void {
|
||||
if (itemBehaviorRegistry.handleModeInput(state.mode, input)) {
|
||||
return;
|
||||
}
|
||||
dispatchModeInput({
|
||||
mode: state.mode,
|
||||
input,
|
||||
@@ -2546,9 +2544,6 @@ function handleModeInput(input: ModeInput): void {
|
||||
handleChatModeInput(currentCode, currentKey, currentCtrlKey),
|
||||
micGainEdit: ({ code: currentCode, key: currentKey, ctrlKey: currentCtrlKey }) =>
|
||||
handleMicGainEditModeInput(currentCode, currentKey, currentCtrlKey),
|
||||
pianoUse: (currentInput) => {
|
||||
itemBehaviorRegistry.handleModeInput(state.mode, currentInput);
|
||||
},
|
||||
commandPalette: ({ code: currentCode, key: currentKey }) => handleCommandPaletteModeInput(currentCode, currentKey),
|
||||
effectSelect: ({ code: currentCode, key: currentKey }) => handleEffectSelectModeInput(currentCode, currentKey),
|
||||
helpView: ({ code: currentCode }) => handleHelpViewModeInput(currentCode),
|
||||
@@ -2617,9 +2612,9 @@ setupKeyboardInputHandlers({
|
||||
handleModeInput,
|
||||
canOpenCommandPaletteInMode,
|
||||
openCommandPalette,
|
||||
shouldForwardModeKeyUp: shouldForwardPianoKeyUp,
|
||||
onModeKeyUp: ({ code, shiftKey }) => {
|
||||
itemBehaviorRegistry.handleModeKeyUp('pianoUse', {
|
||||
getModeKeyUpTarget: (activeMode) => itemBehaviorRegistry.getModeKeyUpTarget(activeMode, commandPaletteReturnMode),
|
||||
onModeKeyUp: (mode, { code, shiftKey }) => {
|
||||
itemBehaviorRegistry.handleModeKeyUp(mode, {
|
||||
code,
|
||||
shiftKey,
|
||||
});
|
||||
|
||||
@@ -46,9 +46,8 @@ type MessageHandlerDeps = {
|
||||
randomFootstepUrl: () => string;
|
||||
playRemoteSpatialStepOrTeleport: (url: string, peerX: number, peerY: number) => void;
|
||||
handleItemActionResultStatus: (message: Extract<IncomingMessage, { type: 'item_action_result' }>) => boolean;
|
||||
handleRemotePianoNote: (message: Extract<IncomingMessage, { type: 'item_piano_note' }>) => void;
|
||||
handlePianoStatus: (message: Extract<IncomingMessage, { type: 'item_piano_status' }>) => void;
|
||||
stopAllRemoteNotesForSender: (senderId: string) => void;
|
||||
handleItemBehaviorIncomingMessage: (message: IncomingMessage) => boolean;
|
||||
handleItemBehaviorPeerLeft: (senderId: string) => void;
|
||||
TELEPORT_SOUND_URL: string;
|
||||
TELEPORT_START_SOUND_URL: string;
|
||||
getAudioLayers: () => { world: boolean; item: boolean };
|
||||
@@ -225,7 +224,7 @@ export function createOnMessageHandler(deps: MessageHandlerDeps): (message: Inco
|
||||
if (peer) {
|
||||
deps.updateStatus(`${peer.nickname} has left.`);
|
||||
}
|
||||
deps.stopAllRemoteNotesForSender(message.id);
|
||||
deps.handleItemBehaviorPeerLeft(message.id);
|
||||
deps.state.peers.delete(message.id);
|
||||
deps.peerManager.removePeer(message.id);
|
||||
break;
|
||||
@@ -335,7 +334,7 @@ export function createOnMessageHandler(deps: MessageHandlerDeps): (message: Inco
|
||||
|
||||
case 'item_piano_note': {
|
||||
if (!deps.getAudioLayers().item) break;
|
||||
deps.handleRemotePianoNote(message);
|
||||
deps.handleItemBehaviorIncomingMessage(message);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -346,7 +345,7 @@ export function createOnMessageHandler(deps: MessageHandlerDeps): (message: Inco
|
||||
}
|
||||
|
||||
case 'item_piano_status': {
|
||||
deps.handlePianoStatus(message);
|
||||
deps.handleItemBehaviorIncomingMessage(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user