1
0
Fork 0

1.4.2来啦!

Browse Source
This commit is contained in:
yunyun 2026-06-02 16:16:41 +08:00
parent 39c5292bef
commit bf3cf73fd8
4 changed files with 129 additions and 43 deletions

127
main.js
View File

@ -655,9 +655,82 @@ ipcMain.handle('cancel-close', () => {
// 取消启动游戏 // 取消启动游戏
let launchCancelFlag = false; let launchCancelFlag = false;
ipcMain.handle('cancel-launch', () => { let currentGameProcess = null;
let currentGamePid = null;
function killGameProcessTree(pid) {
try {
const result = spawnSync('taskkill', ['/pid', String(pid), '/T', '/F'], { windowsHide: true, timeout: 5000 });
if (result.status === 0) {
writeLog(`[启动] 已终止进程树 PID: ${pid}`);
return true;
}
writeLog(`[启动] taskkill PID ${pid} 返回: ${result.status}, ${result.stderr?.toString().trim()}`);
} catch (e) {
writeLog(`[启动] taskkill 失败: ${e.message}`);
}
return false;
}
function findAndKillMinecraftJavaProcesses() {
try {
const script = `
$javaProcs = Get-Process -Name "java","javaw" -ErrorAction SilentlyContinue
if ($javaProcs) {
foreach ($p in $javaProcs) {
try {
$cmdLine = (Get-CimInstance Win32_Process -Filter "ProcessId=$($p.Id)" -ErrorAction SilentlyContinue).CommandLine
if ($cmdLine -and ($cmdLine -like '*net.minecraft*' -or $cmdLine -like '*minecraft*' -or $cmdLine -like '*forge*' -or $cmdLine -like '*fabric*' -or $cmdLine -like '*optifine*')) {
Stop-Process -Id $p.Id -Force -ErrorAction SilentlyContinue
Write-Output "KILLED:$($p.Id)"
}
} catch {}
}
}
`;
const result = spawnSync('powershell', ['-NoProfile', '-Command', script], { windowsHide: true, timeout: 8000, encoding: 'utf8' });
const output = (result.stdout || '').trim();
if (output) {
const pids = output.split('\n').filter(l => l.startsWith('KILLED:')).map(l => l.replace('KILLED:', ''));
writeLog(`[启动] 通过命令行匹配杀死了 ${pids.length} 个Minecraft Java进程: ${pids.join(', ')}`);
return pids.length > 0;
}
} catch (e) {
writeLog(`[启动] 查找Minecraft进程失败: ${e.message}`);
}
return false;
}
ipcMain.handle('cancel-launch', async () => {
launchCancelFlag = true; launchCancelFlag = true;
writeLog('[启动] 用户取消启动'); writeLog('[启动] 用户取消启动');
let killed = false;
if (currentGameProcess) {
try {
const pid = currentGameProcess.pid;
if (pid) {
writeLog(`[启动] 尝试终止游戏进程 PID: ${pid}`);
killed = killGameProcessTree(pid);
try { currentGameProcess.kill(); } catch {}
}
} catch (e) {
writeLog(`[启动] 终止游戏进程异常: ${e.message}`);
}
currentGameProcess = null;
currentGamePid = null;
}
if (!killed) {
writeLog('[启动] 未找到已保存的游戏进程尝试通过命令行匹配查找Minecraft Java进程');
findAndKillMinecraftJavaProcesses();
}
if (mainWindow) {
mainWindow.webContents.send('game-closed', -1);
}
return { success: true }; return { success: true };
}); });
@ -670,6 +743,7 @@ ipcMain.handle('check-for-updates', async () => {
https.get(url, (res) => { https.get(url, (res) => {
let data = ''; let data = '';
res.setEncoding('utf8');
res.on('data', (chunk) => { res.on('data', (chunk) => {
data += chunk; data += chunk;
@ -694,16 +768,7 @@ ipcMain.handle('check-for-updates', async () => {
// 获取当前版本号从package.json读取 // 获取当前版本号从package.json读取
ipcMain.handle('get-app-version', () => { ipcMain.handle('get-app-version', () => {
try { return APP_VERSION || app.getVersion() || '1.0.0';
const packagePath = path.join(__dirname, 'package.json');
if (fs.existsSync(packagePath)) {
const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
return pkg.version || '1.0.0';
}
} catch (e) {
writeLog('[版本] 读取package.json失败: ' + e.message);
}
return '1.0.0';
}); });
// 打开外部链接 // 打开外部链接
@ -2492,18 +2557,38 @@ ipcMain.handle('launch-minecraft', async (_, opt) => {
} }
} }
currentGameProcess = proc;
currentGamePid = proc.pid;
// 如果已经被取消,立即杀死
if (launchCancelFlag) {
writeLog('[启动] 进程已启动但取消标志已设置,立即终止');
killGameProcessTree(proc.pid);
try { proc.kill(); } catch {}
currentGameProcess = null;
currentGamePid = null;
return { success: false, error: '启动已取消', cancelled: true };
}
// 检测游戏窗口创建成功 // 检测游戏窗口创建成功
await detectGameWindow(proc.pid); await detectGameWindow(proc.pid);
// 检测结束后再次检查取消标志(可能在窗口检测期间被取消)
if (launchCancelFlag) {
writeLog('[启动] 窗口检测结束后检测到取消标志');
return { success: false, error: '启动已取消', cancelled: true };
}
// 捕获输出用于调试 // 捕获输出用于调试
let stdoutData = ''; let stdoutData = '';
let stderrData = ''; let stderrData = '';
proc.stdout?.on('data', d => { proc.stdout?.on('data', d => {
const text = d.toString(); const text = d.toString();
stdoutData += text; stdoutData += text;
if (mainWindow) {
mainWindow.webContents.send('game-log', text); mainWindow.webContents.send('game-log', text);
}
// 检测Minecraft窗口创建成功的标志
if (text.includes('LWJGL Version') || text.includes('OpenGL version') || text.includes('Window')) { if (text.includes('LWJGL Version') || text.includes('OpenGL version') || text.includes('Window')) {
writeLog(`[启动] 检测到窗口初始化日志`); writeLog(`[启动] 检测到窗口初始化日志`);
if (mainWindow) { if (mainWindow) {
@ -2514,9 +2599,10 @@ ipcMain.handle('launch-minecraft', async (_, opt) => {
proc.stderr?.on('data', d => { proc.stderr?.on('data', d => {
const text = d.toString(); const text = d.toString();
stderrData += text; stderrData += text;
if (mainWindow) {
mainWindow.webContents.send('game-log', text); mainWindow.webContents.send('game-log', text);
}
// 检测Minecraft窗口创建成功的标志
if (text.includes('LWJGL Version') || text.includes('OpenGL version') || text.includes('Window')) { if (text.includes('LWJGL Version') || text.includes('OpenGL version') || text.includes('Window')) {
writeLog(`[启动] 检测到窗口初始化日志`); writeLog(`[启动] 检测到窗口初始化日志`);
if (mainWindow) { if (mainWindow) {
@ -2532,6 +2618,13 @@ ipcMain.handle('launch-minecraft', async (_, opt) => {
if (code !== 0 && stderrData) { if (code !== 0 && stderrData) {
writeLog(`[启动] 错误输出: ${stderrData.slice(0, 1000)}`); writeLog(`[启动] 错误输出: ${stderrData.slice(0, 1000)}`);
} }
if (currentGamePid === proc.pid) {
currentGameProcess = null;
currentGamePid = null;
}
if (mainWindow) {
mainWindow.webContents.send('game-closed', code);
}
}); });
return { success: true, pid: proc.pid }; return { success: true, pid: proc.pid };
@ -2555,9 +2648,15 @@ async function detectGameWindow(pid) {
const cancelCheck = setInterval(() => { const cancelCheck = setInterval(() => {
if (launchCancelFlag) { if (launchCancelFlag) {
writeLog(`[启动] 窗口检测期间检测到取消标志,立即退出`); writeLog(`[启动] 窗口检测期间检测到取消标志,立即终止游戏进程并退出`);
clearInterval(cancelCheck); clearInterval(cancelCheck);
try { detector.kill(); } catch {} try { detector.kill(); } catch {}
killGameProcessTree(pid);
try {
if (currentGameProcess) { currentGameProcess.kill(); }
} catch {}
currentGameProcess = null;
currentGamePid = null;
done(); done();
} }
}, 100); }, 100);

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "gpcl", "name": "gpcl",
"version": "1.4.1", "version": "1.4.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "gpcl", "name": "gpcl",
"version": "1.4.1", "version": "1.4.2",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"adm-zip": "^0.5.17", "adm-zip": "^0.5.17",

View File

@ -1,6 +1,6 @@
{ {
"name": "gpcl", "name": "gpcl",
"version": "1.4.1", "version": "1.4.2",
"description": "GoodPlanCraftLauncher", "description": "GoodPlanCraftLauncher",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {

View File

@ -1,4 +1,4 @@
/* /*
* CaelLab BY-SA Code License * CaelLab BY-SA Code License
* Copyright (c) 2026 Yunyun(云云) By 虚舟实验室(CaelLab) / CaelLabGameTS * Copyright (c) 2026 Yunyun(云云) By 虚舟实验室(CaelLab) / CaelLabGameTS
@ -2424,10 +2424,7 @@ launchBtn.addEventListener('click', launchGame);
checkUpdateBtn.classList.add('new-version'); checkUpdateBtn.classList.add('new-version');
if (updateStatus) { if (updateStatus) {
updateStatus.innerHTML = ` updateStatus.innerHTML = `<div class="update-log-title">发现 ${allNewerVersions.length} 个新版本!</div>${renderUpdateVersionCards(allNewerVersions)}`;
<div class="update-log-title">发现 ${allNewerVersions.length} 个新版本</div>
${renderUpdateVersionCards(allNewerVersions)}
`;
updateStatus.classList.add('show', 'has-update'); updateStatus.classList.add('show', 'has-update');
} }
@ -3055,22 +3052,12 @@ function renderUpdateVersionCards(newerVersions) {
const cards = newerVersions.map((v, index) => { const cards = newerVersions.map((v, index) => {
const isNewest = index === newerVersions.length - 1; const isNewest = index === newerVersions.length - 1;
const titleHtml = v.title const titleHtml = v.title ? `<div class="update-card-title">${v.title}</div>` : '';
? `<div class="update-card-title">${v.title}</div>` const logHtml = v.log ? `<div class="update-card-log">${v.log}</div>` : '';
: ''; return `<div class="update-version-card ${isNewest ? 'newest' : ''}">` +
const logHtml = v.log `<div class="update-card-header"><span class="update-card-version">${v.version}</span>` +
? `<div class="update-card-log">${v.log}</div>` `${isNewest ? '<span class="update-card-badge">最新</span>' : ''}</div>` +
: ''; `${titleHtml}${logHtml}</div>`;
return `
<div class="update-version-card ${isNewest ? 'newest' : ''}">
<div class="update-card-header">
<span class="update-card-version">${v.version}</span>
${isNewest ? '<span class="update-card-badge">最新</span>' : ''}
</div>
${titleHtml}
${logHtml}
</div>
`;
}).reverse().join(''); }).reverse().join('');
return `<div class="update-versions-list">${cards}</div>`; return `<div class="update-versions-list">${cards}</div>`;
@ -3101,7 +3088,7 @@ async function checkForUpdates(silent = false) {
allNewerVersions = parsedVersions allNewerVersions = parsedVersions
.filter(v => compareVersions(currentVersion, v.version) > 0) .filter(v => compareVersions(currentVersion, v.version) > 0)
.sort((a, b) => compareVersions(a.version, b.version)); .sort((a, b) => compareVersions(b.version, a.version));
updateAvailable = allNewerVersions.length > 0; updateAvailable = allNewerVersions.length > 0;