binary encode
This commit is contained in:
parent
a678a5b3fc
commit
c31af6b02a
@ -1,10 +1,10 @@
|
|||||||
import { _decorator, instantiate, ProgressBar, Label } from 'cc';
|
import { _decorator, instantiate, ProgressBar, Label } from 'cc';
|
||||||
import { EntityManager } from '../../Base/EntityManager';
|
import { EntityManager } from '../../Base/EntityManager';
|
||||||
import { ApiMsgEnum, EntityTypeEnum, IActor, InputTypeEnum, IVec2 } from '../../Common';
|
import { ApiMsgEnum, EntityTypeEnum, IActor, InputTypeEnum, IVec2, toFixed } from '../../Common';
|
||||||
import { EntityStateEnum } from '../../Enum';
|
import { EntityStateEnum } from '../../Enum';
|
||||||
import DataManager from '../../Global/DataManager';
|
import DataManager from '../../Global/DataManager';
|
||||||
import NetworkManager from '../../Global/NetworkManager';
|
import NetworkManager from '../../Global/NetworkManager';
|
||||||
import { rad2Angle, toFixed } from '../../Utils';
|
import { rad2Angle } from '../../Utils';
|
||||||
import { WeaponManager } from '../Weapon/WeaponManager';
|
import { WeaponManager } from '../Weapon/WeaponManager';
|
||||||
import { PlayerStateMachine } from './ActorStateMachine';
|
import { PlayerStateMachine } from './ActorStateMachine';
|
||||||
const { ccclass } = _decorator;
|
const { ccclass } = _decorator;
|
||||||
@ -59,9 +59,9 @@ export class ActorManager extends EntityManager implements IActor {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (DataManager.Instance.jm.input.length()) {
|
||||||
const { x, y } = DataManager.Instance.jm.input
|
const { x, y } = DataManager.Instance.jm.input
|
||||||
NetworkManager.Instance.sendMsg(ApiMsgEnum.MsgClientSync, {
|
NetworkManager.Instance.sendMsg(ApiMsgEnum.MsgClientSync, {
|
||||||
input: {
|
|
||||||
type: InputTypeEnum.ActorMove,
|
type: InputTypeEnum.ActorMove,
|
||||||
id: this.id,
|
id: this.id,
|
||||||
direction: {
|
direction: {
|
||||||
@ -69,10 +69,11 @@ export class ActorManager extends EntityManager implements IActor {
|
|||||||
y: toFixed(y),
|
y: toFixed(y),
|
||||||
},
|
},
|
||||||
dt: toFixed(dt)
|
dt: toFixed(dt)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
render(data: IActor) {
|
render(data: IActor) {
|
||||||
this.renderHP(data)
|
this.renderHP(data)
|
||||||
this.renderPosition(data)
|
this.renderPosition(data)
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import { _decorator, Node, Vec2, UITransform } from 'cc'
|
import { _decorator, Node, Vec2, UITransform } from 'cc'
|
||||||
import { EntityManager } from '../../Base/EntityManager'
|
import { EntityManager } from '../../Base/EntityManager'
|
||||||
import { ApiMsgEnum, EntityTypeEnum, InputTypeEnum } from '../../Common'
|
import { ApiMsgEnum, EntityTypeEnum, InputTypeEnum, toFixed } from '../../Common'
|
||||||
import { EntityStateEnum, EventEnum } from '../../Enum'
|
import { EntityStateEnum, EventEnum } from '../../Enum'
|
||||||
import DataManager from '../../Global/DataManager'
|
import DataManager from '../../Global/DataManager'
|
||||||
import EventManager from '../../Global/EventManager'
|
import EventManager from '../../Global/EventManager'
|
||||||
import NetworkManager from '../../Global/NetworkManager'
|
import NetworkManager from '../../Global/NetworkManager'
|
||||||
import { toFixed } from '../../Utils'
|
|
||||||
import { WeaponStateMachine } from './WeaponStateMachine'
|
import { WeaponStateMachine } from './WeaponStateMachine'
|
||||||
const { ccclass } = _decorator
|
const { ccclass } = _decorator
|
||||||
|
|
||||||
@ -62,7 +61,6 @@ export class WeaponManager extends EntityManager {
|
|||||||
const directionVec2 = new Vec2(pointWorldPos.x - anchorWorldPos.x, pointWorldPos.y - anchorWorldPos.y).normalize()
|
const directionVec2 = new Vec2(pointWorldPos.x - anchorWorldPos.x, pointWorldPos.y - anchorWorldPos.y).normalize()
|
||||||
|
|
||||||
NetworkManager.Instance.sendMsg(ApiMsgEnum.MsgClientSync, {
|
NetworkManager.Instance.sendMsg(ApiMsgEnum.MsgClientSync, {
|
||||||
input: {
|
|
||||||
type: InputTypeEnum.WeaponShoot,
|
type: InputTypeEnum.WeaponShoot,
|
||||||
owner: this.owner,
|
owner: this.owner,
|
||||||
position: {
|
position: {
|
||||||
@ -73,7 +71,6 @@ export class WeaponManager extends EntityManager {
|
|||||||
x: toFixed(directionVec2.x),
|
x: toFixed(directionVec2.x),
|
||||||
y: toFixed(directionVec2.y),
|
y: toFixed(directionVec2.y),
|
||||||
},
|
},
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Node, Prefab, SpriteFrame } from 'cc'
|
import { Node, Prefab, SpriteFrame } from 'cc'
|
||||||
import Singleton from '../Base/Singleton'
|
import Singleton from '../Base/Singleton'
|
||||||
import { EntityTypeEnum, IBullet, IClientInput, InputTypeEnum, IRoom, IState } from '../Common'
|
import { EntityTypeEnum, IBullet, IClientInput, InputTypeEnum, IRoom, IState, toFixed } from '../Common'
|
||||||
import { ActorManager } from '../Entity/Actor/ActorManager'
|
import { ActorManager } from '../Entity/Actor/ActorManager'
|
||||||
import { BulletManager } from '../Entity/Bullet/BulletManager'
|
import { BulletManager } from '../Entity/Bullet/BulletManager'
|
||||||
import { EventEnum } from '../Enum'
|
import { EventEnum } from '../Enum'
|
||||||
@ -82,8 +82,8 @@ export default class DataManager extends Singleton {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
player.position.x += x * PLAYER_SPEED * dt
|
player.position.x += toFixed(x * PLAYER_SPEED * dt)
|
||||||
player.position.y += y * PLAYER_SPEED * dt
|
player.position.y += toFixed(y * PLAYER_SPEED * dt)
|
||||||
player.direction = { x, y }
|
player.direction = { x, y }
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -112,8 +112,8 @@ export default class DataManager extends Singleton {
|
|||||||
const player = players[j];
|
const player = players[j];
|
||||||
if (((player.position.x - bullet.position.x) ** 2 + (player.position.y - bullet.position.y) ** 2) < (PLAYER_RADIUS + BULLET_RADIUS) ** 2) {
|
if (((player.position.x - bullet.position.x) ** 2 + (player.position.y - bullet.position.y) ** 2) < (PLAYER_RADIUS + BULLET_RADIUS) ** 2) {
|
||||||
EventManager.Instance.emit(EventEnum.ExplosionBorn, bullet.id, {
|
EventManager.Instance.emit(EventEnum.ExplosionBorn, bullet.id, {
|
||||||
x: (player.position.x + bullet.position.x) / 2,
|
x: toFixed((player.position.x + bullet.position.x) / 2),
|
||||||
y: (player.position.y + bullet.position.y) / 2,
|
y: toFixed((player.position.y + bullet.position.y) / 2),
|
||||||
})
|
})
|
||||||
|
|
||||||
player.hp -= WEAPON_DAMAGE
|
player.hp -= WEAPON_DAMAGE
|
||||||
@ -132,8 +132,8 @@ export default class DataManager extends Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const bullet of this.state.bullets) {
|
for (const bullet of this.state.bullets) {
|
||||||
bullet.position.x += bullet.direction.x * BULLET_SPEED * dt
|
bullet.position.x += toFixed(bullet.direction.x * BULLET_SPEED * dt)
|
||||||
bullet.position.y += bullet.direction.y * BULLET_SPEED * dt
|
bullet.position.y += toFixed(bullet.direction.y * BULLET_SPEED * dt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import Singleton from '../Base/Singleton'
|
import Singleton from '../Base/Singleton'
|
||||||
import { IModel } from '../Common';
|
import { ApiMsgEnum, IModel, strdecode, strencode } from '../Common';
|
||||||
|
import { binaryEncode } from '../Common/Binary';
|
||||||
|
import { binaryDecode } from '../Utils';
|
||||||
|
|
||||||
const TIMEOUT = 5000
|
const TIMEOUT = 5000
|
||||||
|
|
||||||
@ -17,7 +19,7 @@ export default class NetworkManager extends Singleton {
|
|||||||
|
|
||||||
ws: WebSocket
|
ws: WebSocket
|
||||||
port = 8888
|
port = 8888
|
||||||
cbs: Map<string, Function[]> = new Map()
|
maps: Map<ApiMsgEnum, Function[]> = new Map()
|
||||||
isConnected = false
|
isConnected = false
|
||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
@ -27,6 +29,8 @@ export default class NetworkManager extends Singleton {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.ws = new WebSocket(`ws://localhost:${this.port}`)
|
this.ws = new WebSocket(`ws://localhost:${this.port}`)
|
||||||
|
|
||||||
|
this.ws.binaryType = 'arraybuffer';
|
||||||
this.ws.onopen = () => {
|
this.ws.onopen = () => {
|
||||||
console.log("ws onopen")
|
console.log("ws onopen")
|
||||||
this.isConnected = true
|
this.isConnected = true
|
||||||
@ -45,15 +49,15 @@ export default class NetworkManager extends Singleton {
|
|||||||
|
|
||||||
this.ws.onmessage = (e) => {
|
this.ws.onmessage = (e) => {
|
||||||
try {
|
try {
|
||||||
const json = JSON.parse(e.data)
|
const json = binaryDecode(e.data)
|
||||||
const { name, data } = json
|
const { name, data } = json
|
||||||
try {
|
try {
|
||||||
if (this.cbs.has(name) && this.cbs.get(name).length) {
|
if (this.maps.has(name) && this.maps.get(name).length) {
|
||||||
console.log(json);
|
console.log(json);
|
||||||
this.cbs.get(name).forEach(cb => cb(data))
|
this.maps.get(name).forEach(cb => cb(data))
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("this.cbs.get(name).forEach(cb => cb(restData))", error)
|
console.log("this.maps.get(name).forEach(cb => cb(restData))", error)
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -80,7 +84,7 @@ export default class NetworkManager extends Singleton {
|
|||||||
}
|
}
|
||||||
this.listenMsg(name as any, cb)
|
this.listenMsg(name as any, cb)
|
||||||
|
|
||||||
this.ws.send(JSON.stringify({ name, data }))
|
this.sendMsg(name as any, data)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
resolve({ success: false, error: error as Error })
|
resolve({ success: false, error: error as Error })
|
||||||
@ -89,21 +93,24 @@ 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]) {
|
||||||
this.ws.send(JSON.stringify({ name, data }))
|
const view = binaryEncode(name, data)
|
||||||
|
console.log("view", view.buffer);
|
||||||
|
|
||||||
|
this.ws.send(view.buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
listenMsg<T extends keyof IModel['msg']>(name: T, cb: (args: IModel['msg'][T]) => void) {
|
listenMsg<T extends keyof IModel['msg']>(name: T, cb: (args: IModel['msg'][T]) => void) {
|
||||||
if (this.cbs.has(name)) {
|
if (this.maps.has(name)) {
|
||||||
this.cbs.get(name).push(cb)
|
this.maps.get(name).push(cb)
|
||||||
} else {
|
} else {
|
||||||
this.cbs.set(name, [cb])
|
this.maps.set(name, [cb])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unlistenMsg(name: string, cb: Function) {
|
unlistenMsg(name: ApiMsgEnum, cb: Function) {
|
||||||
if (this.cbs.has(name)) {
|
if (this.maps.has(name)) {
|
||||||
const index = this.cbs.get(name).indexOf(cb)
|
const index = this.maps.get(name).indexOf(cb)
|
||||||
index > -1 && this.cbs.get(name).splice(index, 1)
|
index > -1 && this.maps.get(name).splice(index, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import NetworkManager from '../Global/NetworkManager';
|
|||||||
import ObjectPoolManager from '../Global/ObjectPoolManager';
|
import ObjectPoolManager from '../Global/ObjectPoolManager';
|
||||||
import { BulletManager } from '../Entity/Bullet/BulletManager';
|
import { BulletManager } from '../Entity/Bullet/BulletManager';
|
||||||
import { ApiMsgEnum, EntityTypeEnum, IMsgServerSync, InputTypeEnum } from '../Common';
|
import { ApiMsgEnum, EntityTypeEnum, IMsgServerSync, InputTypeEnum } from '../Common';
|
||||||
import { toFixed } from '../Utils';
|
|
||||||
|
|
||||||
const { ccclass } = _decorator;
|
const { ccclass } = _decorator;
|
||||||
|
|
||||||
@ -86,7 +85,7 @@ export class BattleManager extends Component {
|
|||||||
map.setParent(this.stage)
|
map.setParent(this.stage)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSync({ inputs }: IMsgServerSync) {
|
handleSync(inputs: IMsgServerSync) {
|
||||||
for (const input of inputs) {
|
for (const input of inputs) {
|
||||||
DataManager.Instance.applyInput(input)
|
DataManager.Instance.applyInput(input)
|
||||||
}
|
}
|
||||||
@ -102,7 +101,7 @@ export class BattleManager extends Component {
|
|||||||
|
|
||||||
tick(dt: number) {
|
tick(dt: number) {
|
||||||
this.tickPlayer(dt)
|
this.tickPlayer(dt)
|
||||||
this.tickGlobal(dt)
|
// this.tickGlobal(dt)
|
||||||
}
|
}
|
||||||
|
|
||||||
tickPlayer(dt: number) {
|
tickPlayer(dt: number) {
|
||||||
@ -115,14 +114,14 @@ export class BattleManager extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tickGlobal(dt: number) {
|
// tickGlobal(dt: number) {
|
||||||
NetworkManager.Instance.sendMsg(ApiMsgEnum.MsgClientSync, {
|
// NetworkManager.Instance.sendMsg(ApiMsgEnum.MsgClientSync, {
|
||||||
input: {
|
// input: {
|
||||||
type: InputTypeEnum.TimePast,
|
// type: InputTypeEnum.TimePast,
|
||||||
dt: toFixed(dt),
|
// dt: toFixed(dt),
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
this.renderPlayer()
|
this.renderPlayer()
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { SpriteFrame } from "cc"
|
import { SpriteFrame } from "cc"
|
||||||
|
import { ApiMsgEnum, InputTypeEnum, strdecode } from "../Common"
|
||||||
|
|
||||||
const INDEX_REG = /\((\d+)\)/
|
const INDEX_REG = /\((\d+)\)/
|
||||||
|
|
||||||
@ -9,4 +10,78 @@ export const sortSpriteFrame = (spriteFrame: Array<SpriteFrame>) =>
|
|||||||
|
|
||||||
export const rad2Angle = (rad: number) => rad / Math.PI * 180
|
export const rad2Angle = (rad: number) => rad / Math.PI * 180
|
||||||
|
|
||||||
export const toFixed = (num: number, digit: number = 4): number => Math.floor(num * 10 ** digit) / 10 ** digit
|
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))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
apps/server/src/Common/Binary.ts
Normal file
67
apps/server/src/Common/Binary.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import { ApiMsgEnum, InputTypeEnum } from "./Enum";
|
||||||
|
import { strencode } from "./Utils";
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let index = 0
|
||||||
|
const str = JSON.stringify(data)
|
||||||
|
const ta = strencode(str)
|
||||||
|
const ab = new ArrayBuffer(ta.length + 1)
|
||||||
|
const view = new DataView(ab)
|
||||||
|
view.setUint8(index++, proto)
|
||||||
|
for (let i = 0; i < ta.length; i++) {
|
||||||
|
view.setUint8(index++, ta[i])
|
||||||
|
}
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
}
|
9
apps/server/src/Common/Binary.ts.meta
Normal file
9
apps/server/src/Common/Binary.ts.meta
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.23",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "9888254e-f4d1-4b2a-a814-75fb288e474f",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
@ -1,23 +1,46 @@
|
|||||||
|
// export enum ApiMsgEnum {
|
||||||
|
// ApiPlayerList = 'ApiPlayerList',
|
||||||
|
// ApiPlayerJoin = 'ApiPlayerJoin',
|
||||||
|
// ApiRoomList = 'ApiRoomList',
|
||||||
|
// ApiRoomCreate = 'ApiRoomCreate',
|
||||||
|
// ApiRoomJoin = 'ApiRoomJoin',
|
||||||
|
// ApiRoomLeave = 'ApiRoomLeave',
|
||||||
|
// ApiGameStart = 'ApiGameStart',
|
||||||
|
// MsgPlayerList = 'MsgPlayerList',
|
||||||
|
// MsgRoomList = 'MsgRoomList',
|
||||||
|
// MsgRoom = 'MsgRoom',
|
||||||
|
// MsgGameStart = 'MsgGameStart',
|
||||||
|
// MsgClientSync = 'MsgClientSync',
|
||||||
|
// MsgServerSync = 'MsgServerSync',
|
||||||
|
// }
|
||||||
|
|
||||||
export enum ApiMsgEnum {
|
export enum ApiMsgEnum {
|
||||||
ApiPlayerList = 'ApiPlayerList',
|
ApiPlayerList,
|
||||||
ApiPlayerJoin = 'ApiPlayerJoin',
|
ApiPlayerJoin,
|
||||||
ApiRoomList = 'ApiRoomList',
|
ApiRoomList,
|
||||||
ApiRoomCreate = 'ApiRoomCreate',
|
ApiRoomCreate,
|
||||||
ApiRoomJoin = 'ApiRoomJoin',
|
ApiRoomJoin,
|
||||||
ApiRoomLeave = 'ApiRoomLeave',
|
ApiRoomLeave,
|
||||||
ApiGameStart = 'ApiGameStart',
|
ApiGameStart,
|
||||||
MsgPlayerList = 'MsgPlayerList',
|
MsgPlayerList,
|
||||||
MsgRoomList = 'MsgRoomList',
|
MsgRoomList,
|
||||||
MsgRoom = 'MsgRoom',
|
MsgRoom,
|
||||||
MsgGameStart = 'MsgGameStart',
|
MsgGameStart,
|
||||||
MsgClientSync = 'MsgClientSync',
|
MsgClientSync,
|
||||||
MsgServerSync = 'MsgServerSync',
|
MsgServerSync,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// export enum InputTypeEnum {
|
||||||
|
// ActorMove = 'ActorMove',
|
||||||
|
// WeaponShoot = 'WeaponShoot',
|
||||||
|
// TimePast = 'TimePast',
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
export enum InputTypeEnum {
|
export enum InputTypeEnum {
|
||||||
ActorMove = 'ActorMove',
|
ActorMove,
|
||||||
WeaponShoot = 'WeaponShoot',
|
WeaponShoot,
|
||||||
TimePast = 'TimePast',
|
TimePast,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum EntityTypeEnum {
|
export enum EntityTypeEnum {
|
||||||
|
@ -22,10 +22,6 @@ export interface IMsgGameStart {
|
|||||||
state: IState
|
state: IState
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMsgClientSync {
|
export type IMsgClientSync = IClientInput
|
||||||
input: IClientInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IMsgServerSync {
|
export type IMsgServerSync = Array<IClientInput>
|
||||||
inputs: Array<IClientInput>
|
|
||||||
}
|
|
42
apps/server/src/Common/Utils.ts
Normal file
42
apps/server/src/Common/Utils.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
export const toFixed = (num: number, digit: number = 4): number => Math.floor(num * 10 ** digit) / 10 ** digit
|
||||||
|
|
||||||
|
export const strencode = (str: string) => {
|
||||||
|
let byteArray: number[] = [];
|
||||||
|
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 new Uint8Array(byteArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const strdecode = (bytes: Uint8Array) => {
|
||||||
|
let array: number[] = [];
|
||||||
|
let offset = 0;
|
||||||
|
let charCode = 0;
|
||||||
|
let end = bytes.length;
|
||||||
|
while (offset < end) {
|
||||||
|
if (bytes[offset] < 128) {
|
||||||
|
charCode = bytes[offset];
|
||||||
|
offset += 1;
|
||||||
|
} else if (bytes[offset] < 224) {
|
||||||
|
charCode = ((bytes[offset] & 0x3f) << 6) + (bytes[offset + 1] & 0x3f);
|
||||||
|
offset += 2;
|
||||||
|
} else if (bytes[offset] < 240) {
|
||||||
|
charCode = ((bytes[offset] & 0x0f) << 12) + ((bytes[offset + 1] & 0x3f) << 6) + (bytes[offset + 2] & 0x3f);
|
||||||
|
offset += 3;
|
||||||
|
} else {
|
||||||
|
charCode = ((bytes[offset] & 0x07) << 18) + ((bytes[offset + 1] & 0x3f) << 12) + ((bytes[offset + 1] & 0x3f) << 6) + (bytes[offset + 2] & 0x3f);
|
||||||
|
offset += 4;
|
||||||
|
}
|
||||||
|
array.push(charCode);
|
||||||
|
}
|
||||||
|
return String.fromCharCode.apply(null, array);
|
||||||
|
}
|
9
apps/server/src/Common/Utils.ts.meta
Normal file
9
apps/server/src/Common/Utils.ts.meta
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.23",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "b44d77f2-f4af-4b06-bc29-986d191fe180",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
64
apps/server/src/Core/MyServer.ts
Normal file
64
apps/server/src/Core/MyServer.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import { EventEmitter } from 'stream';
|
||||||
|
import WebSocket, { WebSocketServer } from 'ws';
|
||||||
|
import { ApiMsgEnum } from '../Common';
|
||||||
|
import { Connection, ConnectionEventEnum } from './Connection';
|
||||||
|
|
||||||
|
export interface IMyServerOptions {
|
||||||
|
port: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum MyServerEventEnum {
|
||||||
|
Connect = 'Connect',
|
||||||
|
DisConnect = 'DisConnect',
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MyServer extends EventEmitter {
|
||||||
|
wss?: WebSocketServer
|
||||||
|
port: number
|
||||||
|
connections: Set<Connection> = new Set()
|
||||||
|
apiMap: Map<ApiMsgEnum, Function> = new Map()
|
||||||
|
|
||||||
|
constructor({ port = 8080 }: Partial<IMyServerOptions>) {
|
||||||
|
super()
|
||||||
|
this.port = port
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.wss = new WebSocketServer({ port: this.port });
|
||||||
|
this.wss.on('connection', this.handleConnect.bind(this));
|
||||||
|
|
||||||
|
this.wss.on("error", (e) => {
|
||||||
|
reject(e)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.wss.on("close", () => {
|
||||||
|
console.log("MyServer 服务关闭");
|
||||||
|
})
|
||||||
|
|
||||||
|
this.wss.on("listening", () => {
|
||||||
|
resolve(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handleConnect(ws: WebSocket) {
|
||||||
|
//初始化
|
||||||
|
const connection = new Connection(this, ws)
|
||||||
|
|
||||||
|
//向外告知有人来了
|
||||||
|
this.connections.add(connection)
|
||||||
|
this.emit(MyServerEventEnum.Connect, connection)
|
||||||
|
|
||||||
|
//向外告知有人走了
|
||||||
|
connection.on(ConnectionEventEnum.Close, (code: number, reason: string) => {
|
||||||
|
this.connections.delete(connection)
|
||||||
|
this.emit(MyServerEventEnum.DisConnect, connection, code, reason)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setApi(apiName: ApiMsgEnum, cb: Function) {
|
||||||
|
this.apiMap.set(apiName, cb)
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import Connection from '../Core/Connection'
|
import { Connection } from "../Core"
|
||||||
|
|
||||||
export default class Player {
|
export default class Player {
|
||||||
id: number
|
id: number
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import Singleton from '../Base/Singleton'
|
import Singleton from '../Base/Singleton'
|
||||||
import { ApiMsgEnum, IApiPlayerJoinReq } from '../Common'
|
import { ApiMsgEnum, IApiPlayerJoinReq } from '../Common'
|
||||||
|
import { Connection } from '../Core'
|
||||||
import Player from './Player'
|
import Player from './Player'
|
||||||
import RoomManager from './RoomManager'
|
import RoomManager from './RoomManager'
|
||||||
import Connection from '../Core/Connection'
|
|
||||||
|
|
||||||
export default class PlayerManager extends Singleton {
|
export default class PlayerManager extends Singleton {
|
||||||
static get Instance() {
|
static get Instance() {
|
||||||
return super.GetInstance<PlayerManager>()
|
return super.GetInstance<PlayerManager>()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ApiMsgEnum, EntityTypeEnum, IClientInput, IState } from '../Common'
|
import { ApiMsgEnum, EntityTypeEnum, IClientInput, InputTypeEnum, IState, toFixed } from '../Common'
|
||||||
import type Player from './Player'
|
import type Player from './Player'
|
||||||
import PlayerManager from './PlayerManager'
|
import PlayerManager from './PlayerManager'
|
||||||
import RoomManager from './RoomManager'
|
import RoomManager from './RoomManager'
|
||||||
@ -6,12 +6,14 @@ import RoomManager from './RoomManager'
|
|||||||
export default class Room {
|
export default class Room {
|
||||||
id: number
|
id: number
|
||||||
players: Set<Player> = new Set()
|
players: Set<Player> = new Set()
|
||||||
|
lastSyncTime?: number
|
||||||
|
|
||||||
|
private inputs: Array<IClientInput> = []
|
||||||
|
|
||||||
constructor(rid: number) {
|
constructor(rid: number) {
|
||||||
this.id = rid
|
this.id = rid
|
||||||
}
|
}
|
||||||
|
|
||||||
private inputs: Array<IClientInput> = []
|
|
||||||
|
|
||||||
join(uid: number) {
|
join(uid: number) {
|
||||||
const player = PlayerManager.Instance.getPlayerById(uid)
|
const player = PlayerManager.Instance.getPlayerById(uid)
|
||||||
@ -71,12 +73,15 @@ export default class Room {
|
|||||||
this.listenPlayer()
|
this.listenPlayer()
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
this.syncInput()
|
this.syncInput()
|
||||||
}, 300)
|
}, 100)
|
||||||
|
setInterval(() => {
|
||||||
|
this.timePast()
|
||||||
|
}, 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
listenPlayer() {
|
listenPlayer() {
|
||||||
for (const player of this.players) {
|
for (const player of this.players) {
|
||||||
player.connection.listenMsg(ApiMsgEnum.MsgClientSync, ({ input }) => {
|
player.connection.listenMsg(ApiMsgEnum.MsgClientSync, (input) => {
|
||||||
this.inputs.push(input)
|
this.inputs.push(input)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -85,10 +90,21 @@ export default class Room {
|
|||||||
syncInput() {
|
syncInput() {
|
||||||
const inputs = this.inputs
|
const inputs = this.inputs
|
||||||
this.inputs = []
|
this.inputs = []
|
||||||
|
|
||||||
|
|
||||||
for (const player of this.players) {
|
for (const player of this.players) {
|
||||||
player.connection.sendMsg(ApiMsgEnum.MsgServerSync, {
|
player.connection.sendMsg(ApiMsgEnum.MsgServerSync, inputs)
|
||||||
inputs
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
timePast() {
|
||||||
|
let now = process.uptime();
|
||||||
|
const dt = now - (this.lastSyncTime ?? now)
|
||||||
|
this.inputs.push({
|
||||||
|
type: InputTypeEnum.TimePast,
|
||||||
|
dt: toFixed(dt)
|
||||||
|
})
|
||||||
|
this.lastSyncTime = now;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { IApiGameStartReq, IApiGameStartRes, IApiPlayerJoinReq, IApiPlayerJoinRes, IApiPlayerListReq, IApiPlayerListRes, IApiRoomCreateReq, IApiRoomCreateRes, IApiRoomJoinReq, IApiRoomJoinRes, IApiRoomLeaveReq, IApiRoomLeaveRes, IApiRoomListReq, IApiRoomListRes } from './Api'
|
import { IApiGameStartReq, IApiGameStartRes, IApiPlayerJoinReq, IApiPlayerJoinRes, IApiPlayerListReq, IApiPlayerListRes, IApiRoomCreateReq, IApiRoomCreateRes, IApiRoomJoinReq, IApiRoomJoinRes, IApiRoomLeaveReq, IApiRoomLeaveRes, IApiRoomListReq, IApiRoomListRes } from './Api'
|
||||||
import { ApiMsgEnum } from './Enum'
|
import { ApiMsgEnum } from './Enum'
|
||||||
import { IMsgClientSync, IMsgGameStart, IMsgPlayerList, IMsgRoom, IMsgRoomList, IMsgServerSync } from './Msg'
|
import { IMsgClientSync, IMsgGameStart, IMsgPlayerList, IMsgRoom, IMsgRoomList, IMsgServerSync } from './Msg'
|
||||||
import { IClientInput } from './State'
|
|
||||||
export * from './Api'
|
export * from './Api'
|
||||||
export * from './Msg'
|
export * from './Msg'
|
||||||
export * from './Enum'
|
export * from './Enum'
|
||||||
export * from './Model'
|
export * from './Model'
|
||||||
export * from './State'
|
export * from './State'
|
||||||
|
export * from './Utils'
|
||||||
|
|
||||||
export interface IModel {
|
export interface IModel {
|
||||||
api: {
|
api: {
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
import WebSocket from 'ws';
|
import WebSocket from 'ws';
|
||||||
import { EventEmitter } from 'stream';
|
import { EventEmitter } from 'stream';
|
||||||
import MyServer, { IData } from '.';
|
import { MyServer } from './MyServer';
|
||||||
import { getTime } from '../Utils';
|
import { binaryDecode, getTime } from '../Utils';
|
||||||
import { IModel } from '../Common';
|
import { ApiMsgEnum, IModel } from '../Common';
|
||||||
|
import { binaryEncode } from '../Common/Binary';
|
||||||
|
|
||||||
export enum ConnectionEventEnum {
|
export enum ConnectionEventEnum {
|
||||||
Close = 'Close',
|
Close = 'Close',
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Connection extends EventEmitter {
|
export class Connection extends EventEmitter {
|
||||||
server: MyServer
|
server: MyServer
|
||||||
ws: WebSocket
|
ws: WebSocket
|
||||||
msgMap: Map<string, Function[]> = new Map()
|
msgMap: Map<ApiMsgEnum, Function[]> = new Map()
|
||||||
playerId?: number;
|
playerId?: number;
|
||||||
|
|
||||||
constructor(server: MyServer, ws: WebSocket) {
|
constructor(server: MyServer, ws: WebSocket) {
|
||||||
@ -24,10 +25,12 @@ export default 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 { name, data } = JSON.parse(str)
|
const json = binaryDecode(buffer)
|
||||||
console.log(`${getTime()}接收|${this.playerId || -1}|${str}`)
|
const { name, data } = json
|
||||||
|
// console.log(`${getTime()}接收|字节数${buffer.length}|${this.playerId || -1}|${str}`)
|
||||||
|
console.log(`${getTime()}接收|字节数${buffer.length}|${this.playerId || -1}|${JSON.stringify(json)}`)
|
||||||
if (this.server.apiMap.has(name)) {
|
if (this.server.apiMap.has(name)) {
|
||||||
try {
|
try {
|
||||||
const cb = this.server.apiMap.get(name)
|
const cb = this.server.apiMap.get(name)
|
||||||
@ -49,7 +52,8 @@ export default class Connection extends EventEmitter {
|
|||||||
this.msgMap.get(name)?.forEach(cb => cb(data))
|
this.msgMap.get(name)?.forEach(cb => cb(data))
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(`解析失败,${str}不是合法的JSON格式:`, error)
|
// console.log(`解析失败,${str}不是合法的JSON格式:`, error)
|
||||||
|
console.log(error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -74,7 +78,9 @@ export default class Connection extends EventEmitter {
|
|||||||
name,
|
name,
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
console.log(`${getTime()}发送|${this.playerId || -1}|${msg}`)
|
const view = binaryEncode(name, data)
|
||||||
this.ws.send(msg)
|
const buffer = Buffer.from(view.buffer)
|
||||||
|
console.log(`${getTime()}发送|字节数${buffer.length}|${this.playerId || -1}|${msg}`)
|
||||||
|
this.ws.send(buffer)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,71 +1,2 @@
|
|||||||
import { EventEmitter } from 'stream';
|
export * from './MyServer'
|
||||||
import WebSocket, { WebSocketServer } from 'ws';
|
export * from './Connection'
|
||||||
import Connection, { ConnectionEventEnum } from './Connection';
|
|
||||||
|
|
||||||
export interface IMyServerOptions {
|
|
||||||
port: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export type IData = Record<string, any>
|
|
||||||
|
|
||||||
export interface ICallApiRet {
|
|
||||||
success: boolean;
|
|
||||||
error?: Error;
|
|
||||||
res?: IData
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum MyServerEventEnum {
|
|
||||||
Connect = 'Connect',
|
|
||||||
DisConnect = 'DisConnect',
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class MyServer extends EventEmitter {
|
|
||||||
wss?: WebSocketServer
|
|
||||||
port: number
|
|
||||||
connections: Set<Connection> = new Set()
|
|
||||||
apiMap: Map<string, Function> = new Map()
|
|
||||||
|
|
||||||
constructor({ port = 8080 }: Partial<IMyServerOptions>) {
|
|
||||||
super()
|
|
||||||
this.port = port
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.wss = new WebSocketServer({ port: this.port });
|
|
||||||
this.wss.on('connection', this.handleConnect.bind(this));
|
|
||||||
|
|
||||||
this.wss.on("error", (e) => {
|
|
||||||
reject(e)
|
|
||||||
})
|
|
||||||
|
|
||||||
this.wss.on("close", () => {
|
|
||||||
console.log("MyServer 服务关闭");
|
|
||||||
})
|
|
||||||
|
|
||||||
this.wss.on("listening", () => {
|
|
||||||
resolve(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
handleConnect(ws: WebSocket) {
|
|
||||||
//初始化
|
|
||||||
const connection = new Connection(this, ws)
|
|
||||||
|
|
||||||
//向外告知有人来了
|
|
||||||
this.connections.add(connection)
|
|
||||||
this.emit(MyServerEventEnum.Connect, connection)
|
|
||||||
|
|
||||||
//向外告知有人走了
|
|
||||||
connection.on(ConnectionEventEnum.Close, (code: number, reason: string) => {
|
|
||||||
this.connections.delete(connection)
|
|
||||||
this.emit(MyServerEventEnum.DisConnect, connection, code, reason)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
setApi(apiName: string, cb: Function) {
|
|
||||||
this.apiMap.set(apiName, cb)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,6 @@
|
|||||||
import MyServer, { MyServerEventEnum } from './Core';
|
import { Connection, MyServer, MyServerEventEnum } from './Core';
|
||||||
import PlayerManager from './Biz/PlayerManager';
|
import PlayerManager from './Biz/PlayerManager';
|
||||||
import RoomManager from './Biz/RoomManager';
|
import RoomManager from './Biz/RoomManager';
|
||||||
import Connection from './Core/Connection';
|
|
||||||
import { getTime, symlinkCommon } from './Utils';
|
import { getTime, symlinkCommon } from './Utils';
|
||||||
import { ApiMsgEnum, IApiGameStartReq, IApiGameStartRes, IApiPlayerJoinReq, IApiPlayerJoinRes, IApiPlayerListReq, IApiPlayerListRes, IApiRoomCreateReq, IApiRoomCreateRes, IApiRoomJoinReq, IApiRoomJoinRes, IApiRoomLeaveReq, IApiRoomLeaveRes, IApiRoomListReq, IApiRoomListRes, IModel } from './Common';
|
import { ApiMsgEnum, IApiGameStartReq, IApiGameStartRes, IApiPlayerJoinReq, IApiPlayerJoinRes, IApiPlayerListReq, IApiPlayerListRes, IApiRoomCreateReq, IApiRoomCreateRes, IApiRoomJoinReq, IApiRoomJoinRes, IApiRoomLeaveReq, IApiRoomLeaveRes, IApiRoomListReq, IApiRoomListRes, IModel } from './Common';
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
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]
|
||||||
|
|
||||||
@ -35,3 +36,78 @@ export const copyCommon = async () => {
|
|||||||
await fs.copy(src, dst)
|
await fs.copy(src, dst)
|
||||||
console.log('同步成功!')
|
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))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
test.js
32
test.js
@ -1,2 +1,30 @@
|
|||||||
const a = new Date()
|
const msg = JSON.stringify({
|
||||||
console.log(a.toLocaleString().split("├")[0]);
|
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]);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user