ソースを参照

Merge branch 'f/LEGACY-18-Preliminary-correctness-check' of LotRO_Legacy/Legacy_v2 into dev

Ivan Arkhipov 4 年 前
コミット
0a812662f2

+ 169 - 7
src/Legacy/legacyapplication.cpp

@@ -1,4 +1,7 @@
 #include "legacyapplication.h"
+#include "models/lotrodatmanager.h"
+#include "models/patchlist.h"
+#include "widgets/mainwindow.h"
 
 #include <QLockFile>
 #include <QMessageBox>
@@ -6,6 +9,46 @@
 #include <QFontDatabase>
 #include <QMessageLogContext>
 #include <QTextStream>
+#include <QTcpSocket>
+
+extern Q_CORE_EXPORT int qt_ntfs_permission_lookup;
+
+bool urlExists(QUrl theurl){
+    QTextStream out(stdout);
+    QTcpSocket socket;
+    QByteArray buffer;
+
+    socket.connectToHost(theurl.host(), 80);
+    if (socket.waitForConnected()) {
+        //Standard http request
+        socket.write("GET / HTTP/1.1\r\n"
+                 "host: " + theurl.host().toUtf8() + "\r\n\r\n");
+        if (socket.waitForReadyRead()) {
+            while(socket.bytesAvailable()){
+                buffer.append(socket.readAll());
+                int packetSize=buffer.size();
+                while(packetSize>0)
+                {
+                    //Output server response for debugging
+                    out << "[" << buffer.data() << "]" <<endl;
+
+                    //set Url if 200, 301, or 302 response given assuming that server will redirect
+                    if (buffer.contains("200 OK") ||
+                        buffer.contains("302 Found") ||
+                        buffer.contains("301 Moved")) {
+                        return true;
+                    }
+                    buffer.remove(0,packetSize);
+                    //packetSize=getPacketSize(buffer);
+                    packetSize=buffer.size();
+
+                } //while packet size >0
+            } //while socket.bytesavail
+
+        } //socket wait for ready read
+    }//socket write
+return false;
+}
 
 void logMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
 {
@@ -51,16 +94,17 @@ void logMessageHandler(QtMsgType type, const QMessageLogContext &context, const
 
 LegacyApplication::LegacyApplication(int &argc, char **argv)
     : QApplication(argc, argv)
-    , lotro_dat_manager(nullptr)
-    , lotro_dat_manager_thread(nullptr)
-    , patch_list(nullptr)
-    , gui(nullptr)
 {
     qInstallMessageHandler(logMessageHandler);
+
+    try_to_init_functional_modules_timer_.setInterval(1 * 1000); // one second
+    connect(&try_to_init_functional_modules_timer_, &QTimer::timeout, this, &LegacyApplication::TryToInitFunctionalModules);
+    try_to_init_functional_modules_timer_.stop();
 }
 
 bool LegacyApplication::init()
 {
+    qt_ntfs_permission_lookup++;
     qDebug() << "Starting initialisation...";
 
     QCoreApplication::setOrganizationName("LotroLegacy");
@@ -113,9 +157,127 @@ bool LegacyApplication::init()
     qDebug() << "Starting GUI initialisation...";
 
     gui = new MainWindow(patch_list);
-    qDebug() << "Legacy Initialization finished!";
+    connect(this, &LegacyApplication::ErrorStatusChanged, gui, &MainWindow::onErrorStatusChanged);
+    connect(this, &LegacyApplication::SecondsToNextTryToInitChanged, gui, &MainWindow::onSecondsToNextTryToInitChanged);
+
+    qDebug() << "Starting functional modules initialisation...";
 
-    patch_list->startAutoUpdate();
-    patch_list->initialize();
+    TryToInitFunctionalModules();
     return true;
 }
+
+LegacyApplication::ErrorStatus LegacyApplication::CheckErrorStatus()
+{
+    size_t status = E_NO_ERRORS;
+    QString game_folder_path = Settings::getValue("Lotro/game_path").toString();
+    QString locale_prefix = Settings::getValue("Lotro/original_locale").toString();
+
+    // Checking LotroLauncher.exe availability
+    QFileInfo lotro_launcher_exe(game_folder_path + "/LotroLauncher.exe");
+    if (!lotro_launcher_exe.exists()) {
+        status |= E_WRONG_GAME_FOLDER;
+    } else {
+        if (!QFile::setPermissions(lotro_launcher_exe.absoluteFilePath(),
+                QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ExeUser)) {
+            status |= E_WRONG_FILE_PERMISSIONS;
+        }
+    }
+
+    // Checking DAT files availability & correctness
+    if (!patch_list->getManager()->Initialised() || !patch_list->getManager()->DatPathIsRelevant()) {
+        QString client_local_filepath = game_folder_path + "/client_local_" + locale_prefix + ".dat";
+        QString client_general_filepath = game_folder_path + "/client_general.dat";
+
+        QFileInfo client_local_file(client_local_filepath);
+        QFileInfo client_general_file(client_general_filepath);
+
+        if (!client_general_file.exists() || !client_local_file.exists()) {
+            status |= E_DAT_FILES_MISSING;
+        } else {
+            if (!QFile::setPermissions(client_general_filepath,
+                                       QFileDevice::ReadUser |
+                                       QFileDevice::WriteUser |
+                                       QFileDevice::ExeUser)) {
+                status |= E_WRONG_FILE_PERMISSIONS;
+                status |= E_CANNOT_ACCESS_DAT_FILES;
+            }
+
+            if (!QFile::setPermissions(client_local_filepath,
+                                       QFileDevice::ReadUser |
+                                       QFileDevice::WriteUser |
+                                       QFileDevice::ExeUser)) {
+                status |= E_WRONG_FILE_PERMISSIONS;
+                status |= E_CANNOT_ACCESS_DAT_FILES;
+            }
+        }
+
+        LOTRO_DAT::DatFile test_file;
+        auto result = test_file.Initialise(client_local_filepath.toStdString(), 0);
+        if (!result.result) {
+            if (result.msg == "DATINIT: Error, cannot initialize dat due to internal IO error") {
+                status |= E_CANNOT_ACCESS_DAT_FILES;
+            } else {
+                status |= E_DAT_FILE_INCORRECT;
+            }
+        }
+
+        result = test_file.Initialise(client_general_filepath.toStdString(), 0);
+        if (!result.result) {
+            if (result.msg == "DATINIT: Error, cannot initialize dat due to internal IO error") {
+                status |= E_CANNOT_ACCESS_DAT_FILES;
+            } else {
+                status |= E_DAT_FILE_INCORRECT;
+            }
+        }
+    }
+
+    if (!urlExists(QUrl(Settings::getValue("Network/patch_updates_url").toString()))) {
+        status |= E_NO_SERVER_CONNECTION;
+    }
+
+    qDebug() << "LegacyApplication: StatusCheck: " << ErrorStatus(status);
+    return ErrorStatus(status);
+}
+
+void LegacyApplication::TryToInitFunctionalModules()
+{
+    if (seconds_after_previous_try_to_init_ + 1 < try_to_init_timeout_) {
+        seconds_after_previous_try_to_init_++;
+        emit SecondsToNextTryToInitChanged(try_to_init_timeout_ - seconds_after_previous_try_to_init_);
+        return;
+    }
+
+    try_to_init_functional_modules_timer_.stop();
+    seconds_after_previous_try_to_init_ = 0;
+    ErrorStatus status = CheckErrorStatus();
+    emit ErrorStatusChanged(status);
+
+    if (status == E_NO_ERRORS) {
+        patch_list->initialize();
+        patch_list->startAutoUpdate();
+    } else {
+        qWarning() << "LegacyApplication: Couldnt init functional modules!";
+        try_to_init_functional_modules_timer_.start();
+    }
+}
+
+QDebug operator<<(QDebug dbg, const LegacyApplication::ErrorStatus &status)
+{
+    bool wrong_game_folder = (status & LegacyApplication::E_WRONG_GAME_FOLDER);
+    bool dat_files_missing = (status & LegacyApplication::E_DAT_FILES_MISSING);
+    bool wrong_file_permissions = (status & LegacyApplication::E_WRONG_FILE_PERMISSIONS);
+    bool cannot_access_dat_files = (status & LegacyApplication::E_CANNOT_ACCESS_DAT_FILES);
+    bool dat_file_incorrect = (status & LegacyApplication::E_DAT_FILE_INCORRECT);
+    bool no_server_connection = (status & LegacyApplication::E_NO_SERVER_CONNECTION);
+
+    dbg << "ErrorStatus( "
+        << (wrong_game_folder ? "Wrong game folder!" : "")
+        << (dat_files_missing ? "Dat files missing!" : "")
+        << (wrong_file_permissions ? "Wrong file permissions!" : "")
+        << (cannot_access_dat_files ? "Cannot access dat files!" : "")
+        << (dat_file_incorrect ? "Incorrect dat file!" : "")
+        << (no_server_connection ? "No server connection!" : "")
+        <<")";
+
+    return dbg;
+}

+ 36 - 8
src/Legacy/legacyapplication.h

@@ -3,25 +3,53 @@
 
 #include <QApplication>
 #include <QThread>
+#include <QTimer>
 
-#include "models/lotrodatmanager.h"
-#include "models/patchlist.h"
-#include "widgets/mainwindow.h"
+class LotroDatManager;
+class PatchList;
+class MainWindow;
 
 class LegacyApplication : public QApplication
 {
+    Q_OBJECT
+
 public:
+    enum ErrorStatus : int {
+        E_WRONG_GAME_FOLDER = 32,
+        E_DAT_FILES_MISSING = 16,
+        E_WRONG_FILE_PERMISSIONS = 8,
+        E_CANNOT_ACCESS_DAT_FILES = 4,
+        E_DAT_FILE_INCORRECT = 2,
+        E_NO_SERVER_CONNECTION = 1,
+        E_NO_ERRORS = 0
+    };
+
     LegacyApplication(int &argc, char** argv);
 
     bool init();
 
-private:
-    LotroDatManager *lotro_dat_manager;
-    QThread *lotro_dat_manager_thread;
+signals:
+    void ErrorStatusChanged(ErrorStatus status);
+
+    void SecondsToNextTryToInitChanged(size_t seconds_elapsed);
 
-    PatchList *patch_list;
+public slots:
+    void TryToInitFunctionalModules();
 
-    MainWindow *gui;
+private:
+    ErrorStatus CheckErrorStatus();
+
+private:
+    LotroDatManager *lotro_dat_manager = nullptr;
+    QThread *lotro_dat_manager_thread = nullptr;
+    PatchList *patch_list = nullptr;
+    MainWindow *gui = nullptr;
+
+    QTimer try_to_init_functional_modules_timer_;
+    size_t seconds_after_previous_try_to_init_ = 10; // init value should be equal to try_to_init_timeout_
+    const size_t try_to_init_timeout_ = 10;
 };
 
+QDebug operator<<(QDebug dbg, const LegacyApplication::ErrorStatus &status);
+
 #endif // LEGACYAPPLICATION_H

+ 15 - 0
src/Legacy/models/lotrodatmanager.cpp

@@ -128,6 +128,21 @@ bool LotroDatManager::NotPatched()
     return !client_local_file_.GetStatusModule().CheckIfNotPatched() && !client_local_file_.GetStatusModule().CheckIfNotPatched();
 }
 
+bool LotroDatManager::DatPathIsRelevant()
+{
+    QString game_folder = Settings::getValue("Lotro/game_path").toString();
+    QString locale_prefix = Settings::getValue("Lotro/original_locale").toString();
+
+    QString client_local_filepath = game_folder + "/client_local_" + locale_prefix + ".dat";
+    QString client_general_filepath = game_folder + "/client_general.dat";
+
+    QString client_local_current_path = QString::fromStdString(client_local_file_.GetIO().GetFilename().value);
+    QString client_general_current_path = QString::fromStdString(client_general_file_.GetIO().GetFilename().value);
+
+    return QFileInfo(client_local_filepath) != QFileInfo(client_local_current_path)
+            || QFileInfo(client_general_filepath) != QFileInfo(client_general_current_path);
+}
+
 void LotroDatManager::initializeManager()
 {
     emit operationStarted("initializeManager");

+ 1 - 0
src/Legacy/models/lotrodatmanager.h

@@ -45,6 +45,7 @@ public:
 
     bool NotPatched();
 
+    bool DatPathIsRelevant();
 
 public slots:
     void initializeManager();

+ 2 - 1
src/Legacy/models/patchlist.h

@@ -6,6 +6,8 @@
 #include <QThread>
 #include <QList>
 
+#include "legacyapplication.h"
+
 #include "models/patch/patch.h"
 #include "models/patch/textspatch.h"
 #include "models/patch/graphicspatch.h"
@@ -47,7 +49,6 @@ public slots:
     void forceInstallPatches();
 
     void cleanUpPatchDirectory();
-
 private slots:
     void onPatchOperationStarted(Patch::Operation operation, Patch* patch);
     void onPatchOperationFinished(Patch::Operation operation, Patch* patch);

+ 10 - 0
src/Legacy/widgets/mainwindow.cpp

@@ -391,6 +391,16 @@ void MainWindow::hideChooseVersionDialog()
     choose_locale_dialog_->hide();
 }
 
+void MainWindow::onErrorStatusChanged(LegacyApplication::ErrorStatus status)
+{
+    // TODO: Settings widget
+    status_widget_->onErrorStatusChanged(status);
+}
+
+void MainWindow::onSecondsToNextTryToInitChanged(size_t seconds_elapsed)
+{
+    status_widget_->onSecondsToNextTryToInitChanged(seconds_elapsed);
+}
 
 void MainWindow::on_minimizeButton_clicked()
 {

+ 5 - 0
src/Legacy/widgets/mainwindow.h

@@ -17,6 +17,7 @@
 #include "aboutwidget.h"
 #include "helpwidget.h"
 #include "chooseversiondialog.h"
+#include "legacyapplication.h"
 
 namespace Ui {
 class MainWindow;
@@ -26,6 +27,7 @@ class MenuEntry;
 class DialogWindow;
 class PatchList;
 
+enum LegacyApplication::ErrorStatus;
 class MainWindow : public QMainWindow
 {
     Q_OBJECT
@@ -42,6 +44,9 @@ public slots:
     void showChooseVersionDialog();
     void hideChooseVersionDialog();
 
+    void onErrorStatusChanged(LegacyApplication::ErrorStatus status);
+    void onSecondsToNextTryToInitChanged(size_t seconds_elapsed);
+
     void showMessageDialog(QObject* emitter, QString message, QString ok_button_text = "Ок", QString cancel_button_text = "Отмена");
     void updateFontSizes();
 

+ 60 - 2
src/Legacy/widgets/statuswidget.cpp

@@ -42,7 +42,8 @@ StatusWidget::StatusWidget(PatchList *legacy_patches, QWidget *parent)
     random_tooltip_generator_timer_.start();
 
     process_completed_tooltip_hide_timer_.setInterval(10 * 1000); // 10 seconds message "Installation completed" will be shown
-    connect(&process_completed_tooltip_hide_timer_, &QTimer::timeout, this, [this](){unsetToolTipMessage(E_PROCESS);});
+    process_completed_tooltip_hide_timer_.stop();
+    connect(&process_completed_tooltip_hide_timer_, &QTimer::timeout, this, [this](){unsetToolTipMessage(E_PROCESS); process_completed_tooltip_hide_timer_.stop();});
 }
 
 StatusWidget::~StatusWidget()
@@ -102,6 +103,63 @@ void StatusWidget::unsetToolTipMessage(StatusWidget::ToolTipState state)
     fadeBetweenToolTips(message_id);
 }
 
+void StatusWidget::onErrorStatusChanged(LegacyApplication::ErrorStatus status)
+{
+    if (status == LegacyApplication::E_NO_ERRORS) {
+        init_error_type_message_ = "";
+        unsetToolTipMessage(E_ERROR);
+        ui->news_list->updateNewsWidget();
+        ui->server_status_widget->updateServerStatus();
+        ui->weekly_code_widget->updateWeeklyCodeWidget();
+        return;
+    }
+
+    error_timeout_message_ = "Повторная попытка инициализации через 10 секунд";
+    if (status & LegacyApplication::E_WRONG_GAME_FOLDER) {
+        init_error_type_message_ = "Ошибка инициализации: некорректная папка с игрой!\n";
+        setToolTipMessage(init_error_type_message_ + error_timeout_message_, E_ERROR);
+        return;
+    }
+
+    if (status & LegacyApplication::E_DAT_FILES_MISSING) {
+        init_error_type_message_ = "Ошибка инициализации: отсутствуют файлы данных игры!\n";
+        setToolTipMessage(init_error_type_message_ + error_timeout_message_, E_ERROR);
+        return;
+    }
+
+    if (status & LegacyApplication::E_WRONG_FILE_PERMISSIONS) {
+        init_error_type_message_ = "Ошибка инициализации: недостаточно прав для изменения файлов данных!\n";
+        setToolTipMessage(init_error_type_message_ + error_timeout_message_, E_ERROR);
+        return;
+    }
+
+    if (status & LegacyApplication::E_CANNOT_ACCESS_DAT_FILES) {
+        init_error_type_message_ = "Ошибка инициализации: нет доступа к файлам данных!\n";
+        setToolTipMessage(init_error_type_message_ + error_timeout_message_, E_ERROR);
+        return;
+    }
+
+    if (status & LegacyApplication::E_DAT_FILE_INCORRECT) {
+        init_error_type_message_ = "Ошибка инициализации: формат файла данных устарел или некорректен!\n";
+        setToolTipMessage(init_error_type_message_ + error_timeout_message_, E_ERROR);
+        return;
+    }
+
+    if (status & LegacyApplication::E_NO_SERVER_CONNECTION) {
+        init_error_type_message_ = "Ошибка инициализации: нет связи с сервером Наследия!\n";
+        setToolTipMessage(init_error_type_message_ + error_timeout_message_, E_ERROR);
+        return;
+    }
+}
+
+void StatusWidget::onSecondsToNextTryToInitChanged(size_t seconds_elapsed)
+{
+    if (!init_error_type_message_.isEmpty()) {
+        error_timeout_message_ = "Повторная попытка инициализации через " + QString::number(seconds_elapsed) + " секунд";
+        setToolTipMessage(init_error_type_message_ + error_timeout_message_, E_ERROR);
+    }
+}
+
 void StatusWidget::resizeEvent(QResizeEvent *)
 {
     double coefficient = window_width / default_window_width;
@@ -200,7 +258,7 @@ void StatusWidget::on_game_button_clicked()
 
 void StatusWidget::updatePatchProgressStatus(Patch::OperationProgress progress)
 {
-    if (last_statusbar_update_time_.elapsed() > 400) {
+    if (last_statusbar_update_time_.elapsed() > 200) {
         QString text = "Выполнение операций...";
         if (progress.download_total_bytes != 0) {
             text += "\nЗагрузка данных: " + QString::number(progress.getDownloadPercent(), 'f', 1) + "% ("

+ 9 - 0
src/Legacy/widgets/statuswidget.h

@@ -10,6 +10,7 @@
 #include <QLabel>
 
 #include "models/patchlist.h"
+#include "legacyapplication.h"
 
 namespace Ui {
 class StatusWidget;
@@ -38,6 +39,11 @@ public slots:
 
     void unsetToolTipMessage(ToolTipState state);
 
+    // for LegacyApplication signals
+    void onErrorStatusChanged(LegacyApplication::ErrorStatus status);
+
+    void onSecondsToNextTryToInitChanged(size_t seconds_elapsed);
+
 protected:
     void resizeEvent(QResizeEvent *event) override;
 
@@ -80,6 +86,9 @@ private:
 
     QTimer random_tooltip_generator_timer_;
     QTimer process_completed_tooltip_hide_timer_;
+
+    QString init_error_type_message_ = "";
+    QString error_timeout_message_ = "";
 };
 
 #endif // STATUSWIDGET_H