1.0
This commit is contained in:
		
							
								
								
									
										779
									
								
								xyzw_web_helper-main开源源码更新/src/utils/bonProtocol.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										779
									
								
								xyzw_web_helper-main开源源码更新/src/utils/bonProtocol.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,779 @@
 | 
			
		||||
/**
 | 
			
		||||
 * BON (Binary Object Notation) 协议实现
 | 
			
		||||
 * 基于提供的真实 BON 源码重新实现
 | 
			
		||||
 */
 | 
			
		||||
import lz4 from 'lz4js';
 | 
			
		||||
 | 
			
		||||
// -----------------------------
 | 
			
		||||
// BON 编解码器核心实现
 | 
			
		||||
// -----------------------------
 | 
			
		||||
 | 
			
		||||
export class Int64 {
 | 
			
		||||
  constructor(high, low) {
 | 
			
		||||
    this.high = high;
 | 
			
		||||
    this.low = low;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class DataReader {
 | 
			
		||||
  constructor(bytes) {
 | 
			
		||||
    this._data = bytes || new Uint8Array(0);
 | 
			
		||||
    this._view = null;
 | 
			
		||||
    this.position = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get data() { return this._data; }
 | 
			
		||||
  get dataView() {
 | 
			
		||||
    return this._view || (this._view = new DataView(this._data.buffer, this._data.byteOffset, this._data.byteLength));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  reset(bytes) {
 | 
			
		||||
    this._data = bytes;
 | 
			
		||||
    this.position = 0;
 | 
			
		||||
    this._view = null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  validate(n) {
 | 
			
		||||
    if (this.position + n > this._data.length) {
 | 
			
		||||
      console.error('read eof');
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  readUInt8() {
 | 
			
		||||
    if (!this.validate(1)) return;
 | 
			
		||||
    return this._data[this.position++];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  readInt16() {
 | 
			
		||||
    if (!this.validate(2)) return;
 | 
			
		||||
    const v = this._data[this.position++] | (this._data[this.position++] << 8);
 | 
			
		||||
    return (v << 16) >> 16;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  readInt32() {
 | 
			
		||||
    if (!this.validate(4)) return;
 | 
			
		||||
    const v = this._data[this.position++] | (this._data[this.position++] << 8) | (this._data[this.position++] << 16) | (this._data[this.position++] << 24);
 | 
			
		||||
    return v | 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  readInt64() {
 | 
			
		||||
    const lo = this.readInt32();
 | 
			
		||||
    if (lo === undefined) return;
 | 
			
		||||
    let _lo = lo;
 | 
			
		||||
    if (_lo < 0) _lo += 0x100000000;
 | 
			
		||||
    const hi = this.readInt32();
 | 
			
		||||
    if (hi === undefined) return;
 | 
			
		||||
    return _lo + 0x100000000 * hi;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  readFloat32() {
 | 
			
		||||
    if (!this.validate(4)) return;
 | 
			
		||||
    const v = this.dataView.getFloat32(this.position, true);
 | 
			
		||||
    this.position += 4;
 | 
			
		||||
    return v;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  readFloat64() {
 | 
			
		||||
    if (!this.validate(8)) return;
 | 
			
		||||
    const v = this.dataView.getFloat64(this.position, true);
 | 
			
		||||
    this.position += 8;
 | 
			
		||||
    return v;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  read7BitInt() {
 | 
			
		||||
    let value = 0;
 | 
			
		||||
    let shift = 0;
 | 
			
		||||
    let b = 0;
 | 
			
		||||
    let count = 0;
 | 
			
		||||
    do {
 | 
			
		||||
      if (count++ === 35) throw new Error('Format_Bad7BitInt32');
 | 
			
		||||
      b = this.readUInt8();
 | 
			
		||||
      value |= (b & 0x7F) << shift;
 | 
			
		||||
      shift += 7;
 | 
			
		||||
    } while ((b & 0x80) !== 0);
 | 
			
		||||
    return value >>> 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  readUTF() {
 | 
			
		||||
    const len = this.read7BitInt();
 | 
			
		||||
    return this.readUTFBytes(len);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  readUint8Array(length, copy = false) {
 | 
			
		||||
    const start = this.position;
 | 
			
		||||
    const end = start + length;
 | 
			
		||||
    const out = copy ? this._data.slice(start, end) : this._data.subarray(start, end);
 | 
			
		||||
    this.position = end;
 | 
			
		||||
    return out;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  readUTFBytes(length) {
 | 
			
		||||
    if (length === 0) return '';
 | 
			
		||||
    if (!this.validate(length)) return;
 | 
			
		||||
    const str = new TextDecoder('utf8').decode(this._data.subarray(this.position, this.position + length));
 | 
			
		||||
    this.position += length;
 | 
			
		||||
    return str;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let _shared = new Uint8Array(524288); // 512 KB initial buffer
 | 
			
		||||
 | 
			
		||||
export class DataWriter {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    this.position = 0;
 | 
			
		||||
    this._view = null;
 | 
			
		||||
    this.data = _shared;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get dataView() {
 | 
			
		||||
    return this._view || (this._view = new DataView(this.data.buffer, 0, this.data.byteLength));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  reset() {
 | 
			
		||||
    this.data = _shared;
 | 
			
		||||
    this._view = null;
 | 
			
		||||
    this.position = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ensureBuffer(size) {
 | 
			
		||||
    if (this.position + size <= _shared.byteLength) return;
 | 
			
		||||
    const prev = _shared;
 | 
			
		||||
    const need = this.position + size;
 | 
			
		||||
    const nextLen = Math.max(Math.floor((_shared.byteLength * 12) / 10), need);
 | 
			
		||||
    _shared = new Uint8Array(nextLen);
 | 
			
		||||
    _shared.set(prev, 0);
 | 
			
		||||
    this.data = _shared;
 | 
			
		||||
    this._view = null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  writeInt8(v) {
 | 
			
		||||
    this.ensureBuffer(1);
 | 
			
		||||
    this.data[this.position++] = v | 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  writeInt16(v) {
 | 
			
		||||
    this.ensureBuffer(2);
 | 
			
		||||
    this.data[this.position++] = v | 0;
 | 
			
		||||
    this.data[this.position++] = (v >> 8) & 0xFF;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  writeInt32(v) {
 | 
			
		||||
    this.ensureBuffer(4);
 | 
			
		||||
    this.data[this.position++] = v | 0;
 | 
			
		||||
    this.data[this.position++] = (v >> 8) & 0xFF;
 | 
			
		||||
    this.data[this.position++] = (v >> 16) & 0xFF;
 | 
			
		||||
    this.data[this.position++] = (v >> 24) & 0xFF;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  writeInt64(v) {
 | 
			
		||||
    this.writeInt32(v);
 | 
			
		||||
    if (v < 0) {
 | 
			
		||||
      this.writeInt32(~Math.floor((-v) / 0x100000000));
 | 
			
		||||
    } else {
 | 
			
		||||
      this.writeInt32(Math.floor(v / 0x100000000) | 0);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  writeFloat32(v) {
 | 
			
		||||
    this.ensureBuffer(4);
 | 
			
		||||
    this.dataView.setFloat32(this.position, v, true);
 | 
			
		||||
    this.position += 4;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  writeFloat64(v) {
 | 
			
		||||
    this.ensureBuffer(8);
 | 
			
		||||
    this.dataView.setFloat64(this.position, v, true);
 | 
			
		||||
    this.position += 8;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _write7BitInt(v) {
 | 
			
		||||
    let n = v >>> 0;
 | 
			
		||||
    while (n >= 0x80) {
 | 
			
		||||
      this.data[this.position++] = (n & 0xFF) | 0x80;
 | 
			
		||||
      n >>>= 7;
 | 
			
		||||
    }
 | 
			
		||||
    this.data[this.position++] = n & 0x7F;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  write7BitInt(v) {
 | 
			
		||||
    this.ensureBuffer(5);
 | 
			
		||||
    this._write7BitInt(v);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _7BitIntLen(v) {
 | 
			
		||||
    return v < 0 ? 5
 | 
			
		||||
      : v < 0x80 ? 1
 | 
			
		||||
      : v < 0x4000 ? 2
 | 
			
		||||
      : v < 0x200000 ? 3
 | 
			
		||||
      : v < 0x10000000 ? 4
 | 
			
		||||
      : 5;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  writeUTF(str) {
 | 
			
		||||
    const t = str.length;
 | 
			
		||||
    if (t === 0) {
 | 
			
		||||
      this.write7BitInt(0);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const max = 6 * t;
 | 
			
		||||
    this.ensureBuffer(5 + max);
 | 
			
		||||
    const start = this.position;
 | 
			
		||||
    this.position += this._7BitIntLen(max);
 | 
			
		||||
    const from = this.position;
 | 
			
		||||
    const reserved = from - start;
 | 
			
		||||
    
 | 
			
		||||
    const encoder = new TextEncoder();
 | 
			
		||||
    const { written } = encoder.encodeInto(str, this.data.subarray(this.position));
 | 
			
		||||
    this.position += written;
 | 
			
		||||
    const after = this.position;
 | 
			
		||||
    const size = after - from;
 | 
			
		||||
    
 | 
			
		||||
    this.position = start;
 | 
			
		||||
    this._write7BitInt(size);
 | 
			
		||||
    const used = this.position - start;
 | 
			
		||||
    if (used !== reserved) {
 | 
			
		||||
      this.data.copyWithin(from + (used - reserved), from, after);
 | 
			
		||||
    }
 | 
			
		||||
    this.position = from + size + (used - reserved);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  writeUint8Array(src, offset = 0, length) {
 | 
			
		||||
    const start = offset | 0;
 | 
			
		||||
    const end = Math.min(src.byteLength, start + (length ?? src.byteLength));
 | 
			
		||||
    const n = end - start;
 | 
			
		||||
    if (n <= 0) return;
 | 
			
		||||
    this.ensureBuffer(n);
 | 
			
		||||
    this.data.set(src.subarray(start, end), this.position);
 | 
			
		||||
    this.position += n;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  writeUTFBytes(str) {
 | 
			
		||||
    this.ensureBuffer(6 * str.length);
 | 
			
		||||
    const encoder = new TextEncoder();
 | 
			
		||||
    const { written } = encoder.encodeInto(str, this.data.subarray(this.position));
 | 
			
		||||
    this.position += written;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getBytes(clone = false) {
 | 
			
		||||
    return clone ? this.data.slice(0, this.position) : this.data.subarray(0, this.position);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class BonEncoder {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    this.dw = new DataWriter();
 | 
			
		||||
    this.strMap = new Map();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  reset() {
 | 
			
		||||
    this.dw.reset();
 | 
			
		||||
    this.strMap.clear();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  encodeInt(v) {
 | 
			
		||||
    this.dw.writeInt8(1);
 | 
			
		||||
    this.dw.writeInt32(v | 0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  encodeLong(v) {
 | 
			
		||||
    this.dw.writeInt8(2);
 | 
			
		||||
    if (typeof v === 'number') {
 | 
			
		||||
      this.dw.writeInt64(v);
 | 
			
		||||
    } else {
 | 
			
		||||
      this.dw.writeInt32(v.low | 0);
 | 
			
		||||
      this.dw.writeInt32(v.high | 0);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  encodeFloat(v) {
 | 
			
		||||
    this.dw.writeInt8(3);
 | 
			
		||||
    this.dw.writeFloat32(v);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  encodeDouble(v) {
 | 
			
		||||
    this.dw.writeInt8(4);
 | 
			
		||||
    this.dw.writeFloat64(v);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  encodeNumber(v) {
 | 
			
		||||
    if ((v | 0) === v) this.encodeInt(v);
 | 
			
		||||
    else if (Math.floor(v) === v) this.encodeLong(v);
 | 
			
		||||
    else this.encodeDouble(v);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  encodeString(s) {
 | 
			
		||||
    const hit = this.strMap.get(s);
 | 
			
		||||
    if (hit !== undefined) {
 | 
			
		||||
      this.dw.writeInt8(99); // StringRef
 | 
			
		||||
      this.dw.write7BitInt(hit);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    this.dw.writeInt8(5); // String
 | 
			
		||||
    this.dw.writeUTF(s);
 | 
			
		||||
    this.strMap.set(s, this.strMap.size);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  encodeBoolean(b) {
 | 
			
		||||
    this.dw.writeInt8(6);
 | 
			
		||||
    this.dw.writeInt8(b ? 1 : 0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  encodeNull() {
 | 
			
		||||
    this.dw.writeInt8(0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  encodeDateTime(d) {
 | 
			
		||||
    this.dw.writeInt8(10);
 | 
			
		||||
    this.dw.writeInt64(d.getTime());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  encodeBinary(u8) {
 | 
			
		||||
    this.dw.writeInt8(7);
 | 
			
		||||
    this.dw.write7BitInt(u8.byteLength);
 | 
			
		||||
    this.dw.writeUint8Array(u8);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  encodeArray(arr) {
 | 
			
		||||
    this.dw.writeInt8(9);
 | 
			
		||||
    this.dw.write7BitInt(arr.length);
 | 
			
		||||
    for (let i = 0; i < arr.length; i++) this.encode(arr[i]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  encodeMap(mp) {
 | 
			
		||||
    this.dw.writeInt8(8);
 | 
			
		||||
    this.dw.write7BitInt(mp.size);
 | 
			
		||||
    mp.forEach((v, k) => {
 | 
			
		||||
      this.encode(k);
 | 
			
		||||
      this.encode(v);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  encodeObject(obj) {
 | 
			
		||||
    this.dw.writeInt8(8);
 | 
			
		||||
    const keys = [];
 | 
			
		||||
    for (const k in obj) {
 | 
			
		||||
      if (!Object.prototype.hasOwnProperty.call(obj, k)) continue;
 | 
			
		||||
      if (k.startsWith('_')) continue;
 | 
			
		||||
      const type = typeof obj[k];
 | 
			
		||||
      if (type === 'function' || type === 'undefined') continue;
 | 
			
		||||
      keys.push(k);
 | 
			
		||||
    }
 | 
			
		||||
    this.dw.write7BitInt(keys.length);
 | 
			
		||||
    for (const k of keys) {
 | 
			
		||||
      this.encode(k);
 | 
			
		||||
      this.encode(obj[k]);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  encode(v) {
 | 
			
		||||
    if (v == null) {
 | 
			
		||||
      this.encodeNull();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    switch (v.constructor) {
 | 
			
		||||
      case Number:
 | 
			
		||||
        this.encodeNumber(v);
 | 
			
		||||
        return;
 | 
			
		||||
      case Boolean:
 | 
			
		||||
        this.encodeBoolean(v);
 | 
			
		||||
        return;
 | 
			
		||||
      case String:
 | 
			
		||||
        this.encodeString(v);
 | 
			
		||||
        return;
 | 
			
		||||
      case Int64:
 | 
			
		||||
        this.encodeLong(v);
 | 
			
		||||
        return;
 | 
			
		||||
      case Array:
 | 
			
		||||
        this.encodeArray(v);
 | 
			
		||||
        return;
 | 
			
		||||
      case Map:
 | 
			
		||||
        this.encodeMap(v);
 | 
			
		||||
        return;
 | 
			
		||||
      case Date:
 | 
			
		||||
        this.encodeDateTime(v);
 | 
			
		||||
        return;
 | 
			
		||||
      case Uint8Array:
 | 
			
		||||
        this.encodeBinary(v);
 | 
			
		||||
        return;
 | 
			
		||||
      default:
 | 
			
		||||
        if (typeof v !== 'object') {
 | 
			
		||||
          this.encodeNull();
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
        this.encodeObject(v);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getBytes(clone = false) {
 | 
			
		||||
    return this.dw.getBytes(clone);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class BonDecoder {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    this.dr = new DataReader(new Uint8Array(0));
 | 
			
		||||
    this.strArr = [];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  reset(bytes) {
 | 
			
		||||
    this.dr.reset(bytes);
 | 
			
		||||
    this.strArr.length = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  decode() {
 | 
			
		||||
    const tag = this.dr.readUInt8();
 | 
			
		||||
    switch (tag) {
 | 
			
		||||
      default:
 | 
			
		||||
        return null;
 | 
			
		||||
      case 1:
 | 
			
		||||
        return this.dr.readInt32();
 | 
			
		||||
      case 2:
 | 
			
		||||
        return this.dr.readInt64();
 | 
			
		||||
      case 3:
 | 
			
		||||
        return this.dr.readFloat32();
 | 
			
		||||
      case 4:
 | 
			
		||||
        return this.dr.readFloat64();
 | 
			
		||||
      case 5: {
 | 
			
		||||
        const s = this.dr.readUTF();
 | 
			
		||||
        this.strArr.push(s);
 | 
			
		||||
        return s;
 | 
			
		||||
      }
 | 
			
		||||
      case 6:
 | 
			
		||||
        return this.dr.readUInt8() === 1;
 | 
			
		||||
      case 7: {
 | 
			
		||||
        const len = this.dr.read7BitInt();
 | 
			
		||||
        return this.dr.readUint8Array(len, false);
 | 
			
		||||
      }
 | 
			
		||||
      case 8: {
 | 
			
		||||
        const count = this.dr.read7BitInt();
 | 
			
		||||
        const obj = {};
 | 
			
		||||
        for (let i = 0; i < count; i++) {
 | 
			
		||||
          const k = this.decode();
 | 
			
		||||
          const v = this.decode();
 | 
			
		||||
          obj[k] = v;
 | 
			
		||||
        }
 | 
			
		||||
        return obj;
 | 
			
		||||
      }
 | 
			
		||||
      case 9: {
 | 
			
		||||
        const len = this.dr.read7BitInt();
 | 
			
		||||
        const arr = new Array(len);
 | 
			
		||||
        for (let i = 0; i < len; i++) arr[i] = this.decode();
 | 
			
		||||
        return arr;
 | 
			
		||||
      }
 | 
			
		||||
      case 10:
 | 
			
		||||
        return new Date(this.dr.readInt64());
 | 
			
		||||
      case 99:
 | 
			
		||||
        return this.strArr[this.dr.read7BitInt()];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 单例实例
 | 
			
		||||
const _enc = new BonEncoder();
 | 
			
		||||
const _dec = new BonDecoder();
 | 
			
		||||
 | 
			
		||||
// BON 编解码函数
 | 
			
		||||
export const bon = {
 | 
			
		||||
  encode: (value, clone = true) => {
 | 
			
		||||
    _enc.reset();
 | 
			
		||||
    _enc.encode(value);
 | 
			
		||||
    return _enc.getBytes(clone);
 | 
			
		||||
  },
 | 
			
		||||
  decode: (bytes) => {
 | 
			
		||||
    _dec.reset(bytes);
 | 
			
		||||
    return _dec.decode();
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** —— 协议消息包装,与原 ProtoMsg 类等价 —— */
 | 
			
		||||
export class ProtoMsg {
 | 
			
		||||
  constructor(raw) {
 | 
			
		||||
    if (raw?.cmd) {
 | 
			
		||||
      raw.cmd = raw.cmd.toLowerCase();
 | 
			
		||||
    }
 | 
			
		||||
    this._raw = raw;
 | 
			
		||||
    this._rawData = undefined;
 | 
			
		||||
    this._data = undefined;
 | 
			
		||||
    this._t = undefined;
 | 
			
		||||
    this._sendMsg = undefined;
 | 
			
		||||
    this.rtt = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get sendMsg() { return this._sendMsg; }
 | 
			
		||||
  get seq() { return this._raw.seq; }
 | 
			
		||||
  get resp() { return this._raw.resp; }
 | 
			
		||||
  get ack() { return this._raw.ack; }
 | 
			
		||||
  get cmd() { return this._raw?.cmd && this._raw?.cmd.toLowerCase(); }
 | 
			
		||||
  get code() { return ~~this._raw.code; }
 | 
			
		||||
  get error() { return this._raw.error; }
 | 
			
		||||
  get time() { return this._raw.time; }
 | 
			
		||||
  get body() { return this._raw.body; }
 | 
			
		||||
 | 
			
		||||
  /** 惰性 decode body → rawData(bon.decode) */
 | 
			
		||||
  get rawData() {
 | 
			
		||||
    if (this._rawData !== undefined || this.body === undefined) return this._rawData;
 | 
			
		||||
    this._rawData = bon.decode(this.body);
 | 
			
		||||
    return this._rawData;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 指定数据类型 */
 | 
			
		||||
  setDataType(t) {
 | 
			
		||||
    if (t) this._t = { name: t.name ?? 'Anonymous', ctor: t };
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 配置"请求"对象,让 respType 自动对齐 */
 | 
			
		||||
  setSendMsg(msg) {
 | 
			
		||||
    this._sendMsg = msg;
 | 
			
		||||
    return this.setDataType(msg.respType);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 将 rawData 反序列化为业务对象 */
 | 
			
		||||
  getData(clazz) {
 | 
			
		||||
    if (this._data !== undefined || this.rawData === undefined) return this._data;
 | 
			
		||||
 | 
			
		||||
    let t = this._t;
 | 
			
		||||
    if (clazz && t && clazz !== t.ctor) {
 | 
			
		||||
      console.warn(`getData type not match, ${clazz.name} != ${t.name}`);
 | 
			
		||||
      t = { name: clazz.name, ctor: clazz };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this._data = this.rawData;
 | 
			
		||||
    return this._data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  toLogString() {
 | 
			
		||||
    const e = { ...this._raw };
 | 
			
		||||
    delete e.body;
 | 
			
		||||
    e.data = this.rawData;
 | 
			
		||||
    e.rtt = this.rtt;
 | 
			
		||||
    return JSON.stringify(e);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** —— 加解密器注册表 —— */
 | 
			
		||||
const registry = new Map();
 | 
			
		||||
 | 
			
		||||
/** lz4 + 头部掩码的 "lx" 方案 */
 | 
			
		||||
const lx = {
 | 
			
		||||
  encrypt: (buf) => {
 | 
			
		||||
    let e = lz4.compress(buf);
 | 
			
		||||
    const t = 2 + ~~(Math.random() * 248);
 | 
			
		||||
    for (let n = Math.min(e.length, 100); --n >= 0; ) e[n] ^= t;
 | 
			
		||||
    
 | 
			
		||||
    // 写入标识与混淆位
 | 
			
		||||
    e[0] = 112; e[1] = 108;
 | 
			
		||||
    e[2] = (e[2] & 0b10101010) | ((t >> 7 & 1) << 6) | ((t >> 6 & 1) << 4) | ((t >> 5 & 1) << 2) | (t >> 4 & 1);
 | 
			
		||||
    e[3] = (e[3] & 0b10101010) | ((t >> 3 & 1) << 6) | ((t >> 2 & 1) << 4) | ((t >> 1 & 1) << 2) | (t & 1);
 | 
			
		||||
    return e;
 | 
			
		||||
  },
 | 
			
		||||
  decrypt: (e) => {
 | 
			
		||||
    const t =
 | 
			
		||||
      ((e[2] >> 6 & 1) << 7) | ((e[2] >> 4 & 1) << 6) | ((e[2] >> 2 & 1) << 5) | ((e[2] & 1) << 4) |
 | 
			
		||||
      ((e[3] >> 6 & 1) << 3) | ((e[3] >> 4 & 1) << 2) | ((e[3] >> 2 & 1) << 1) | (e[3] & 1);
 | 
			
		||||
    for (let n = Math.min(100, e.length); --n >= 2; ) e[n] ^= t;
 | 
			
		||||
    e[0] = 4; e[1] = 34; e[2] = 77; e[3] = 24; // 还原头以便 lz4 解
 | 
			
		||||
    return lz4.decompress(e);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** 随机首 4 字节 + XOR 的 "x" 方案 */
 | 
			
		||||
const x = {
 | 
			
		||||
  encrypt: (e) => {
 | 
			
		||||
    const rnd = ~~(Math.random() * 0xFFFFFFFF) >>> 0;
 | 
			
		||||
    const n = new Uint8Array(e.length + 4);
 | 
			
		||||
    n[0] = rnd & 0xFF; n[1] = (rnd >>> 8) & 0xFF; n[2] = (rnd >>> 16) & 0xFF; n[3] = (rnd >>> 24) & 0xFF;
 | 
			
		||||
    n.set(e, 4);
 | 
			
		||||
    const r = 2 + ~~(Math.random() * 248);
 | 
			
		||||
    for (let i = n.length; --i >= 0; ) n[i] ^= r;
 | 
			
		||||
    n[0] = 112; n[1] = 120;
 | 
			
		||||
    n[2] = (n[2] & 0b10101010) | ((r >> 7 & 1) << 6) | ((r >> 6 & 1) << 4) | ((r >> 5 & 1) << 2) | (r >> 4 & 1);
 | 
			
		||||
    n[3] = (n[3] & 0b10101010) | ((r >> 3 & 1) << 6) | ((r >> 2 & 1) << 4) | ((r >> 1 & 1) << 2) | (r & 1);
 | 
			
		||||
    return n;
 | 
			
		||||
  },
 | 
			
		||||
  decrypt: (e) => {
 | 
			
		||||
    const t =
 | 
			
		||||
      ((e[2] >> 6 & 1) << 7) | ((e[2] >> 4 & 1) << 6) | ((e[2] >> 2 & 1) << 5) | ((e[2] & 1) << 4) |
 | 
			
		||||
      ((e[3] >> 6 & 1) << 3) | ((e[3] >> 4 & 1) << 2) | ((e[3] >> 2 & 1) << 1) | (e[3] & 1);
 | 
			
		||||
    for (let n = e.length; --n >= 4; ) e[n] ^= t;
 | 
			
		||||
    return e.subarray(4);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** 依赖 globalThis.XXTEA 的 "xtm" 方案 */
 | 
			
		||||
const xtm = {
 | 
			
		||||
  encrypt: (e) => globalThis.XXTEA ? globalThis.XXTEA.encryptMod({ data: e.buffer, length: e.length }) : e,
 | 
			
		||||
  decrypt: (e) => globalThis.XXTEA ? globalThis.XXTEA.decryptMod({ data: e.buffer, length: e.length }) : e,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** 注册器 */
 | 
			
		||||
function register(name, impl) {
 | 
			
		||||
  registry.set(name, impl);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
register('lx', lx);
 | 
			
		||||
register('x', x);
 | 
			
		||||
register('xtm', xtm);
 | 
			
		||||
 | 
			
		||||
/** 默认使用 x 加密(自动检测解密) */
 | 
			
		||||
const passthrough = {
 | 
			
		||||
  encrypt: (e) => getEnc('x').encrypt(e),
 | 
			
		||||
  decrypt: (e) => {
 | 
			
		||||
    if (e.length > 4 && e[0] === 112 && e[1] === 108)        e = getEnc('lx').decrypt(e);
 | 
			
		||||
    else if (e.length > 4 && e[0] === 112 && e[1] === 120)   e = getEnc('x').decrypt(e);
 | 
			
		||||
    else if (e.length > 3 && e[0] === 112 && e[1] === 116)   e = getEnc('xtm').decrypt(e);
 | 
			
		||||
    return e;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** 对外:按名称取加解密器;找不到则用默认 */
 | 
			
		||||
export function getEnc(name) {
 | 
			
		||||
  return registry.get(name) ?? passthrough;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 对外:encode(bon.encode → 加密) */
 | 
			
		||||
export function encode(obj, enc) {
 | 
			
		||||
  let bytes = bon.encode(obj, false);
 | 
			
		||||
  const out = enc.encrypt(bytes);
 | 
			
		||||
  return out.buffer.byteLength === out.length ? out.buffer : out.buffer.slice(0, out.length);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 对外:parse(解密 → bon.decode → ProtoMsg) */
 | 
			
		||||
export function parse(buf, enc) {
 | 
			
		||||
  const u8 = new Uint8Array(buf);
 | 
			
		||||
  const plain = enc.decrypt(u8);
 | 
			
		||||
  const raw = bon.decode(plain);
 | 
			
		||||
  return new ProtoMsg(raw);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 游戏消息模板
 | 
			
		||||
export const GameMessages = {
 | 
			
		||||
  // 心跳消息
 | 
			
		||||
  heartBeat: (ack = 0, seq = 0) => ({
 | 
			
		||||
    ack,
 | 
			
		||||
    body: undefined,
 | 
			
		||||
    c: undefined,
 | 
			
		||||
    cmd: "_sys/ack",
 | 
			
		||||
    hint: undefined,
 | 
			
		||||
    seq,
 | 
			
		||||
    time: Date.now()
 | 
			
		||||
  }),
 | 
			
		||||
 | 
			
		||||
  // 获取角色信息
 | 
			
		||||
  getRoleInfo: (ack = 0, seq = 0, params = {}) => ({
 | 
			
		||||
    cmd: "role_getroleinfo",
 | 
			
		||||
    body: encode({
 | 
			
		||||
      clientVersion: "1.65.3-wx",
 | 
			
		||||
      inviteUid: 0,
 | 
			
		||||
      platform: "hortor",
 | 
			
		||||
      platformExt: "mix",
 | 
			
		||||
      scene: "",
 | 
			
		||||
      ...params
 | 
			
		||||
    }, getEnc('x')),
 | 
			
		||||
    ack: ack || 0,
 | 
			
		||||
    seq: seq || 0,
 | 
			
		||||
    time: Date.now()
 | 
			
		||||
  }),
 | 
			
		||||
 | 
			
		||||
  // 获取数据包版本
 | 
			
		||||
  getDataBundleVer: (ack = 0, seq = 0, params = {}) => ({
 | 
			
		||||
    cmd: "system_getdatabundlever",
 | 
			
		||||
    body: encode({
 | 
			
		||||
      isAudit: false,
 | 
			
		||||
      ...params
 | 
			
		||||
    }, getEnc('x')),
 | 
			
		||||
    ack: ack || 0,
 | 
			
		||||
    seq: seq || 0,
 | 
			
		||||
    time: Date.now()
 | 
			
		||||
  }),
 | 
			
		||||
 | 
			
		||||
  // 购买金币
 | 
			
		||||
  buyGold: (ack = 0, seq = 0, params = {}) => ({
 | 
			
		||||
    ack,
 | 
			
		||||
    body: encode({
 | 
			
		||||
      buyNum: 1,
 | 
			
		||||
      ...params
 | 
			
		||||
    }, getEnc('x')),
 | 
			
		||||
    cmd: "system_buygold",
 | 
			
		||||
    seq,
 | 
			
		||||
    time: Date.now()
 | 
			
		||||
  }),
 | 
			
		||||
 | 
			
		||||
  // 签到奖励
 | 
			
		||||
  signInReward: (ack = 0, seq = 0, params = {}) => ({
 | 
			
		||||
    ack,
 | 
			
		||||
    body: encode({
 | 
			
		||||
      ...params
 | 
			
		||||
    }, getEnc('x')),
 | 
			
		||||
    cmd: "system_signinreward",
 | 
			
		||||
    seq,
 | 
			
		||||
    time: Date.now()
 | 
			
		||||
  }),
 | 
			
		||||
 | 
			
		||||
  // 领取每日任务奖励
 | 
			
		||||
  claimDailyReward: (ack = 0, seq = 0, params = {}) => ({
 | 
			
		||||
    ack,
 | 
			
		||||
    body: encode({
 | 
			
		||||
      rewardId: 0,
 | 
			
		||||
      ...params
 | 
			
		||||
    }, getEnc('x')),
 | 
			
		||||
    cmd: "task_claimdailyreward",
 | 
			
		||||
    seq,
 | 
			
		||||
    time: Date.now()
 | 
			
		||||
  })
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 创建全局实例
 | 
			
		||||
export const g_utils = {
 | 
			
		||||
  getEnc,
 | 
			
		||||
  encode: (obj, encName = 'x') => encode(obj, getEnc(encName)),
 | 
			
		||||
  parse: (data, encName = 'auto') => parse(data, getEnc(encName)),
 | 
			
		||||
  bon // 添加BON编解码器
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 兼容性导出(保持旧的接口)
 | 
			
		||||
export const bonProtocol = {
 | 
			
		||||
  encode: bon.encode,
 | 
			
		||||
  decode: bon.decode,
 | 
			
		||||
  createMessage: (cmd, body = {}, ack = 0, seq = 0, options = {}) => ({
 | 
			
		||||
    cmd,
 | 
			
		||||
    body: bon.encode(body),
 | 
			
		||||
    ack: ack || 0,
 | 
			
		||||
    seq: seq || 0,
 | 
			
		||||
    time: Date.now(),
 | 
			
		||||
    ...options
 | 
			
		||||
  }),
 | 
			
		||||
  parseMessage: (messageData) => {
 | 
			
		||||
    try {
 | 
			
		||||
      let message;
 | 
			
		||||
      if (typeof messageData === 'string') {
 | 
			
		||||
        message = JSON.parse(messageData);
 | 
			
		||||
      } else {
 | 
			
		||||
        message = messageData;
 | 
			
		||||
      }
 | 
			
		||||
      if (message.body && (message.body instanceof ArrayBuffer || message.body instanceof Uint8Array)) {
 | 
			
		||||
        message.body = bon.decode(message.body);
 | 
			
		||||
      }
 | 
			
		||||
      return message;
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      console.error('消息解析失败:', error);
 | 
			
		||||
      return {
 | 
			
		||||
        error: true,
 | 
			
		||||
        message: '消息解析失败',
 | 
			
		||||
        originalData: messageData
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  generateSeq: () => Math.floor(Math.random() * 1000000),
 | 
			
		||||
  generateMessageId: () => 'msg_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 导出单独的加密器类以兼容测试文件
 | 
			
		||||
export const LXCrypto = lx;
 | 
			
		||||
export const XCrypto = x;
 | 
			
		||||
export const XTMCrypto = xtm;
 | 
			
		||||
 | 
			
		||||
export default { ProtoMsg, getEnc, encode, parse, GameMessages, g_utils, bon, bonProtocol };
 | 
			
		||||
		Reference in New Issue
	
	Block a user