diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 93f18fd9f..228a76ff1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -60,12 +60,6 @@ jobs: cp ./bass/c/x64/bass.lib ./lib/ cp ./bass/x64/bass.dll ./bin/ - curl http://www.un4seen.com/files/bassmidi24.zip -o bassmidi.zip - unzip -d bass -o bassmidi.zip - cp ./bass/c/bassmidi.h ./lib - cp ./bass/c/x64/bassmidi.lib ./lib/ - cp ./bass/x64/bassmidi.dll ./bin/ - curl http://www.un4seen.com/files/bassopus24.zip -o bassopus.zip unzip -d bass -o bassopus.zip cp ./bass/c/bassopus.h ./lib @@ -115,14 +109,14 @@ jobs: build-linux: needs: formatting-check - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@master with: submodules: recursive - name: Install Qt - uses: jurplel/install-qt-action@v3 + uses: jurplel/install-qt-action@v4 with: aqtversion: '==3.1.*' version: '6.5.3' @@ -149,12 +143,6 @@ jobs: cp ./bass/libs/x86_64/libbass.so ./lib/ cp ./bass/libs/x86_64/libbass.so ./bin/ - curl http://www.un4seen.com/files/bassmidi24-linux.zip -o bassmidi.zip - unzip -d bass -o bassmidi.zip - cp ./bass/bassmidi.h ./lib - cp ./bass/libs/x86_64/libbassmidi.so ./lib/ - cp ./bass/libs/x86_64/libbassmidi.so ./bin/ - curl http://www.un4seen.com/files/bassopus24-linux.zip -o bassopus.zip unzip -d bass -o bassopus.zip cp ./bass/bassopus.h ./lib @@ -173,11 +161,26 @@ jobs: cmake . -D CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE="${{ github.workspace }}/bin/imageformats/" cmake --build . --config Release + # install plugin + cp plugins/imageformats/libqapng.so ${QT_ROOT_DIR}/plugins/imageformats + - name: Build run: | cmake . cmake --build . --config Release + - name: Clone Themes + uses: actions/checkout@master + with: + repository: AttorneyOnline/AO2-Themes + path: "bin/base/themes" + + - name: Cleanup Themes Checkout + run: | + rm ./bin/base/themes/.gitignore + rm ./bin/base/themes/.gitattributes + rm -r ./bin/base/themes/.git + - name: Deploy Linux shell: bash run: | @@ -185,23 +188,52 @@ jobs: mkdir ./imageformats cp ../qtapng/plugins/imageformats/libqapng.so ./imageformats cp ../scripts/launch.sh . + cp ../README_LINUX.md . chmod +x launch.sh chmod +x Attorney_Online - - name: Clone Themes - uses: actions/checkout@master - with: - repository: AttorneyOnline/AO2-Themes - path: "bin/base/themes" + cd .. + tar --transform='flags=r;s|bin|Attorney_Online|' -cvf Attorney_Online-Dynamic.tar bin - - name: Cleanup Themes Checkout + - name: Create AppImage + shell: bash run: | - rm ./bin/base/themes/.gitignore - rm ./bin/base/themes/.gitattributes - rm -r ./bin/base/themes/.git + # necessary, apparently + sudo apt install libxcb-cursor0 + # from https://github.com/probonopd/go-appimage/blob/master/src/appimagetool/README.md + wget -c https://github.com/$(wget -q https://github.com/probonopd/go-appimage/releases/expanded_assets/continuous -O - | grep "appimagetool-.*-x86_64.AppImage" | head -n 1 | cut -d '"' -f 2) + mv appimagetool-*-x86_64.AppImage appimagetool + chmod +x appimagetool + + mkdir -p AppDir/usr/bin + mkdir -p AppDir/usr/lib/plugins/imageformats + mkdir -p AppDir/usr/share/applications + + cp bin/Attorney_Online AppDir/usr/bin + cp bin/lib*.so AppDir/usr/lib + cp scripts/Attorney_Online.desktop AppDir/usr/share/applications + cp data/logo-client.png AppDir/Attorney_Online.png + + QTDIR=${QT_ROOT_DIR} ./appimagetool deploy AppDir/usr/share/applications/Attorney_Online.desktop + ARCH=x86_64 VERSION=2.11 ./appimagetool AppDir + + mkdir bin-appimage + cp -r bin/base bin-appimage + cp README_LINUX.md bin-appimage + cp Attorney_Online-*-x86_64.AppImage bin-appimage + chmod +x bin-appimage/Attorney_Online-*-x86_64.AppImage + + tar -cvf Attorney_Online-AppImage.tar bin-appimage/* + tar --transform='flags=r;s|bin-appimage|Attorney_Online|' -cvf Attorney_Online-AppImage.tar bin-appimage + + - name: Upload Dynamic Artifact + uses: actions/upload-artifact@master + with: + name: Attorney_Online-Linux-Dynamic + path: Attorney_Online-Dynamic.tar - - name: Upload Artifact + - name: Upload AppImage Artifact uses: actions/upload-artifact@master with: - name: Attorney_Online-Linux - path: ${{github.workspace}}/bin + name: Attorney_Online-Linux-AppImage + path: Attorney_Online-AppImage.tar diff --git a/CMakeLists.txt b/CMakeLists.txt index ab47dbc44..2e5b24e14 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,7 +114,17 @@ endif() target_include_directories(Attorney_Online PRIVATE src lib) target_link_directories(Attorney_Online PRIVATE lib) -target_link_libraries(Attorney_Online PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Concurrent Qt${QT_VERSION_MAJOR}::WebSockets Qt${QT_VERSION_MAJOR}::UiTools bass bassopus bassmidi) +target_link_libraries(Attorney_Online PRIVATE + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Network + Qt${QT_VERSION_MAJOR}::Widgets + Qt${QT_VERSION_MAJOR}::Concurrent + Qt${QT_VERSION_MAJOR}::WebSockets + Qt${QT_VERSION_MAJOR}::UiTools + bass + bassopus +) if(AO_ENABLE_DISCORD_RPC) target_compile_definitions(Attorney_Online PRIVATE AO_ENABLE_DISCORD_RPC) diff --git a/README_LINUX.md b/README_LINUX.md new file mode 100644 index 000000000..ceabf14c7 --- /dev/null +++ b/README_LINUX.md @@ -0,0 +1,26 @@ +## Running on Linux + +There are two download options for running on Linux: the **dynamically-linked** build and the **AppImage**. The dynamic build is lighter, but might only run on newer systems. The AppImage is a bit bigger, but should run seamlessly on most systems (anything newer than Ubuntu 22.04 LTS). + +### AppImage + +If you downloaded the **AppImage** version, it should just be plug-and-play. If you run into errors or bugs, contact us in our [Discord server](https://discord.gg/wWvQ3pw) or open an [issue on GitHub](https://github.com/AttorneyOnline/AO2-Client/issues). + +### Dynamic + +If you downloaded the **dynamically-linked** version, use the `launch.sh` script to run AO. + +You may need to install some libraries in your system. These are the commands to run on a terminal for some distributions: + +* Arch Linux: +``` +$ sudo pacman -S qt6-base qt6-tools qt6-websockets qt6-imageformats +``` +* Fedora: +``` +$ sudo dnf install qt6-qtbase qt6-qttools qt6-qtwebsockets qt6-qtimageformats +``` +* Ubuntu 22.04 LTS: +``` +$ sudo apt-get install qt6base-dev libqt6uitools6 libqt6websockets6 qt6-image-formats-plugins +``` diff --git a/scripts/Attorney_Online.desktop b/scripts/Attorney_Online.desktop new file mode 100644 index 000000000..16ed60029 --- /dev/null +++ b/scripts/Attorney_Online.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Application +Name=Attorney Online +Comment=The courtroom drama simulator +Exec=Attorney_Online +Icon=Attorney_Online +Categories=Game; diff --git a/scripts/launch.sh b/scripts/launch.sh index dcf6e7afb..bdae56d4d 100644 --- a/scripts/launch.sh +++ b/scripts/launch.sh @@ -1,5 +1,10 @@ -#!/bin/sh +#!/bin/bash +# Required to launch correctly -# Required in CI to launch correctly +# Move to AO's directory +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +cd "${SCRIPT_DIR}" + +# Run with correct linker path chmod +x Attorney_Online LD_LIBRARY_PATH=. ./Attorney_Online diff --git a/src/aoapplication.cpp b/src/aoapplication.cpp index f3260ed23..7a8e4ea3e 100644 --- a/src/aoapplication.cpp +++ b/src/aoapplication.cpp @@ -7,10 +7,9 @@ #include "options.h" #include "widgets/aooptionsdialog.h" -#include - static QtMessageHandler original_message_handler; static AOApplication *message_handler_context; + void message_handler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { Q_EMIT message_handler_context->qt_log_message(type, context, msg); @@ -221,33 +220,28 @@ void AOApplication::initBASS() BASS_Init(static_cast(a), 48000, BASS_DEVICE_LATENCY, nullptr, nullptr); load_bass_plugins(); qInfo() << info.name << "was set as the default audio output device."; - BASS_SetConfigPtr(BASS_CONFIG_MIDI_DEFFONT, QString(get_base_path() + "soundfont.sf2").toStdString().c_str()); return; } } BASS_Init(-1, 48000, BASS_DEVICE_LATENCY, nullptr, nullptr); load_bass_plugins(); } - BASS_SetConfigPtr(BASS_CONFIG_MIDI_DEFFONT, QString(get_base_path() + "soundfont.sf2").toStdString().c_str()); } #if (defined(_WIN32) || defined(_WIN64)) void AOApplication::load_bass_plugins() { BASS_PluginLoad("bassopus.dll", 0); - BASS_PluginLoad("bassmidi.dll", 0); } #elif defined __APPLE__ void AOApplication::load_bass_plugins() { BASS_PluginLoad("libbassopus.dylib", 0); - BASS_PluginLoad("libbassmidi.dylib", 0); } #elif (defined(LINUX) || defined(__linux__)) void AOApplication::load_bass_plugins() { BASS_PluginLoad("libbassopus.so", 0); - BASS_PluginLoad("libbassmidi.so", 0); } #else #error This operating system is unsupported for BASS plugins. diff --git a/src/aomusicplayer.cpp b/src/aomusicplayer.cpp index 4265a4637..c4a562cb1 100644 --- a/src/aomusicplayer.cpp +++ b/src/aomusicplayer.cpp @@ -35,7 +35,7 @@ QString AOMusicPlayer::playStream(QString song, int streamId, bool loopEnabled, } QString f_path = song; - DWORD newstream; + HSTREAM newstream; if (f_path.startsWith("http")) { if (!Options::getInstance().streamingEnabled()) @@ -51,17 +51,7 @@ QString AOMusicPlayer::playStream(QString song, int streamId, bool loopEnabled, flags |= BASS_STREAM_PRESCAN | BASS_UNICODE | BASS_ASYNCFILE; f_path = ao_app->get_real_path(ao_app->get_music_path(song)); - - QString extension = f_path.split('.').last(); - static const QStringList VALID_EXTENSION_LIST{"mo3", "xm", "mod", "s3m", "it", "mtm", "umx"}; - if (VALID_EXTENSION_LIST.contains(extension, Qt::CaseInsensitive)) - { - newstream = BASS_MusicLoad(FALSE, f_path.utf16(), 0, 0, flags, 1); - } - else - { - newstream = BASS_StreamCreateFile(FALSE, f_path.utf16(), 0, 0, flags); - } + newstream = BASS_StreamCreateFile(FALSE, f_path.utf16(), 0, 0, flags); } int error = BASS_ErrorGetCode(); diff --git a/src/file_functions.cpp b/src/file_functions.cpp index fef34f549..cbdd640d1 100644 --- a/src/file_functions.cpp +++ b/src/file_functions.cpp @@ -1,5 +1,9 @@ #include "file_functions.h" +#include +#include +#include + bool file_exists(QString file_path) { if (file_path.isEmpty()) @@ -31,24 +35,47 @@ bool exists(QString p_path) return file.exists(); } -QString get_base_path() +QString get_app_path() { - QString base_path; -#ifdef ANDROID - QString sdcard_storage = getenv("SECONDARY_STORAGE"); - if (dir_exists(sdcard_storage + "/base/")) + QString path = QCoreApplication::applicationDirPath(); + +#ifdef Q_OS_ANDROID + QString storage_path = qgetenv("SECONDARY_STORAGE"); + if (dir_exists(storage_path)) { - base_path = sdcard_storage + "/base/"; + path = storage_path; } else { - QString external_storage = getenv("EXTERNAL_STORAGE"); - base_path = external_storage + "/base/"; + QString external_path = qgetenv("EXTERNAL_STORAGE"); + if (dir_exists(external_path)) + { + path = external_path; + } } -#elif defined(__APPLE__) - base_path = QCoreApplication::applicationDirPath() + "/../../../base/"; -#else - base_path = QCoreApplication::applicationDirPath() + "/base/"; #endif - return base_path; + +#ifdef Q_OS_LINUX + QString app_path = qgetenv("APPIMAGE"); + if (!app_path.isEmpty()) + { + path = QFileInfo(app_path).absoluteDir().path(); + } +#endif + +#ifdef Q_OS_MAC + path += "/../../.."; +#endif + + if (path.endsWith(QDir::separator())) + { + path.chop(1); + } + + return path; +} + +QString get_base_path() +{ + return QDir(get_app_path()).absoluteFilePath("base") + "/"; } diff --git a/src/file_functions.h b/src/file_functions.h index ca1eeb5ec..252148fd0 100644 --- a/src/file_functions.h +++ b/src/file_functions.h @@ -1,11 +1,10 @@ #pragma once -#include -#include -#include #include bool file_exists(QString file_path); bool dir_exists(QString file_path); bool exists(QString p_path); + +QString get_app_path(); QString get_base_path(); diff --git a/src/lobby.cpp b/src/lobby.cpp index 788c77f07..9918ca3d6 100644 --- a/src/lobby.cpp +++ b/src/lobby.cpp @@ -424,7 +424,7 @@ void Lobby::on_demo_clicked(QTreeWidgetItem *item, int column) return; } - QString l_filepath = (QApplication::applicationDirPath() + "/logs/%1/%2").arg(item->data(0, Qt::DisplayRole).toString(), item->data(1, Qt::DisplayRole).toString()); + QString l_filepath = (get_app_path() + "/logs/%1/%2").arg(item->data(0, Qt::DisplayRole).toString(), item->data(1, Qt::DisplayRole).toString()); ao_app->demo_server->start_server(); ServerInfo demo_server; demo_server.address = "127.0.0.1"; diff --git a/src/text_file_functions.cpp b/src/text_file_functions.cpp index 418de47c0..763efb58e 100644 --- a/src/text_file_functions.cpp +++ b/src/text_file_functions.cpp @@ -133,7 +133,7 @@ bool AOApplication::append_to_file(QString p_text, QString p_file, bool make_dir QMultiMap AOApplication::load_demo_logs_list() const { - QString l_log_path = applicationDirPath() + "/logs/"; + QString l_log_path = get_app_path() + "/logs/"; QDir l_log_folder(l_log_path); l_log_folder.setFilter(QDir::NoDotAndDotDot | QDir::Dirs); @@ -360,7 +360,7 @@ QString AOApplication::get_court_sfx(QString p_identifier, QString p_misc) QString AOApplication::get_sfx_suffix(VPath sound_to_check) { - QStringList suffixes = {".opus", ".ogg", ".mp3", ".wav", ".mid", ".midi", ".xm", ".it", ".s3m", ".mod", ".mtm", ".umx"}; + QStringList suffixes = {".opus", ".ogg", ".mp3", ".wav"}; // Check if we were provided a direct filepath with a suffix already QString path = sound_to_check.toQString(); // Loop through our suffixes diff --git a/src/widgets/aooptionsdialog.cpp b/src/widgets/aooptionsdialog.cpp index a0b7d7ce5..4d55652eb 100644 --- a/src/widgets/aooptionsdialog.cpp +++ b/src/widgets/aooptionsdialog.cpp @@ -436,12 +436,12 @@ void AOOptionsDialog::setupUI() FROM_UI(QPushButton, mount_add); connect(ui_mount_add, &QPushButton::clicked, this, [this] { - QString path = QFileDialog::getExistingDirectory(this, tr("Select a base folder"), QApplication::applicationDirPath(), QFileDialog::ShowDirsOnly); + QString path = QFileDialog::getExistingDirectory(this, tr("Select a base folder"), get_app_path(), QFileDialog::ShowDirsOnly); if (path.isEmpty()) { return; } - QDir dir(QApplication::applicationDirPath()); + QDir dir(get_app_path()); QString relative = dir.relativeFilePath(path); if (!relative.contains("../")) {