diff --git a/src/core/apis/packet.ts b/src/core/apis/packet.ts index f112da87..3d743ad4 100644 --- a/src/core/apis/packet.ts +++ b/src/core/apis/packet.ts @@ -2,7 +2,6 @@ import * as crypto from 'crypto'; import * as os from 'os'; import { ChatType, InstanceContext, NapCatCore } from '..'; import offset from '@/core/external/offset.json'; -import { PacketClient, RecvPacketData } from '@/core/packet/client'; import { PacketSession } from "@/core/packet/session"; import { OidbPacket, PacketHexStr } from "@/core/packet/packer"; import { NapProtoEncodeStructType, NapProtoDecodeStructType, NapProtoMsg } from '@/core/packet/proto/NapProto'; @@ -25,6 +24,7 @@ import { AIVoiceChatType, AIVoiceItemList } from "@/core/packet/entities/aiChat" import { OidbSvcTrpcTcp0X929B_0Resp, OidbSvcTrpcTcp0X929D_0Resp } from "@/core/packet/proto/oidb/Oidb.0x929"; import { IndexNode, MsgInfo } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq"; import { NTV2RichMediaResp } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaResp"; +import { RecvPacketData } from "@/core/packet/client/client"; interface OffsetType { @@ -40,7 +40,6 @@ export class NTQQPacketApi { context: InstanceContext; core: NapCatCore; logger: LogWrapper; - serverUrl: string | undefined; qqVersion: string | undefined; packetSession: PacketSession | undefined; @@ -50,9 +49,8 @@ export class NTQQPacketApi { this.logger = core.context.logger; this.packetSession = undefined; const config = this.core.configLoader.configData; - if (config && config.packetServer && config.packetServer.length > 0) { - const serverUrl = this.core.configLoader.configData.packetServer ?? '127.0.0.1:8086'; - this.InitSendPacket(serverUrl, this.context.basicInfoWrapper.getFullQQVesion()) + if (config) { + this.InitSendPacket(this.context.basicInfoWrapper.getFullQQVesion()) .then() .catch(this.core.context.logger.logError.bind(this.core.context.logger)); } else { @@ -64,17 +62,14 @@ export class NTQQPacketApi { return this.packetSession?.client.available ?? false; } - async InitSendPacket(serverUrl: string, qqversion: string) { - this.serverUrl = serverUrl; + async InitSendPacket(qqversion: string) { this.qqVersion = qqversion; - const offsetTable: OffsetType = offset; - const table = offsetTable[qqversion + '-' + os.arch()]; + const table = typedOffset[qqversion + '-' + os.arch()]; if (!table) { this.logger.logError('PacketServer Offset table not found for QQVersion: ', qqversion + '-' + os.arch()); return false; } - const url = 'ws://' + this.serverUrl + '/ws'; - this.packetSession = new PacketSession(this.core.context.logger, new PacketClient(url, this.core)); + this.packetSession = new PacketSession(this.core); const cb = () => { if (this.packetSession && this.packetSession.client) { this.packetSession.client.init(process.pid, table.recv, table.send).then().catch(this.logger.logError.bind(this.logger)); diff --git a/src/core/packet/client.ts b/src/core/packet/client.ts deleted file mode 100644 index 49400798..00000000 --- a/src/core/packet/client.ts +++ /dev/null @@ -1,183 +0,0 @@ -import { LogWrapper } from "@/common/log"; -import { LRUCache } from "@/common/lru-cache"; -import WebSocket, { Data } from "ws"; -import crypto, { createHash } from "crypto"; -import { NapCatCore } from "@/core"; -import { OidbPacket, PacketHexStr } from "@/core/packet/packer"; - -export interface RecvPacket { - type: string, // 仅recv - trace_id_md5?: string, - data: RecvPacketData -} - -export interface RecvPacketData { - seq: number - cmd: string - hex_data: string -} - -export class PacketClient { - private websocket: WebSocket | undefined; - private isConnected: boolean = false; - private reconnectAttempts: number = 0; - private readonly maxReconnectAttempts: number = 60;//现在暂时不可配置 - private readonly cb = new LRUCache Promise>(500); // trace_id-type callback - private readonly clientUrl: string = ''; - readonly napCatCore: NapCatCore; - private readonly logger: LogWrapper; - - constructor(url: string, core: NapCatCore) { - this.clientUrl = url; - this.napCatCore = core; - this.logger = core.context.logger; - } - - get available(): boolean { - return this.isConnected && this.websocket !== undefined; - } - - private randText(len: number) { - let text = ''; - const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - for (let i = 0; i < len; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - return text; - } - - connect(cb: any): Promise { - return new Promise((resolve, reject) => { - //this.logger.log.bind(this.logger)(`[Core] [Packet Server] Attempting to connect to ${this.clientUrl}`); - this.websocket = new WebSocket(this.clientUrl); - this.websocket.on('error', (err) => { }/*this.logger.logError.bind(this.logger)('[Core] [Packet Server] Error:', err.message)*/); - - this.websocket.onopen = () => { - this.isConnected = true; - this.reconnectAttempts = 0; - this.logger.log.bind(this.logger)(`[Core] [Packet Server] 已连接到 ${this.clientUrl}`); - cb(); - resolve(); - }; - - this.websocket.onerror = (error) => { - //this.logger.logError.bind(this.logger)(`WebSocket error: ${error}`); - reject(new Error(`${error.message}`)); - }; - - this.websocket.onmessage = (event) => { - // const message = JSON.parse(event.data.toString()); - // console.log("Received message:", message); - this.handleMessage(event.data).then().catch(); - }; - - this.websocket.onclose = () => { - this.isConnected = false; - //this.logger.logWarn.bind(this.logger)(`[Core] [Packet Server] Disconnected from ${this.clientUrl}`); - this.attemptReconnect(cb); - }; - }); - } - - private attemptReconnect(cb: any): void { - try { - if (this.reconnectAttempts < this.maxReconnectAttempts) { - this.reconnectAttempts++; - setTimeout(() => { - this.connect(cb).catch((error) => { - this.logger.logError.bind(this.logger)(`[Core] [Packet Server] 尝试重连失败:${error.message}`); - }); - }, 5000 * this.reconnectAttempts); - } else { - this.logger.logError.bind(this.logger)(`[Core] [Packet Server] ${this.clientUrl} 已达到最大重连次数!`); - } - } catch (error: any) { - this.logger.logError.bind(this.logger)(`[Core] [Packet Server] 重连时出错: ${error.message}`); - } - } - - private async registerCallback(trace_id: string, type: string, callback: (json: RecvPacketData) => Promise): Promise { - this.cb.put(createHash('md5').update(trace_id).digest('hex') + type, callback); - } - - async init(pid: number, recv: string, send: string): Promise { - if (!this.isConnected || !this.websocket) { - throw new Error("WebSocket is not connected"); - } - const initMessage = { - action: 'init', - pid: pid, - recv: recv, - send: send - }; - this.websocket.send(JSON.stringify(initMessage)); - } - - private async sendCommand(cmd: string, data: string, trace_id: string, rsp: boolean = false, timeout: number = 20000, sendcb: (json: RecvPacketData) => void = () => { - }): Promise { - return new Promise((resolve, reject) => { - if (!this.isConnected || !this.websocket) { - throw new Error("WebSocket is not connected"); - } - const commandMessage = { - action: 'send', - cmd: cmd, - data: data, - trace_id: trace_id - }; - this.websocket.send(JSON.stringify(commandMessage)); - if (rsp) { - this.registerCallback(trace_id, 'recv', async (json: RecvPacketData) => { - clearTimeout(timeoutHandle); - resolve(json); - }); - } - this.registerCallback(trace_id, 'send', async (json: RecvPacketData) => { - sendcb(json); - if (!rsp) { - clearTimeout(timeoutHandle); - resolve(json); - } - }); - const timeoutHandle = setTimeout(() => { - reject(new Error(`sendCommand timed out after ${timeout} ms for ${cmd} with trace_id ${trace_id}`)); - }, timeout); - }); - } - - private async handleMessage(message: Data): Promise { - try { - const json: RecvPacket = JSON.parse(message.toString()); - const trace_id_md5 = json.trace_id_md5; - const action = json?.type ?? 'init'; - const event = this.cb.get(trace_id_md5 + action); - if (event) { - await event(json.data); - } - //console.log("Received message:", json); - } catch (error) { - this.logger.logError.bind(this.logger)(`Error parsing message: ${error}`); - } - } - - async sendPacket(cmd: string, data: PacketHexStr, rsp = false): Promise { - // wtfk tx - // 校验失败和异常 可能返回undefined - return new Promise((resolve, reject) => { - if (!this.available) { - this.logger.logError('NapCat.Packet 未初始化!'); - return undefined; - } - const md5 = crypto.createHash('md5').update(data).digest('hex'); - const trace_id = (this.randText(4) + md5 + data).slice(0, data.length / 2); - this.sendCommand(cmd, data, trace_id, rsp, 20000, async () => { - // await sleep(10); - await this.napCatCore.context.session.getMsgService().sendSsoCmdReqByContend(cmd, trace_id); - }).then((res) => resolve(res)).catch((e: Error) => reject(e)); - }); - } - - async sendOidbPacket(pkt: OidbPacket, rsp = false): Promise { - return this.sendPacket(pkt.cmd, pkt.data, rsp); - } -} diff --git a/src/core/packet/native.ts b/src/core/packet/client/client.ts similarity index 54% rename from src/core/packet/native.ts rename to src/core/packet/client/client.ts index d5e16cb2..fae30594 100644 --- a/src/core/packet/native.ts +++ b/src/core/packet/client/client.ts @@ -1,35 +1,37 @@ -import { LogWrapper } from "@/common/log"; import { LRUCache } from "@/common/lru-cache"; -import crypto, { createHash } from "crypto"; import { NapCatCore } from "@/core"; +import { LogWrapper } from "@/common/log"; +import crypto, { createHash } from "crypto"; import { OidbPacket, PacketHexStr } from "@/core/packet/packer"; -import path, { dirname } from "path"; -import { fileURLToPath } from "url"; -import fs from "fs"; -import { constants } from "os"; -import { console } from "inspector"; +import { NapCatConfig } from "@/core/helper/config"; -export interface RecvPacketData { - seq: number; - cmd: string; - hex_data: string; +export interface RecvPacket { + type: string, // 仅recv + trace_id_md5?: string, + data: RecvPacketData } -export class NativePacketClient { - private isInit: boolean = false; - private readonly cb = new LRUCache Promise>(500); // trace_id-type callback - readonly napCatCore: NapCatCore; - private readonly logger: LogWrapper; - private supportedPlatforms = ['win32.x64']; - private MoeHooExport: any = { exports: {} }; +export interface RecvPacketData { + seq: number + cmd: string + hex_data: string +} - constructor(core: NapCatCore) { +export abstract class PacketClient { + readonly napCatCore: NapCatCore; + protected readonly logger: LogWrapper; + protected readonly cb = new LRUCache Promise>(500); // trace_id-type callback + protected isAvailable: boolean = false; + protected config: NapCatConfig; + + protected constructor(core: NapCatCore) { this.napCatCore = core; this.logger = core.context.logger; + this.config = core.configLoader.configData; } get available(): boolean { - return this.isInit; + return this.isAvailable; } private randText(len: number): string { @@ -41,55 +43,37 @@ export class NativePacketClient { return text; } + static create(core: NapCatCore): PacketClient { + throw new Error("Must be implemented by subclasses"); + } + + static compatibilityScore(logger: LogWrapper): number { + throw new Error("Must be implemented by subclasses"); + } + + abstract init(pid: number, recv: string, send: string): Promise; + + abstract connect(cb: () => void): Promise; + + abstract sendCommandImpl(cmd: string, data: string, trace_id: string): void; + private async registerCallback(trace_id: string, type: string, callback: (json: RecvPacketData) => Promise): Promise { this.cb.put(createHash('md5').update(trace_id).digest('hex') + type, callback); } - async init(pid: number, recv: string, send: string): Promise { - const platform = process.platform + '.' + process.arch; - if (!this.supportedPlatforms.includes(platform)) { - throw new Error(`Unsupported platform: ${platform}`); - } - - const moehoo_path = path.join(dirname(fileURLToPath(import.meta.url)), './moehoo/moehoo.' + platform + '.node'); - if (!fs.existsSync(moehoo_path)) { - throw new Error(`moehoo.${platform}.node not found`); - } - - process.dlopen(this.MoeHooExport, moehoo_path, constants.dlopen.RTLD_LAZY); - this.MoeHooExport.exports.InitHook(pid, recv, send, (type: number, uin: string, seq: number, cmd: string, hex_data: string) => { - const callback = this.cb.get(createHash('md5').update(Buffer.from(hex_data, 'hex')).digest('hex') + (type === 0 ? 'send' : 'recv')); - if (callback) { - callback({ seq, cmd, hex_data }); - } else { - this.logger.logError(`Callback not found for hex_data: ${hex_data}`); - } - console.log('type:', type, 'uin:', uin, 'seq:', seq, 'cmd:', cmd, 'hex_data:', hex_data); - }); - } - - private async sendCommand( - cmd: string, - data: string, - trace_id: string, - rsp: boolean = false, - timeout: number = 20000, - sendcb: (json: RecvPacketData) => void = () => { } - ): Promise { + private async sendCommand(cmd: string, data: string, trace_id: string, rsp: boolean = false, timeout: number = 20000, sendcb: (json: RecvPacketData) => void = () => { + }): Promise { return new Promise((resolve, reject) => { - if (!this.available) { - throw new Error("MoeHoo is not Init"); + if (!this.isAvailable) { + throw new Error("WebSocket is not connected"); } - - this.MoeHooExport.exports.SendPacket(cmd, data, crypto.createHash('md5').update(trace_id).digest('hex')); - + this.sendCommandImpl(cmd, data, trace_id); if (rsp) { this.registerCallback(trace_id, 'recv', async (json: RecvPacketData) => { clearTimeout(timeoutHandle); resolve(json); }); } - this.registerCallback(trace_id, 'send', async (json: RecvPacketData) => { sendcb(json); if (!rsp) { @@ -97,7 +81,6 @@ export class NativePacketClient { resolve(json); } }); - const timeoutHandle = setTimeout(() => { reject(new Error(`sendCommand timed out after ${timeout} ms for ${cmd} with trace_id ${trace_id}`)); }, timeout); @@ -123,4 +106,4 @@ export class NativePacketClient { async sendOidbPacket(pkt: OidbPacket, rsp = false): Promise { return this.sendPacket(pkt.cmd, pkt.data, rsp); } -} \ No newline at end of file +} diff --git a/src/core/packet/client/nativeClient.ts b/src/core/packet/client/nativeClient.ts new file mode 100644 index 00000000..0ee13e0c --- /dev/null +++ b/src/core/packet/client/nativeClient.ts @@ -0,0 +1,67 @@ +import crypto, { createHash } from "crypto"; +import { NapCatCore } from "@/core"; +import path, { dirname } from "path"; +import { fileURLToPath } from "url"; +import fs from "fs"; +import { console } from "inspector"; +import { PacketClient } from "@/core/packet/client/client"; +import { constants, platform, type } from "node:os"; +import { LogWrapper } from "@/common/log"; + +export class NativePacketClient extends PacketClient { + static supportedPlatforms = ['win32.x64']; + private MoeHooExport: any = { exports: {} }; + + protected constructor(core: NapCatCore) { + super(core); + } + + get available(): boolean { + return this.isAvailable; + } + + static compatibilityScore(logger: LogWrapper): number { + const platform = process.platform + '.' + process.arch; + if (!this.supportedPlatforms.includes(platform)) { + logger.logError(`[NativePacketClient] Unsupported platform: ${platform}`); + return 0; + } + const moehoo_path = path.join(dirname(fileURLToPath(import.meta.url)), './moehoo/moehoo.' + platform + '.node'); + if (!fs.existsSync(moehoo_path)) { + logger.logError(`[NativePacketClient] Missing moehoo binary: ${moehoo_path}`); + return 0; + } + return 10; + } + + static create(core: NapCatCore): NativePacketClient { + return new NativePacketClient(core); + } + + async init(pid: number, recv: string, send: string): Promise { + const platform = process.platform + '.' + process.arch; + const moehoo_path = path.join(dirname(fileURLToPath(import.meta.url)), './moehoo/moehoo.' + platform + '.node'); + process.dlopen(this.MoeHooExport, moehoo_path, constants.dlopen.RTLD_LAZY); + this.MoeHooExport.exports.InitHook(pid, recv, send, (type: number, uin: string, seq: number, cmd: string, hex_data: string) => { + const callback = this.cb.get(createHash('md5').update(Buffer.from(hex_data, 'hex')).digest('hex') + (type === 0 ? 'send' : 'recv')); + if (callback) { + callback({ seq, cmd, hex_data }); + } else { + this.logger.logError(`Callback not found for hex_data: ${hex_data}`); + } + // TODO: cannot use console.log here, fxxk tx + // Error [ERR_INSPECTOR_NOT_AVAILABLE]: Inspector is not available + // console.log('type:', type, 'uin:', uin, 'seq:', seq, 'cmd:', cmd, 'hex_data:', hex_data); + }); + this.isAvailable = true; + } + + sendCommandImpl(cmd: string, data: string, trace_id: string): void { + this.MoeHooExport.exports.SendPacket(cmd, data, crypto.createHash('md5').update(trace_id).digest('hex')); + } + + connect(cb: () => void): Promise { + cb(); + return Promise.resolve(); + } +} diff --git a/src/core/packet/client/wsClient.ts b/src/core/packet/client/wsClient.ts new file mode 100644 index 00000000..bf34e0e0 --- /dev/null +++ b/src/core/packet/client/wsClient.ts @@ -0,0 +1,113 @@ +import { Data, WebSocket } from "ws"; +import { NapCatCore } from "@/core"; +import { PacketClient, RecvPacket } from "@/core/packet/client/client"; +import { LogWrapper } from "@/common/log"; + +export class wsPacketClient extends PacketClient { + private websocket: WebSocket | undefined; + private reconnectAttempts: number = 0; + private readonly maxReconnectAttempts: number = 60; // 现在暂时不可配置 + private readonly clientUrl: string = ''; + private clientUrlWrap: (url: string) => string = (url: string) => `ws://${url}/ws`; + + protected constructor(core: NapCatCore) { + super(core); + this.clientUrl = this.clientUrlWrap(this.config.packetServer ?? '127.0.0.1:8086'); + } + + static compatibilityScore(logger: LogWrapper): number { + return 10; + } + + static create(core: NapCatCore): wsPacketClient { + return new wsPacketClient(core); + } + + connect(cb: () => void): Promise { + return new Promise((resolve, reject) => { + //this.logger.log.bind(this.logger)(`[Core] [Packet Server] Attempting to connect to ${this.clientUrl}`); + this.websocket = new WebSocket(this.clientUrl); + this.websocket.on('error', (err) => { }/*this.logger.logError.bind(this.logger)('[Core] [Packet Server] Error:', err.message)*/); + + this.websocket.onopen = () => { + this.isAvailable = true; + this.reconnectAttempts = 0; + this.logger.log.bind(this.logger)(`[Core] [Packet Server] 已连接到 ${this.clientUrl}`); + cb(); + resolve(); + }; + + this.websocket.onerror = (error) => { + //this.logger.logError.bind(this.logger)(`WebSocket error: ${error}`); + reject(new Error(`${error.message}`)); + }; + + this.websocket.onmessage = (event) => { + // const message = JSON.parse(event.data.toString()); + // console.log("Received message:", message); + this.handleMessage(event.data).then().catch(); + }; + + this.websocket.onclose = () => { + this.isAvailable = false; + //this.logger.logWarn.bind(this.logger)(`[Core] [Packet Server] Disconnected from ${this.clientUrl}`); + this.attemptReconnect(cb); + }; + }); + } + + private attemptReconnect(cb: any): void { + try { + if (this.reconnectAttempts < this.maxReconnectAttempts) { + this.reconnectAttempts++; + setTimeout(() => { + this.connect(cb).catch((error) => { + this.logger.logError.bind(this.logger)(`[Core] [Packet Server] 尝试重连失败:${error.message}`); + }); + }, 5000 * this.reconnectAttempts); + } else { + this.logger.logError.bind(this.logger)(`[Core] [Packet Server] ${this.clientUrl} 已达到最大重连次数!`); + } + } catch (error: any) { + this.logger.logError.bind(this.logger)(`[Core] [Packet Server] 重连时出错: ${error.message}`); + } + } + + async init(pid: number, recv: string, send: string): Promise { + if (!this.isAvailable || !this.websocket) { + throw new Error("WebSocket is not connected"); + } + const initMessage = { + action: 'init', + pid: pid, + recv: recv, + send: send + }; + this.websocket.send(JSON.stringify(initMessage)); + } + + sendCommandImpl(cmd: string, data: string, trace_id: string) : void { + const commandMessage = { + action: 'send', + cmd: cmd, + data: data, + trace_id: trace_id + }; + this.websocket!.send(JSON.stringify(commandMessage)); + } + + private async handleMessage(message: Data): Promise { + try { + const json: RecvPacket = JSON.parse(message.toString()); + const trace_id_md5 = json.trace_id_md5; + const action = json?.type ?? 'init'; + const event = this.cb.get(trace_id_md5 + action); + if (event) { + await event(json.data); + } + //console.log("Received message:", json); + } catch (error) { + this.logger.logError.bind(this.logger)(`Error parsing message: ${error}`); + } + } +} diff --git a/src/core/packet/highway/session.ts b/src/core/packet/highway/session.ts index 997c5aa9..bb870164 100644 --- a/src/core/packet/highway/session.ts +++ b/src/core/packet/highway/session.ts @@ -1,7 +1,6 @@ import * as fs from "node:fs"; import { ChatType, Peer } from "@/core"; import { LogWrapper } from "@/common/log"; -import { PacketClient } from "@/core/packet/client"; import { PacketPacker } from "@/core/packet/packer"; import { NapProtoMsg } from "@/core/packet/proto/NapProto"; import { HttpConn0x6ff_501Response } from "@/core/packet/proto/action/action"; @@ -19,6 +18,7 @@ import { int32ip2str, oidbIpv4s2HighwayIpv4s } from "@/core/packet/highway/utils import { calculateSha1, calculateSha1StreamBytes, computeMd5AndLengthWithLimit } from "@/core/packet/utils/crypto/hash"; import { OidbSvcTrpcTcp0x6D6Response } from "@/core/packet/proto/oidb/Oidb.0x6D6"; import { OidbSvcTrpcTcp0XE37_800Response, OidbSvcTrpcTcp0XE37Response } from "@/core/packet/proto/oidb/Oidb.0XE37_800"; +import { PacketClient } from "@/core/packet/client/client"; export const BlockSize = 1024 * 1024; diff --git a/src/core/packet/packer.ts b/src/core/packet/packer.ts index 848742a4..4e738306 100644 --- a/src/core/packet/packer.ts +++ b/src/core/packet/packer.ts @@ -22,7 +22,6 @@ import { PacketMsg } from "@/core/packet/message/message"; import { OidbSvcTrpcTcp0x6D6 } from "@/core/packet/proto/oidb/Oidb.0x6D6"; import { OidbSvcTrpcTcp0XE37_1200 } from "@/core/packet/proto/oidb/Oidb.0xE37_1200"; import { PacketMsgConverter } from "@/core/packet/message/converter"; -import { PacketClient } from "@/core/packet/client"; import { OidbSvcTrpcTcp0XE37_1700 } from "@/core/packet/proto/oidb/Oidb.0xE37_1700"; import { OidbSvcTrpcTcp0XE37_800 } from "@/core/packet/proto/oidb/Oidb.0XE37_800"; import { OidbSvcTrpcTcp0XEB7 } from "./proto/oidb/Oidb.0xEB7"; @@ -30,6 +29,7 @@ import { MiniAppReqParams } from "@/core/packet/entities/miniApp"; import { MiniAppAdaptShareInfoReq } from "@/core/packet/proto/action/miniAppAdaptShareInfo"; import { AIVoiceChatType } from "@/core/packet/entities/aiChat"; import { OidbSvcTrpcTcp0X929B_0, OidbSvcTrpcTcp0X929D_0 } from "@/core/packet/proto/oidb/Oidb.0x929"; +import { PacketClient } from "@/core/packet/client/client"; export type PacketHexStr = string & { readonly hexNya: unique symbol }; diff --git a/src/core/packet/session.ts b/src/core/packet/session.ts index 7e636281..208997dc 100644 --- a/src/core/packet/session.ts +++ b/src/core/packet/session.ts @@ -1,7 +1,19 @@ -import { PacketClient } from "@/core/packet/client"; import { PacketHighwaySession } from "@/core/packet/highway/session"; import { LogWrapper } from "@/common/log"; import { PacketPacker } from "@/core/packet/packer"; +import { PacketClient } from "@/core/packet/client/client"; +import { NativePacketClient } from "@/core/packet/client/nativeClient"; +import { wsPacketClient } from "@/core/packet/client/wsClient"; +import { NapCatCore } from "@/core"; + +type clientPriority = { + [key: number]: typeof PacketClient; +} + +const clientPriority: clientPriority = { + 10: NativePacketClient, + 1: wsPacketClient, +}; export class PacketSession { readonly logger: LogWrapper; @@ -9,10 +21,29 @@ export class PacketSession { readonly packer: PacketPacker; readonly highwaySession: PacketHighwaySession; - constructor(logger: LogWrapper, client: PacketClient) { - this.logger = logger; - this.client = client; + constructor(core: NapCatCore) { + this.logger = core.context.logger; + this.client = this.judgeClient(core); this.packer = new PacketPacker(this.logger, this.client); this.highwaySession = new PacketHighwaySession(this.logger, this.client, this.packer); } + + private judgeClient(core: NapCatCore): PacketClient { + let selectedClient: typeof PacketClient | null = null; + let maxScore = -1; + for (const key in clientPriority) { + const priority = parseInt(key); + const ClientClass = clientPriority[priority]; + const score = priority * ClientClass.compatibilityScore(core.context.logger); + if (score > maxScore) { + maxScore = score; + selectedClient = ClientClass; + } + } + if (!selectedClient) { + throw new Error("No compatible PacketClient found"); + } + this.logger.log(`[Packet] 自动选择了: ${selectedClient.name}`); + return selectedClient.create(core); + } }