我正在尝试使用nestjs和websockets创建一个浏览器多人游戏。该游戏的核心是 4 人纸牌游戏,我需要管理状态服务器端,以便 a) 玩家无法在请求中看到彼此的纸牌,b) 如果数据库被破坏,纸牌也不会存储在那里。
目前,我的用户单击一个播放按钮,他们会点击一个静态端点,该端点要么创建一个新的“游戏”,要么将它们添加到一个打开的游戏中。响应发送回一个游戏对象,我用它来连接 websocket
/game/:id
这是我的网络套接字:
@WebSocketGateway({
namespace: /\/game\/[a-zA-Z0-9]+/, // Dynamically scoped by game ID
cors: {
origin: '*',
},
})
export class GameGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
@WebSocketServer() server: Server
constructor(
protected _jwtService: JwtService,
protected _gameService: GameService,
) {}
afterInit() {
console.log('Gateway initialized')
}
// Handle player connection
async handleConnection(@ConnectedSocket() client: Socket) {
client.nsp.emit('player_connected', {})
}
// Handle player disconnection
async handleDisconnect(@ConnectedSocket() client: Socket) {
console.log('Disconnection')
}
// Emit an event to the specific game namespace when the registration is closed
@OnEvent('game.registration-closed')
handleGameReady(game: Game) {
/**
* TODO: Begin Setup of game
*/
this.server.of(`game/${game._id}`).emit('pregame', game)
}
}
我不想创建一个
/game
websocket 并分成房间,因为我需要为每个游戏都有范围状态,而且我认为它不能很好地管理 1 个对象内的所有房间状态。因此我选择了动态命名的网关/\/game\/[a-zA-Z0-9]+/,
我的问题在我的服务中,当游戏已满时,我将状态更改为关闭并发出注册已关闭的事件,我们可以进入下一步
if (game.players.length === 4) {
console.log('Hello')
this._emitter.emit('game.registration-closed', game)
}
我可以在我的网关中看到此活动
@OnEvent('game.registration-closed')
handleGameReady(game: Game) {
/**
* TODO: Begin Setup of game
*/
this.server.of(`game/${game._id}`).emit('pregame', game)
}
但是,当我尝试向已连接的玩家发送此消息时,我得到
this.server.of is not a function
我没有使用房间,所以我不能使用 this.server.to 或者我得到
this.children.forEach
无法读取未定义的属性
第一个原因,
您的代码的问题可能是您尝试使用动态创建的 this.server.of(...) 向 WebSocket 命名空间 (/game/:id) 发送消息。不管怎样,你遇到了一个错误:“this.server.of不是一个函数”,这个错误主要是因为方法.of()可能无法被识别或者在WebSocket版本中不可用您正在使用的服务器。
另一个可能的原因,
因为当您在事件侦听器内部发出时,this.server的上下文可能无法正确设置,或者也可能不包含动态创建的命名空间。因此,这可能会导致错误this.server.of作为不是一个函数。 为了解决问题,最好使用单个 Web Socket 网关中的房间来管理状态,并避免创建单独的命名空间。
我在下面提到了您想要的每个步骤的正确更正代码,请正确地从这里获取参考,并根据您的进一步要求进行新的更改。
首先创建一个WebSocket:
import {
WebSocketGateway,
WebSocketServer,
OnGatewayInit,
OnGatewayConnection,
OnGatewayDisconnect,
ConnectedSocket,
SubscribeMessage,
MessageBody,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { GameService } from './game.service';
import { OnEvent } from '@nestjs/event-emitter';
@WebSocketGateway({
namespace: '/game',
cors: {
origin: '*',
},
})
export class GameGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
@WebSocketServer() server: Server;
constructor(private _gameService: GameService) {}
afterInit() {
console.log('Gateway initialized');
}
async handleConnection(@ConnectedSocket() client: Socket) {
console.log('Player connected:', client.id);
}
async handleDisconnect(@ConnectedSocket() client: Socket) {
console.log('Player disconnected:', client.id);
}
@SubscribeMessage('join_game')
async handleJoinGame(@ConnectedSocket() client: Socket, @MessageBody() gameId: string) {
console.log(`Player ${client.id} joining game ${gameId}`);
client.join(gameId);
this._gameService.addPlayerToGame(gameId, client.id);
}
@OnEvent('game.registration-closed')
handleGameReady(game: any) {
console.log(`Game ${game._id} registration closed. Emitting pregame.`);
this.server.to(game._id).emit('pregame', game);
}
}
现在实施您的游戏服务,例如:
import { Injectable } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';
@Injectable()
export class GameService {
private games: Map<string, any> = new Map();
constructor(private _emitter: EventEmitter2) {}
createGame(gameId: string) {
if (!this.games.has(gameId)) {
this.games.set(gameId, {
id: gameId,
players: [],
status: 'open',
});
console.log(`Game ${gameId} created.`);
}
}
addPlayerToGame(gameId: string, playerId: string) {
const game = this.games.get(gameId);
if (game && game.status === 'open') {
game.players.push(playerId);
console.log(`Player ${playerId} added to game ${gameId}.`);
if (game.players.length === 4) {
game.status = 'closed';
this._emitter.emit('game.registration-closed', game);
console.log(`Game ${gameId} registration closed.`);
}
}
}
getGame(gameId: string) {
return this.games.get(gameId);
}
removePlayerFromGame(gameId: string, playerId: string) {
const game = this.games.get(gameId);
if (game) {
game.players = game.players.filter(id => id !== playerId);
console.log(`Player ${playerId} removed from game ${gameId}.`);
}
}
}
我还会建议您添加一个游戏控制器来管理游戏创建和新玩家的添加。如果您愿意,代码将类似于:
import { Controller, Post, Body } from '@nestjs/common';
import { GameService } from './game.service';
@Controller('game')
export class GameController {
constructor(private readonly gameService: GameService) {}
@Post('create')
createGame(@Body('gameId') gameId: string) {
this.gameService.createGame(gameId);
return { message: `Game ${gameId} created.` };
}
@Post('add-player')
addPlayerToGame(@Body('gameId') gameId: string, @Body('playerId') playerId: string) {
this.gameService.addPlayerToGame(gameId, playerId);
return { message: `Player ${playerId} added to game ${gameId}.` };
}
}
正确使用这些代码,它集成了您的所有功能并解决疑问,如果您遇到任何困难,请告诉我。