Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
7bf55f9928 | ||
|
c7a859b9bb | ||
|
dc8ce750ff | ||
|
5628b0403e | ||
|
3b17c31857 | ||
|
1ade200675 |
@ -1,19 +1,20 @@
|
|||||||
import { _decorator, Component, Node, Label } from "cc";
|
import { _decorator, Component, Node, Label } from "cc"
|
||||||
import DataManager from "../Global/DataManager";
|
import { IPlayer } from "../Common"
|
||||||
const { ccclass, property } = _decorator;
|
import DataManager from "../Global/DataManager"
|
||||||
|
const { ccclass, property } = _decorator
|
||||||
|
|
||||||
@ccclass("PlayerManager")
|
@ccclass("PlayerManager")
|
||||||
export class PlayerManager extends Component {
|
export class PlayerManager extends Component {
|
||||||
init({ id, nickname, rid }: { id: number; nickname: string; rid: number }) {
|
init({ id, nickname, rid }: IPlayer) {
|
||||||
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 !== -1) {
|
if (rid) {
|
||||||
str += `(房间${rid})`;
|
str += `(房间${rid})`
|
||||||
}
|
}
|
||||||
label.string = str;
|
label.string = str
|
||||||
this.node.active = true;
|
this.node.active = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
import { _decorator, Component, Node, Label } from "cc";
|
import { _decorator, Component, Node, Label } from "cc"
|
||||||
import { EventEnum } from "../Enum";
|
import { IRoom } from "../Common"
|
||||||
import EventManager from "../Global/EventManager";
|
import { EventEnum } from "../Enum"
|
||||||
const { ccclass, property } = _decorator;
|
import EventManager from "../Global/EventManager"
|
||||||
|
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 }: { id: number; players: Array<{ id: number; nickname: string }> }) {
|
init({ id, players }: IRoom) {
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { EventEmitter } from "stream";
|
import { EventEmitter } from "stream"
|
||||||
import WebSocket, { WebSocketServer } from "ws";
|
import WebSocket, { WebSocketServer } from "ws"
|
||||||
import { ApiMsgEnum } from "../Common";
|
import { ApiMsgEnum, IModel } 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(apiName: ApiMsgEnum, cb: Function) {
|
setApi<T extends keyof IModel["api"]>(name: T, cb: (connection: Connection, args: IModel["api"][T]["req"]) => IModel["api"][T]["res"]) {
|
||||||
this.apiMap.set(apiName, cb);
|
this.apiMap.set(name, cb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,55 +1,54 @@
|
|||||||
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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 = -1;
|
player.rid = undefined
|
||||||
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 = -1;
|
player.rid = undefined
|
||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ 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()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -50,6 +51,7 @@ 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),
|
||||||
|
Loading…
Reference in New Issue
Block a user