diff --git a/apps/client/.eslintrc b/apps/client/.eslintrc deleted file mode 100644 index e0a4c31..0000000 --- a/apps/client/.eslintrc +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "@antfu", - "rules": { - "curly": "off", - "no-console": "off", - "no-cond-assign": "off", - "no-useless-call": "off", - "@typescript-eslint/brace-style": "off", - "@typescript-eslint/consistent-type-imports": "off" - } -} \ No newline at end of file diff --git a/apps/client/assets/Scenes/Hall.scene b/apps/client/assets/Scenes/Hall.scene index ab4c938..c514ad0 100644 --- a/apps/client/assets/Scenes/Hall.scene +++ b/apps/client/assets/Scenes/Hall.scene @@ -1887,7 +1887,7 @@ }, "component": "", "_componentId": "a14b40zxfhFXKk12/1/OrYr", - "handler": "createRoom", + "handler": "handleCreateRoom", "customEventData": "" }, { diff --git a/apps/client/assets/Scenes/Room.scene b/apps/client/assets/Scenes/Room.scene index 0c462f3..d242fc9 100644 --- a/apps/client/assets/Scenes/Room.scene +++ b/apps/client/assets/Scenes/Room.scene @@ -1255,7 +1255,7 @@ } ], "_interactable": true, - "_transition": 0, + "_transition": 3, "_normalColor": { "__type__": "cc.Color", "r": 214, @@ -1301,7 +1301,7 @@ "__expectedType__": "cc.SpriteFrame" }, "_duration": 0.1, - "_zoomScale": 1.2, + "_zoomScale": 0.9, "_target": { "__id__": 30 }, @@ -1549,7 +1549,7 @@ } ], "_interactable": true, - "_transition": 0, + "_transition": 3, "_normalColor": { "__type__": "cc.Color", "r": 214, @@ -1595,7 +1595,7 @@ "__expectedType__": "cc.SpriteFrame" }, "_duration": 0.1, - "_zoomScale": 1.2, + "_zoomScale": 0.9, "_target": { "__id__": 38 }, diff --git a/apps/client/assets/Scripts/Base/EntityManager.ts b/apps/client/assets/Scripts/Base/EntityManager.ts index f5301a0..4b0d91d 100644 --- a/apps/client/assets/Scripts/Base/EntityManager.ts +++ b/apps/client/assets/Scripts/Base/EntityManager.ts @@ -1,22 +1,21 @@ -import { _decorator, Component } from 'cc'; -import { EntityStateEnum } from '../Enum'; -import StateMachine from './StateMachine'; +import { _decorator, Component } from "cc"; +import { EntityStateEnum } from "../Enum"; +import StateMachine from "./StateMachine"; const { ccclass, property } = _decorator; -@ccclass('EntityManager') +@ccclass("EntityManager") export abstract class EntityManager extends Component { - fsm: StateMachine - private _state: EntityStateEnum + fsm: StateMachine; + private _state: EntityStateEnum; get state() { - return this._state + return this._state; } set state(newState) { - this._state = newState - this.fsm.setParams(newState, true) + this._state = newState; + this.fsm.setParams(newState, true); } - abstract init(...args: any[]): void + abstract init(...args: any[]): void; } - diff --git a/apps/client/assets/Scripts/Base/Singleton.ts b/apps/client/assets/Scripts/Base/Singleton.ts index b755b94..5564bcb 100644 --- a/apps/client/assets/Scripts/Base/Singleton.ts +++ b/apps/client/assets/Scripts/Base/Singleton.ts @@ -1,13 +1,12 @@ export default class Singleton { - private static _instance: any = null + private static _instance: any = null; static GetInstance(): T { if (this._instance === null) { - this._instance = new this() + this._instance = new this(); } - return this._instance + return this._instance; } - protected constructor() { - } -} \ No newline at end of file + protected constructor() {} +} diff --git a/apps/client/assets/Scripts/Base/State.ts b/apps/client/assets/Scripts/Base/State.ts index 02a7d48..1953a02 100644 --- a/apps/client/assets/Scripts/Base/State.ts +++ b/apps/client/assets/Scripts/Base/State.ts @@ -1,48 +1,45 @@ -import { animation, AnimationClip, Sprite, SpriteFrame } from 'cc' -import DataManager from '../Global/DataManager' -import { ResourceManager } from '../Global/ResourceManager' -import { sortSpriteFrame } from '../Utils' -import StateMachine from './StateMachine' +import { animation, AnimationClip, Sprite, SpriteFrame } from "cc"; +import DataManager from "../Global/DataManager"; +import { ResourceManager } from "../Global/ResourceManager"; +import { sortSpriteFrame } from "../Utils"; +import StateMachine from "./StateMachine"; /*** * unit:milisecond */ -export const ANIMATION_SPEED = 1 / 10 +export const ANIMATION_SPEED = 1 / 10; /*** * 状态(每组动画的承载容器,持有SpriteAnimation组件执行播放) */ export default class State { - private animationClip: AnimationClip + private animationClip: AnimationClip; constructor( private fsm: StateMachine, private path: string, private wrapMode: AnimationClip.WrapMode = AnimationClip.WrapMode.Normal, - private force: boolean = false, + private force: boolean = false ) { //生成动画轨道属性 - const track = new animation.ObjectTrack() - track.path = new animation.TrackPath().toComponent(Sprite).toProperty('spriteFrame') - const spriteFrames = DataManager.Instance.textureMap.get(this.path) - const frames: Array<[number, SpriteFrame]> = sortSpriteFrame(spriteFrames).map((item, index) => [ - index * ANIMATION_SPEED, - item, - ]) - track.channel.curve.assignSorted(frames) + const track = new animation.ObjectTrack(); + track.path = new animation.TrackPath().toComponent(Sprite).toProperty("spriteFrame"); + const spriteFrames = DataManager.Instance.textureMap.get(this.path); + const frames: Array<[number, SpriteFrame]> = sortSpriteFrame(spriteFrames).map((item, index) => [index * ANIMATION_SPEED, item]); + track.channel.curve.assignSorted(frames); //动画添加轨道 - this.animationClip = new AnimationClip() - this.animationClip.name = this.path - this.animationClip.duration = frames.length * ANIMATION_SPEED - this.animationClip.addTrack(track) - this.animationClip.wrapMode = this.wrapMode + this.animationClip = new AnimationClip(); + this.animationClip.name = this.path; + this.animationClip.duration = frames.length * ANIMATION_SPEED; + this.animationClip.addTrack(track); + this.animationClip.wrapMode = this.wrapMode; } run() { if (this.fsm.animationComponent.defaultClip?.name === this.animationClip.name && !this.force) { - return + return; } - this.fsm.animationComponent.defaultClip = this.animationClip - this.fsm.animationComponent.play() + this.fsm.animationComponent.defaultClip = this.animationClip; + this.fsm.animationComponent.play(); } } diff --git a/apps/client/assets/Scripts/Base/StateMachine.ts b/apps/client/assets/Scripts/Base/StateMachine.ts index b6bd400..a0fb6df 100644 --- a/apps/client/assets/Scripts/Base/StateMachine.ts +++ b/apps/client/assets/Scripts/Base/StateMachine.ts @@ -1,30 +1,30 @@ -import { _decorator, Animation, Component } from 'cc' -import { EntityTypeEnum } from '../Common' -import { FsmParamTypeEnum } from '../Enum' -const { ccclass } = _decorator -import State from './State' -import SubStateMachine from './SubStateMachine' +import { _decorator, Animation, Component } from "cc"; +import { EntityTypeEnum } from "../Common"; +import { FsmParamTypeEnum } from "../Enum"; +const { ccclass } = _decorator; +import State from "./State"; +import SubStateMachine from "./SubStateMachine"; -type ParamsValueType = boolean | number +type ParamsValueType = boolean | number; export interface IParamsValue { - type: FsmParamTypeEnum - value: ParamsValueType + type: FsmParamTypeEnum; + value: ParamsValueType; } export const getInitParamsTrigger = () => { return { type: FsmParamTypeEnum.Trigger, value: false, - } -} + }; +}; export const getInitParamsNumber = () => { return { type: FsmParamTypeEnum.Number, value: 0, - } -} + }; +}; /*** * 流动图 @@ -39,47 +39,47 @@ export const getInitParamsNumber = () => { /*** * 有限状态机基类 */ -@ccclass('StateMachine') +@ccclass("StateMachine") export default abstract class StateMachine extends Component { - private _currentState: State | SubStateMachine = null - params: Map = new Map() - stateMachines: Map = new Map() - animationComponent: Animation - type: EntityTypeEnum + private _currentState: State | SubStateMachine = null; + params: Map = new Map(); + stateMachines: Map = new Map(); + animationComponent: Animation; + type: EntityTypeEnum; getParams(paramName: string) { if (this.params.has(paramName)) { - return this.params.get(paramName).value + return this.params.get(paramName).value; } } setParams(paramName: string, value: ParamsValueType) { if (this.params.has(paramName)) { - this.params.get(paramName).value = value - this.run() - this.resetTrigger() + this.params.get(paramName).value = value; + this.run(); + this.resetTrigger(); } } get currentState() { - return this._currentState + return this._currentState; } set currentState(newState) { if (!newState) { - return + return; } - this._currentState = newState - this._currentState.run() + this._currentState = newState; + this._currentState.run(); } /*** - * 清空所有trigger - */ + * 清空所有trigger + */ resetTrigger() { for (const [, value] of this.params) { if (value.type === FsmParamTypeEnum.Trigger) { - value.value = false + value.value = false; } } } @@ -87,6 +87,6 @@ export default abstract class StateMachine extends Component { /*** * 由子类重写,方法目标是根据当前状态和参数修改currentState */ - abstract init(...args: any[]): void - abstract run(): void + abstract init(...args: any[]): void; + abstract run(): void; } diff --git a/apps/client/assets/Scripts/Base/SubStateMachine.ts b/apps/client/assets/Scripts/Base/SubStateMachine.ts index 6e6cbb3..6f822a5 100644 --- a/apps/client/assets/Scripts/Base/SubStateMachine.ts +++ b/apps/client/assets/Scripts/Base/SubStateMachine.ts @@ -1,30 +1,30 @@ -import State from './State' -import StateMachine from './StateMachine' +import State from "./State"; +import StateMachine from "./StateMachine"; /*** * 子有限状态机基类 * 用处:例如有个idle的state,但是有多个方向,为了让主状态机更整洁,可以把同类型的但具体不同的state都封装在子状态机中 */ export default abstract class SubStateMachine { - private _currentState: State = null - stateMachines: Map = new Map() + private _currentState: State = null; + stateMachines: Map = new Map(); constructor(public fsm: StateMachine) {} get currentState() { - return this._currentState + return this._currentState; } set currentState(newState) { if (!newState) { - return + return; } - this._currentState = newState - this._currentState.run() + this._currentState = newState; + this._currentState.run(); } /*** * 具体类实现 */ - abstract run(): void + abstract run(): void; } diff --git a/apps/client/assets/Scripts/Entity/Actor/ActorManager.ts b/apps/client/assets/Scripts/Entity/Actor/ActorManager.ts index f0dc438..34e9158 100644 --- a/apps/client/assets/Scripts/Entity/Actor/ActorManager.ts +++ b/apps/client/assets/Scripts/Entity/Actor/ActorManager.ts @@ -1,147 +1,141 @@ -import { _decorator, instantiate, ProgressBar, Label, Vec3, Tween, tween, director } from 'cc'; -import { EntityManager } from '../../Base/EntityManager'; -import { ApiMsgEnum, EntityTypeEnum, IActor, InputTypeEnum, IVec2, toFixed } from '../../Common'; -import { EntityStateEnum, EventEnum, SceneEnum } from '../../Enum'; -import DataManager from '../../Global/DataManager'; -import EventManager from '../../Global/EventManager'; -import NetworkManager from '../../Global/NetworkManager'; -import { rad2Angle } from '../../Utils'; -import { WeaponManager } from '../Weapon/WeaponManager'; -import { PlayerStateMachine } from './ActorStateMachine'; +import { _decorator, instantiate, ProgressBar, Label, Vec3, Tween, tween } from "cc"; +import { EntityManager } from "../../Base/EntityManager"; +import { EntityTypeEnum, IActor, InputTypeEnum, IVec2, toFixed } from "../../Common"; +import { EntityStateEnum, EventEnum, SceneEnum } from "../../Enum"; +import DataManager from "../../Global/DataManager"; +import EventManager from "../../Global/EventManager"; +import { rad2Angle } from "../../Utils"; +import { WeaponManager } from "../Weapon/WeaponManager"; +import { ActorStateMachine } from "./ActorStateMachine"; const { ccclass } = _decorator; -@ccclass('ActorManager') +@ccclass("ActorManager") export class ActorManager extends EntityManager implements IActor { - //静态数据 - id: number - nickname: string - type: EntityTypeEnum - weaponType: EntityTypeEnum - bulletType: EntityTypeEnum + //静态数据 + id: number; + nickname: string; + type: EntityTypeEnum; + weaponType: EntityTypeEnum; + bulletType: EntityTypeEnum; - //动态数据 - hp: number - position: IVec2 - direction: IVec2 + //动态数据 + hp: number; + position: IVec2; + direction: IVec2; - private hpBar: ProgressBar - private label: Label - private weapon: WeaponManager + private hpBar: ProgressBar; + private label: Label; + private weapon: WeaponManager; - private tw: Tween - private targetPos: Vec3 + private tw: Tween; + private targetPos: Vec3; + private isDead = false; - private isDead = false + init(data: IActor) { + const { id, nickname, type, weaponType, bulletType } = data; + this.id = id; + this.nickname = nickname; + this.type = type; + this.weaponType = weaponType; + this.bulletType = bulletType; - get isSelf() { - return DataManager.Instance.myPlayerId === this.id + this.hpBar = this.node.getComponentInChildren(ProgressBar); + this.label = this.node.getComponentInChildren(Label); + this.label.string = nickname; + + this.fsm = this.addComponent(ActorStateMachine); + this.fsm.init(type); + this.state = EntityStateEnum.Idle; + + const weaponPrefab = DataManager.Instance.prefabMap.get(this.weaponType); + const weapon = instantiate(weaponPrefab); + weapon.setParent(this.node); + this.weapon = weapon.addComponent(WeaponManager); + this.weapon.init(data); + + this.targetPos = undefined; + this.node.active = false; + } + + async tick(dt: number) { + if (DataManager.Instance.myPlayerId !== this.id || this.isDead) { + return; } - init(data: IActor) { - const { id, nickname, type, weaponType, bulletType } = data - this.id = id - this.nickname = nickname - this.type = type - this.weaponType = weaponType - this.bulletType = bulletType - - this.hpBar = this.node.getComponentInChildren(ProgressBar) - this.label = this.node.getComponentInChildren(Label) - this.label.string = nickname - - this.fsm = this.addComponent(PlayerStateMachine) - this.fsm.init(type) - this.state = EntityStateEnum.Idle - - const weaponPrefab = DataManager.Instance.prefabMap.get(this.weaponType) - const weapon = instantiate(weaponPrefab) - weapon.setParent(this.node) - this.weapon = weapon.addComponent(WeaponManager) - this.weapon.init(data) - - this.targetPos = undefined - this.node.active = false + if (this.hp <= 0) { + EventManager.Instance.emit(EventEnum.GameEnd); + this.isDead = true; + return; } - async tick(dt: number) { - if (!this.isSelf) { - return - } - - if (this.hp <= 0 && !this.isDead) { - EventManager.Instance.emit(EventEnum.GameEnd) - this.isDead = true - return - } - - if (DataManager.Instance.jm.input.length()) { - const { x, y } = DataManager.Instance.jm.input - NetworkManager.Instance.sendMsg(ApiMsgEnum.MsgClientSync, { - type: InputTypeEnum.ActorMove, - id: this.id, - direction: { - x: toFixed(x), - y: toFixed(y), - }, - dt: toFixed(dt) - }) - } - + if (DataManager.Instance.jm.input.length()) { + const { x, y } = DataManager.Instance.jm.input; + EventManager.Instance.emit(EventEnum.ClientSync, { + type: InputTypeEnum.ActorMove, + id: this.id, + direction: { + x: toFixed(x), + y: toFixed(y), + }, + dt: toFixed(dt), + }); } + } - render(data: IActor) { - this.renderHP(data) - this.renderPosition(data) - this.renderDirection(data) + render(data: IActor) { + this.renderHP(data); + this.renderPosition(data); + this.renderDirection(data); + } + + renderHP(data: IActor) { + this.hp = data.hp; + this.hpBar.progress = data.hp / this.hpBar.totalLength; + } + + renderPosition(data: IActor) { + const newPos = new Vec3(data.position.x, data.position.y); + if (!this.targetPos) { + this.node.active = true; + this.node.setPosition(newPos); + this.targetPos = new Vec3(newPos); + } else if (!this.targetPos.equals(newPos)) { + this.tw?.stop(); + this.node.setPosition(this.targetPos); + this.targetPos.set(newPos); + this.state = EntityStateEnum.Run; + this.tw = tween(this.node) + .to(0.1, { + position: this.targetPos, + }) + .call(() => { + this.state = EntityStateEnum.Idle; + }) + .start(); } + // this.node.setPosition(data.position.x, data.position.y) + } - renderHP(data: IActor) { - this.hp = data.hp - this.hpBar.progress = data.hp / this.hpBar.totalLength + renderDirection(data: IActor) { + const { x, y } = data.direction; + if (x !== 0) { + this.node.setScale(x > 0 ? 1 : -1, 1); + this.label.node.setScale(x > 0 ? 1 : -1, 1); + this.hpBar.node.setScale(x > 0 ? 1 : -1, 1); } + const side = Math.sqrt(x * x + y * y); + const angle = rad2Angle(Math.asin(y / side)); + this.weapon.node.setRotationFromEuler(0, 0, angle); - renderPosition(data: IActor) { - const newPos = new Vec3(data.position.x, data.position.y) - if (!this.targetPos) { - this.node.active = true - this.node.setPosition(newPos) - this.targetPos = new Vec3(newPos) - } else if (!this.targetPos.equals(newPos)) { - this.tw?.stop() - this.node.setPosition(this.targetPos) - this.targetPos.set(newPos) - this.state = EntityStateEnum.Run - this.tw = tween(this.node).to(0.1, { - position: this.targetPos - }).call(() => { - this.state = EntityStateEnum.Idle - }).start() - } - - // this.node.setPosition(data.position.x, data.position.y) - } - - renderDirection(data: IActor) { - const { x, y } = data.direction - if (x !== 0) { - this.node.setScale(x > 0 ? 1 : -1, 1) - this.label.node.setScale(x > 0 ? 1 : -1, 1) - this.hpBar.node.setScale(x > 0 ? 1 : -1, 1) - } - const side = Math.sqrt(x * x + y * y) - const angle = rad2Angle(Math.asin(y / side)) - this.weapon.node.setRotationFromEuler(0, 0, angle) - - // const { x, y } = joystick.input - // let angle: number, sign: number - // if (x !== 0) { - // angle = rad2Angle(Math.atan(y / x)) - // sign = joystick.input.x > 0 ? 1 : -1 - // } else { - // angle = rad2Angle(Math.PI / 2) - // sign = joystick.input.y > 0 ? 1 : -1 - // } - // this.node.setRotationFromEuler(0, 0, sign * angle) - } + // const { x, y } = joystick.input + // let angle: number, sign: number + // if (x !== 0) { + // angle = rad2Angle(Math.atan(y / x)) + // sign = joystick.input.x > 0 ? 1 : -1 + // } else { + // angle = rad2Angle(Math.PI / 2) + // sign = joystick.input.y > 0 ? 1 : -1 + // } + // this.node.setRotationFromEuler(0, 0, sign * angle) + } } - diff --git a/apps/client/assets/Scripts/Entity/Actor/ActorStateMachine.ts b/apps/client/assets/Scripts/Entity/Actor/ActorStateMachine.ts index f35ba25..1d055ce 100644 --- a/apps/client/assets/Scripts/Entity/Actor/ActorStateMachine.ts +++ b/apps/client/assets/Scripts/Entity/Actor/ActorStateMachine.ts @@ -1,48 +1,47 @@ -import { _decorator, Animation, AnimationClip } from 'cc' -import State from '../../Base/State' -import StateMachine, { getInitParamsTrigger } from '../../Base/StateMachine' -import { EntityTypeEnum } from '../../Common' -import { EntityStateEnum, ParamsNameEnum } from '../../Enum' -const { ccclass } = _decorator +import { _decorator, Animation, AnimationClip } from "cc"; +import State from "../../Base/State"; +import StateMachine, { getInitParamsTrigger } from "../../Base/StateMachine"; +import { EntityTypeEnum } from "../../Common"; +import { EntityStateEnum, ParamsNameEnum } from "../../Enum"; +const { ccclass } = _decorator; -@ccclass('PlayerStateMachine') -export class PlayerStateMachine extends StateMachine { +@ccclass("ActorStateMachine") +export class ActorStateMachine extends StateMachine { init(type: EntityTypeEnum) { - this.type = type - this.animationComponent = this.node.addComponent(Animation) - this.initParams() - this.initStateMachines() - this.initAnimationEvent() + this.type = type; + this.animationComponent = this.node.addComponent(Animation); + this.initParams(); + this.initStateMachines(); + this.initAnimationEvent(); } initParams() { - this.params.set(ParamsNameEnum.Idle, getInitParamsTrigger()) - this.params.set(ParamsNameEnum.Run, getInitParamsTrigger()) + this.params.set(ParamsNameEnum.Idle, getInitParamsTrigger()); + this.params.set(ParamsNameEnum.Run, getInitParamsTrigger()); } initStateMachines() { - this.stateMachines.set(ParamsNameEnum.Idle, new State(this, `${this.type}${EntityStateEnum.Idle}`, AnimationClip.WrapMode.Loop)) - this.stateMachines.set(ParamsNameEnum.Run, new State(this, `${this.type}${EntityStateEnum.Run}`, AnimationClip.WrapMode.Loop)) + this.stateMachines.set(ParamsNameEnum.Idle, new State(this, `${this.type}${EntityStateEnum.Idle}`, AnimationClip.WrapMode.Loop)); + this.stateMachines.set(ParamsNameEnum.Run, new State(this, `${this.type}${EntityStateEnum.Run}`, AnimationClip.WrapMode.Loop)); } - initAnimationEvent() { - } + initAnimationEvent() {} run() { switch (this.currentState) { case this.stateMachines.get(ParamsNameEnum.Idle): case this.stateMachines.get(ParamsNameEnum.Run): if (this.params.get(ParamsNameEnum.Run).value) { - this.currentState = this.stateMachines.get(ParamsNameEnum.Run) + this.currentState = this.stateMachines.get(ParamsNameEnum.Run); } else if (this.params.get(ParamsNameEnum.Idle).value) { - this.currentState = this.stateMachines.get(ParamsNameEnum.Idle) + this.currentState = this.stateMachines.get(ParamsNameEnum.Idle); } else { - this.currentState = this.currentState + this.currentState = this.currentState; } - break + break; default: - this.currentState = this.stateMachines.get(ParamsNameEnum.Idle) - break + this.currentState = this.stateMachines.get(ParamsNameEnum.Idle); + break; } } } diff --git a/apps/client/assets/Scripts/Entity/Bullet/BulletManager.ts b/apps/client/assets/Scripts/Entity/Bullet/BulletManager.ts index 8586b13..94d27fc 100644 --- a/apps/client/assets/Scripts/Entity/Bullet/BulletManager.ts +++ b/apps/client/assets/Scripts/Entity/Bullet/BulletManager.ts @@ -1,79 +1,83 @@ -import { Tween, tween, Vec3, _decorator } from 'cc' -import { EntityManager } from '../../Base/EntityManager' -import { EntityTypeEnum, IBullet, IVec2 } from '../../Common' -import { EntityStateEnum, EventEnum } from '../../Enum' -import DataManager from '../../Global/DataManager' -import EventManager from '../../Global/EventManager' -import ObjectPoolManager from '../../Global/ObjectPoolManager' -import { rad2Angle } from '../../Utils' -import { ExplosionManager } from '../Explosion/ExplosionManager' -import { BulletStateMachine } from './BulletStateMachine' -const { ccclass } = _decorator +import { Tween, tween, Vec3, _decorator } from "cc"; +import { EntityManager } from "../../Base/EntityManager"; +import { EntityTypeEnum, IBullet, IVec2 } from "../../Common"; +import { EntityStateEnum, EventEnum } from "../../Enum"; +import DataManager from "../../Global/DataManager"; +import EventManager from "../../Global/EventManager"; +import ObjectPoolManager from "../../Global/ObjectPoolManager"; +import { rad2Angle } from "../../Utils"; +import { ExplosionManager } from "../Explosion/ExplosionManager"; +import { BulletStateMachine } from "./BulletStateMachine"; +const { ccclass } = _decorator; -@ccclass('BulletManager') +@ccclass("BulletManager") export class BulletManager extends EntityManager implements IBullet { //静态数据 - id: number - owner: number - type: EntityTypeEnum + id: number; + owner: number; + type: EntityTypeEnum; //动态数据 - position: IVec2 - direction: IVec2 + position: IVec2; + direction: IVec2; - private angle: number - private tw: Tween - private targetPos: Vec3 + private angle: number; + private tw: Tween; + private targetPos: Vec3; init({ id, owner, type }: IBullet) { - this.id = id - this.owner = owner - this.type = type + this.id = id; + this.owner = owner; + this.type = type; - this.fsm = this.addComponent(BulletStateMachine) - this.fsm.init(type) - this.state = EntityStateEnum.Idle - this.node.active = false - this.targetPos = undefined + this.fsm = this.addComponent(BulletStateMachine); + this.fsm.init(type); + this.state = EntityStateEnum.Idle; - EventManager.Instance.on(EventEnum.ExplosionBorn, this.handleExplosion, this) + this.node.active = false; + this.targetPos = undefined; + this.angle = undefined; + + EventManager.Instance.on(EventEnum.ExplosionBorn, this.handleExplosion, this); } handleExplosion(id: number, { x, y }: IVec2) { if (this.id !== id) { - return + return; } - const explosion = ObjectPoolManager.Instance.get(EntityTypeEnum.Explosion) - const explosionManager = explosion.getComponent(ExplosionManager) || explosion.addComponent(ExplosionManager) + const explosion = ObjectPoolManager.Instance.get(EntityTypeEnum.Explosion); + const explosionManager = explosion.getComponent(ExplosionManager) || explosion.addComponent(ExplosionManager); explosionManager.init(EntityTypeEnum.Explosion, { - x, y, - }) + x, + y, + }); - EventManager.Instance.off(EventEnum.ExplosionBorn, this.handleExplosion, this) - ObjectPoolManager.Instance.ret(this.node) - DataManager.Instance.bulletMap.delete(this.id) - this.angle = undefined + EventManager.Instance.off(EventEnum.ExplosionBorn, this.handleExplosion, this); + ObjectPoolManager.Instance.ret(this.node); + DataManager.Instance.bulletMap.delete(this.id); } render(data: IBullet) { - this.renderPosition(data) - this.renderDirection(data) + this.renderPosition(data); + this.renderDirection(data); } renderPosition(data: IBullet) { - const newPos = new Vec3(data.position.x, data.position.y) + const newPos = new Vec3(data.position.x, data.position.y); if (!this.targetPos) { - this.node.active = true - this.node.setPosition(newPos) - this.targetPos = new Vec3(newPos) + this.node.active = true; + this.node.setPosition(newPos); + this.targetPos = new Vec3(newPos); } else if (!this.targetPos.equals(newPos)) { - this.tw?.stop() - this.node.setPosition(this.targetPos) - this.targetPos.set(newPos) - this.tw = tween(this.node).to(0.1, { - position: this.targetPos - }).start() + this.tw?.stop(); + this.node.setPosition(this.targetPos); + this.targetPos.set(newPos); + this.tw = tween(this.node) + .to(0.1, { + position: this.targetPos, + }) + .start(); } // this.node.setPosition(data.position.x, data.position.y) @@ -81,12 +85,12 @@ export class BulletManager extends EntityManager implements IBullet { renderDirection(data: IBullet) { if (this.angle === undefined) { - const { x, y } = data.direction - const side = Math.sqrt(x * x + y * y) - this.angle = x > 0 ? rad2Angle(Math.asin(y / side)) : rad2Angle(Math.asin(- y / side)) + 180 + const { x, y } = data.direction; + const side = Math.sqrt(x * x + y * y); + this.angle = x > 0 ? rad2Angle(Math.asin(y / side)) : rad2Angle(Math.asin(-y / side)) + 180; } - this.node.setRotationFromEuler(0, 0, this.angle) + this.node.setRotationFromEuler(0, 0, this.angle); // let angle: number, sign: number // if (x !== 0) { diff --git a/apps/client/assets/Scripts/Entity/Bullet/BulletStateMachine.ts b/apps/client/assets/Scripts/Entity/Bullet/BulletStateMachine.ts index 389c6a4..8354a73 100644 --- a/apps/client/assets/Scripts/Entity/Bullet/BulletStateMachine.ts +++ b/apps/client/assets/Scripts/Entity/Bullet/BulletStateMachine.ts @@ -1,45 +1,43 @@ -import { _decorator, Animation } from 'cc' -import State from '../../Base/State' -import StateMachine, { getInitParamsTrigger } from '../../Base/StateMachine' -import { EntityTypeEnum } from '../../Common' -import { EntityStateEnum, ParamsNameEnum } from '../../Enum' -const { ccclass } = _decorator +import { _decorator, Animation } from "cc"; +import State from "../../Base/State"; +import StateMachine, { getInitParamsTrigger } from "../../Base/StateMachine"; +import { EntityTypeEnum } from "../../Common"; +import { EntityStateEnum, ParamsNameEnum } from "../../Enum"; +const { ccclass } = _decorator; -@ccclass('BulletStateMachine') +@ccclass("BulletStateMachine") export class BulletStateMachine extends StateMachine { init(type: EntityTypeEnum) { - this.type = type - this.animationComponent = this.node.addComponent(Animation) + this.type = type; + this.animationComponent = this.node.addComponent(Animation); - this.initParams() - this.initStateMachines() - this.initAnimationEvent() + this.initParams(); + this.initStateMachines(); + this.initAnimationEvent(); } initParams() { - this.params.set(ParamsNameEnum.Idle, getInitParamsTrigger()) + this.params.set(ParamsNameEnum.Idle, getInitParamsTrigger()); } initStateMachines() { - this.stateMachines.set(ParamsNameEnum.Idle, new State(this, `${this.type}${EntityStateEnum.Idle}`)) + this.stateMachines.set(ParamsNameEnum.Idle, new State(this, `${this.type}${EntityStateEnum.Idle}`)); } - initAnimationEvent() { - - } + initAnimationEvent() {} run() { switch (this.currentState) { case this.stateMachines.get(ParamsNameEnum.Idle): if (this.params.get(ParamsNameEnum.Idle).value) { - this.currentState = this.stateMachines.get(ParamsNameEnum.Idle) + this.currentState = this.stateMachines.get(ParamsNameEnum.Idle); } else { - this.currentState = this.currentState + this.currentState = this.currentState; } - break + break; default: - this.currentState = this.stateMachines.get(ParamsNameEnum.Idle) - break + this.currentState = this.stateMachines.get(ParamsNameEnum.Idle); + break; } } } diff --git a/apps/client/assets/Scripts/Entity/Explosion/ExplosionManager.ts b/apps/client/assets/Scripts/Entity/Explosion/ExplosionManager.ts index 54a95ba..bb9fd6b 100644 --- a/apps/client/assets/Scripts/Entity/Explosion/ExplosionManager.ts +++ b/apps/client/assets/Scripts/Entity/Explosion/ExplosionManager.ts @@ -1,17 +1,16 @@ -import { _decorator } from 'cc' -import { EntityManager } from '../../Base/EntityManager' -import { EntityTypeEnum, IVec2 } from '../../Common' -import { EntityStateEnum } from '../../Enum' -import { ExplosionStateMachine } from './ExplosionStateMachine' -const { ccclass, property } = _decorator +import { _decorator } from "cc"; +import { EntityManager } from "../../Base/EntityManager"; +import { EntityTypeEnum, IVec2 } from "../../Common"; +import { EntityStateEnum } from "../../Enum"; +import { ExplosionStateMachine } from "./ExplosionStateMachine"; +const { ccclass, property } = _decorator; -@ccclass('ExplosionManager') +@ccclass("ExplosionManager") export class ExplosionManager extends EntityManager { - init(type: EntityTypeEnum, { x, y }: IVec2) { - this.node.setPosition(x, y) - this.fsm = this.addComponent(ExplosionStateMachine) - this.fsm.init(type) - this.state = EntityStateEnum.Idle + this.node.setPosition(x, y); + this.fsm = this.addComponent(ExplosionStateMachine); + this.fsm.init(type); + this.state = EntityStateEnum.Idle; } } diff --git a/apps/client/assets/Scripts/Entity/Explosion/ExplosionStateMachine.ts b/apps/client/assets/Scripts/Entity/Explosion/ExplosionStateMachine.ts index ec4d32f..8479363 100644 --- a/apps/client/assets/Scripts/Entity/Explosion/ExplosionStateMachine.ts +++ b/apps/client/assets/Scripts/Entity/Explosion/ExplosionStateMachine.ts @@ -1,52 +1,52 @@ -import { _decorator, Animation } from 'cc' -import State from '../../Base/State' -import StateMachine, { getInitParamsTrigger } from '../../Base/StateMachine' -import { EntityTypeEnum } from '../../Common' -import { EntityStateEnum, ParamsNameEnum } from '../../Enum' -import ObjectPoolManager from '../../Global/ObjectPoolManager' -const { ccclass, property } = _decorator +import { _decorator, Animation } from "cc"; +import State from "../../Base/State"; +import StateMachine, { getInitParamsTrigger } from "../../Base/StateMachine"; +import { EntityTypeEnum } from "../../Common"; +import { EntityStateEnum, ParamsNameEnum } from "../../Enum"; +import ObjectPoolManager from "../../Global/ObjectPoolManager"; +const { ccclass, property } = _decorator; -@ccclass('ExplosionStateMachine') +@ccclass("ExplosionStateMachine") export class ExplosionStateMachine extends StateMachine { init(type: EntityTypeEnum) { - this.type = type - this.animationComponent = this.node.addComponent(Animation) + this.type = type; + this.animationComponent = this.node.addComponent(Animation); - this.initParams() - this.initStateMachines() - this.initAnimationEvent() + this.initParams(); + this.initStateMachines(); + this.initAnimationEvent(); } initParams() { - this.params.set(ParamsNameEnum.Idle, getInitParamsTrigger()) + this.params.set(ParamsNameEnum.Idle, getInitParamsTrigger()); } initStateMachines() { - this.stateMachines.set(ParamsNameEnum.Idle, new State(this, `${this.type}${EntityStateEnum.Idle}`)) + this.stateMachines.set(ParamsNameEnum.Idle, new State(this, `${this.type}${EntityStateEnum.Idle}`)); } initAnimationEvent() { this.animationComponent.on(Animation.EventType.FINISHED, () => { - const whiteList = [EntityStateEnum.Idle] - const name = this.animationComponent.defaultClip.name - if (whiteList.some(v => name.includes(v))) { - ObjectPoolManager.Instance.ret(this.node) + const whiteList = [EntityStateEnum.Idle]; + const name = this.animationComponent.defaultClip.name; + if (whiteList.some((v) => name.includes(v))) { + ObjectPoolManager.Instance.ret(this.node); } - }) + }); } run() { switch (this.currentState) { case this.stateMachines.get(ParamsNameEnum.Idle): if (this.params.get(ParamsNameEnum.Idle).value) { - this.currentState = this.stateMachines.get(ParamsNameEnum.Idle) + this.currentState = this.stateMachines.get(ParamsNameEnum.Idle); } else { - this.currentState = this.currentState + this.currentState = this.currentState; } - break + break; default: - this.currentState = this.stateMachines.get(ParamsNameEnum.Idle) - break + this.currentState = this.stateMachines.get(ParamsNameEnum.Idle); + break; } } } diff --git a/apps/client/assets/Scripts/Entity/Weapon/WeaponManager.ts b/apps/client/assets/Scripts/Entity/Weapon/WeaponManager.ts index 7d7fb12..6333473 100644 --- a/apps/client/assets/Scripts/Entity/Weapon/WeaponManager.ts +++ b/apps/client/assets/Scripts/Entity/Weapon/WeaponManager.ts @@ -1,66 +1,61 @@ -import { _decorator, Node, Vec2, UITransform } from 'cc' -import { EntityManager } from '../../Base/EntityManager' -import { ApiMsgEnum, EntityTypeEnum, InputTypeEnum, toFixed } from '../../Common' -import { EntityStateEnum, EventEnum } from '../../Enum' -import DataManager from '../../Global/DataManager' -import EventManager from '../../Global/EventManager' -import NetworkManager from '../../Global/NetworkManager' -import { WeaponStateMachine } from './WeaponStateMachine' -const { ccclass } = _decorator +import { _decorator, Node, Vec2, UITransform } from "cc"; +import { EntityManager } from "../../Base/EntityManager"; +import { EntityTypeEnum, InputTypeEnum, toFixed } from "../../Common"; +import { EntityStateEnum, EventEnum } from "../../Enum"; +import DataManager from "../../Global/DataManager"; +import EventManager from "../../Global/EventManager"; +import { WeaponStateMachine } from "./WeaponStateMachine"; +const { ccclass } = _decorator; -@ccclass('WeaponManager') +@ccclass("WeaponManager") export class WeaponManager extends EntityManager { - owner: number - type: EntityTypeEnum + owner: number; + type: EntityTypeEnum; - private body: Node - private anchor: Node - private point: Node + private body: Node; + private anchor: Node; + private point: Node; - get isSelf() { - return DataManager.Instance.myPlayerId === this.owner - } + init({ id, weaponType }: { id: number; weaponType: EntityTypeEnum }) { + this.owner = id; + this.type = weaponType; - init({ id, weaponType }: { id: number, weaponType: EntityTypeEnum }) { - this.owner = id - this.type = weaponType + this.node.setSiblingIndex(0); + this.body = this.node.getChildByName("Body"); + this.anchor = this.body.getChildByName("Anchor"); + this.point = this.anchor.getChildByName("Point"); - this.node.setSiblingIndex(0) - this.body = this.node.getChildByName("Body") - this.anchor = this.body.getChildByName("Anchor") - this.point = this.anchor.getChildByName("Point") + this.fsm = this.body.addComponent(WeaponStateMachine); + this.fsm.init(weaponType); + this.state = EntityStateEnum.Idle; - this.fsm = this.body.addComponent(WeaponStateMachine) - this.fsm.init(weaponType) - this.state = EntityStateEnum.Idle - - EventManager.Instance.on(EventEnum.WeaponShoot, this.handleWeaponShoot, this) - EventManager.Instance.on(EventEnum.BulletBorn, this.handleBulletBorn, this) + EventManager.Instance.on(EventEnum.WeaponShoot, this.handleWeaponShoot, this); + EventManager.Instance.on(EventEnum.BulletBorn, this.handleBulletBorn, this); } onDestroy() { - EventManager.Instance.off(EventEnum.WeaponShoot, this.handleWeaponShoot, this) - EventManager.Instance.off(EventEnum.BulletBorn, this.handleBulletBorn, this) + EventManager.Instance.off(EventEnum.WeaponShoot, this.handleWeaponShoot, this); + EventManager.Instance.off(EventEnum.BulletBorn, this.handleBulletBorn, this); } handleBulletBorn(owner: number) { if (this.owner !== owner) { - return + return; } - this.state = EntityStateEnum.Attack + this.state = EntityStateEnum.Attack; } handleWeaponShoot() { - if (!this.isSelf) { - return + if (DataManager.Instance.myPlayerId !== this.owner) { + return; } - const pointWorldPos = this.point.getWorldPosition() + const pointWorldPos = this.point.getWorldPosition(); const pointStagePos = DataManager.Instance.stage.getComponent(UITransform).convertToNodeSpaceAR(pointWorldPos); - const anchorWorldPos = this.anchor.getWorldPosition() - const directionVec2 = new Vec2(pointWorldPos.x - anchorWorldPos.x, pointWorldPos.y - anchorWorldPos.y).normalize() + const anchorWorldPos = this.anchor.getWorldPosition(); + const directionVec2 = new Vec2(pointWorldPos.x - anchorWorldPos.x, pointWorldPos.y - anchorWorldPos.y).normalize(); - NetworkManager.Instance.sendMsg(ApiMsgEnum.MsgClientSync, { + EventManager.Instance.emit(EventEnum.ClientSync, { type: InputTypeEnum.WeaponShoot, owner: this.owner, position: { @@ -71,6 +66,6 @@ export class WeaponManager extends EntityManager { x: toFixed(directionVec2.x), y: toFixed(directionVec2.y), }, - }) + }); } } diff --git a/apps/client/assets/Scripts/Enum/index.ts b/apps/client/assets/Scripts/Enum/index.ts index c700b67..17e56d0 100644 --- a/apps/client/assets/Scripts/Enum/index.ts +++ b/apps/client/assets/Scripts/Enum/index.ts @@ -1,59 +1,60 @@ export enum FsmParamTypeEnum { - Number = 'Number', - Trigger = 'Trigger', -} - -export enum EntityStateEnum { - Idle = 'Idle', - Run = 'Run', - Attack = 'Attack', + Number = "Number", + Trigger = "Trigger", } export enum ParamsNameEnum { - Idle = 'Idle', - Run = 'Run', - Attack = 'Attack', + Idle = "Idle", + Run = "Run", + Attack = "Attack", +} + +export enum EntityStateEnum { + Idle = "Idle", + Run = "Run", + Attack = "Attack", } export enum EventEnum { - WeaponShoot = 'WeaponShoot', - BulletBorn = 'BulletBorn', - ExplosionBorn = 'ExplosionBorn', - RoomJoin = 'RoomJoin', - GameStart = 'GameStart', - GameEnd = 'GameEnd', + WeaponShoot = "WeaponShoot", + BulletBorn = "BulletBorn", + ExplosionBorn = "ExplosionBorn", + RoomJoin = "RoomJoin", + GameStart = "GameStart", + GameEnd = "GameEnd", + ClientSync = "ClientSync", } export enum PrefabPathEnum { - Map1 = 'prefab/Map1', - Actor1 = 'prefab/Actor', - Actor2 = 'prefab/Actor', - Weapon1 = 'prefab/Weapon1', - Weapon2 = 'prefab/Weapon2', - Bullet1 = 'prefab/Bullet', - Bullet2 = 'prefab/Bullet', - Explosion = 'prefab/Explosion', - JoyStick = 'prefab/JoyStick', - Shoot = 'prefab/Shoot', + Map1 = "prefab/Map1", + Actor1 = "prefab/Actor", + Actor2 = "prefab/Actor", + Weapon1 = "prefab/Weapon1", + Weapon2 = "prefab/Weapon2", + Bullet1 = "prefab/Bullet", + Bullet2 = "prefab/Bullet", + Explosion = "prefab/Explosion", + JoyStick = "prefab/JoyStick", + Shoot = "prefab/Shoot", } export enum TexturePathEnum { - Actor1Idle = 'texture/actor/actor1/idle', - Actor1Run = 'texture/actor/actor1/run', - Actor2Idle = 'texture/actor/actor2/idle', - Actor2Run = 'texture/actor/actor2/run', - Weapon1Idle = 'texture/weapon/weapon1/idle', - Weapon1Attack = 'texture/weapon/weapon1/attack', - Weapon2Idle = 'texture/weapon/weapon2/idle', - Weapon2Attack = 'texture/weapon/weapon2/attack', - Bullet1Idle = 'texture/bullet/bullet1', - Bullet2Idle = 'texture/bullet/bullet2', - ExplosionIdle = 'texture/explosion', + Actor1Idle = "texture/actor/actor1/idle", + Actor1Run = "texture/actor/actor1/run", + Actor2Idle = "texture/actor/actor2/idle", + Actor2Run = "texture/actor/actor2/run", + Weapon1Idle = "texture/weapon/weapon1/idle", + Weapon1Attack = "texture/weapon/weapon1/attack", + Weapon2Idle = "texture/weapon/weapon2/idle", + Weapon2Attack = "texture/weapon/weapon2/attack", + Bullet1Idle = "texture/bullet/bullet1", + Bullet2Idle = "texture/bullet/bullet2", + ExplosionIdle = "texture/explosion", } export enum SceneEnum { - Login = 'Login', - Hall = 'Hall', - Room = 'Room', - Battle = 'Battle', -} \ No newline at end of file + Login = "Login", + Hall = "Hall", + Room = "Room", + Battle = "Battle", +} diff --git a/apps/client/assets/Scripts/Global/DataManager.ts b/apps/client/assets/Scripts/Global/DataManager.ts index 64988b4..5a2f24a 100644 --- a/apps/client/assets/Scripts/Global/DataManager.ts +++ b/apps/client/assets/Scripts/Global/DataManager.ts @@ -1,151 +1,160 @@ -import { Node, Prefab, SpriteFrame, clamp } from 'cc' -import Singleton from '../Base/Singleton' -import { EntityTypeEnum, IBullet, IClientInput, InputTypeEnum, IRoom, IState, toFixed } from '../Common' -import { ActorManager } from '../Entity/Actor/ActorManager' -import { BulletManager } from '../Entity/Bullet/BulletManager' -import { EventEnum } from '../Enum' -import { JoyStickManager } from '../UI/JoyStickManager' -import EventManager from './EventManager' +import { Node, Prefab, SpriteFrame, clamp } from "cc"; +import Singleton from "../Base/Singleton"; +import { EntityTypeEnum, IBullet, IClientInput, InputTypeEnum, IRoom, IState, toFixed } from "../Common"; +import { ActorManager } from "../Entity/Actor/ActorManager"; +import { BulletManager } from "../Entity/Bullet/BulletManager"; +import { EventEnum } from "../Enum"; +import { JoyStickManager } from "../UI/JoyStickManager"; +import EventManager from "./EventManager"; +const PLAYER_SPEED = 100; +const BULLET_SPEED = 600; -const PLAYER_SPEED = 100 -const BULLET_SPEED = 600 +const WEAPON_DAMAGE = 5; -const WEAPON_DAMAGE = 5 +const PLAYER_RADIUS = 50; +const BULLET_RADIUS = 10; -const PLAYER_RADIUS = 50 -const BULLET_RADIUS = 10 - -const mapW = 960 -const mapH = 640 +const mapW = 960; +const mapH = 640; export default class DataManager extends Singleton { static get Instance() { - return super.GetInstance() + return super.GetInstance(); } //登陆数据 - myPlayerId = 1 + myPlayerId = 1; //大厅数据 - roomInfo: IRoom + roomInfo: IRoom; //游戏数据 - stage: Node - jm: JoyStickManager - prefabMap: Map = new Map() - textureMap: Map = new Map() - actorMap: Map = new Map() - bulletMap: Map = new Map() + stage: Node; + jm: JoyStickManager; + prefabMap: Map = new Map(); + textureMap: Map = new Map(); + actorMap: Map = new Map(); + bulletMap: Map = new Map(); reset() { - this.stage = null - this.jm = null - this.actorMap.clear() - this.bulletMap.clear() - this.prefabMap.clear() - this.textureMap.clear() + this.frameId = 0; + this.stage = null; + this.jm = null; + this.actorMap.clear(); + this.bulletMap.clear(); + this.prefabMap.clear(); + this.textureMap.clear(); } + frameId = 0; + lastState: IState; state: IState = { - players: [{ - id: 2, - nickname: "哈哈1", - position: { - x: -200, - y: -200 + actors: [ + { + id: 2, + nickname: "哈哈1", + position: { + x: -200, + y: -200, + }, + direction: { + x: 1, + y: 0, + }, + hp: 100, + type: EntityTypeEnum.Actor1, + weaponType: EntityTypeEnum.Weapon1, + bulletType: EntityTypeEnum.Bullet1, }, - direction: { - x: 1, - y: 0 + { + id: 1, + nickname: "哈哈2", + position: { + x: 200, + y: 200, + }, + direction: { + x: 0, + y: -1, + }, + hp: 100, + type: EntityTypeEnum.Actor2, + weaponType: EntityTypeEnum.Weapon2, + bulletType: EntityTypeEnum.Bullet2, }, - hp: 100, - type: EntityTypeEnum.Actor1, - weaponType: EntityTypeEnum.Weapon1, - bulletType: EntityTypeEnum.Bullet1, - }, { - id: 1, - nickname: "哈哈2", - position: { - x: 200, - y: 200 - }, - direction: { - x: 0, - y: -1 - }, - hp: 100, - type: EntityTypeEnum.Actor2, - weaponType: EntityTypeEnum.Weapon2, - bulletType: EntityTypeEnum.Bullet2, - }], + ], bullets: [], - nextBulletId: 1 - } + nextBulletId: 1, + }; applyInput(input: IClientInput) { switch (input.type) { case InputTypeEnum.ActorMove: { - const { direction: { x, y }, dt, id } = input - const player = this.state.players.find(e => e.id === id) + const { + direction: { x, y }, + dt, + id, + } = input; + const player = this.state.actors.find((e) => e.id === id); if (!player) { - return + return; } - player.position.x += toFixed(x * PLAYER_SPEED * dt) - player.position.y += toFixed(y * PLAYER_SPEED * dt) + player.position.x += toFixed(x * PLAYER_SPEED * dt); + player.position.y += toFixed(y * PLAYER_SPEED * dt); - player.position.x = clamp(player.position.x, -mapW / 2, mapW / 2) - player.position.y = clamp(player.position.y, -mapH / 2, mapH / 2) + player.position.x = clamp(player.position.x, -mapW / 2, mapW / 2); + player.position.y = clamp(player.position.y, -mapH / 2, mapH / 2); - player.direction = { x, y } - break + player.direction = { x, y }; + break; } case InputTypeEnum.WeaponShoot: { - const { owner, position, direction } = input + const { owner, position, direction } = input; const bullet: IBullet = { id: this.state.nextBulletId++, owner, position, direction, - type: this.actorMap.get(owner).bulletType - } - this.state.bullets.push(bullet) + type: this.actorMap.get(owner).bulletType, + }; + this.state.bullets.push(bullet); - EventManager.Instance.emit(EventEnum.BulletBorn, owner) - break + EventManager.Instance.emit(EventEnum.BulletBorn, owner); + break; } case InputTypeEnum.TimePast: { - const { dt } = input - const { bullets, players } = this.state + const { dt } = input; + const { bullets, actors } = this.state; for (let i = bullets.length - 1; i >= 0; i--) { const bullet = bullets[i]; - for (let j = players.length - 1; j >= 0; 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) { + for (let j = actors.length - 1; j >= 0; j--) { + const player = actors[j]; + 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, { x: toFixed((player.position.x + bullet.position.x) / 2), y: toFixed((player.position.y + bullet.position.y) / 2), - }) + }); - player.hp -= WEAPON_DAMAGE - bullets.splice(i, 1) - break + player.hp -= WEAPON_DAMAGE; + bullets.splice(i, 1); + break; } } if (Math.abs(bullet.position.x) > mapW / 2 || Math.abs(bullet.position.y) > mapH / 2) { EventManager.Instance.emit(EventEnum.ExplosionBorn, bullet.id, { x: bullet.position.x, y: bullet.position.y, - }) - bullets.splice(i, 1) + }); + bullets.splice(i, 1); } } for (const bullet of this.state.bullets) { - bullet.position.x += toFixed(bullet.direction.x * BULLET_SPEED * dt) - bullet.position.y += toFixed(bullet.direction.y * BULLET_SPEED * dt) + bullet.position.x += toFixed(bullet.direction.x * BULLET_SPEED * dt); + bullet.position.y += toFixed(bullet.direction.y * BULLET_SPEED * dt); } } } diff --git a/apps/client/assets/Scripts/Global/EventManager.ts b/apps/client/assets/Scripts/Global/EventManager.ts index 42e99a6..9100cb7 100644 --- a/apps/client/assets/Scripts/Global/EventManager.ts +++ b/apps/client/assets/Scripts/Global/EventManager.ts @@ -1,5 +1,5 @@ -import Singleton from '../Base/Singleton' -import { EventEnum } from '../Enum'; +import Singleton from "../Base/Singleton"; +import { EventEnum } from "../Enum"; interface IItem { cb: Function; @@ -23,7 +23,7 @@ export default class EventManager extends Singleton { off(event: EventEnum, cb: Function, ctx: unknown) { if (this.map.has(event)) { - const index = this.map.get(event).findIndex(i => cb === i.cb && i.ctx === ctx); + const index = this.map.get(event).findIndex((i) => cb === i.cb && i.ctx === ctx); index > -1 && this.map.get(event).splice(index, 1); } } @@ -31,7 +31,7 @@ export default class EventManager extends Singleton { emit(event: EventEnum, ...params: unknown[]) { if (this.map.has(event)) { this.map.get(event).forEach(({ cb, ctx }) => { - cb.apply(ctx, params) + cb.apply(ctx, params); }); } } diff --git a/apps/client/assets/Scripts/Global/NetworkManager.ts b/apps/client/assets/Scripts/Global/NetworkManager.ts index 581d3e3..9a8e045 100644 --- a/apps/client/assets/Scripts/Global/NetworkManager.ts +++ b/apps/client/assets/Scripts/Global/NetworkManager.ts @@ -1,8 +1,8 @@ -import Singleton from '../Base/Singleton' -import { ApiMsgEnum, IModel } from '../Common'; -import { binaryEncode, binaryDecode } from '../Common/Binary'; +import Singleton from "../Base/Singleton"; +import { ApiMsgEnum, IModel } from "../Common"; +import { binaryEncode, binaryDecode } from "../Common/Binary"; -const TIMEOUT = 5000 +const TIMEOUT = 5000; interface IItem { cb: Function; @@ -12,109 +12,107 @@ interface IItem { export interface ICallApiRet { success: boolean; error?: Error; - res?: T + res?: T; } - -type aaa = keyof IModel export default class NetworkManager extends Singleton { static get Instance() { - return super.GetInstance() + return super.GetInstance(); } - ws: WebSocket - port = 8888 - maps: Map> = new Map() - isConnected = false + ws: WebSocket; + port = 8888; + maps: Map> = new Map(); + isConnected = false; connect() { return new Promise((resolve, reject) => { if (this.isConnected) { - resolve(true) - return + resolve(true); + return; } - this.ws = new WebSocket(`ws://localhost:${this.port}`) + this.ws = new WebSocket(`ws://localhost:${this.port}`); //onmessage接受的数据类型,只有在后端返回字节数组的时候才有效果 - this.ws.binaryType = 'arraybuffer'; + this.ws.binaryType = "arraybuffer"; this.ws.onopen = () => { - this.isConnected = true - resolve(true) - } + this.isConnected = true; + resolve(true); + }; this.ws.onerror = (e) => { - this.isConnected = false - console.log(e) - reject("ws onerror") - } + this.isConnected = false; + console.log(e); + reject("ws onerror"); + }; this.ws.onclose = () => { - this.isConnected = false - reject("ws onclose") - } + this.isConnected = false; + reject("ws onclose"); + }; this.ws.onmessage = (e) => { try { - const json = binaryDecode(e.data) - const { name, data } = json + const json = binaryDecode(e.data); + const { name, data } = json; try { if (this.maps.has(name) && this.maps.get(name).length) { console.log(json); - this.maps.get(name).forEach(({ cb, ctx }) => cb.call(ctx, data)) + this.maps.get(name).forEach(({ cb, ctx }) => cb.call(ctx, data)); } } catch (error) { - console.log("onmessage:", error) + console.log("onmessage:", error); } } catch (error) { - console.log('解析失败,不是合法的JSON格式', error) + console.log("解析失败,不是合法的JSON格式", error); } - } - }) + }; + }); } - callApi(name: T, data: IModel['api'][T]['req']): Promise> { + callApi(name: T, data: IModel["api"][T]["req"]): Promise> { return new Promise((resolve) => { try { // 超时处理 const timer = setTimeout(() => { - resolve({ success: false, error: new Error('timeout') }) - this.unlistenMsg(name as any, cb, null) - }, TIMEOUT) + resolve({ success: false, error: new Error("timeout") }); + this.unlistenMsg(name as any, cb, null); + }, TIMEOUT); // 回调处理 const cb = (res) => { - resolve(res) - clearTimeout(timer) - this.unlistenMsg(name as any, cb, null) - } - this.listenMsg(name as any, cb, null) + resolve(res); + clearTimeout(timer); + this.unlistenMsg(name as any, cb, null); + }; + this.listenMsg(name as any, cb, null); - this.sendMsg(name as any, data) + this.sendMsg(name as any, data); } catch (error) { - resolve({ success: false, error: error as Error }) + resolve({ success: false, error: error as Error }); } - }) + }); } - async sendMsg(name: T, data: IModel['msg'][T]) { - const view = binaryEncode(name, data) - let delay = parseInt(new URLSearchParams(location.search).get('delay') || '0') || 0; - await new Promise((r) => setTimeout(r, delay)) - this.ws.send(view.buffer) + async sendMsg(name: T, data: IModel["msg"][T]) { + const view = binaryEncode(name, data); + let delay = parseInt(new URLSearchParams(location.search).get("delay") || "0") || 0; + await new Promise((r) => setTimeout(r, delay)); + this.ws.send(view.buffer); } - listenMsg(name: T, cb: (args: IModel['msg'][T]) => void, ctx: unknown) { + listenMsg(name: T, cb: (args: IModel["msg"][T]) => void, ctx: unknown) { if (this.maps.has(name)) { - this.maps.get(name).push({ ctx, cb }) + this.maps.get(name).push({ ctx, cb }); } else { - this.maps.set(name, [{ ctx, cb }]) + this.maps.set(name, [{ ctx, cb }]); } } - unlistenMsg(name: T, cb: (args: IModel['msg'][T]) => void, ctx: unknown) { + unlistenMsg(name: T, cb: (args: IModel["msg"][T]) => void, ctx: unknown) { if (this.maps.has(name)) { - const items = this.maps.get(name) - const index = items.findIndex(i => cb === i.cb && i.ctx === ctx); - index > -1 && items.splice(index, 1) + const items = this.maps.get(name); + const index = items.findIndex((i) => cb === i.cb && i.ctx === ctx); + index > -1 && items.splice(index, 1); } } } diff --git a/apps/client/assets/Scripts/Global/ObjectPoolManager.ts b/apps/client/assets/Scripts/Global/ObjectPoolManager.ts index 1a496fc..f7553e7 100644 --- a/apps/client/assets/Scripts/Global/ObjectPoolManager.ts +++ b/apps/client/assets/Scripts/Global/ObjectPoolManager.ts @@ -1,55 +1,55 @@ -import Singleton from '../Base/Singleton' -import { instantiate, Node } from 'cc' -import DataManager from './DataManager' -import { EntityTypeEnum } from '../Common' +import Singleton from "../Base/Singleton"; +import { instantiate, Node } from "cc"; +import DataManager from "./DataManager"; +import { EntityTypeEnum } from "../Common"; export default class ObjectPoolManager extends Singleton { static get Instance() { - return super.GetInstance() + return super.GetInstance(); } - private objectPool: Node = null - private map: Map = new Map() + private objectPool: Node = null; + private map: Map = new Map(); private getContainerName(objectName: EntityTypeEnum) { - return objectName + 'Pool' + return objectName + "Pool"; } reset() { - this.objectPool = null - this.map.clear() + this.objectPool = null; + this.map.clear(); } get(objectName: EntityTypeEnum) { if (this.objectPool === null) { - this.objectPool = new Node("ObjectPool") - this.objectPool.setParent(DataManager.Instance.stage) + this.objectPool = new Node("ObjectPool"); + this.objectPool.setParent(DataManager.Instance.stage); } if (!this.map.has(objectName)) { - this.map.set(objectName, []) - const container = new Node(this.getContainerName(objectName)) - container.setParent(this.objectPool) + this.map.set(objectName, []); + const container = new Node(this.getContainerName(objectName)); + container.setParent(this.objectPool); } - let node: Node - const nodes = this.map.get(objectName) + let node: Node; + const nodes = this.map.get(objectName); if (!nodes.length) { - const prefab = DataManager.Instance.prefabMap.get(objectName) - node = instantiate(prefab) - node.name = objectName - node.setParent(this.objectPool.getChildByName(this.getContainerName(objectName))) + const prefab = DataManager.Instance.prefabMap.get(objectName); + node = instantiate(prefab); + node.name = objectName; + node.setParent(this.objectPool.getChildByName(this.getContainerName(objectName))); } else { - node = nodes.pop() + node = nodes.pop(); } - node.active = true - return node + node.active = true; + return node; } ret(object: Node) { - object.active = false - const objectName = object.name as EntityTypeEnum - this.map.get(objectName).push(object) + object.active = false; + const objectName = object.name as EntityTypeEnum; + this.map.get(objectName).push(object); } } diff --git a/apps/client/assets/Scripts/Global/ResourceManager.ts b/apps/client/assets/Scripts/Global/ResourceManager.ts index 4ebc672..779eae7 100644 --- a/apps/client/assets/Scripts/Global/ResourceManager.ts +++ b/apps/client/assets/Scripts/Global/ResourceManager.ts @@ -1,32 +1,32 @@ -import { _decorator, resources, Asset } from 'cc' -import Singleton from '../Base/Singleton' +import { _decorator, resources, Asset } from "cc"; +import Singleton from "../Base/Singleton"; export class ResourceManager extends Singleton { static get Instance() { - return super.GetInstance() + return super.GetInstance(); } loadRes(path: string, type: new (...args: any[]) => T) { return new Promise((resolve, reject) => { resources.load(path, type, (err, res) => { if (err) { - reject(err) - return + reject(err); + return; } - resolve(res) - }) - }) + resolve(res); + }); + }); } loadDir(path: string, type: new (...args: any[]) => T) { return new Promise((resolve, reject) => { resources.loadDir(path, type, (err, res) => { if (err) { - reject(err) - return + reject(err); + return; } - resolve(res) - }) - }) + resolve(res); + }); + }); } } diff --git a/apps/client/assets/Scripts/Scene/BattleManager.ts b/apps/client/assets/Scripts/Scene/BattleManager.ts index 147837d..501bee4 100644 --- a/apps/client/assets/Scripts/Scene/BattleManager.ts +++ b/apps/client/assets/Scripts/Scene/BattleManager.ts @@ -1,192 +1,218 @@ -import { _decorator, Component, Node, Prefab, instantiate, SpriteFrame, director } from 'cc'; -import { ActorManager } from '../Entity/Actor/ActorManager'; -import DataManager from '../Global/DataManager'; -import { JoyStickManager } from '../UI/JoyStickManager'; -import { ResourceManager } from '../Global/ResourceManager'; -import { EventEnum, PrefabPathEnum, SceneEnum, TexturePathEnum } from '../Enum'; -import NetworkManager from '../Global/NetworkManager'; -import ObjectPoolManager from '../Global/ObjectPoolManager'; -import { BulletManager } from '../Entity/Bullet/BulletManager'; -import { ApiMsgEnum, EntityTypeEnum, IMsgServerSync, InputTypeEnum } from '../Common'; -import EventManager from '../Global/EventManager'; +import { _decorator, Component, Node, Prefab, instantiate, SpriteFrame, director } from "cc"; +import { ActorManager } from "../Entity/Actor/ActorManager"; +import DataManager from "../Global/DataManager"; +import { JoyStickManager } from "../UI/JoyStickManager"; +import { ResourceManager } from "../Global/ResourceManager"; +import { EventEnum, PrefabPathEnum, SceneEnum, TexturePathEnum } from "../Enum"; +import NetworkManager from "../Global/NetworkManager"; +import ObjectPoolManager from "../Global/ObjectPoolManager"; +import { BulletManager } from "../Entity/Bullet/BulletManager"; +import { ApiMsgEnum, EntityTypeEnum, IClientInput, IMsgServerSync, InputTypeEnum, toFixed } from "../Common"; +import EventManager from "../Global/EventManager"; +import { deepClone } from "../Utils"; const { ccclass } = _decorator; -@ccclass('BattleManager') +@ccclass("BattleManager") export class BattleManager extends Component { - private stage: Node - private ui: Node - private shouldUpdate = false + private stage: Node; + private ui: Node; + private shouldUpdate = false; + private pendingMsg = []; - async start() { - //清空 - this.clearGame() + async start() { + //清空 + this.clearGame(); - //资源加载和网络连接同步执行 - await Promise.all([this.loadRes(), this.connectServer()]) + //资源加载和网络连接同步执行 + await Promise.all([this.loadRes(), this.connectServer()]); - this.initGame() + this.initGame(); - // 在场景初始化完毕之前,卡主别的玩家,准备好以后再告知服务器,等所有玩家都准备好以后才开始,这里就不做了 - NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgServerSync, this.handleSync, this); - NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgGameEnd, this.leaveGame, this); - EventManager.Instance.on(EventEnum.GameEnd, this.handleGameEnd, this) + // 在场景初始化完毕之前,卡主别的玩家,准备好以后再告知服务器,等所有玩家都准备好以后才开始,这里就不做了 + NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgServerSync, this.listenServerSync, this); + NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgGameEnd, this.listenGameEnd, this); + EventManager.Instance.on(EventEnum.ClientSync, this.handleClientSync, this); + EventManager.Instance.on(EventEnum.GameEnd, this.handleGameEnd, this); + } + + clearGame() { + //事件 + NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgServerSync, this.listenServerSync, this); + NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgGameEnd, this.listenGameEnd, this); + EventManager.Instance.off(EventEnum.ClientSync, this.handleClientSync, this); + EventManager.Instance.off(EventEnum.GameEnd, this.handleGameEnd, this); + + //数据 + this.shouldUpdate = false; + ObjectPoolManager.Instance.reset(); + DataManager.Instance.reset(); + + //节点 + this.stage = DataManager.Instance.stage = this.node.getChildByName("Stage"); + this.ui = this.node.getChildByName("UI"); + this.stage.destroyAllChildren(); + this.ui.destroyAllChildren(); + } + + async loadRes() { + const list = []; + for (const type in PrefabPathEnum) { + const p = ResourceManager.Instance.loadRes(PrefabPathEnum[type], Prefab).then((prefab) => { + DataManager.Instance.prefabMap.set(type, prefab); + }); + list.push(p); } - - clearGame() { - //监听 - NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgServerSync, this.handleSync, this); - NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgGameEnd, this.leaveGame, this); - EventManager.Instance.off(EventEnum.GameEnd, this.handleGameEnd, this) - - //数据 - this.shouldUpdate = false - ObjectPoolManager.Instance.reset() - DataManager.Instance.reset() - - //节点 - this.stage = DataManager.Instance.stage = this.node.getChildByName("Stage") - this.ui = this.node.getChildByName("UI") - this.stage.destroyAllChildren() - this.ui.destroyAllChildren() + for (const type in TexturePathEnum) { + const p = ResourceManager.Instance.loadDir(TexturePathEnum[type], SpriteFrame).then((spriteFrames) => { + DataManager.Instance.textureMap.set(type, spriteFrames); + }); + list.push(p); } + await Promise.all(list); + } - async loadRes() { - const list = [] - for (const type in PrefabPathEnum) { - const p = ResourceManager.Instance.loadRes(PrefabPathEnum[type], Prefab).then((prefab) => { - DataManager.Instance.prefabMap.set(type, prefab) - }) - list.push(p) - } - for (const type in TexturePathEnum) { - const p = ResourceManager.Instance.loadDir(TexturePathEnum[type], SpriteFrame).then((spriteFrames) => { - DataManager.Instance.textureMap.set(type, spriteFrames) - }) - list.push(p) - } - await Promise.all(list) + async connectServer() { + if (!(await NetworkManager.Instance.connect().catch(() => false))) { + await new Promise((resolve) => setTimeout(resolve, 1000)); + await this.connectServer(); } + } - async connectServer() { - if (!await NetworkManager.Instance.connect().catch(() => false)) { - await new Promise((resolve) => setTimeout(resolve, 1000)) - await this.connectServer() - } + async initGame() { + this.initJoyStick(); + this.initShoot(); + this.initMap(); + this.shouldUpdate = true; + } + + initJoyStick() { + const prefab = DataManager.Instance.prefabMap.get(EntityTypeEnum.JoyStick); + const joySitck = instantiate(prefab); + joySitck.setParent(this.ui); + const jm = (DataManager.Instance.jm = joySitck.getComponent(JoyStickManager)); + jm.init(); + } + + initShoot() { + const prefab = DataManager.Instance.prefabMap.get(EntityTypeEnum.Shoot); + const shoot = instantiate(prefab); + shoot.setParent(this.ui); + } + + initMap() { + const prefab = DataManager.Instance.prefabMap.get(EntityTypeEnum.Map1); + const map = instantiate(prefab); + map.setParent(this.stage); + } + + update(dt: number) { + if (!this.shouldUpdate) { + return; } + this.render(); + this.tick(dt); + } - leaveGame() { - this.clearGame() - director.loadScene(SceneEnum.Hall); + tick(dt: number) { + this.tickPlayer(dt); + // this.tickGlobal(dt) + } + + tickPlayer(dt: number) { + for (const p of DataManager.Instance.state.actors) { + const playerManager = DataManager.Instance.actorMap.get(p.id); + if (!playerManager) { + return; + } + playerManager.tick(dt); } + } - async handleGameEnd() { - const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiGameEnd, { rid: DataManager.Instance.roomInfo.id }) - if (!success) { - console.log(error) - return; - } + // tickGlobal(dt: number) { + // DataManager.Instance.applyInput({ + // type: InputTypeEnum.TimePast, + // dt: toFixed(dt), + // }) + // } + + render() { + this.renderPlayer(); + this.renderBullet(); + } + + renderPlayer() { + for (const p of DataManager.Instance.state.actors) { + let actorManager = DataManager.Instance.actorMap.get(p.id); + if (!actorManager) { + const playerPrefab = DataManager.Instance.prefabMap.get(p.type); + const player = instantiate(playerPrefab); + player.setParent(this.stage); + actorManager = player.addComponent(ActorManager); + DataManager.Instance.actorMap.set(p.id, actorManager); + actorManager.init(p); + } else { + actorManager.render(p); + } } + } - async initGame() { - this.initJoyStick() - this.initShoot() - this.initMap() - this.shouldUpdate = true + renderBullet() { + for (const b of DataManager.Instance.state.bullets) { + let bulletManager = DataManager.Instance.bulletMap.get(b.id); + if (!bulletManager) { + const bullet = ObjectPoolManager.Instance.get(b.type); + bulletManager = bullet.getComponent(BulletManager) || bullet.addComponent(BulletManager); + DataManager.Instance.bulletMap.set(b.id, bulletManager); + bulletManager.init(b); + } else { + bulletManager.render(b); + } } + } - initJoyStick() { - const prefab = DataManager.Instance.prefabMap.get(EntityTypeEnum.JoyStick) - const joySitck = instantiate(prefab) - joySitck.setParent(this.ui) - const jm = DataManager.Instance.jm = joySitck.getComponent(JoyStickManager) - jm.init() + handleClientSync(input: IClientInput) { + const msg = { + frameId: DataManager.Instance.frameId++, + input, + }; + NetworkManager.Instance.sendMsg(ApiMsgEnum.MsgClientSync, msg); + + //移动才做预测,射击不做 + if (input.type === InputTypeEnum.ActorMove) { + DataManager.Instance.applyInput(input); + this.pendingMsg.push(msg); } + } - initShoot() { - const prefab = DataManager.Instance.prefabMap.get(EntityTypeEnum.Shoot) - const shoot = instantiate(prefab) - shoot.setParent(this.ui) + listenServerSync({ lastFrameId, inputs }: IMsgServerSync) { + //回滚上次服务器状态 + DataManager.Instance.state = DataManager.Instance.lastState; + //应用服务器输入 + for (const input of inputs) { + DataManager.Instance.applyInput(input); } + //记录最新的服务器状态 + DataManager.Instance.lastState = deepClone(DataManager.Instance.state); - initMap() { - const prefab = DataManager.Instance.prefabMap.get(EntityTypeEnum.Map1) - const map = instantiate(prefab) - map.setParent(this.stage) + //过滤本地输入 + this.pendingMsg = this.pendingMsg.filter((msg) => msg.frameId > lastFrameId); + //应用本地输入 + for (const msg of this.pendingMsg) { + DataManager.Instance.applyInput(msg.input); } + } - update(dt: number) { - if (!this.shouldUpdate) { - return - } - this.render() - this.tick(dt) - } - - tick(dt: number) { - this.tickPlayer(dt) - // this.tickGlobal(dt) - } - - tickPlayer(dt: number) { - for (const p of DataManager.Instance.state.players) { - const playerManager = DataManager.Instance.actorMap.get(p.id) - if (!playerManager) { - return - } - playerManager.tick(dt) - } - } - - // tickGlobal(dt: number) { - // NetworkManager.Instance.sendMsg(ApiMsgEnum.MsgClientSync, { - // input: { - // type: InputTypeEnum.TimePast, - // dt: toFixed(dt), - // } - // }) - // } - - render() { - this.renderPlayer() - this.renderBullet() - } - - renderPlayer() { - for (const p of DataManager.Instance.state.players) { - let playerManager = DataManager.Instance.actorMap.get(p.id) - if (!playerManager) { - const playerPrefab = DataManager.Instance.prefabMap.get(p.type) - const player = instantiate(playerPrefab) - player.setParent(this.stage) - playerManager = player.addComponent(ActorManager) - DataManager.Instance.actorMap.set(p.id, playerManager) - playerManager.init(p) - } else { - playerManager.render(p) - } - } - } - - renderBullet() { - for (const b of DataManager.Instance.state.bullets) { - let bulletManager = DataManager.Instance.bulletMap.get(b.id) - if (!bulletManager) { - const bullet = ObjectPoolManager.Instance.get(b.type) - bulletManager = bullet.getComponent(BulletManager) || bullet.addComponent(BulletManager) - DataManager.Instance.bulletMap.set(b.id, bulletManager) - bulletManager.init(b) - } else { - bulletManager.render(b) - } - } - } - - handleSync(inputs: IMsgServerSync) { - for (const input of inputs) { - DataManager.Instance.applyInput(input) - } + async handleGameEnd() { + const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiGameEnd, { rid: DataManager.Instance.roomInfo.id }); + if (!success) { + console.log(error); + return; } + } + listenGameEnd() { + this.clearGame(); + director.loadScene(SceneEnum.Hall); + } } - diff --git a/apps/client/assets/Scripts/Scene/HallManager.ts b/apps/client/assets/Scripts/Scene/HallManager.ts index b0766cb..aaedafe 100644 --- a/apps/client/assets/Scripts/Scene/HallManager.ts +++ b/apps/client/assets/Scripts/Scene/HallManager.ts @@ -1,121 +1,122 @@ -import { _decorator, Component, Node, Prefab, director, instantiate } from 'cc'; -import { ApiMsgEnum, IApiPlayerListRes, IApiRoomListRes, IMsgPlayerList, IMsgRoomList } from '../Common'; -import { EventEnum, SceneEnum } from '../Enum'; -import DataManager from '../Global/DataManager'; -import EventManager from '../Global/EventManager'; -import NetworkManager from '../Global/NetworkManager'; -import { PlayerManager } from '../UI/PlayerManager'; -import { RoomManager } from '../UI/RoomManager'; +import { _decorator, Component, Node, Prefab, director, instantiate } from "cc"; +import { ApiMsgEnum, IApiPlayerListRes, IApiRoomListRes, IMsgPlayerList, IMsgRoomList } from "../Common"; +import { EventEnum, SceneEnum } from "../Enum"; +import DataManager from "../Global/DataManager"; +import EventManager from "../Global/EventManager"; +import NetworkManager from "../Global/NetworkManager"; +import { PlayerManager } from "../UI/PlayerManager"; +import { RoomManager } from "../UI/RoomManager"; const { ccclass, property } = _decorator; -@ccclass('HallManager') +@ccclass("HallManager") export class HallManager extends Component { - @property(Node) - playerContainer: Node = null; + @property(Node) + playerContainer: Node = null; - @property(Prefab) - playerPrefab: Prefab = null; + @property(Prefab) + playerPrefab: Prefab = null; - @property(Node) - roomContainer: Node = null; + @property(Node) + roomContainer: Node = null; - @property(Prefab) - roomPrefab: Prefab = null; + @property(Prefab) + roomPrefab: Prefab = null; - onLoad() { - director.preloadScene(SceneEnum.Room); - EventManager.Instance.on(EventEnum.RoomJoin, this.joinRoom, this) - NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgPlayerList, this.renderPlayers, this); - NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgRoomList, this.renderRooms, this); + onLoad() { + director.preloadScene(SceneEnum.Room); + EventManager.Instance.on(EventEnum.RoomJoin, this.handleJoinRoom, this); + NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgPlayerList, this.renderPlayers, this); + NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgRoomList, this.renderRooms, this); + } + + onDestroy() { + EventManager.Instance.off(EventEnum.RoomJoin, this.handleJoinRoom, this); + NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgPlayerList, this.renderPlayers, this); + NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgRoomList, this.renderRooms, this); + } + + start() { + this.playerContainer.destroyAllChildren(); + this.roomContainer.destroyAllChildren(); + this.getPlayers(); + this.getRooms(); + } + + async getPlayers() { + const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiPlayerList, {}); + if (!success) { + console.log(error); + return; } - onDestroy() { - EventManager.Instance.off(EventEnum.RoomJoin, this.joinRoom, this) - NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgPlayerList, this.renderPlayers, this); - NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgRoomList, this.renderRooms, this); + this.renderPlayers(res); + } + + renderPlayers({ list }: IApiPlayerListRes | IMsgPlayerList) { + for (const item of this.playerContainer.children) { + item.active = false; + } + while (this.playerContainer.children.length < list.length) { + const playerItem = instantiate(this.playerPrefab); + playerItem.active = false; + playerItem.setParent(this.playerContainer); } - start() { - this.getPlayers() - this.getRooms() + for (let i = 0; i < list.length; i++) { + const data = list[i]; + const node = this.playerContainer.children[i]; + const playerManager = node.getComponent(PlayerManager); + playerManager.init(data); + } + } + + async getRooms() { + const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiRoomList, {}); + if (!success) { + console.log(error); + return; } - async getPlayers() { - const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiPlayerList, {}); - if (!success) { - console.log(error) - return; - } + this.renderRooms(res); + } - this.renderPlayers(res) + renderRooms = ({ list }: IApiRoomListRes | IMsgRoomList) => { + for (const item of this.roomContainer.children) { + item.active = false; + } + while (this.roomContainer.children.length < list.length) { + const roomItem = instantiate(this.roomPrefab); + roomItem.active = false; + roomItem.setParent(this.roomContainer); } - renderPlayers = ({ list }: IApiPlayerListRes | IMsgPlayerList) => { - for (const item of this.playerContainer.children) { - item.active = false - } - while (this.playerContainer.children.length < list.length) { - const playerItem = instantiate(this.playerPrefab); - playerItem.active = false - playerItem.setParent(this.playerContainer) - } + for (let i = 0; i < list.length; i++) { + const data = list[i]; + const node = this.roomContainer.children[i]; + const roomItemManager = node.getComponent(RoomManager); + roomItemManager.init(data); + } + }; - for (let i = 0; i < list.length; i++) { - const data = list[i]; - const node = this.playerContainer.children[i] - const playerItemManager = node.getComponent(PlayerManager) - playerItemManager.init(data) - } + async handleCreateRoom() { + const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiRoomCreate, {}); + if (!success) { + console.log(error); + return; } - async getRooms() { - const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiRoomList, {}); - if (!success) { - console.log(error) - return; - } + DataManager.Instance.roomInfo = res.room; + director.loadScene(SceneEnum.Room); + } - this.renderRooms(res) + async handleJoinRoom(rid: number) { + const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiRoomJoin, { rid }); + if (!success) { + console.log(error); + return; } - renderRooms = ({ list }: IApiRoomListRes | IMsgRoomList) => { - for (const item of this.roomContainer.children) { - item.active = false - } - while (this.roomContainer.children.length < list.length) { - const roomItem = instantiate(this.roomPrefab); - roomItem.active = false - roomItem.setParent(this.roomContainer) - } - - for (let i = 0; i < list.length; i++) { - const data = list[i]; - const node = this.roomContainer.children[i] - const roomItemManager = node.getComponent(RoomManager) - roomItemManager.init(data) - } - } - - async createRoom() { - const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiRoomCreate, {}); - if (!success) { - console.log(error) - return; - } - - DataManager.Instance.roomInfo = res.room - director.loadScene(SceneEnum.Room); - } - - async joinRoom(rid: number) { - const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiRoomJoin, { rid }); - if (!success) { - console.log(error) - return; - } - - DataManager.Instance.roomInfo = res.room - director.loadScene(SceneEnum.Room); - } + DataManager.Instance.roomInfo = res.room; + director.loadScene(SceneEnum.Room); + } } - diff --git a/apps/client/assets/Scripts/Scene/LoginManager.ts b/apps/client/assets/Scripts/Scene/LoginManager.ts index 76742ec..2ab71c4 100644 --- a/apps/client/assets/Scripts/Scene/LoginManager.ts +++ b/apps/client/assets/Scripts/Scene/LoginManager.ts @@ -1,45 +1,44 @@ -import { _decorator, Component, EditBox, director } from 'cc'; -import { ApiMsgEnum } from '../Common'; -import { SceneEnum } from '../Enum'; -import DataManager from '../Global/DataManager'; -import NetworkManager from '../Global/NetworkManager'; +import { _decorator, Component, EditBox, director } from "cc"; +import { ApiMsgEnum } from "../Common"; +import { SceneEnum } from "../Enum"; +import DataManager from "../Global/DataManager"; +import NetworkManager from "../Global/NetworkManager"; const { ccclass, property } = _decorator; -@ccclass('LoginManager') +@ccclass("LoginManager") export class LoginManager extends Component { - input: EditBox + input: EditBox; - onLoad() { - this.input = this.node.getChildByName('Input').getComponent(EditBox) - director.preloadScene(SceneEnum.Hall); + onLoad() { + this.input = this.node.getChildByName("Input").getComponent(EditBox); + director.preloadScene(SceneEnum.Hall); + } + + async start() { + await NetworkManager.Instance.connect(); + console.log("服务连接成功!"); + } + + async handleClick() { + if (!NetworkManager.Instance.isConnected) { + console.log("未连接!"); + await NetworkManager.Instance.connect(); + } + const nickname = this.input.string; + if (!nickname) { + console.log("请输入昵称!"); + return; + } + let { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiPlayerJoin, { + nickname, + }); + + if (!success) { + console.log(error); + return; } - async start() { - await NetworkManager.Instance.connect(); - console.log("服务连接成功!"); - } - - async handleClick() { - if (!NetworkManager.Instance.isConnected) { - console.log("未连接!"); - await NetworkManager.Instance.connect(); - } - const nickname = this.input.string; - if (!nickname) { - console.log("请输入昵称!") - return; - } - let { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiPlayerJoin, { - nickname, - }); - - if (!success) { - console.log(error); - return; - } - - DataManager.Instance.myPlayerId = res.player.id; - director.loadScene(SceneEnum.Hall); - } + DataManager.Instance.myPlayerId = res.player.id; + director.loadScene(SceneEnum.Hall); + } } - diff --git a/apps/client/assets/Scripts/Scene/RoomManager.ts b/apps/client/assets/Scripts/Scene/RoomManager.ts index a25cac1..2d420d2 100644 --- a/apps/client/assets/Scripts/Scene/RoomManager.ts +++ b/apps/client/assets/Scripts/Scene/RoomManager.ts @@ -1,76 +1,77 @@ -import { _decorator, Component, Node, Prefab, director, instantiate } from 'cc'; -import { ApiMsgEnum, IMsgGameStart, IMsgRoom } from '../Common'; -import { SceneEnum } from '../Enum'; -import DataManager from '../Global/DataManager'; -import NetworkManager from '../Global/NetworkManager'; -import { PlayerManager } from '../UI/PlayerManager'; +import { _decorator, Component, Node, Prefab, director, instantiate } from "cc"; +import { ApiMsgEnum, IMsgGameStart, IMsgRoom } from "../Common"; +import { SceneEnum } from "../Enum"; +import DataManager from "../Global/DataManager"; +import NetworkManager from "../Global/NetworkManager"; +import { PlayerManager } from "../UI/PlayerManager"; +import { deepClone } from "../Utils"; const { ccclass, property } = _decorator; -@ccclass('RoomManager') +@ccclass("RoomManager") export class RoomManager extends Component { - @property(Node) - playerContainer: Node = null; + @property(Node) + playerContainer: Node = null; - @property(Prefab) - playerPrefab: Prefab = null; + @property(Prefab) + playerPrefab: Prefab = null; - onLoad() { - director.preloadScene(SceneEnum.Battle); - NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgRoom, this.renderPlayers, this); - NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgGameStart, this.handleGameStart, this); + onLoad() { + director.preloadScene(SceneEnum.Battle); + NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgRoom, this.renderPlayers, this); + NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgGameStart, this.handleGameStart, this); + } + + onDestroy() { + NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgRoom, this.renderPlayers, this); + NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgGameStart, this.handleGameStart, this); + } + + async start() { + this.renderPlayers({ + room: DataManager.Instance.roomInfo, + }); + } + + renderPlayers({ room: { players: list } }: IMsgRoom) { + for (const item of this.playerContainer.children) { + item.active = false; + } + while (this.playerContainer.children.length < list.length) { + const playerItem = instantiate(this.playerPrefab); + playerItem.active = false; + playerItem.setParent(this.playerContainer); } - onDestroy() { - NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgRoom, this.renderPlayers, this); - NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgGameStart, this.handleGameStart, this); + for (let i = 0; i < list.length; i++) { + const data = list[i]; + const node = this.playerContainer.children[i]; + const playerItemManager = node.getComponent(PlayerManager); + playerItemManager.init(data); + } + } + + async handleLeave() { + const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiRoomLeave, {}); + if (!success) { + console.log(error); + return; } - async start() { - this.renderPlayers({ - room: DataManager.Instance.roomInfo - }) + DataManager.Instance.roomInfo = null; + director.loadScene(SceneEnum.Hall); + } + + async handleStart() { + const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiGameStart, { rid: DataManager.Instance.roomInfo.id }); + if (!success) { + console.log(error); + return; } + } - renderPlayers({ room: { players: list } }: IMsgRoom) { - for (const item of this.playerContainer.children) { - item.active = false - } - while (this.playerContainer.children.length < list.length) { - const playerItem = instantiate(this.playerPrefab); - playerItem.active = false - playerItem.setParent(this.playerContainer) - } - - for (let i = 0; i < list.length; i++) { - const data = list[i]; - const node = this.playerContainer.children[i] - const playerItemManager = node.getComponent(PlayerManager) - playerItemManager.init(data) - } - } - - async handleLeave() { - const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiRoomLeave, {}); - if (!success) { - console.log(error) - return; - } - - DataManager.Instance.roomInfo = null - director.loadScene(SceneEnum.Hall); - } - - async handleStart() { - const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiGameStart, { rid: DataManager.Instance.roomInfo.id }); - if (!success) { - console.log(error) - return; - } - } - - handleGameStart({ state }: IMsgGameStart) { - DataManager.Instance.state = state - director.loadScene(SceneEnum.Battle); - } + handleGameStart({ state }: IMsgGameStart) { + DataManager.Instance.state = state; + DataManager.Instance.lastState = deepClone(DataManager.Instance.state); + director.loadScene(SceneEnum.Battle); + } } - diff --git a/apps/client/assets/Scripts/UI/JoyStickManager.ts b/apps/client/assets/Scripts/UI/JoyStickManager.ts index ba56d6e..d9eb54f 100644 --- a/apps/client/assets/Scripts/UI/JoyStickManager.ts +++ b/apps/client/assets/Scripts/UI/JoyStickManager.ts @@ -1,57 +1,55 @@ -import { _decorator, Component, Node, input, Input, EventTouch, Vec2, Vec3, UITransform } from 'cc'; +import { _decorator, Component, Node, input, Input, EventTouch, Vec2, Vec3, UITransform } from "cc"; const { ccclass, property } = _decorator; -@ccclass('JoyStickManager') +@ccclass("JoyStickManager") export class JoyStickManager extends Component { - input: Vec2 = Vec2.ZERO + input: Vec2 = Vec2.ZERO; - private body: Node - private stick: Node - private touchStartPos: Vec2 - private defaultPos: Vec2 - private radius: number = 0 + private body: Node; + private stick: Node; + private touchStartPos: Vec2; + private defaultPos: Vec2; + private radius: number = 0; - init() { - this.body = this.node.getChildByName("Body") - this.stick = this.body.getChildByName("Stick") - const { x, y } = this.body.position - this.defaultPos = new Vec2(x, y) - this.radius = this.body.getComponent(UITransform).contentSize.x / 2 + init() { + this.body = this.node.getChildByName("Body"); + this.stick = this.body.getChildByName("Stick"); + const { x, y } = this.body.position; + this.defaultPos = new Vec2(x, y); + this.radius = this.body.getComponent(UITransform).contentSize.x / 2; - input.on(Input.EventType.TOUCH_START, this.onTouchMove, this); - input.on(Input.EventType.TOUCH_MOVE, this.onTouchMove, this); - input.on(Input.EventType.TOUCH_END, this.onTouchEnd, this); + input.on(Input.EventType.TOUCH_START, this.onTouchMove, this); + input.on(Input.EventType.TOUCH_MOVE, this.onTouchMove, this); + input.on(Input.EventType.TOUCH_END, this.onTouchEnd, this); + } + onDestroy() { + input.off(Input.EventType.TOUCH_START, this.onTouchMove, this); + input.off(Input.EventType.TOUCH_MOVE, this.onTouchMove, this); + input.off(Input.EventType.TOUCH_END, this.onTouchEnd, this); + } + + onTouchMove(e: EventTouch) { + const touchPos = e.touch.getUILocation(); + if (!this.touchStartPos) { + this.touchStartPos = touchPos.clone(); + this.body.setPosition(this.touchStartPos.x, this.touchStartPos.y); } - onDestroy() { - input.off(Input.EventType.TOUCH_START, this.onTouchMove, this); - input.off(Input.EventType.TOUCH_MOVE, this.onTouchMove, this); - input.off(Input.EventType.TOUCH_END, this.onTouchEnd, this); + const stickPos = new Vec2(touchPos.x - this.touchStartPos.x, touchPos.y - this.touchStartPos.y); + const len = stickPos.length(); + if (len > this.radius) { + stickPos.multiplyScalar(this.radius / len); } - onTouchMove(e: EventTouch) { - const touchPos = e.touch.getUILocation() - if (!this.touchStartPos) { - this.touchStartPos = touchPos.clone() - this.body.setPosition(this.touchStartPos.x, this.touchStartPos.y); - } + this.stick.setPosition(stickPos.x, stickPos.y); + this.input = stickPos.clone().normalize(); + } - const stickPos = new Vec2(touchPos.x - this.touchStartPos.x, touchPos.y - this.touchStartPos.y) - const len = stickPos.length() - if (len > this.radius) { - stickPos.multiplyScalar(this.radius / len) - } - - this.stick.setPosition(stickPos.x, stickPos.y) - this.input = stickPos.clone().normalize() - } - - onTouchEnd() { - this.body.setPosition(this.defaultPos.x, this.defaultPos.y) - this.stick.setPosition(0, 0) - this.touchStartPos = undefined - this.input = Vec2.ZERO - } + onTouchEnd() { + this.body.setPosition(this.defaultPos.x, this.defaultPos.y); + this.stick.setPosition(0, 0); + this.touchStartPos = undefined; + this.input = Vec2.ZERO; + } } - diff --git a/apps/client/assets/Scripts/UI/PlayerManager.ts b/apps/client/assets/Scripts/UI/PlayerManager.ts index 018492d..3a8f6af 100644 --- a/apps/client/assets/Scripts/UI/PlayerManager.ts +++ b/apps/client/assets/Scripts/UI/PlayerManager.ts @@ -1,22 +1,19 @@ -import { _decorator, Component, Node, Label } from 'cc'; -import { EventEnum } from '../Enum'; -import DataManager from '../Global/DataManager'; -import EventManager from '../Global/EventManager'; +import { _decorator, Component, Node, Label } from "cc"; +import DataManager from "../Global/DataManager"; const { ccclass, property } = _decorator; -@ccclass('PlayerManager') +@ccclass("PlayerManager") export class PlayerManager extends Component { - init({ id, nickname, rid }: { id: number, nickname: string, rid: number }) { - const label = this.getComponent(Label) - let str = nickname + init({ id, nickname, rid }: { id: number; nickname: string; rid: number }) { + const label = this.getComponent(Label); + let str = nickname; if (DataManager.Instance.myPlayerId === id) { - str += `(我)` + str += `(我)`; } if (rid !== -1) { - str += `(房间${rid})` + str += `(房间${rid})`; } - label.string = str - this.node.active = true + label.string = str; + this.node.active = true; } } - diff --git a/apps/client/assets/Scripts/UI/RoomManager.ts b/apps/client/assets/Scripts/UI/RoomManager.ts index f0986ee..c9c2ebf 100644 --- a/apps/client/assets/Scripts/UI/RoomManager.ts +++ b/apps/client/assets/Scripts/UI/RoomManager.ts @@ -1,20 +1,19 @@ -import { _decorator, Component, Node, Label } from 'cc'; -import { EventEnum } from '../Enum'; -import EventManager from '../Global/EventManager'; +import { _decorator, Component, Node, Label } from "cc"; +import { EventEnum } from "../Enum"; +import EventManager from "../Global/EventManager"; const { ccclass, property } = _decorator; -@ccclass('RoomManager') +@ccclass("RoomManager") export class RoomManager extends Component { - id: number - init({ id, players }: { id: number, players: Array<{ id: number, nickname: string }> }) { - this.id = id - const label = this.getComponent(Label) - label.string = `房间id:${id},当前人数:${players.length}` - this.node.active = true + id: number; + init({ id, players }: { id: number; players: Array<{ id: number; nickname: string }> }) { + this.id = id; + const label = this.getComponent(Label); + label.string = `房间id:${id},当前人数:${players.length}`; + this.node.active = true; } handleClick() { - EventManager.Instance.emit(EventEnum.RoomJoin, this.id) + EventManager.Instance.emit(EventEnum.RoomJoin, this.id); } } - diff --git a/apps/client/assets/Scripts/UI/ShootManager.ts b/apps/client/assets/Scripts/UI/ShootManager.ts index b16eb1d..d0525df 100644 --- a/apps/client/assets/Scripts/UI/ShootManager.ts +++ b/apps/client/assets/Scripts/UI/ShootManager.ts @@ -1,13 +1,11 @@ -import { _decorator, Component, Node } from 'cc'; -import { EventEnum } from '../Enum'; -import EventManager from '../Global/EventManager'; +import { _decorator, Component, Node } from "cc"; +import { EventEnum } from "../Enum"; +import EventManager from "../Global/EventManager"; const { ccclass, property } = _decorator; -@ccclass('ShootManager') +@ccclass("ShootManager") export class ShootManager extends Component { - shoot() { - EventManager.Instance.emit(EventEnum.WeaponShoot) - } - + shoot() { + EventManager.Instance.emit(EventEnum.WeaponShoot); + } } - diff --git a/apps/client/assets/Scripts/Utils/index.ts b/apps/client/assets/Scripts/Utils/index.ts index 5ccb01e..a859672 100644 --- a/apps/client/assets/Scripts/Utils/index.ts +++ b/apps/client/assets/Scripts/Utils/index.ts @@ -1,11 +1,25 @@ -import { SpriteFrame } from "cc" -import { ApiMsgEnum, InputTypeEnum, strdecode } from "../Common" +import { SpriteFrame } from "cc"; -const INDEX_REG = /\((\d+)\)/ +const INDEX_REG = /\((\d+)\)/; -const getNumberWithinString = (str: string) => parseInt(str.match(INDEX_REG)?.[1] || '0') +const getNumberWithinString = (str: string) => parseInt(str.match(INDEX_REG)?.[1] || "0"); export const sortSpriteFrame = (spriteFrame: Array) => - 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 \ No newline at end of file +export const rad2Angle = (rad: number) => (rad / Math.PI) * 180; + +export const deepClone = (obj: any) => { + if (typeof obj !== "object" || obj === null) { + return obj; + } + + const res = Array.isArray(obj) ? [] : {}; + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + res[key] = deepClone(obj[key]); + } + } + + return res; +}; diff --git a/apps/server/src/Common/Api.ts b/apps/server/src/Common/Api.ts index 72d12cb..b12b162 100644 --- a/apps/server/src/Common/Api.ts +++ b/apps/server/src/Common/Api.ts @@ -1,61 +1,60 @@ export interface IPlayer { - id: number, nickname: string, rid: number + id: number; + nickname: string; + rid: number; } export interface IRoom { - id: number, players: Array + id: number; + players: Array; } -export interface IApiPlayerListReq { -} +export interface IApiPlayerListReq {} export interface IApiPlayerListRes { - list: Array + list: Array; } export interface IApiPlayerJoinReq { - nickname: string + nickname: string; } export interface IApiPlayerJoinRes { - player: IPlayer + player: IPlayer; } -export interface IApiRoomListReq { -} +export interface IApiRoomListReq {} export interface IApiRoomListRes { - list: Array + list: Array; } -export interface IApiRoomCreateReq { -} +export interface IApiRoomCreateReq {} export interface IApiRoomCreateRes { - room: IRoom + room: IRoom; } export interface IApiRoomJoinReq { - rid: number + rid: number; } export interface IApiRoomJoinRes { - room: IRoom + room: IRoom; } -export interface IApiRoomLeaveReq { -} +export interface IApiRoomLeaveReq {} -export interface IApiRoomLeaveRes { } +export interface IApiRoomLeaveRes {} export interface IApiGameStartReq { - rid: number + rid: number; } -export interface IApiGameStartRes { } +export interface IApiGameStartRes {} export interface IApiGameEndReq { - rid: number + rid: number; } -export interface IApiGameEndRes { } \ No newline at end of file +export interface IApiGameEndRes {} diff --git a/apps/server/src/Common/Binary.ts b/apps/server/src/Common/Binary.ts index 054b1f0..6ce1521 100644 --- a/apps/server/src/Common/Binary.ts +++ b/apps/server/src/Common/Binary.ts @@ -1,91 +1,100 @@ import { ApiMsgEnum, InputTypeEnum } from "./Enum"; import { strdecode, strencode, toFixed } 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) +const encodeActorMove = (input: any, view: DataView, index: number) => { + view.setUint8(index++, input.type) + view.setUint8(index++, input.id) + view.setFloat32(index, input.direction.x) index += 4 - view.setFloat32(index, data.direction.y) + view.setFloat32(index, input.direction.y) index += 4 - view.setFloat32(index, data.dt) + view.setFloat32(index, input.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) +const encodeWeaponShoot = (input: any, view: DataView, index: number) => { + view.setUint8(index++, input.type) + view.setUint8(index++, input.owner) + view.setFloat32(index, input.position.x) index += 4 - view.setFloat32(index, data.position.y) + view.setFloat32(index, input.position.y) index += 4 - view.setFloat32(index, data.direction.x) + view.setFloat32(index, input.direction.x) index += 4 - view.setFloat32(index, data.direction.y) + view.setFloat32(index, input.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) +export const encodeTimePast = (input: any, view: DataView, index: number) => { + view.setUint8(index++, input.type) + view.setFloat32(index, input.dt) index += 4 } -export const binaryEncode = (proto: ApiMsgEnum, data: any): DataView => { - if (proto === ApiMsgEnum.MsgClientSync) { - if (data.type === InputTypeEnum.ActorMove) { +export const binaryEncode = (name: ApiMsgEnum, data: any): DataView => { + if (name === ApiMsgEnum.MsgClientSync) { + //name 1字节 + frameId 4字节 + 数据长度 n 字节 + const { frameId, input } = data + if (input.type === InputTypeEnum.ActorMove) { let index = 0 - const ab = new ArrayBuffer(3 + 12) + const ab = new ArrayBuffer(1 + 4 + 14) const view = new DataView(ab) - view.setUint8(index++, proto) - encodeActorMove(proto, data, view, index) + view.setUint8(index++, name) + view.setUint32(index, frameId) + index += 4 + encodeActorMove(input, view, index) return view - } else if (data.type === InputTypeEnum.WeaponShoot) { + } else if (input.type === InputTypeEnum.WeaponShoot) { let index = 0 - const ab = new ArrayBuffer(3 + 16) + const ab = new ArrayBuffer(1 + 4 + 18) const view = new DataView(ab) - view.setUint8(index++, proto) - encodeWeaponShoot(proto, data, view, index) + view.setUint8(index++, name) + view.setUint32(index, frameId) + index += 4 + encodeWeaponShoot(input, view, index) return view } else { let index = 0 - const ab = new ArrayBuffer(2 + 4) + const ab = new ArrayBuffer(1 + 4 + 5) const view = new DataView(ab) - view.setUint8(index++, proto) - encdoeTimePast(proto, data, view, index) + view.setUint8(index++, name) + view.setUint32(index, frameId) + index += 4 + encodeTimePast(input, view, index) return view } - } else if (proto === ApiMsgEnum.MsgServerSync) { + } else if (name === ApiMsgEnum.MsgServerSync) { + const { lastFrameId, inputs } = data let total = 0 - for (let i = 0; i < data.length; i++) { - const item = data[i]; - if (item.type === InputTypeEnum.ActorMove) { + for (const input of inputs) { + if (input.type === InputTypeEnum.ActorMove) { total += 14 - } else if (item.type === InputTypeEnum.WeaponShoot) { + } else if (input.type === InputTypeEnum.WeaponShoot) { total += 18 } else { total += 5 } } - const ab = new ArrayBuffer(1 + 1 + total) + //name 1字节 + lastFrameId 4字节 + 数组长度 1字节 + 数据长度 n 字节 + const ab = new ArrayBuffer(1 + 4 + 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) + view.setUint8(index++, name) + view.setUint32(index, lastFrameId) + index += 4 + view.setUint8(index++, inputs.length) + for (const input of inputs) { + if (input.type === InputTypeEnum.ActorMove) { + encodeActorMove(input, view, index) index += 14 - } else if (item.type === InputTypeEnum.WeaponShoot) { - encodeWeaponShoot(proto, item, view, index) + } else if (input.type === InputTypeEnum.WeaponShoot) { + encodeWeaponShoot(input, view, index) index += 18 } else { - encdoeTimePast(proto, item, view, index) + encodeTimePast(input, view, index) index += 5 } } - return view } else { let index = 0 @@ -93,7 +102,7 @@ export const binaryEncode = (proto: ApiMsgEnum, data: any): DataView => { const ta = strencode(str) const ab = new ArrayBuffer(ta.length + 1) const view = new DataView(ab) - view.setUint8(index++, proto) + view.setUint8(index++, name) for (let i = 0; i < ta.length; i++) { view.setUint8(index++, ta[i]) } @@ -109,20 +118,17 @@ const decodeActorMove = (view: DataView, index: number) => { index += 4 const dt = toFixed(view.getFloat32(index)) index += 4 - const msg = { - name: ApiMsgEnum.MsgClientSync, - data: { - type: InputTypeEnum.ActorMove, - id, - direction: { - x: directionX, - y: directionY, - }, - dt - } + const input = { + type: InputTypeEnum.ActorMove, + id, + direction: { + x: directionX, + y: directionY, + }, + dt } - return msg + return input } const decodeWeaponShoot = (view: DataView, index: number) => { @@ -135,76 +141,96 @@ const decodeWeaponShoot = (view: DataView, index: number) => { index += 4 const directionY = toFixed(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, - }, - } + const input = { + type: InputTypeEnum.WeaponShoot, + owner, + position: { + x: positionX, + y: positionY, + }, + direction: { + x: directionX, + y: directionY, + }, } - - return msg + return input } const decodeTimePast = (view: DataView, index: number) => { const dt = toFixed(view.getFloat32(index)) index += 4 - const msg = { - name: ApiMsgEnum.MsgClientSync, - data: { - type: InputTypeEnum.TimePast, - dt, - } + const input = { + type: InputTypeEnum.TimePast, + dt, } - return msg + return input } export const binaryDecode = (buffer: ArrayBuffer) => { let index = 0 const view = new DataView(buffer) - const proto = view.getUint8(index++) + const name = view.getUint8(index++) - if (proto === ApiMsgEnum.MsgClientSync) { + if (name === ApiMsgEnum.MsgClientSync) { + const frameId = view.getUint32(index) + index += 4 const inputType = view.getUint8(index++) if (inputType === InputTypeEnum.ActorMove) { - return decodeActorMove(view, index) + const input = decodeActorMove(view, index) + return { + name, + data: { + frameId, + input + } + } } else if (inputType === InputTypeEnum.WeaponShoot) { - return decodeWeaponShoot(view, index) + const input = decodeWeaponShoot(view, index) + return { + name, + data: { + frameId, + input + } + } } else { - return decodeTimePast(view, index) + const input = decodeTimePast(view, index) + return { + name, + data: { + frameId, + input + } + } } - } else if (proto === ApiMsgEnum.MsgServerSync) { + } else if (name === ApiMsgEnum.MsgServerSync) { + const lastFrameId = view.getUint32(index) + index += 4 const len = view.getUint8(index++) - const res = [] + const inputs = [] for (let i = 0; i < len; i++) { const inputType = view.getUint8(index++) - if (inputType === InputTypeEnum.ActorMove) { - res.push(decodeActorMove(view, index).data) + inputs.push(decodeActorMove(view, index)) index += 13 } else if (inputType === InputTypeEnum.WeaponShoot) { - res.push(decodeWeaponShoot(view, index).data) + inputs.push(decodeWeaponShoot(view, index)) index += 17 } else { - res.push(decodeTimePast(view, index).data) + inputs.push(decodeTimePast(view, index)) index += 4 } } return { name: ApiMsgEnum.MsgServerSync, - data: res + data: { + lastFrameId, + inputs + } } } else { return { - name: proto, + name: name, data: JSON.parse(strdecode(new Uint8Array(buffer.slice(1)))) } } diff --git a/apps/server/src/Common/Enum.ts b/apps/server/src/Common/Enum.ts index f7cf599..2dd72f5 100644 --- a/apps/server/src/Common/Enum.ts +++ b/apps/server/src/Common/Enum.ts @@ -38,7 +38,6 @@ export enum ApiMsgEnum { // TimePast = 'TimePast', // } - export enum InputTypeEnum { ActorMove, WeaponShoot, @@ -46,14 +45,14 @@ export enum InputTypeEnum { } export enum EntityTypeEnum { - Map1 = 'Map1', - Actor1 = 'Actor1', - Actor2 = 'Actor2', - Weapon1 = 'Weapon1', - Weapon2 = 'Weapon2', - Bullet1 = 'Bullet1', - Bullet2 = 'Bullet2', - Explosion = 'Explosion', - JoyStick = 'JoyStick', - Shoot = 'Shoot', -} \ No newline at end of file + Map1 = "Map1", + Actor1 = "Actor1", + Actor2 = "Actor2", + Weapon1 = "Weapon1", + Weapon2 = "Weapon2", + Bullet1 = "Bullet1", + Bullet2 = "Bullet2", + Explosion = "Explosion", + JoyStick = "JoyStick", + Shoot = "Shoot", +} diff --git a/apps/server/src/Common/Model.ts b/apps/server/src/Common/Model.ts index 53655fc..d086e3f 100644 --- a/apps/server/src/Common/Model.ts +++ b/apps/server/src/Common/Model.ts @@ -1,50 +1,66 @@ -import { IApiGameEndReq, IApiGameStartReq, IApiGameStartRes, IApiPlayerJoinReq, IApiPlayerJoinRes, IApiPlayerListReq, IApiPlayerListRes, IApiRoomCreateReq, IApiRoomCreateRes, IApiRoomJoinReq, IApiRoomJoinRes, IApiRoomLeaveReq, IApiRoomLeaveRes, IApiRoomListReq, IApiRoomListRes, IApiGameEndRes } from './Api' -import { ApiMsgEnum } from './Enum' -import { IMsgClientSync, IMsgGameEnd, IMsgGameStart, IMsgPlayerList, IMsgRoom, IMsgRoomList, IMsgServerSync } from './Msg' +import { + IApiGameEndReq, + IApiGameStartReq, + IApiGameStartRes, + IApiPlayerJoinReq, + IApiPlayerJoinRes, + IApiPlayerListReq, + IApiPlayerListRes, + IApiRoomCreateReq, + IApiRoomCreateRes, + IApiRoomJoinReq, + IApiRoomJoinRes, + IApiRoomLeaveReq, + IApiRoomLeaveRes, + IApiRoomListReq, + IApiRoomListRes, + IApiGameEndRes, +} from "./Api"; +import { ApiMsgEnum } from "./Enum"; +import { IMsgClientSync, IMsgGameEnd, IMsgGameStart, IMsgPlayerList, IMsgRoom, IMsgRoomList, IMsgServerSync } from "./Msg"; export interface IModel { api: { [ApiMsgEnum.ApiPlayerJoin]: { - req: IApiPlayerJoinReq, - res: IApiPlayerJoinRes, - } + req: IApiPlayerJoinReq; + res: IApiPlayerJoinRes; + }; [ApiMsgEnum.ApiPlayerList]: { - req: IApiPlayerListReq, - res: IApiPlayerListRes, - } + req: IApiPlayerListReq; + res: IApiPlayerListRes; + }; [ApiMsgEnum.ApiRoomList]: { - req: IApiRoomListReq, - res: IApiRoomListRes, - } + req: IApiRoomListReq; + res: IApiRoomListRes; + }; [ApiMsgEnum.ApiRoomCreate]: { - req: IApiRoomCreateReq, - res: IApiRoomCreateRes, - } + req: IApiRoomCreateReq; + res: IApiRoomCreateRes; + }; [ApiMsgEnum.ApiRoomJoin]: { - req: IApiRoomJoinReq, - res: IApiRoomJoinRes, - } + req: IApiRoomJoinReq; + res: IApiRoomJoinRes; + }; [ApiMsgEnum.ApiRoomLeave]: { - req: IApiRoomLeaveReq, - res: IApiRoomLeaveRes, - } + req: IApiRoomLeaveReq; + res: IApiRoomLeaveRes; + }; [ApiMsgEnum.ApiGameStart]: { - req: IApiGameStartReq, - res: IApiGameStartRes, - }, + req: IApiGameStartReq; + res: IApiGameStartRes; + }; [ApiMsgEnum.ApiGameEnd]: { - req: IApiGameEndReq, - res: IApiGameEndRes, - } - }, + req: IApiGameEndReq; + res: IApiGameEndRes; + }; + }; msg: { - [ApiMsgEnum.MsgPlayerList]: IMsgPlayerList - [ApiMsgEnum.MsgRoomList]: IMsgRoomList, - [ApiMsgEnum.MsgRoom]: IMsgRoom, - [ApiMsgEnum.MsgGameStart]: IMsgGameStart, - [ApiMsgEnum.MsgGameEnd]: IMsgGameEnd, - [ApiMsgEnum.MsgClientSync]: IMsgClientSync, - [ApiMsgEnum.MsgServerSync]: IMsgServerSync, - } + [ApiMsgEnum.MsgPlayerList]: IMsgPlayerList; + [ApiMsgEnum.MsgRoomList]: IMsgRoomList; + [ApiMsgEnum.MsgRoom]: IMsgRoom; + [ApiMsgEnum.MsgGameStart]: IMsgGameStart; + [ApiMsgEnum.MsgGameEnd]: IMsgGameEnd; + [ApiMsgEnum.MsgClientSync]: IMsgClientSync; + [ApiMsgEnum.MsgServerSync]: IMsgServerSync; + }; } - diff --git a/apps/server/src/Common/Msg.ts b/apps/server/src/Common/Msg.ts index fff1eb2..a9f1da1 100644 --- a/apps/server/src/Common/Msg.ts +++ b/apps/server/src/Common/Msg.ts @@ -1,31 +1,34 @@ -import { IPlayer, IRoom } from "./Api" -import { IClientInput, IState } from "./State" +import { IPlayer, IRoom } from "./Api"; +import { IClientInput, IState } from "./State"; export interface IMsgPlayerList { - list: Array + list: Array; } export interface IMsgRoomList { - list: Array + list: Array; } export interface IMsgRoom { - room: IRoom + room: IRoom; } export interface IMsgGameStart { - state: IState + state: IState; } - export interface IMsgGameStart { - state: IState + state: IState; } -export interface IMsgGameEnd { +export interface IMsgGameEnd {} + +export interface IMsgClientSync { + frameId: number; + input: IClientInput; } - -export type IMsgClientSync = IClientInput - -export type IMsgServerSync = Array \ No newline at end of file +export interface IMsgServerSync { + lastFrameId: number; + inputs: Array; +} diff --git a/apps/server/src/Common/State.ts b/apps/server/src/Common/State.ts index 11114c3..4396041 100644 --- a/apps/server/src/Common/State.ts +++ b/apps/server/src/Common/State.ts @@ -1,50 +1,50 @@ -import { EntityTypeEnum, InputTypeEnum } from "./Enum" +import { EntityTypeEnum, InputTypeEnum } from "./Enum"; export interface IActor { - id: number - nickname: string - type: EntityTypeEnum - weaponType: EntityTypeEnum - bulletType: EntityTypeEnum + id: number; + nickname: string; + type: EntityTypeEnum; + weaponType: EntityTypeEnum; + bulletType: EntityTypeEnum; //动态数据 - hp: number - position: IVec2 - direction: IVec2 + hp: number; + position: IVec2; + direction: IVec2; } export interface IBullet { - id: number - owner: number - type: EntityTypeEnum + id: number; + owner: number; + type: EntityTypeEnum; //动态数据 - position: IVec2 - direction: IVec2 + position: IVec2; + direction: IVec2; } export interface IVec2 { x: number; - y: number + y: number; } export interface IState { - players: IActor[], - bullets: IBullet[], - nextBulletId: number + actors: IActor[]; + bullets: IBullet[]; + nextBulletId: number; } -export type IClientInput = IActorMove | IWeaponShoot | ITimePast +export type IClientInput = IActorMove | IWeaponShoot | ITimePast; export interface IActorMove { - type: InputTypeEnum.ActorMove + type: InputTypeEnum.ActorMove; id: number; direction: IVec2; dt: number; } export interface IWeaponShoot { - type: InputTypeEnum.WeaponShoot + type: InputTypeEnum.WeaponShoot; owner: number; position: IVec2; direction: IVec2; @@ -52,5 +52,5 @@ export interface IWeaponShoot { export interface ITimePast { type: InputTypeEnum.TimePast; - dt: number -} \ No newline at end of file + dt: number; +} diff --git a/apps/server/src/Common/Utils.ts b/apps/server/src/Common/Utils.ts index 9629064..898a1d9 100644 --- a/apps/server/src/Common/Utils.ts +++ b/apps/server/src/Common/Utils.ts @@ -1,4 +1,4 @@ -export const toFixed = (num: number, digit: number = 4): number => Math.floor(num * 10 ** digit) / 10 ** digit +export const toFixed = (num: number, digit: number = 4): number => Math.floor(num * 10 ** digit) / 10 ** digit; export const strencode = (str: string) => { let byteArray: number[] = []; @@ -11,11 +11,16 @@ export const strencode = (str: string) => { } 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)); + 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[] = []; @@ -33,10 +38,14 @@ export const strdecode = (bytes: Uint8Array) => { 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); + 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); -} \ No newline at end of file +}; diff --git a/apps/server/src/Core/MyServer.ts b/apps/server/src/Core/MyServer.ts index 02a5e73..15707d5 100644 --- a/apps/server/src/Core/MyServer.ts +++ b/apps/server/src/Core/MyServer.ts @@ -1,64 +1,63 @@ -import { EventEmitter } from 'stream'; -import WebSocket, { WebSocketServer } from 'ws'; -import { ApiMsgEnum } from '../Common'; -import { Connection, ConnectionEventEnum } from './Connection'; +import { EventEmitter } from "stream"; +import WebSocket, { WebSocketServer } from "ws"; +import { ApiMsgEnum } from "../Common"; +import { Connection, ConnectionEventEnum } from "./Connection"; export interface IMyServerOptions { - port: number + port: number; } export enum MyServerEventEnum { - Connect = 'Connect', - DisConnect = 'DisConnect', + Connect = "Connect", + DisConnect = "DisConnect", } export class MyServer extends EventEmitter { - wss?: WebSocketServer - port: number - connections: Set = new Set() - apiMap: Map = new Map() + wss?: WebSocketServer; + port: number; + connections: Set = new Set(); + apiMap: Map = new Map(); constructor({ port = 8080 }: Partial) { - super() - this.port = port + 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("connection", this.handleConnect.bind(this)); this.wss.on("error", (e) => { - reject(e) - }) + reject(e); + }); this.wss.on("close", () => { console.log("MyServer 服务关闭"); - }) + }); this.wss.on("listening", () => { - resolve(true) - }) - }) - + resolve(true); + }); + }); } handleConnect(ws: WebSocket) { //初始化 - const connection = new Connection(this, ws) + const connection = new Connection(this, ws); //向外告知有人来了 - this.connections.add(connection) - this.emit(MyServerEventEnum.Connect, connection) + 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) - }) + this.connections.delete(connection); + this.emit(MyServerEventEnum.DisConnect, connection, code, reason); + }); } setApi(apiName: ApiMsgEnum, cb: Function) { - this.apiMap.set(apiName, cb) + this.apiMap.set(apiName, cb); } -} \ No newline at end of file +} diff --git a/apps/server/src/base/Singleton.ts b/apps/server/src/base/Singleton.ts index b755b94..5564bcb 100644 --- a/apps/server/src/base/Singleton.ts +++ b/apps/server/src/base/Singleton.ts @@ -1,13 +1,12 @@ export default class Singleton { - private static _instance: any = null + private static _instance: any = null; static GetInstance(): T { if (this._instance === null) { - this._instance = new this() + this._instance = new this(); } - return this._instance + return this._instance; } - protected constructor() { - } -} \ No newline at end of file + protected constructor() {} +} diff --git a/apps/server/src/biz/Player.ts b/apps/server/src/biz/Player.ts index 3be2644..f375c38 100644 --- a/apps/server/src/biz/Player.ts +++ b/apps/server/src/biz/Player.ts @@ -1,16 +1,16 @@ -import { Connection } from "../Core" +import { Connection } from "../Core"; export default class Player { - id: number - nickname: string - connection: Connection - rid: number + id: number; + nickname: string; + connection: Connection; + rid: number; - constructor({ id, nickname, connection }: Pick) { - this.id = id - this.nickname = nickname - this.connection = connection - this.connection.playerId = this.id - this.rid = -1 + constructor({ id, nickname, connection }: Pick) { + this.id = id; + this.nickname = nickname; + this.connection = connection; + this.connection.playerId = this.id; + this.rid = -1; } } diff --git a/apps/server/src/biz/PlayerManager.ts b/apps/server/src/biz/PlayerManager.ts index ff041a8..137baff 100644 --- a/apps/server/src/biz/PlayerManager.ts +++ b/apps/server/src/biz/PlayerManager.ts @@ -1,54 +1,55 @@ -import Singleton from '../Base/Singleton' -import { ApiMsgEnum, IApiPlayerJoinReq } from '../Common' -import { Connection } from '../Core' -import Player from './Player' -import RoomManager from './RoomManager' +import Singleton from "../Base/Singleton"; +import { ApiMsgEnum, IApiPlayerJoinReq } from "../Common"; +import { Connection } from "../Core"; +import Player from "./Player"; +import RoomManager from "./RoomManager"; export default class PlayerManager extends Singleton { static get Instance() { - return super.GetInstance() + return super.GetInstance(); } - playerId = 1 - players: Set = new Set() - idMapPlayer: Map = new Map() + players: Set = new Set(); + + private nextPlayerId = 1; + private idMapPlayer: Map = new Map(); createPlayer({ connection, nickname }: IApiPlayerJoinReq & { connection: Connection }) { - const player = new Player({ id: this.playerId++, connection, nickname }) - this.players.add(player) - this.idMapPlayer.set(player.id, player) - return player + const player = new Player({ id: this.nextPlayerId++, connection, nickname }); + this.players.add(player); + this.idMapPlayer.set(player.id, player); + return player; } removePlayer(uid: number) { - const player = this.idMapPlayer.get(uid) + const player = this.idMapPlayer.get(uid); if (player) { - const rid = player.rid + const rid = player.rid; if (rid !== undefined) { RoomManager.Instance.leaveRoom(rid, uid); RoomManager.Instance.syncRooms(); RoomManager.Instance.syncRoom(rid); } - this.players.delete(player) - this.idMapPlayer.delete(uid) - this.syncPlayers() + this.players.delete(player); + this.idMapPlayer.delete(uid); + this.syncPlayers(); } } getPlayerById(uid: number) { - return this.idMapPlayer.get(uid) + return this.idMapPlayer.get(uid); } syncPlayers() { for (const player of this.players) { - player.connection.sendMsg(ApiMsgEnum.MsgPlayerList, { list: this.getPlayersView() }) + player.connection.sendMsg(ApiMsgEnum.MsgPlayerList, { list: this.getPlayersView() }); } } getPlayersView(players: Set = this.players) { - return [...players].map((player) => this.getPlayerView(player)) + return [...players].map((player) => this.getPlayerView(player)); } getPlayerView({ id, nickname, rid }: Player) { - return { id, nickname, rid } + return { id, nickname, rid }; } } diff --git a/apps/server/src/biz/Room.ts b/apps/server/src/biz/Room.ts index 7e14021..32d9bd3 100644 --- a/apps/server/src/biz/Room.ts +++ b/apps/server/src/biz/Room.ts @@ -1,60 +1,63 @@ -import { ApiMsgEnum, EntityTypeEnum, IClientInput, InputTypeEnum, IState, toFixed } from '../Common' -import type Player from './Player' -import PlayerManager from './PlayerManager' -import RoomManager from './RoomManager' +import { ApiMsgEnum, EntityTypeEnum, IClientInput, IMsgClientSync, InputTypeEnum, IState, toFixed } from "../Common"; +import { Connection } from "../Core"; +import type Player from "./Player"; +import PlayerManager from "./PlayerManager"; +import RoomManager from "./RoomManager"; export default class Room { - id: number - players: Set = new Set() - lastSyncTime?: number + id: number; + players: Set = new Set(); - private timers: NodeJS.Timer[] = [] - private inputs: Array = [] + private lastTime?: number; + private timers: NodeJS.Timer[] = []; + private pendingInput: Array = []; + private lastPlayerFrameIdMap: Map = new Map(); constructor(rid: number) { - this.id = rid + this.id = rid; } join(uid: number) { - const player = PlayerManager.Instance.getPlayerById(uid) + const player = PlayerManager.Instance.getPlayerById(uid); if (player) { - player.rid = this.id - this.players.add(player) + player.rid = this.id; + this.players.add(player); } } leave(uid: number) { - const player = PlayerManager.Instance.getPlayerById(uid) + const player = PlayerManager.Instance.getPlayerById(uid); if (player) { - player.rid = -1 - player.connection.unlistenMsg(ApiMsgEnum.MsgClientSync, this.getInput, this) - this.players.delete(player) + player.rid = -1; + player.connection.unlistenMsg(ApiMsgEnum.MsgClientSync, this.getClientMsg, this); + this.players.delete(player); if (!this.players.size) { - RoomManager.Instance.closeRoom(this.id) + RoomManager.Instance.closeRoom(this.id); } } } close() { - this.timers.forEach(t => clearInterval(t)) + this.timers.forEach((t) => clearInterval(t)); for (const player of this.players) { - player.connection.sendMsg(ApiMsgEnum.MsgGameEnd, {}) - player.connection.unlistenMsg(ApiMsgEnum.MsgClientSync, this.getInput, this) + player.rid = -1; + player.connection.sendMsg(ApiMsgEnum.MsgGameEnd, {}); + player.connection.unlistenMsg(ApiMsgEnum.MsgClientSync, this.getClientMsg, this); } - this.players.clear() + this.players.clear(); } sync() { for (const player of this.players) { player.connection.sendMsg(ApiMsgEnum.MsgRoom, { - room: RoomManager.Instance.getRoomView(this) - }) + room: RoomManager.Instance.getRoomView(this), + }); } } start() { const state: IState = { - players: [...this.players].map((player, index) => ({ + actors: [...this.players].map((player, index) => ({ id: player.id, nickname: player.nickname, position: { @@ -63,52 +66,56 @@ export default class Room { }, direction: { x: 1, - y: 0 + y: 0, }, hp: 100, - type: EntityTypeEnum.Actor2, - weaponType: EntityTypeEnum.Weapon2, - bulletType: EntityTypeEnum.Bullet2, + type: index === 0 ? EntityTypeEnum.Actor1 : EntityTypeEnum.Actor2, + weaponType: index === 0 ? EntityTypeEnum.Weapon1 : EntityTypeEnum.Weapon2, + bulletType: index === 0 ? EntityTypeEnum.Bullet1 : EntityTypeEnum.Bullet2, })), bullets: [], - nextBulletId: 1 - } + nextBulletId: 1, + }; for (const player of this.players) { player.connection.sendMsg(ApiMsgEnum.MsgGameStart, { - state - }) - player.connection.listenMsg(ApiMsgEnum.MsgClientSync, this.getInput, this) + state, + }); + player.connection.listenMsg(ApiMsgEnum.MsgClientSync, this.getClientMsg, this); } let t1 = setInterval(() => { - this.sendInput() - }, 100) + this.sendServerMsg(); + }, 100); let t2 = setInterval(() => { - this.timePast() - }, 16) - this.timers = [t1, t2] + this.timePast(); + }, 16); + this.timers = [t1, t2]; } - getInput(input: IClientInput) { - this.inputs.push(input) + getClientMsg(connection: Connection, { frameId, input }: IMsgClientSync) { + this.lastPlayerFrameIdMap.set(connection.playerId, frameId); + this.pendingInput.push(input); } - sendInput() { - const inputs = this.inputs - this.inputs = [] + sendServerMsg() { + const pendingInput = this.pendingInput; + this.pendingInput = []; for (const player of this.players) { - player.connection.sendMsg(ApiMsgEnum.MsgServerSync, inputs) + player.connection.sendMsg(ApiMsgEnum.MsgServerSync, { + lastFrameId: this.lastPlayerFrameIdMap.get(player.id) ?? 0, + inputs: pendingInput, + }); } } timePast() { let now = process.uptime(); - const dt = now - (this.lastSyncTime ?? now) - this.inputs.push({ + const dt = now - (this.lastTime ?? now); + this.pendingInput.push({ type: InputTypeEnum.TimePast, - dt: toFixed(dt) - }) - this.lastSyncTime = now; + dt: toFixed(dt), + }); + this.lastTime = now; } } diff --git a/apps/server/src/biz/RoomManager.ts b/apps/server/src/biz/RoomManager.ts index 313cc39..1059f82 100644 --- a/apps/server/src/biz/RoomManager.ts +++ b/apps/server/src/biz/RoomManager.ts @@ -1,77 +1,77 @@ -import Singleton from '../Base/Singleton' -import { ApiMsgEnum } from '../Common' -import PlayerManager from './PlayerManager' -import Room from './Room' +import Singleton from "../Base/Singleton"; +import { ApiMsgEnum } from "../Common"; +import PlayerManager from "./PlayerManager"; +import Room from "./Room"; export default class RoomManager extends Singleton { static get Instance() { - return super.GetInstance() + return super.GetInstance(); } - roomId = 1 - rooms: Set = new Set() - idMapRoom: Map = new Map() + nextRoomId = 1; + rooms: Set = new Set(); + idMapRoom: Map = new Map(); createRoom() { - const room = new Room(this.roomId++) - this.rooms.add(room) - this.idMapRoom.set(room.id, room) - return room + const room = new Room(this.nextRoomId++); + this.rooms.add(room); + this.idMapRoom.set(room.id, room); + return room; } joinRoom(rid: number, uid: number) { - const room = this.getRoomById(rid) + const room = this.getRoomById(rid); if (room) { - room.join(uid) - return room + room.join(uid); + return room; } } leaveRoom(rid: number, uid: number) { - const room = this.getRoomById(rid) + const room = this.getRoomById(rid); if (room) { - room.leave(uid) + room.leave(uid); } } closeRoom(rid: number) { - const room = this.getRoomById(rid) + const room = this.getRoomById(rid); if (room) { - room.close() - this.rooms.delete(room) - this.idMapRoom.delete(rid) + room.close(); + this.rooms.delete(room); + this.idMapRoom.delete(rid); } } startRoom(rid: number) { - const room = this.getRoomById(rid) + const room = this.getRoomById(rid); if (room) { - room.start() + room.start(); } } getRoomById(id: number) { - return this.idMapRoom.get(id) + return this.idMapRoom.get(id); } syncRooms() { for (const player of PlayerManager.Instance.players) { - player.connection.sendMsg(ApiMsgEnum.MsgRoomList, { list: this.getRoomsView() }) + player.connection.sendMsg(ApiMsgEnum.MsgRoomList, { list: this.getRoomsView() }); } } syncRoom(rid: number) { - const room = this.idMapRoom.get(rid) + const room = this.idMapRoom.get(rid); if (room) { - room.sync() + room.sync(); } } getRoomsView(rooms: Set = this.rooms) { - return [...rooms].map((room) => this.getRoomView(room)) + return [...rooms].map((room) => this.getRoomView(room)); } getRoomView({ id, players }: Room) { - return { id, players: PlayerManager.Instance.getPlayersView(players) } + return { id, players: PlayerManager.Instance.getPlayersView(players) }; } } diff --git a/apps/server/src/common/index.ts b/apps/server/src/common/index.ts index 9f27c3d..f1f9242 100644 --- a/apps/server/src/common/index.ts +++ b/apps/server/src/common/index.ts @@ -1,8 +1,7 @@ -export * from './Api' -export * from './Msg' -export * from './Enum' -export * from './Model' -export * from './State' -export * from './Utils' -export * from './Binary' - +export * from "./Api"; +export * from "./Msg"; +export * from "./Enum"; +export * from "./Model"; +export * from "./State"; +export * from "./Utils"; +export * from "./Binary"; diff --git a/apps/server/src/core/Connection.ts b/apps/server/src/core/Connection.ts index 288b3b4..b9372f1 100644 --- a/apps/server/src/core/Connection.ts +++ b/apps/server/src/core/Connection.ts @@ -1,12 +1,12 @@ -import WebSocket from 'ws'; -import { EventEmitter } from 'stream'; -import { MyServer } from './MyServer'; -import { getTime, toArrayBuffer } from '../Utils'; -import { ApiMsgEnum, IModel } from '../Common'; -import { binaryEncode, binaryDecode } from '../Common/Binary'; +import WebSocket from "ws"; +import { EventEmitter } from "stream"; +import { MyServer } from "./MyServer"; +import { getTime, buffer2ArrayBuffer } from "../Utils"; +import { ApiMsgEnum, IModel } from "../Common"; +import { binaryEncode, binaryDecode } from "../Common/Binary"; export enum ConnectionEventEnum { - Close = 'Close', + Close = "Close", } interface IItem { @@ -15,80 +15,84 @@ interface IItem { } export class Connection extends EventEmitter { - server: MyServer - ws: WebSocket - msgMap: Map> = new Map() + server: MyServer; + ws: WebSocket; + msgMap: Map> = new Map(); playerId?: number; constructor(server: MyServer, ws: WebSocket) { - super() + super(); - this.server = server - this.ws = ws - this.ws.on('close', (code: number, reason: Buffer) => { - this.emit(ConnectionEventEnum.Close, code, reason.toString()) - }) + this.server = server; + this.ws = ws; + this.ws.on("close", (code: number, reason: Buffer) => { + this.emit(ConnectionEventEnum.Close, code, reason.toString()); + }); - this.ws.on('message', (buffer: Buffer) => { + this.ws.on("message", (buffer: Buffer) => { // const str = buffer.toString() try { - const json = binaryDecode(toArrayBuffer(buffer)) - const { name, data } = json + const json = binaryDecode(buffer2ArrayBuffer(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)}`) + console.log(`${getTime()}接收|字节数${buffer.length}|${this.playerId || -1}|${JSON.stringify(json)}`); if (this.server.apiMap.has(name)) { try { - const cb = this.server.apiMap.get(name) - const res = cb.call(null, data) + const cb = this.server.apiMap.get(name); + const res = cb.call(null, this, data); this.sendMsg(name, { success: true, res, - }) + }); } catch (error) { this.sendMsg(name, { success: false, error: (error as Error)?.message, - }) + }); } } else { try { if (this.msgMap.has(name)) { - this.msgMap.get(name)?.forEach(({ cb, ctx }) => cb.call(ctx, data)) + this.msgMap.get(name).forEach(({ cb, ctx }) => cb.call(ctx, this, data)); } } catch (error) { - console.log(error) + console.log(error); } } } catch (error) { - console.log(`解析失败,不是合法的JSON格式:`, error) + console.log(`解析失败,不是合法的JSON格式:`, error); } - }) + }); } - listenMsg(name: T, cb: (args: IModel['msg'][T]) => void, ctx: unknown) { + listenMsg(name: T, cb: (connection: Connection, arg: IModel["msg"][T]) => void, ctx: unknown) { if (this.msgMap.has(name)) { - this.msgMap.get(name)?.push({ cb, ctx }) + this.msgMap.get(name).push({ cb, ctx }); } else { - this.msgMap.set(name, [{ cb, ctx }]) + this.msgMap.set(name, [{ cb, ctx }]); } } - unlistenMsg(name: T, cb: (args: IModel['msg'][T]) => void, ctx: unknown) { + unlistenMsg(name: T, cb: (connection: Connection, arg: IModel["msg"][T]) => void, ctx: unknown) { if (this.msgMap.has(name)) { - const items = this.msgMap.get(name) - const index = items.findIndex(i => cb === i.cb && i.ctx === ctx); - index > -1 && items.splice(index, 1) + const items = this.msgMap.get(name); + const index = items.findIndex((i) => cb === i.cb && i.ctx === ctx); + index > -1 && items.splice(index, 1); } } - sendMsg(name: T, data: IModel['msg'][T]) { + sendMsg(name: T, data: IModel["msg"][T]) { const msg = JSON.stringify({ name, - data - }) - const view = binaryEncode(name, data) - const buffer = Buffer.from(view.buffer) - console.log(`${getTime()}发送|字节数${buffer.length}|${this.playerId || -1}|内存${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)}MB|${msg}`) - this.ws.send(buffer) + data, + }); + const view = binaryEncode(name, data); + const buffer = Buffer.from(view.buffer); + console.log( + `${getTime()}发送|字节数${buffer.length}|${this.playerId || -1}|内存${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed( + 2 + )}MB|${msg}` + ); + this.ws.send(buffer); } -} \ No newline at end of file +} diff --git a/apps/server/src/core/index.ts b/apps/server/src/core/index.ts index 9ddae88..d68225a 100644 --- a/apps/server/src/core/index.ts +++ b/apps/server/src/core/index.ts @@ -1,2 +1,2 @@ -export * from './MyServer' -export * from './Connection' \ No newline at end of file +export * from "./MyServer"; +export * from "./Connection"; diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts index 98728cc..a70900f 100644 --- a/apps/server/src/index.ts +++ b/apps/server/src/index.ts @@ -1,144 +1,165 @@ -import { Connection, MyServer, MyServerEventEnum } from './Core'; -import PlayerManager from './Biz/PlayerManager'; -import RoomManager from './Biz/RoomManager'; -import { getTime, symlinkCommon } from './Utils'; -import { ApiMsgEnum, IApiGameEndReq, IApiGameEndRes, IApiGameStartReq, IApiGameStartRes, IApiPlayerJoinReq, IApiPlayerJoinRes, IApiPlayerListReq, IApiPlayerListRes, IApiRoomCreateReq, IApiRoomCreateRes, IApiRoomJoinReq, IApiRoomJoinRes, IApiRoomLeaveReq, IApiRoomLeaveRes, IApiRoomListReq, IApiRoomListRes, IModel } from './Common'; +import { Connection, MyServer, MyServerEventEnum } from "./Core"; +import PlayerManager from "./Biz/PlayerManager"; +import RoomManager from "./Biz/RoomManager"; +import { getTime, symlinkCommon } from "./Utils"; +import { + ApiMsgEnum, + IApiGameEndReq, + IApiGameEndRes, + IApiGameStartReq, + IApiGameStartRes, + IApiPlayerJoinReq, + IApiPlayerJoinRes, + IApiPlayerListReq, + IApiPlayerListRes, + IApiRoomCreateReq, + IApiRoomCreateRes, + IApiRoomJoinReq, + IApiRoomJoinRes, + IApiRoomLeaveReq, + IApiRoomLeaveRes, + IApiRoomListReq, + IApiRoomListRes, + IModel, +} from "./Common"; -const server = new MyServer({ port: 8888 }) +const server = new MyServer({ port: 8888 }); // event server.on(MyServerEventEnum.Connect, (connection: Connection) => { - console.log(`${getTime()}来人|人数|${server.connections.size}`) -}) + console.log(`${getTime()}来人|人数|${server.connections.size}`); +}); server.on(MyServerEventEnum.DisConnect, (connection: Connection) => { - console.log(`${getTime()}走人|人数|${server.connections.size}`) + console.log(`${getTime()}走人|人数|${server.connections.size}`); if (connection.playerId) { - PlayerManager.Instance.removePlayer(connection.playerId) + PlayerManager.Instance.removePlayer(connection.playerId); } -}) +}); // api server.setApi(ApiMsgEnum.ApiPlayerList, (connection: Connection, data: IApiPlayerListReq): IApiPlayerListRes => { - return { list: PlayerManager.Instance.getPlayersView() } -}) + return { list: PlayerManager.Instance.getPlayersView() }; +}); server.setApi(ApiMsgEnum.ApiPlayerJoin, (connection: Connection, { nickname }: IApiPlayerJoinReq): IApiPlayerJoinRes => { - const player = PlayerManager.Instance.createPlayer({ connection, nickname }) - PlayerManager.Instance.syncPlayers() + const player = PlayerManager.Instance.createPlayer({ connection, nickname }); + PlayerManager.Instance.syncPlayers(); return { - player: PlayerManager.Instance.getPlayerView(player) - } -}) + player: PlayerManager.Instance.getPlayerView(player), + }; +}); server.setApi(ApiMsgEnum.ApiRoomList, (connection: Connection, data: IApiRoomListReq): IApiRoomListRes => { - return { list: RoomManager.Instance.getRoomsView() } -}) + return { list: RoomManager.Instance.getRoomsView() }; +}); server.setApi(ApiMsgEnum.ApiRoomCreate, (connection: Connection, data: IApiRoomCreateReq): IApiRoomCreateRes => { if (connection.playerId) { - const room = RoomManager.Instance.joinRoom(RoomManager.Instance.createRoom().id, connection.playerId) + const room = RoomManager.Instance.joinRoom(RoomManager.Instance.createRoom().id, connection.playerId); if (room) { - RoomManager.Instance.syncRooms() - PlayerManager.Instance.syncPlayers() + RoomManager.Instance.syncRooms(); + PlayerManager.Instance.syncPlayers(); return { - room: RoomManager.Instance.getRoomView(room) - } + room: RoomManager.Instance.getRoomView(room), + }; } else { - throw new Error("ApiRoomCreate room不存在") + throw new Error("ApiRoomCreate room不存在"); } } else { - throw new Error("ApiRoomCreate 玩家未登录") + throw new Error("ApiRoomCreate 玩家未登录"); } -}) +}); server.setApi(ApiMsgEnum.ApiRoomJoin, (connection: Connection, data: IApiRoomJoinReq): IApiRoomJoinRes => { if (connection.playerId) { - const room = RoomManager.Instance.joinRoom(data.rid, connection.playerId) + const room = RoomManager.Instance.joinRoom(data.rid, connection.playerId); if (room) { - RoomManager.Instance.syncRooms() - PlayerManager.Instance.syncPlayers() - RoomManager.Instance.syncRoom(room.id) + RoomManager.Instance.syncRooms(); + PlayerManager.Instance.syncPlayers(); + RoomManager.Instance.syncRoom(room.id); return { - room: RoomManager.Instance.getRoomView(room) - } + room: RoomManager.Instance.getRoomView(room), + }; } else { - throw new Error("ApiRoomJoin room不存在") + throw new Error("ApiRoomJoin room不存在"); } } else { - throw new Error("ApiRoomJoin 玩家未登录") + throw new Error("ApiRoomJoin 玩家未登录"); } -}) +}); server.setApi(ApiMsgEnum.ApiRoomLeave, (connection: Connection, data: IApiRoomLeaveReq): IApiRoomLeaveRes => { if (connection.playerId) { - const player = PlayerManager.Instance.getPlayerById(connection.playerId) + const player = PlayerManager.Instance.getPlayerById(connection.playerId); if (player) { - const rid = player.rid + const rid = player.rid; if (rid) { - RoomManager.Instance.leaveRoom(rid, player.id) - PlayerManager.Instance.syncPlayers() - RoomManager.Instance.syncRooms() - RoomManager.Instance.syncRoom(rid) - return {} + RoomManager.Instance.leaveRoom(rid, player.id); + PlayerManager.Instance.syncPlayers(); + RoomManager.Instance.syncRooms(); + RoomManager.Instance.syncRoom(rid); + return {}; } else { - throw new Error("ApiRoomLeave 玩家不在房间") + throw new Error("ApiRoomLeave 玩家不在房间"); } } else { - throw new Error("ApiRoomLeave 玩家不存在") + throw new Error("ApiRoomLeave 玩家不存在"); } } else { - throw new Error("ApiRoomLeave 玩家未登录") + throw new Error("ApiRoomLeave 玩家未登录"); } -}) +}); server.setApi(ApiMsgEnum.ApiGameStart, (connection: Connection, data: IApiGameStartReq): IApiGameStartRes => { if (connection.playerId) { - const player = PlayerManager.Instance.getPlayerById(connection.playerId) + const player = PlayerManager.Instance.getPlayerById(connection.playerId); if (player) { - const rid = player.rid + const rid = player.rid; if (rid) { - RoomManager.Instance.startRoom(rid) - // PlayerManager.Instance.syncPlayers() - // RoomManager.Instance.syncRooms() - return {} + RoomManager.Instance.startRoom(rid); + PlayerManager.Instance.syncPlayers(); + RoomManager.Instance.syncRooms(); + return {}; } else { - throw new Error("ApiRoomLeave 玩家不在房间") + throw new Error("ApiRoomLeave 玩家不在房间"); } } else { - throw new Error("ApiRoomLeave 玩家不存在") + throw new Error("ApiRoomLeave 玩家不存在"); } } else { - throw new Error("ApiRoomLeave 玩家未登录") + throw new Error("ApiRoomLeave 玩家未登录"); } -}) +}); server.setApi(ApiMsgEnum.ApiGameEnd, (connection: Connection, data: IApiGameEndReq): IApiGameEndRes => { if (connection.playerId) { - const player = PlayerManager.Instance.getPlayerById(connection.playerId) + const player = PlayerManager.Instance.getPlayerById(connection.playerId); if (player) { - const rid = player.rid + const rid = player.rid; if (rid) { - RoomManager.Instance.closeRoom(rid) - PlayerManager.Instance.syncPlayers() - RoomManager.Instance.syncRooms() - return {} + RoomManager.Instance.closeRoom(rid); + PlayerManager.Instance.syncPlayers(); + RoomManager.Instance.syncRooms(); + return {}; } else { - throw new Error("ApiGameEnd 玩家不在房间") + throw new Error("ApiGameEnd 玩家不在房间"); } } else { - throw new Error("ApiGameEnd 玩家不存在") + throw new Error("ApiGameEnd 玩家不存在"); } } else { - throw new Error("ApiGameEnd 玩家未登录") + throw new Error("ApiGameEnd 玩家未登录"); } -}) - +}); // start!! -server.start().then(() => { - symlinkCommon() - console.log("服务启动!") -}).catch((e) => { - console.log("服务异常", e) -}) +server + .start() + .then(() => { + symlinkCommon(); + console.log("服务启动!"); + }) + .catch((e) => { + console.log("服务异常", e); + }); diff --git a/apps/server/src/utils/binary.ts b/apps/server/src/utils/binary.ts deleted file mode 100644 index 30e201c..0000000 --- a/apps/server/src/utils/binary.ts +++ /dev/null @@ -1,821 +0,0 @@ -/** - * 类似于protobuffer,但此库及其精简且专为js打造 - * @author zp - * @version 1.1.0 - */ - -/** - * [注] nodejs环境下通过Uint8Array将ArrayBuffer和Buffer互相转换 - * @example - * // Buffer ---> ArrayBuffer - * function toArrayBuffer(buf) { - * var ab = new ArrayBuffer(buf.length); - * var view = new Uint8Array(ab); - * for (var i = 0; i < buf.length; ++i) { - * view[i] = buf[i]; - * } - * return ab; - * } - * // ArrayBuffer ---> Buffer - * function toBuffer(ab) { - * var buf = new Buffer(ab.byteLength); - * var view = new Uint8Array(ab); - * for (var i = 0; i < buf.length; ++i) { - * buf[i] = view[i]; - * } - * return buf; - * } - */ - -/** - * @example - * var { registerProto, Type, encode, decode, singleArray } = binary; - * registerProto(1001, { - * name: Type.String, - * age: Type.Uint8, - * sex: Type.Uint8 - * }) - * registerProto(1002, { - * info: 1001, - * gold: Type.Uint16, - * items: [Type.Uint16, Type.String] - * }) - * registerProto(1003, { - * array0: Type.Array, - * array1: singleArray(1002), - * array2: singleArray([1001, 1002]), - * array3: singleArray(Type.Uint16), - * array4: singleArray([Type.Uint16, Type.String]) - * }) - - * var buffer = encode({ name: 'Mary', age: 18, sex: 0 }, 1001); - * decode(buffer); - - * var buffer = encode({ info: { name: 'Mary', age: 18, sex: 0 }, gold: 10, array: [100, 2, 3] }, 1002); - * decode(buffer); - - * var buffer = encode({ - * array0: ['你好啊','我很好'], - * array1: [{ info: { name: 'James', age: 30, sex: 1 }, gold: 10, array: [100, 2, 3] }], - * array2: [[{}, { info: { name: 'Mary', age: 18, sex: 0 }, gold: 10, array: [100, 2, 3] }]], - * array3: [568], - * array4: [[0, '零'], [1, '一'], [2, '二'], [3, '三']] - * }, 1003); - * decode(buffer); - */ - -/** - * https://segmentfault.com/a/1190000014533505 中提到如果服务器开启了压缩了话,需要进行解压操作,并推荐了pako.js - * (具体也不知道这个压缩怎么回事,测试的时候也没遇到这个问题,先写注释记下来) - * @see https://github.com/nodeca/pako/edit/master/dist/pako.js - * @example - * let compressdata = new Uint8Array(buffer, byteOff, length); - * let uncompress = pako.inflate(compressdata);//解压数据 - * let uncompressdata = uncompress.buffer;// ArrayBuffer {} - * let dataViewData = new DataView(uncompressdata, 0);//解压后数据 - */ - - -/** -* A minimal base64 implementation for number arrays. -* @memberof util -* @namespace -*/ -class Base64 { - // Base64 encoding table - private b64 = new Array(64); - // Base64 decoding table - private s64 = new Array(123); - private invalidEncoding = "invalid encoding"; - - constructor() { - // 65..90, 97..122, 48..57, 43, 47 - for (var i = 0; i < 64;) { - this.s64[this.b64[i] = i < 26 ? i + 65 : i < 52 ? i + 71 : i < 62 ? i - 4 : i - 59 | 43] = i++; - } - } - - /** - * Calculates the byte length of a base64 encoded string. - * @param {string} string Base64 encoded string - * @returns {number} Byte length - */ - length(string: string): number { - var p = string.length; - if (!p) - return 0; - var n = 0; - while (--p % 4 > 1 && string.charAt(p) === "=") - ++n; - return Math.ceil(string.length * 3) / 4 - n; - }; - - /** - * Encodes a buffer to a base64 encoded string. - * @param {DataView} buffer Source buffer - * @param {number} start Source start - * @param {number} end Source end - * @returns {string} Base64 encoded string - */ - read(buffer: DataView, start: number, end: number): string { - var parts = null, - chunk = []; - var i = 0, // output index - j = 0, // goto index - t; // temporary - while (start < end) { - var b = buffer.getUint8(start++); - switch (j) { - case 0: - chunk[i++] = this.b64[b >> 2]; - t = (b & 3) << 4; - j = 1; - break; - case 1: - chunk[i++] = this.b64[t | b >> 4]; - t = (b & 15) << 2; - j = 2; - break; - case 2: - chunk[i++] = this.b64[t | b >> 6]; - chunk[i++] = this.b64[b & 63]; - j = 0; - break; - } - if (i > 8191) { - (parts || (parts = [])).push(String.fromCharCode.apply(String, chunk)); - i = 0; - } - } - if (j) { - chunk[i++] = this.b64[t]; - chunk[i++] = 61; - if (j === 1) - chunk[i++] = 61; - } - if (parts) { - if (i) - parts.push(String.fromCharCode.apply(String, chunk.slice(0, i))); - return parts.join(""); - } - return String.fromCharCode.apply(String, chunk.slice(0, i)); - }; - - - /** - * Decodes a base64 encoded string to a buffer. - * @param {string} string Source string - * @param {DataView} buffer Destination buffer - * @param {number} offset Destination offset - * @returns {number} Number of bytes written - * @throws {Error} If encoding is invalid - */ - write(string: string, buffer: DataView, offset: number): number { - var start = offset; - var j = 0, // goto index - t; // temporary - for (var i = 0; i < string.length;) { - var c = string.charCodeAt(i++); - if (c === 61 && j > 1) - break; - if ((c = this.s64[c]) === undefined) - throw Error(this.invalidEncoding); - switch (j) { - case 0: - t = c; - j = 1; - break; - case 1: - buffer.setUint8(offset++, t << 2 | (c & 48) >> 4); - t = c; - j = 2; - break; - case 2: - buffer.setUint8(offset++, (t & 15) << 4 | (c & 60) >> 2); - t = c; - j = 3; - break; - case 3: - buffer.setUint8(offset++, (t & 3) << 6 | c); - j = 0; - break; - } - } - if (j === 1) - throw Error(this.invalidEncoding); - return offset - start; - }; -} - -const base64 = new Base64(); - - -/** -* A minimal UTF8 implementation for number arrays. -* @memberof util -* @namespace -*/ -class UTF8 { - /** - * Calculates the UTF8 byte length of a string. - */ - length(string: string): number { - var len = 0, - c = 0; - for (var i = 0; i < string.length; ++i) { - c = string.charCodeAt(i); - if (c < 128) - len += 1; - else if (c < 2048) - len += 2; - else if ((c & 0xFC00) === 0xD800 && (string.charCodeAt(i + 1) & 0xFC00) === 0xDC00) { - ++i; - len += 4; - } else - len += 3; - } - return len; - }; - - /** - * Reads UTF8 bytes as a string. - */ - read(buffer: DataView, start: number, end: number): string { - var len = end - start; - if (len < 1) - return ""; - var parts = null, - chunk = [], - i = 0, // char offset - t; // temporary - while (start < end) { - t = buffer.getUint8(start++); - if (t < 128) - chunk[i++] = t; - else if (t > 191 && t < 224) - chunk[i++] = (t & 31) << 6 | buffer.getUint8(start++) & 63; - else if (t > 239 && t < 365) { - t = ((t & 7) << 18 | (buffer.getUint8(start++) & 63) << 12 | (buffer.getUint8(start++) & 63) << 6 | buffer.getUint8(start++) & 63) - 0x10000; - chunk[i++] = 0xD800 + (t >> 10); - chunk[i++] = 0xDC00 + (t & 1023); - } else - chunk[i++] = (t & 15) << 12 | (buffer.getUint8(start++) & 63) << 6 | buffer.getUint8(start++) & 63; - if (i > 8191) { - (parts || (parts = [])).push(String.fromCharCode.apply(String, chunk)); - i = 0; - } - } - if (parts) { - if (i) - parts.push(String.fromCharCode.apply(String, chunk.slice(0, i))); - return parts.join(""); - } - return String.fromCharCode.apply(String, chunk.slice(0, i)); - }; - - /** - * Writes a string as UTF8 bytes. - */ - write(string: string, buffer: DataView, offset: number): number { - var start = offset, - c1, // character 1 - c2; // character 2 - for (var i = 0; i < string.length; ++i) { - c1 = string.charCodeAt(i); - if (c1 < 128) { - buffer.setUint8(offset++, c1); - } else if (c1 < 2048) { - buffer.setUint8(offset++, c1 >> 6 | 192); - buffer.setUint8(offset++, c1 & 63 | 128); - } else if ((c1 & 0xFC00) === 0xD800 && ((c2 = string.charCodeAt(i + 1)) & 0xFC00) === 0xDC00) { - c1 = 0x10000 + ((c1 & 0x03FF) << 10) + (c2 & 0x03FF); - ++i; - buffer.setUint8(offset++, c1 >> 18 | 240); - buffer.setUint8(offset++, c1 >> 12 & 63 | 128); - buffer.setUint8(offset++, c1 >> 6 & 63 | 128); - buffer.setUint8(offset++, c1 & 63 | 128); - } else { - buffer.setUint8(offset++, c1 >> 12 | 224); - buffer.setUint8(offset++, c1 >> 6 & 63 | 128); - buffer.setUint8(offset++, c1 & 63 | 128); - } - } - return offset - start; - }; -} - -const utf8 = new UTF8(); - -class Encode { - private buffer: ArrayBuffer = null; - private view: DataView = null; - private index: number = 0; - - constructor(length: number) { - this.buffer = new ArrayBuffer(length) - this.view = new DataView(this.buffer); - this.index = 0; - } - - Int8(data: number) { - if (!isNumber(data)) data = 0; - return this.view.setInt8(this.index++, data); - } - - Uint8(data: number) { - if (!isNumber(data)) data = 0; - return this.view.setUint8(this.index++, data); - } - - Int16(data: number) { - if (!isNumber(data)) data = 0; - var value = this.view.setInt16(this.index, data); - this.index += 2; - return value; - } - - Uint16(data: number) { - if (!isNumber(data)) data = 0; - var value = this.view.setUint16(this.index, data); - this.index += 2; - return value; - } - - Int32(data: number) { - if (!isNumber(data)) data = 0; - var value = this.view.setInt32(this.index, data); - this.index += 4; - return value; - } - - Uint32(data: number) { - if (!isNumber(data)) data = 0; - var value = this.view.setUint32(this.index, data); - this.index += 4; - return value; - } - - Float32(data: number) { - if (!isNumber(data)) data = 0; - var value = this.view.setFloat32(this.index, data); - this.index += 4; - return value; - } - - Float64(data: number) { - if (!isNumber(data)) data = 0; - var value = this.view.setFloat64(this.index, data); - this.index += 8; - return value; - } - - Boolean(data) { - return this.Uint8(data ? 1 : 0); - } - - String(string) { - if (!isString(string)) string = ''; - - const len = utf8.write(string, this.view, this.index + 2); - this.Uint16(len); - this.index += len; - } - - Base64(string) { - if (!isBase64(string)) string = ''; - - const len = base64.write(string, this.view, this.index + 2); - this.Uint16(len); - this.index += len; - } - - Array(array) { - if (isArray(array) && !isEmpty(array)) { - return this.String(JSON.stringify(array)); - } else { - return this.String(''); - } - } - - Object(obj) { - if (isMap(obj) && !isEmpty(obj)) { - return this.String(JSON.stringify(obj)); - } else { - return this.String(''); - } - } - - Buffer() { - return this.buffer; - } -} - -class Decode { - private view: DataView = null; - private index: number = 0; - - constructor(buffer: ArrayBuffer) { - this.view = new DataView(buffer); - this.index = 0; - } - - Int8() { - return this.view.getInt8(this.index++); - } - - Uint8() { - return this.view.getUint8(this.index++); - } - - Int16() { - const value = this.view.getInt16(this.index); - this.index += 2; - return value; - } - - Uint16() { - const value = this.view.getUint16(this.index); - this.index += 2; - return value; - } - - Int32() { - const value = this.view.getInt32(this.index); - this.index += 4; - return value; - } - - Uint32() { - const value = this.view.getUint32(this.index); - this.index += 4; - return value; - } - - Float32() { - const value = this.view.getFloat32(this.index); - this.index += 4; - return value; - } - - Float64() { - const value = this.view.getFloat64(this.index); - this.index += 8; - return value; - } - - Boolean() { - return !!this.Uint8(); - } - - String() { - const len = this.Uint16(); - this.index += len; - return utf8.read(this.view, this.index - len, this.index); - } - - Base64() { - const len = this.Uint16(); - this.index += len; - return base64.read(this.view, this.index - len, this.index); - } - - Array() { - const str = this.String(); - return str ? JSON.parse(str) : []; - } - - Object() { - const str = this.String(); - return str ? JSON.parse(str) : {}; - } -} - -const getType = function (param) { - return Object.prototype.toString.call(param).slice(8, -1).toLowerCase(); -} - -const isObject = function (param) { - return param && typeof param === 'object'; -} - -const isArray = function (param) { - return getType(param) === 'array'; -} - -const isMap = function (param) { - return getType(param) === 'object'; -} - -const isString = function (param) { - return getType(param) === 'string'; -} - -const isNumber = function (param) { - return getType(param) === 'number'; -} - -const isBoolean = function (param) { - return getType(param) === 'boolean'; -} - -const isBase64 = function (param) { - return isString(param) && /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(param); -} - -function stringStartsWith(str1: string, str2: string) { - if (str1 === str2) { - return true; - } - for (let index = 0; index < str2.length; index++) { - if (str1[index] !== str2[index]) { - return false; - } - } - return true; -} - -function isEmpty(obj) { - if (isArray(obj)) { - return !obj.length; - } else if (isMap(obj)) { - for (const key in obj) { - return false; - } - } - return true; -} - -function compareStr(str1: string, str2: string) { - if (str1 === str2) { - return 0; - } - if (str1.length > str2.length) { - return 1; - } - if (str1.length < str2.length) { - return -1; - } - - for (let i = 0, code1 = 0, code2 = 0; i < str1.length; i++) { - if (str2.length <= i) { - return 1; - } else { - code1 = str1.charCodeAt(i); - code2 = str2.charCodeAt(i); - if (code1 > code2) { - return 1; - } else if (code1 < code2) { - return -1; - } - } - } - return 0; -} - -function sortKeys(obj) { - if (isMap(obj)) { - let index = 0; - const keys: string[] = []; - for (const key in obj) { - for (index = keys.length - 1; index >= 0; index--) { - if (compareStr(key, keys[index]) >= 0) { - break; - } - } - if (index === keys.length - 1) { - keys.push(key); - } else { - keys.splice(index + 1, 0, key); - } - } - return keys; - } else if (isArray(obj)) { - return obj.map(function (v, k) { - return k; - }) - } - - return []; -} - -function realType(type) { - if (isObject(type)) { - return type; - } - return protoCache[type] || type; -} - -const singleArrayPrefix = 'SingleArray'; - -function isSingleArray(str: string) { - return isString(str) && stringStartsWith(str, singleArrayPrefix); -} - -function SingleArrayProto(str: string) { - const stringify = str.slice(singleArrayPrefix.length + 1, -1); - return JSON.parse(stringify); -} - -/** -* 标记单一类型的数组 -* @param proto -*/ -export const singleArray = function (proto) { - return `${singleArrayPrefix}(${JSON.stringify(proto)})`; -} - -function DataLen(data: any, proto: any) { - proto = realType(proto); - - let length = 0; - if (isMap(proto)) { - if (!isMap(data)) data = {}; - for (const key in proto) { - length += DataLen(data[key], proto[key]); - } - } else if (isArray(proto)) { - if (!isArray(data)) data = []; - proto.forEach(function (type, index) { - length += DataLen(data[index], type); - }) - } else if (proto === 'String') { - // 如果是String的话,固定开头有2字节记录字符串长度 - length += 2; - if (isString(data)) length += utf8.length(data); - } else if (proto === 'Object' || proto === 'Array') { - // Object和Array类型也会将数据通过JSON.stringify转成String格式 - length += 2; - if (!isEmpty(data)) length += utf8.length(JSON.stringify(data)); - } else if (proto === 'Base64') { - // 如果是Base64的话,固定开头有2字节记录字符串长度 - length += 2; - if (isBase64(data)) length += base64.length(data); - } else if (isSingleArray(proto)) { - // 如果是SingleArray的话,固定开头有2字节记录数组长度 - length += 2; - if (!isArray(data)) data = []; - proto = realType(SingleArrayProto(proto)); - data.forEach(function (value) { - length += DataLen(value, proto); - }) - } else if (TypeByte[proto]) { - length += TypeByte[proto]; - } else { - throw new Error("'proto' is bad"); - } - - return length; -} - -function encodeData(encode: Encode, data: any, proto: any) { - proto = realType(proto); - - if (isMap(proto)) { - if (!isMap(data)) data = {}; - sortKeys(proto).forEach(function (key) { - encodeData(encode, data[key], proto[key]); - }) - } else if (isArray(proto)) { - if (!isArray(data)) data = []; - proto.forEach(function (type, index) { - encodeData(encode, data[index], type); - }) - } else if (isSingleArray(proto)) { - if (!isArray(data)) data = []; - encode.Uint16(data.length); - proto = realType(SingleArrayProto(proto)); - data.forEach(function (value) { - encodeData(encode, value, proto); - }) - } else { - encode[proto](data); - } -} - -function decodeData(decode: Decode, proto: any) { - proto = realType(proto); - - if (isMap(proto)) { - const obj = {}; - sortKeys(proto).forEach(function (key) { - obj[key] = decodeData(decode, proto[key]); - }); - return obj; - } else if (isArray(proto)) { - return proto.map(function (type) { - return decodeData(decode, type); - }); - } else if (isSingleArray(proto)) { - const arr = []; - const len = decode.Uint16(); - proto = realType(SingleArrayProto(proto)); - for (let index = 0; index < len; index++) { - arr.push(decodeData(decode, proto)); - } - return arr; - } else { - return decode[proto](); - } -} - -const TypeByte = { - 'Int8': 1, - 'Uint8': 1, - 'Int16': 2, - 'Uint16': 2, - 'Int32': 4, - 'Uint32': 4, - 'Float32': 4, - 'Float64': 8, - 'BigInt64': 8, - 'BigUint64': 8, - 'Boolean': 1, - 'String': 1, - 'Base64': 1, - 'Array': 1, - 'Object': 1 -} - -export const Type = { - 'Int8': 'Int8', // 1byte -128 to 127 - 'Uint8': 'Uint8', // 1byte 0 to 255 - 'Uint8Clamped': 'Uint8', // 1byte 0 to 255 - 'Int16': 'Int16', // 2byte -32768 to 32767 - 'Uint16': 'Uint16', // 2byte 0 to 65535 - 'Int32': 'Int32', // 4byte -2147483648 to 2147483647 - 'Uint32': 'Uint32', // 4byte 0 to 4294967295 - 'Float32': 'Float32', // 4byte 1.2x10^-38 to 3.4x10^38 - 'Float64': 'Float64', // 8byte 5.0x10^-324 to 1.8x10^308 - 'BigInt64': 'BigInt64', // 8byte -2^63 to (2^63)-1 - 'BigUint64': 'BigUint64', // 8byte 0 to (2^64)-1 - 'Boolean': 'Boolean', // 1byte 0 to 255 - 'String': 'String', // 1byte 0 to 255 - 'Base64': 'Base64', // 1byte 0 to 255 - 'Array': 'Array', // 1byte 0 to 255 - 'Object': 'Object' // 1byte 0 to 255 -} - -/** -* 序列化 -* 开头2字节用来存储proto的id -*/ -export const encode = function (obj: Object, id: number | string) { - const proto = protoCache[id]; - if (proto) { - const len = DataLen(obj, proto); - const encode = new Encode(len + 2); - encode.Uint16(Number(id)); - encodeData(encode, obj, proto); - return encode.Buffer(); - } else { - throw new Error("encode error: 'id' is bad"); - } -} - -/** -* 反序列化 -* 开头2字节代表proto的id -*/ -export const decode = function (buffer: ArrayBuffer) { - const decode = new Decode(buffer); - const id = decode.Uint16(); - const proto = protoCache[id]; - if (proto) { - return decodeData(decode, proto); - } else { - throw new Error("decode error: 'buffer' is bad"); - } -} - -/** -* proto缓存 -*/ -const protoCache = {} - -/** -* 注册proto -* id: 必须是个正整数(或正整数字符串), 取值范围[0,65535] -*/ -export const registerProto = function (id: number | string, proto: any) { - if (typeof id === 'string') id = Number(id); - - if (isNumber(id) && Math.floor(id) === id && id >= 0 && id <= 65535 && !Type[id]) { - protoCache[id] = proto; - } else { - throw new Error("registerProto error: 'id' is bad"); - } -} - -export const registerProtoMap = function (protoMap: any) { - if (isMap(protoMap)) { - for (const id in protoMap) { - registerProto(id, protoMap[id]); - } - } else { - throw new Error("registerProtoMap error: 'protoMap' is bad"); - } -} - -export const protoToJson = function () { - return JSON.stringify(protoCache); -} \ No newline at end of file diff --git a/apps/server/src/utils/eMath.ts b/apps/server/src/utils/eMath.ts deleted file mode 100644 index b7d324f..0000000 --- a/apps/server/src/utils/eMath.ts +++ /dev/null @@ -1,402 +0,0 @@ -/** - * @author zp - */ -/** - * 多平台一致精确计算库,比decimal更轻量更快 - * [Math.round、Math.min、Math.max,Math.floor、Math.ceil,这些系统方法一般情况下是可以放心使用的] - */ - const exactMath = Object.create(null); - module.exports = exactMath; - - // 计算精度 - // sin、cos、tan方法的误差小数点后16位 - const ACCURACY_SIN_ERROR = 1e-16; - const ACCURACY_COS_ERROR = 1e-16; - const ACCURACY_TAN_ERROR = 1e-16; - - // 角度弧度常量 - const DEG = 57.29577951308232; - const RAD = 0.017453292519943295; - - // 系统常量 - exactMath.PI = 3.141592653589793; - exactMath.E = 2.718281828459045; - exactMath.LN2 = 0.6931471805599453; - exactMath.LN10 = 2.302585092994046; - exactMath.LOG2E = 1.4426950408889634; - exactMath.LOG10E = 0.4342944819032518; - exactMath.SQRT1_2 = 0.7071067811865476; - exactMath.SQRT2 = 1.4142135623730951; - - /** - * 链式调用 - * @example - * const value = exactMath.value(10).add(20.123).mul(2).sqrt().value; - */ - let chain = null; - exactMath.value = function (value) { - if (!chain) { - chain = { - value: 0, - valueOf() { return this.value; }, - toString() { return String(this.value); } - } - for (const key in exactMath) { - if (key !== 'value' && typeof exactMath[key] === 'function') { - chain[key] = function (...args) { - this.value = exactMath[key].call(exactMath, this.value, ...args); - return this; - } - } - } - } - - chain.value = value; - return chain; - } - - /****************************************************基础****************************************************/ - /** - * 获得小数位数 - * @param {Number} num 浮点数 - * @returns {Number} - */ - exactMath.getDecimalPlace = function (num) { - if (num && num !== Math.floor(num)) { - for (let n = 1, m = 10, temp = 0; n < 20; n += 1, m *= 10) { - temp = num * m; - if (temp == Math.floor(temp)) return n; - } - return 20; - } else { - return 0; - } - } - /** - * 保留n为小数,并四舍五入 - * @example - * (2.335).toFixed(2) - * exactMath.toFixed(2.335, 2) - * @param {Number} num 浮点数 - * @param {Number} n 整数 - * @returns {Number} - */ - exactMath.toFixed = function (num, n = 0) { - if (n == 0) { - return Math.round(num); - } else { - const m = Math.pow(10, n); - return Math.round(num * (m * 10) / 10) / m; - } - } - - exactMath.abs = function (x) { - return Math.abs(x); - } - exactMath.round = function (x) { - return Math.round(x); - } - exactMath.ceil = function (x) { - return Math.ceil(x) - } - exactMath.floor = function (x) { - return Math.floor(x) - } - exactMath.min = function (...args) { - return Math.min(...args); - } - exactMath.max = function (...args) { - return Math.max(...args); - } - - /** - * 小数相加 - * @param {Number} num1 浮点数 - * @param {Number} num2 浮点数 - * @returns {Number} - */ - exactMath.add = function (...args) { - if (args.length === 2) { - const num1 = args[0]; - const num2 = args[1]; - const m = Math.pow(10, Math.max(this.getDecimalPlace(num1), this.getDecimalPlace(num2))); - return (this.toFixed(num1 * m) + this.toFixed(num2 * m)) / m; - } else { - return args.reduce((a, b) => this.add(a, b)) - } - }; - /** - * 小数相减 - * @param {Number} num1 浮点数 - * @param {Number} num2 浮点数 - * @returns {Number} - */ - exactMath.sub = function (...args) { - if (args.length === 2) { - const num1 = args[0]; - const num2 = args[1]; - const m = Math.pow(10, Math.max(this.getDecimalPlace(num1), this.getDecimalPlace(num2))); - - return (this.toFixed(num1 * m) - this.toFixed(num2 * m)) / m; - } else { - return args.reduce((a, b) => this.sub(a, b)) - } - }; - /** - * 小数相乘 - * @param {Number} num1 浮点数 - * @param {Number} num2 浮点数 - * @returns {Number} - */ - exactMath.mul = function (...args) { - if (args.length === 2) { - let num1 = args[0]; - let num2 = args[1]; - - // 方案1: - // 直接相乘,但是相乘两数小数点过多会导致中间值[(n1 * m1) * (n2 * m2)]过大 - // const n1 = this.getDecimalPlace(num1); - // const n2 = this.getDecimalPlace(num2); - // const m1 = Math.pow(10, n1); - // const m2 = Math.pow(10, n2); - // return (n1 * m1) * (n2 * m2) / (m1 * m2); - - // 方案2: - // 用除法实现乘法,不会存在过大中间值 - let n1 = this.getDecimalPlace(num1); - let n2 = this.getDecimalPlace(num2); - - let m = Math.pow(10, n2); - num2 = m / this.toFixed(num2 * m); - - m = Math.pow(10, Math.max(n1, this.getDecimalPlace(num2))); - m = this.toFixed(num1 * m) / this.toFixed(num2 * m); - - let n = Math.min(this.getDecimalPlace(m), n1 + n2); - return this.toFixed(m, n); - } else { - return args.reduce((a, b) => this.mul(a, b)) - } - }; - /** - * 小数相除法 - * @param {Number} num1 浮点数 - * @param {Number} num2 浮点数 - * @returns {Number} - */ - exactMath.div = function (...args) { - if (args.length === 2) { - const num1 = args[0]; - const num2 = args[1]; - - const m = Math.pow(10, Math.max(this.getDecimalPlace(num1), this.getDecimalPlace(num2))); - return this.toFixed(num1 * m) / this.toFixed(num2 * m); - } else { - return args.reduce((a, b) => this.div(a, b)) - } - }; - /** - * 取余 - * @param {Number} num1 浮点数 - * @param {Number} num2 浮点数 - * @returns {Number} - */ - exactMath.rem = function (...args) { - if (args.length === 2) { - const num1 = args[0]; - const num2 = args[1]; - const m = Math.pow(10, Math.max(this.getDecimalPlace(num1), this.getDecimalPlace(num2))); - - return this.toFixed(num1 * m) % this.toFixed(num2 * m) / m; - } else { - return args.reduce((a, b) => this.rem(a, b)) - } - }; - - /** - * n次方,仅支持整数次方(正负都可以) - * @param {Number} num 浮点数 - * @param {Number} n 整数 - */ - exactMath.pow = function (num, n) { - if (num == 0 && n == 0) { - return 1; - } - if (num == 0 && n > 0) { - return 0 - } - if (num == 0 && n < 0) { - return Infinity; - } - // num为负数,n为负小数,返回NaN - if (num < 0 && n < 0 && Math.round(n) != n) { - return NaN; - } - - if (Math.round(n) != n) { - throw new Error('n must be an integer'); - } - - let result = 1; - - if (n > 0) { - for (let index = 0; index < n; index++) { - result = this.mul(result, num); - } - } else if (n < 0) { - for (let index = 0, len = Math.abs(n); index < len; index++) { - result = this.div(result, num); - } - } - - return result; - }; - /** - * 开方运算【牛顿迭代法】 - * - * @param {Number} n - * @returns - */ - exactMath.sqrt = function (n) { - if (n < 0) return NaN; - if (n === 0) return 0; - if (n === 1) return 1; - let last = 0; - let res = 1; - let c = 50; - while (res != last && --c >= 0) { - last = res; - res = this.div(this.add(res, this.div(n, res)), 2) - } - return res; - - // float InvSqrt(float x) - // { - // float xhalf = 0.5f * x; - // int i = * (int *) & x; // get bits for floating VALUE - // i = 0x5f375a86 - (i >> 1); // gives initial guess y0 - // x = * (float *) & i; // convert bits BACK to float - // x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases accuracy - // x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases accuracy - // x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases accuracy - // return 1 / x; - // } - }; - - /****************************************************随机****************************************************/ - function getSeed(seed) { - if (isNaN(seed)) { - seed = Math.floor(Math.random() * 233280); - } else { - seed = Math.floor(seed % 233280); - } - return seed; - } - - let randomSeed = getSeed(); - - /** - * 设置随机种子 - */ - exactMath.setSeed = function (seed) { - randomSeed = getSeed(seed); - }; - - /** - * 随机 - */ - exactMath.random = function () { - randomSeed = (randomSeed * 9301 + 49297) % 233280; - return randomSeed / 233280.0; - }; - - /** - * 根据随机种子随机 - * @param {number} seed - */ - exactMath.randomBySeed = function (seed) { - seed = getSeed(seed); - seed = (seed * 9301 + 49297) % 233280; - return seed / 233280.0; - }; - - /****************************************************角度弧度转换****************************************************/ - /** - * 弧度数转角度数 - * @param {Number} radians 浮点数 - * @returns {Numbe} 浮点数 - */ - exactMath.radiansToDegrees = function (radians) { - return this.div(radians, RAD); - }; - /** - * 角度数转弧度数 - * @param {Number} degrees 浮点数 - * @returns {Numbe} 浮点数 - */ - exactMath.degreesToRadians = function (degrees) { - return this.div(degrees, DEG); - }; - /** - * 将角度值转换到[0, 360)范围内 - * @param {Number} angle 浮点数 - * @returns {Number} 整数 - */ - exactMath.get0To360Angle = function (angle) { - if (angle === 0) { - return 0; - } else if (angle < 0) { - return this.add(this.rem(angle, 360), 360); - } else { - return this.rem(angle, 360); - } - }; - /****************************************************三角函数****************************************************/ - /** - * 查表 - */ - exactMath._sin = {}; - exactMath._cos = {}; - exactMath._tan = {}; - - /** - * 3个三角函数,根据需求自行添加 - * 为了效率,应该尽量使用查表法 - * 表内查不到的,目前使用系统方法的结果并取前4位小数 - */ - exactMath.sin = function (x) { - if (this._sin.hasOwnProperty(x)) { - return this._sin[x]; - } - - // if (x == 0) { - // return 0; - // } else if (x == 90) { - // return 1; - // } - - // let n = x, sum = 0, i = 1; - // do { - // i++; - // sum = this.add(sum, n); - // // n = -n * x * x / (2 * i - 1) / (2 * i - 2); - // n = this.div(this.mul(-1, n, x, x), this.sub(this.mul(2, i), 1), this.sub(this.mul(2, i), 2)); - // } while (Math.abs(n) >= ACCURACY_SIN_ERROR); - // return sum; - - return this.toFixed(Math.sin(x), 4); - }; - exactMath.cos = function (x) { - if (this._cos.hasOwnProperty(x)) { - return this._cos[x]; - } - - return this.toFixed(Math.cos(x), 4); - }; - exactMath.tan = function (x) { - if (this._tan.hasOwnProperty(x)) { - return this._tan[x]; - } - - return this.toFixed(Math.tan(x), 4); - }; \ No newline at end of file diff --git a/apps/server/src/utils/index.ts b/apps/server/src/utils/index.ts index 52c5c46..dc5b589 100644 --- a/apps/server/src/utils/index.ts +++ b/apps/server/src/utils/index.ts @@ -1,46 +1,54 @@ -import fs from 'fs-extra' -import path from 'path' +import fs from "fs-extra"; +import path from "path"; -export const getTime = () => new Date().toLocaleString().split("├")[0] +export const getTime = () => new Date().toLocaleString().split("├")[0]; //symlink同步 export const symlinkCommon = async () => { - const src = path.resolve(__dirname, '../Common') - const dst = path.resolve(__dirname, '../../../client/assets/Scripts/Common') + const src = path.resolve(__dirname, "../Common"); + const dst = path.resolve(__dirname, "../../../client/assets/Scripts/Common"); - if (await fs.lstat(dst).then(v => v.isSymbolicLink()).catch(() => false) && await fs.readlink(dst) === src) { - console.log('同步成功!') + if ( + (await fs + .lstat(dst) + .then((v) => v.isSymbolicLink()) + .catch(() => false)) && + (await fs.readlink(dst)) === src + ) { + console.log("同步成功!"); } else { - fs.symlink(src, dst).then(() => { - console.log('同步成功!') - }).catch((e) => { - console.log('同步失败!', e) - }) + fs.symlink(src, dst) + .then(() => { + console.log("同步成功!"); + }) + .catch((e) => { + console.log("同步失败!", e); + }); } -} +}; //copy同步 export const copyCommon = async () => { - const src = path.resolve(__dirname, '../Common') - const dst = path.resolve(__dirname, '../../../client/assets/Scripts/Common') + const src = path.resolve(__dirname, "../Common"); + const dst = path.resolve(__dirname, "../../../client/assets/Scripts/Common"); console.log(src, dst); // clean - await fs.remove(dst) + await fs.remove(dst); //create - await fs.ensureDir(dst) + await fs.ensureDir(dst); // copy - await fs.copy(src, dst) - console.log('同步成功!') -} + await fs.copy(src, dst); + console.log("同步成功!"); +}; -export const toArrayBuffer = (buffer: Buffer) => { +export const buffer2ArrayBuffer = (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; -} \ No newline at end of file +}; diff --git a/package.json b/package.json index b4c8538..0a02136 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,6 @@ "apps/*" ], "scripts": { - "lint": "eslint .", - "lint:fix": "eslint . --fix", "dev": "yarn workspace @game/server run dev" } -} \ No newline at end of file +}