feat: FunConnect v1.0.0 - Minecraft联机平台完整版
- server: Node.js TCP中继服务器,支持多节点集群 - web: React管理面板(仪表盘、房间管理、节点管理) - client: Electron桌面客户端(连接、创建/加入房间、本地代理) - deploy: Ubuntu一键部署脚本
This commit is contained in:
173
client/src/main/index.ts
Normal file
173
client/src/main/index.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
import { app, BrowserWindow, ipcMain, dialog } from 'electron';
|
||||
import * as path from 'path';
|
||||
import { RelayClient } from './relay-client';
|
||||
import { LocalProxy } from './local-proxy';
|
||||
import { ApiClient } from './api-client';
|
||||
|
||||
let mainWindow: BrowserWindow | null = null;
|
||||
let relayClient: RelayClient | null = null;
|
||||
let localProxy: LocalProxy | null = null;
|
||||
let apiClient: ApiClient | null = null;
|
||||
|
||||
function createWindow() {
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 900,
|
||||
height: 650,
|
||||
minWidth: 800,
|
||||
minHeight: 600,
|
||||
title: 'FunConnect - Minecraft 联机客户端',
|
||||
frame: false,
|
||||
titleBarStyle: 'hidden',
|
||||
backgroundColor: '#1a1a2e',
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js'),
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false,
|
||||
},
|
||||
});
|
||||
|
||||
mainWindow.loadFile(path.join(__dirname, '../../renderer/index.html'));
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null;
|
||||
cleanup();
|
||||
});
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
if (localProxy) {
|
||||
localProxy.stop();
|
||||
localProxy = null;
|
||||
}
|
||||
if (relayClient) {
|
||||
relayClient.disconnect();
|
||||
relayClient = null;
|
||||
}
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow();
|
||||
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow();
|
||||
});
|
||||
});
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
cleanup();
|
||||
if (process.platform !== 'darwin') app.quit();
|
||||
});
|
||||
|
||||
// ===== IPC Handlers =====
|
||||
|
||||
// Window controls
|
||||
ipcMain.handle('window:minimize', () => mainWindow?.minimize());
|
||||
ipcMain.handle('window:maximize', () => {
|
||||
if (mainWindow?.isMaximized()) mainWindow.unmaximize();
|
||||
else mainWindow?.maximize();
|
||||
});
|
||||
ipcMain.handle('window:close', () => mainWindow?.close());
|
||||
|
||||
// Server connection
|
||||
ipcMain.handle('server:connect', async (_event, serverUrl: string) => {
|
||||
try {
|
||||
apiClient = new ApiClient(serverUrl);
|
||||
const health = await apiClient.getHealth();
|
||||
return { success: true, data: health };
|
||||
} catch (err: any) {
|
||||
return { success: false, error: err.message };
|
||||
}
|
||||
});
|
||||
|
||||
// Room operations
|
||||
ipcMain.handle('rooms:list', async () => {
|
||||
if (!apiClient) return { success: false, error: '未连接服务器' };
|
||||
try {
|
||||
const data = await apiClient.getRooms();
|
||||
return { success: true, data };
|
||||
} catch (err: any) {
|
||||
return { success: false, error: err.message };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('rooms:create', async (_event, roomData: any) => {
|
||||
if (!apiClient) return { success: false, error: '未连接服务器' };
|
||||
try {
|
||||
const data = await apiClient.createRoom(roomData);
|
||||
return { success: true, data };
|
||||
} catch (err: any) {
|
||||
return { success: false, error: err.response?.data?.error || err.message };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('rooms:delete', async (_event, roomId: string) => {
|
||||
if (!apiClient) return { success: false, error: '未连接服务器' };
|
||||
try {
|
||||
await apiClient.deleteRoom(roomId);
|
||||
return { success: true };
|
||||
} catch (err: any) {
|
||||
return { success: false, error: err.message };
|
||||
}
|
||||
});
|
||||
|
||||
// Join room - start local proxy and relay connection
|
||||
ipcMain.handle('rooms:join', async (_event, opts: { serverHost: string; serverPort: number; roomId: string; localPort: number }) => {
|
||||
try {
|
||||
cleanup();
|
||||
|
||||
relayClient = new RelayClient(opts.serverHost, opts.serverPort, opts.roomId);
|
||||
localProxy = new LocalProxy(opts.localPort, relayClient);
|
||||
|
||||
await localProxy.start();
|
||||
|
||||
relayClient.on('connected', () => {
|
||||
mainWindow?.webContents.send('relay:status', { status: 'connected' });
|
||||
});
|
||||
relayClient.on('disconnected', () => {
|
||||
mainWindow?.webContents.send('relay:status', { status: 'disconnected' });
|
||||
});
|
||||
relayClient.on('error', (err: string) => {
|
||||
mainWindow?.webContents.send('relay:status', { status: 'error', error: err });
|
||||
});
|
||||
|
||||
return { success: true, localPort: opts.localPort };
|
||||
} catch (err: any) {
|
||||
return { success: false, error: err.message };
|
||||
}
|
||||
});
|
||||
|
||||
// Host room - start relay and local proxy from local MC server
|
||||
ipcMain.handle('rooms:host', async (_event, opts: { serverUrl: string; roomName: string; localMcPort: number; gameVersion: string; gameEdition: string; maxPlayers: number; password?: string }) => {
|
||||
if (!apiClient) return { success: false, error: '未连接服务器' };
|
||||
try {
|
||||
const result = await apiClient.createRoom({
|
||||
name: opts.roomName,
|
||||
hostName: 'FunConnect',
|
||||
hostPort: opts.localMcPort,
|
||||
gameVersion: opts.gameVersion,
|
||||
gameEdition: opts.gameEdition,
|
||||
maxPlayers: opts.maxPlayers,
|
||||
password: opts.password,
|
||||
});
|
||||
return { success: true, data: result };
|
||||
} catch (err: any) {
|
||||
return { success: false, error: err.response?.data?.error || err.message };
|
||||
}
|
||||
});
|
||||
|
||||
// Disconnect
|
||||
ipcMain.handle('relay:disconnect', () => {
|
||||
cleanup();
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
// Stats
|
||||
ipcMain.handle('server:stats', async () => {
|
||||
if (!apiClient) return { success: false, error: '未连接服务器' };
|
||||
try {
|
||||
const data = await apiClient.getStats();
|
||||
return { success: true, data };
|
||||
} catch (err: any) {
|
||||
return { success: false, error: err.message };
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user