feat: FunConnect v1.0.0 - Minecraft联机平台完整版
- server: Node.js TCP中继服务器,支持多节点集群 - web: React管理面板(仪表盘、房间管理、节点管理) - client: Electron桌面客户端(连接、创建/加入房间、本地代理) - deploy: Ubuntu一键部署脚本
This commit is contained in:
187
server/src/room.ts
Normal file
187
server/src/room.ts
Normal file
@@ -0,0 +1,187 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import net from 'net';
|
||||
import logger from './logger';
|
||||
|
||||
export interface Player {
|
||||
id: string;
|
||||
name: string;
|
||||
socket: net.Socket;
|
||||
joinedAt: Date;
|
||||
}
|
||||
|
||||
export interface RoomInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
hostId: string;
|
||||
hostName: string;
|
||||
hostPort: number;
|
||||
gameVersion: string;
|
||||
gameEdition: 'java' | 'bedrock';
|
||||
maxPlayers: number;
|
||||
currentPlayers: number;
|
||||
nodeId: string;
|
||||
createdAt: Date;
|
||||
password?: string;
|
||||
}
|
||||
|
||||
export class Room {
|
||||
public id: string;
|
||||
public name: string;
|
||||
public hostId: string;
|
||||
public hostName: string;
|
||||
public hostSocket: net.Socket | null = null;
|
||||
public hostPort: number;
|
||||
public gameVersion: string;
|
||||
public gameEdition: 'java' | 'bedrock';
|
||||
public maxPlayers: number;
|
||||
public password?: string;
|
||||
public nodeId: string;
|
||||
public createdAt: Date;
|
||||
public players: Map<string, Player> = new Map();
|
||||
private playerSockets: Map<string, net.Socket> = new Map();
|
||||
|
||||
constructor(options: {
|
||||
name: string;
|
||||
hostName: string;
|
||||
hostPort: number;
|
||||
gameVersion: string;
|
||||
gameEdition: 'java' | 'bedrock';
|
||||
maxPlayers: number;
|
||||
nodeId: string;
|
||||
password?: string;
|
||||
}) {
|
||||
this.id = uuidv4().substring(0, 8).toUpperCase();
|
||||
this.name = options.name;
|
||||
this.hostId = uuidv4();
|
||||
this.hostName = options.hostName;
|
||||
this.hostPort = options.hostPort;
|
||||
this.gameVersion = options.gameVersion;
|
||||
this.gameEdition = options.gameEdition;
|
||||
this.maxPlayers = options.maxPlayers;
|
||||
this.nodeId = options.nodeId;
|
||||
this.password = options.password;
|
||||
this.createdAt = new Date();
|
||||
}
|
||||
|
||||
get currentPlayers(): number {
|
||||
return this.players.size;
|
||||
}
|
||||
|
||||
get isFull(): boolean {
|
||||
return this.currentPlayers >= this.maxPlayers;
|
||||
}
|
||||
|
||||
addPlayer(name: string, socket: net.Socket): Player {
|
||||
const player: Player = {
|
||||
id: uuidv4(),
|
||||
name,
|
||||
socket,
|
||||
joinedAt: new Date(),
|
||||
};
|
||||
this.players.set(player.id, player);
|
||||
this.playerSockets.set(player.id, socket);
|
||||
logger.info(`Player ${name} joined room ${this.name} (${this.id})`);
|
||||
return player;
|
||||
}
|
||||
|
||||
removePlayer(playerId: string): void {
|
||||
const player = this.players.get(playerId);
|
||||
if (player) {
|
||||
logger.info(`Player ${player.name} left room ${this.name} (${this.id})`);
|
||||
this.players.delete(playerId);
|
||||
this.playerSockets.delete(playerId);
|
||||
}
|
||||
}
|
||||
|
||||
toInfo(): RoomInfo {
|
||||
return {
|
||||
id: this.id,
|
||||
name: this.name,
|
||||
hostId: this.hostId,
|
||||
hostName: this.hostName,
|
||||
hostPort: this.hostPort,
|
||||
gameVersion: this.gameVersion,
|
||||
gameEdition: this.gameEdition,
|
||||
maxPlayers: this.maxPlayers,
|
||||
currentPlayers: this.currentPlayers,
|
||||
nodeId: this.nodeId,
|
||||
createdAt: this.createdAt,
|
||||
password: this.password ? '***' : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
for (const [, player] of this.players) {
|
||||
try {
|
||||
player.socket.destroy();
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
if (this.hostSocket) {
|
||||
try {
|
||||
this.hostSocket.destroy();
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
this.players.clear();
|
||||
this.playerSockets.clear();
|
||||
logger.info(`Room ${this.name} (${this.id}) destroyed`);
|
||||
}
|
||||
}
|
||||
|
||||
export class RoomManager {
|
||||
private rooms: Map<string, Room> = new Map();
|
||||
private maxRooms: number;
|
||||
|
||||
constructor(maxRooms: number) {
|
||||
this.maxRooms = maxRooms;
|
||||
}
|
||||
|
||||
createRoom(options: {
|
||||
name: string;
|
||||
hostName: string;
|
||||
hostPort: number;
|
||||
gameVersion: string;
|
||||
gameEdition: 'java' | 'bedrock';
|
||||
maxPlayers: number;
|
||||
nodeId: string;
|
||||
password?: string;
|
||||
}): Room | null {
|
||||
if (this.rooms.size >= this.maxRooms) {
|
||||
logger.warn('Max rooms reached, cannot create new room');
|
||||
return null;
|
||||
}
|
||||
const room = new Room(options);
|
||||
this.rooms.set(room.id, room);
|
||||
logger.info(`Room created: ${room.name} (${room.id}) on node ${room.nodeId}`);
|
||||
return room;
|
||||
}
|
||||
|
||||
getRoom(roomId: string): Room | undefined {
|
||||
return this.rooms.get(roomId);
|
||||
}
|
||||
|
||||
deleteRoom(roomId: string): boolean {
|
||||
const room = this.rooms.get(roomId);
|
||||
if (room) {
|
||||
room.destroy();
|
||||
this.rooms.delete(roomId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
listRooms(): RoomInfo[] {
|
||||
return Array.from(this.rooms.values()).map(r => r.toInfo());
|
||||
}
|
||||
|
||||
getRoomCount(): number {
|
||||
return this.rooms.size;
|
||||
}
|
||||
|
||||
getTotalPlayers(): number {
|
||||
let total = 0;
|
||||
for (const [, room] of this.rooms) {
|
||||
total += room.currentPlayers;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user