Files
fast_install/start.html
T

1467 lines
52 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>R0对战平台</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
font-family: 'Segoe UI', 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif;
-webkit-user-select: none;
user-select: none;
width: 640px;
height: 480px;
overflow: hidden;
background: #0a0a0a;
}
/* 主容器 */
.box {
width: 640px;
height: 480px;
position: relative;
overflow: hidden;
background: linear-gradient(135deg, #0f0f0f 0%, #1a1a1a 100%);
}
/* 视频背景容器 */
.video-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}
.background-video {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
min-width: 100%;
min-height: 100%;
width: auto;
height: auto;
object-fit: cover;
filter: brightness(0.4) contrast(1.1) saturate(0.85) blur(1.2px);
opacity: 0.65;
animation: videoFadeIn 1.5s ease-out;
}
@keyframes videoFadeIn {
0% {
opacity: 0;
filter: brightness(0.2) contrast(1.1) saturate(0.85) blur(4px);
}
100% {
opacity: 0.65;
filter: brightness(0.4) contrast(1.1) saturate(0.85) blur(1.2px);
}
}
/* 渐变遮罩层 */
.overlay-gradient {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(ellipse at center, transparent 0%, rgba(0, 0, 0, 0.4) 40%, rgba(0, 0, 0, 0.8) 100%);
z-index: 2;
}
/* 扫描线效果 */
.scanlines {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 3;
pointer-events: none;
background: repeating-linear-gradient(
0deg,
transparent,
transparent 2px,
rgba(255, 255, 255, 0.01) 2px,
rgba(255, 255, 255, 0.01) 4px
);
animation: scanlines 8s linear infinite;
}
@keyframes scanlines {
0% {
transform: translateY(0);
}
100% {
transform: translateY(10px);
}
}
/* 主内容区域 */
.main-box {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 480px;
height: 360px;
z-index: 10;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
pointer-events: none; /* 禁用鼠标交互,保持界面独立 */
}
/* Logo 容器 - 玻璃态效果 */
.logo-container {
position: relative;
width: 320px;
height: 180px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 40px;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 20px;
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.1),
0 0 80px rgba(76, 175, 80, 0.15);
animation: logoContainerFadeIn 0.8s ease-out;
pointer-events: none; /* 禁用鼠标交互 */
cursor: default; /* 确保鼠标指针不会改变 */
}
@keyframes logoContainerFadeIn {
0% {
opacity: 0;
transform: scale(0.9) translateY(-20px);
}
100% {
opacity: 1;
transform: scale(1) translateY(0);
}
}
/* Logo 主体 */
.logo-main {
position: relative;
z-index: 2;
}
.logo-rel {
width: 200px;
height: auto;
filter: drop-shadow(0 4px 20px rgba(76, 175, 80, 0.3));
animation: logoFloat 3s ease-in-out infinite;
}
@keyframes logoFloat {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-5px);
}
}
/* Logo 光效 */
.logo-glow {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 240px;
height: 240px;
background: radial-gradient(circle, rgba(76, 175, 80, 0.2) 0%, transparent 70%);
filter: blur(20px);
animation: glowPulse 2s ease-in-out infinite;
z-index: 1;
}
@keyframes glowPulse {
0%, 100% {
opacity: 0.3;
transform: translate(-50%, -50%) scale(1);
}
50% {
opacity: 0.6;
transform: translate(-50%, -50%) scale(1.1);
}
}
/* 进度条容器 */
.progress-container {
width: 280px;
margin-top: 20px;
animation: fadeInUp 0.6s ease-out 0.3s both;
}
@keyframes fadeInUp {
0% {
opacity: 0;
transform: translateY(20px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
/* 进度条背景 */
.progress-box {
position: relative;
width: 100%;
height: 4px;
background: rgba(255, 255, 255, 0.05);
border-radius: 2px;
overflow: hidden;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3);
}
/* 进度条填充 */
#progress {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 0%;
background: linear-gradient(90deg, #4CAF50 0%, #6FFF63 100%);
border-radius: 2px;
transition: width 0.3s ease;
box-shadow: 0 0 10px rgba(76, 175, 80, 0.5);
}
/* 进度条光效 */
#progress::after {
content: '';
position: absolute;
top: 0;
right: 0;
width: 20px;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4));
animation: shimmer 1s linear infinite;
}
@keyframes shimmer {
0% {
transform: translateX(-20px);
}
100% {
transform: translateX(20px);
}
}
/* 状态文本 */
#text-tip {
display: block;
margin-top: 15px;
text-align: center;
color: rgba(255, 255, 255, 0.7);
font-size: 13px;
letter-spacing: 0.5px;
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
/* 进度百分比 */
#text-progress {
display: inline-block;
margin-left: 10px;
color: #6FFF63;
font-weight: 500;
text-shadow: 0 0 10px rgba(111, 255, 99, 0.5);
}
/* 慢速提示 */
#slow {
position: absolute;
bottom: 40px;
left: 50%;
transform: translateX(-50%);
background: linear-gradient(135deg, rgba(15, 15, 15, 0.95) 0%, rgba(25, 25, 25, 0.95) 100%);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 10px;
padding: 12px 20px;
color: rgba(255, 255, 255, 0.8);
font-size: 12px;
opacity: 0;
transition: opacity 0.3s ease;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
z-index: 20;
pointer-events: auto; /* 确保慢速提示框可以交互 */
}
#slow a {
color: #6FFF63;
text-decoration: none;
font-weight: 500;
margin-left: 5px;
transition: color 0.2s ease;
pointer-events: auto; /* 保持链接可点击 */
cursor: pointer;
}
#slow a:hover {
color: #4CAF50;
text-decoration: underline;
}
/* 装饰性元素 */
.decoration-dots {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 8px;
z-index: 5;
}
.dot {
width: 6px;
height: 6px;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
animation: dotPulse 1.5s ease-in-out infinite;
}
.dot:nth-child(1) { animation-delay: 0s; }
.dot:nth-child(2) { animation-delay: 0.2s; }
.dot:nth-child(3) { animation-delay: 0.4s; }
@keyframes dotPulse {
0%, 100% {
opacity: 0.2;
transform: scale(1);
}
50% {
opacity: 0.6;
transform: scale(1.2);
}
}
/* 角落装饰线 */
.corner-decoration {
position: absolute;
width: 40px;
height: 40px;
opacity: 0.3;
z-index: 4;
}
.corner-decoration::before,
.corner-decoration::after {
content: '';
position: absolute;
background: #4CAF50;
}
.corner-decoration.top-left {
top: 20px;
left: 20px;
}
.corner-decoration.top-left::before {
top: 0;
left: 0;
width: 100%;
height: 1px;
}
.corner-decoration.top-left::after {
top: 0;
left: 0;
width: 1px;
height: 100%;
}
.corner-decoration.bottom-right {
bottom: 20px;
right: 20px;
}
.corner-decoration.bottom-right::before {
bottom: 0;
right: 0;
width: 100%;
height: 1px;
}
.corner-decoration.bottom-right::after {
bottom: 0;
right: 0;
width: 1px;
height: 100%;
}
/* 隐藏原有的不需要的元素 */
.left-5, .right-e, .shadow, .text-img {
display: none;
}
/* 版本信息 */
.version-info {
position: absolute;
bottom: 10px;
right: 15px;
color: rgba(255, 255, 255, 0.3);
font-size: 10px;
z-index: 5;
letter-spacing: 0.5px;
}
/* 添加粒子效果 */
.particles {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 4;
pointer-events: none;
}
.particle {
position: absolute;
width: 2px;
height: 2px;
background: rgba(111, 255, 99, 0.4);
border-radius: 50%;
animation: floatUp 10s linear infinite;
}
@keyframes floatUp {
0% {
transform: translateY(100vh) translateX(0);
opacity: 0;
}
10% {
opacity: 0.4;
}
90% {
opacity: 0.4;
}
100% {
transform: translateY(-10vh) translateX(100px);
opacity: 0;
}
}
.particle:nth-child(1) { left: 10%; animation-delay: 0s; animation-duration: 8s; }
.particle:nth-child(2) { left: 20%; animation-delay: 1s; animation-duration: 10s; }
.particle:nth-child(3) { left: 30%; animation-delay: 2s; animation-duration: 9s; }
.particle:nth-child(4) { left: 40%; animation-delay: 1.5s; animation-duration: 11s; }
.particle:nth-child(5) { left: 50%; animation-delay: 3s; animation-duration: 8.5s; }
.particle:nth-child(6) { left: 60%; animation-delay: 2.5s; animation-duration: 9.5s; }
.particle:nth-child(7) { left: 70%; animation-delay: 1.8s; animation-duration: 10.5s; }
.particle:nth-child(8) { left: 80%; animation-delay: 3.5s; animation-duration: 8s; }
.particle:nth-child(9) { left: 90%; animation-delay: 4s; animation-duration: 9s; }
.particle:nth-child(10) { left: 85%; animation-delay: 0.5s; animation-duration: 11s; }
/* 移除鼠标交互效果 - 保持界面独立于鼠标操作 */
/* Logo容器不再响应鼠标悬浮 */
/* 添加加载完成时的过渡效果 */
.fade-out {
animation: fadeOut 0.5s ease-out forwards;
}
@keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
/* 增强进度条完成时的效果 */
#progress.complete {
background: linear-gradient(90deg, #4CAF50 0%, #8BC34A 50%, #6FFF63 100%);
box-shadow: 0 0 20px rgba(76, 175, 80, 0.8);
}
/* 添加加载提示文字的动画 */
@keyframes textPulse {
0%, 100% {
opacity: 0.7;
}
50% {
opacity: 1;
}
}
#text-tip.loading {
animation: textPulse 1.5s ease-in-out infinite;
}
</style>
</head>
<body>
<div class="box">
<!-- 视频背景层 -->
<div class="video-container">
<video class="background-video" autoplay loop muted>
<source src="./images/loading.mp4" type="video/mp4">
</video>
</div>
<!-- 渐变遮罩层 -->
<div class="overlay-gradient"></div>
<!-- 扫描线效果 -->
<div class="scanlines"></div>
<!-- 粒子效果 -->
<div class="particles">
<span class="particle"></span>
<span class="particle"></span>
<span class="particle"></span>
<span class="particle"></span>
<span class="particle"></span>
<span class="particle"></span>
<span class="particle"></span>
<span class="particle"></span>
<span class="particle"></span>
<span class="particle"></span>
</div>
<!-- 角落装饰 -->
<div class="corner-decoration top-left"></div>
<div class="corner-decoration bottom-right"></div>
<!-- 主内容区域 -->
<div class="main-box">
<!-- Logo 容器 -->
<!-- <div class="logo-container"> -->
<div class="logo-glow"></div>
<div class="logo-main">
<img src="./images/bg.png" class="logo-rel" />
</div>
<!-- </div> -->
<!-- 进度条容器 -->
<div class="progress-container">
<div class="progress-box">
<div id="progress"></div>
</div>
<div id="text-tip">正在检测升级</div>
<span id="text-progress"></span>
</div>
</div>
<!-- 装饰性点 -->
<div class="decoration-dots">
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
</div>
<!-- 慢速提示 -->
<div id="slow">
<span>⚠️ 当前网速较慢,建议下载最新安装包</span>
<a onclick="openExternal('https://r0csgo.com/', true);" href="javascript:;">点击下载</a>
</div>
<!-- 版本信息 -->
<div class="version-info">v2.0</div>
</div>
</body>
<script>
const { ipcRenderer } = require('electron');
const fs = require('fs');
const path = require('path');
const axios = require('axios');
const StreamZip = require('node-stream-zip');
const crypto = require('crypto');
const cmd = require('node-cmd');
const regedit = require('regedit');
// 设置外部VBS路径
const getVbsDirectory = async () => {
const exePath = await ipcRenderer.invoke('app-get-path', 'exe');
return path.join(path.dirname(exePath), './resources/vbs');
};
// 异步初始化vbs目录
getVbsDirectory().then(vbsDirectory => {
regedit.setExternalVBSLocation(vbsDirectory);
});
// 运行库下载地址
const VC_REDISTRIB_PACKAGE_X86_URL = 'https://cdn.r0csgo.com/runtime/VC_redist.x86.exe';
const VC_REDISTRIB_PACKAGE_X64_URL = 'https://cdn.r0csgo.com/runtime/VC_redist.x64.exe';
const DOTNET_SDK_X64_URL = 'https://cdn.r0csgo.com/runtime/windowsdesktop-runtime-8.0.21-win-x64.exe';
const DOTNET_SDK_X86_URL = 'https://cdn.r0csgo.com/runtime/windowsdesktop-runtime-8.0.21-win-x86.exe';
const ASPNET_CORE_X64_URL = 'https://cdn.r0csgo.com/runtime/aspnetcore-runtime-8.0.21-win-x64.exe';
const ASPNET_CORE_X86_URL = 'https://cdn.r0csgo.com/runtime/aspnetcore-runtime-8.0.21-win-x86.exe';
setTimeout(() => {
//页面停留超过60秒,太慢了
var slowEl = document.getElementById('slow');
slowEl.style.opacity = '1';
slowEl.style.pointerEvents = 'auto';
}, 60000);
function openExternal(url, close = false) {
require('electron').shell.openExternal(url);
if (close) {
setTimeout(() => {
require('electron').ipcRenderer.invoke('app-quit');
}, 2000);
}
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
// 检测VC运行库是否已安装
function checkVCRedistributable() {
return new Promise((resolve) => {
// VC运行库注册表键值
const vcKeys = [
'HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x86',
'HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\15.0\\VC\\Runtimes\\x86',
'HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\16.0\\VC\\Runtimes\\x86',
'HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\17.0\\VC\\Runtimes\\x86',
'HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x64',
'HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\15.0\\VC\\Runtimes\\x64',
'HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\16.0\\VC\\Runtimes\\x64',
'HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\17.0\\VC\\Runtimes\\x64'
];
regedit.list(vcKeys, (err, result) => {
if (err) {
console.log('检测VC运行库时出错:', err);
resolve(false);
return;
}
// 检查是否有任何一个键存在且已安装
for (const key of vcKeys) {
if (result[key] && result[key].exists && result[key].values && result[key].values.Installed) {
const installed = result[key].values.Installed.value;
if (installed === 1) {
console.log('找到已安装的VC运行库:', key);
resolve(true);
return;
}
}
}
console.log('未找到已安装的VC运行库');
resolve(false);
});
});
}
// 检测.NET运行库是否已安装
function checkDotNetRuntime() {
return new Promise((resolve) => {
// 使用node-cmd执行dotnet --list-runtimes命令
cmd.run('dotnet --list-runtimes', (err, data, stderr) => {
if (err || stderr) {
console.log('检测.NET运行库时出错:', err || stderr);
resolve(false);
return;
}
// 检查输出中是否包含8.0.21版本的Microsoft.WindowsDesktop.App
const lines = data.split('\n');
for (const line of lines) {
if (line.includes('Microsoft.WindowsDesktop.App') && line.includes('8.0.21')) {
console.log('找到已安装的.NET运行库:', line.trim());
resolve(true);
return;
}
}
console.log('未找到.NET WindowsDesktop 8.0.21运行库');
resolve(false);
});
});
}
// 检测ASP.NET Core运行库是否已安装
function checkAspNetCoreRuntime() {
return new Promise((resolve) => {
// 使用node-cmd执行dotnet --list-runtimes命令
cmd.run('dotnet --list-runtimes', (err, data, stderr) => {
if (err || stderr) {
console.log('检测ASP.NET Core运行库时出错:', err || stderr);
resolve(false);
return;
}
// 检查输出中是否包含8.0.21版本的Microsoft.AspNetCore.App
const lines = data.split('\n');
for (const line of lines) {
if (line.includes('Microsoft.AspNetCore.App') && line.includes('8.0.21')) {
console.log('找到已安装的ASP.NET Core运行库:', line.trim());
resolve(true);
return;
}
}
console.log('未找到ASP.NET Core 8.0.21运行库');
resolve(false);
});
});
}
// 获取系统架构
function getSystemArchitecture() {
return process.arch === 'x64' ? 'x64' : 'x86';
}
// 下载运行库
async function downloadRuntime(url, fileName) {
return new Promise((resolve, reject) => {
const request = require('request');
const progress = require('request-progress');
progress(request(url), {})
.on('progress', function(state) {
const percentCompleted = Math.floor(state.percent * 100);
updateProgress(percentCompleted, `正在下载运行库 ${percentCompleted}%`);
})
.on('error', function(err) {
reject(err);
})
.on('end', function() {
setTimeout(()=>{
resolve();//防止EBUSY,下载完等一会再安装
}, 500);
})
.pipe(fs.createWriteStream(fileName));
});
}
// 安装运行库
async function installRuntime(fileName, runtimeName) {
return new Promise((resolve, reject) => {
document.getElementById("text-tip").innerHTML = `正在安装${runtimeName}...`;
const { spawn } = require('child_process');
// 使用静默安装参数
const installer = spawn(fileName, ['/install', '/passive', '/norestart'], {
stdio: ['ignore', 'pipe', 'pipe']
});
let output = '';
let errorOutput = '';
installer.stdout.on('data', (data) => {
output += data.toString();
});
installer.stderr.on('data', (data) => {
errorOutput += data.toString();
});
installer.on('close', (code) => {
console.log(`${runtimeName}安装完成,退出代码: ${code}`);
console.log('安装输出:', output);
if (errorOutput) {
console.log('安装错误输出:', errorOutput);
}
// 删除安装包文件
try {
if (fs.existsSync(fileName)) {
fs.unlinkSync(fileName);
}
} catch (deleteError) {
console.log('删除安装包失败:', deleteError);
}
if (code === 0 || code === 3010) { // 0表示成功,3010表示需要重启但安装成功
resolve(true);
} else {
reject(new Error(`${runtimeName}安装失败,退出代码: ${code}`));
}
});
installer.on('error', (error) => {
console.error(`${runtimeName}安装进程启动失败:`, error);
reject(error);
});
});
}
// 安装VC运行库
async function installVCRedistributable() {
const arch = getSystemArchitecture();
const url = arch === 'x64' ? VC_REDISTRIB_PACKAGE_X64_URL : VC_REDISTRIB_PACKAGE_X86_URL;
const fileName = `VC_redist.${arch}.exe`;
try {
await downloadRuntime(url, fileName);
await installRuntime(fileName, 'VC++运行库');
return true;
} catch (error) {
console.error('VC运行库安装失败:', error);
return false;
}
}
// 安装.NET运行库
async function installDotNetRuntime() {
const arch = getSystemArchitecture();
const url = arch === 'x64' ? DOTNET_SDK_X64_URL : DOTNET_SDK_X86_URL;
const fileName = `windowsdesktop-runtime-8.0.21-win-${arch}.exe`;
try {
await downloadRuntime(url, fileName);
await installRuntime(fileName, '.NET运行库');
return true;
} catch (error) {
console.error('.NET运行库安装失败:', error);
return false;
}
}
// 安装ASP.NET Core运行库
async function installAspNetCoreRuntime() {
const arch = getSystemArchitecture();
const url = arch === 'x64' ? ASPNET_CORE_X64_URL : ASPNET_CORE_X86_URL;
const fileName = `aspnetcore-runtime-8.0.21-win-${arch}.exe`;
try {
await downloadRuntime(url, fileName);
await installRuntime(fileName, 'ASP.NET Core运行库');
return true;
} catch (error) {
console.error('ASP.NET Core运行库安装失败:', error);
return false;
}
}
// 检查并安装必要的运行环境
async function checkAndInstallRuntimeEnvironments() {
try {
document.getElementById("text-tip").innerHTML = "正在检测运行环境...";
document.getElementById("progress").style.width = "5%";
// 检测VC运行库
const vcInstalled = await checkVCRedistributable();
document.getElementById("progress").style.width = "20%";
// 检测.NET运行库
const dotnetInstalled = await checkDotNetRuntime();
document.getElementById("progress").style.width = "35%";
// 检测ASP.NET Core运行库
const aspnetInstalled = await checkAspNetCoreRuntime();
document.getElementById("progress").style.width = "50%";
let installationNeeded = false;
// 如果VC运行库未安装,则安装
if (!vcInstalled) {
console.log('VC运行库未安装,开始安装...');
document.getElementById("text-tip").innerHTML = "正在安装VC++运行库...";
const vcInstallSuccess = await installVCRedistributable();
if (!vcInstallSuccess) {
console.error('VC运行库安装失败');
document.getElementById("text-tip").innerHTML = "VC++运行库安装失败,但程序将继续运行...";
await sleep(2000);
}
installationNeeded = true;
document.getElementById("progress").style.width = "65%";
} else {
console.log('VC运行库已安装');
document.getElementById("progress").style.width = "65%";
}
// 如果.NET运行库未安装,则安装
if (!dotnetInstalled) {
console.log('.NET运行库未安装,开始安装...');
document.getElementById("text-tip").innerHTML = "正在安装.NET运行库...";
const dotnetInstallSuccess = await installDotNetRuntime();
if (!dotnetInstallSuccess) {
console.error('.NET运行库安装失败');
document.getElementById("text-tip").innerHTML = ".NET运行库安装失败,但程序将继续运行...";
await sleep(2000);
}
installationNeeded = true;
document.getElementById("progress").style.width = "80%";
} else {
console.log('.NET运行库已安装');
document.getElementById("progress").style.width = "80%";
}
// 如果ASP.NET Core运行库未安装,则安装
if (!aspnetInstalled) {
console.log('ASP.NET Core运行库未安装,开始安装...');
document.getElementById("text-tip").innerHTML = "正在安装ASP.NET Core运行库...";
const aspnetInstallSuccess = await installAspNetCoreRuntime();
if (!aspnetInstallSuccess) {
console.error('ASP.NET Core运行库安装失败');
document.getElementById("text-tip").innerHTML = "ASP.NET Core运行库安装失败,但程序将继续运行...";
await sleep(2000);
}
installationNeeded = true;
} else {
console.log('ASP.NET Core运行库已安装');
}
document.getElementById("progress").style.width = "100%";
if (installationNeeded) {
document.getElementById("text-tip").innerHTML = "运行环境安装完成";
await sleep(1000);
} else {
document.getElementById("text-tip").innerHTML = "运行环境检测完成";
await sleep(500);
}
return true;
} catch (error) {
console.error('运行环境检测失败:', error);
document.getElementById("text-tip").innerHTML = "运行环境检测失败,程序将继续运行...";
await sleep(2000);
return false;
}
}
// 计算文件hash
function calculateFileHash(filePath) {
return new Promise((resolve, reject) => {
const hash = crypto.createHash('md5');
const stream = fs.createReadStream(filePath);
stream.on('error', reject);
stream.on('data', chunk => hash.update(chunk));
stream.on('end', () => resolve(hash.digest('hex')));
});
}
// 解压business.asar
async function extractBusinessAsar() {
const asarPath = './resources/business.asar';
const extractPath = './business_temp';
if (fs.existsSync(extractPath)) {
fs.rmSync(extractPath, { recursive: true, force: true });
}
fs.mkdirSync(extractPath, { recursive: true });
const asar = require('asar');
await asar.extractAll(asarPath, extractPath);
return extractPath;
}
// 重新打包business.asar
async function packBusinessAsar(sourcePath) {
const asarPath = './resources/business.asar';
const asar = require('asar');
await asar.createPackage(sourcePath, asarPath);
}
// 应用增量更新
async function applyIncrementalUpdate(updatePath, manifest) {
const businessPath = await extractBusinessAsar();
for (const file of manifest.files) {
const targetPath = path.join(businessPath, file.path);
const sourcePath = path.join(updatePath, file.path);
switch (file.action) {
case 'add':
case 'modify':
// 确保目标目录存在
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
fs.copyFileSync(sourcePath, targetPath);
break;
case 'delete':
if (fs.existsSync(targetPath)) {
fs.unlinkSync(targetPath);
}
break;
}
}
await packBusinessAsar(businessPath);
// 清理临时文件
fs.rmSync(businessPath, { recursive: true, force: true });
fs.rmSync(updatePath, { recursive: true, force: true });
}
// 应用多个增量更新(优化版本:只解压和打包business.asar一次)
async function applyMultipleIncrementalUpdates(extractedUpdates) {
// 只解压business.asar一次
const businessPath = await extractBusinessAsar();
// 按版本顺序依次应用所有更新
for (let i = 0; i < extractedUpdates.length; i++) {
const { update, extractPath, manifest } = extractedUpdates[i];
console.log(`应用更新: ${update.from_version} -> ${update.to_version}`);
// 应用当前更新包的所有文件变更
for (const file of manifest.files) {
const targetPath = path.join(businessPath, file.path);
const sourcePath = path.join(extractPath, file.path);
switch (file.action) {
case 'add':
case 'modify':
// 确保目标目录存在
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
if (fs.existsSync(sourcePath)) {
fs.copyFileSync(sourcePath, targetPath);
} else {
console.warn(`源文件不存在: ${sourcePath}`);
}
break;
case 'delete':
if (fs.existsSync(targetPath)) {
fs.unlinkSync(targetPath);
}
break;
}
}
}
// 只重新打包business.asar一次
await packBusinessAsar(businessPath);
// 清理business临时目录
fs.rmSync(businessPath, { recursive: true, force: true });
}
// 下载增量更新包
async function downloadIncrementalUpdate(downloadUrl, fileName) {
return new Promise((resolve, reject) => {
const request = require('request');
const progress = require('request-progress');
progress(request(downloadUrl), {})
.on('progress', function(state) {
const percentCompleted = Math.floor(state.percent * 100);
updateProgress(percentCompleted, `正在下载更新包 ${percentCompleted}%`);
})
.on('error', function(err) {
reject(err);
})
.on('end', function() {
resolve();
})
.pipe(fs.createWriteStream(fileName));
});
}
// 下载全量更新包(exe安装包)
async function downloadFullUpdate(downloadUrl, fileName) {
return new Promise((resolve, reject) => {
const request = require('request');
const progress = require('request-progress');
progress(request(downloadUrl), {})
.on('progress', function(state) {
const percentCompleted = Math.floor(state.percent * 100);
const speedMBps = Math.floor(state.speed / 1024 / 1024);
updateProgress(percentCompleted, `正在下载完整安装包 ${percentCompleted}% - ${speedMBps}MB/s`);
})
.on('error', function(err) {
reject(err);
})
.on('end', function() {
resolve();
})
.pipe(fs.createWriteStream(fileName));
});
}
// 安装全量更新
async function installFullUpdate(fileName) {
try {
document.getElementById("text-tip").innerHTML = "正在启动安装程序...";
const { spawn } = require('child_process');
// 启动安装程序,使用静默安装参数
const installer = spawn(fileName, ['/SILENT'], {
detached: true,
stdio: 'ignore'
});
installer.unref();
document.getElementById("text-tip").innerHTML = "安装程序已启动,即将退出当前版本...";
await sleep(2000);
// 监控安装程序是否启动
const cmd = require("node-cmd");
let installationStarted = false;
let checkCount = 0;
const maxChecks = 10;
const checkInstaller = () => {
return new Promise((resolve) => {
// 检查安装程序进程是否存在
const execs = require('child_process').exec;
execs(`tasklist /FI "IMAGENAME eq ${fileName}" /FO CSV /NH`, (err, stdout, stderr) => {
if (!err && stdout.includes(fileName)) {
resolve(true);
} else {
resolve(false);
}
});
});
};
// 循环检查安装程序是否启动
while (checkCount < maxChecks && !installationStarted) {
installationStarted = await checkInstaller();
if (!installationStarted) {
await sleep(1000);
checkCount++;
}
}
if (installationStarted) {
document.getElementById("text-tip").innerHTML = "安装程序运行中,正在退出应用...";
await sleep(1000);
// 退出当前应用
ipcRenderer.send('quit-app');
} else {
document.getElementById("text-tip").innerHTML = "安装程序启动失败,请手动运行安装包";
await sleep(3000);
// 显示慢速下载提示
slow();
}
} catch (error) {
console.error("全量更新安装失败:", error);
document.getElementById("text-tip").innerHTML = "安装失败,请手动运行安装包";
await sleep(3000);
slow();
}
}
// 解压增量更新包
async function extractIncrementalUpdate(zipPath) {
const extractPath = './update_temp';
if (fs.existsSync(extractPath)) {
fs.rmSync(extractPath, { recursive: true, force: true });
}
fs.mkdirSync(extractPath, { recursive: true });
const zip = new StreamZip.async({ file: zipPath });
await zip.extract(null, extractPath);
await zip.close();
// 读取manifest.json
const manifestPath = path.join(extractPath, 'manifest.json');
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
return { extractPath, manifest };
}
// 处理多个增量更新(优化版本:批量下载,统一处理)
async function processMultipleUpdates(updates) {
const downloadedUpdates = [];
// 第一阶段:批量下载所有更新包
document.getElementById("text-tip").innerHTML = `正在下载 ${updates.length} 个更新包...`;
for (let i = 0; i < updates.length; i++) {
const update = updates[i];
const fileName = `update_${update.to_version}.zip`;
updateProgress(Math.floor((i + 1) / updates.length * 50), `正在下载更新包 ${i + 1}/${updates.length} (${update.to_version})`);
await downloadIncrementalUpdate(update.download_url, fileName);
downloadedUpdates.push({
update,
fileName,
zipPath: `./${fileName}`
});
}
// 第二阶段:解压所有更新包
updateProgress(60, "正在解压更新包...");
const extractedUpdates = [];
for (let i = 0; i < downloadedUpdates.length; i++) {
const downloadedUpdate = downloadedUpdates[i];
const { extractPath, manifest } = await extractIncrementalUpdate(downloadedUpdate.zipPath);
extractedUpdates.push({
update: downloadedUpdate.update,
extractPath,
manifest,
zipPath: downloadedUpdate.zipPath
});
}
// 第三阶段:统一应用所有更新(只解压和打包business.asar一次)
updateProgress(80, "正在应用所有更新...");
await applyMultipleIncrementalUpdates(extractedUpdates);
// 第四阶段:清理所有临时文件
updateProgress(95, "正在清理临时文件...");
for (const extractedUpdate of extractedUpdates) {
// 清理解压目录
if (fs.existsSync(extractedUpdate.extractPath)) {
fs.rmSync(extractedUpdate.extractPath, { recursive: true, force: true });
}
// 清理下载的zip文件
if (fs.existsSync(extractedUpdate.zipPath)) {
fs.unlinkSync(extractedUpdate.zipPath);
}
}
}
// 获取business.asar内的版本号
async function getBusinessVersion() {
try {
const businessAsarPath = './resources/business.asar';
// 检查business.asar是否存在
if (!fs.existsSync(businessAsarPath)) {
console.log('business.asar不存在,使用主应用版本');
return await ipcRenderer.invoke('get-app-version');
}
const asar = require('asar');
// 从business.asar中读取package.json
try {
const packageJsonContent = asar.extractFile(businessAsarPath, 'package.json');
const businessPackageJson = JSON.parse(packageJsonContent.toString());
return businessPackageJson.version;
} catch (error) {
console.log('无法从business.asar读取版本,使用主应用版本:', error);
return await ipcRenderer.invoke('get-app-version');
}
} catch (error) {
console.log('获取business版本失败,使用主应用版本:', error);
return await ipcRenderer.invoke('get-app-version');
}
}
// 增量更新检测和处理
async function checkIncrementalUpdate() {
try {
const version = await getBusinessVersion();
// 使用request替代axios进行API调用
const request = require('request');
const response = await new Promise((resolve, reject) => {
request(`https://a-p-i.r0csgo.com/v2/update/check?current_version=${version}`, (error, response, body) => {
if (error) {
reject(error);
} else {
try {
resolve(JSON.parse(body));
} catch (e) {
reject(e);
}
}
});
});
const data = response;
console.log("更新检测结果:", data);
if (!data.has_update) {
// 无更新,检测运行环境后启动应用
document.getElementById("text-tip").innerHTML = "当前版本最新,正在检测运行环境...";
await checkAndInstallRuntimeEnvironments();
document.getElementById("text-tip").innerHTML = "正在启动应用...";
await sleep(1000);
await startApplication();
return;
}
if (data.update_type === 'incremental') {
// 单个增量更新
document.getElementById("text-tip").innerHTML = `检测到新版本 ${data.latest_version},正在下载增量更新...`;
const fileName = `update_${data.latest_version}.zip`;
await downloadIncrementalUpdate(data.download_url, fileName);
const { extractPath, manifest } = await extractIncrementalUpdate(fileName);
await applyIncrementalUpdate(extractPath, manifest);
// 清理下载的zip文件
fs.unlinkSync(fileName);
document.getElementById("text-tip").innerHTML = "增量更新完成,正在检测运行环境...";
await checkAndInstallRuntimeEnvironments();
document.getElementById("text-tip").innerHTML = "正在加载应用...";
await sleep(1000);
// 直接启动应用,无需重启
await startApplication();
} else if (data.update_type === 'multi_incremental') {
// 多个增量更新
document.getElementById("text-tip").innerHTML = `检测到新版本 ${data.latest_version},需要应用 ${data.incremental_updates.length} 个增量更新...`;
await processMultipleUpdates(data.incremental_updates);
document.getElementById("text-tip").innerHTML = "所有增量更新完成,正在检测运行环境...";
await checkAndInstallRuntimeEnvironments();
document.getElementById("text-tip").innerHTML = "正在加载应用...";
await sleep(1000);
// 直接启动应用,无需重启
await startApplication();
} else if (data.update_type === 'full') {
// 全量更新 - 下载完整安装包
document.getElementById("text-tip").innerHTML = `检测到新版本 ${data.latest_version},正在下载完整安装包...`;
const fileName = `r0_arena_${data.latest_version}.exe`;
await downloadFullUpdate(data.download_url, fileName);
document.getElementById("text-tip").innerHTML = "下载完成,正在安装新版本...";
await sleep(1000);
// 运行全量更新安装
await installFullUpdate(fileName);
}
} catch (error) {
console.error("增量更新检测失败:", error);
document.getElementById("text-tip").innerHTML = "更新检测失败,正在检测运行环境...";
await checkAndInstallRuntimeEnvironments();
document.getElementById("text-tip").innerHTML = "使用当前版本启动...";
await sleep(1000);
await startApplication();
}
}
// 启动应用
async function startApplication() {
updateProgress(100, "加载完成,正在启动...");
// 添加完成效果
setTimeout(() => {
const progress = document.getElementById("progress");
if (progress) {
progress.classList.add('complete');
}
}, 100);
// 淡出动画后跳转
setTimeout(() => {
fadeOutAndNavigate(async () => {
// 检测是否为开发环境
const isDev = await ipcRenderer.invoke('is-dev-environment');
// 根据环境设置不同的窗口大小
if (isDev) {
await ipcRenderer.invoke('window-set-size', 1451, 750);
} else {
await ipcRenderer.invoke('window-set-size', 1200, 750);
}
await ipcRenderer.invoke('window-center');
// 获取端口信息
const ports = await ipcRenderer.invoke('get-ports');
window.location.href = `http://127.0.0.1:${ports.pagePort}/signin`;
});
}, 500);
}
// 启动检测
console.log("开始增量更新检测");
// 初始化界面
async function initUI() {
// 获取并显示版本号
try {
const version = await getBusinessVersion();
const versionElement = document.querySelector('.version-info');
if (versionElement) {
versionElement.textContent = `v${version}`;
}
} catch (error) {
console.error('获取版本号失败:', error);
}
// 添加初始动画效果
setTimeout(() => {
const progressBar = document.getElementById("progress");
if (progressBar) {
progressBar.style.width = "5%";
}
}, 500);
}
// 页面加载完成后初始化
window.addEventListener('DOMContentLoaded', () => {
initUI();
setTimeout(() => {
checkIncrementalUpdate();
}, 100);
});
function slow() {
var slowEl = document.getElementById("slow");
slowEl.style.opacity = '1';
slowEl.style.pointerEvents = 'auto';
}
// 优化进度条更新函数
function updateProgress(percent, message) {
const progress = document.getElementById("progress");
const textTip = document.getElementById("text-tip");
const textProgress = document.getElementById("text-progress");
if (progress) {
progress.style.width = percent + "%";
// 当进度达到100%时添加完成效果
if (percent >= 100) {
progress.classList.add('complete');
}
}
if (textTip && message) {
textTip.innerHTML = message;
textTip.classList.add('loading');
}
if (textProgress && percent > 0) {
textProgress.innerHTML = ` ${percent}%`;
}
}
// 添加页面淡出效果
function fadeOutAndNavigate(callback) {
const box = document.querySelector('.box');
if (box) {
box.classList.add('fade-out');
setTimeout(callback, 500);
} else {
callback();
}
}
</script>
</html>