binary optimization
This commit is contained in:
parent
c31af6b02a
commit
eeaa7915de
@ -87,7 +87,6 @@ export default class DataManager extends Singleton {
|
|||||||
player.direction = { x, y }
|
player.direction = { x, y }
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
case InputTypeEnum.WeaponShoot: {
|
case InputTypeEnum.WeaponShoot: {
|
||||||
const { owner, position, direction } = input
|
const { owner, position, direction } = input
|
||||||
const bullet: IBullet = {
|
const bullet: IBullet = {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import Singleton from '../Base/Singleton'
|
import Singleton from '../Base/Singleton'
|
||||||
import { ApiMsgEnum, IModel, strdecode, strencode } from '../Common';
|
import { ApiMsgEnum, IModel, strdecode, strencode } from '../Common';
|
||||||
import { binaryEncode } from '../Common/Binary';
|
import { binaryEncode, binaryDecode } from '../Common/Binary';
|
||||||
import { binaryDecode } from '../Utils';
|
|
||||||
|
|
||||||
const TIMEOUT = 5000
|
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]) {
|
sendMsg<T extends keyof IModel['msg']>(name: T, data: IModel['msg'][T]) {
|
||||||
const view = binaryEncode(name, data)
|
const view = binaryEncode(name, data)
|
||||||
console.log("view", view.buffer);
|
|
||||||
|
|
||||||
this.ws.send(view.buffer)
|
this.ws.send(view.buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,79 +9,3 @@ export const sortSpriteFrame = (spriteFrame: Array<SpriteFrame>) =>
|
|||||||
spriteFrame.sort((a, b) => getNumberWithinString(a.name) - getNumberWithinString(b.name))
|
spriteFrame.sort((a, b) => getNumberWithinString(a.name) - getNumberWithinString(b.name))
|
||||||
|
|
||||||
export const rad2Angle = (rad: number) => rad / Math.PI * 180
|
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))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +1,7 @@
|
|||||||
import { ApiMsgEnum, InputTypeEnum } from "./Enum";
|
import { ApiMsgEnum, InputTypeEnum } from "./Enum";
|
||||||
import { strencode } from "./Utils";
|
import { strdecode, strencode } from "./Utils";
|
||||||
|
|
||||||
export const binaryEncode = (proto: ApiMsgEnum, data: any): DataView => {
|
const encodeActorMove = (proto: ApiMsgEnum, data: any, view: DataView, index: number) => {
|
||||||
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.type)
|
||||||
view.setUint8(index++, data.id)
|
view.setUint8(index++, data.id)
|
||||||
view.setFloat32(index, data.direction.x)
|
view.setFloat32(index, data.direction.x)
|
||||||
@ -17,15 +10,11 @@ export const binaryEncode = (proto: ApiMsgEnum, data: any): DataView => {
|
|||||||
index += 4
|
index += 4
|
||||||
view.setFloat32(index, data.dt)
|
view.setFloat32(index, data.dt)
|
||||||
index += 4
|
index += 4
|
||||||
return view
|
|
||||||
}
|
}
|
||||||
case InputTypeEnum.WeaponShoot: {
|
|
||||||
let index = 0
|
const encodeWeaponShoot = (proto: ApiMsgEnum, data: any, view: DataView, index: number) => {
|
||||||
const ab = new ArrayBuffer(3 + 16)
|
|
||||||
const view = new DataView(ab)
|
|
||||||
view.setUint8(index++, proto)
|
|
||||||
view.setUint8(index++, data.type)
|
view.setUint8(index++, data.type)
|
||||||
view.setUint8(index++, data.id)
|
view.setUint8(index++, data.owner)
|
||||||
view.setFloat32(index, data.position.x)
|
view.setFloat32(index, data.position.x)
|
||||||
index += 4
|
index += 4
|
||||||
view.setFloat32(index, data.position.y)
|
view.setFloat32(index, data.position.y)
|
||||||
@ -34,24 +23,70 @@ export const binaryEncode = (proto: ApiMsgEnum, data: any): DataView => {
|
|||||||
index += 4
|
index += 4
|
||||||
view.setFloat32(index, data.direction.y)
|
view.setFloat32(index, data.direction.y)
|
||||||
index += 4
|
index += 4
|
||||||
return view
|
|
||||||
}
|
}
|
||||||
case InputTypeEnum.TimePast: {
|
|
||||||
let index = 0
|
export const encdoeTimePast = (proto: ApiMsgEnum, data: any, view: DataView, index: number) => {
|
||||||
const ab = new ArrayBuffer(1 + 1 + 4)
|
|
||||||
const view = new DataView(ab)
|
|
||||||
view.setUint8(index++, proto)
|
|
||||||
view.setUint8(index++, data.type)
|
view.setUint8(index++, data.type)
|
||||||
view.setFloat32(index, data.dt)
|
view.setFloat32(index, data.dt)
|
||||||
index += 4
|
index += 4
|
||||||
return view
|
|
||||||
}
|
}
|
||||||
default: {
|
|
||||||
const ab = new ArrayBuffer(0)
|
export const binaryEncode = (proto: ApiMsgEnum, data: any): DataView => {
|
||||||
|
if (proto === ApiMsgEnum.MsgClientSync) {
|
||||||
|
if (data.type === InputTypeEnum.ActorMove) {
|
||||||
|
let index = 0
|
||||||
|
const ab = new ArrayBuffer(3 + 12)
|
||||||
const view = new DataView(ab)
|
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
|
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 {
|
} else {
|
||||||
let index = 0
|
let index = 0
|
||||||
const str = JSON.stringify(data)
|
const str = JSON.stringify(data)
|
||||||
@ -65,3 +100,112 @@ export const binaryEncode = (proto: ApiMsgEnum, data: any): DataView => {
|
|||||||
return view
|
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 './Model'
|
||||||
export * from './State'
|
export * from './State'
|
||||||
export * from './Utils'
|
export * from './Utils'
|
||||||
|
export * from './Binary'
|
||||||
|
|
||||||
export interface IModel {
|
export interface IModel {
|
||||||
api: {
|
api: {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import WebSocket from 'ws';
|
import WebSocket from 'ws';
|
||||||
import { EventEmitter } from 'stream';
|
import { EventEmitter } from 'stream';
|
||||||
import { MyServer } from './MyServer';
|
import { MyServer } from './MyServer';
|
||||||
import { binaryDecode, getTime } from '../Utils';
|
import { getTime, toArrayBuffer } from '../Utils';
|
||||||
import { ApiMsgEnum, IModel } from '../Common';
|
import { ApiMsgEnum, IModel } from '../Common';
|
||||||
import { binaryEncode } from '../Common/Binary';
|
import { binaryEncode, binaryDecode } from '../Common/Binary';
|
||||||
|
|
||||||
export enum ConnectionEventEnum {
|
export enum ConnectionEventEnum {
|
||||||
Close = 'Close',
|
Close = 'Close',
|
||||||
@ -27,7 +27,7 @@ export class Connection extends EventEmitter {
|
|||||||
this.ws.on('message', (buffer: Buffer) => {
|
this.ws.on('message', (buffer: Buffer) => {
|
||||||
// const str = buffer.toString()
|
// const str = buffer.toString()
|
||||||
try {
|
try {
|
||||||
const json = binaryDecode(buffer)
|
const json = binaryDecode(toArrayBuffer(buffer))
|
||||||
const { name, data } = json
|
const { name, data } = json
|
||||||
// console.log(`${getTime()}接收|字节数${buffer.length}|${this.playerId || -1}|${str}`)
|
// console.log(`${getTime()}接收|字节数${buffer.length}|${this.playerId || -1}|${str}`)
|
||||||
console.log(`${getTime()}接收|字节数${buffer.length}|${this.playerId || -1}|${JSON.stringify(json)}`)
|
console.log(`${getTime()}接收|字节数${buffer.length}|${this.playerId || -1}|${JSON.stringify(json)}`)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { ApiMsgEnum, InputTypeEnum, strdecode } from '../Common'
|
|
||||||
|
|
||||||
export const getTime = () => new Date().toLocaleString().split("├")[0]
|
export const getTime = () => new Date().toLocaleString().split("├")[0]
|
||||||
|
|
||||||
@ -37,77 +36,11 @@ export const copyCommon = async () => {
|
|||||||
console.log('同步成功!')
|
console.log('同步成功!')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const binaryDecode = (buffer: Buffer) => {
|
export const toArrayBuffer = (buffer: Buffer) => {
|
||||||
let index = 0
|
var ab = new ArrayBuffer(buffer.length);
|
||||||
const type = buffer.readUint8(index++)
|
var view = new Uint8Array(ab);
|
||||||
|
for (var i = 0; i < buffer.length; ++i) {
|
||||||
if (type === ApiMsgEnum.MsgClientSync) {
|
view[i] = buffer[i];
|
||||||
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))))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return ab;
|
||||||
}
|
}
|
34
test.js
34
test.js
@ -1,30 +1,4 @@
|
|||||||
const msg = JSON.stringify({
|
const ab = new ArrayBuffer(10)
|
||||||
a: 123,
|
const view = new DataView(ab)
|
||||||
b: true,
|
view.setFloat32(0, 0.0023)
|
||||||
c: "456"
|
console.log(view);
|
||||||
})
|
|
||||||
|
|
||||||
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]);
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user