Move piano glue behind behavior registry

This commit is contained in:
Jage9
2026-03-08 20:31:35 -04:00
parent ffba9530c8
commit 34818d30f5
6 changed files with 89 additions and 65 deletions

View File

@@ -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);
}
}
}

View File

@@ -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,29 +61,37 @@ export function createPianoBehavior(deps: ItemBehaviorDeps): ItemBehavior {
if (mode !== 'pianoUse') return false;
return controller.runModeCommand(commandId);
},
onRemotePianoNote: (message) => {
if (message.on) {
controller.playRemoteNote({
itemId: message.itemId,
senderId: message.senderId,
keyId: message.keyId,
midi: message.midi,
instrument: message.instrument,
voiceMode: message.voiceMode,
octave: message.octave,
attack: message.attack,
decay: message.decay,
release: message.release,
brightness: message.brightness,
x: message.x,
y: message.y,
emitRange: message.emitRange,
});
} else {
controller.stopRemoteNote(message.senderId, message.keyId);
onIncomingMessage: (message) => {
if (message.type === 'item_piano_note') {
if (message.on) {
controller.playRemoteNote({
itemId: message.itemId,
senderId: message.senderId,
keyId: message.keyId,
midi: message.midi,
instrument: message.instrument,
voiceMode: message.voiceMode,
octave: message.octave,
attack: message.attack,
decay: message.decay,
release: message.release,
brightness: message.brightness,
x: message.x,
y: message.y,
emitRange: message.emitRange,
});
} 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);
},
};

View File

@@ -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;
};