2025-02-08 09:18:42 +00:00
# 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 net ;
delete reply ;
2025-02-08 23:29:54 +00:00
delete ui ;
2025-02-08 09:18:42 +00:00
}
2025-02-08 23:29:54 +00:00
// Kick off the installation
2025-02-08 09:18:42 +00:00
void MainWindow : : on_install_update_button_clicked ( )
{
2025-02-08 22:32:57 +00:00
if ( ConfirmationPopup ( ) ) {
QNetworkRequest request ( downloadUrl ) ;
reply = net - > get ( request ) ;
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 ( ) ) ) ;
}
2025-02-08 09:18:42 +00:00
}
2025-02-08 23:29:54 +00:00
// Callback from network manager on download. Update the progress bar as we download
2025-02-08 09:18:42 +00:00
void MainWindow : : UpdateProgress ( qint64 read , qint64 total ) {
ui - > mf_progress_bar - > setMaximum ( total ) ;
ui - > mf_progress_bar - > setValue ( read ) ;
}
2025-02-08 23:29:54 +00:00
// This is a callback to when the download is completed by the network manager
2025-02-08 09:18:42 +00:00
void MainWindow : : FinishedDownloading ( ) {
QByteArray data = reply - > readAll ( ) ;
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... " ) ) ;
2025-02-08 22:32:57 +00:00
// Unzip the file - this version isn't really checking if the file is really us so at the very least the name needs to match.
// Otherwise we could end up damaging the ecosystem more than necessary.
if ( std : : filesystem : : exists ( downloadLocation . toStdString ( ) ) ) {
Install ( ) ;
} else {
QMessageBox : : critical ( nullptr , " Wrong format " , " The downloaded file appears to not exist - or is more likely just not named mods.zip -> you either downloaded a bad file or Jesse named his file wrong. Tell him to fix it!! " ) ;
PostInstallMode ( ) ;
}
2025-02-08 09:18:42 +00:00
}
2025-02-08 23:29:54 +00:00
// Build the unzip command depending on the users operating system
2025-02-08 09:18:42 +00:00
std : : string MainWindow : : BuildUnzipCmd ( ) {
std : : stringstream ss ;
2025-02-08 20:25:06 +00:00
# ifdef WIN64
2025-02-08 09:18:42 +00:00
ss < < " tar -xf " < < downloadLocation . toStdString ( ) < < " -C " < < modsDir . toStdString ( ) ;
# elif __unix__
2025-02-08 23:29:54 +00:00
qDebug ( ) < < " DL Loc: " < < downloadLocation < < " modsDir: " < < modsDir ;
2025-02-08 09:18:42 +00:00
ss < < " unzip " < < downloadLocation . toStdString ( ) < < " -d " < < modsDir . toStdString ( ) ;
# endif
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 ) ;
2025-02-08 22:32:57 +00:00
modsDir . append ( " mods " ) ;
2025-02-08 09:18:42 +00:00
// 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!
2025-02-08 22:32:57 +00:00
if ( std : : filesystem : : is_directory ( modsDir . toStdString ( ) ) & & modsDir ! = downloadLocation ) {
std : : filesystem : : remove_all ( modsDir . toStdString ( ) ) ; // Force empty the mods folder inside the .minecraft dir
}
if ( ! std : : filesystem : : exists ( modsDir . toStdString ( ) ) ) {
std : : filesystem : : create_directory ( modsDir . toStdString ( ) ) ;
2025-02-08 09:18:42 +00:00
}
ui - > mf_progress_bar - > setValue ( installStage + + ) ;
system ( BuildUnzipCmd ( ) . c_str ( ) ) ;
ui - > mf_progress_bar - > setValue ( installStage + + ) ;
2025-02-08 22:32:57 +00:00
// Clean up after ourselves; remove the file if possible
try {
if ( std : : filesystem : : exists ( downloadLocation . toStdString ( ) ) ) {
std : : filesystem : : remove ( downloadLocation . toStdString ( ) ) ;
}
} catch ( std : : exception e ) {
QString errorMsg = QString ( " Slight error - unable to cleanup after ourselves and could not remove leftover downloaded zip file located at %1 " ) . arg ( downloadLocation ) ;
QMessageBox : : information ( nullptr , " Failed to cleanup! " , errorMsg ) ;
}
2025-02-08 09:18:42 +00:00
QMessageBox : : information ( nullptr , " Completed " , " The installer has successfully finished. You may now close the installer and launch Minecraft! " ) ;
PostInstallMode ( ) ;
}
2025-02-08 23:29:54 +00:00
// Callback for downloading
2025-02-08 09:18:42 +00:00
void MainWindow : : Error ( QNetworkReply : : NetworkError code ) {
reply - > deleteLater ( ) ;
QMessageBox : : warning ( nullptr , " Error " , reply - > errorString ( ) ) ;
}
2025-02-08 23:29:54 +00:00
// Setup the window to be ready to update/install the mods folder
2025-02-08 09:18:42 +00:00
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 ) ;
}
2025-02-08 23:29:54 +00:00
// Set window variables after installation and unzipping
2025-02-08 09:18:42 +00:00
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 ) ;
}
2025-02-08 23:29:54 +00:00
// Make sure the user is sure they are OK with the current configuration
2025-02-08 22:32:57 +00:00
bool MainWindow : : ConfirmationPopup ( ) {
// This is the preinstall pop up box
2025-02-08 23:29:54 +00:00
QString tInstallFabric = " YOU MUST HAVE FABRIC INSTALLED PRIOR TO USING THIS INSTALLER " ;
2025-02-08 22:32:57 +00:00
QString tempInstallPath = " Install to: " + modsDir ;
QString tempDlSrvr = " Grab mods.zip from: " + downloadUrl . toString ( ) ;
2025-02-08 23:29:54 +00:00
QString deleteAll = " This installer will DELETE YOUR EXISTING MODS DIRECTORY. Back it up if you care about it beforehand! " ;
QString formatted = tInstallFabric . append ( tempInstallPath ) . append ( " \n " ) . append ( " \n " ) . append ( tempDlSrvr ) . append ( " \n " ) . append ( deleteAll ) ;
2025-02-08 22:32:57 +00:00
QMessageBox : : StandardButton reply = QMessageBox : : question ( nullptr , " Confirm install options... " , formatted ) ;
return reply = = QMessageBox : : Yes ;
}
2025-02-08 23:29:54 +00:00
// This handles setting data prior to the install period dependent on the users operating system.
2025-02-08 09:18:42 +00:00
void MainWindow : : UpdateOSAgnosticInformation ( ) {
# ifdef _WIN64
char * appDataLoc = std : : getenv ( " APPDATA " ) ;
if ( appDataLoc ) {
std : : filesystem : : path execPath ( appDataLoc ) ;
2025-02-08 20:25:06 +00:00
downloadLocation = QString : : fromStdString ( execPath . string ( ) ) ; // Stick the mods inside the roaming directory; but not the .minecraft directory itself.
2025-02-08 09:18:42 +00:00
modsDir = downloadLocation + " \\ .minecraft \\ " ;
2025-02-08 20:25:06 +00:00
downloadLocation . append ( " \\ mods.zip " ) ; // Should point to %APPDATA%\mods.zip
2025-02-08 22:32:57 +00:00
} else {
QMessageBox : : warning ( nullptr , " Warning " , " Unable to resolve APPDATA environment variable. You must manually set the .minecraft folder location " ) ;
2025-02-08 09:18:42 +00:00
}
# elif __unix__
downloadLocation = getenv ( " HOME " ) ;
modsDir = downloadLocation + " /.minecraft/ " ; // Default value
2025-02-08 23:29:54 +00:00
downloadLocation . append ( " /mods.zip " ) ;
2025-02-08 09:18:42 +00:00
# endif
// Update the UI to reflect the default values correctly
ui - > sf_dl_server - > setPlainText ( downloadUrl . toString ( ) ) ;
2025-02-08 22:32:57 +00:00
ui - > mcInstallDir - > setText ( modsDir ) ;
2025-02-08 09:18:42 +00:00
}
2025-02-08 23:29:54 +00:00
// This just closes the application but DOES call the destructor.
// It technically segfaults on the way out for some odd reason; but the pointers are cleaned up. Who knows!
2025-02-08 09:18:42 +00:00
void MainWindow : : on_close_launcher_clicked ( )
{
QApplication : : quit ( ) ;
}
2025-02-08 23:29:54 +00:00
// This is called if the users default .minecraft path cannot be found (by a user pressing the button of course)
2025-02-08 09:18:42 +00:00
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 ;
}
ui - > mcInstallDir - > setText ( modsDir ) ;
}
2025-02-08 22:32:57 +00:00
void MainWindow : : on_sf_dl_server_textChanged ( )
{
downloadUrl = ui - > sf_dl_server - > toPlainText ( ) ;
}