mirror of
https://github.com/GSManagerXZ/GameServerManager.git
synced 2026-06-06 05:19:49 +08:00
完善功能
This commit is contained in:
@@ -18,4 +18,4 @@ const possiblePaths = [
|
||||
```
|
||||
14. 涉及交互弹窗的,不要使用浏览器的对话框 使用符合面板风格的弹窗组件
|
||||
15. 如果需要识别操作系统平台有专门的函数,你需要查找不需要单独写
|
||||
16. 项目分为前端和后端 浏览器和后端访问的不一定在同一环境和平台,所以请不要使用浏览器侧相关检测等。应当使用后端方法。
|
||||
16. 编写完毕后需要运行npx tsc --noEmit进行检测
|
||||
@@ -29,8 +29,9 @@ interface JavaEnvironment {
|
||||
installStage?: 'download' | 'extract'
|
||||
}
|
||||
|
||||
interface SystemInfo {
|
||||
interface LocalSystemInfo {
|
||||
platform: string
|
||||
rawPlatform?: string // 原始平台标识 (win32/linux/darwin)
|
||||
arch: string
|
||||
}
|
||||
|
||||
@@ -83,7 +84,7 @@ interface DirectXEnvironment {
|
||||
}
|
||||
|
||||
const EnvironmentManagerPage: React.FC = () => {
|
||||
const [systemInfo, setSystemInfo] = useState<SystemInfo | null>(null)
|
||||
const [systemInfo, setSystemInfo] = useState<LocalSystemInfo | null>(null)
|
||||
const [javaEnvironments, setJavaEnvironments] = useState<JavaEnvironment[]>([])
|
||||
const [vcRedistEnvironments, setVcRedistEnvironments] = useState<VcRedistEnvironment[]>([])
|
||||
const [directxEnvironments, setDirectxEnvironments] = useState<DirectXEnvironment[]>([])
|
||||
@@ -165,6 +166,7 @@ const EnvironmentManagerPage: React.FC = () => {
|
||||
if (response.success && response.data) {
|
||||
setSystemInfo({
|
||||
platform: response.data.platform,
|
||||
rawPlatform: response.data.rawPlatform,
|
||||
arch: response.data.arch
|
||||
})
|
||||
}
|
||||
@@ -1174,7 +1176,11 @@ const EnvironmentManagerPage: React.FC = () => {
|
||||
// 获取平台图标
|
||||
const getPlatformIcon = () => {
|
||||
if (!systemInfo) return <Monitor className="w-4 h-4" />
|
||||
return systemInfo.platform === 'win32'
|
||||
// 优先使用 rawPlatform,回退到检查 platform 字段
|
||||
const isWindows = systemInfo?.rawPlatform
|
||||
? systemInfo.rawPlatform === 'win32'
|
||||
: systemInfo.platform?.toLowerCase().includes('windows')
|
||||
return isWindows
|
||||
? <Monitor className="w-4 h-4" />
|
||||
: <Server className="w-4 h-4" />
|
||||
}
|
||||
@@ -1182,7 +1188,11 @@ const EnvironmentManagerPage: React.FC = () => {
|
||||
// 获取平台名称
|
||||
const getPlatformName = () => {
|
||||
if (!systemInfo) return '未知'
|
||||
return systemInfo.platform === 'win32' ? 'Windows' : 'Linux'
|
||||
// 优先使用 rawPlatform,回退到检查 platform 字段
|
||||
const isWindows = systemInfo?.rawPlatform
|
||||
? systemInfo.rawPlatform === 'win32'
|
||||
: systemInfo.platform?.toLowerCase().includes('windows')
|
||||
return isWindows ? 'Windows' : 'Linux'
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
|
||||
@@ -52,7 +52,18 @@ interface Games {
|
||||
[key: string]: GameInfo
|
||||
}
|
||||
|
||||
|
||||
// 辅助函数:判断是否为 Windows 平台
|
||||
const isWindowsPlatform = (systemInfo: any): boolean => {
|
||||
// 优先使用 rawPlatform(原始平台标识)
|
||||
if (systemInfo?.rawPlatform) {
|
||||
return systemInfo.rawPlatform === 'win32'
|
||||
}
|
||||
// 回退到检查 platform 字段(友好名称)
|
||||
if (systemInfo?.platform) {
|
||||
return systemInfo.platform.toLowerCase().includes('windows')
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const GameDeploymentPage: React.FC = () => {
|
||||
const { addNotification } = useNotificationStore()
|
||||
@@ -685,8 +696,9 @@ const GameDeploymentPage: React.FC = () => {
|
||||
setCloudBuildLogs(prev => [...prev, '部署完成!'])
|
||||
|
||||
// 使用后端检测到的启动命令
|
||||
const startCommand = response.data.startCommand ||
|
||||
(systemInfo?.platform === 'win32' ? '.\\start.bat' : 'bash start.sh')
|
||||
// 使用辅助函数判断平台(优先使用 rawPlatform,回退到 platform)
|
||||
const startCommand = response.data.startCommand ||
|
||||
(isWindowsPlatform(systemInfo) ? '.\\start.bat' : 'bash start.sh')
|
||||
|
||||
setCloudBuildComplete(true)
|
||||
setCloudBuildResult({
|
||||
@@ -724,10 +736,11 @@ const GameDeploymentPage: React.FC = () => {
|
||||
const instanceName = `${cloudBuildResult.coreName}-${cloudBuildResult.version}`
|
||||
setCloudInstanceName(instanceName)
|
||||
setCloudInstanceDescription(`${cloudBuildResult.coreName} ${cloudBuildResult.version} 服务端`)
|
||||
|
||||
|
||||
// 使用后端检测到的启动命令,如果没有则使用默认值
|
||||
const startCommand = cloudBuildResult.startCommand ||
|
||||
(systemInfo?.platform === 'win32' ? '.\\start.bat' : 'bash start.sh')
|
||||
// 使用辅助函数判断平台(优先使用 rawPlatform,回退到 platform)
|
||||
const startCommand = cloudBuildResult.startCommand ||
|
||||
(isWindowsPlatform(systemInfo) ? '.\\start.bat' : 'bash start.sh')
|
||||
setCloudInstanceStartCommand(startCommand)
|
||||
|
||||
setShowCreateCloudInstanceModal(true)
|
||||
@@ -1654,13 +1667,14 @@ const GameDeploymentPage: React.FC = () => {
|
||||
}
|
||||
|
||||
// 根据服务器类型生成启动命令
|
||||
const generateStartCommand = (serverType: string, selectedJava: string = 'default', isWindows: boolean = process.platform === 'win32') => {
|
||||
const generateStartCommand = (serverType: string, selectedJava: string = 'default', isWindows: boolean = isWindowsPlatform(systemInfo)) => {
|
||||
const lowerServerType = serverType.toLowerCase()
|
||||
const javaExecutable = getSelectedJavaExecutable(selectedJava)
|
||||
|
||||
if (lowerServerType.includes('forge') || lowerServerType.includes('neoforge')) {
|
||||
// Forge/NeoForge 使用启动脚本
|
||||
return isWindows ? 'run.bat' : './run.sh'
|
||||
// 使用辅助函数判断平台(优先使用 rawPlatform,回退到 platform)
|
||||
return isWindows ? '.\\run.bat' : 'bash run.sh'
|
||||
} else if (lowerServerType.includes('fabric') || lowerServerType.includes('quilt')) {
|
||||
// Fabric/Quilt 重命名为 server.jar
|
||||
return `${javaExecutable} -jar server.jar`
|
||||
@@ -1922,11 +1936,12 @@ const GameDeploymentPage: React.FC = () => {
|
||||
// 监听Java版本选择变化,自动更新启动命令
|
||||
useEffect(() => {
|
||||
// 更新Minecraft实例启动命令
|
||||
if (selectedServer && selectedVersion && selectedMinecraftJava) {
|
||||
// 只在用户手动选择时更新,不覆盖下载后扫描得到的启动命令
|
||||
if (selectedServer && selectedVersion && selectedMinecraftJava && !downloadResult) {
|
||||
setInstanceStartCommand(generateStartCommand(selectedServer, selectedMinecraftJava))
|
||||
setStartCommandEdited(false)
|
||||
}
|
||||
}, [selectedMinecraftJava, selectedServer])
|
||||
}, [selectedMinecraftJava, selectedServer, downloadResult])
|
||||
|
||||
useEffect(() => {
|
||||
// 更新整合包实例启动命令
|
||||
@@ -1986,7 +2001,8 @@ const GameDeploymentPage: React.FC = () => {
|
||||
const folderName = `modrinth-${cleanPackId}-${cleanVersion}`
|
||||
|
||||
// 根据平台使用正确的路径分隔符
|
||||
const isWindows = systemInfo?.platform === 'win32'
|
||||
// 使用辅助函数判断平台(优先使用 rawPlatform,回退到 platform)
|
||||
const isWindows = isWindowsPlatform(systemInfo)
|
||||
const separator = isWindows ? '\\' : '/'
|
||||
|
||||
// 确保基础路径以分隔符结尾
|
||||
@@ -3494,7 +3510,8 @@ const GameDeploymentPage: React.FC = () => {
|
||||
const folderName = `modrinth-${cleanPackId}-${cleanVersion}`
|
||||
|
||||
// 根据平台使用正确的路径分隔符
|
||||
const isWindows = systemInfo?.platform === 'win32'
|
||||
// 使用辅助函数判断平台(优先使用 rawPlatform,回退到 platform)
|
||||
const isWindows = isWindowsPlatform(systemInfo)
|
||||
const separator = isWindows ? '\\' : '/'
|
||||
|
||||
// 确保基础路径以分隔符结尾
|
||||
@@ -3966,28 +3983,38 @@ const GameDeploymentPage: React.FC = () => {
|
||||
</p>
|
||||
<button
|
||||
onClick={async () => {
|
||||
console.log('[Minecraft实例创建] 点击创建实例按钮')
|
||||
console.log('[Minecraft实例创建] selectedServer:', selectedServer)
|
||||
console.log('[Minecraft实例创建] downloadResult:', downloadResult)
|
||||
|
||||
setInstanceName(`${selectedServer}-${selectedVersion}`)
|
||||
setInstanceDescription(`Minecraft ${selectedServer} ${selectedVersion} 服务器`)
|
||||
|
||||
|
||||
// 打开弹窗前预扫描并填充启动命令
|
||||
try {
|
||||
console.log('[Minecraft实例创建] 打开弹窗前进行目录扫描:', downloadResult.targetDirectory)
|
||||
const scanResult = await apiClient.scanMinecraftDirectory(downloadResult.targetDirectory)
|
||||
console.log('[Minecraft实例创建] 扫描结果:', scanResult)
|
||||
if (scanResult.success && scanResult.data?.recommendedStartCommand) {
|
||||
let cmd = scanResult.data.recommendedStartCommand as string
|
||||
console.log('[Minecraft实例创建] 后端推荐命令:', cmd)
|
||||
if (scanResult.data.startMethod === 'jar_file' && selectedMinecraftJava !== 'default') {
|
||||
const javaExecutable = getSelectedJavaExecutable(selectedMinecraftJava)
|
||||
cmd = cmd.replace(/^java\b/, javaExecutable)
|
||||
}
|
||||
console.log('[Minecraft实例创建] 最终使用命令:', cmd)
|
||||
setInstanceStartCommand(cmd)
|
||||
setStartCommandEdited(false)
|
||||
} else {
|
||||
const fallback = generateStartCommand(selectedServer, selectedMinecraftJava)
|
||||
console.log('[Minecraft实例创建] 使用回退命令:', fallback)
|
||||
setInstanceStartCommand(fallback)
|
||||
setStartCommandEdited(false)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[Minecraft实例创建] 扫描异常:', e)
|
||||
const fallback = generateStartCommand(selectedServer, selectedMinecraftJava)
|
||||
console.log('[Minecraft实例创建] 异常回退命令:', fallback)
|
||||
setInstanceStartCommand(fallback)
|
||||
setStartCommandEdited(false)
|
||||
}
|
||||
|
||||
@@ -757,9 +757,13 @@ const InstanceManagerPage: React.FC = () => {
|
||||
|
||||
// 检测平台和启动命令格式是否匹配
|
||||
// 使用后端服务器的平台信息来判断
|
||||
const serverPlatform = systemInfo?.platform?.toLowerCase() || ''
|
||||
const isWindowsServer = serverPlatform.includes('win32') || serverPlatform.includes('windows')
|
||||
const isLinuxServer = serverPlatform.includes('linux') || serverPlatform.includes('darwin')
|
||||
// 优先使用 rawPlatform,回退到检查 platform 字段
|
||||
const isWindowsServer = systemInfo?.rawPlatform
|
||||
? systemInfo.rawPlatform === 'win32'
|
||||
: systemInfo?.platform?.toLowerCase().includes('windows') || false
|
||||
const isLinuxServer = systemInfo?.rawPlatform
|
||||
? (systemInfo.rawPlatform === 'linux' || systemInfo.rawPlatform === 'darwin')
|
||||
: systemInfo?.platform?.toLowerCase().includes('linux') || false
|
||||
|
||||
// Windows 平台使用了 Linux 格式 (./)
|
||||
const isWindowsWithLinuxSlash = startCommand.startsWith('./') && isWindowsServer
|
||||
|
||||
@@ -136,6 +136,7 @@ export interface SystemStats {
|
||||
|
||||
export interface SystemInfo {
|
||||
platform: string
|
||||
rawPlatform?: string // 原始平台标识 (win32/linux/darwin),用于平台判断
|
||||
arch: string
|
||||
hostname: string
|
||||
ipv4: string[]
|
||||
|
||||
@@ -96,6 +96,7 @@ export interface ModpackDeployResult {
|
||||
targetDirectory?: string;
|
||||
installedMods?: number;
|
||||
loaderVersion?: string;
|
||||
serverType?: string; // 服务端类型 (forge/neoforge/fabric/quilt)
|
||||
}
|
||||
|
||||
// ==================== Mrpack处理器类 ====================
|
||||
@@ -585,13 +586,14 @@ export class MrpackServerAPI {
|
||||
if (onProgress) {
|
||||
onProgress('整合包部署完成!', 'success');
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: '整合包部署成功',
|
||||
targetDirectory,
|
||||
installedMods: downloadedMods,
|
||||
loaderVersion: indexData.dependencies[loaderType] || 'latest'
|
||||
loaderVersion: indexData.dependencies[loaderType] || 'latest',
|
||||
serverType: loaderType // 返回服务端类型,用于前端生成启动命令
|
||||
};
|
||||
} catch (error) {
|
||||
// 清理临时文件
|
||||
|
||||
@@ -11,6 +11,7 @@ const execAsync = promisify(exec)
|
||||
|
||||
interface SystemInfo {
|
||||
platform: string
|
||||
rawPlatform: string // 原始平台标识 (win32/linux/darwin)
|
||||
arch: string
|
||||
hostname: string
|
||||
ipv4: string[]
|
||||
@@ -426,9 +427,10 @@ export class SystemManager extends EventEmitter {
|
||||
|
||||
// 获取本地IP地址
|
||||
const { ipv4, ipv6 } = this.getLocalIpAddresses()
|
||||
|
||||
|
||||
return {
|
||||
platform: platformName,
|
||||
rawPlatform: os.platform(), // 保存原始平台标识
|
||||
arch: os.arch(),
|
||||
hostname: os.hostname(),
|
||||
ipv4,
|
||||
|
||||
@@ -163,32 +163,27 @@ router.post('/download', authenticateToken, async (req, res) => {
|
||||
const files = await fs.readdir(targetPath)
|
||||
let startCommand = ''
|
||||
const isWindows = process.platform === 'win32'
|
||||
|
||||
// 检测是否有run文件
|
||||
const runFile = files.find(file =>
|
||||
file.toLowerCase().startsWith('run') &&
|
||||
(file.endsWith('.bat') || file.endsWith('.sh') || !file.includes('.'))
|
||||
)
|
||||
|
||||
if (runFile) {
|
||||
// 如果存在run文件,优先使用
|
||||
if (isWindows) {
|
||||
startCommand = `.\\${runFile}`
|
||||
} else {
|
||||
startCommand = runFile.endsWith('.sh') ? `bash ${runFile}` : `./${runFile}`
|
||||
// 根据平台优先选择对应的启动脚本
|
||||
if (isWindows) {
|
||||
// Windows平台:优先 run.bat > start.bat
|
||||
const runBat = files.find(file => file.toLowerCase() === 'run.bat')
|
||||
const startBat = files.find(file => file.toLowerCase() === 'start.bat')
|
||||
|
||||
if (runBat) {
|
||||
startCommand = `.\\${runBat}`
|
||||
} else if (startBat) {
|
||||
startCommand = `.\\${startBat}`
|
||||
}
|
||||
} else {
|
||||
// 检测start文件
|
||||
const hasStartBat = files.includes('start.bat')
|
||||
const hasStartSh = files.includes('start.sh')
|
||||
|
||||
if (hasStartBat && hasStartSh) {
|
||||
// 根据当前平台选择
|
||||
startCommand = isWindows ? '.\\start.bat' : 'bash start.sh'
|
||||
} else if (hasStartBat) {
|
||||
startCommand = '.\\start.bat'
|
||||
} else if (hasStartSh) {
|
||||
startCommand = 'bash start.sh'
|
||||
// Linux/Mac平台:优先 run.sh > start.sh
|
||||
const runSh = files.find(file => file.toLowerCase() === 'run.sh')
|
||||
const startSh = files.find(file => file.toLowerCase() === 'start.sh')
|
||||
|
||||
if (runSh) {
|
||||
startCommand = `bash ${runSh}`
|
||||
} else if (startSh) {
|
||||
startCommand = `bash ${startSh}`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,13 +29,15 @@ const packageManager = new LinuxPackageManager()
|
||||
// 获取系统信息
|
||||
router.get('/system-info', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const rawPlatform = os.platform()
|
||||
const systemInfo = {
|
||||
platform: os.platform(),
|
||||
platform: rawPlatform, // 保持原样,用于显示
|
||||
rawPlatform: rawPlatform, // 原始平台标识,用于判断
|
||||
arch: os.arch(),
|
||||
type: os.type(),
|
||||
release: os.release()
|
||||
}
|
||||
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: systemInfo
|
||||
|
||||
@@ -877,17 +877,18 @@ router.post('/scan-minecraft-directory', authenticateToken, async (req: Request,
|
||||
// 优先选择run.bat,否则使用第一个找到的.bat文件
|
||||
const runBat = batFiles.find(f => f.toLowerCase() === 'run.bat')
|
||||
const scriptFile = runBat || batFiles[0]
|
||||
recommendedStartCommand = scriptFile
|
||||
recommendedStartCommand = `.\\${scriptFile}` // 添加 .\ 路径前缀
|
||||
startMethod = 'bat_script'
|
||||
logger.info(`[智能检测] 推荐使用BAT脚本: ${scriptFile}`)
|
||||
logger.info(`[智能检测] 推荐使用BAT脚本: ${recommendedStartCommand}`)
|
||||
} else if (!isWindows && shFiles.length > 0) {
|
||||
// Linux/Mac平台优先使用.sh脚本
|
||||
// 优先选择run.sh,否则使用第一个找到的.sh文件
|
||||
const runSh = shFiles.find(f => f.toLowerCase() === 'run.sh')
|
||||
const scriptFile = runSh || shFiles[0]
|
||||
recommendedStartCommand = `./${scriptFile}`
|
||||
// 使用 bash 命令执行,与云构建保持一致
|
||||
recommendedStartCommand = `bash ${scriptFile}`
|
||||
startMethod = 'sh_script'
|
||||
logger.info(`[智能检测] 推荐使用SH脚本: ${scriptFile}`)
|
||||
logger.info(`[智能检测] 推荐使用SH脚本: ${recommendedStartCommand}`)
|
||||
} else if (jarFiles.length > 0) {
|
||||
// 如果没有对应平台的脚本,使用jar文件
|
||||
// 优先选择server.jar,否则使用第一个找到的jar文件
|
||||
@@ -899,7 +900,9 @@ router.post('/scan-minecraft-directory', authenticateToken, async (req: Request,
|
||||
} else {
|
||||
logger.warn(`[智能检测] 未找到任何启动文件 (jar/bat/sh)`)
|
||||
}
|
||||
|
||||
|
||||
logger.info(`[智能检测] 平台: ${platform}, isWindows: ${isWindows}, 推荐命令: ${recommendedStartCommand}`)
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
|
||||
Reference in New Issue
Block a user