mirror of
https://github.com/k1tbyte/Wand-Enhancer.git
synced 2026-09-02 22:00:38 +00:00
13759b1db6
- reduce ASAR IO overhead with streamed archive reads, buffered copies, faster relative-path handling and placeholder integrity records - fix in-place app.asar.unpacked packing/extraction self-copy cases that caused locked-file failures - tighten JS patch discovery with candidate bundle filters and search hints - require prebuilt remote-panel dist artifacts and clean up embedded bridge/script packaging - add unified build entrypoints for PowerShell, cmd and bash and move native CMake output under .tmp - add release metadata validation, changelog section extraction, pre-commit hook and GitHub Actions validation/release pipelines - make CHANGELOG the source of truth for release notes and document the tag-driven release flow - add updater release notes UI with latest/full changelog loading and localize the new update strings - modularize bridge renderer scripts, add installed apps and game status sync, and support remote launch/stop commands - centralize bridge protocol, IPC and WebSocket constants and improve LAN IP selection for QR pairing - refactor remote panel controls/state enums, persist accent color, polish library/session UI and refresh assets
156 lines
4.3 KiB
TypeScript
156 lines
4.3 KiB
TypeScript
import { CLIENT_VERSION } from './constants';
|
|
import {
|
|
type HelloMessage,
|
|
type IncomingMessage,
|
|
type OutgoingMessage,
|
|
PROTOCOL_VERSION,
|
|
type RemoteCommandMessage,
|
|
type SetValueMessage,
|
|
isIncomingMessage,
|
|
} from './protocol';
|
|
|
|
type SocketHandlers = {
|
|
onConnecting: () => void;
|
|
onOpen: () => void;
|
|
onMessage: (message: IncomingMessage) => void;
|
|
onClose: () => void;
|
|
onError: (message: string) => void;
|
|
};
|
|
|
|
export class PanelSocketClient {
|
|
private socket: WebSocket | null = null;
|
|
private intentionalDisconnect = false;
|
|
|
|
constructor(
|
|
private readonly url: string,
|
|
private readonly handlers: SocketHandlers,
|
|
) { }
|
|
|
|
connect(pairingToken?: string): void {
|
|
this.disconnect();
|
|
this.intentionalDisconnect = false;
|
|
this.handlers.onConnecting();
|
|
|
|
const socket = new WebSocket(this.url);
|
|
this.socket = socket;
|
|
|
|
socket.addEventListener('open', () => {
|
|
this.handlers.onOpen();
|
|
this.send(this.createHelloMessage(pairingToken));
|
|
});
|
|
|
|
socket.addEventListener('message', (event) => this.handleMessage(event));
|
|
|
|
socket.addEventListener('close', () => {
|
|
if (this.socket === socket) {
|
|
this.socket = null;
|
|
}
|
|
|
|
if (!this.intentionalDisconnect) {
|
|
this.handlers.onClose();
|
|
}
|
|
});
|
|
|
|
socket.addEventListener('error', () => {
|
|
this.handlers.onError('WebSocket connection failed.');
|
|
});
|
|
}
|
|
|
|
disconnect(): void {
|
|
this.intentionalDisconnect = true;
|
|
this.socket?.close();
|
|
this.socket = null;
|
|
}
|
|
|
|
isOpen(): boolean {
|
|
return Boolean(this.socket && this.socket.readyState === WebSocket.OPEN);
|
|
}
|
|
|
|
send(message: OutgoingMessage): boolean {
|
|
const socket = this.socket;
|
|
if (!socket || socket.readyState !== WebSocket.OPEN) {
|
|
return false;
|
|
}
|
|
|
|
socket.send(JSON.stringify(message));
|
|
return true;
|
|
}
|
|
|
|
setValue(trainerId: string, target: string, value: unknown, cheatId?: string): boolean {
|
|
const message: SetValueMessage = {
|
|
type: 'set_value',
|
|
version: PROTOCOL_VERSION,
|
|
requestId: `set_${target}_${Date.now()}`,
|
|
payload: {
|
|
trainerId,
|
|
target,
|
|
value,
|
|
cheatId,
|
|
},
|
|
};
|
|
|
|
return this.send(message);
|
|
}
|
|
|
|
launchGame(gameId: string, titleId?: string): boolean {
|
|
const message: RemoteCommandMessage = {
|
|
type: 'remote_command',
|
|
version: PROTOCOL_VERSION,
|
|
requestId: `command_launch_${Date.now()}`,
|
|
payload: {
|
|
action: 'launch',
|
|
gameId,
|
|
titleId,
|
|
},
|
|
};
|
|
|
|
return this.send(message);
|
|
}
|
|
|
|
stopPlaying(gameId?: string, titleId?: string): boolean {
|
|
const message: RemoteCommandMessage = {
|
|
type: 'remote_command',
|
|
version: PROTOCOL_VERSION,
|
|
requestId: `command_stop_${Date.now()}`,
|
|
payload: {
|
|
action: 'stop',
|
|
gameId,
|
|
titleId,
|
|
},
|
|
};
|
|
|
|
return this.send(message);
|
|
}
|
|
|
|
private createHelloMessage(pairingToken?: string): HelloMessage {
|
|
return {
|
|
type: 'hello',
|
|
version: PROTOCOL_VERSION,
|
|
requestId: `hello_${Date.now()}`,
|
|
payload: {
|
|
client: 'mobile-web',
|
|
clientVersion: CLIENT_VERSION,
|
|
pairingToken,
|
|
capabilities: {
|
|
supportsDeltaValues: true,
|
|
supportsTrainerSwitch: true,
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
private handleMessage(event: MessageEvent): void {
|
|
try {
|
|
const parsed = JSON.parse(String(event.data)) as unknown;
|
|
if (!isIncomingMessage(parsed)) {
|
|
this.handlers.onError('Received an invalid protocol message.');
|
|
return;
|
|
}
|
|
|
|
this.handlers.onMessage(parsed);
|
|
} catch (error) {
|
|
this.handlers.onError(error instanceof Error ? error.message : 'Failed to parse websocket message.');
|
|
}
|
|
}
|
|
}
|