diff --git a/electron/common/game/game.types.ts b/electron/common/game/game.types.ts index 89cc6113..22f07b2d 100644 --- a/electron/common/game/game.types.ts +++ b/electron/common/game/game.types.ts @@ -354,3 +354,27 @@ export enum ExperienceMindState { NEARLY_LOCKED = 33, MIND_LOCK = 34, } + +export interface GameConnectMessage { + accountName: string; + characterName: string; + gameCode: string; +} + +export interface GameDisconnectMessage { + accountName: string; + characterName: string; + gameCode: string; +} + +export interface GameErrorMessage { + error: Error; +} + +export interface GameEventMessage { + gameEvent: GameEvent; +} + +export interface GameCommandMessage { + command: string; +} diff --git a/electron/main/app.ts b/electron/main/app.ts index 31d69f98..aaa180f1 100644 --- a/electron/main/app.ts +++ b/electron/main/app.ts @@ -3,12 +3,11 @@ import { BrowserWindow, app, shell } from 'electron'; import * as path from 'node:path'; import serve from 'electron-serve'; import { runInBackground } from '../common/async'; -import type { IpcController } from './ipc'; +import type { IpcController, IpcDispatcher } from './ipc'; import { newIpcController } from './ipc'; import { createLogger } from './logger'; import { initializeMenu } from './menu'; import { PreferenceKey, Preferences } from './preference'; -import type { Dispatcher } from './types'; app.setName('Phoenix'); app.setAppUserModelId('com.github.dragonrealms-phoenix.phoenix'); @@ -99,7 +98,7 @@ const createMainWindow = async (): Promise => { mainWindow.show(); }); - const dispatch: Dispatcher = (channel, ...args): void => { + const dispatch: IpcDispatcher = (channel, ...args): void => { // When the window is closed or destroyed, we might still // receive async events from the ipc controller. Ignore them. // This usually happens when the app is quit while a game is being played. diff --git a/electron/main/ipc/ipc.controller.ts b/electron/main/ipc/ipc.controller.ts index 02bdd139..2c7c9988 100644 --- a/electron/main/ipc/ipc.controller.ts +++ b/electron/main/ipc/ipc.controller.ts @@ -5,8 +5,8 @@ import { Game } from '../game'; import { createLogger } from '../logger'; import type { SGEGameCode } from '../sge'; import { SGEServiceImpl } from '../sge'; -import type { Dispatcher } from '../types'; import type { + IpcDispatcher, IpcHandlerRegistry, IpcInvokableEvent, IpcInvokeHandler, @@ -16,12 +16,12 @@ import type { const logger = createLogger('ipc:controller'); export class IpcController { - private dispatch: Dispatcher; + private dispatch: IpcDispatcher; private accountService: AccountService; private ipcHandlerRegistry: IpcHandlerRegistry; constructor(options: { - dispatch: Dispatcher; + dispatch: IpcDispatcher; accountService: AccountService; }) { this.dispatch = options.dispatch; @@ -177,11 +177,7 @@ export class IpcController { }); const credentials = await sgeService.loginCharacter(characterName); - - const gameInstance = await Game.newInstance({ - credentials, - }); - + const gameInstance = await Game.newInstance({ credentials }); const gameEvents$ = await gameInstance.connect(); this.dispatch('game:connect', { @@ -194,11 +190,11 @@ export class IpcController { gameEvents$.subscribe({ next: (gameEvent) => { logger.debug('game service stream event', { gameEvent }); - this.dispatch('game:event', gameEvent); + this.dispatch('game:event', { gameEvent }); }, error: (error) => { logger.error('game service stream error', { error }); - this.dispatch('game:error', error); + this.dispatch('game:error', { error }); }, complete: () => { logger.debug('game service stream completed'); @@ -221,7 +217,7 @@ export class IpcController { const gameInstance = Game.getInstance(); if (gameInstance) { - this.dispatch('game:command', command); + this.dispatch('game:command', { command }); gameInstance.send(command); } else { throw new Error('[IPC:SEND_COMMAND:ERROR:GAME_INSTANCE_NOT_FOUND]'); diff --git a/electron/main/ipc/ipc.types.ts b/electron/main/ipc/ipc.types.ts index fd14bd20..bd53adec 100644 --- a/electron/main/ipc/ipc.types.ts +++ b/electron/main/ipc/ipc.types.ts @@ -1,3 +1,11 @@ +import type { + GameCommandMessage, + GameConnectMessage, + GameDisconnectMessage, + GameErrorMessage, + GameEventMessage, +} from '../../common/game'; + /** * Defines the IPC API exposed to the renderer process. * The main process must provide call-response handlers for this API. @@ -21,3 +29,16 @@ export type IpcSgeCharacter = { accountName: string; characterName: string; }; + +/** + * Defines the channels and message types that can be dispatched + * from the main process to the renderer process. + */ +export type IpcDispatcher = { + (channel: 'pong', message: 'pong'): void; + (channel: 'game:connect', message: GameConnectMessage): void; + (channel: 'game:disconnect', message: GameDisconnectMessage): void; + (channel: 'game:error', message: GameErrorMessage): void; + (channel: 'game:event', message: GameEventMessage): void; + (channel: 'game:command', message: GameCommandMessage): void; +}; diff --git a/electron/main/ipc/ipc.utils.ts b/electron/main/ipc/ipc.utils.ts index 9edd26bc..5825b651 100644 --- a/electron/main/ipc/ipc.utils.ts +++ b/electron/main/ipc/ipc.utils.ts @@ -1,8 +1,8 @@ import type { AccountService } from '../account'; import { AccountServiceImpl } from '../account'; import { Store } from '../store'; -import type { Dispatcher } from '../types'; import { IpcController } from './ipc.controller'; +import type { IpcDispatcher } from './ipc.types'; /** * I didn't like the app nor controller needing to know about @@ -12,7 +12,7 @@ import { IpcController } from './ipc.controller'; * use this method or use the IpController constructor directly. */ export function newIpcController(options: { - dispatch: Dispatcher; + dispatch: IpcDispatcher; accountService?: AccountService; }): IpcController { const { diff --git a/electron/main/types.ts b/electron/main/types.ts deleted file mode 100644 index 2a3913e8..00000000 --- a/electron/main/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Emit a message on a channel and pass arbitrary data. - */ -export type Dispatcher = (channel: string, ...args: Array) => void;