This commit is contained in:
Jkibbels 2025-02-08 04:18:42 -05:00
commit 29186b549e
7 changed files with 495 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
build/Desktop_Qt_6_7_3-Debug
CMakeLists.txt.user

73
CMakeLists.txt Normal file
View File

@ -0,0 +1,73 @@
cmake_minimum_required(VERSION 3.16)
project(keeblarcraft_installer VERSION 0.1 LANGUAGES CXX)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Network)
set(PROJECT_SOURCES
main.cpp
mainwindow.cpp
mainwindow.h
mainwindow.ui
)
file(COPY background.png DESTINATION ${CMAKE_BINARY_DIR})
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(keeblarcraft_installer
MANUAL_FINALIZATION
${PROJECT_SOURCES}
)
# Define target properties for Android with Qt 6 as:
# set_property(TARGET keeblarcraft_installer APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
# ${CMAKE_CURRENT_SOURCE_DIR}/android)
# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
else()
if(ANDROID)
add_library(keeblarcraft_installer SHARED
${PROJECT_SOURCES}
)
# Define properties for Android with Qt 5 after find_package() calls as:
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
else()
add_executable(keeblarcraft_installer
${PROJECT_SOURCES}
)
endif()
endif()
target_link_libraries(keeblarcraft_installer PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network)
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
# If you are developing for iOS or macOS you should consider setting an
# explicit, fixed bundle identifier manually though.
if(${QT_VERSION} VERSION_LESS 6.1.0)
set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.keeblarcraft_installer)
endif()
set_target_properties(keeblarcraft_installer PROPERTIES
${BUNDLE_ID_OPTION}
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUElibglu1-mesa-dev
)
include(GNUInstallDirs)
install(TARGETS keeblarcraft_installer
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
if(QT_VERSION_MAJOR EQUAL 6)
qt_finalize_executable(keeblarcraft_installer)
endif()

BIN
background.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

12
main.cpp Normal file
View File

@ -0,0 +1,12 @@
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.setFixedSize(1166, 580); // Tested on one resolution, we will USE one resolution!
w.show();
return a.exec();
}

181
mainwindow.cpp Normal file
View File

@ -0,0 +1,181 @@
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QMessageBox>
#include <QProgressBar>
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <filesystem>
#include <sstream>
#include <QFileDialog>
#ifdef _WIN64
#include <Windows.h>
#endif
namespace {
constexpr uint8_t MAX_INSTALL_STAGES = 3;
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
UpdateOSAgnosticInformation();
net = new QNetworkAccessManager(this);
PreInstallMode();
QWidget::setWindowTitle("Keeblarcraft Mod Installer");
// ui->centralwidget->setStyleSheet("background-image:url(background.png); background-position: center;"); // Needs work
}
MainWindow::~MainWindow()
{
delete ui;
delete net;
delete reply;
}
void MainWindow::on_install_update_button_clicked()
{
QNetworkRequest request(downloadUrl);
reply = net->get(request);
// connect(reply, SIGNAL(&QNetworkReply::errorOccurred), this, SLOT(Error(QNetworkReply::NetworkError)));
connect(reply, &QNetworkReply::errorOccurred, this, [this](QNetworkReply::NetworkError) {
reply->deleteLater();
QMessageBox::warning(nullptr, "Error", reply->errorString());
});
connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(UpdateProgress(qint64, qint64)));
connect(reply, SIGNAL(finished()), this, SLOT(FinishedDownloading()));
}
void MainWindow::UpdateProgress(qint64 read, qint64 total) {
ui->mf_progress_bar->setMaximum(total);
ui->mf_progress_bar->setValue(read);
}
void MainWindow::FinishedDownloading() {
QByteArray data = reply->readAll();
downloadLocation = "/home/jesse/Downloads/mods.zip";
QFile file(downloadLocation);
if (file.open(QIODevice::WriteOnly)) {
qint64 bytesWritten = file.write(data);
} else {
QMessageBox::warning(nullptr, "Error", file.errorString());
}
reply->deleteLater();
// Update button to not be pressable again. Will probably stick this on a timer in the future
ui->install_update_button->setEnabled(false);
ui->install_update_button->show();
ui->install_update_button->setText(QString("Unzipping..."));
// Unzip
Install();
}
std::string MainWindow::BuildUnzipCmd() {
std::stringstream ss;
#ifdef _WINDOWS
ss << "tar -xf " << downloadLocation.toStdString() << " -C " << modsDir.toStdString();
#elif __unix__
ss << "unzip " << downloadLocation.toStdString() << " -d " << modsDir.toStdString();
#endif
qDebug() << "Tar command is: " << ss.str();
return ss.str();
}
// Installation step covers everything from unzipping to installing the mods to making sure fabric is installed
void MainWindow::Install() {
uint8_t installStage = 1;
ui->mf_progress_bar->setValue(installStage++);
ui->mf_progress_bar->setMaximum(MAX_INSTALL_STAGES);
// COND 1: If mods dir exists we need to nuke it
// COND 2: If mods dir exists & it was our install location do NOT nuke it!
if (std::filesystem::is_directory(modsDir.toStdString().append("mods")) && modsDir != downloadLocation) {
modsDir.append("mods");
std::filesystem::remove_all(modsDir.toStdString());
}
ui->mf_progress_bar->setValue(installStage++);
system(BuildUnzipCmd().c_str());
ui->mf_progress_bar->setValue(installStage++);
QMessageBox::information(nullptr, "Completed", "The installer has successfully finished. You may now close the installer and launch Minecraft!");
PostInstallMode();
}
void MainWindow::Error(QNetworkReply::NetworkError code) {
reply->deleteLater();
QMessageBox::warning(nullptr, "Error", reply->errorString());
}
void MainWindow::PreInstallMode() {
ui->mf_progress_bar->setEnabled(false);
ui->install_update_button->setText("Update / Install");
ui->install_update_button->setVisible(true);
ui->close_launcher->setEnabled(false);
ui->close_launcher->setVisible(false);
}
void MainWindow::PostInstallMode() {
ui->mf_progress_bar->setEnabled(false);
ui->install_update_button->setEnabled(false);
ui->install_update_button->setVisible(false);
ui->close_launcher->setEnabled(true);
ui->close_launcher->setVisible(true);
}
void MainWindow::UpdateOSAgnosticInformation() {
#ifdef _WIN64
isLinux = false;
char* appDataLoc = std::getenv("APPDATA");
if (appDataLoc) {
std::filesystem::path execPath(appDataLoc);
downloadLocation = QString(execPath.c_str()); // Stick the mods inside the roaming directory; but not the .minecraft directory itself.
modsDir = downloadLocation + "\\.minecraft\\";
}
#elif __unix__
isLinux = true;
downloadLocation = getenv("HOME");
modsDir = downloadLocation + "/.minecraft/"; // Default value
ui->mcInstallDir->setText(modsDir); // Tell the user the default location for their .minecraft folder.
#endif
// Update the UI to reflect the default values correctly
ui->sf_dl_server->setPlainText(downloadUrl.toString());
}
void MainWindow::on_close_launcher_clicked()
{
delete reply;
delete net;
QApplication::quit();
}
// unused
void MainWindow::on_changeInstallPathButton_triggered(QAction *arg1){}
void MainWindow::on_changeInstallPathButton_clicked()
{
QString directory = QFileDialog::getExistingDirectory(this, tr("Find Files"), QDir::currentPath());
if (!directory.isEmpty()) {
// We will trust the user that this is the .minecraft folder and do 0 error checking.
modsDir = directory;
}
qDebug() << "New active .minecraft dir is: " << modsDir;
ui->mcInstallDir->setText(modsDir);
}

51
mainwindow.h Normal file
View File

@ -0,0 +1,51 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <QIODevice>
#include <string>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void UpdateOSAgnosticInformation();
void Install();
std::string BuildUnzipCmd();
void PreInstallMode();
void PostInstallMode();
private slots:
void UpdateProgress(qint64, qint64);
void FinishedDownloading();
void Error(QNetworkReply::NetworkError code);
void on_install_update_button_clicked();
void on_close_launcher_clicked();
void on_changeInstallPathButton_triggered(QAction *arg1);
void on_changeInstallPathButton_clicked();
private:
Ui::MainWindow *ui;
QNetworkAccessManager* net = nullptr;
QUrl downloadUrl {"https://www.techyjessy.tech/downloads/mods.zip"};
QString modsDir;
QString downloadLocation;
QNetworkReply* reply = nullptr;
bool isLinux = true;
};
#endif // MAINWINDOW_H

176
mainwindow.ui Normal file
View File

@ -0,0 +1,176 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1166</width>
<height>580</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QFrame" name="main_frame">
<property name="geometry">
<rect>
<x>-1</x>
<y>79</y>
<width>1171</width>
<height>481</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<widget class="QPushButton" name="install_update_button">
<property name="geometry">
<rect>
<x>460</x>
<y>350</y>
<width>241</width>
<height>91</height>
</rect>
</property>
<property name="text">
<string>Update / Install</string>
</property>
</widget>
<widget class="QTextEdit" name="mf_info_text">
<property name="geometry">
<rect>
<x>130</x>
<y>30</y>
<width>891</width>
<height>51</height>
</rect>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;meta charset=&quot;utf-8&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
hr { height: 1px; border-width: 0; }
li.unchecked::marker { content: &quot;\2610&quot;; }
li.checked::marker { content: &quot;\2612&quot;; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Fira Sans'; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Fira Sans Semi-Light';&quot;&gt;Hi! This is the first version of the Keeblarcraft installer! For now all it does is check if you have the modpack installed &amp;amp; only hangs onto the Config files and overwrites all the individual mod jar files. Have fun!&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
<widget class="QProgressBar" name="mf_progress_bar">
<property name="geometry">
<rect>
<x>770</x>
<y>360</y>
<width>321</width>
<height>61</height>
</rect>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
<widget class="QPlainTextEdit" name="sf_dl_server">
<property name="geometry">
<rect>
<x>30</x>
<y>90</y>
<width>431</width>
<height>41</height>
</rect>
</property>
<property name="plainText">
<string>Download Server Here
</string>
</property>
</widget>
<widget class="QPushButton" name="close_launcher">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>460</x>
<y>350</y>
<width>241</width>
<height>91</height>
</rect>
</property>
<property name="text">
<string>Close Installer</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>470</x>
<y>95</y>
<width>181</width>
<height>31</height>
</rect>
</property>
<property name="text">
<string>&lt;-- Download server here</string>
</property>
</widget>
<widget class="QLabel" name="mcInstallDir">
<property name="geometry">
<rect>
<x>10</x>
<y>370</y>
<width>431</width>
<height>41</height>
</rect>
</property>
<property name="text">
<string>Default minecraft path here...</string>
</property>
</widget>
<widget class="QToolButton" name="changeInstallPathButton">
<property name="geometry">
<rect>
<x>410</x>
<y>380</y>
<width>24</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</widget>
<widget class="QFrame" name="ribbon_frame">
<property name="geometry">
<rect>
<x>-1</x>
<y>-1</y>
<width>1171</width>
<height>81</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1166</width>
<height>21</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>