binary optimization
This commit is contained in:
		| @@ -87,7 +87,6 @@ export default class DataManager extends Singleton { | ||||
|         player.direction = { x, y } | ||||
|         break | ||||
|       } | ||||
|  | ||||
|       case InputTypeEnum.WeaponShoot: { | ||||
|         const { owner, position, direction } = input | ||||
|         const bullet: IBullet = { | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| import Singleton from '../Base/Singleton' | ||||
| import { ApiMsgEnum, IModel, strdecode, strencode } from '../Common'; | ||||
| import { binaryEncode } from '../Common/Binary'; | ||||
| import { binaryDecode } from '../Utils'; | ||||
| import { binaryEncode, binaryDecode } from '../Common/Binary'; | ||||
|  | ||||
| const TIMEOUT = 5000 | ||||
|  | ||||
| @@ -94,8 +93,6 @@ export default class NetworkManager extends Singleton { | ||||
|  | ||||
|   sendMsg<T extends keyof IModel['msg']>(name: T, data: IModel['msg'][T]) { | ||||
|     const view = binaryEncode(name, data) | ||||
|     console.log("view", view.buffer); | ||||
|  | ||||
|     this.ws.send(view.buffer) | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -8,80 +8,4 @@ const getNumberWithinString = (str: string) => parseInt(str.match(INDEX_REG)?.[1 | ||||
| export const sortSpriteFrame = (spriteFrame: Array<SpriteFrame>) => | ||||
|   spriteFrame.sort((a, b) => getNumberWithinString(a.name) - getNumberWithinString(b.name)) | ||||
|  | ||||
| export const rad2Angle = (rad: number) => rad / Math.PI * 180 | ||||
|  | ||||
| export const binaryDecode = (buffer: ArrayBuffer) => { | ||||
|   let index = 0 | ||||
|   const view = new DataView(buffer) | ||||
|   const type = view.getUint8(index++) | ||||
|  | ||||
|   if (type === ApiMsgEnum.MsgClientSync) { | ||||
|     const inputType = view.getUint8(index++) | ||||
|     if (inputType === InputTypeEnum.ActorMove) { | ||||
|       const id = view.getUint8(index++) | ||||
|       const directionX = view.getFloat32(index) | ||||
|       index += 4 | ||||
|       const directionY = view.getFloat32(index) | ||||
|       index += 4 | ||||
|       const dt = view.getFloat32(index) | ||||
|       index += 4 | ||||
|       const msg = { | ||||
|         name: ApiMsgEnum.MsgClientSync, | ||||
|         data: { | ||||
|           type: InputTypeEnum.ActorMove, | ||||
|           id, | ||||
|           direction: { | ||||
|             x: directionX, | ||||
|             y: directionY, | ||||
|           }, | ||||
|           dt | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       return msg | ||||
|     } else if (inputType === InputTypeEnum.WeaponShoot) { | ||||
|       const id = view.getUint8(index++) | ||||
|       const positionX = view.getFloat32(index) | ||||
|       index += 4 | ||||
|       const positionY = view.getFloat32(index) | ||||
|       index += 4 | ||||
|       const directionX = view.getFloat32(index) | ||||
|       index += 4 | ||||
|       const directionY = view.getFloat32(index) | ||||
|       index += 4 | ||||
|       const msg = { | ||||
|         name: ApiMsgEnum.MsgClientSync, | ||||
|         data: { | ||||
|           type: InputTypeEnum.WeaponShoot, | ||||
|           id, | ||||
|           position: { | ||||
|             x: positionX, | ||||
|             y: positionY, | ||||
|           }, | ||||
|           direction: { | ||||
|             x: directionX, | ||||
|             y: directionY, | ||||
|           }, | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       return msg | ||||
|     } else { | ||||
|       const dt = view.getFloat32(index) | ||||
|       index += 4 | ||||
|       const msg = { | ||||
|         name: ApiMsgEnum.MsgClientSync, | ||||
|         data: { | ||||
|           type: InputTypeEnum.TimePast, | ||||
|           dt, | ||||
|         } | ||||
|       } | ||||
|       return msg | ||||
|     } | ||||
|   } else { | ||||
|     return { | ||||
|       name: type, | ||||
|       data: JSON.parse(strdecode(new Uint8Array(buffer.slice(1)))) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| export const rad2Angle = (rad: number) => rad / Math.PI * 180 | ||||
| @@ -1,57 +1,92 @@ | ||||
| import { ApiMsgEnum, InputTypeEnum } from "./Enum"; | ||||
| import { strencode } from "./Utils"; | ||||
| import { strdecode, strencode } from "./Utils"; | ||||
|  | ||||
| const encodeActorMove = (proto: ApiMsgEnum, data: any, view: DataView, index: number) => { | ||||
|   view.setUint8(index++, data.type) | ||||
|   view.setUint8(index++, data.id) | ||||
|   view.setFloat32(index, data.direction.x) | ||||
|   index += 4 | ||||
|   view.setFloat32(index, data.direction.y) | ||||
|   index += 4 | ||||
|   view.setFloat32(index, data.dt) | ||||
|   index += 4 | ||||
| } | ||||
|  | ||||
| const encodeWeaponShoot = (proto: ApiMsgEnum, data: any, view: DataView, index: number) => { | ||||
|   view.setUint8(index++, data.type) | ||||
|   view.setUint8(index++, data.owner) | ||||
|   view.setFloat32(index, data.position.x) | ||||
|   index += 4 | ||||
|   view.setFloat32(index, data.position.y) | ||||
|   index += 4 | ||||
|   view.setFloat32(index, data.direction.x) | ||||
|   index += 4 | ||||
|   view.setFloat32(index, data.direction.y) | ||||
|   index += 4 | ||||
| } | ||||
|  | ||||
| export const encdoeTimePast = (proto: ApiMsgEnum, data: any, view: DataView, index: number) => { | ||||
|   view.setUint8(index++, data.type) | ||||
|   view.setFloat32(index, data.dt) | ||||
|   index += 4 | ||||
| } | ||||
|  | ||||
| export const binaryEncode = (proto: ApiMsgEnum, data: any): DataView => { | ||||
|   if (proto === ApiMsgEnum.MsgClientSync) { | ||||
|     switch (data.type) { | ||||
|       case InputTypeEnum.ActorMove: { | ||||
|         let index = 0 | ||||
|         const ab = new ArrayBuffer(3 + 12) | ||||
|         const view = new DataView(ab) | ||||
|         view.setUint8(index++, proto) | ||||
|         view.setUint8(index++, data.type) | ||||
|         view.setUint8(index++, data.id) | ||||
|         view.setFloat32(index, data.direction.x) | ||||
|         index += 4 | ||||
|         view.setFloat32(index, data.direction.y) | ||||
|         index += 4 | ||||
|         view.setFloat32(index, data.dt) | ||||
|         index += 4 | ||||
|         return view | ||||
|       } | ||||
|       case InputTypeEnum.WeaponShoot: { | ||||
|         let index = 0 | ||||
|         const ab = new ArrayBuffer(3 + 16) | ||||
|         const view = new DataView(ab) | ||||
|         view.setUint8(index++, proto) | ||||
|         view.setUint8(index++, data.type) | ||||
|         view.setUint8(index++, data.id) | ||||
|         view.setFloat32(index, data.position.x) | ||||
|         index += 4 | ||||
|         view.setFloat32(index, data.position.y) | ||||
|         index += 4 | ||||
|         view.setFloat32(index, data.direction.x) | ||||
|         index += 4 | ||||
|         view.setFloat32(index, data.direction.y) | ||||
|         index += 4 | ||||
|         return view | ||||
|       } | ||||
|       case InputTypeEnum.TimePast: { | ||||
|         let index = 0 | ||||
|         const ab = new ArrayBuffer(1 + 1 + 4) | ||||
|         const view = new DataView(ab) | ||||
|         view.setUint8(index++, proto) | ||||
|         view.setUint8(index++, data.type) | ||||
|         view.setFloat32(index, data.dt) | ||||
|         index += 4 | ||||
|         return view | ||||
|       } | ||||
|       default: { | ||||
|         const ab = new ArrayBuffer(0) | ||||
|         const view = new DataView(ab) | ||||
|         return view | ||||
|     if (data.type === InputTypeEnum.ActorMove) { | ||||
|       let index = 0 | ||||
|       const ab = new ArrayBuffer(3 + 12) | ||||
|       const view = new DataView(ab) | ||||
|       view.setUint8(index++, proto) | ||||
|       encodeActorMove(proto, data, view, index) | ||||
|       return view | ||||
|     } else if (data.type === InputTypeEnum.WeaponShoot) { | ||||
|       let index = 0 | ||||
|       const ab = new ArrayBuffer(3 + 16) | ||||
|       const view = new DataView(ab) | ||||
|       view.setUint8(index++, proto) | ||||
|       encodeWeaponShoot(proto, data, view, index) | ||||
|       return view | ||||
|     } else { | ||||
|       let index = 0 | ||||
|       const ab = new ArrayBuffer(2 + 4) | ||||
|       const view = new DataView(ab) | ||||
|       view.setUint8(index++, proto) | ||||
|       encdoeTimePast(proto, data, view, index) | ||||
|       return view | ||||
|     } | ||||
|   } else if (proto === ApiMsgEnum.MsgServerSync) { | ||||
|     let total = 0 | ||||
|     for (let i = 0; i < data.length; i++) { | ||||
|       const item = data[i]; | ||||
|       if (item.type === InputTypeEnum.ActorMove) { | ||||
|         total += 14 | ||||
|       } else if (item.type === InputTypeEnum.WeaponShoot) { | ||||
|         total += 18 | ||||
|       } else { | ||||
|         total += 5 | ||||
|       } | ||||
|     } | ||||
|     const ab = new ArrayBuffer(1 + 1 + total) | ||||
|     const view = new DataView(ab) | ||||
|     let index = 0 | ||||
|     view.setUint8(index++, proto) | ||||
|     view.setUint8(index++, data.length) | ||||
|     for (let i = 0; i < data.length; i++) { | ||||
|       const item = data[i]; | ||||
|       if (item.type === InputTypeEnum.ActorMove) { | ||||
|         encodeActorMove(proto, item, view, index) | ||||
|         index += 14 | ||||
|       } else if (item.type === InputTypeEnum.WeaponShoot) { | ||||
|         encodeWeaponShoot(proto, item, view, index) | ||||
|         index += 18 | ||||
|       } else { | ||||
|         encdoeTimePast(proto, item, view, index) | ||||
|         index += 5 | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return view | ||||
|   } else { | ||||
|     let index = 0 | ||||
|     const str = JSON.stringify(data) | ||||
| @@ -64,4 +99,113 @@ export const binaryEncode = (proto: ApiMsgEnum, data: any): DataView => { | ||||
|     } | ||||
|     return view | ||||
|   } | ||||
| } | ||||
|  | ||||
| const decodeActorMove = (view: DataView, index: number) => { | ||||
|   const id = view.getUint8(index++) | ||||
|   const directionX = view.getFloat32(index) | ||||
|   index += 4 | ||||
|   const directionY = view.getFloat32(index) | ||||
|   index += 4 | ||||
|   const dt = view.getFloat32(index) | ||||
|   index += 4 | ||||
|   const msg = { | ||||
|     name: ApiMsgEnum.MsgClientSync, | ||||
|     data: { | ||||
|       type: InputTypeEnum.ActorMove, | ||||
|       id, | ||||
|       direction: { | ||||
|         x: directionX, | ||||
|         y: directionY, | ||||
|       }, | ||||
|       dt | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return msg | ||||
| } | ||||
|  | ||||
| const decodeWeaponShoot = (view: DataView, index: number) => { | ||||
|   const owner = view.getUint8(index++) | ||||
|   const positionX = view.getFloat32(index) | ||||
|   index += 4 | ||||
|   const positionY = view.getFloat32(index) | ||||
|   index += 4 | ||||
|   const directionX = view.getFloat32(index) | ||||
|   index += 4 | ||||
|   const directionY = view.getFloat32(index) | ||||
|   index += 4 | ||||
|   const msg = { | ||||
|     name: ApiMsgEnum.MsgClientSync, | ||||
|     data: { | ||||
|       type: InputTypeEnum.WeaponShoot, | ||||
|       owner, | ||||
|       position: { | ||||
|         x: positionX, | ||||
|         y: positionY, | ||||
|       }, | ||||
|       direction: { | ||||
|         x: directionX, | ||||
|         y: directionY, | ||||
|       }, | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return msg | ||||
| } | ||||
|  | ||||
| const decodeTimePast = (view: DataView, index: number) => { | ||||
|   const dt = view.getFloat32(index) | ||||
|   index += 4 | ||||
|   const msg = { | ||||
|     name: ApiMsgEnum.MsgClientSync, | ||||
|     data: { | ||||
|       type: InputTypeEnum.TimePast, | ||||
|       dt, | ||||
|     } | ||||
|   } | ||||
|   return msg | ||||
| } | ||||
|  | ||||
| export const binaryDecode = (buffer: ArrayBuffer) => { | ||||
|   let index = 0 | ||||
|   const view = new DataView(buffer) | ||||
|   const proto = view.getUint8(index++) | ||||
|  | ||||
|   if (proto === ApiMsgEnum.MsgClientSync) { | ||||
|     const inputType = view.getUint8(index++) | ||||
|     if (inputType === InputTypeEnum.ActorMove) { | ||||
|       return decodeActorMove(view, index) | ||||
|     } else if (inputType === InputTypeEnum.WeaponShoot) { | ||||
|       return decodeWeaponShoot(view, index) | ||||
|     } else { | ||||
|       return decodeTimePast(view, index) | ||||
|     } | ||||
|   } else if (proto === ApiMsgEnum.MsgServerSync) { | ||||
|     const len = view.getUint8(index++) | ||||
|     const res = [] | ||||
|     for (let i = 0; i < len; i++) { | ||||
|       const inputType = view.getUint8(index++) | ||||
|  | ||||
|       if (inputType === InputTypeEnum.ActorMove) { | ||||
|         res.push(decodeActorMove(view, index).data) | ||||
|         index += 13 | ||||
|       } else if (inputType === InputTypeEnum.WeaponShoot) { | ||||
|         res.push(decodeWeaponShoot(view, index).data) | ||||
|         index += 17 | ||||
|       } else { | ||||
|         res.push(decodeTimePast(view, index).data) | ||||
|         index += 4 | ||||
|       } | ||||
|     } | ||||
|     return { | ||||
|       name: ApiMsgEnum.MsgServerSync, | ||||
|       data: res | ||||
|     } | ||||
|   } else { | ||||
|     return { | ||||
|       name: proto, | ||||
|       data: JSON.parse(strdecode(new Uint8Array(buffer.slice(1)))) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -7,6 +7,7 @@ export * from './Enum' | ||||
| export * from './Model' | ||||
| export * from './State' | ||||
| export * from './Utils' | ||||
| export * from './Binary' | ||||
|  | ||||
| export interface IModel { | ||||
|   api: { | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| import WebSocket from 'ws'; | ||||
| import { EventEmitter } from 'stream'; | ||||
| import { MyServer } from './MyServer'; | ||||
| import { binaryDecode, getTime } from '../Utils'; | ||||
| import { getTime, toArrayBuffer } from '../Utils'; | ||||
| import { ApiMsgEnum, IModel } from '../Common'; | ||||
| import { binaryEncode } from '../Common/Binary'; | ||||
| import { binaryEncode, binaryDecode } from '../Common/Binary'; | ||||
|  | ||||
| export enum ConnectionEventEnum { | ||||
|   Close = 'Close', | ||||
| @@ -27,7 +27,7 @@ export class Connection extends EventEmitter { | ||||
|     this.ws.on('message', (buffer: Buffer) => { | ||||
|       // const str = buffer.toString() | ||||
|       try { | ||||
|         const json = binaryDecode(buffer) | ||||
|         const json = binaryDecode(toArrayBuffer(buffer)) | ||||
|         const { name, data } = json | ||||
|         // console.log(`${getTime()}接收|字节数${buffer.length}|${this.playerId || -1}|${str}`) | ||||
|         console.log(`${getTime()}接收|字节数${buffer.length}|${this.playerId || -1}|${JSON.stringify(json)}`) | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| import fs from 'fs-extra' | ||||
| import path from 'path' | ||||
| import { ApiMsgEnum, InputTypeEnum, strdecode } from '../Common' | ||||
|  | ||||
| export const getTime = () => new Date().toLocaleString().split("├")[0] | ||||
|  | ||||
| @@ -37,77 +36,11 @@ export const copyCommon = async () => { | ||||
|   console.log('同步成功!') | ||||
| } | ||||
|  | ||||
| export const binaryDecode = (buffer: Buffer) => { | ||||
|   let index = 0 | ||||
|   const type = buffer.readUint8(index++) | ||||
|  | ||||
|   if (type === ApiMsgEnum.MsgClientSync) { | ||||
|     const inputType = buffer.readUint8(index++) | ||||
|     if (inputType === InputTypeEnum.ActorMove) { | ||||
|       const id = buffer.readUint8(index++) | ||||
|       const directionX = buffer.readFloatBE(index) | ||||
|       index += 4 | ||||
|       const directionY = buffer.readFloatBE(index) | ||||
|       index += 4 | ||||
|       const dt = buffer.readFloatBE(index) | ||||
|       index += 4 | ||||
|       const msg = { | ||||
|         name: ApiMsgEnum.MsgClientSync, | ||||
|         data: { | ||||
|           type: InputTypeEnum.ActorMove, | ||||
|           id, | ||||
|           direction: { | ||||
|             x: directionX, | ||||
|             y: directionY, | ||||
|           }, | ||||
|           dt | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       return msg | ||||
|     } else if (inputType === InputTypeEnum.WeaponShoot) { | ||||
|       const id = buffer.readUint8(index++) | ||||
|       const positionX = buffer.readFloatBE(index) | ||||
|       index += 4 | ||||
|       const positionY = buffer.readFloatBE(index) | ||||
|       index += 4 | ||||
|       const directionX = buffer.readFloatBE(index) | ||||
|       index += 4 | ||||
|       const directionY = buffer.readFloatBE(index) | ||||
|       index += 4 | ||||
|       const msg = { | ||||
|         name: ApiMsgEnum.MsgClientSync, | ||||
|         data: { | ||||
|           type: InputTypeEnum.WeaponShoot, | ||||
|           id, | ||||
|           position: { | ||||
|             x: positionX, | ||||
|             y: positionY, | ||||
|           }, | ||||
|           direction: { | ||||
|             x: directionX, | ||||
|             y: directionY, | ||||
|           }, | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       return msg | ||||
|     } else { | ||||
|       const dt = buffer.readFloatBE(index) | ||||
|       index += 4 | ||||
|       const msg = { | ||||
|         name: ApiMsgEnum.MsgClientSync, | ||||
|         data: { | ||||
|           type: InputTypeEnum.TimePast, | ||||
|           dt, | ||||
|         } | ||||
|       } | ||||
|       return msg | ||||
|     } | ||||
|   } else { | ||||
|     return { | ||||
|       name: type, | ||||
|       data: JSON.parse(strdecode(new Uint8Array(buffer.slice(1)))) | ||||
|     } | ||||
| export const toArrayBuffer = (buffer: Buffer) => { | ||||
|   var ab = new ArrayBuffer(buffer.length); | ||||
|   var view = new Uint8Array(ab); | ||||
|   for (var i = 0; i < buffer.length; ++i) { | ||||
|     view[i] = buffer[i]; | ||||
|   } | ||||
|   return ab; | ||||
| } | ||||
							
								
								
									
										34
									
								
								test.js
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								test.js
									
									
									
									
									
								
							| @@ -1,30 +1,4 @@ | ||||
| const msg = JSON.stringify({ | ||||
|   a: 123, | ||||
|   b: true, | ||||
|   c: "456" | ||||
| }) | ||||
|  | ||||
| const strencode = (str) => { | ||||
|   let byteArray = []; | ||||
|   for (let i = 0; i < str.length; i++) { | ||||
|     let charCode = str.charCodeAt(i); | ||||
|     if (charCode <= 0x7f) { | ||||
|       byteArray.push(charCode); | ||||
|     } else if (charCode <= 0x7ff) { | ||||
|       byteArray.push(0xc0 | (charCode >> 6), 0x80 | (charCode & 0x3f)); | ||||
|     } else if (charCode <= 0xffff) { | ||||
|       byteArray.push(0xe0 | (charCode >> 12), 0x80 | ((charCode & 0xfc0) >> 6), 0x80 | (charCode & 0x3f)); | ||||
|     } else { | ||||
|       byteArray.push(0xf0 | (charCode >> 18), 0x80 | ((charCode & 0x3f000) >> 12), 0x80 | ((charCode & 0xfc0) >> 6), 0x80 | (charCode & 0x3f)); | ||||
|     } | ||||
|   } | ||||
|   return byteArray; | ||||
| } | ||||
|  | ||||
| var arr = strencode(msg) | ||||
| var buffer = Buffer.from(msg) | ||||
|  | ||||
| console.log(buffer) | ||||
| for (let i = 0; i < arr.length; i++) { | ||||
|   console.log(buffer[i], arr[i]); | ||||
| } | ||||
| const ab = new ArrayBuffer(10) | ||||
| const view = new DataView(ab) | ||||
| view.setFloat32(0, 0.0023) | ||||
| console.log(view); | ||||
		Reference in New Issue
	
	Block a user