From ee73cd24bf2cf814ce5ade8bb5084b7127781700 Mon Sep 17 00:00:00 2001 From: SuYvHan <925773501@qq.com> Date: Thu, 21 Aug 2025 21:46:08 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=95=9C=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pages/GameDeploymentPage.tsx | 231 ++++++++++++++++-- client/src/utils/api.ts | 2 +- .../game/othergame/mrpack-server-api.ts | 34 ++- .../game/othergame/unified-functions.ts | 21 +- server/src/routes/moreGames.ts | 19 +- 5 files changed, 260 insertions(+), 47 deletions(-) diff --git a/client/src/pages/GameDeploymentPage.tsx b/client/src/pages/GameDeploymentPage.tsx index 940eea1..7146d7b 100644 --- a/client/src/pages/GameDeploymentPage.tsx +++ b/client/src/pages/GameDeploymentPage.tsx @@ -84,6 +84,9 @@ const GameDeploymentPage: React.FC = () => { const [instanceName, setInstanceName] = useState('') const [instanceDescription, setInstanceDescription] = useState('') const [instanceStartCommand, setInstanceStartCommand] = useState('') + const [instanceStartFile, setInstanceStartFile] = useState('') + const [instanceJarArgs, setInstanceJarArgs] = useState('-Xmx2G -Xms1G') + const [availableStartFiles, setAvailableStartFiles] = useState([]) const [creatingInstance, setCreatingInstance] = useState(false) // 更多游戏部署相关状态 @@ -122,6 +125,9 @@ const GameDeploymentPage: React.FC = () => { const [mrpackInstanceName, setMrpackInstanceName] = useState('') const [mrpackInstanceDescription, setMrpackInstanceDescription] = useState('') const [mrpackInstanceStartCommand, setMrpackInstanceStartCommand] = useState('') + const [mrpackInstanceStartFile, setMrpackInstanceStartFile] = useState('') + const [mrpackInstanceJarArgs, setMrpackInstanceJarArgs] = useState('-Xmx2G -Xms1G') + const [mrpackAvailableStartFiles, setMrpackAvailableStartFiles] = useState([]) const [creatingMrpackInstance, setCreatingMrpackInstance] = useState(false) // 在线部署相关状态 @@ -681,13 +687,26 @@ const GameDeploymentPage: React.FC = () => { }) // 监听Minecraft下载完成 - socketRef.current.on('minecraft-download-complete', (data) => { + socketRef.current.on('minecraft-download-complete', async (data) => { console.log('收到下载完成事件:', data) if (data.downloadId === currentDownloadId.current) { setMinecraftDownloading(false) setDownloadComplete(true) setDownloadResult(data.data) + // 扫描启动文件 + if (data.data?.targetDirectory) { + const files = await scanStartFiles(data.data.targetDirectory) + setAvailableStartFiles(files) + if (files.length > 0) { + // 自动选择第一个文件 + setInstanceStartFile(files[0]) + // 根据文件类型生成启动命令 + const command = generateStartCommandFromFile(files[0], instanceJarArgs) + setInstanceStartCommand(command) + } + } + addNotification({ type: 'success', title: '下载完成', @@ -733,7 +752,7 @@ const GameDeploymentPage: React.FC = () => { }) // 监听Minecraft整合包部署完成 - socketRef.current.on('more-games-deploy-complete', (data) => { + socketRef.current.on('more-games-deploy-complete', async (data) => { // 检查是否是整合包部署的完成事件 if (data.deploymentId && data.deploymentId.startsWith('mrpack-deploy-')) { setMrpackDeploying(false) @@ -741,12 +760,25 @@ const GameDeploymentPage: React.FC = () => { setMrpackDeployResult(data.data) currentMrpackDeploymentId.current = null // 重置整合包部署ID - // 自动生成启动命令 - if (data.data?.serverType) { - setMrpackInstanceStartCommand(generateStartCommand(data.data.serverType)) + // 扫描启动文件 + if (data.data?.targetDirectory) { + const files = await scanStartFiles(data.data.targetDirectory) + setMrpackAvailableStartFiles(files) + if (files.length > 0) { + // 自动选择第一个文件 + setMrpackInstanceStartFile(files[0]) + // 根据文件类型生成启动命令 + const command = generateStartCommandFromFile(files[0], mrpackInstanceJarArgs) + setMrpackInstanceStartCommand(command) + } } else { - // 默认启动命令 - setMrpackInstanceStartCommand(data.data?.serverJarPath ? `java -jar "${data.data.serverJarPath}"` : 'java -jar server.jar') + // 自动生成启动命令(备用逻辑) + if (data.data?.serverType) { + setMrpackInstanceStartCommand(generateStartCommand(data.data.serverType)) + } else { + // 默认启动命令 + setMrpackInstanceStartCommand(data.data?.serverJarPath ? `java -jar "${data.data.serverJarPath}"` : 'java -jar server.jar') + } } addNotification({ @@ -1049,7 +1081,40 @@ const GameDeploymentPage: React.FC = () => { setHoveredMrpack(null) } - // 根据服务器类型生成启动命令 + // 扫描目录中的启动文件 + const scanStartFiles = async (directory: string) => { + try { + const response = await apiClient.getFiles(directory) + if (response.success && response.data && response.data.files) { + const files = response.data.files.filter((file: any) => { + const fileName = typeof file === 'string' ? file : file.name + const ext = fileName.toLowerCase() + return ext.endsWith('.jar') || ext.endsWith('.bat') || ext.endsWith('.sh') + }).map((file: any) => typeof file === 'string' ? file : file.name) + return files + } + } catch (error) { + console.error('扫描启动文件失败:', error) + } + return [] + } + + // 根据选择的文件生成启动命令 + const generateStartCommandFromFile = (fileName: string, jarArgs: string = '') => { + if (!fileName) return '' + + const ext = fileName.toLowerCase() + if (ext.endsWith('.jar')) { + return `java ${jarArgs} -jar ${fileName}` + } else if (ext.endsWith('.bat')) { + return fileName + } else if (ext.endsWith('.sh')) { + return `./${fileName}` + } + return fileName + } + + // 根据服务器类型生成启动命令(保留原有逻辑作为备用) const generateStartCommand = (serverType: string, isWindows: boolean = process.platform === 'win32') => { const lowerServerType = serverType.toLowerCase() @@ -1313,6 +1378,13 @@ const GameDeploymentPage: React.FC = () => { setCreateInstanceModalAnimating(false) setTimeout(() => { setShowCreateInstanceModal(false) + // 重置表单 + setInstanceName('') + setInstanceDescription('') + setInstanceStartCommand('') + setInstanceStartFile('') + setInstanceJarArgs('') + setAvailableStartFiles([]) }, 300) } @@ -1325,6 +1397,9 @@ const GameDeploymentPage: React.FC = () => { setMrpackInstanceName('') setMrpackInstanceDescription('') setMrpackInstanceStartCommand('') + setMrpackInstanceStartFile('') + setMrpackInstanceJarArgs('') + setMrpackAvailableStartFiles([]) }, 300) } @@ -2348,13 +2423,25 @@ const GameDeploymentPage: React.FC = () => { setInstanceStartCommand(e.target.value)} + + + {/* JAR启动参数(仅当选择.jar文件时显示) */} + {instanceStartFile && instanceStartFile.toLowerCase().endsWith('.jar') && ( +
+ + { + setInstanceJarArgs(e.target.value) + // 更新启动命令 + const command = generateStartCommandFromFile(instanceStartFile, e.target.value) + setInstanceStartCommand(command) + }} + className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white dark:bg-gray-700 text-gray-900 dark:text-white" + placeholder="例如: -Xmx2G -Xms1G" + /> +
+ )} + + {/* 生成的启动命令(只读显示) */} + {instanceStartCommand && ( +
+ + +
+ )}
@@ -3761,19 +3895,66 @@ const GameDeploymentPage: React.FC = () => { />
- {/* 启动命令 */} + {/* 启动文件选择 */}
- setMrpackInstanceStartCommand(e.target.value)} +
+ + {/* JAR启动参数(仅当选择.jar文件时显示) */} + {mrpackInstanceStartFile && mrpackInstanceStartFile.toLowerCase().endsWith('.jar') && ( +
+ + { + setMrpackInstanceJarArgs(e.target.value) + // 更新启动命令 + const command = generateStartCommandFromFile(mrpackInstanceStartFile, e.target.value) + setMrpackInstanceStartCommand(command) + }} + className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white dark:bg-gray-700 text-gray-900 dark:text-white" + placeholder="例如: -Xmx2G -Xms1G" + /> +
+ )} + + {/* 生成的启动命令(只读显示) */} + {mrpackInstanceStartCommand && ( +
+ + +
+ )}
diff --git a/client/src/utils/api.ts b/client/src/utils/api.ts index ce89995..bebad59 100644 --- a/client/src/utils/api.ts +++ b/client/src/utils/api.ts @@ -413,7 +413,7 @@ class ApiClient { // 文件管理API async getFiles(path: string) { - return this.get('/files', { params: { path } }) + return this.get('/files/list', { params: { path } }) } async uploadFile(file: File, path: string) { diff --git a/server/src/modules/game/othergame/mrpack-server-api.ts b/server/src/modules/game/othergame/mrpack-server-api.ts index b2e0637..c618554 100644 --- a/server/src/modules/game/othergame/mrpack-server-api.ts +++ b/server/src/modules/game/othergame/mrpack-server-api.ts @@ -101,14 +101,40 @@ export interface ModpackDeployResult { // ==================== Mrpack处理器类 ==================== export class MrpackServerAPI { - private static readonly MODRINTH_API_BASE = 'https://api.modrinth.com/v2'; + private static readonly MODRINTH_API_SOURCES = { + official: 'https://api.modrinth.com/v2', + mirror: 'https://mod.mcimirror.top/modrinth/v2' + }; private static readonly SEARCH_ENDPOINT = '/search'; private tempDir: string; private cancelled: boolean = false; private currentProcess?: ChildProcess; + private apiSource: 'official' | 'mirror'; - constructor(tempDir?: string) { + constructor(tempDir?: string, apiSource: 'official' | 'mirror' = 'official') { this.tempDir = tempDir || path.join(process.cwd(), 'temp-mrpack'); + this.apiSource = apiSource; + } + + /** + * 获取当前使用的API基础URL + */ + private getApiBaseUrl(): string { + return MrpackServerAPI.MODRINTH_API_SOURCES[this.apiSource]; + } + + /** + * 设置API源 + */ + setApiSource(apiSource: 'official' | 'mirror'): void { + this.apiSource = apiSource; + } + + /** + * 获取当前API源 + */ + getApiSource(): 'official' | 'mirror' { + return this.apiSource; } /** @@ -157,7 +183,7 @@ export class MrpackServerAPI { params.append('index', options.index || 'relevance'); const response = await axios.get( - `${MrpackServerAPI.MODRINTH_API_BASE}${MrpackServerAPI.SEARCH_ENDPOINT}?${params.toString()}` + `${this.getApiBaseUrl()}${MrpackServerAPI.SEARCH_ENDPOINT}?${params.toString()}` ); return response.data; @@ -175,7 +201,7 @@ export class MrpackServerAPI { async getProjectVersions(projectId: string): Promise { try { const response = await axios.get( - `${MrpackServerAPI.MODRINTH_API_BASE}/project/${projectId}/version`, + `${this.getApiBaseUrl()}/project/${projectId}/version`, { headers: { 'User-Agent': 'GSM3/1.0.0 (game server manager)' diff --git a/server/src/modules/game/othergame/unified-functions.ts b/server/src/modules/game/othergame/unified-functions.ts index b24e23e..bcd7dd1 100644 --- a/server/src/modules/game/othergame/unified-functions.ts +++ b/server/src/modules/game/othergame/unified-functions.ts @@ -180,6 +180,7 @@ export interface MrpackDeployOptions { deploymentId?: string; options?: any; onProgress?: LogCallback; + apiSource?: 'official' | 'mirror'; } export interface MrpackIndex { @@ -1134,13 +1135,13 @@ async function findFactorioExecutable(extractPath: string): Promise { - const mrpackAPI = new MrpackServerAPI(); +export async function searchMrpackModpacks(options: MrpackSearchOptions = {}, apiSource: 'official' | 'mirror' = 'official'): Promise { + const mrpackAPI = new MrpackServerAPI(undefined, apiSource); return await mrpackAPI.searchModpacks(options); } -export async function getMrpackProjectVersions(projectId: string): Promise { - const mrpackAPI = new MrpackServerAPI(); +export async function getMrpackProjectVersions(projectId: string, apiSource: 'official' | 'mirror' = 'official'): Promise { + const mrpackAPI = new MrpackServerAPI(undefined, apiSource); return await mrpackAPI.getProjectVersions(projectId); } @@ -1148,8 +1149,8 @@ export async function getMrpackProjectVersions(projectId: string): Promise { - const mrpackAPI = new MrpackServerAPI(); +export async function downloadAndParseMrpack(mrpackUrl: string, apiSource: 'official' | 'mirror' = 'official'): Promise { + const mrpackAPI = new MrpackServerAPI(undefined, apiSource); return await mrpackAPI.downloadAndParseMrpack(mrpackUrl); } @@ -1157,7 +1158,7 @@ export async function downloadAndParseMrpack(mrpackUrl: string): Promise { - const { projectId, versionId, mrpackUrl, targetDirectory, onProgress } = options; + const { projectId, versionId, mrpackUrl, targetDirectory, onProgress, apiSource = 'official' } = options; // 创建部署实例 const deployment = globalDeploymentManager.createDeployment('mrpack', targetDirectory, onProgress, options.deploymentId); @@ -1191,7 +1192,9 @@ export async function deployMrpackServer(options: MrpackDeployOptions): Promise< onProgress(`正在请求版本信息: ${versionId}`, 'info'); } - const versionResponse = await axios.get(`https://api.modrinth.com/v2/version/${versionId}`, { + // 根据选择的API源构建URL + const apiBaseUrl = apiSource === 'mirror' ? 'https://mod.mcimirror.top/modrinth/v2' : 'https://api.modrinth.com/v2'; + const versionResponse = await axios.get(`${apiBaseUrl}/version/${versionId}`, { headers: { 'User-Agent': 'GSM3/1.0.0' }, @@ -1233,7 +1236,7 @@ export async function deployMrpackServer(options: MrpackDeployOptions): Promise< } // 使用 MrpackServerAPI 进行部署 - const mrpackAPI = new MrpackServerAPI(); + const mrpackAPI = new MrpackServerAPI(undefined, apiSource); // 设置取消监听 deployment.cancellationToken.onCancelled(() => { diff --git a/server/src/routes/moreGames.ts b/server/src/routes/moreGames.ts index e8ef954..a7d6965 100644 --- a/server/src/routes/moreGames.ts +++ b/server/src/routes/moreGames.ts @@ -570,8 +570,8 @@ router.get('/version/:gameId', authenticateToken, async (req: Request, res: Resp // 搜索Minecraft整合包 router.get('/mrpack/search', authenticateToken, async (req: Request, res: Response) => { try { - const { query, limit = 20, offset = 0, categories, versions, loaders } = req.query - + const { query, limit = 20, offset = 0, categories, versions, loaders, apiSource = 'official' } = req.query + const searchOptions = { query: query as string, limit: parseInt(limit as string), @@ -580,8 +580,8 @@ router.get('/mrpack/search', authenticateToken, async (req: Request, res: Respon versions: versions ? (versions as string).split(',') : undefined, loaders: loaders ? (loaders as string).split(',') : undefined } - - const result = await searchMrpackModpacks(searchOptions) + + const result = await searchMrpackModpacks(searchOptions, apiSource as 'official' | 'mirror') res.json({ success: true, @@ -603,7 +603,8 @@ router.get('/mrpack/search', authenticateToken, async (req: Request, res: Respon router.get('/mrpack/project/:projectId/versions', authenticateToken, async (req: Request, res: Response) => { try { const { projectId } = req.params - + const { apiSource = 'official' } = req.query + if (!projectId) { return res.status(400).json({ success: false, @@ -611,8 +612,8 @@ router.get('/mrpack/project/:projectId/versions', authenticateToken, async (req: message: '项目ID为必填项' }) } - - const versions = await getMrpackProjectVersions(projectId) + + const versions = await getMrpackProjectVersions(projectId, apiSource as 'official' | 'mirror') res.json({ success: true, @@ -633,7 +634,7 @@ router.get('/mrpack/project/:projectId/versions', authenticateToken, async (req: // 部署Minecraft整合包 router.post('/deploy/mrpack', authenticateToken, async (req: Request, res: Response) => { try { - const { projectId, versionId, installPath, options = {}, socketId } = req.body + const { projectId, versionId, installPath, options = {}, socketId, apiSource = 'official' } = req.body if (!projectId || !versionId || !installPath) { return res.status(400).json({ @@ -684,6 +685,8 @@ router.post('/deploy/mrpack', authenticateToken, async (req: Request, res: Respo targetDirectory: installPath, deploymentId, options, + apiSource: apiSource as 'official' | 'mirror', + apiSource: apiSource as 'official' | 'mirror', onProgress: (message, type = 'info') => { if (io && socketId) { io.to(socketId).emit('more-games-deploy-log', { From ff92f108dc9582c1b4c9c89649e204a33bb9fdcd Mon Sep 17 00:00:00 2001 From: SuYvHan <925773501@qq.com> Date: Thu, 21 Aug 2025 22:06:24 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=95=9C=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pages/GameDeploymentPage.tsx | 38 ++++++++++++++++++++++--- client/src/utils/api.ts | 13 +++++++-- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/client/src/pages/GameDeploymentPage.tsx b/client/src/pages/GameDeploymentPage.tsx index 51a2808..72c894e 100644 --- a/client/src/pages/GameDeploymentPage.tsx +++ b/client/src/pages/GameDeploymentPage.tsx @@ -173,6 +173,10 @@ const GameDeploymentPage: React.FC = () => { const [mrpackDeployProgress, setMrpackDeployProgress] = useState(null) const [mrpackDeployLogs, setMrpackDeployLogs] = useState([]) const [mrpackDeployComplete, setMrpackDeployComplete] = useState(false) + const [mrpackApiSource, setMrpackApiSource] = useState<'official' | 'mirror'>(() => { + // 从localStorage读取保存的选择 + return (localStorage.getItem('mrpackApiSource') as 'official' | 'mirror') || 'official' + }) // 整合包悬停详情状态 const [hoveredMrpack, setHoveredMrpack] = useState(null) @@ -530,6 +534,12 @@ const GameDeploymentPage: React.FC = () => { } } + // 保存整合包API源选择到localStorage + const handleMrpackApiSourceChange = (apiSource: 'official' | 'mirror') => { + setMrpackApiSource(apiSource) + localStorage.setItem('mrpackApiSource', apiSource) + } + // 搜索Minecraft整合包 const searchMrpackModpacks = async () => { if (!mrpackSearchQuery.trim()) { @@ -545,7 +555,8 @@ const GameDeploymentPage: React.FC = () => { setMrpackSearchLoading(true) const response = await apiClient.searchMrpackModpacks({ query: mrpackSearchQuery, - limit: 20 + limit: 20, + apiSource: mrpackApiSource }) if (response.success) { @@ -569,7 +580,7 @@ const GameDeploymentPage: React.FC = () => { const fetchMrpackVersions = async (projectId: string) => { try { setMrpackVersionsLoading(true) - const response = await apiClient.getMrpackProjectVersions(projectId) + const response = await apiClient.getMrpackProjectVersions(projectId, mrpackApiSource) if (response.success) { setMrpackVersions(response.data || []) @@ -643,7 +654,8 @@ const GameDeploymentPage: React.FC = () => { projectId: selectedMrpack.project_id, versionId: selectedMrpackVersion.id, installPath: mrpackInstallPath, - socketId + socketId, + apiSource: mrpackApiSource }) if (response.success) { @@ -3314,7 +3326,25 @@ const GameDeploymentPage: React.FC = () => {

搜索Minecraft整合包

- + + {/* API源选择 */} +
+ + +

+ 镜像源可能提供更快的访问速度,但内容可能略有延迟 +

+
+
Date: Thu, 21 Aug 2025 22:15:44 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AF=AD=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/routes/moreGames.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/routes/moreGames.ts b/server/src/routes/moreGames.ts index a7d6965..f2d04b5 100644 --- a/server/src/routes/moreGames.ts +++ b/server/src/routes/moreGames.ts @@ -686,7 +686,6 @@ router.post('/deploy/mrpack', authenticateToken, async (req: Request, res: Respo deploymentId, options, apiSource: apiSource as 'official' | 'mirror', - apiSource: apiSource as 'official' | 'mirror', onProgress: (message, type = 'info') => { if (io && socketId) { io.to(socketId).emit('more-games-deploy-log', {