diff --git a/.github/workflows/build-clifp-linux.yml b/.github/workflows/build-clifp-linux.yml index a111b8d..6c95f64 100644 --- a/.github/workflows/build-clifp-linux.yml +++ b/.github/workflows/build-clifp-linux.yml @@ -18,35 +18,15 @@ jobs: fail-fast: false matrix: os: [ubuntu-20.04, ubuntu-22.04] - compiler: [clang] - lib_linkage: [shared, static] - include: - - os: ubuntu-20.04 - compiler: clang - c_comp: clang-12 - cxx_comp: clang++-12 - qt_comp: clang12 - - os: ubuntu-22.04 - compiler: gcc - c_comp: gcc-12 - cxx_comp: g++-12 - qt_comp: clang14 - lib_linkage: shared - - os: ubuntu-22.04 - compiler: gcc - c_comp: gcc-12 - cxx_comp: g++-12 - qt_comp: clang14 - lib_linkage: static - - os: ubuntu-22.04 - compiler: clang - c_comp: clang-14 - cxx_comp: clang++-14 - qt_comp: clang14 - - lib_linkage: shared - cmake_bsl: ON - - lib_linkage: static - cmake_bsl: OFF + compiler: [{c: gcc-12, cxx: g++-12, qt: clang14}, {c: clang-12, cxx: clang++-12, qt: clang12}, {c: clang-14, cxx: clang++-14, qt: clang14}] + linkage: [{type: shared, cmake_bsl: ON}, {type: static, cmake_bsl: OFF}] + exclude: + - os: ubuntu-20.04 + compiler: {c: gcc-12, cxx: g++-12, qt: clang14} + - os: ubuntu-20.04 + compiler: {c: clang-14, cxx: clang++-14, qt: clang14} + - os: ubuntu-22.04 + compiler: {c: clang-12, cxx: clang++-12, qt: clang12} runs-on: ${{ matrix.os }} env: cmake_gen: Ninja Multi-Config @@ -60,21 +40,14 @@ jobs: - name: Install Qt (custom build) uses: oblivioncth/actions/general/install-and-cache-qt-from-ffynnon@dev with: - version: 6.6.0 + version: 6.7.2 os: linux - compiler: ${{ matrix.qt_comp }} - linkage: ${{ matrix.lib_linkage }} + compiler: ${{ matrix.compiler.qt }} + linkage: ${{ matrix.linkage.type }} path: ${{ env.qt_install_dir }} credentials: ${{ secrets.qt_ffynnon_cred }} - name: Update package index run: sudo apt-get update - - name: WORKAROUND FOR https://github.com/actions/runner-images/issues/8659 - if: matrix.os == 'ubuntu-22.04' - run: | - echo "TEMPORARY WORKAROUND FOR GITHUB RUNNER BUG #8659\n\nRemoving GCC 13 as it breaks Clang14" - sudo rm -f /etc/apt/sources.list.d/ubuntu-toolchain-r-ubuntu-test-jammy.list - sudo apt-get update - sudo apt-get install -y --allow-downgrades libc6=2.35-0ubuntu3.4 libc6-dev=2.35-0ubuntu3.4 libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04 - name: Install OpenGL lib run: sudo apt-get install libglu1-mesa-dev - name: Install XCB Related libs @@ -90,8 +63,17 @@ jobs: run: sudo apt-get install libegl1-mesa-dev - name: Install Wayland run: sudo apt-get install libwayland-dev + - name: Install DBus [22.04] + if: matrix.os == 'ubuntu-22.04' + run: sudo apt-get install libdbus-1-dev + - name: Install libzstd-dev + run: sudo apt-get install libzstd-dev + - name: Install libdbus-1-dev + run: sudo apt-get install libdbus-1-dev + - name: Install libbrotli-dev + run: sudo apt-get install libbrotli-dev - name: Checkout CLIFp - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: ${{ env.clifp_src_suffix }} fetch-depth: 0 # Required for verbose versioning to work correctly @@ -99,7 +81,7 @@ jobs: working-directory: ${{ env.clifp_src_dir }} run: | echo Configuring CMake... - "$qt_cmake" -G "$cmake_gen" -S "$clifp_src_dir" -B "$clifp_build_dir" -D BUILD_SHARED_LIBS="${{ matrix.cmake_bsl }}" -D CMAKE_CXX_COMPILER="${{ matrix.cxx_comp }}" -D CMAKE_C_COMPILER="${{ matrix.c_comp }}" + "$qt_cmake" -G "$cmake_gen" -S "$clifp_src_dir" -B "$clifp_build_dir" -D BUILD_SHARED_LIBS="${{ matrix.linkage.cmake_bsl }}" -D CMAKE_CXX_COMPILER="${{ matrix.compiler.cxx }}" -D CMAKE_C_COMPILER="${{ matrix.compiler.c }}" echo Changing to build directory... cd "$clifp_build_dir" echo Building CLIFp Release... @@ -112,10 +94,10 @@ jobs: - name: Get CLIFp artifact name run: | cpack_name=$(find "${{ env.clifp_package_path }}" -type f -name "*.zip") - artifact_name="$(basename "$cpack_name" .zip) [${{ matrix.cxx_comp }}]" + artifact_name="$(basename "$cpack_name" .zip) [${{ matrix.compiler.cxx }}]" echo "current_artifact_name=$artifact_name" >> $GITHUB_ENV - name: Upload CLIFp build artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ env.current_artifact_name }} path: ${{ env.clifp_install_path }} diff --git a/.github/workflows/build-clifp-windows.yml b/.github/workflows/build-clifp-windows.yml index 57c9375..eb6f224 100644 --- a/.github/workflows/build-clifp-windows.yml +++ b/.github/workflows/build-clifp-windows.yml @@ -17,12 +17,7 @@ jobs: strategy: fail-fast: false matrix: - lib_linkage: [shared, static] - include: - - lib_linkage: shared - cmake_bsl: ON - - lib_linkage: static - cmake_bsl: OFF + linkage: [{type: shared, cmake_bsl: ON}, {type: static, cmake_bsl: OFF}] runs-on: windows-latest env: vs_dir: C:/Program Files/Microsoft Visual Studio/2022/Enterprise @@ -37,14 +32,14 @@ jobs: - name: Install Qt (custom build) uses: oblivioncth/actions/general/install-and-cache-qt-from-ffynnon@dev with: - version: 6.6.0 + version: 6.7.2 os: windows compiler: msvc2022 - linkage: ${{ matrix.lib_linkage }} + linkage: ${{ matrix.linkage.type }} path: ${{ env.qt_install_dir }} credentials: ${{ secrets.qt_ffynnon_cred }} - name: Checkout CLIFp - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: ${{ env.clifp_src_suffix }} fetch-depth: 0 # Required for verbose versioning to work correctly @@ -55,7 +50,7 @@ jobs: echo "Setup C++ Build Environment..." CALL "${{ env.vs_dir }}\Common7\Tools\VsDevCmd.bat" -arch=amd64 echo "Configure CMake using Qt wrapper..." - CALL "${{ env.qt_cmake }}" -G "${{ env.cmake_gen }}" -S "${{ env.clifp_src_dir}}" -B "${{ env.clifp_build_dir }}" -D BUILD_SHARED_LIBS=${{ matrix.cmake_bsl }} + CALL "${{ env.qt_cmake }}" -G "${{ env.cmake_gen }}" -S "${{ env.clifp_src_dir}}" -B "${{ env.clifp_build_dir }}" -D BUILD_SHARED_LIBS=${{ matrix.linkage.cmake_bsl }} echo "Changing to build directory..." cd "%clifp_build_dir%" echo "Building CLIFp release..." @@ -70,7 +65,7 @@ jobs: $artifact_name=$((Get-ChildItem -Path "${{ env.clifp_package_path }}" -Filter *.zip)[0].BaseName) + ' [msvc]' echo "current_artifact_name=$artifact_name" >> $Env:GITHUB_ENV - name: Upload CLIFp build artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ env.current_artifact_name }} path: ${{ env.clifp_install_path }} diff --git a/.github/workflows/master-pull-request-merge-reaction.yml b/.github/workflows/master-pull-request-merge-reaction.yml index 7ccf682..065604b 100644 --- a/.github/workflows/master-pull-request-merge-reaction.yml +++ b/.github/workflows/master-pull-request-merge-reaction.yml @@ -84,7 +84,7 @@ jobs: - name: Checkout repo uses: actions/checkout@v4 - name: Setup pages - uses: actions/configure-pages@v3 + uses: actions/configure-pages@v4 - name: Upload pages artifact uses: actions/upload-pages-artifact@v1 with: @@ -100,7 +100,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download built CLIFp artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: path: ${{ env.artifacts_path }} - name: Zip up release artifacts diff --git a/.gitignore b/.gitignore index 4bd8fc8..8d76c0c 100644 --- a/.gitignore +++ b/.gitignore @@ -66,6 +66,9 @@ Thumbs.db # Python byte code *.pyc +# Build within source +/build + # Binaries # -------- *.dll diff --git a/CMakeLists.txt b/CMakeLists.txt index 070179c..457b3ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,14 +6,14 @@ cmake_minimum_required(VERSION 3.24.0...3.26.0) # Project # NOTE: DON'T USE TRAILING ZEROS IN VERSIONS project(CLIFp - VERSION 0.9.10 + VERSION 0.9.11 LANGUAGES CXX DESCRIPTION "Command-line Interface for Flashpoint Archive" ) # Get helper scripts include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/FetchOBCMake.cmake) -fetch_ob_cmake("v0.3.4") +fetch_ob_cmake("v0.3.5") # Initialize project according to standard rules include(OB/Project) @@ -21,7 +21,7 @@ ob_standard_project_setup() # Additional Project Variables set(PROJECT_FORMAL_NAME "CLI Flashpoint") -set(TARGET_FP_VERSION_PREFIX 12.1) +set(TARGET_FP_VERSION_PREFIX 13.0) # Configuration options # Handled by fetched libs, but set this here formally since they aren't part of the main project @@ -44,12 +44,17 @@ endif() # Import Qt set(CLIFP_QT_COMPONENTS Core + Core5Compat Gui Widgets Sql Network ) +if(CMAKE_SYSTEM_NAME STREQUAL Linux) + list(APPEND CLIFP_QT_COMPONENTS WaylandClient) # To enable wayland support +endif() + # Find Qt package add_compile_definitions(QT_DISABLE_DEPRECATED_BEFORE=0x060000) find_package(Qt6 REQUIRED COMPONENTS ${CLIFP_QT_COMPONENTS}) @@ -72,14 +77,14 @@ endif() include(OB/FetchQx) ob_fetch_qx( - REF "v0.5.6" + REF "v0.5.7" COMPONENTS ${CLIFP_QX_COMPONENTS} ) # Fetch libfp (build and import from source) include(OB/Fetchlibfp) -ob_fetch_libfp("v0.5.2") +ob_fetch_libfp("v0.5.3") # Fetch QI-QMP (build and import from source) include(OB/FetchQI-QMP) diff --git a/README.md b/README.md index ab4dad9..d9f690e 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Other than a few pop-up dialogs used for alerts and errors, CLIFp runs completel ## Quickstart -Download the latest **static** [release](https://github.com/oblivioncth/CLIFp/releases) that's appropriate for your system and place it in the root of your Flashpoint directory. +Download the latest [release](https://github.com/oblivioncth/CLIFp/releases) that's appropriate for your system (see [Release Builds](#release-builds) for guidance on that) and place it in the root of your Flashpoint directory. Play a game: @@ -304,4 +304,31 @@ The functionality of the tray icon may be expanded upon in future releases. - [OBCMake](https://github.com/oblivioncth/OBCmake) ### Details -The source for this project is managed by a sensible CMake configuration that allows for straightforward compilation and consumption of its target(s), either as a sub-project or as an imported package. All required dependencies except for Qt6 are automatically acquired via CMake's FetchContent mechanism. \ No newline at end of file +The source for this project is managed by a sensible CMake configuration that allows for straightforward compilation and consumption of its target(s), either as a sub-project or as an imported package. All required dependencies except for Qt6 are automatically acquired via CMake's FetchContent mechanism. + +## Release Builds +For guidance on which release build is likely best for your system, compare your system to the table below and try whichever is most comparable to yours in the order shown. The compiler used is somewhat arbitrary, so equivalent compilers are combined in the table. If one build type with a given compiler does not work for you it's unlikely the other listed as part of the same line will, but you can try it if you want. + +| System | Builds | +| ----------------------------------------- | ------------------------------------------------------------------------ | +| Windows | 1) Windows Static
2) Windows Shared | +| Ubuntu 24.04, Ubuntu 22.02, Arch, SteamOS | 1) Linux Static Clang++14/GCC++12
2) Linux Shared Clang++14/GCC++12 | +| Ubuntu 20.04 | 1) Linux Static Clang++12
2) Linux Shared Clang++12 | + +Note that for shared builds, you must place the entire directory structure of the release in your Flashpoint folder and run CLIFp from within it's bin directory, like so: +``` +Flashpoint/ +├── CLIFp/ +│ ├── bin/ +│ │ └── clifp.exe +│ ├── cmake +│ ├── LICENSE +│ ├── README.md +│ └── ... +├── version.txt +├── preferences.json +└── ... +``` +For static builds you can simply take the CLIFp executable out of the bin directory and place it directly in your Flashpoint folder. + +Remember these are just guidelines. Ultimately use whichever build you like, or build CLIFp yourself. \ No newline at end of file diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 2bd230b..eca7181 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -51,8 +51,8 @@ set(CLIFP_SOURCE tools/blockingprocessmanager.cpp tools/deferredprocessmanager.h tools/deferredprocessmanager.cpp - tools/mounter_proxy.h - tools/mounter_proxy.cpp + tools/mounter_game_server.h + tools/mounter_game_server.cpp tools/mounter_qmp.h tools/mounter_qmp.cpp tools/mounter_router.h diff --git a/app/src/command/c-download.cpp b/app/src/command/c-download.cpp index fb8ad67..d921e68 100644 --- a/app/src/command/c-download.cpp +++ b/app/src/command/c-download.cpp @@ -75,6 +75,10 @@ Qx::Error CDownload::perform() const Fp::Toolkit* tk = mCore.fpInstall().toolkit(); for(const auto& pg : pItr->playlistGames()) { + /* TODO: This doesn't handle Game Redirects, i.e. if one ID on a playlist becomes a redirect entry in the future. + * Either need to add redirects here, or implement them in the DB module of libfp (full implementation). + */ + // Get data Fp::GameData gameData; if(Fp::DbError gdErr = db->getGameData(gameData, pg.gameId()); gdErr.isValid()) @@ -92,8 +96,13 @@ Qx::Error CDownload::perform() if(tk->datapackIsPresent(gameData)) continue; - // Queue download - downloadTask->addFile({.target = tk->datapackUrl(gameData), .dest = tk->datapackPath(gameData), .checksum = gameData.sha256()}); + // Queue download, if possible + TDownloadError packError = downloadTask->addDatapack(tk, &gameData); + if(packError.isValid()) + { + postError(packError); + return packError; + } // Note data id dataIds.append(gameData.id()); diff --git a/app/src/command/c-play.cpp b/app/src/command/c-play.cpp index 5331cc2..4de0a1d 100644 --- a/app/src/command/c-play.cpp +++ b/app/src/command/c-play.cpp @@ -244,7 +244,7 @@ Qx::Error CPlay::enqueueAdditionalApp(const Fp::AddApp& addApp, const QString& p TExec* addAppTask = new TExec(&mCore); addAppTask->setIdentifier(addApp.name()); addAppTask->setStage(taskStage); - addAppTask->setExecutable(addAppPathInfo.canonicalFilePath()); + addAppTask->setExecutable(QDir::cleanPath(addAppPathInfo.absoluteFilePath())); // Like canonical but doesn't care if path DNE addAppTask->setDirectory(addAppPathInfo.absoluteDir()); addAppTask->setParameters(addApp.launchCommand()); addAppTask->setEnvironment(mCore.childTitleProcessEnvironment()); @@ -272,7 +272,7 @@ Qx::Error CPlay::enqueueGame(const Fp::Game& game, const Fp::GameData& gameData, TExec* gameTask = new TExec(&mCore); gameTask->setIdentifier(game.title()); gameTask->setStage(taskStage); - gameTask->setExecutable(gamePathInfo.canonicalFilePath()); + gameTask->setExecutable(QDir::cleanPath(gamePathInfo.absoluteFilePath())); // Like canonical but doesn't care if path DNE gameTask->setDirectory(gamePathInfo.absoluteDir()); gameTask->setParameters(!gameData.isNull() ? gameData.launchCommand() : game.launchCommand()); gameTask->setEnvironment(mCore.childTitleProcessEnvironment()); @@ -314,6 +314,9 @@ Qx::Error CPlay::perform() else if(Qx::Error ide = getTitleId(titleId); ide.isValid()) return ide; + // Bail if ID is missing (user cancel) + if(titleId.isNull()) + return CPlayError(); logEvent(LOG_EVENT_HANDLING_AUTO); diff --git a/app/src/command/c-run.cpp b/app/src/command/c-run.cpp index 76a9553..36218c1 100644 --- a/app/src/command/c-run.cpp +++ b/app/src/command/c-run.cpp @@ -53,8 +53,8 @@ Qx::Error CRun::perform() TExec* runTask = new TExec(&mCore); runTask->setIdentifier(NAME + u" program"_s); runTask->setStage(Task::Stage::Primary); - runTask->setExecutable(inputInfo.canonicalFilePath()); - runTask->setDirectory(inputInfo.canonicalPath()); + runTask->setExecutable(QDir::cleanPath(inputInfo.absoluteFilePath())); // Like canonical but doesn't care if path DNE + runTask->setDirectory(inputInfo.absolutePath()); runTask->setParameters(mParser.value(CL_OPTION_PARAM)); runTask->setEnvironment(mCore.childTitleProcessEnvironment()); runTask->setProcessType(TExec::ProcessType::Blocking); diff --git a/app/src/command/title-command.cpp b/app/src/command/title-command.cpp index c0ba6d2..86c9e0e 100644 --- a/app/src/command/title-command.cpp +++ b/app/src/command/title-command.cpp @@ -210,6 +210,10 @@ Qx::Error TitleCommand::getTitleId(QUuid& id) postError(err); return err; } + QUuid origId = titleId; + titleId = mCore.fpInstall().database()->handleGameRedirects(titleId); // Redirect shortcut + if(titleId != origId) + logEvent(LOG_EVENT_GAME_REDIRECT.arg(origId.toString(QUuid::WithoutBraces), titleId.toString(QUuid::WithoutBraces))); } else if(mParser.isSet(CL_OPTION_TITLE) || mParser.isSet(CL_OPTION_TITLE_STRICT)) { diff --git a/app/src/command/title-command.h b/app/src/command/title-command.h index 9f6cc18..eb0a1f9 100644 --- a/app/src/command/title-command.h +++ b/app/src/command/title-command.h @@ -59,6 +59,7 @@ class TitleCommand : public Command static inline const QString LOG_EVENT_SEL_RAND = u"Selecting a playable title at random..."_s; static inline const QString LOG_EVENT_INIT_RAND_ID = u"Randomly chose primary title is \"%1\""_s; static inline const QString LOG_EVENT_INIT_RAND_PLAY_ADD_COUNT = u"Chosen title has %1 playable additional-apps"_s; + static inline const QString LOG_EVENT_GAME_REDIRECT = u"Game redirected: %1 -> %2"_s; static inline const QString LOG_EVENT_RAND_DET_PRIM = u"Selected primary title"_s; static inline const QString LOG_EVENT_RAND_DET_ADD_APP = u"Selected additional-app \"%1\""_s; static inline const QString LOG_EVENT_RAND_GET_INFO = u"Querying random game info..."_s; diff --git a/app/src/kernel/core.cpp b/app/src/kernel/core.cpp index bf0b114..3521113 100644 --- a/app/src/kernel/core.cpp +++ b/app/src/kernel/core.cpp @@ -295,7 +295,7 @@ void Core::logEvent(const QString& event) { logEvent(NAME, event); } void Core::logTask(const Task* task) { logTask(NAME, task); } ErrorCode Core::logFinish(const Qx::Error& errorState) { return logFinish(NAME, errorState); } void Core::postError(const Qx::Error& error, bool log) { postError(NAME, error, log); } -int Core::postBlockingError(const Qx::Error& error, bool log, QMessageBox::StandardButtons bs, QMessageBox::StandardButton def) { return postBlockingError(NAME, error, log); } +int Core::postBlockingError(const Qx::Error& error, bool log, QMessageBox::StandardButtons bs, QMessageBox::StandardButton def) { return postBlockingError(NAME, error, log, bs, def); } //Public: Qx::Error Core::initialize(QStringList& commandLine) @@ -436,37 +436,58 @@ void Core::attachFlashpoint(std::unique_ptr flashpointInstall) mFlashpointInstall = std::move(flashpointInstall); // Note install details - logEvent(LOG_EVENT_FLASHPOINT_VERSION_TXT.arg(mFlashpointInstall->nameVersionString())); - logEvent(LOG_EVENT_FLASHPOINT_VERSION.arg(mFlashpointInstall->version().toString())); - logEvent(LOG_EVENT_FLASHPOINT_EDITION.arg(ENUM_NAME(mFlashpointInstall->edition()))); + auto info = mFlashpointInstall->versionInfo(); + logEvent(LOG_EVENT_FLASHPOINT_VERSION_TXT.arg(info->fullString())); + logEvent(LOG_EVENT_FLASHPOINT_VERSION.arg(info->version().toString())); + logEvent(LOG_EVENT_FLASHPOINT_EDITION.arg(ENUM_NAME(info->edition()))); logEvent(LOG_EVENT_OUTFITTED_DAEMON.arg(ENUM_NAME(mFlashpointInstall->outfittedDaemon()))); // Initialize child process env vars QProcessEnvironment de = QProcessEnvironment::systemEnvironment(); - QString fpPath = mFlashpointInstall->dir().absolutePath(); + const QString fpPath = mFlashpointInstall->dir().absolutePath(); #ifdef __linux__ + //-Mimic startup script setup------------------------- + logEvent(LOG_EVENT_LINUX_SPECIFIC_STARTUP_STEPS); + + QString winFpPath = u"Z:"_s + QString(fpPath).replace('/', '\\'); + bool immutable = mFlashpointInstall->dir().exists(u"Libraries"_s); // NOTE: This check likely will need to be modified over time + logEvent(LOG_EVENT_LINUX_BUILD_TYPE.arg(immutable ? u"isn't"_s : u"is"_s)); + // Add platform support environment variables - if(mFlashpointInstall->outfittedDaemon() == Fp::Daemon::Qemu) // Appimage based build + de.insert(u"DIR"_s, fpPath); + de.insert(u"FP_STARTUP_PATH"_s, winFpPath + u"\\FPSoftware"_s); + de.insert(u"FP_BROWSER_PLUGINS"_s, winFpPath + u"\\FPSoftware\\BrowserPlugins"_s); + de.insert(u"WINEPREFIX"_s, fpPath + u"/FPSoftware/Wine"_s); + + if(immutable) { + de.insert(u"LD_LIBRARY_PATH"_s, fpPath + u"/Libraries/lib"_s); QString pathValue = de.value(u"PATH"_s); - pathValue.prepend(fpPath + u"/FPSoftware/FPWine/bin:"_s + fpPath + u"/FPSoftware/FPQemuPHP:"_s); + pathValue.prepend(fpPath + u"/Libraries/bin:"_s); de.insert(u"PATH"_s, pathValue); qputenv("PATH", pathValue.toLocal8Bit()); // Path needs to be updated for self as well - de.insert(u"GTK_USE_PORTAL"_s, "1"); - de.remove(u"LD_PRELOAD"_s); - } - else // Regular Linux build - { - QString winFpPath = u"Z:"_s + fpPath; - - de.insert(u"DIR"_s, fpPath); - de.insert(u"WINDOWS_DIR"_s, winFpPath); - de.insert(u"FP_STARTUP_PATH"_s, winFpPath + u"\\FPSoftware"_s); - de.insert(u"FP_BROWSER_PLUGINS"_s, winFpPath + u"\\FPSoftware\\BrowserPlugins"_s); - de.insert(u"WINEPREFIX"_s, fpPath + u"/FPSoftware/Wine"_s); + /* This step likely isn't necesary, as it seems to be done only for the launcher, which of course + * we replace (and we use Qt so GTK doesn't really come into play); however, since this variable + * is exported it does affect child processes so we include this anyway just in case. + */ + int gtkRes = QProcess::execute(u"sh"_s, {u"-c"_s, u"ldconfig -p | grep libgtk-3.so >/dev/null 2>&1"_s}); + if(gtkRes != 0) + { + logEvent(LOG_EVENT_LINUX_GTK3_MISSING); + de.insert(u"GTK_USE_PORTAL"_s, "1"); + } } + else + de.insert(u"LD_LIBRARY_PATH"_s, fpPath + u"/Legacy"_s); + + /* Ensure ruffle has execute permissions (may not be present). The launcher delays this action until 15 + * seconds after start since it may update Ruffle, but we don't touch it so we don't have to wait + */ + QFile ruffle(fpPath + u"/Data/Ruffle/standalone/latest/ruffle"_s); // TODO: Might want to add this under Fp::Install + if(!ruffle.setPermissions(QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther)) + logEvent(LOG_ERR_FAILED_SETTING_RUFFLE_PERMS); #endif TExec::setDefaultProcessEnvironment(de); @@ -559,6 +580,43 @@ CoreError Core::enqueueStartupTasks(const QString& serverOverride) mTaskQueue.push(xhostSet); logTask(xhostSet); } + + // Setup WINE prefix if not present + QDir winePrefix(mFlashpointInstall->dir().absolutePath() + u"/FPSoftware/Wine"_s); + if(!winePrefix.exists()) + { + // Copy backup prefix + QDir winePrefixBak(mFlashpointInstall->dir().absolutePath() + u"/FPSoftware/.winebak"_s); + if(Qx::copyDirectory(winePrefixBak, winePrefix, true, Qx::ReplaceMode::Skip).isFailure()) + qWarning("Failed to restore WINE prefix backup"); + + // Setup WINE registry modification + auto wineEnv = TExec::defaultProcessEnvironment(); + wineEnv.insert(u"WINEDLLOVERRIDES"_s, u"control.exe,explorer.exe,mscoree,plugplay.exe,services.exe,winedevice.exe,winemenubuilder.exe=d"_s); + + TExec* wineReg = new TExec(this); + wineReg->setIdentifier(u"WINE Registry Setup"_s); + wineReg->setStage(Task::Stage::Startup); + wineReg->setExecutable(u"wine"_s); + wineReg->setDirectory(mFlashpointInstall->dir()); + wineReg->setParameters(QStringList{ + u"reg"_s, + u"add"_s, + u"HKLM\\Software\\Wine\\MSHTML\\2.47.4"_s, + u"/v"_s, + u"GeckoPath"_s, + u"/d"_s, + u"C:\\windows\\syswow64\\gecko\\2.47.4\\wine_gecko\\"_s, + u"/reg:32"_s, + u"/f"_s + }); + wineReg->setEnvironment(wineEnv); + wineReg->setProcessType(TExec::ProcessType::Blocking); + + mTaskQueue.push(wineReg); + logTask(wineReg); + } + #endif // Get settings @@ -693,6 +751,22 @@ void Core::enqueueShutdownTasks() mTaskQueue.push(xhostClear); logTask(xhostClear); } + + // Backup WINE flash saves + QString uname = qgetenv("USER"); + QDir saveSrc(mFlashpointInstall->dir().absoluteFilePath(u"FPSoftware/Wine/drive_c/users/"_s + uname + u"/AppData/Roaming/Macromedia/Flash Player/#SharedObjects"_s)); + QDir saveDest(mFlashpointInstall->dir().absoluteFilePath(u"FPSoftware/.winebak/drive_c/users/"_s + uname + u"/AppData/Roaming/Macromedia/Flash Player"_s)); + + TGeneric* wineSaveBackup = new TGeneric(this); + wineSaveBackup->setStage(Task::Stage::Shutdown); + wineSaveBackup->setDescription(u"Backup Flash WINE saves"_s); + wineSaveBackup->setAction([saveSrc, saveDest]{ + saveDest.mkpath(u"."_s); + return Qx::copyDirectory(saveSrc, saveDest, true, Qx::ReplaceMode::Skip); + }); + + mTaskQueue.push(wineSaveBackup); + logTask(wineSaveBackup); #endif } @@ -733,20 +807,23 @@ Qx::Error Core::enqueueDataPackTasks(const Fp::GameData& gameData) const Fp::Toolkit* tk = mFlashpointInstall->toolkit(); - QString packFilename = gameData.path(); QString packPath = tk->datapackPath(gameData); + QString packFilename = tk->datapackFilename(gameData); // Enqueue pack download if it's not available if(!tk->datapackIsPresent(gameData)) { logEvent(LOG_EVENT_DATA_PACK_MISS); - QUrl packUrl = tk->datapackUrl(gameData); - TDownload* downloadTask = new TDownload(this); downloadTask->setStage(Task::Stage::Auxiliary); downloadTask->setDescription(u"data pack "_s + packFilename); - downloadTask->addFile({.target = packUrl, .dest = packPath, .checksum = gameData.sha256()}); + TDownloadError packError = downloadTask->addDatapack(tk, &gameData); + if(packError.isValid()) + { + postError(packError); + return packError; + } mTaskQueue.push(downloadTask); logTask(downloadTask); diff --git a/app/src/kernel/core.h b/app/src/kernel/core.h index 8c1a6cd..c31cc64 100644 --- a/app/src/kernel/core.h +++ b/app/src/kernel/core.h @@ -58,7 +58,7 @@ class QX_ERROR_TYPE(CoreError, "CoreError", 1200) {TitleNotFound, u"Could not find the title in the Flashpoint database."_s}, {TooManyResults, u"More results than can be presented were returned in a search."_s}, {ConfiguredServerMissing, u"The configured server was not found within the Flashpoint services store."_s}, - {UnknownDatapackParam, u"Unrecognized datapack parameters were present. The game likely won't work correctly."_s}, + {UnknownDatapackParam, u"Unrecognized datapack parameters were present. The game likely won't work correctly."_s} }; //-Instance Variables------------------------------------------------------------- @@ -157,6 +157,7 @@ class Core : public QObject // Logging - Errors static inline const QString LOG_ERR_INVALID_PARAM = u"Invalid parameters provided"_s; static inline const QString LOG_ERR_CRITICAL = u"Aborting execution due to previous critical errors"_s; + static inline const QString LOG_ERR_FAILED_SETTING_RUFFLE_PERMS= u"Failed to mark ruffle as executable!"_s; // Logging - Messages static inline const QString LOG_EVENT_INIT = u"Initializing CLIFp..."_s; @@ -187,6 +188,11 @@ class Core : public QObject static inline const QString LOG_EVENT_LAUNCHER_WATCH_HOOKED = u"Launcher hooked for waiting"_s; static inline const QString LOG_EVENT_LAUNCHER_CLOSED_RESULT = u"CLIFp cannot continue running in companion mode without the launcher's services."_s; + // Logging - Linux Specific Startup Steps + static inline const QString LOG_EVENT_LINUX_SPECIFIC_STARTUP_STEPS= u"Handling Linux specific startup steps..."_s; + static inline const QString LOG_EVENT_LINUX_BUILD_TYPE = u"Linux build %1 mutable."_s; + static inline const QString LOG_EVENT_LINUX_GTK3_MISSING = u"GTK3 isn't installed, setting GTK_USE_PORTAL=1"_s; + // Logging - Title Search static inline const QString LOG_EVENT_GAME_SEARCH = u"Searching for game with title '%1'"_s; static inline const QString LOG_EVENT_ADD_APP_SEARCH = u"Searching for additional-app with title '%1' and parent %2"_s; diff --git a/app/src/kernel/driver.cpp b/app/src/kernel/driver.cpp index cc7fb75..b2e2832 100644 --- a/app/src/kernel/driver.cpp +++ b/app/src/kernel/driver.cpp @@ -163,6 +163,8 @@ void Driver::cleanup() void Driver::finish() { + logEvent(LOG_EVENT_FINISH); + // Clear update cache if(CUpdate::isUpdateCacheClearable()) { diff --git a/app/src/kernel/driver.h b/app/src/kernel/driver.h index 1451ed7..50a795d 100644 --- a/app/src/kernel/driver.h +++ b/app/src/kernel/driver.h @@ -82,6 +82,7 @@ class Driver : public QObject static inline const QString LOG_EVENT_QUIT_REQUEST_REDUNDANT = u"Received redundant quit request"_s; static inline const QString LOG_EVENT_CLEARED_UPDATE_CACHE = u"Cleared stale update cache."_s; static inline const QString LOG_EVENT_CORE_ABORT = u"Core abort signaled, quitting now."_s; + static inline const QString LOG_EVENT_FINISH = u"Finishing run..."_s; // Meta static inline const QString NAME = u"driver"_s; diff --git a/app/src/task/t-download.cpp b/app/src/task/t-download.cpp index 439fd09..7041bab 100644 --- a/app/src/task/t-download.cpp +++ b/app/src/task/t-download.cpp @@ -1,17 +1,28 @@ // Unit Include #include "t-download.h" +// Flashpoint Includes +#include + //=============================================================================================================== // TDownloadError //=============================================================================================================== //-Constructor------------------------------------------------------------- //Private: -TDownloadError::TDownloadError(Type t, const QString& s) : +TDownloadError::TDownloadError(Type t, const QString& s, const QString& d) : mType(t), - mSpecific(s) + mSpecific(s), + mDetails(d) {} +TDownloadError::TDownloadError(Qx::DownloadManagerReport dmReport) +{ + mType = dmReport.wasSuccessful() ? NoError : Incomplete; + mSpecific = dmReport.outcomeString(); + mDetails = dmReport.details(); +} + //-Instance Functions------------------------------------------------------------- //Public: bool TDownloadError::isValid() const { return mType != NoError; } @@ -23,6 +34,7 @@ Qx::Severity TDownloadError::deriveSeverity() const { return Qx::Critical; } quint32 TDownloadError::deriveValue() const { return mType; } QString TDownloadError::derivePrimary() const { return ERR_STRINGS.value(mType); } QString TDownloadError::deriveSecondary() const { return mSpecific; } +QString TDownloadError::deriveDetails() const { return mDetails; } //=============================================================================================================== // TDownload @@ -35,6 +47,7 @@ TDownload::TDownload(QObject* parent) : { // Setup download manager mDownloadManager.setOverwrite(true); + mDownloadManager.setDeletePartialDownloads(true); mDownloadManager.setVerificationMethod(QCryptographicHash::Sha256); // Download event handlers @@ -90,6 +103,20 @@ QList TDownload::files() const { return mFiles; } QString TDownload::description() const { return mDescription; } void TDownload::addFile(const Qx::DownloadTask file) { mFiles.append(file); } + +TDownloadError TDownload::addDatapack(const Fp::Toolkit* tk, const Fp::GameData* gameData) +{ + // TODO: CDownload runs this in a loop, which is why Q_UNLIKELY is used, but in the long run a different approach in which download + // ability is checked for once ahead of time is probably best + if(Q_UNLIKELY(!tk->canDownloadDatapacks())) + return TDownloadError(TDownloadError::OfflineEdition, tk->datapackFilename(*gameData)); + + // TODO: This makes it apparent that a class like "CompleteGameData" might be warranted, with a constructor that takes GameData and + // Toolkit and then produces a representation of the GameData with a complete path/url, though the URL would be null for Ultimate + addFile({.target = tk->datapackUrl(*gameData), .dest = tk->datapackPath(*gameData), .checksum = gameData->sha256()}); + return TDownloadError(); +} + void TDownload::setDescription(const QString& desc) { mDescription = desc; } void TDownload::perform() @@ -128,7 +155,7 @@ void TDownload::postDownload(Qx::DownloadManagerReport downloadReport) emitEventOccurred(LOG_EVENT_DOWNLOAD_SUCC); else { - errorStatus = TDownloadError(TDownloadError::Incomeplete, downloadReport.outcomeString()); + errorStatus = TDownloadError(downloadReport); emitErrorOccurred(errorStatus); } diff --git a/app/src/task/t-download.h b/app/src/task/t-download.h index 11e8055..1662055 100644 --- a/app/src/task/t-download.h +++ b/app/src/task/t-download.h @@ -8,6 +8,13 @@ // Project Includes #include "task/task.h" +// FP Forward Declarations +namespace Fp +{ + class Toolkit; + class GameData; +} + class QX_ERROR_TYPE(TDownloadError, "TDownloadError", 1252) { friend class TDownload; @@ -15,25 +22,29 @@ class QX_ERROR_TYPE(TDownloadError, "TDownloadError", 1252) public: enum Type { - NoError = 0, - Incomeplete = 2 + NoError, + Incomplete, + OfflineEdition }; //-Class Variables------------------------------------------------------------- private: static inline const QHash ERR_STRINGS{ {NoError, u""_s}, - {Incomeplete, u"The download(s) could not be completed."_s} + {Incomplete, u"The download(s) could not be completed."_s}, + {OfflineEdition, u"A datapack download was prompted in an offline edition of Flashpoint."_s} }; //-Instance Variables------------------------------------------------------------- private: Type mType; QString mSpecific; + QString mDetails; //-Constructor------------------------------------------------------------- private: - TDownloadError(Type t = NoError, const QString& s = {}); + TDownloadError(Type t = NoError, const QString& s = {}, const QString& d = {}); + TDownloadError(Qx::DownloadManagerReport dmReport); //-Instance Functions------------------------------------------------------------- public: @@ -46,6 +57,7 @@ class QX_ERROR_TYPE(TDownloadError, "TDownloadError", 1252) quint32 deriveValue() const override; QString derivePrimary() const override; QString deriveSecondary() const override; + QString deriveDetails() const override; }; class TDownload : public Task @@ -91,6 +103,7 @@ class TDownload : public Task QString description() const; void addFile(const Qx::DownloadTask file); + TDownloadError addDatapack(const Fp::Toolkit* tk, const Fp::GameData* gameData); void setDescription(const QString& desc); void perform() override; diff --git a/app/src/task/t-exec.cpp b/app/src/task/t-exec.cpp index 8318011..fca12c1 100644 --- a/app/src/task/t-exec.cpp +++ b/app/src/task/t-exec.cpp @@ -260,7 +260,8 @@ void TExec::perform() } if(smDeferredProcessManager) - smDeferredProcessManager->manage(mIdentifier, taskProcess); // Add process to list for deferred termination + smDeferredProcessManager->manage(mIdentifier, taskProcess); // NOLINT(clang-analyzer-cplusplus.NewDelete) Add process to list for deferred termination + else qWarning("Deferred process started without a deferred process manager installed!"); break; diff --git a/app/src/task/t-exec_linux.cpp b/app/src/task/t-exec_linux.cpp index d4fe9a6..21f173b 100644 --- a/app/src/task/t-exec_linux.cpp +++ b/app/src/task/t-exec_linux.cpp @@ -89,12 +89,11 @@ QString TExec::resolveExecutablePath() * of how the system would handle it. */ - // Exception: If a windows batch script is called for (shouldn't happen), see if there is a matching shell script - // as a last resort + // Exception: If a windows batch script is called for by this point (uncommon), see if there is a matching shell script QFileInfo execInfo(mExecutable); if(execInfo.suffix() == SHELL_EXT_WIN) { - execInfo.setFile(mExecutable.chopped(sizeof(SHELL_EXT_WIN)) + SHELL_EXT_LINUX); + execInfo.setFile(mExecutable.chopped(SHELL_EXT_WIN.size()) + SHELL_EXT_LINUX); emit eventOccurred(NAME, LOG_EVENT_FORCED_BASH); } diff --git a/app/src/task/t-mount.cpp b/app/src/task/t-mount.cpp index 1be8bf1..6f5ad07 100644 --- a/app/src/task/t-mount.cpp +++ b/app/src/task/t-mount.cpp @@ -29,7 +29,7 @@ TMount::TMount(QObject* parent) : //-Instance Functions------------------------------------------------------------- //Private: template - requires Qx::any_of + requires Qx::any_of void TMount::initMounter(M*& mounter) { mounter = new M(this); @@ -79,7 +79,7 @@ void TMount::perform() { initMounter(mMounterProxy); mMounterProxy->setFilePath(mPath); - mMounterProxy->setProxyServerPort(22501); + mMounterProxy->setGameServerPort(22501); } else { diff --git a/app/src/task/t-mount.h b/app/src/task/t-mount.h index 84dbfb9..84478d6 100644 --- a/app/src/task/t-mount.h +++ b/app/src/task/t-mount.h @@ -12,7 +12,7 @@ // Project Includes #include "task/task.h" -#include "tools/mounter_proxy.h" +#include "tools/mounter_game_server.h" #include "tools/mounter_qmp.h" #include "tools/mounter_router.h" @@ -32,7 +32,7 @@ class TMount : public Task //-Instance Variables------------------------------------------------------------------------------------------------ private: // Mounters - MounterProxy* mMounterProxy; + MounterGameServer* mMounterProxy; MounterQmp* mMounterQmp; MounterRouter* mMounterRouter; @@ -51,7 +51,7 @@ class TMount : public Task //-Instance Functions------------------------------------------------------------------------------------------------------ private: template - requires Qx::any_of + requires Qx::any_of void initMounter(M*& mounter); public: diff --git a/app/src/tools/mounter_proxy.cpp b/app/src/tools/mounter_game_server.cpp similarity index 56% rename from app/src/tools/mounter_proxy.cpp rename to app/src/tools/mounter_game_server.cpp index 1227acb..7aee5b8 100644 --- a/app/src/tools/mounter_proxy.cpp +++ b/app/src/tools/mounter_game_server.cpp @@ -1,5 +1,5 @@ // Unit Includes -#include "mounter_proxy.h" +#include "mounter_game_server.h" // Qt Includes #include @@ -20,22 +20,22 @@ //-Constructor------------------------------------------------------------- //Private: -MounterProxyError::MounterProxyError(Type t, const QString& s) : +MounterGameServerError::MounterGameServerError(Type t, const QString& s) : mType(t), mSpecific(s) {} //-Instance Functions------------------------------------------------------------- //Public: -bool MounterProxyError::isValid() const { return mType != NoError; } -QString MounterProxyError::specific() const { return mSpecific; } -MounterProxyError::Type MounterProxyError::type() const { return mType; } +bool MounterGameServerError::isValid() const { return mType != NoError; } +QString MounterGameServerError::specific() const { return mSpecific; } +MounterGameServerError::Type MounterGameServerError::type() const { return mType; } //Private: -Qx::Severity MounterProxyError::deriveSeverity() const { return Qx::Critical; } -quint32 MounterProxyError::deriveValue() const { return mType; } -QString MounterProxyError::derivePrimary() const { return ERR_STRINGS.value(mType); } -QString MounterProxyError::deriveSecondary() const { return mSpecific; } +Qx::Severity MounterGameServerError::deriveSeverity() const { return Qx::Critical; } +quint32 MounterGameServerError::deriveValue() const { return mType; } +QString MounterGameServerError::derivePrimary() const { return ERR_STRINGS.value(mType); } +QString MounterGameServerError::deriveSecondary() const { return mSpecific; } //=============================================================================================================== // Mounter @@ -43,17 +43,17 @@ QString MounterProxyError::deriveSecondary() const { return mSpecific; } //-Constructor---------------------------------------------------------------------------------------------------------- //Public: -MounterProxy::MounterProxy(QObject* parent) : +MounterGameServer::MounterGameServer(QObject* parent) : QObject(parent), mMounting(false), - mProxyServerPort(0) + mGameServerPort(0) { // Setup Network Access Manager mNam.setAutoDeleteReplies(true); - mNam.setTransferTimeout(PROXY_TRANSFER_TIMEOUT); + mNam.setTransferTimeout(GAME_SERVER_TRANSFER_TIMEOUT); // Connections - Work - connect(&mNam, &QNetworkAccessManager::finished, this, &MounterProxy::proxyMountFinishedHandler); + connect(&mNam, &QNetworkAccessManager::finished, this, &MounterGameServer::gameServerMountFinishedHandler); /* Network check (none of these should be triggered, they are here in case a FP update would required * them to be used as to help make that clear in the logs when the update causes this to stop working). @@ -76,45 +76,45 @@ MounterProxy::MounterProxy(QObject* parent) : //-Instance Functions--------------------------------------------------------------------------------------------------------- //Private: -void MounterProxy::finish(const MounterProxyError& errorState) +void MounterGameServer::finish(const MounterGameServerError& errorState) { mMounting = false; emit mountFinished(errorState); } -void MounterProxy::noteProxyRequest(QNetworkAccessManager::Operation op, const QUrl& url, QByteArrayView data) +void MounterGameServer::noteProxyRequest(QNetworkAccessManager::Operation op, const QUrl& url, QByteArrayView data) { signalEventOccurred(EVENT_REQUEST_SENT.arg(ENUM_NAME(op), url.toString(), QString::fromLatin1(data))); } -void MounterProxy::noteProxyResponse(const QString& response) +void MounterGameServer::noteProxyResponse(const QString& response) { - signalEventOccurred(EVENT_PROXY_RESPONSE.arg(response)); + signalEventOccurred(EVENT_GAMESERVER_RESPONSE.arg(response)); } -void MounterProxy::signalEventOccurred(const QString& event) { emit eventOccurred(NAME, event); } -void MounterProxy::signalErrorOccurred(const MounterProxyError& errorMessage) { emit errorOccurred(NAME, errorMessage); } +void MounterGameServer::signalEventOccurred(const QString& event) { emit eventOccurred(NAME, event); } +void MounterGameServer::signalErrorOccurred(const MounterGameServerError& errorMessage) { emit errorOccurred(NAME, errorMessage); } //Public: -bool MounterProxy::isMounting() { return mMounting; } +bool MounterGameServer::isMounting() { return mMounting; } -quint16 MounterProxy::proxyServerPort() const { return mProxyServerPort; } -QString MounterProxy::filePath() const { return mFilePath; } +quint16 MounterGameServer::gameServerPort() const { return mGameServerPort; } +QString MounterGameServer::filePath() const { return mFilePath; } -void MounterProxy::setProxyServerPort(quint16 port) { mProxyServerPort = port; } -void MounterProxy::setFilePath(const QString& path) { mFilePath = QDir::toNativeSeparators(path); } +void MounterGameServer::setGameServerPort(quint16 port) { mGameServerPort = port; } +void MounterGameServer::setFilePath(const QString& path) { mFilePath = QDir::toNativeSeparators(path); } //-Signals & Slots------------------------------------------------------------------------------------------------------------ //Private Slots: -void MounterProxy::proxyMountFinishedHandler(QNetworkReply* reply) +void MounterGameServer::gameServerMountFinishedHandler(QNetworkReply* reply) { - assert(reply == mProxyMountReply.get()); + assert(reply == mGameServerMountReply.get()); - MounterProxyError err; + MounterGameServerError err; if(reply->error() != QNetworkReply::NoError) { - err = MounterProxyError(MounterProxyError::ProxyMount, reply->errorString()); + err = MounterGameServerError(MounterGameServerError::ProxyMount, reply->errorString()); signalErrorOccurred(err); } else @@ -127,7 +127,7 @@ void MounterProxy::proxyMountFinishedHandler(QNetworkReply* reply) } //Public Slots: -void MounterProxy::mount() +void MounterGameServer::mount() { signalEventOccurred(EVENT_MOUNTING); @@ -137,35 +137,38 @@ void MounterProxy::mount() QUrl mountUrl; mountUrl.setScheme(u"http"_s); mountUrl.setHost(u"localhost"_s); - mountUrl.setPort(mProxyServerPort); + mountUrl.setPort(mGameServerPort); mountUrl.setPath(u"/fpProxy/api/mountzip"_s); // Req QNetworkRequest mountReq(mountUrl); // Header - mountReq.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - // These headers are used by the stock launcher, but don't seem to be needed - // - // mountReq.setRawHeader("Connection"_ba, "close"_ba); - // mountReq.setRawHeader("Accept"_ba, "application/json, text/plain, */*"_ba); - // mountReq.setRawHeader("Accept-Encoding"_ba, "gzip, compress, deflate, br"_ba); + mountReq.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"_ba); + /* These following headers are used by the stock launcher, but don't seem to be needed; + * however, we include them anyway out of posterity in hopes of avoiding future issues + * in case FPGS starts caring + */ + mountReq.setHeader(QNetworkRequest::UserAgentHeader, "axios/1.6.8"_ba); + mountReq.setRawHeader("Connection"_ba, "close"_ba); + mountReq.setRawHeader("Accept"_ba, "application/json, text/plain, */*"_ba); + mountReq.setRawHeader("Accept-Encoding"_ba, "gzip, compress, deflate, br"_ba); // Data QJsonDocument jdData(QJsonObject{{u"filePath"_s, mFilePath}}); QByteArray data = jdData.toJson(QJsonDocument::Compact); //-POST Request--------------------------------- - mProxyMountReply = mNam.post(mountReq, data); + mGameServerMountReply = mNam.post(mountReq, data); // Log request - noteProxyRequest(mProxyMountReply->operation(), mountUrl, data); + noteProxyRequest(mGameServerMountReply->operation(), mountUrl, data); // Await finished() signal... } -void MounterProxy::abort() +void MounterGameServer::abort() { - if(mProxyMountReply && mProxyMountReply->isRunning()) - mProxyMountReply->abort(); + if(mGameServerMountReply && mGameServerMountReply->isRunning()) + mGameServerMountReply->abort(); } diff --git a/app/src/tools/mounter_proxy.h b/app/src/tools/mounter_game_server.h similarity index 74% rename from app/src/tools/mounter_proxy.h rename to app/src/tools/mounter_game_server.h index 864d183..2a1cb74 100644 --- a/app/src/tools/mounter_proxy.h +++ b/app/src/tools/mounter_game_server.h @@ -1,5 +1,5 @@ -#ifndef MOUNTER_PROXY_H -#define MOUNTER_PROXY_H +#ifndef MOUNTER_GAME_SERVER_H +#define MOUNTER_GAME_SERVER_H // Qt Includes #include @@ -11,9 +11,9 @@ #include #include -class QX_ERROR_TYPE(MounterProxyError, "MounterError", 1232) +class QX_ERROR_TYPE(MounterGameServerError, "MounterError", 1232) { - friend class MounterProxy; + friend class MounterGameServer; //-Class Enums------------------------------------------------------------- public: enum Type @@ -36,7 +36,7 @@ class QX_ERROR_TYPE(MounterProxyError, "MounterError", 1232) //-Constructor------------------------------------------------------------- private: - MounterProxyError(Type t = NoError, const QString& s = {}); + MounterGameServerError(Type t = NoError, const QString& s = {}); //-Instance Functions------------------------------------------------------------- public: @@ -51,7 +51,7 @@ class QX_ERROR_TYPE(MounterProxyError, "MounterError", 1232) QString deriveSecondary() const override; }; -class MounterProxy : public QObject +class MounterGameServer : public QObject { Q_OBJECT //-Class Variables------------------------------------------------------------------------------------------------------ @@ -60,52 +60,52 @@ class MounterProxy : public QObject static inline const QString NAME = u"Mounter"_s; // Events - External - static inline const QString EVENT_PROXY_RESPONSE = u"Proxy Response: \"%1\""_s; + static inline const QString EVENT_GAMESERVER_RESPONSE = u"Game Server Response: \"%1\""_s; // Events - Internal - static inline const QString EVENT_MOUNTING = u"Mounting data pack via proxy server..."_s; + static inline const QString EVENT_MOUNTING = u"Mounting data pack via game server..."_s; static inline const QString EVENT_REQUEST_SENT = u"Sent HTTP request\n" "\tOperation: %1\n" "\tURL: %2\n" "\tData: %3"_s; // Connections - static const int PROXY_TRANSFER_TIMEOUT = 30000; // ms + static const int GAME_SERVER_TRANSFER_TIMEOUT = 30000; // ms //-Instance Variables------------------------------------------------------------------------------------------------------------ private: bool mMounting; - int mProxyServerPort; + int mGameServerPort; QString mFilePath; QNetworkAccessManager mNam; - QPointer mProxyMountReply; + QPointer mGameServerMountReply; //-Constructor------------------------------------------------------------------------------------------------- public: - explicit MounterProxy(QObject* parent = nullptr); + explicit MounterGameServer(QObject* parent = nullptr); //-Instance Functions--------------------------------------------------------------------------------------------------------- private: - void finish(const MounterProxyError& errorState); + void finish(const MounterGameServerError& errorState); void noteProxyRequest(QNetworkAccessManager::Operation op, const QUrl& url, QByteArrayView data); void noteProxyResponse(const QString& response); void signalEventOccurred(const QString& event); - void signalErrorOccurred(const MounterProxyError& errorMessage); + void signalErrorOccurred(const MounterGameServerError& errorMessage); public: bool isMounting(); - quint16 proxyServerPort() const; + quint16 gameServerPort() const; QString filePath() const; - void setProxyServerPort(quint16 port); + void setGameServerPort(quint16 port); void setFilePath(const QString& path); //-Signals & Slots------------------------------------------------------------------------------------------------------------ private slots: - void proxyMountFinishedHandler(QNetworkReply* reply); + void gameServerMountFinishedHandler(QNetworkReply* reply); public slots: void mount(); @@ -113,12 +113,12 @@ public slots: signals: void eventOccurred(const QString& name, const QString& event); - void errorOccurred(const QString& name, const MounterProxyError& errorMessage); - void mountFinished(const MounterProxyError& errorState); + void errorOccurred(const QString& name, const MounterGameServerError& errorMessage); + void mountFinished(const MounterGameServerError& errorState); // For now these just cause a busy state void mountProgress(qint64 progress); void mountProgressMaximumChanged(qint64 maximum); }; -#endif // MOUNTER_PROXY_H +#endif // MOUNTER_GAME_SERVER_H