Compare commits

..

No commits in common. "dev" and "master" have entirely different histories.
dev ... master

7 changed files with 128 additions and 129 deletions

View File

@ -1,20 +1,19 @@
import { _decorator, Component, Node, Label } from "cc" import { _decorator, Component, Node, Label } from "cc";
import { IPlayer } from "../Common" import DataManager from "../Global/DataManager";
import DataManager from "../Global/DataManager" const { ccclass, property } = _decorator;
const { ccclass, property } = _decorator
@ccclass("PlayerManager") @ccclass("PlayerManager")
export class PlayerManager extends Component { export class PlayerManager extends Component {
init({ id, nickname, rid }: IPlayer) { init({ id, nickname, rid }: { id: number; nickname: string; rid: number }) {
const label = this.getComponent(Label) const label = this.getComponent(Label);
let str = nickname let str = nickname;
if (DataManager.Instance.myPlayerId === id) { if (DataManager.Instance.myPlayerId === id) {
str += `(我)` str += `(我)`;
} }
if (rid) { if (rid !== -1) {
str += `(房间${rid})` str += `(房间${rid})`;
} }
label.string = str label.string = str;
this.node.active = true this.node.active = true;
} }
} }

View File

@ -1,20 +1,19 @@
import { _decorator, Component, Node, Label } from "cc" import { _decorator, Component, Node, Label } from "cc";
import { IRoom } from "../Common" import { EventEnum } from "../Enum";
import { EventEnum } from "../Enum" import EventManager from "../Global/EventManager";
import EventManager from "../Global/EventManager" const { ccclass, property } = _decorator;
const { ccclass, property } = _decorator
@ccclass("RoomManager") @ccclass("RoomManager")
export class RoomManager extends Component { export class RoomManager extends Component {
id: number id: number;
init({ id, players }: IRoom) { init({ id, players }: { id: number; players: Array<{ id: number; nickname: string }> }) {
this.id = id this.id = id;
const label = this.getComponent(Label) const label = this.getComponent(Label);
label.string = `房间id:${id},当前人数:${players.length}` label.string = `房间id:${id},当前人数:${players.length}`;
this.node.active = true this.node.active = true;
} }
handleClick() { handleClick() {
EventManager.Instance.emit(EventEnum.RoomJoin, this.id) EventManager.Instance.emit(EventEnum.RoomJoin, this.id);
} }
} }

View File

@ -1,10 +1,10 @@
import { EventEmitter } from "stream" import { EventEmitter } from "stream";
import WebSocket, { WebSocketServer } from "ws" import WebSocket, { WebSocketServer } from "ws";
import { ApiMsgEnum, IModel } from "../Common" import { ApiMsgEnum } from "../Common";
import { Connection, ConnectionEventEnum } from "./Connection" import { Connection, ConnectionEventEnum } from "./Connection";
export interface IMyServerOptions { export interface IMyServerOptions {
port: number port: number;
} }
export enum MyServerEventEnum { export enum MyServerEventEnum {
@ -13,51 +13,51 @@ export enum MyServerEventEnum {
} }
export class MyServer extends EventEmitter { export class MyServer extends EventEmitter {
wss?: WebSocketServer wss?: WebSocketServer;
port: number port: number;
connections: Set<Connection> = new Set() connections: Set<Connection> = new Set();
apiMap: Map<ApiMsgEnum, Function> = new Map() apiMap: Map<ApiMsgEnum, Function> = new Map();
constructor({ port = 8080 }: Partial<IMyServerOptions>) { constructor({ port = 8080 }: Partial<IMyServerOptions>) {
super() super();
this.port = port this.port = port;
} }
start() { start() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.wss = new WebSocketServer({ port: this.port }) 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) => { this.wss.on("error", (e) => {
reject(e) reject(e);
}) });
this.wss.on("close", () => { this.wss.on("close", () => {
console.log("MyServer 服务关闭") console.log("MyServer 服务关闭");
}) });
this.wss.on("listening", () => { this.wss.on("listening", () => {
resolve(true) resolve(true);
}) });
}) });
} }
handleConnect(ws: WebSocket) { handleConnect(ws: WebSocket) {
//初始化 //初始化
const connection = new Connection(this, ws) const connection = new Connection(this, ws);
//向外告知有人来了 //向外告知有人来了
this.connections.add(connection) this.connections.add(connection);
this.emit(MyServerEventEnum.Connect, connection) this.emit(MyServerEventEnum.Connect, connection);
//向外告知有人走了 //向外告知有人走了
connection.on(ConnectionEventEnum.Close, (code: number, reason: string) => { connection.on(ConnectionEventEnum.Close, (code: number, reason: string) => {
this.connections.delete(connection) this.connections.delete(connection);
this.emit(MyServerEventEnum.DisConnect, connection, code, reason) this.emit(MyServerEventEnum.DisConnect, connection, code, reason);
}) });
} }
setApi<T extends keyof IModel["api"]>(name: T, cb: (connection: Connection, args: IModel["api"][T]["req"]) => IModel["api"][T]["res"]) { setApi(apiName: ApiMsgEnum, cb: Function) {
this.apiMap.set(name, cb) this.apiMap.set(apiName, cb);
} }
} }

View File

@ -1,14 +1,16 @@
import { Connection } from "../Core" import { Connection } from "../Core";
export default class Player { export default class Player {
id: number id: number;
nickname: string nickname: string;
connection: Connection connection: Connection;
rid: number rid: number;
constructor({ id, nickname, connection }: Pick<Player, "id" | "nickname" | "connection">) { constructor({ id, nickname, connection }: Pick<Player, "id" | "nickname" | "connection">) {
this.id = id this.id = id;
this.nickname = nickname this.nickname = nickname;
this.connection = connection this.connection = connection;
this.connection.playerId = this.id;
this.rid = -1;
} }
} }

View File

@ -1,54 +1,55 @@
import Singleton from "../Base/Singleton" import Singleton from "../Base/Singleton";
import { ApiMsgEnum, IApiPlayerJoinReq } from "../Common" import { ApiMsgEnum, IApiPlayerJoinReq } from "../Common";
import { Connection } from "../Core" import { Connection } from "../Core";
import Player from "./Player" import Player from "./Player";
import RoomManager from "./RoomManager" import RoomManager from "./RoomManager";
export default class PlayerManager extends Singleton { export default class PlayerManager extends Singleton {
static get Instance() { static get Instance() {
return super.GetInstance<PlayerManager>() return super.GetInstance<PlayerManager>();
} }
players: Set<Player> = new Set() players: Set<Player> = new Set();
private nextPlayerId = 1 private nextPlayerId = 1;
private idMapPlayer: Map<number, Player> = new Map() private idMapPlayer: Map<number, Player> = new Map();
createPlayer({ connection, nickname }: IApiPlayerJoinReq & { connection: Connection }) { createPlayer({ connection, nickname }: IApiPlayerJoinReq & { connection: Connection }) {
const player = new Player({ id: this.nextPlayerId++, connection, nickname }) const player = new Player({ id: this.nextPlayerId++, connection, nickname });
this.players.add(player) this.players.add(player);
this.idMapPlayer.set(player.id, player) this.idMapPlayer.set(player.id, player);
return player return player;
} }
removePlayer(uid: number) { removePlayer(uid: number) {
const player = this.idMapPlayer.get(uid) const player = this.idMapPlayer.get(uid);
if (player) { if (player) {
const rid = player.rid const rid = player.rid;
if (rid !== undefined) { if (rid !== undefined) {
RoomManager.Instance.leaveRoom(rid, uid) RoomManager.Instance.leaveRoom(rid, uid);
RoomManager.Instance.syncRooms() RoomManager.Instance.syncRooms();
RoomManager.Instance.syncRoom(rid) RoomManager.Instance.syncRoom(rid);
} }
this.players.delete(player) this.players.delete(player);
this.idMapPlayer.delete(uid) this.idMapPlayer.delete(uid);
this.syncPlayers();
} }
} }
getPlayerById(uid: number) { getPlayerById(uid: number) {
return this.idMapPlayer.get(uid) return this.idMapPlayer.get(uid);
} }
syncPlayers() { syncPlayers() {
for (const player of this.players) { 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<Player> = this.players) { getPlayersView(players: Set<Player> = this.players) {
return [...players].map((player) => this.getPlayerView(player)) return [...players].map((player) => this.getPlayerView(player));
} }
getPlayerView({ id, nickname, rid }: Player) { getPlayerView({ id, nickname, rid }: Player) {
return { id, nickname, rid } return { id, nickname, rid };
} }
} }

View File

@ -1,57 +1,57 @@
import { ApiMsgEnum, EntityTypeEnum, IClientInput, IMsgClientSync, InputTypeEnum, IState, toFixed } from "../Common" import { ApiMsgEnum, EntityTypeEnum, IClientInput, IMsgClientSync, InputTypeEnum, IState, toFixed } from "../Common";
import { Connection } from "../Core" import { Connection } from "../Core";
import type Player from "./Player" import type Player from "./Player";
import PlayerManager from "./PlayerManager" import PlayerManager from "./PlayerManager";
import RoomManager from "./RoomManager" import RoomManager from "./RoomManager";
export default class Room { export default class Room {
id: number id: number;
players: Set<Player> = new Set() players: Set<Player> = new Set();
private lastTime?: number private lastTime?: number;
private timers: NodeJS.Timer[] = [] private timers: NodeJS.Timer[] = [];
private pendingInput: Array<IClientInput> = [] private pendingInput: Array<IClientInput> = [];
private lastPlayerFrameIdMap: Map<number, number> = new Map() private lastPlayerFrameIdMap: Map<number, number> = new Map();
constructor(rid: number) { constructor(rid: number) {
this.id = rid this.id = rid;
} }
join(uid: number) { join(uid: number) {
const player = PlayerManager.Instance.getPlayerById(uid) const player = PlayerManager.Instance.getPlayerById(uid);
if (player) { if (player) {
player.rid = this.id player.rid = this.id;
this.players.add(player) this.players.add(player);
} }
} }
leave(uid: number) { leave(uid: number) {
const player = PlayerManager.Instance.getPlayerById(uid) const player = PlayerManager.Instance.getPlayerById(uid);
if (player) { if (player) {
player.rid = undefined player.rid = -1;
player.connection.unlistenMsg(ApiMsgEnum.MsgClientSync, this.getClientMsg, this) player.connection.unlistenMsg(ApiMsgEnum.MsgClientSync, this.getClientMsg, this);
this.players.delete(player) this.players.delete(player);
if (!this.players.size) { if (!this.players.size) {
RoomManager.Instance.closeRoom(this.id) RoomManager.Instance.closeRoom(this.id);
} }
} }
} }
close() { close() {
this.timers.forEach((t) => clearInterval(t)) this.timers.forEach((t) => clearInterval(t));
for (const player of this.players) { for (const player of this.players) {
player.rid = undefined player.rid = -1;
player.connection.sendMsg(ApiMsgEnum.MsgGameEnd, {}) player.connection.sendMsg(ApiMsgEnum.MsgGameEnd, {});
player.connection.unlistenMsg(ApiMsgEnum.MsgClientSync, this.getClientMsg, this) player.connection.unlistenMsg(ApiMsgEnum.MsgClientSync, this.getClientMsg, this);
} }
this.players.clear() this.players.clear();
} }
sync() { sync() {
for (const player of this.players) { for (const player of this.players) {
player.connection.sendMsg(ApiMsgEnum.MsgRoom, { player.connection.sendMsg(ApiMsgEnum.MsgRoom, {
room: RoomManager.Instance.getRoomView(this), room: RoomManager.Instance.getRoomView(this),
}) });
} }
} }
@ -75,47 +75,47 @@ export default class Room {
})), })),
bullets: [], bullets: [],
nextBulletId: 1, nextBulletId: 1,
} };
for (const player of this.players) { for (const player of this.players) {
player.connection.sendMsg(ApiMsgEnum.MsgGameStart, { player.connection.sendMsg(ApiMsgEnum.MsgGameStart, {
state, state,
}) });
player.connection.listenMsg(ApiMsgEnum.MsgClientSync, this.getClientMsg, this) player.connection.listenMsg(ApiMsgEnum.MsgClientSync, this.getClientMsg, this);
} }
let t1 = setInterval(() => { let t1 = setInterval(() => {
this.sendServerMsg() this.sendServerMsg();
}, 100) }, 100);
let t2 = setInterval(() => { let t2 = setInterval(() => {
this.timePast() this.timePast();
}, 16) }, 16);
this.timers = [t1, t2] this.timers = [t1, t2];
} }
getClientMsg(connection: Connection, { frameId, input }: IMsgClientSync) { getClientMsg(connection: Connection, { frameId, input }: IMsgClientSync) {
this.lastPlayerFrameIdMap.set(connection.playerId, frameId) this.lastPlayerFrameIdMap.set(connection.playerId, frameId);
this.pendingInput.push(input) this.pendingInput.push(input);
} }
sendServerMsg() { sendServerMsg() {
const pendingInput = this.pendingInput const pendingInput = this.pendingInput;
this.pendingInput = [] this.pendingInput = [];
for (const player of this.players) { for (const player of this.players) {
player.connection.sendMsg(ApiMsgEnum.MsgServerSync, { player.connection.sendMsg(ApiMsgEnum.MsgServerSync, {
lastFrameId: this.lastPlayerFrameIdMap.get(player.id) ?? 0, lastFrameId: this.lastPlayerFrameIdMap.get(player.id) ?? 0,
inputs: pendingInput, inputs: pendingInput,
}) });
} }
} }
timePast() { timePast() {
let now = process.uptime() let now = process.uptime();
const dt = now - (this.lastTime ?? now) const dt = now - (this.lastTime ?? now);
this.pendingInput.push({ this.pendingInput.push({
type: InputTypeEnum.TimePast, type: InputTypeEnum.TimePast,
dt: toFixed(dt), dt: toFixed(dt),
}) });
this.lastTime = now this.lastTime = now;
} }
} }

View File

@ -40,7 +40,6 @@ server.on(MyServerEventEnum.DisConnect, (connection: Connection) => {
console.log(`${getTime()}走人|人数|${server.connections.size}`) console.log(`${getTime()}走人|人数|${server.connections.size}`)
if (connection.playerId) { if (connection.playerId) {
PlayerManager.Instance.removePlayer(connection.playerId) PlayerManager.Instance.removePlayer(connection.playerId)
PlayerManager.Instance.syncPlayers()
} }
}) })
@ -51,7 +50,6 @@ server.setApi(ApiMsgEnum.ApiPlayerList, (connection: Connection, data: IApiPlaye
server.setApi(ApiMsgEnum.ApiPlayerJoin, (connection: Connection, { nickname }: IApiPlayerJoinReq): IApiPlayerJoinRes => { server.setApi(ApiMsgEnum.ApiPlayerJoin, (connection: Connection, { nickname }: IApiPlayerJoinReq): IApiPlayerJoinRes => {
const player = PlayerManager.Instance.createPlayer({ connection, nickname }) const player = PlayerManager.Instance.createPlayer({ connection, nickname })
connection.playerId = player.id
PlayerManager.Instance.syncPlayers() PlayerManager.Instance.syncPlayers()
return { return {
player: PlayerManager.Instance.getPlayerView(player), player: PlayerManager.Instance.getPlayerView(player),