1.4.2来啦!
This commit is contained in:
parent
39c5292bef
commit
bf3cf73fd8
135
main.js
135
main.js
|
|
@ -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;
|
||||||
mainWindow.webContents.send('game-log', text);
|
if (mainWindow) {
|
||||||
|
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;
|
||||||
mainWindow.webContents.send('game-log', text);
|
if (mainWindow) {
|
||||||
|
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);
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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": {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue