Files
maimailunacher_old/C++/2.2.6/mainwindow.cpp
2025-10-21 23:47:29 +08:00

1555 lines
51 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "mainwindow.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QProgressBar>
#include <QTextEdit>
#include <QLineEdit>
#include <QCheckBox>
#include <QMessageBox>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QJsonDocument>
#include <QJsonArray>
#include <QFile>
#include <QDir>
#include <QProcess>
#include <QDesktopServices>
#include <QUrl>
#include <QSystemTrayIcon>
#include <QMenu>
#include <QCloseEvent>
#include <QThread>
#include <QSslConfiguration>
#include <QSslSocket>
#include <QUuid>
#include <QCryptographicHash>
#include <QDateTime>
#include <QFuture>
#include <QtConcurrent>
#include <QDebug>
#include <QGuiApplication>
#include <QScreen>
#include <QFileDialog>
#include <QTimer>
#include <QUrlQuery>
#include <QRegularExpression>
#include <windows.h>
#include <sddl.h>
#include <winreg.h>
const QString UPDATE_F_VERSION_FILE = "";
const QString DATA_DIR = "";
void ensureDataDirExists()
{
QString dataDir = "";
QDir dDrive("");
// 检查D盘是否存在
if (!dDrive.exists()) {
dataDir = "";
}
QDir dir(dataDir);
if (!dir.exists()) {
dir.mkpath(".");
}
const wchar_t* path = reinterpret_cast<const wchar_t*>(dataDir.utf16());
DWORD attributes = GetFileAttributesW(path);
if (attributes != INVALID_FILE_ATTRIBUTES) {
SetFileAttributesW(path, attributes | FILE_ATTRIBUTE_HIDDEN);
}
}
AuthWindow::AuthWindow(const QString &deviceId, const QString &savedKami, QWidget *parent)
: QDialog(parent)
{
setWindowTitle("卡密验证");
setFixedSize(400, 250);
setWindowModality(Qt::ApplicationModal);
QVBoxLayout *layout = new QVBoxLayout(this);
layout->setContentsMargins(20, 20, 20, 20);
layout->setSpacing(15);
QLabel *deviceLabel = new QLabel("设备ID: " + deviceId);
deviceLabel->setStyleSheet("font-size: 10pt;");
layout->addWidget(deviceLabel);
QLabel *kamiLabel = new QLabel("卡密:");
layout->addWidget(kamiLabel);
kamiEntry = new QLineEdit();
kamiEntry->setPlaceholderText("请输入您的卡密");
if (!savedKami.isEmpty()) {
kamiEntry->setText(savedKami);
}
layout->addWidget(kamiEntry);
rememberCheck = new QCheckBox("记住卡密");
rememberCheck->setChecked(true);
layout->addWidget(rememberCheck);
QHBoxLayout *btnLayout = new QHBoxLayout();
QPushButton *okBtn = new QPushButton("验证");
connect(okBtn, &QPushButton::clicked, this, &QDialog::accept);
btnLayout->addWidget(okBtn);
QPushButton *cancelBtn = new QPushButton("取消");
connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject);
btnLayout->addWidget(cancelBtn);
layout->addLayout(btnLayout);
}
QString AuthWindow::getKami() const
{
return kamiEntry->text().trimmed();
}
bool AuthWindow::getRemember() const
{
return rememberCheck->isChecked();
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, m_isFirstUpdateInProgress(false) // 先初始化这个
, gameProcess(nullptr) // 然后初始化这个
{
// 检测是否是首次启动
QDir dataDir(DATA_DIR);
isFirstLaunch = !dataDir.exists();
ensureDataDirExists();
settings = new QSettings("GameStudio", "maimaiLauncher", this);
loadSettings();
SERVER_URL = "";
VERSION_FILE = "";
UPDATE_ZIP = "";
ANNOUNCEMENT_FILE = "";
DEVICE_CODE_FILE = DATA_DIR + "";
CARD_FILE = DATA_DIR + "";
deviceId = getDeviceId();
savedKami = loadSavedKami();
setupUI();
checkAdminRights();
// 加载本地版本信息
loadLocalVersion();
checkPackageExists();
disableButtons();
if (!savedKami.isEmpty()) {
authStatus->setText("使用保存的卡密验证中...");
QTimer::singleShot(100, this, [this]() {
performNetworkAuthentication(savedKami, true);
checkAndDeleteFiles(); // 添加删除检查
});
} else {
authStatus->setText("等待卡密验证");
QTimer::singleShot(100, this, &MainWindow::showAuthWindow);
}
fetchAnnouncement();
quitTimer = new QTimer(this);
quitTimer->setSingleShot(true);
connect(quitTimer, &QTimer::timeout, this, &MainWindow::quitApplication);
}
MainWindow::~MainWindow()
{
saveSettings();
delete settings;
}
void MainWindow::loadLocalVersion()
{
QString versionFilePath = UPDATE_PATH + "/" + VERSION_FILE;
QFile file(versionFilePath);
if (file.exists() && file.open(QIODevice::ReadOnly)) {
QByteArray data = file.readAll();
file.close();
QJsonDocument doc = QJsonDocument::fromJson(data);
if (!doc.isNull() && doc.isObject()) {
localVersion = doc.object();
QString ver = localVersion["version"].toString();
versionLabel->setText("版本: v" + ver);
qDebug() << "加载本地版本: v" << ver;
} else {
versionLabel->setText("版本: 文件损坏");
qDebug() << "版本文件损坏";
}
} else {
// 如果版本文件不存在,创建初始版本
localVersion = QJsonObject();
localVersion["version"] = "0.0.0";
saveLocalVersion();
versionLabel->setText("版本: 未安装");
qDebug() << "创建初始版本文件";
}
}
void MainWindow::setupUI()
{
setWindowTitle("maimai启动器 v" + LAUNCHER_VERSION);
setFixedSize(800, 600);
QWidget *centralWidget = new QWidget(this);
QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget);
QWidget *pathWidget = new QWidget();
QHBoxLayout *pathLayout = new QHBoxLayout(pathWidget);
pathLayout->setContentsMargins(10, 5, 10, 5);
QLabel *pathTitle = new QLabel("Package路径:");
pathLabel = new QLabel(UPDATE_PATH);
pathLabel->setStyleSheet("background-color: #f0f0f0; border: 1px solid #ccc; padding: 3px;");
pathLabel->setMinimumWidth(300);
pathSelectBtn = new QPushButton("选择路径");
pathSelectBtn->setFixedSize(80, 25);
connect(pathSelectBtn, &QPushButton::clicked, this, &MainWindow::selectPackagePath);
pathLayout->addWidget(pathTitle);
pathLayout->addWidget(pathLabel, 1);
pathLayout->addWidget(pathSelectBtn);
mainLayout->addWidget(pathWidget);
QWidget *contentWidget = new QWidget();
QHBoxLayout *contentLayout = new QHBoxLayout(contentWidget);
QWidget *leftWidget = new QWidget();
QVBoxLayout *leftLayout = new QVBoxLayout(leftWidget);
leftLayout->setContentsMargins(10, 10, 10, 10);
QLabel *titleLabel = new QLabel("maimai启动器");
titleLabel->setStyleSheet("font-size: 16pt; font-weight: bold;");
leftLayout->addWidget(titleLabel, 0, Qt::AlignCenter);
QLabel *adminLabel = new QLabel("(已获得管理员权限)");
adminLabel->setStyleSheet("color: green; font-size: 8pt;");
leftLayout->addWidget(adminLabel, 0, Qt::AlignCenter);
authStatus = new QLabel("验证状态: 正在初始化...");
authStatus->setStyleSheet("color: blue; font-weight: bold;");
leftLayout->addWidget(authStatus, 0, Qt::AlignCenter);
vipInfo = new QLabel("VIP状态: 未验证");
vipInfo->setStyleSheet("color: purple;");
leftLayout->addWidget(vipInfo, 0, Qt::AlignCenter);
versionLabel = new QLabel("版本: 加载中...");
leftLayout->addWidget(versionLabel, 0, Qt::AlignCenter);
progressBar = new QProgressBar();
progressBar->setFixedHeight(20);
leftLayout->addWidget(progressBar);
statusLabel = new QLabel("等待验证...");
leftLayout->addWidget(statusLabel, 0, Qt::AlignCenter);
QWidget *buttonWidget = new QWidget();
QVBoxLayout *buttonLayout = new QVBoxLayout(buttonWidget);
QHBoxLayout *row1 = new QHBoxLayout();
startBtn = new QPushButton("启动游戏");
startBtn->setFixedSize(120, 35);
startBtn->setEnabled(false);
connect(startBtn, &QPushButton::clicked, this, &MainWindow::startGame);
row1->addWidget(startBtn);
oddBtn = new QPushButton("启动ODD");
oddBtn->setFixedSize(120, 35);
oddBtn->setEnabled(false);
connect(oddBtn, &QPushButton::clicked, this, &MainWindow::startOdd);
row1->addWidget(oddBtn);
buttonLayout->addLayout(row1);
QHBoxLayout *row2 = new QHBoxLayout();
updateBtn = new QPushButton("更新");
updateBtn->setFixedSize(120, 35);
updateBtn->setEnabled(false);
connect(updateBtn, &QPushButton::clicked, this, &MainWindow::forceUpdate);
row2->addWidget(updateBtn);
hostsBtn = new QPushButton("修改hosts");
hostsBtn->setFixedSize(120, 35);
hostsBtn->setEnabled(false);
connect(hostsBtn, &QPushButton::clicked, this, &MainWindow::modifyHosts);
row2->addWidget(hostsBtn);
buttonLayout->addLayout(row2);
// 修复:将"更新完整包"按钮添加到row3
QHBoxLayout *row3 = new QHBoxLayout();
buyBtn = new QPushButton("购买卡密");
buyBtn->setFixedSize(120, 35);
connect(buyBtn, &QPushButton::clicked, this, &MainWindow::openBuyPage);
row3->addWidget(buyBtn);
fullUpdateBtn = new QPushButton("更新完整包");
fullUpdateBtn->setFixedSize(120, 35);
connect(fullUpdateBtn, &QPushButton::clicked, this, &MainWindow::forceFullUpdate);
row3->addWidget(fullUpdateBtn);
buttonLayout->addLayout(row3);
QHBoxLayout *row4 = new QHBoxLayout();
wikiBtn = new QPushButton("wiki文档");
wikiBtn->setFixedSize(120, 35);
connect(wikiBtn, &QPushButton::clicked, this, &MainWindow::openWikiPage);
row4->addWidget(wikiBtn);
// 添加Bug报告按钮
bugReportBtn = new QPushButton("反馈Bug");
bugReportBtn->setFixedSize(120, 35);
connect(bugReportBtn, &QPushButton::clicked, this, &MainWindow::reportBug);
row4->addWidget(bugReportBtn);
buttonLayout->addLayout(row4);
leftLayout->addWidget(buttonWidget);
QGroupBox *rightGroup = new QGroupBox("最新公告");
rightGroup->setStyleSheet("QGroupBox { font-weight: bold; }");
QVBoxLayout *rightLayout = new QVBoxLayout(rightGroup);
announcementText = new QTextEdit();
announcementText->setReadOnly(true);
announcementText->setText("正在加载公告...");
announcementText->setStyleSheet("font-size: 10pt;");
rightLayout->addWidget(announcementText);
contentLayout->addWidget(leftWidget, 2);
contentLayout->addWidget(rightGroup, 1);
mainLayout->addWidget(contentWidget, 1);
QLabel *footerLabel = new QLabel("闲鱼小xin喵");
footerLabel->setStyleSheet("color: gray; font-size: 8pt;");
mainLayout->addWidget(footerLabel, 0, Qt::AlignRight | Qt::AlignBottom);
setCentralWidget(centralWidget);
networkManager = new QNetworkAccessManager(this);
}
void MainWindow::forceFullUpdate()
{
if (!isAuthenticated) {
QMessageBox::warning(this, "未验证", "请先完成卡密验证");
return;
}
// 确认用户操作
if (QMessageBox::question(this, "更新完整包",
"确定要下载并安装完整游戏包吗?\n这将覆盖所有本地文件。",
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
return;
}
// 禁用相关按钮
fullUpdateBtn->setEnabled(false);
startBtn->setEnabled(false);
statusLabel->setText("开始下载完整游戏包...");
// 调用首次更新函数(该函数已实现完整包下载)
fetchFirstUpdateVersion();
}
void MainWindow::updateAnnouncement(const QJsonObject &announcement)
{
QString title = announcement["title"].toString("公告");
QString date = announcement["date"].toString(QDate::currentDate().toString("yyyy-MM-dd"));
QString content = announcement["content"].toString("暂无公告内容。");
announcementText->clear();
announcementText->append(QString("<div style='color: blue; font-size: 12pt; font-weight: bold;'>%1</div>").arg(title));
announcementText->append(QString("<div style='color: blue;'>发布日期: %1</div>").arg(date));
announcementText->append("<hr>");
announcementText->append(QString("<div style='font-size: 10pt;'>%1</div>").arg(content));
}
void MainWindow::reportBug()
{
// 创建邮件主题和正文
QString subject = QString("maimai启动器Bug报告 (v%1)").arg(LAUNCHER_VERSION);
QString body = QString("设备ID: %1\n\n请描述您遇到的问题:\n").arg(deviceId);
// 创建mailto链接
QString mailto = QString("mailto:2932869213@qq.com?subject=%1&body=%2")
.arg(QString(QUrl::toPercentEncoding(subject)))
.arg(QString(QUrl::toPercentEncoding(body)));
// 打开默认邮件客户端
if (!QDesktopServices::openUrl(QUrl(mailto))) {
QMessageBox::warning(this, "错误", "无法打开邮件客户端。请确保已安装邮件程序。");
}
}
void MainWindow::activateButtons()
{
if (isAuthenticated) {
startBtn->setEnabled(true);
oddBtn->setEnabled(true);
updateBtn->setEnabled(true);
hostsBtn->setEnabled(true);
fullUpdateBtn->setEnabled(true);
}
buyBtn->setEnabled(true);
pathSelectBtn->setEnabled(true);
wikiBtn->setEnabled(true);
}
void MainWindow::disableButtons()
{
startBtn->setEnabled(false);
oddBtn->setEnabled(false);
updateBtn->setEnabled(false);
hostsBtn->setEnabled(false);
fullUpdateBtn->setEnabled(false);
wikiBtn->setEnabled(false);
}
void MainWindow::openWikiPage()
{
QDesktopServices::openUrl(QUrl(""));
}
void MainWindow::checkAdminRights()
{
BOOL isAdmin = FALSE;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
PSID AdministratorsGroup;
if (AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0,
&AdministratorsGroup)) {
if (!CheckTokenMembership(NULL, AdministratorsGroup, &isAdmin)) {
isAdmin = FALSE;
}
FreeSid(AdministratorsGroup);
}
if (!isAdmin) {
QMessageBox::information(this, "权限提升",
"启动器需要管理员权限运行请允许UAC提示。");
wchar_t path[MAX_PATH];
GetModuleFileNameW(NULL, path, MAX_PATH);
ShellExecuteW(NULL, L"runas", path, NULL, NULL, SW_SHOWNORMAL);
QApplication::quit();
}
}
int MainWindow::compareVersions(const QString &v1, const QString &v2)
{
QStringList parts1 = v1.split('.');
QStringList parts2 = v2.split('.');
int maxParts = qMax(parts1.size(), parts2.size());
for (int i = 0; i < maxParts; i++) {
int num1 = (i < parts1.size()) ? parts1[i].toInt() : 0;
int num2 = (i < parts2.size()) ? parts2[i].toInt() : 0;
if (num1 < num2) return -1;
if (num1 > num2) return 1;
}
return 0;
}
// 修改后的解压函数,支持密码
bool MainWindow::extractZip(const QString &zipPath, const QString &extractDir, const QString &password)
{
QFile zipFile(zipPath);
if (!zipFile.exists()) {
qDebug() << "ZIP文件不存在:" << zipPath;
return false;
}
QDir dir(extractDir);
if (!dir.exists()) {
if (!dir.mkpath(".")) {
qDebug() << "无法创建目录:" << extractDir;
return false;
}
}
QString program;
QStringList arguments;
// 使用7z进行解压支持密码
QString sevenZipPath = QCoreApplication::applicationDirPath() + "/7z.exe";
if (QFile::exists(sevenZipPath)) {
program = sevenZipPath;
arguments << "x" << "-y" << "-o" + extractDir;
if (!password.isEmpty()) {
arguments << "-p" + password;
}
arguments << zipPath;
} else {
// 如果没有7z.exe使用系统内置命令不支持密码
program = "powershell";
arguments << "-Command" << "Expand-Archive -Path \"" + zipPath + "\" -DestinationPath \"" + extractDir + "\" -Force";
}
QProcess process;
process.start(program, arguments);
if (!process.waitForStarted()) {
qDebug() << "无法启动解压进程";
return false;
}
if (!process.waitForFinished(300000)) {
qDebug() << "解压进程超时";
return false;
}
if (process.exitCode() != 0) {
qDebug() << "解压失败,错误码:" << process.exitCode();
qDebug() << "错误输出:" << process.readAllStandardError();
return false;
}
qDebug() << "成功解压文件到" << extractDir;
return true;
}
void MainWindow::startGame()
{
if (!isAuthenticated) {
QMessageBox::warning(this, "未验证", "请先完成卡密验证");
return;
}
QString batPath = UPDATE_PATH + "";
if (!QFile::exists(batPath)) {
QMessageBox::critical(this, "错误", "找不到启动脚本: " + batPath);
return;
}
disableButtons();
statusLabel->setText("正在启动游戏...");
// 使用QProcess启动bat文件
QProcess *gameProcess = new QProcess(this);
gameProcess->setWorkingDirectory(UPDATE_PATH);
// 连接游戏结束信号
connect(gameProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, &MainWindow::onGameFinished);
// 启动bat文件
gameProcess->start("cmd.exe", QStringList() << "/c" << batPath);
if (!gameProcess->waitForStarted()) {
statusLabel->setText("无法启动游戏");
activateButtons();
return;
}
statusLabel->setText("游戏运行中...");
}
void MainWindow::startGameProcess()
{
// 确保 gameProcess 被正确创建
if (gameProcess) {
gameProcess->kill();
gameProcess->deleteLater();
gameProcess = nullptr;
}
gameProcess = new QProcess(this);
gameProcess->setWorkingDirectory(UPDATE_PATH);
// 连接游戏结束信号
connect(gameProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, &MainWindow::onGameFinished);
// 启动注入程序 - 使用新的 QProcess 实例
QProcess *injectProcess = new QProcess(this);
injectProcess->setWorkingDirectory(UPDATE_PATH);
QStringList injectArgs;
injectArgs << "-d" << "-k" << "mai2hook.dll" << "amdaemon.exe"
<< "-f" << "-c" << "config_common.json" << "config_server.json" << "config_client.json";
// 增加超时时间到15秒15000毫秒
injectProcess->start("inject", injectArgs);
// 增加等待时间到15秒
if (!injectProcess->waitForFinished(15000)) {
statusLabel->setText("注入程序超时");
injectProcess->deleteLater();
activateButtons();
return;
}
injectProcess->deleteLater();
// 启动游戏主程序
QStringList gameArgs;
gameArgs << "-screen-fullscreen" << "1" << "-screen-width" << "1080" << "-screen-height" << "1920" << "-silent-crashes";
gameProcess->start("Sinmai.exe", gameArgs);
if (!gameProcess->waitForStarted()) {
statusLabel->setText("无法启动游戏");
activateButtons();
return;
}
statusLabel->setText("游戏运行中...");
}
void MainWindow::onGameFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
Q_UNUSED(exitCode);
Q_UNUSED(exitStatus);
statusLabel->setText("游戏进程已结束");
activateButtons();
// 删除QProcess对象
QProcess *senderProcess = qobject_cast<QProcess*>(sender());
if (senderProcess) {
senderProcess->deleteLater();
}
}
void MainWindow::startOdd()
{
if (!isAuthenticated) {
QMessageBox::warning(this, "未验证", "请先完成卡密验证");
return;
}
QString batPath = UPDATE_PATH + "";
if (!QFile::exists(batPath)) {
QMessageBox::critical(this, "错误", "找不到ODD启动脚本: " + batPath);
return;
}
// 启动bat文件
QProcess::startDetached("cmd.exe", QStringList() << "/c" << batPath, UPDATE_PATH);
statusLabel->setText("正在启动ODD驱动程序...");
}
void MainWindow::modifyHosts()
{
if (!isAuthenticated) {
QMessageBox::warning(this, "未验证", "请先完成卡密验证");
return;
}
QString batPath = UPDATE_PATH + "";
if (!QFile::exists(batPath)) {
QMessageBox::critical(this, "错误", "找不到hosts修改脚本: " + batPath);
return;
}
// 启动bat文件
QProcess::startDetached("cmd.exe", QStringList() << "/c" << batPath, UPDATE_PATH);
statusLabel->setText("正在修改hosts文件...");
}
void MainWindow::forceUpdate()
{
if (!isAuthenticated) {
QMessageBox::warning(this, "未验证", "请先完成卡密验证");
return;
}
statusLabel->setText("开始强制更新...");
fetchVersionForForceUpdate();
}
void MainWindow::fetchVersionForForceUpdate()
{
QUrl url(SERVER_URL + VERSION_FILE);
QNetworkRequest request(url);
request.setRawHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone);
request.setSslConfiguration(sslConfig);
QNetworkReply *reply = networkManager->get(request);
connect(reply, &QNetworkReply::finished, this, [=]() {
if (reply->error() != QNetworkReply::NoError) {
statusLabel->setText("连接服务器失败");
return;
}
QByteArray data = reply->readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
if (doc.isNull()) {
statusLabel->setText("版本信息解析错误");
return;
}
updateGame(doc.object());
reply->deleteLater();
});
}
void MainWindow::openBuyPage()
{
QDesktopServices::openUrl(QUrl("https://m.tb.cn/h.hYesG5B?tk=qva9Vs7587S"));
}
void MainWindow::fetchAnnouncement()
{
QUrl url(SERVER_URL + "g/" + ANNOUNCEMENT_FILE);
QNetworkRequest request(url);
request.setRawHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone);
request.setSslConfiguration(sslConfig);
QNetworkReply *reply = networkManager->get(request);
connect(reply, &QNetworkReply::finished, this, &MainWindow::onAnnouncementFetched);
}
void MainWindow::onAnnouncementFetched()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
QJsonObject announcement;
if (reply->error() == QNetworkReply::NoError) {
QByteArray data = reply->readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
if (!doc.isNull()) {
announcement = doc.object();
}
}
if (announcement.isEmpty()) {
announcement["title"] = "公告";
announcement["content"] = "无法连接到服务器获取最新公告。\n请检查网络连接或稍后再试。";
announcement["date"] = QDate::currentDate().toString("yyyy-MM-dd");
}
updateAnnouncement(announcement);
reply->deleteLater();
}
void MainWindow::checkForUpdates()
{
if (!isAuthenticated) {
statusLabel->setText("请先完成卡密验证");
return;
}
// 如果正在进行首次更新,则跳过常规更新检查
if (m_isFirstUpdateInProgress) {
qDebug() << "跳过常规更新检查(首次更新进行中)";
return;
}
statusLabel->setText("正在检查更新...");
QUrl url(SERVER_URL + VERSION_FILE);
QNetworkRequest request(url);
request.setRawHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone);
request.setSslConfiguration(sslConfig);
QNetworkReply *reply = networkManager->get(request);
connect(reply, &QNetworkReply::finished, this, &MainWindow::onVersionChecked);
}
void MainWindow::onVersionChecked()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
if (reply->error() != QNetworkReply::NoError) {
statusLabel->setText("连接服务器失败");
qDebug() << "连接服务器失败:" << reply->errorString();
return;
}
QByteArray data = reply->readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
if (doc.isNull()) {
statusLabel->setText("版本信息解析错误");
qDebug() << "版本信息解析错误";
return;
}
QJsonObject remoteVersion = doc.object();
QString remoteVer = remoteVersion["version"].toString();
QString localVer = localVersion["version"].toString();
qDebug() << "本地版本:" << localVer << "远程版本:" << remoteVer;
int comparison = compareVersions(remoteVer, localVer);
if (comparison <= 0) {
statusLabel->setText("游戏已是最新版本");
versionLabel->setText("版本: v" + localVer);
qDebug() << "游戏已是最新版本";
} else {
statusLabel->setText("发现新版本 v" + remoteVer);
versionLabel->setText("版本: v" + localVer + " → v" + remoteVer);
qDebug() << "需要更新: 本地 v" << localVer << "-> 远程 v" << remoteVer;
updateGame(remoteVersion); // 执行增量更新
}
reply->deleteLater();
}
void MainWindow::updateGame(const QJsonObject &remoteVersion)
{
if (remoteVersion.isEmpty()) {
statusLabel->setText("无效的版本信息");
return;
}
QString remoteVer = remoteVersion["version"].toString();
QString localVer = localVersion["version"].toString();
// 检查下载URL是否存在
if (!remoteVersion.contains("url") || remoteVersion["url"].toString().isEmpty()) {
statusLabel->setText("更新URL无效");
return;
}
QString updateUrl = remoteVersion["url"].toString();
disableButtons();
statusLabel->setText("正在下载增量更新...");
QUrl url(updateUrl); // 使用从JSON中获取的URL
QNetworkRequest request(url);
request.setRawHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone);
request.setSslConfiguration(sslConfig);
QNetworkReply *reply = networkManager->get(request);
connect(reply, &QNetworkReply::downloadProgress, this, [=](qint64 bytesReceived, qint64 bytesTotal) {
if (bytesTotal > 0) {
int percent = static_cast<int>((bytesReceived * 100) / bytesTotal);
progressBar->setValue(percent);
statusLabel->setText(QString("下载增量更新: %1%").arg(percent));
}
});
connect(reply, &QNetworkReply::finished, this, [=]() {
onUpdateDownloaded(reply, remoteVersion);
});
}
void MainWindow::onUpdateDownloaded(QNetworkReply *reply, const QJsonObject &version)
{
if (reply->error() != QNetworkReply::NoError) {
statusLabel->setText("下载失败: " + reply->errorString());
qDebug() << "下载失败:" << reply->errorString();
activateButtons();
return;
}
QByteArray data = reply->readAll();
QFile file(UPDATE_ZIP);
if (file.open(QIODevice::WriteOnly)) {
file.write(data);
file.close();
} else {
qDebug() << "无法保存更新文件";
}
statusLabel->setText("正在解压文件...");
progressBar->setValue(0);
// 从版本信息中获取密码
QString password = version["password"].toString();
QFutureWatcher<bool> *watcher = new QFutureWatcher<bool>(this);
connect(watcher, &QFutureWatcher<bool>::finished, this, [=]() {
if (watcher->result()) {
// 更新版本信息并保存
QJsonObject newLocalVersion;
newLocalVersion["version"] = version["version"].toString();
if (version.contains("changelog")) {
newLocalVersion["changelog"] = version["changelog"];
}
if (version.contains("timestamp")) {
newLocalVersion["timestamp"] = version["timestamp"];
}
localVersion = newLocalVersion;
saveLocalVersion();
// 重新加载本地版本以确保一致性
loadLocalVersion();
// 更新界面显示
versionLabel->setText("版本: v" + localVersion["version"].toString());
statusLabel->setText("更新完成!");
progressBar->setValue(100);
QFile::remove(UPDATE_ZIP);
QMessageBox::information(this, "更新完成", "游戏已成功更新到最新版本!");
qDebug() << "更新完成: v" << localVersion["version"].toString();
} else {
statusLabel->setText("解压失败");
QMessageBox::critical(this, "更新失败", "解压更新包失败");
qDebug() << "解压失败";
}
activateButtons();
watcher->deleteLater();
});
QFuture<bool> future = QtConcurrent::run([=]() {
return extractZip(UPDATE_ZIP, UPDATE_PATH, password);
});
watcher->setFuture(future);
reply->deleteLater();
}
void MainWindow::saveLocalVersion()
{
// 创建精简的版本对象
QJsonObject saveVersion;
saveVersion["version"] = localVersion["version"].toString();
// 只保存必要的字段
if (localVersion.contains("changelog")) {
saveVersion["changelog"] = localVersion["changelog"];
}
if (localVersion.contains("timestamp")) {
saveVersion["timestamp"] = localVersion["timestamp"];
}
QFile file(UPDATE_PATH + "/" + VERSION_FILE);
if (file.open(QIODevice::WriteOnly)) {
QJsonDocument doc(saveVersion);
file.write(doc.toJson());
file.close();
}
}
void MainWindow::selectPackagePath()
{
QString dir = QFileDialog::getExistingDirectory(
this,
tr("选择Package目录"),
UPDATE_PATH,
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks
);
if (!dir.isEmpty()) {
UPDATE_PATH = dir;
pathLabel->setText(UPDATE_PATH);
// 更新相关文件路径
BAT_FILE = UPDATE_PATH + "/2-Start.bat";
ODD_BAT_FILE = UPDATE_PATH + "/1-管理员运行odd.bat";
HOSTS_BAT = UPDATE_PATH + "/hosts.bat";
// 重新加载本地版本
loadLocalVersion();
checkPackageExists();
saveSettings();
}
}
void MainWindow::checkPackageExists()
{
QDir packageDir(UPDATE_PATH);
bool exists = packageDir.exists();
startBtn->setEnabled(false);
oddBtn->setEnabled(false);
hostsBtn->setEnabled(false);
wikiBtn->setEnabled(true);
fullUpdateBtn->setEnabled(isAuthenticated);
updateBtn->setEnabled(isAuthenticated);
buyBtn->setEnabled(true);
pathSelectBtn->setEnabled(true);
if (!exists) {
statusLabel->setText("警告: Package目录不存在!");
} else if (isAuthenticated) {
startBtn->setEnabled(true);
oddBtn->setEnabled(true);
hostsBtn->setEnabled(true);
}
}
void MainWindow::saveSettings()
{
settings->setValue("packagePath", UPDATE_PATH);
settings->sync();
}
void MainWindow::loadSettings()
{
if (settings->contains("packagePath")) {
UPDATE_PATH = settings->value("packagePath").toString();
} else {
UPDATE_PATH = "Package";
}
BAT_FILE = UPDATE_PATH + "/2-Start.bat";
ODD_BAT_FILE = UPDATE_PATH + "/1-管理员运行odd.bat";
HOSTS_BAT = UPDATE_PATH + "/hosts.bat";
}
// 修改 getDeviceId 函数
QString MainWindow::getDeviceId()
{
ensureDataDirExists();
// 确定数据目录路径
QString dataDir = "D:/maimaiLauncherData";
QDir dDrive("D:/");
if (!dDrive.exists()) {
dataDir = "C:/maimaiLauncherData";
}
DEVICE_CODE_FILE = dataDir + "/device_code.dat";
QFile file(DEVICE_CODE_FILE);
if (file.exists() && file.open(QIODevice::ReadOnly)) {
QString id = QString::fromUtf8(file.readAll()).trimmed();
file.close();
if (!id.isEmpty()) return id;
}
QString deviceInfo = "";
HKEY hKey;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
0, KEY_READ, &hKey) == ERROR_SUCCESS) {
wchar_t cpuName[256];
DWORD size = sizeof(cpuName);
DWORD type;
if (RegQueryValueEx(hKey, L"ProcessorNameString", NULL, &type,
(LPBYTE)cpuName, &size) == ERROR_SUCCESS) {
deviceInfo += QString::fromWCharArray(cpuName);
}
RegCloseKey(hKey);
}
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port 0\\Scsi Bus 0\\Target Id 0\\Logical Unit Id 0",
0, KEY_READ, &hKey) == ERROR_SUCCESS) {
wchar_t diskId[256];
DWORD size = sizeof(diskId);
DWORD type;
if (RegQueryValueEx(hKey, L"SerialNumber", NULL, &type,
(LPBYTE)diskId, &size) == ERROR_SUCCESS) {
deviceInfo += QString::fromWCharArray(diskId);
}
RegCloseKey(hKey);
}
QCryptographicHash hash(QCryptographicHash::Sha256);
hash.addData(deviceInfo.toUtf8());
QString deviceId = hash.result().toHex().left(32);
if (file.open(QIODevice::WriteOnly)) {
file.write(deviceId.toUtf8());
file.close();
const wchar_t* path = reinterpret_cast<const wchar_t*>(DEVICE_CODE_FILE.utf16());
DWORD attributes = GetFileAttributesW(path);
if (attributes != INVALID_FILE_ATTRIBUTES) {
SetFileAttributesW(path, attributes | FILE_ATTRIBUTE_HIDDEN);
}
}
return deviceId;
}
QString MainWindow::loadSavedKami()
{
ensureDataDirExists();
// 确定数据目录路径
QString dataDir = "D:/maimaiLauncherData";
QDir dDrive("D:/");
if (!dDrive.exists()) {
dataDir = "C:/maimaiLauncherData";
}
CARD_FILE = dataDir + "/card.dat";
QFile file(CARD_FILE);
if (file.exists() && file.open(QIODevice::ReadOnly)) {
QString kami = QString::fromUtf8(file.readAll()).trimmed();
file.close();
return kami;
}
return "";
}
bool MainWindow::saveKami(const QString &kami)
{
ensureDataDirExists();
// 确定数据目录路径
QString dataDir = "D:/maimaiLauncherData";
QDir dDrive("D:/");
if (!dDrive.exists()) {
dataDir = "C:/maimaiLauncherData";
}
CARD_FILE = dataDir + "/card.dat";
QFile file(CARD_FILE);
if (file.open(QIODevice::WriteOnly)) {
file.write(kami.toUtf8());
file.close();
const wchar_t* path = reinterpret_cast<const wchar_t*>(CARD_FILE.utf16());
DWORD attributes = GetFileAttributesW(path);
if (attributes != INVALID_FILE_ATTRIBUTES) {
SetFileAttributesW(path, attributes | FILE_ATTRIBUTE_HIDDEN);
}
return true;
}
return false;
}
bool MainWindow::clearSavedKami()
{
// 确定数据目录路径
QString dataDir = "D:/maimaiLauncherData";
QDir dDrive("D:/");
if (!dDrive.exists()) {
dataDir = "C:/maimaiLauncherData";
}
CARD_FILE = dataDir + "/card.dat";
QFile file(CARD_FILE);
return file.exists() ? file.remove() : true;
}
void MainWindow::showAuthWindow()
{
if (authWindow) {
authWindow->deleteLater();
}
authWindow = new AuthWindow(deviceId, savedKami, this);
if (authWindow->exec() == QDialog::Accepted) {
QString kami = authWindow->getKami();
bool remember = authWindow->getRemember();
if (!kami.isEmpty()) {
authStatus->setText("验证中...");
performNetworkAuthentication(kami, remember);
}
} else {
authStatus->setText("验证已取消");
QMessageBox::critical(this, "验证取消", "您必须完成验证才能使用启动器。\n程序将在5秒后关闭...");
quitTimer->start(5000);
}
}
void MainWindow::performNetworkAuthentication(const QString &kami, bool remember)
{
QUrl url(AUTH_API);
QUrlQuery query;
query.addQueryItem("api", "kmlogon");
query.addQueryItem("app", APP_ID);
query.addQueryItem("kami", kami);
query.addQueryItem("markcode", deviceId);
url.setQuery(query);
QNetworkRequest request(url);
request.setRawHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone);
request.setSslConfiguration(sslConfig);
QNetworkReply *reply = networkManager->get(request);
connect(reply, &QNetworkReply::finished, this, [=]() {
QString errorMsg;
QString vipExpiry;
bool success = false;
if (reply->error() == QNetworkReply::NoError) {
QByteArray data = reply->readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
if (!doc.isNull() && doc.isObject()) {
QJsonObject json = doc.object();
int code = json["code"].toInt(-1);
if (json.contains("code")) {
if (code == 200) {
if (json.contains("msg") && json["msg"].isObject()) {
QJsonObject msg = json["msg"].toObject();
if (msg.contains("vip")) {
vipExpiry = msg["vip"].toString();
success = true;
errorMsg = "验证成功";
} else {
errorMsg = "服务器响应缺少vip字段";
}
} else {
errorMsg = "服务器响应格式错误";
}
} else {
QMap<int, QString> errorMap = {
{101, "应用不存在 (101)"},
{102, "应用已关闭 (102)"},
{171, "接口维护中 (171)"},
{172, "接口未添加或不存在 (172)"},
{104, "签名为空 (104)"},
{105, "数据过期 (105)"},
{106, "签名有误 (106)"},
{148, "卡密为空 (148)"},
{149, "卡密不存在 (149)"},
{150, "已使用 (150)"},
{151, "卡密禁用 (151)"},
{169, "IP不一致 (169)"}
};
errorMsg = errorMap.value(code, "未知错误 (代码: " + QString::number(code) + ")");
}
} else {
errorMsg = "服务器响应缺少code字段";
}
} else {
errorMsg = "响应解析错误: " + data;
}
} else {
errorMsg = "网络错误: " + reply->errorString() + " (代码: " + QString::number(reply->error()) + ")";
}
reply->deleteLater();
onAuthenticationFinished(kami, remember, success, errorMsg, vipExpiry);
});
}
void MainWindow::onAuthenticationFinished(const QString &kami, bool remember, bool success, const QString &message, const QString &vipExpiry)
{
authStatus->setText(message);
if (success) {
isAuthenticated = true;
QDateTime expireTime = QDateTime::fromSecsSinceEpoch(vipExpiry.toLongLong());
QString expireStr = expireTime.toString("yyyy-MM-dd HH:mm:ss");
vipInfo->setText("VIP到期: " + expireStr);
if (remember) {
if (saveKami(kami)) {
savedKami = kami;
} else {
authStatus->setText(authStatus->text() + " (保存卡密失败)");
}
} else {
clearSavedKami();
savedKami = "";
}
// 如果是首次启动
if (isFirstLaunch) {
// 提示选择Package目录
QMessageBox::information(this, "首次启动", "请选择游戏Package目录");
selectPackagePath();
// 提示首次更新 - 使用update_f.json
if (QMessageBox::question(this, "首次启动", "检测到第一次启动,是否立即进行首次更新?",
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
statusLabel->setText("开始首次更新...");
fetchFirstUpdateVersion(); // 调用首次更新函数
} else {
// 用户选择不进行首次更新,直接检查常规更新
checkForUpdates(); // 新增:非首次启动时检查更新
}
isFirstLaunch = false; // 标记已处理首次启动
} else {
// 非首次启动,直接检查常规更新
checkForUpdates(); // 新增:非首次启动时检查更新
}
activateButtons();
fullUpdateBtn->setEnabled(true);
checkPackageExists();
checkLauncherVersion(); // 检查启动器版本
checkAndDeleteFiles();
} else {
isAuthenticated = false;
vipInfo->setText("VIP状态: 验证失败");
clearSavedKami();
savedKami = "";
disableButtons();
QMessageBox::critical(this, "验证失败", "验证失败: " + message + "\n程序将在5秒后关闭...");
quitTimer->start(5000);
}
}
void MainWindow::checkAndDeleteFiles()
{
QUrl url(SERVER_URL + "delete.json");
QNetworkRequest request(url);
request.setRawHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone);
request.setSslConfiguration(sslConfig);
QNetworkReply *reply = networkManager->get(request);
connect(reply, &QNetworkReply::finished, this, [=]() {
if (reply->error() != QNetworkReply::NoError) {
qDebug() << "无法获取删除列表:" << reply->errorString();
return;
}
QByteArray data = reply->readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
if (doc.isNull() || !doc.isArray()) {
qDebug() << "删除列表格式错误";
return;
}
QJsonArray filesToDelete = doc.array();
processDeleteList(filesToDelete);
reply->deleteLater();
});
}
void MainWindow::processDeleteList(const QJsonArray &filesToDelete)
{
int deletedCount = 0;
int failedCount = 0;
for (const QJsonValue &value : filesToDelete) {
QString relativePath = value.toString();
if (relativePath.isEmpty()) continue;
QString fullPath = UPDATE_PATH + "/" + relativePath;
QFile file(fullPath);
if (file.exists()) {
// 如果是只读文件,先取消只读属性
const wchar_t* wPath = reinterpret_cast<const wchar_t*>(fullPath.utf16());
DWORD attrs = GetFileAttributesW(wPath);
if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_READONLY)) {
SetFileAttributesW(wPath, attrs & ~FILE_ATTRIBUTE_READONLY);
}
if (file.remove()) {
qDebug() << "已删除文件:" << fullPath;
deletedCount++;
} else {
qDebug() << "删除失败:" << fullPath << file.errorString();
failedCount++;
}
}
}
if (deletedCount > 0 || failedCount > 0) {
qDebug() << "删除操作完成: 成功删除" << deletedCount
<< "个文件," << failedCount << "个文件删除失败";
}
}
void MainWindow::fetchFirstUpdateVersion()
{
m_isFirstUpdateInProgress = true; // 标记首次更新开始
QUrl url(SERVER_URL + UPDATE_F_VERSION_FILE);
QNetworkRequest request(url);
request.setRawHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone);
request.setSslConfiguration(sslConfig);
QNetworkReply *reply = networkManager->get(request);
connect(reply, &QNetworkReply::finished, this, [=]() {
if (reply->error() != QNetworkReply::NoError) {
statusLabel->setText("首次更新: 连接服务器失败");
m_isFirstUpdateInProgress = false;
return;
}
QByteArray data = reply->readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
if (doc.isNull()) {
statusLabel->setText("首次更新: 版本信息解析错误");
m_isFirstUpdateInProgress = false;
return;
}
QJsonObject remoteVersion = doc.object();
QString remoteVer = remoteVersion["version"].toString();
statusLabel->setText("首次更新: 下载完整包 " + remoteVer);
// 使用新的文件名
QString FULL_UPDATE_ZIP = "update_f.zip";
// 获取完整包URL
QString updateUrl = remoteVersion["url"].toString();
if (updateUrl.isEmpty()) {
statusLabel->setText("首次更新: URL无效");
m_isFirstUpdateInProgress = false;
return;
}
// 从版本信息中获取密码
QString password = remoteVersion["password"].toString();
// 下载完整包
QUrl fullUrl(updateUrl);
QNetworkRequest fullRequest(fullUrl);
fullRequest.setRawHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
fullRequest.setSslConfiguration(sslConfig);
QNetworkReply *downloadReply = networkManager->get(fullRequest);
connect(downloadReply, &QNetworkReply::downloadProgress, this, [=](qint64 bytesReceived, qint64 bytesTotal) {
if (bytesTotal > 0) {
int percent = static_cast<int>((bytesReceived * 100) / bytesTotal);
progressBar->setValue(percent);
statusLabel->setText(QString("下载完整包: %1%").arg(percent));
}
});
connect(downloadReply, &QNetworkReply::finished, this, [=]() {
if (downloadReply->error() != QNetworkReply::NoError) {
statusLabel->setText("完整包下载失败: " + downloadReply->errorString());
m_isFirstUpdateInProgress = false;
return;
}
// 保存完整包
QByteArray fullData = downloadReply->readAll();
QFile fullFile(FULL_UPDATE_ZIP);
if (fullFile.open(QIODevice::WriteOnly)) {
fullFile.write(fullData);
fullFile.close();
}
statusLabel->setText("正在解压完整包...");
progressBar->setValue(0);
QFutureWatcher<bool> *watcher = new QFutureWatcher<bool>(this);
connect(watcher, &QFutureWatcher<bool>::finished, this, [=]() {
if (watcher->result()) {
// 更新版本信息并保存
QJsonObject newLocalVersion;
newLocalVersion["version"] = remoteVersion["version"].toString();
if (remoteVersion.contains("changelog")) {
newLocalVersion["changelog"] = remoteVersion["changelog"];
}
if (remoteVersion.contains("timestamp")) {
newLocalVersion["timestamp"] = remoteVersion["timestamp"];
}
localVersion = newLocalVersion;
saveLocalVersion();
// 更新界面显示
versionLabel->setText("版本: v" + remoteVer);
statusLabel->setText("首次更新完成!");
progressBar->setValue(100);
QFile::remove(FULL_UPDATE_ZIP);
QMessageBox::information(this, "首次更新完成", "游戏已成功安装完整包!");
// 标记首次更新完成
m_isFirstUpdateInProgress = false;
// 立即执行一次增量更新检查
statusLabel->setText("检查增量更新...");
checkForUpdates();
} else {
statusLabel->setText("解压完整包失败");
m_isFirstUpdateInProgress = false;
}
watcher->deleteLater();
});
QFuture<bool> future = QtConcurrent::run([=]() {
return extractZip(FULL_UPDATE_ZIP, UPDATE_PATH, password);
});
watcher->setFuture(future);
downloadReply->deleteLater();
});
reply->deleteLater();
});
}
// 检查启动器版本
void MainWindow::checkLauncherVersion()
{
QUrl url(SERVER_URL + "");
QNetworkRequest request(url);
request.setRawHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone);
request.setSslConfiguration(sslConfig);
QNetworkReply *reply = networkManager->get(request);
connect(reply, &QNetworkReply::finished, this, [=]() {
if (reply->error() != QNetworkReply::NoError) {
// 无法连接服务器,弹窗提示并闪退
QMessageBox::critical(nullptr, "网络错误", "无法连接服务器,启动器即将关闭");
QTimer::singleShot(0, this, &MainWindow::quitApplication);
return;
}
QByteArray data = reply->readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
if (doc.isNull() || !doc.isObject()) {
// 数据解析错误,不退出
qDebug() << "启动器版本信息解析错误";
return;
}
QJsonObject remoteData = doc.object();
QString remoteVersion = remoteData["version"].toString();
QString downloadUrl = remoteData["url"].toString(); // 保留但不再使用
if (compareVersions(remoteVersion, LAUNCHER_VERSION) > 0) {
// 当前版本过旧
QMessageBox msgBox;
msgBox.setWindowTitle("启动器版本过旧");
msgBox.setText(QString("发现新版本启动器 v%1当前版本 v%2。请下载最新版本启动器。\n程序将在5秒后关闭。").arg(remoteVersion).arg(LAUNCHER_VERSION));
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.exec();
// 5秒后退出
QTimer::singleShot(5000, this, &MainWindow::quitApplication);
}
reply->deleteLater();
});
}
void MainWindow::quitApplication()
{
QApplication::quit();
}