From b02adccf8799944e65bde0a6a0c9bf7b1a8589ee Mon Sep 17 00:00:00 2001 From: ZeldaZach Date: Sun, 27 Mar 2022 19:59:37 -0400 Subject: [PATCH] Support Qt6, Min Qt5.8, Fix Win32, Fix Servatrice Add lock around deleting arrows for commanding cards Add support for Qt6 w/ Backwards Qt5 Handle Qt5/6 cross compilation better Last cleanups caps matter Fix serv Prevent crash on 6.3.0 Linux & bump to 5.8 min Prevent out of bounds indexing Delete shutdown timer if it exists Fixup ticket comments, remove unneeded guards Try to add support for missing OSes Update .ci/release_template.md Update PR based on comments Update XML name after done and remove Hirsute Address local game crash Address comments from PR (again) Tests don't work on mac, will see if a problem on other OSes make soundengine more consistent across qt versions disable tests on distros that are covered by others Fix Oracle Crash due to bad memory access Update Oracle to use new Qt6 way of adding translations Add support for Qt5/Qt6 compiling of Cockatrice Remove unneeded calls to QtMath/cmath/math.h Update how we handle bitwise comparisons for enums with Tray Icon Change header guards to not duplicate function Leave comment & Fix Path for GHA Qt Update common/server.h Update cockatrice/src/window_main.cpp Rollback change on cmake module path for NSIS check docker image requirements add size limit to ccache put variables in quotes properly set build type on mac avoid names used in cmake fix up cmake module path cmake 3.10 does not recognize prepend Support Tests in FindQtRuntime set ccache size on non debug builds as well immediately return when removing non existing client handle incTxBytes with a signal instead don't set common link libraries in cockatrice/CMakeLists.txt add comments set macos qt version to 6 Try upgrading XCode versions to latest they can be supported on Ensure Qt gets linked add tmate so i can see what's going on Qt6 points two directories further down than Qt5 with regard to the top lib path, so we need to account for this Establish Plugins directory for Qt6 Establish TLS plugins for Qt6 services Minor change for release channel network manager Let windows build in parallel cores Wrong symbols Qt6 patch up for signal add missing qt6 package on deb builds boolean expressions are hard negative indexes should go to the end Intentionally fail cache move size checks to individual zone types Hardcode libs needed for building on Windows, as the regex was annoying Update wording use the --parallel option in all builds clean up the .ci scripts some more tweak fedora build add os parameter to compile.sh I don't really like this but it seems the easiest way I'd prefer if these types of quirks would live in the main configuration file, the yml fixup yml readd appended cache key to vcpkg step fix windows 32 quirk the json hash is already added to the key as well remove os parameter and clean up ci files set name_build.sh to output relative paths set backwards compatible version of xcode and qt on mac set QTDIR for mac builds on qt5 has no effect for qt6 export BUILD_DIR to name_build.sh merge mac build steps merge homebrew steps, set package suffix link qt5 remove brew link set qtdir to qt5 only compile.sh vars need to be empty not 0 fix sets manager search bar on qt 5.12/15 fix oracle subprocess errors being ignored on qt 5 clean up translation loading move en@source translation file so it will not get included in packages NOTE: this needs to be done at transifex as well! Use generator platform over osname Short circuit if not Win defined --- .ci/Debian10/Dockerfile | 1 + .ci/Fedora36/Dockerfile | 14 + .ci/{UbuntuHirsute => UbuntuJammy}/Dockerfile | 19 +- .ci/compile.sh | 93 +- .ci/docker.sh | 44 +- .ci/download_openssl.sh | 35 + .ci/lint_cpp.sh | 24 +- .ci/name_build.sh | 12 +- .ci/release_template.md | 26 +- .github/workflows/desktop-build.yml | 193 ++-- .gitignore | 2 +- CMakeLists.txt | 117 +-- README.md | 1 + cmake/FindQtRuntime.cmake | 118 +++ cmake/FindWin32SslRuntime.cmake | 96 +- cockatrice/CMakeLists.txt | 124 ++- .../cockatrice_en@source.ts | 0 cockatrice/src/abstractclient.cpp | 1 + cockatrice/src/arrowitem.cpp | 20 +- .../src/carddbparser/cockatricexml3.cpp | 69 +- .../src/carddbparser/cockatricexml4.cpp | 65 +- cockatrice/src/cardinfowidget.cpp | 13 +- cockatrice/src/carditem.h | 1 + cockatrice/src/chatview/chatview.cpp | 14 +- cockatrice/src/chatview/chatview.h | 4 + cockatrice/src/deck_loader.cpp | 3 +- cockatrice/src/deckview.cpp | 12 +- cockatrice/src/dlg_connect.cpp | 3 +- cockatrice/src/dlg_manage_sets.cpp | 6 +- cockatrice/src/dlg_settings.cpp | 42 +- cockatrice/src/dlg_settings.h | 2 +- cockatrice/src/filter_string.cpp | 1 - cockatrice/src/gamescene.cpp | 4 +- cockatrice/src/gamesmodel.cpp | 4 - cockatrice/src/handzone.cpp | 4 +- cockatrice/src/localserver.cpp | 5 +- cockatrice/src/localserver.h | 1 + cockatrice/src/main.cpp | 26 +- cockatrice/src/pilezone.cpp | 4 + cockatrice/src/pixmapgenerator.cpp | 2 +- cockatrice/src/player.cpp | 5 + cockatrice/src/playerlistwidget.cpp | 4 + cockatrice/src/releasechannel.cpp | 3 +- cockatrice/src/releasechannel.h | 4 +- cockatrice/src/remoteclient.cpp | 5 + cockatrice/src/remotedecklist_treewidget.cpp | 2 +- .../src/remotereplaylist_treewidget.cpp | 2 +- cockatrice/src/replay_timeline_widget.cpp | 4 + cockatrice/src/setsmodel.cpp | 14 +- cockatrice/src/settingscache.cpp | 2 +- cockatrice/src/soundengine.cpp | 62 +- cockatrice/src/soundengine.h | 7 +- cockatrice/src/spoilerbackgroundupdater.cpp | 4 +- cockatrice/src/stackzone.cpp | 4 +- cockatrice/src/tab.cpp | 6 +- cockatrice/src/tab_room.cpp | 4 +- cockatrice/src/tab_room.h | 2 +- cockatrice/src/tab_supervisor.cpp | 6 +- cockatrice/src/tab_supervisor.h | 4 + cockatrice/src/tappedout_interface.cpp | 3 +- cockatrice/src/tip_of_the_day.cpp | 16 +- cockatrice/src/userinfobox.cpp | 2 +- cockatrice/src/userlist.cpp | 4 + cockatrice/src/window_main.cpp | 27 +- cockatrice/src/zoneviewzone.cpp | 12 +- common/CMakeLists.txt | 4 +- common/decklist.cpp | 6 +- common/expression.cpp | 28 +- common/rng_sfmt.cpp | 2 +- common/server.cpp | 9 +- common/server.h | 4 +- common/server_card.h | 4 +- common/server_cardzone.cpp | 6 +- common/server_database_interface.h | 7 +- common/server_game.cpp | 23 +- common/server_player.cpp | 2 +- common/server_protocolhandler.cpp | 7 +- common/server_protocolhandler.h | 1 - common/serverinfo_user_container.cpp | 2 +- common/serverinfo_user_container.h | 2 +- common/stringsizes.h | 1 - dbconverter/CMakeLists.txt | 38 +- oracle/CMakeLists.txt | 147 ++-- oracle/{translations => }/oracle_en@source.ts | 0 oracle/src/main.cpp | 25 +- oracle/src/oracleimporter.cpp | 2 +- oracle/src/oraclewizard.cpp | 44 +- oracle/src/oraclewizard.h | 4 +- oracle/src/qt-json/json.cpp | 822 +++++++++--------- oracle/src/zip/unzip.h | 135 +-- oracle/src/zip/zipglobal.cpp | 18 +- servatrice/CMakeLists.txt | 45 +- servatrice/src/main.cpp | 22 +- servatrice/src/servatrice.cpp | 33 +- servatrice/src/servatrice.h | 20 +- servatrice/src/servatrice_connection_pool.h | 4 +- .../src/servatrice_database_interface.cpp | 5 +- .../src/servatrice_database_interface.h | 66 +- servatrice/src/server_logger.cpp | 2 +- servatrice/src/server_logger.h | 2 +- servatrice/src/serversocketinterface.cpp | 42 +- servatrice/src/serversocketinterface.h | 1 + servatrice/src/settingscache.cpp | 2 +- servatrice/src/smtp/qxtmail_p.h | 7 +- servatrice/src/smtp/qxtmailmessage.cpp | 2 + servatrice/src/smtp/qxtsmtp.cpp | 451 ++++------ tests/CMakeLists.txt | 4 - tests/carddatabase/CMakeLists.txt | 19 +- tests/carddatabase/filter_string_test.cpp | 1 - tests/expression_test.cpp | 4 +- tests/loading_from_clipboard/CMakeLists.txt | 3 +- vcpkg | 2 +- vcpkg.json | 11 + vcpkg.txt | 4 - 114 files changed, 1925 insertions(+), 1603 deletions(-) create mode 100644 .ci/Fedora36/Dockerfile rename .ci/{UbuntuHirsute => UbuntuJammy}/Dockerfile (57%) create mode 100644 .ci/download_openssl.sh create mode 100644 cmake/FindQtRuntime.cmake rename cockatrice/{translations => }/cockatrice_en@source.ts (100%) rename oracle/{translations => }/oracle_en@source.ts (100%) mode change 100755 => 100644 oracle/src/zip/unzip.h mode change 100755 => 100644 oracle/src/zip/zipglobal.cpp create mode 100644 vcpkg.json delete mode 100644 vcpkg.txt diff --git a/.ci/Debian10/Dockerfile b/.ci/Debian10/Dockerfile index 5dda090e1..9b8cb5574 100644 --- a/.ci/Debian10/Dockerfile +++ b/.ci/Debian10/Dockerfile @@ -19,6 +19,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ qt5-default \ qtbase5-dev \ qtmultimedia5-dev \ + qttools5-dev \ qttools5-dev-tools \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* diff --git a/.ci/Fedora36/Dockerfile b/.ci/Fedora36/Dockerfile new file mode 100644 index 000000000..8225060a0 --- /dev/null +++ b/.ci/Fedora36/Dockerfile @@ -0,0 +1,14 @@ +FROM fedora:36 + +RUN dnf install -y \ + ccache \ + cmake \ + gcc-c++ \ + git \ + mariadb-devel \ + protobuf-devel \ + qt6-{qttools,qtsvg,qtmultimedia,qtwebsockets,qt5compat}-devel \ + rpm-build \ + xz-devel \ + zlib-devel \ + && dnf clean all diff --git a/.ci/UbuntuHirsute/Dockerfile b/.ci/UbuntuJammy/Dockerfile similarity index 57% rename from .ci/UbuntuHirsute/Dockerfile rename to .ci/UbuntuJammy/Dockerfile index e6d8998b4..78279b325 100644 --- a/.ci/UbuntuHirsute/Dockerfile +++ b/.ci/UbuntuJammy/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:hirsute +FROM ubuntu:jammy RUN apt-get update && \ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ @@ -9,16 +9,19 @@ RUN apt-get update && \ file \ g++ \ git \ + libgl-dev \ liblzma-dev \ libmariadb-dev-compat \ libprotobuf-dev \ - libqt5multimedia5-plugins \ - libqt5sql5-mysql \ - libqt5svg5-dev \ - libqt5websockets5-dev \ + libqt6core5compat6-dev \ + libqt6multimedia6 \ + libqt6sql6-mysql \ + libqt6svg6-dev \ + libqt6websockets6-dev \ protobuf-compiler \ - qtmultimedia5-dev \ - qttools5-dev \ - qttools5-dev-tools \ + qt6-l10n-tools \ + qt6-multimedia-dev \ + qt6-tools-dev \ + qt6-tools-dev-tools \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* diff --git a/.ci/compile.sh b/.ci/compile.sh index 72c737812..a6bd6475e 100755 --- a/.ci/compile.sh +++ b/.ci/compile.sh @@ -3,18 +3,18 @@ # This script is to be used by the ci environment from the project root directory, do not use it from somewhere else. # Compiles cockatrice inside of a ci environment -# --format runs the clang-format script first # --install runs make install # --package [] runs make package, optionally force the type # --suffix renames package with this suffix, requires arg # --server compiles servatrice # --test runs tests -# --debug or --release or sets the build type ie CMAKE_BUILD_TYPE -# --ccache uses ccache and shows stats +# --debug or --release sets the build type ie CMAKE_BUILD_TYPE +# --ccache [] uses ccache and shows stats, optionally provide size # --dir sets the name of the build dir, default is "build" -# uses env: BUILDTYPE CHECK_FORMAT MAKE_INSTALL MAKE_PACKAGE PACKAGE_TYPE PACKAGE_SUFFIX MAKE_SERVER MAKE_TEST USE_CCACHE BUILD_DIR (correspond to args: /--debug/--release --format --install --package --suffix --server --test --ccache --dir ) +# --parallel sets how many cores cmake should build with in parallel +# uses env: BUILDTYPE MAKE_INSTALL MAKE_PACKAGE PACKAGE_TYPE PACKAGE_SUFFIX MAKE_SERVER MAKE_TEST USE_CCACHE CCACHE_SIZE BUILD_DIR PARALLEL_COUNT +# (correspond to args: --debug/--release --install --package --suffix --server --test --ccache --dir --parallel ) # exitcode: 1 for failure, 3 for invalid arguments -LINT_SCRIPT=".ci/lint_cpp.sh" # Read arguments while [[ $# != 0 ]]; do @@ -22,10 +22,6 @@ while [[ $# != 0 ]]; do '--') shift ;; - '--format') - CHECK_FORMAT=1 - shift - ;; '--install') MAKE_INSTALL=1 shift @@ -33,7 +29,7 @@ while [[ $# != 0 ]]; do '--package') MAKE_PACKAGE=1 shift - if [[ $# != 0 && $1 != -* ]]; then + if [[ $# != 0 && ${1:0:1} != - ]]; then PACKAGE_TYPE="$1" shift fi @@ -66,6 +62,10 @@ while [[ $# != 0 ]]; do '--ccache') USE_CCACHE=1 shift + if [[ $# != 0 && ${1:0:1} != - ]]; then + CCACHE_SIZE="$1" + shift + fi ;; '--dir') shift @@ -76,67 +76,81 @@ while [[ $# != 0 ]]; do BUILD_DIR="$1" shift ;; - *) - if [[ $1 == -* ]]; then - echo "::error file=$0::unrecognized option: $1" + '--parallel') + shift + if [[ $# == 0 ]]; then + echo "::error file=$0::--parallel expects an argument" exit 3 fi - BUILDTYPE="$1" + PARALLEL_COUNT="$1" shift ;; + *) + echo "::error file=$0::unrecognized option: $1" + exit 3 + ;; esac done -# Check formatting using clang-format -if [[ $CHECK_FORMAT ]]; then - echo "::group::Run linter" - source "$LINT_SCRIPT" - echo "::endgroup::" -fi - set -e # Setup ./servatrice/check_schema_version.sh +if [[ ! $BUILDTYPE ]]; then + BUILDTYPE=Release +fi if [[ ! $BUILD_DIR ]]; then BUILD_DIR="build" fi mkdir -p "$BUILD_DIR" cd "$BUILD_DIR" -if [[ ! $CMAKE_BUILD_PARALLEL_LEVEL ]]; then - CMAKE_BUILD_PARALLEL_LEVEL=2 # default machines have 2 cores -fi - # Add cmake flags -flags=() +flags=("-DCMAKE_BUILD_TYPE=$BUILDTYPE") if [[ $MAKE_SERVER ]]; then flags+=("-DWITH_SERVER=1") fi if [[ $MAKE_TEST ]]; then flags+=("-DTEST=1") fi -if [[ ! $BUILDTYPE ]]; then - BUILDTYPE=Release +if [[ $USE_CCACHE ]]; then + flags+=("-DUSE_CCACHE=1") + if [[ $CCACHE_SIZE ]]; then + # note, this setting persists after running the script + ccache --max-size "$CCACHE_SIZE" + fi fi -flags+=("-DCMAKE_BUILD_TYPE=$BUILDTYPE") if [[ $PACKAGE_TYPE ]]; then flags+=("-DCPACK_GENERATOR=$PACKAGE_TYPE") fi -if [[ $(uname) == "Darwin" ]]; then - if [[ $USE_CCACHE ]]; then - # prepend ccache compiler binaries to path - PATH="/usr/local/opt/ccache/libexec:$PATH" +# Add cmake --build flags +buildflags=(--config "$BUILDTYPE") +if [[ $PARALLEL_COUNT ]]; then + if [[ $(cmake --build /not_a_dir --parallel |& head -1) =~ parallel ]]; then + # workaround for bionic having an old cmake + echo "this version of cmake does not support --parallel, using native build tool -j instead" + buildflags+=(-- -j "$PARALLEL_COUNT") + # note, no normal build flags should be added after this + else + buildflags+=(--parallel "$PARALLEL_COUNT") fi - # Add qt install location when using homebrew - flags+=("-DCMAKE_PREFIX_PATH=/usr/local/opt/qt5/") fi +function ccachestatsverbose() { + # note, verbose only works on newer ccache, discard the error + local got + if got="$(ccache --show-stats --verbose 2>/dev/null)"; then + echo "$got" + else + ccache --show-stats + fi +} + # Compile if [[ $USE_CCACHE ]]; then echo "::group::Show ccache stats" - ccache --show-stats + ccachestatsverbose echo "::endgroup::" fi @@ -146,12 +160,12 @@ cmake .. "${flags[@]}" echo "::endgroup::" echo "::group::Build project" -cmake --build . --config "$BUILDTYPE" +cmake --build . "${buildflags[@]}" echo "::endgroup::" if [[ $USE_CCACHE ]]; then echo "::group::Show ccache stats again" - ccache --show-stats + ccachestatsverbose echo "::endgroup::" fi @@ -174,7 +188,8 @@ if [[ $MAKE_PACKAGE ]]; then if [[ $PACKAGE_SUFFIX ]]; then echo "::group::Update package name" - ../.ci/name_build.sh "$PACKAGE_SUFFIX" + cd .. + BUILD_DIR="$BUILD_DIR" .ci/name_build.sh "$PACKAGE_SUFFIX" echo "::endgroup::" fi fi diff --git a/.ci/docker.sh b/.ci/docker.sh index 12052309e..c6ec1b774 100644 --- a/.ci/docker.sh +++ b/.ci/docker.sh @@ -10,7 +10,8 @@ # --interactive immediately starts the image interactively for debugging # --set-cache sets the location to cache the image or for ccache # requires: docker -# uses env: NAME CACHE BUILD GET SAVE INTERACTIVE (correspond to args: --set-cache --build --get --save --interactive) +# uses env: NAME CACHE BUILD GET SAVE INTERACTIVE +# (correspond to args: --set-cache --build --get --save --interactive) # sets env: RUN CCACHE_DIR IMAGE_NAME RUN_ARGS RUN_OPTS BUILD_SCRIPT # exitcode: 1 for failure, 2 for missing dockerfile, 3 for invalid arguments export BUILD_SCRIPT=".ci/compile.sh" @@ -48,7 +49,7 @@ while [[ $# != 0 ]]; do shift 2 ;; *) - if [[ $1 == -* ]]; then + if [[ ${1:0:1} == - ]]; then echo "unrecognized option: $1" return 3 fi @@ -74,26 +75,27 @@ fi if ! [[ $CACHE ]]; then echo "cache dir is not set!" >&2 -else - if ! [[ -d $CACHE ]]; then - echo "could not find cache dir: $CACHE" >&2 - mkdir -p "$CACHE" - unset GET # the dir is empty - fi - if [[ $GET || $SAVE ]]; then - img_dir="$CACHE/$image_cache" - img_save="$img_dir/$IMAGE_NAME$save_extension" - if ! [[ -d $img_dir ]]; then - echo "could not find image dir: $img_dir" >&2 - mkdir -p "$img_dir" - fi - fi - export CCACHE_DIR="$CACHE/$ccache_cache" - if ! [[ -d $CCACHE_DIR ]]; then - echo "could not find ccache dir: $CCACHE_DIR" >&2 - mkdir -p "$CCACHE_DIR" + CACHE="$(mktemp -d)" + echo "set cache dir to $CACHE" >&2 +fi +if ! [[ -d $CACHE ]]; then + echo "could not find cache dir: $CACHE" >&2 + mkdir -p "$CACHE" + unset GET # the dir is empty +fi +if [[ $GET || $SAVE ]]; then + img_dir="$CACHE/$image_cache" + img_save="$img_dir/$IMAGE_NAME$save_extension" + if ! [[ -d $img_dir ]]; then + echo "could not find image dir: $img_dir" >&2 + mkdir -p "$img_dir" fi fi +export CCACHE_DIR="$CACHE/$ccache_cache" +if ! [[ -d $CCACHE_DIR ]]; then + echo "could not find ccache dir: $CCACHE_DIR" >&2 + mkdir -p "$CCACHE_DIR" +fi # Get the docker image from previously stored save if [[ $GET ]]; then @@ -138,7 +140,7 @@ fi function RUN () { echo "running image:" - if docker images | grep "$IMAGE_NAME"; then + if [[ $(docker images) =~ "$IMAGE_NAME" ]]; then local args=(--mount "type=bind,source=$PWD,target=/src") args+=(--workdir "/src") args+=(--user "$(id -u):$(id -g)") diff --git a/.ci/download_openssl.sh b/.ci/download_openssl.sh new file mode 100644 index 000000000..c6c75eb83 --- /dev/null +++ b/.ci/download_openssl.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Read arguments +while [[ $# != 0 ]]; do + case "$1" in + '--') + shift + ;; + '--arch') + shift + if [[ $# == 0 ]]; then + echo "::error file=$0::--arch expects an argument" + exit 3 + fi + OS_ARCH="$1" + shift + ;; + *) + echo "::error file=$0::unrecognized option: $1" + exit 3 + ;; + esac +done + +set -e + +OPEN_SSL_VERSION="1.1.1n" +DEST_PATH="C:\OpenSSL-Win$OS_ARCH" + +curl -JLSs "https://github.com/CristiFati/Prebuilt-Binaries/raw/master/OpenSSL/v1.1.1/OpenSSL-$OPEN_SSL_VERSION-Win-pc0$OS_ARCH.zip" -o OpenSSL.zip +unzip -q "OpenSSL.zip" +rm "OpenSSL.zip" +mv "OpenSSL\OpenSSL\\$OPEN_SSL_VERSION" "$DEST_PATH" +rm -r "OpenSSL" +echo "Installed OpenSSL v$OPEN_SSL_VERSION to $DEST_PATH" \ No newline at end of file diff --git a/.ci/lint_cpp.sh b/.ci/lint_cpp.sh index e8939da9f..0c06972a9 100755 --- a/.ci/lint_cpp.sh +++ b/.ci/lint_cpp.sh @@ -42,11 +42,11 @@ ${diff#* Exiting... EOM - exit 2 - ;; + exit 2 + ;; - 0) - cat <&2 - echo "" - ;; - esac + *) + echo "" + echo "Something went wrong in our formatting checks: clangify returned $err" >&2 + echo "" + ;; +esac diff --git a/.ci/name_build.sh b/.ci/name_build.sh index d322f7272..b7413b297 100755 --- a/.ci/name_build.sh +++ b/.ci/name_build.sh @@ -3,8 +3,8 @@ # renames the file to [original name][SUFFIX].[original extension] # where SUFFIX is either available in the environment or as the first arg # if MAKE_ZIP is set instead a zip is made -# expected to be run in the build directory -builddir="." +# expected to be run in the build directory unless BUILD_DIR is set +builddir="${BUILD_DIR:=.}" findrx="Cockatrice-*.*" if [[ $1 ]]; then @@ -27,6 +27,7 @@ if [[ ! $file ]]; then echo "::error file=$0::could not find package" exit 1 fi +oldpwd="$PWD" if ! cd "$path"; then echo "::error file=$0::could not get file path" exit 1 @@ -45,6 +46,9 @@ else echo "renaming '$file' to '$filename'" mv "$file" "$filename" fi -ls -l "$PWD/$filename" -echo "::set-output name=path::$PWD/$filename" + +cd "$oldpwd" +relative_path="$path/$filename" +ls -l "$relative_path" +echo "::set-output name=path::$relative_path" echo "::set-output name=name::$filename" diff --git a/.ci/release_template.md b/.ci/release_template.md index 3c8c9d9df..e831fb159 100644 --- a/.ci/release_template.md +++ b/.ci/release_template.md @@ -8,18 +8,20 @@ git push -d origin --REPLACE-WITH-BETA-LIST-- include different targets -->
 Pre-compiled binaries we serve:
- - Windows 7/8/10 (32-bit)
- - Windows 7/8/10 (64-bit)
- - macOS 10.14 ("Mojave")
- - macOS 10.15 ("Catalina")
- - macOS 11.0 ("Big Sur")
- - Ubuntu 18.04 ("Bionic Beaver")
- - Ubuntu 20.04 ("Focal Fossa")
- - Ubuntu 20.10 ("Groovy Gorilla")
- - Ubuntu 21.04 ("Hirsute Hippo")
- - Debian 10 ("Buster")
- - Fedora 33
- - Fedora 34
+ - Windows 7/8/10/11 (32-bit)
+ - Windows 7/8/10/11 (64-bit)
+ - macOS 10.14 ("Mojave")
+ - macOS 10.15 ("Catalina")
+ - macOS 11.0 ("Big Sur")
+ - Ubuntu 18.04 ("Bionic Beaver")
+ - Ubuntu 20.04 ("Focal Fossa")
+ - Ubuntu 21.10 ("Impish Indri")
+ - Ubuntu 22.04 ("Jammy Jellyfish")
+ - Debian 10 ("Buster")
+ - Debian 11 ("Bullseye")
+ - Fedora 34
+ - Fedora 35
+ - Fedora 36
 We are also packaged in Arch Linux's official community repository, courtesy of @FFY00
 General linux support is available via a flatpak package (Flathub)
 
diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index dc8c1d20f..bb8dba579 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -19,9 +19,7 @@ on: jobs: configure: name: Configure - runs-on: ubuntu-latest - outputs: tag: ${{steps.configure.outputs.tag}} sha: ${{steps.configure.outputs.sha}} @@ -55,7 +53,7 @@ jobs: with: fetch-depth: 0 - - name: Prepare release paramaters + - name: Prepare release parameters id: prepare if: steps.configure.outputs.tag != null shell: bash @@ -88,6 +86,7 @@ jobs: - distro: Debian10 package: DEB + test: skip # running tests on all distros is superfluous - distro: Debian11 package: DEB @@ -98,6 +97,10 @@ jobs: - distro: Fedora35 package: RPM + test: skip + + - distro: Fedora36 + package: RPM - distro: UbuntuBionic package: DEB @@ -106,24 +109,23 @@ jobs: package: DEB test: skip # UbuntuFocal has a broken qt for debug builds - - distro: UbuntuHirsute + - distro: UbuntuImpish package: DEB test: skip - - distro: UbuntuImpish + - distro: UbuntuJammy package: DEB name: ${{matrix.distro}} - needs: configure - runs-on: ubuntu-latest - continue-on-error: ${{matrix.allow-failure == 'yes'}} - env: NAME: ${{matrix.distro}} CACHE: /tmp/${{matrix.distro}}-cache # ${{runner.temp}} does not work? + # cache size over the entire repo is 10Gi link: + # https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy + CCACHE_SIZE: 200M steps: - name: Checkout @@ -151,9 +153,11 @@ jobs: - name: Build debug and test if: matrix.test != 'skip' shell: bash + env: + distro: '${{matrix.distro}}' run: | source .ci/docker.sh - RUN --server --debug --test --ccache + RUN --server --debug --test --ccache "$CCACHE_SIZE" --parallel 2 - name: Build release package id: package @@ -162,16 +166,18 @@ jobs: env: suffix: '-${{matrix.distro}}' type: '${{matrix.package}}' + distro: '${{matrix.distro}}' run: | source .ci/docker.sh - RUN --server --release --package "$type" --suffix "$suffix" + RUN --server --release --package "$type" --suffix "$suffix" \ + --ccache "$CCACHE_SIZE" --parallel 2 - name: Upload artifact if: matrix.package != 'skip' uses: actions/upload-artifact@v2 with: name: ${{matrix.distro}}-package - path: ./build/${{steps.package.outputs.name}} + path: ${{steps.package.outputs.path}} if-no-files-found: error - name: Upload to release @@ -181,7 +187,7 @@ jobs: GITHUB_TOKEN: ${{github.token}} with: upload_url: ${{needs.configure.outputs.upload_url}} - asset_path: ./build/${{steps.package.outputs.name}} + asset_path: ${{steps.package.outputs.path}} asset_name: ${{steps.package.outputs.name}} asset_content_type: application/octet-stream @@ -193,39 +199,46 @@ jobs: - target: Debug # tests only os: macos-latest xcode: 12.5.1 + qt_version: 6 type: Debug - do_tests: 0 # tests do not work yet on mac - make_package: false + do_tests: 1 - target: 10.14_Mojave os: macos-10.15 # runs on Catalina xcode: 10.3 # allows compatibility with macOS 10.14 + qt_version: 5 type: Release - do_tests: 0 - make_package: true + # do_tests: 1 # tests do not work on qt5? + make_package: 1 - target: 10.15_Catalina os: macos-10.15 - xcode: 12.1 + xcode: 12.4 + qt_version: 6 type: Release - do_tests: 0 - make_package: true + do_tests: 1 + make_package: 1 - target: 11_Big_Sur os: macos-11 - xcode: 12.5.1 + xcode: 13.2 + qt_version: 6 type: Release - do_tests: 0 - make_package: true + do_tests: 1 + make_package: 1 + +# - target: 12_Monterey +# os: macos-12 +# xcode: 13.3 +# qt_version: 6 +# type: Release +# do_tests: 1 +# make_package: 1 name: macOS ${{matrix.target}} - needs: configure - runs-on: ${{matrix.os}} - continue-on-error: ${{matrix.allow-failure == 'yes'}} - env: DEVELOPER_DIR: /Applications/Xcode_${{matrix.xcode}}.app/Contents/Developer @@ -238,45 +251,32 @@ jobs: shell: bash # cmake cannot find the mysql connector # neither of these works: mariadb-connector-c mysql-connector-c++ - run: brew update && brew install protobuf - - - name: Install QT using homebrew - id: brew_install_qt - continue-on-error: true - shell: bash - run: brew install qt@5 --force-bottle - - - name: Install QT using actions - if: steps.brew_install_qt.outcome != 'success' - uses: jurplel/install-qt-action@v2 + env: + qt_version: 'qt@${{matrix.qt_version}}' + run: | + brew update + brew install protobuf + brew install "$qt_version" --force-bottle - name: Build on Xcode ${{matrix.xcode}} shell: bash + id: build env: - CMAKE_BUILD_PARALLEL_LEVEL: 3 # mac machines actually have 3 cores - run: .ci/compile.sh ${{matrix.type}} --server - - - name: Test - if: matrix.do_tests == 1 - shell: bash - working-directory: build - run: cmake --build . --target test - - - name: Package for ${{matrix.target}} - id: package - if: matrix.make_package - shell: bash - working-directory: build - run: | - cmake --build . --target package - ../.ci/name_build.sh "-macOS-${{matrix.target}}" + BUILDTYPE: '${{matrix.type}}' + MAKE_TEST: '${{matrix.do_tests}}' + MAKE_PACKAGE: '${{matrix.make_package}}' + PACKAGE_SUFFIX: '-macOS-${{matrix.target}}' + # set QTDIR to find qt5, qt6 can be found without this + QTDIR: '/usr/local/opt/qt5' + # Mac machines actually have 3 cores + run: .ci/compile.sh --server --parallel 3 - name: Upload artifact if: matrix.make_package uses: actions/upload-artifact@v2 with: name: macOS-${{matrix.target}}-dmg - path: ${{steps.package.outputs.path}} + path: ${{steps.build.outputs.path}} if-no-files-found: error - name: Upload to release @@ -286,8 +286,8 @@ jobs: GITHUB_TOKEN: ${{github.token}} with: upload_url: ${{needs.configure.outputs.upload_url}} - asset_path: ${{steps.package.outputs.path}} - asset_name: ${{steps.package.outputs.name}} + asset_path: ${{steps.build.outputs.path}} + asset_name: ${{steps.build.outputs.name}} asset_content_type: application/octet-stream build-windows: @@ -296,24 +296,22 @@ jobs: matrix: include: - arch: 32 - triplet: x86 - cmake: Win32 - + vcpkg_default_triplet: x86 + qt_version: '5.15.2' + cmake_generator_platform: Win32 + qt_arch: msvc2019 - arch: 64 - triplet: x64 - cmake: x64 - append: _64 - - name: Windows ${{matrix.arch}}-bit + vcpkg_default_triplet: x64 + qt_version: '6.3.0' + cmake_generator_platform: x64 + qt_arch: msvc2019_64 + qt_modules: "qt5compat qtmultimedia qtwebsockets" + name: Windows (${{matrix.arch}}-bit) needs: configure - runs-on: windows-2019 - env: - QT_VERSION: '5.15.2' - QT_ARCH: msvc2022${{matrix.append}} - CMAKE_GENERATOR: 'Visual Studio 17 2022' + CMAKE_GENERATOR: 'Visual Studio 16 2019' steps: - name: Add msbuild to PATH @@ -321,46 +319,57 @@ jobs: uses: microsoft/setup-msbuild@v1.1 - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive - - name: Restore Qt ${{env.QT_VERSION}} ${{matrix.arch}}-bit from cache + - name: Restore Qt ${{matrix.qt_version}} (${{matrix.arch}}-bit) from cache id: cache-qt - uses: actions/cache@v2 + uses: actions/cache@v1 # Intentionally v1, based on jurplel documentation with: - key: ${{runner.os}}-QtCache-${{env.QT_VERSION}}-${{matrix.arch}} - path: ${{runner.workspace}}/Qt + key: ${{runner.os}}-QtCache-${{matrix.qt_version}}-${{matrix.arch}} + path: '${{github.workspace}}/../Qt' - - name: Install ${{matrix.arch}}-bit Qt - uses: jurplel/install-qt-action@v2 + - name: Install Qt ${{matrix.qt_version}} (${{matrix.arch}}-bit) + uses: jurplel/install-qt-action@v3 with: cached: ${{steps.cache-qt.outputs.cache-hit}} - version: ${{env.QT_VERSION}} - arch: win${{matrix.arch}}_${{env.QT_ARCH}} + version: ${{matrix.qt_version}} + arch: win${{matrix.arch}}_${{matrix.qt_arch}} + modules: ${{matrix.qt_modules}} - - name: Restore or setup vcpkg - uses: lukka/run-vcpkg@v6 + - name: Run vcpkg + uses: lukka/run-vcpkg@v10.2 with: - vcpkgArguments: '@${{github.workspace}}/vcpkg.txt' - vcpkgDirectory: ${{github.workspace}}/vcpkg - appendedCacheKey: ${{hashFiles('**/vcpkg.txt')}} - vcpkgTriplet: ${{matrix.triplet}}-windows + runVcpkgInstall: true + appendedCacheKey: ${{matrix.arch}}-bit + env: + VCPKG_DEFAULT_TRIPLET: '${{matrix.vcpkg_default_triplet}}-windows' + VCPKG_DISABLE_METRICS: 1 - - name: Build Cockatrice ${{matrix.arch}}-bit + - name: Install OpenSSL (${{matrix.arch}}-bit) + shell: bash + run: .ci/download_openssl.sh --arch ${{matrix.arch}} + + - name: Build Cockatrice (${{matrix.arch}}-bit) id: build shell: bash env: PACKAGE_SUFFIX: '-win${{matrix.arch}}' CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}' - CMAKE_GENERATOR_PLATFORM: '${{matrix.cmake}}' - run: ./.ci/compile.sh --server --release --test --package + CMAKE_GENERATOR_PLATFORM: '${{matrix.cmake_generator_platform}}' + QTDIR: '${{github.workspace}}\Qt\${{matrix.qt_version}}\win${{matrix.arch}}_${{matrix.qt_arch}}' + run: .ci/compile.sh --server --release --test --package --parallel 2 + + - name: Setup tmate session + if: ${{ failure() }} + uses: mxschmitt/action-tmate@v3 - name: Upload artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: Windows-${{matrix.arch}}bit-installer - path: ./build/${{steps.build.outputs.name}} + path: ${{steps.build.outputs.path}} if-no-files-found: error - name: Upload to release @@ -370,6 +379,6 @@ jobs: GITHUB_TOKEN: ${{github.token}} with: upload_url: ${{needs.configure.outputs.upload_url}} - asset_path: ./build/${{steps.build.outputs.name}} + asset_path: ${{steps.build.outputs.path}} asset_name: ${{steps.build.outputs.name}} asset_content_type: application/octet-stream diff --git a/.gitignore b/.gitignore index 48263d807..2f4277ab3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ mysql.cnf .DS_Store .idea/ *.aps -cmake-build-debug/ +cmake-build-debug* preferences compile_commands.json .vs/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 38759aade..2a701481b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,9 +5,26 @@ # This file sets all the variables shared between the projects # like the installation path, compilation flags etc.. -# Cmake 3.1 is required to enable C++11 support correctly +# cmake 3.16 is required if using qt6 cmake_minimum_required(VERSION 3.10) +# Early detect ccache +option(USE_CCACHE "Cache the build results with ccache" ON) +# Treat warnings as errors (Debug builds only) +option(WARNING_AS_ERROR "Treat warnings as errors in debug builds" ON) +# Check for translation updates +option(UPDATE_TRANSLATIONS "Update translations on compile" OFF) +# Compile servatrice +option(WITH_SERVER "build servatrice" OFF) +# Compile cockatrice +option(WITH_CLIENT "build cockatrice" ON) +# Compile oracle +option(WITH_ORACLE "build oracle" ON) +# Compile dbconverter +option(WITH_DBCONVERTER "build dbconverter" ON) +# Compile tests +option(TEST "build tests" OFF) + # Default to "Release" build type # User-provided value for CMAKE_BUILD_TYPE must be checked before the PROJECT() call IF(DEFINED CMAKE_BUILD_TYPE) @@ -16,8 +33,6 @@ ELSE() SET(CMAKE_BUILD_TYPE Release CACHE STRING "Type of build") ENDIF() -# Early detect ccache -OPTION(USE_CCACHE "Cache the build results with ccache" ON) if(USE_CCACHE) find_program(CCACHE_PROGRAM ccache) if(CCACHE_PROGRAM) @@ -41,22 +56,23 @@ endif() # A project name is needed for CPack # Version can be overriden by git tags, see cmake/getversion.cmake -PROJECT("Cockatrice" VERSION 2.8.1) +project("Cockatrice" VERSION 2.8.1) # Set release name if not provided via env/cmake var if(NOT DEFINED GIT_TAG_RELEASENAME) set(GIT_TAG_RELEASENAME "Prismatic Bridge") endif() -# Use c++11 for all targets -set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ ISO Standard") +# Use c++17 for all targets +set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ ISO Standard") set(CMAKE_CXX_STANDARD_REQUIRED True) # Set conventional loops set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true) # Search path for cmake modules -SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) +set(COCKATRICE_CMAKE_PATH "${PROJECT_SOURCE_DIR}/cmake") +list(INSERT CMAKE_MODULE_PATH 0 "${COCKATRICE_CMAKE_PATH}") include(getversion) @@ -76,8 +92,8 @@ if(UNIX) if(RULE_LAUNCH_COMPILE) MESSAGE(STATUS "Force enabling CCache usage under macOS") # Set up wrapper scripts - configure_file(${CMAKE_MODULE_PATH}/launch-c.in launch-c) - configure_file(${CMAKE_MODULE_PATH}/launch-cxx.in launch-cxx) + configure_file("${COCKATRICE_CMAKE_PATH}/launch-c.in" launch-c) + configure_file("${COCKATRICE_CMAKE_PATH}/launch-cxx.in" launch-cxx) execute_process(COMMAND chmod a+rx "${CMAKE_BINARY_DIR}/launch-c" "${CMAKE_BINARY_DIR}/launch-cxx") @@ -103,13 +119,10 @@ elseif(WIN32) set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/rundir/${CMAKE_BUILD_TYPE}) endif() -# Treat warnings as errors (Debug builds only) -option(WARNING_AS_ERROR "Treat warnings as errors in debug builds" ON) - # Define proper compilation flags IF(MSVC) - # Visual Studio: Maximum optimization, disable warning C4251 - set(CMAKE_CXX_FLAGS_RELEASE "/Ox /MD /wd4251 ") + # Visual Studio: Maximum optimization, disable warning C4251, establish C++17 compatibility + SET(CMAKE_CXX_FLAGS_RELEASE "/Ox /MD /wd4251 /Zc:__cplusplus /std:c++17 /permissive-") # Generate complete debugging information #set(CMAKE_CXX_FLAGS_DEBUG "/Zi") ELSEIF (CMAKE_COMPILER_IS_GNUCXX) @@ -123,6 +136,10 @@ ELSEIF (CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0 -Wall -Wextra") endif() + IF(APPLE) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17") + ENDIF() + set(ADDITIONAL_DEBUG_FLAGS -Wcast-align -Wmissing-declarations -Wno-long-long -Wno-error=extra -Wno-error=delete-non-virtual-dtor -Wno-error=sign-compare -Wno-error=missing-declarations) FOREACH(FLAG ${ADDITIONAL_DEBUG_FLAGS}) @@ -148,7 +165,7 @@ ENDIF() FIND_PACKAGE(Threads REQUIRED) -# Find Qt5 +# Determine 32 or 64 bit build if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(_lib_suffix 64) else() @@ -165,28 +182,9 @@ elseif(DEFINED ENV{QTDIR}) list(APPEND CMAKE_PREFIX_PATH "$ENV{QTDIR}") endif() -FIND_PACKAGE(Qt5Core 5.5.0 REQUIRED) +MESSAGE(STATUS "Update Translations: ${UPDATE_TRANSLATIONS}") -IF(Qt5Core_FOUND) - MESSAGE(STATUS "Found Qt ${Qt5Core_VERSION_STRING}") - - # FIX: Qt was built with -reduce-relocations - if (Qt5_POSITION_INDEPENDENT_CODE) - SET(CMAKE_POSITION_INDEPENDENT_CODE ON) - endif() - - # guess plugins and libraries directory - set(QT_PLUGINS_DIR "${Qt5Core_DIR}/../../../plugins") - get_target_property(QT_LIBRARY_DIR Qt5::Core LOCATION) - get_filename_component(QT_LIBRARY_DIR ${QT_LIBRARY_DIR} PATH) - -ELSE() - MESSAGE(FATAL_ERROR "No Qt5 found!") -ENDIF() - -# Check for translation updates -OPTION(UPDATE_TRANSLATIONS "Update translations on compile" OFF) -MESSAGE(STATUS "UPDATE TRANSLATIONS: ${UPDATE_TRANSLATIONS}") +include(FindQtRuntime) set(CMAKE_AUTOMOC TRUE) @@ -195,7 +193,7 @@ FIND_PACKAGE(Protobuf REQUIRED) IF(NOT EXISTS "${Protobuf_PROTOC_EXECUTABLE}") MESSAGE(FATAL_ERROR "No protoc command found!") ELSE() - MESSAGE(STATUS "Protoc version ${Protobuf_VERSION} found!") + MESSAGE(STATUS "Found Protobuf ${Protobuf_VERSION} at: ${Protobuf_LIBRARIES}") ENDIF() #Find OpenSSL @@ -209,8 +207,8 @@ IF(MSVC) ENDIF() # Package builder -set(CPACK_PACKAGE_CONTACT "Zach Halpern ") -set(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${PROJECT_NAME}) +set(CPACK_PACKAGE_CONTACT "Zach Halpern ") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PROJECT_NAME}") set(CPACK_PACKAGE_VENDOR "Cockatrice Development Team") set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/README.md") set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") @@ -231,15 +229,33 @@ if(UNIX) # linux IF(CPACK_GENERATOR STREQUAL "RPM") set(CPACK_RPM_PACKAGE_LICENSE "GPLv2") - set(CPACK_RPM_PACKAGE_REQUIRES "protobuf, qt5-qttools, qt5-qtsvg, qt5-qtmultimedia") + set(CPACK_RPM_MAIN_COMPONENT "cockatrice") + IF(Qt6_FOUND) + SET(CPACK_RPM_PACKAGE_REQUIRES "protobuf, qt6-qttools, qt6-qtsvg, qt6-qtmultimedia") + ELSEIF(Qt5_FOUND) + SET(CPACK_RPM_PACKAGE_REQUIRES "protobuf, qt5-qttools, qt5-qtsvg, qt5-qtmultimedia") + ENDIF() set(CPACK_RPM_PACKAGE_GROUP "Amusements/Games") set(CPACK_RPM_PACKAGE_URL "http://github.com/Cockatrice/Cockatrice") + # stop directories from making package conflicts + set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION + /usr/share/applications + /usr/share/icons + /usr/share/icons/hicolor + /usr/share/icons/hicolor/48x48 + /usr/share/icons/hicolor/48x48/apps + /usr/share/icons/hicolor/scalable + /usr/share/icons/hicolor/scalable/apps) ELSE() set(CPACK_GENERATOR DEB) set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) set(CPACK_DEBIAN_PACKAGE_SECTION "games") set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://github.com/Cockatrice/Cockatrice") - set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5multimedia5-plugins, libqt5svg5") + IF(Qt6_FOUND) + set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt6multimedia6, libqt6svg6, qt6-qpa-plugins") + ELSEIF(Qt5_FOUND) + set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5multimedia5-plugins, libqt5svg5") + ENDIF() ENDIF() endif() elseif(WIN32) @@ -252,8 +268,8 @@ elseif(WIN32) # Configure file with custom definitions for NSIS. configure_file( - ${CMAKE_MODULE_PATH}/NSIS.definitions.nsh.in - ${PROJECT_BINARY_DIR}/NSIS.definitions.nsh + "${COCKATRICE_CMAKE_PATH}/NSIS.definitions.nsh.in" + "${PROJECT_BINARY_DIR}/NSIS.definitions.nsh" ) # include vcredist into the package; NSIS will take care of running it @@ -264,38 +280,33 @@ endif() include(CPack) -# Compile servatrice (default off) -option(WITH_SERVER "build servatrice" OFF) add_subdirectory(common) if(WITH_SERVER) add_subdirectory(servatrice) SET(CPACK_INSTALL_CMAKE_PROJECTS "Servatrice;Servatrice;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS}) endif() -# Compile cockatrice (default on) -option(WITH_CLIENT "build cockatrice" ON) if(WITH_CLIENT) add_subdirectory(cockatrice) SET(CPACK_INSTALL_CMAKE_PROJECTS "Cockatrice;Cockatrice;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS}) endif() -# Compile oracle (default on) -option(WITH_ORACLE "build oracle" ON) if(WITH_ORACLE) add_subdirectory(oracle) SET(CPACK_INSTALL_CMAKE_PROJECTS "Oracle;Oracle;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS}) endif() -# Compile dbconverter (default on) -option(WITH_DBCONVERTER "build dbconverter" ON) if(WITH_DBCONVERTER) add_subdirectory(dbconverter) SET(CPACK_INSTALL_CMAKE_PROJECTS "Dbconverter;Dbconverter;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS}) endif() -# Compile tests (default off) -option(TEST "build tests" OFF) if(TEST) include(CTest) add_subdirectory(tests) endif() + +if(Qt6Found AND Qt6_VERSION_MINOR GREATER_EQUAL 3) + # Qt6.3+ requires project finalization to support translations + qt6_finalize_project() +endif() diff --git a/README.md b/README.md index 06ec7ed5a..965dafba8 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,7 @@ The following flags can be passed to `cmake`: - `-DWARNING_AS_ERROR=0` Whether to treat compilation warnings as errors in debug mode (default 1 = yes). - `-DUPDATE_TRANSLATIONS=1` Configure `make` to update the translation .ts files for new strings in the source code. Note: Running `make clean` will remove the .ts files (default 0 = no). - `-DTEST=1` Enable regression tests (default 0 = no). Note: needs googletest, will be downloaded on the fly if unavailable. To run tests: ```make test```. +- `-DFORCE_USE_QT5=1` Skip looking for Qt6 before trying to find Qt5 # Run diff --git a/cmake/FindQtRuntime.cmake b/cmake/FindQtRuntime.cmake new file mode 100644 index 000000000..8fe736cdb --- /dev/null +++ b/cmake/FindQtRuntime.cmake @@ -0,0 +1,118 @@ +# Find a compatible Qt version +# Inputs: WITH_SERVER, WITH_CLIENT, WITH_ORACLE, WITH_DBCONVERTER, FORCE_USE_QT5 +# Optional Input: QT6_DIR -- Hint as to where Qt6 lives on the system +# Optional Input: QT5_DIR -- Hint as to where Qt5 lives on the system +# Output: COCKATRICE_QT_VERSION_NAME -- Example values: Qt5, Qt6 +# Output: SERVATRICE_QT_MODULES +# Output: COCKATRICE_QT_MODULES +# Output: ORACLE_QT_MODULES +# Output: DBCONVERTER_QT_MODULES +# Output: TEST_QT_MODULES + +set(REQUIRED_QT_COMPONENTS Core) +if(WITH_SERVER) + set(_SERVATRICE_NEEDED Network Sql WebSockets) +endif() +if(WITH_CLIENT) + set(_COCKATRICE_NEEDED Concurrent Gui Multimedia Network PrintSupport Svg Widgets WebSockets) +endif() +if(WITH_ORACLE) + set(_ORACLE_NEEDED Concurrent Network Svg Widgets) +endif() +if(WITH_DBCONVERTER) + set(_DBCONVERTER_NEEDED Network Widgets) +endif() +if(TEST) + set(_TEST_NEEDED Widgets) +endif() + +set(REQUIRED_QT_COMPONENTS + ${REQUIRED_QT_COMPONENTS} + ${_SERVATRICE_NEEDED} + ${_COCKATRICE_NEEDED} + ${_ORACLE_NEEDED} + ${_DBCONVERTER_NEEDED} + ${_TEST_NEEDED}) +list(REMOVE_DUPLICATES REQUIRED_QT_COMPONENTS) + +if(NOT FORCE_USE_QT5) + # Core5Compat is Qt6 Only, Linguist is now a component in Qt6 instead of an external package + find_package(Qt6 6.2.3 + COMPONENTS Core5Compat ${REQUIRED_QT_COMPONENTS} + OPTIONAL_COMPONENTS Linguist + QUIET + HINTS ${Qt6_DIR}) +endif() +if(Qt6_FOUND) + set(COCKATRICE_QT_VERSION_NAME Qt6) + + if(Qt6LinguistTools_FOUND) + list(FIND Qt6LinguistTools_TARGETS Qt6::lrelease QT6_LRELEASE_INDEX) + if(QT6_LRELEASE_INDEX EQUAL -1) + message(WARNING "Qt6 lrelease not found.") + endif() + + list(FIND Qt6LinguistTools_TARGETS Qt6::lupdate QT6_LUPDATE_INDEX) + if(QT6_LUPDATE_INDEX EQUAL -1) + message(WARNING "Qt6 lupdate not found.") + endif() + endif() +else() + find_package(Qt5 5.8.0 + COMPONENTS ${REQUIRED_QT_COMPONENTS} + QUIET + HINTS ${Qt5_DIR}) + if(Qt5_FOUND) + set(COCKATRICE_QT_VERSION_NAME Qt5) + else() + message(FATAL_ERROR "No suitable version of Qt was found") + endif() + + # Qt5 Linguist is in a separate package + find_package(Qt5LinguistTools QUIET) + if (Qt5LinguistTools_FOUND) + if(NOT Qt5_LRELEASE_EXECUTABLE) + message(WARNING "Qt5 lrelease not found.") + endif() + if(NOT Qt5_LUPDATE_EXECUTABLE) + message(WARNING "Qt5 lupdate not found.") + endif() + else() + message(WARNING "Linguist Tools not found, cannot handle translations") + endif() +endif() + +if(Qt5_POSITION_INDEPENDENT_CODE OR Qt6_FOUND) + set(CMAKE_POSITION_INDEPENDENT_CODE ON) +endif() + +# Establish Qt Plugins directory & Library directories +get_target_property(QT_LIBRARY_DIR ${COCKATRICE_QT_VERSION_NAME}::Core LOCATION) +get_filename_component(QT_LIBRARY_DIR ${QT_LIBRARY_DIR} DIRECTORY) +if(Qt6_FOUND) + get_filename_component(QT_PLUGINS_DIR "${Qt6Core_DIR}/../../../${QT6_INSTALL_PLUGINS}" ABSOLUTE) + get_filename_component(QT_LIBRARY_DIR "${QT_LIBRARY_DIR}/../../.." ABSOLUTE) + if(UNIX AND APPLE) + # Mac needs a bit more help finding all necessary components + list(APPEND QT_LIBRARY_DIR "/usr/local/lib") + endif() +elseif(Qt5_FOUND) + get_filename_component(QT_PLUGINS_DIR "${Qt5Core_DIR}/../../../plugins" ABSOLUTE) + get_filename_component(QT_LIBRARY_DIR "${QT_LIBRARY_DIR}/.." ABSOLUTE) +endif() +message(DEBUG "QT_PLUGINS_DIR = ${QT_PLUGINS_DIR}") +message(DEBUG "QT_LIBRARY_DIR = ${QT_LIBRARY_DIR}") + +# Establish exports +string(REGEX REPLACE "([^;]+)" "${COCKATRICE_QT_VERSION_NAME}::\\1" SERVATRICE_QT_MODULES "${_SERVATRICE_NEEDED}") +string(REGEX REPLACE "([^;]+)" "${COCKATRICE_QT_VERSION_NAME}::\\1" COCKATRICE_QT_MODULES "${_COCKATRICE_NEEDED}") +string(REGEX REPLACE "([^;]+)" "${COCKATRICE_QT_VERSION_NAME}::\\1" ORACLE_QT_MODULES "${_ORACLE_NEEDED}") +string(REGEX REPLACE "([^;]+)" "${COCKATRICE_QT_VERSION_NAME}::\\1" DB_CONVERTER_QT_MODULES "${_DBCONVERTER_NEEDED}") +string(REGEX REPLACE "([^;]+)" "${COCKATRICE_QT_VERSION_NAME}::\\1" TEST_QT_MODULES "${_TEST_NEEDED}") +if(Qt6_FOUND) + list(APPEND SERVATRICE_QT_MODULES ${COCKATRICE_QT_VERSION_NAME}::Core5Compat) + list(APPEND COCKATRICE_QT_MODULES ${COCKATRICE_QT_VERSION_NAME}::Core5Compat) + LIST(APPEND ORACLE_QT_MODULES ${COCKATRICE_QT_VERSION_NAME}::Core5Compat) +endif() + +message(STATUS "Found Qt ${${COCKATRICE_QT_VERSION_NAME}_VERSION} at: ${${COCKATRICE_QT_VERSION_NAME}_DIR}") \ No newline at end of file diff --git a/cmake/FindWin32SslRuntime.cmake b/cmake/FindWin32SslRuntime.cmake index a3a1998e7..0d3506fdb 100644 --- a/cmake/FindWin32SslRuntime.cmake +++ b/cmake/FindWin32SslRuntime.cmake @@ -1,66 +1,44 @@ # Find the OpenSSL runtime libraries (.dll) for Windows that # will be needed by Qt in order to access https urls. +if(NOT DEFINED WIN32 OR NOT ${WIN32}) + message(STATUS "Non-Windows device trying to execute FindWin32SslRuntime, skipping") + return() +endif() -if (WIN32) - # Get standard installation paths for OpenSSL under Windows +if("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "x64") + message(STATUS "Looking for OpenSSL for ${CMAKE_GENERATOR_PLATFORM}") + file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles) + set(_OPENSSL_ROOT_PATHS + "$ENV{VCPKG_PACKAGES_DIR}/x64-windows/bin" + "C:/OpenSSL-Win64/bin" + "C:/OpenSSL-Win64" + "C:/Tools/vcpkg/installed/x64-windows/bin" + "${_programfiles}/OpenSSL-Win64" + ) + unset(_programfiles) +elseif("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "Win32") + message(STATUS "Looking for OpenSSL for ${CMAKE_GENERATOR_PLATFORM}") + file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles) + set(_OPENSSL_ROOT_PATHS + "$ENV{VCPKG_PACKAGES_DIR}/x86-windows/bin" + "C:/OpenSSL-Win32/bin" + "C:/OpenSSL-Win32" + "C:/OpenSSL" + "C:/Tools/vcpkg/installed/x86-windows/bin" + "${_programfiles}/OpenSSL" + "${_programfiles}/OpenSSL-Win32" + ) + unset(_programfiles) +endif() - # http://www.slproweb.com/products/Win32OpenSSL.html - - if( CMAKE_SIZEOF_VOID_P EQUAL 8 ) - # target win64 - set(_OPENSSL_ROOT_HINTS - ${OPENSSL_ROOT_DIR} - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]" - ENV OPENSSL_ROOT_DIR - ) - file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles) - set(_OPENSSL_ROOT_PATHS - "C:/Tools/vcpkg/installed/x64-windows/bin" - "${_programfiles}/OpenSSL-Win64" - "C:/OpenSSL-Win64/" - ) - unset(_programfiles) - else( CMAKE_SIZEOF_VOID_P EQUAL 8 ) - # target win32 - set(_OPENSSL_ROOT_HINTS - ${OPENSSL_ROOT_DIR} - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]" - ENV OPENSSL_ROOT_DIR - ) - file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles) - set(_OPENSSL_ROOT_PATHS - "C:/Tools/vcpkg/installed/x86-windows/bin" - "${_programfiles}/OpenSSL" - "${_programfiles}/OpenSSL-Win32" - "C:/OpenSSL/" - "C:/OpenSSL-Win32/" - ) - unset(_programfiles) - endif( CMAKE_SIZEOF_VOID_P EQUAL 8 ) - -else () - set(_OPENSSL_ROOT_HINTS - ${OPENSSL_ROOT_DIR} - ENV OPENSSL_ROOT_DIR - ) -endif () - -set(_OPENSSL_ROOT_HINTS_AND_PATHS - HINTS ${_OPENSSL_ROOT_HINTS} - PATHS ${_OPENSSL_ROOT_PATHS} - ) - -# For OpenSSL < 1.1, they are named libeay32 and ssleay32 and even if the dll is 64bit, it's still suffixed as *32.dll -# For OpenSSL >= 1.1, they are named libcrypto and libssl with no suffix -if( CMAKE_SIZEOF_VOID_P EQUAL 8 ) - # target win64 - FIND_FILE(WIN32SSLRUNTIME_LIBEAY NAMES libcrypto-1_1-x64.dll libcrypto.dll libeay32.dll ${_OPENSSL_ROOT_HINTS_AND_PATHS}) - FIND_FILE(WIN32SSLRUNTIME_SSLEAY NAMES libssl-1_1-x64.dll libssl.dll ssleay32.dll ${_OPENSSL_ROOT_HINTS_AND_PATHS}) -else( CMAKE_SIZEOF_VOID_P EQUAL 8 ) - # target win32 - FIND_FILE(WIN32SSLRUNTIME_LIBEAY NAMES libcrypto-1_1.dll libcrypto.dll libeay32.dll ${_OPENSSL_ROOT_HINTS_AND_PATHS}) - FIND_FILE(WIN32SSLRUNTIME_SSLEAY NAMES libssl-1_1.dll libssl.dll ssleay32.dll ${_OPENSSL_ROOT_HINTS_AND_PATHS}) -endif( CMAKE_SIZEOF_VOID_P EQUAL 8 ) +message(STATUS "Looking for OpenSSL @ $ENV{CMAKE_GENERATOR_PLATFORM} in ${_OPENSSL_ROOT_PATHS}") +if("$ENV{CMAKE_GENERATOR_PLATFORM}" STREQUAL "x64") + FIND_FILE(WIN32SSLRUNTIME_LIBEAY NAMES libcrypto-1_1-x64.dll libcrypto.dll PATHS ${_OPENSSL_ROOT_PATHS} NO_DEFAULT_PATH) + FIND_FILE(WIN32SSLRUNTIME_SSLEAY NAMES libssl-1_1-x64.dll libssl.dll PATHS ${_OPENSSL_ROOT_PATHS} NO_DEFAULT_PATH) +elseif("$ENV{CMAKE_GENERATOR_PLATFORM}" STREQUAL "Win32") + FIND_FILE(WIN32SSLRUNTIME_LIBEAY NAMES libcrypto-1_1.dll libcrypto.dll PATHS ${_OPENSSL_ROOT_PATHS} NO_DEFAULT_PATH) + FIND_FILE(WIN32SSLRUNTIME_SSLEAY NAMES libssl-1_1.dll libssl.dll PATHS ${_OPENSSL_ROOT_PATHS} NO_DEFAULT_PATH) +endif() IF(WIN32SSLRUNTIME_LIBEAY AND WIN32SSLRUNTIME_SSLEAY) SET(WIN32SSLRUNTIME_LIBRARIES "${WIN32SSLRUNTIME_LIBEAY}" "${WIN32SSLRUNTIME_SSLEAY}") diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 83edd6ac4..b8b6b11a8 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -138,7 +138,7 @@ IF(UPDATE_TRANSLATIONS) FILE(GLOB_RECURSE translate_cockatrice_SRCS ${CMAKE_SOURCE_DIR}/cockatrice/src/*.cpp ${CMAKE_SOURCE_DIR}/cockatrice/src/*.h) FILE(GLOB_RECURSE translate_common_SRCS ${CMAKE_SOURCE_DIR}/common/*.cpp ${CMAKE_SOURCE_DIR}/common/*.h) SET(translate_SRCS ${translate_cockatrice_SRCS} ${translate_common_SRCS}) - SET(cockatrice_TS "${CMAKE_CURRENT_SOURCE_DIR}/translations/cockatrice_en@source.ts") + SET(cockatrice_TS "${CMAKE_CURRENT_SOURCE_DIR}/cockatrice_en@source.ts") ELSE() FILE(GLOB cockatrice_TS "${CMAKE_CURRENT_SOURCE_DIR}/translations/*.ts") ENDIF(UPDATE_TRANSLATIONS) @@ -153,30 +153,11 @@ if(APPLE) set(cockatrice_SOURCES ${cockatrice_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns) ENDIF(APPLE) -# Qt5 -find_package(Qt5 COMPONENTS Concurrent Multimedia Network PrintSupport Svg WebSockets Widgets REQUIRED) -set(COCKATRICE_QT_MODULES Qt5::Concurrent Qt5::Multimedia Qt5::Network Qt5::PrintSupport Qt5::Svg Qt5::Widgets Qt5::WebSockets) - -# Qt5LinguistTools -find_package(Qt5LinguistTools) -if(Qt5LinguistTools_FOUND) - list(APPEND COCKATRICE_LIBS Qt5::LinguistTools) - - if(NOT Qt5_LRELEASE_EXECUTABLE) - MESSAGE(WARNING "Qt's lrelease not found.") - endif() - - if(UPDATE_TRANSLATIONS) - if(NOT Qt5_LUPDATE_EXECUTABLE) - MESSAGE(WARNING "Qt's lupdate not found.") - endif() - QT5_CREATE_TRANSLATION(cockatrice_QM ${translate_SRCS} ${cockatrice_TS}) - else() - QT5_ADD_TRANSLATION(cockatrice_QM ${cockatrice_TS}) - endif() -endif() - -QT5_ADD_RESOURCES(cockatrice_RESOURCES_RCC ${cockatrice_RESOURCES}) +IF(Qt6_FOUND) + Qt6_ADD_RESOURCES(cockatrice_RESOURCES_RCC ${cockatrice_RESOURCES}) +ELSEIF(Qt5_FOUND) + Qt5_ADD_RESOURCES(cockatrice_RESOURCES_RCC ${cockatrice_RESOURCES}) +ENDIF() # Declare path variables set(ICONDIR share/icons CACHE STRING "icon dir") @@ -188,10 +169,38 @@ INCLUDE_DIRECTORIES(${PROTOBUF_INCLUDE_DIR}) INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR}/common) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) -# Build cockatrice binary and link it -ADD_EXECUTABLE(cockatrice WIN32 MACOSX_BUNDLE ${cockatrice_SOURCES} ${cockatrice_QM} ${cockatrice_RESOURCES_RCC} ${cockatrice_MOC_SRCS}) +set(COCKATRICE_MAC_QM_INSTALL_DIR "cockatrice.app/Contents/Resources/translations") +set(COCKATRICE_UNIX_QM_INSTALL_DIR "share/cockatrice/translations") +set(COCKATRICE_WIN32_QM_INSTALL_DIR "translations") -TARGET_LINK_LIBRARIES(cockatrice cockatrice_common ${COCKATRICE_QT_MODULES}) +if(Qt6_FOUND) + qt6_add_executable(cockatrice WIN32 MACOSX_BUNDLE ${cockatrice_SOURCES} ${cockatrice_RESOURCES_RCC} ${cockatrice_MOC_SRCS} MANUAL_FINALIZATION) +elseif(Qt5_FOUND) + # Qt5 Translations need to be linked at executable creation time + if(Qt5LinguistTools_FOUND) + if(UPDATE_TRANSLATIONS) + qt5_create_translation(cockatrice_QM ${translate_SRCS} ${cockatrice_TS}) + else() + qt5_add_translation(cockatrice_QM ${cockatrice_TS}) + endif() + endif() + add_executable(cockatrice WIN32 MACOSX_BUNDLE ${cockatrice_SOURCES} ${cockatrice_QM} ${cockatrice_RESOURCES_RCC} ${cockatrice_MOC_SRCS}) + if(UNIX) + if(APPLE) + install(FILES ${cockatrice_QM} DESTINATION ${COCKATRICE_MAC_QM_INSTALL_DIR}) + else() + install(FILES ${cockatrice_QM} DESTINATION ${COCKATRICE_UNIX_QM_INSTALL_DIR}) + endif() + elseif(WIN32) + install(FILES ${cockatrice_QM} DESTINATION ${COCKATRICE_WIN32_QM_INSTALL_DIR}) + endif() +endif() + +if(Qt5_FOUND) + target_link_libraries(cockatrice cockatrice_common ${COCKATRICE_QT_MODULES}) +else() + target_link_libraries(cockatrice PUBLIC cockatrice_common ${COCKATRICE_QT_MODULES}) +endif() if(UNIX) if(APPLE) @@ -205,27 +214,23 @@ if(UNIX) set_target_properties(cockatrice PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/Info.plist) INSTALL(TARGETS cockatrice BUNDLE DESTINATION ./) - INSTALL(FILES ${cockatrice_QM} DESTINATION ./cockatrice.app/Contents/Resources/translations) else() # Assume linux INSTALL(TARGETS cockatrice RUNTIME DESTINATION bin/) INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/cockatrice.png DESTINATION ${ICONDIR}/hicolor/48x48/apps) INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/cockatrice.svg DESTINATION ${ICONDIR}/hicolor/scalable/apps) INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/cockatrice.desktop DESTINATION ${DESKTOPDIR}) - INSTALL(FILES ${cockatrice_QM} DESTINATION share/cockatrice/translations) endif() elseif(WIN32) INSTALL(TARGETS cockatrice RUNTIME DESTINATION ./) - INSTALL(FILES ${cockatrice_QM} DESTINATION ./translations) endif() if(APPLE) # these needs to be relative to CMAKE_INSTALL_PREFIX set(plugin_dest_dir cockatrice.app/Contents/Plugins) set(qtconf_dest_dir cockatrice.app/Contents/Resources) - get_filename_component(QT_LIBRARY_DIR "${QT_LIBRARY_DIR}/.." ABSOLUTE) - # qt5 plugins: audio, iconengines, imageformats, platforms, printsupport + # Qt plugins: audio (Qt5), iconengines, imageformats, platforms, printsupport (Qt5), styles, tls (Qt6) install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime FILES_MATCHING PATTERN "*.dSYM" EXCLUDE @@ -236,6 +241,7 @@ if(APPLE) PATTERN "platforms/*.dylib" PATTERN "printsupport/*.dylib" PATTERN "styles/*.dylib" + PATTERN "tls/*.dylib" ) install(CODE " @@ -261,9 +267,32 @@ if(WIN32) install(DIRECTORY "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${CMAKE_BUILD_TYPE}/" DESTINATION ./ FILES_MATCHING PATTERN "*.dll") - # qt5 plugins: audio, iconengines, imageformats, platforms, printsupport - install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime - FILES_MATCHING REGEX "(audio|iconengines|imageformats|platforms|printsupport|styles)/.*[^d]\\.dll") + # Qt plugins: audio (Qt5), iconengines, imageformats, platforms, printsupport (Qt5), styles, tls (Qt6) + install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime FILES_MATCHING + PATTERN "audio/qtaudio_wasapi.dll" + PATTERN "audio/qtaudio_windows.dll" + PATTERN "iconengines/qsvgicon.dll" + PATTERN "imageformats/qgif.dll" + PATTERN "imageformats/qicns.dll" + PATTERN "imageformats/qico.dll" + PATTERN "imageformats/qjpeg.dll" + PATTERN "imageformats/qsvg.dll" + PATTERN "imageformats/qtga.dll" + PATTERN "imageformats/qtiff.dll" + PATTERN "imageformats/qwbmp.dll" + PATTERN "imageformats/qwebp.dll" + PATTERN "platforms/qdirect2d.dll" + PATTERN "platforms/qminimal.dll" + PATTERN "platforms/qoffscreen.dll" + PATTERN "platforms/qwindows.dll" + PATTERN "printsupport/windowsprintersupport.dll" + PATTERN "styles/qcertonlybackend.dll" + PATTERN "styles/qopensslbackend.dll" + PATTERN "styles/qschannelbackend.dll" + PATTERN "styles/qwindowsvistastyle.dll" + PATTERN "tls/qcertonlybackend.dll" + PATTERN "tls/qopensslbackend.dll" + PATTERN "tls/qschannelbackend.dll") install(CODE " file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths] @@ -284,3 +313,26 @@ Data = Resources\") install(FILES ${WIN32SSLRUNTIME_LIBRARIES} DESTINATION ./) endif() endif() + +if(Qt6LinguistTools_FOUND) + #Qt6 Translations happen after the executable is built up + if(UPDATE_TRANSLATIONS) + qt6_add_translations(cockatrice TS_FILES ${cockatrice_TS} SOURCES ${translate_SRCS} QM_FILES_OUTPUT_VARIABLE cockatrice_QM) + else() + qt6_add_translations(cockatrice TS_FILES ${cockatrice_TS} QM_FILES_OUTPUT_VARIABLE cockatrice_QM) + endif() + + if(UNIX) + if(APPLE) + install(FILES ${cockatrice_QM} DESTINATION ${COCKATRICE_MAC_QM_INSTALL_DIR}) + else() + install(FILES ${cockatrice_QM} DESTINATION ${COCKATRICE_UNIX_QM_INSTALL_DIR}) + endif() + elseif(WIN32) + install(FILES ${cockatrice_QM} DESTINATION ${COCKATRICE_WIN32_QM_INSTALL_DIR}) + endif() +endif() + +if(Qt6_FOUND) + qt6_finalize_target(cockatrice) +endif() diff --git a/cockatrice/translations/cockatrice_en@source.ts b/cockatrice/cockatrice_en@source.ts similarity index 100% rename from cockatrice/translations/cockatrice_en@source.ts rename to cockatrice/cockatrice_en@source.ts diff --git a/cockatrice/src/abstractclient.cpp b/cockatrice/src/abstractclient.cpp index ab0f3d25e..8e95e52e7 100644 --- a/cockatrice/src/abstractclient.cpp +++ b/cockatrice/src/abstractclient.cpp @@ -48,6 +48,7 @@ AbstractClient::AbstractClient(QObject *parent) qRegisterMetaType>("QList"); qRegisterMetaType("Event_ReplayAdded"); qRegisterMetaType>("missingFeatures"); + qRegisterMetaType("pendingCommand"); FeatureSet features; features.initalizeFeatureList(clientFeatures); diff --git a/cockatrice/src/arrowitem.cpp b/cockatrice/src/arrowitem.cpp index 89d016b0e..4db9722aa 100644 --- a/cockatrice/src/arrowitem.cpp +++ b/cockatrice/src/arrowitem.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include ArrowItem::ArrowItem(Player *_player, int _id, ArrowTarget *_startItem, ArrowTarget *_targetItem, const QColor &_color) : QGraphicsItem(), player(_player), id(_id), startItem(_startItem), targetItem(_targetItem), color(_color), @@ -71,7 +71,7 @@ void ArrowItem::updatePath(const QPointF &endPoint) const double arrowWidth = 15.0; const double headWidth = 40.0; const double headLength = - headWidth / pow(2, 0.5); // aka headWidth / sqrt (2) but this produces a compile error with MSVC++ + headWidth / qPow(2, 0.5); // aka headWidth / sqrt (2) but this produces a compile error with MSVC++ const double phi = 15; if (!startItem) @@ -86,7 +86,7 @@ void ArrowItem::updatePath(const QPointF &endPoint) if (lineLength < 30) path = QPainterPath(); else { - QPointF c(lineLength / 2, tan(phi * M_PI / 180) * lineLength); + QPointF c(lineLength / 2, qTan(phi * M_PI / 180) * lineLength); QPainterPath centerLine; centerLine.moveTo(0, 0); @@ -97,22 +97,22 @@ void ArrowItem::updatePath(const QPointF &endPoint) QLineF testLine(arrowBodyEndPoint, centerLine.pointAtPercent(percentage + 0.001)); qreal alpha = testLine.angle() - 90; QPointF endPoint1 = - arrowBodyEndPoint + arrowWidth / 2 * QPointF(cos(alpha * M_PI / 180), -sin(alpha * M_PI / 180)); + arrowBodyEndPoint + arrowWidth / 2 * QPointF(qCos(alpha * M_PI / 180), -qSin(alpha * M_PI / 180)); QPointF endPoint2 = - arrowBodyEndPoint + arrowWidth / 2 * QPointF(-cos(alpha * M_PI / 180), sin(alpha * M_PI / 180)); + arrowBodyEndPoint + arrowWidth / 2 * QPointF(-qCos(alpha * M_PI / 180), qSin(alpha * M_PI / 180)); QPointF point1 = - endPoint1 + (headWidth - arrowWidth) / 2 * QPointF(cos(alpha * M_PI / 180), -sin(alpha * M_PI / 180)); + endPoint1 + (headWidth - arrowWidth) / 2 * QPointF(qCos(alpha * M_PI / 180), -qSin(alpha * M_PI / 180)); QPointF point2 = - endPoint2 + (headWidth - arrowWidth) / 2 * QPointF(-cos(alpha * M_PI / 180), sin(alpha * M_PI / 180)); + endPoint2 + (headWidth - arrowWidth) / 2 * QPointF(-qCos(alpha * M_PI / 180), qSin(alpha * M_PI / 180)); - path = QPainterPath(-arrowWidth / 2 * QPointF(cos((phi - 90) * M_PI / 180), sin((phi - 90) * M_PI / 180))); + path = QPainterPath(-arrowWidth / 2 * QPointF(qCos((phi - 90) * M_PI / 180), qSin((phi - 90) * M_PI / 180))); path.quadTo(c, endPoint1); path.lineTo(point1); path.lineTo(QPointF(lineLength, 0)); path.lineTo(point2); path.lineTo(endPoint2); - path.quadTo(c, arrowWidth / 2 * QPointF(cos((phi - 90) * M_PI / 180), sin((phi - 90) * M_PI / 180))); - path.lineTo(-arrowWidth / 2 * QPointF(cos((phi - 90) * M_PI / 180), sin((phi - 90) * M_PI / 180))); + path.quadTo(c, arrowWidth / 2 * QPointF(qCos((phi - 90) * M_PI / 180), qSin((phi - 90) * M_PI / 180))); + path.lineTo(-arrowWidth / 2 * QPointF(qCos((phi - 90) * M_PI / 180), qSin((phi - 90) * M_PI / 180))); } setPos(startPoint); diff --git a/cockatrice/src/carddbparser/cockatricexml3.cpp b/cockatrice/src/carddbparser/cockatricexml3.cpp index 5567426c4..d13148e1e 100644 --- a/cockatrice/src/carddbparser/cockatricexml3.cpp +++ b/cockatrice/src/carddbparser/cockatricexml3.cpp @@ -23,7 +23,7 @@ bool CockatriceXml3Parser::getCanParseFile(const QString &fileName, QIODevice &d QXmlStreamReader xml(&device); while (!xml.atEnd()) { if (xml.readNext() == QXmlStreamReader::StartElement) { - if (xml.name() == COCKATRICE_XML3_TAGNAME) { + if (xml.name().toString() == COCKATRICE_XML3_TAGNAME) { int version = xml.attributes().value("version").toString().toInt(); if (version == COCKATRICE_XML3_TAGVER) { return true; @@ -52,12 +52,13 @@ void CockatriceXml3Parser::parseFile(QIODevice &device) break; } - if (xml.name() == "sets") { + auto name = xml.name().toString(); + if (name == "sets") { loadSetsFromXml(xml); - } else if (xml.name() == "cards") { + } else if (name == "cards") { loadCardsFromXml(xml); - } else if (xml.name() != "") { - qDebug() << "[CockatriceXml3Parser] Unknown item" << xml.name() << ", trying to continue anyway"; + } else if (!name.isEmpty()) { + qDebug() << "[CockatriceXml3Parser] Unknown item" << name << ", trying to continue anyway"; xml.skipCurrentElement(); } } @@ -72,26 +73,27 @@ void CockatriceXml3Parser::loadSetsFromXml(QXmlStreamReader &xml) break; } - if (xml.name() == "set") { + auto name = xml.name().toString(); + if (name == "set") { QString shortName, longName, setType; QDate releaseDate; while (!xml.atEnd()) { if (xml.readNext() == QXmlStreamReader::EndElement) { break; } + name = xml.name().toString(); - if (xml.name() == "name") { + if (name == "name") { shortName = xml.readElementText(QXmlStreamReader::IncludeChildElements); - } else if (xml.name() == "longname") { + } else if (name == "longname") { longName = xml.readElementText(QXmlStreamReader::IncludeChildElements); - } else if (xml.name() == "settype") { + } else if (name == "settype") { setType = xml.readElementText(QXmlStreamReader::IncludeChildElements); - } else if (xml.name() == "releasedate") { + } else if (name == "releasedate") { releaseDate = QDate::fromString(xml.readElementText(QXmlStreamReader::IncludeChildElements), Qt::ISODate); - } else if (xml.name() != "") { - qDebug() << "[CockatriceXml3Parser] Unknown set property" << xml.name() - << ", trying to continue anyway"; + } else if (!name.isEmpty()) { + qDebug() << "[CockatriceXml3Parser] Unknown set property" << name << ", trying to continue anyway"; xml.skipCurrentElement(); } } @@ -146,7 +148,8 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml) break; } - if (xml.name() == "card") { + auto xmlName = xml.name().toString(); + if (xmlName == "card") { QString name = QString(""); QString text = QString(""); QVariantHash properties = QVariantHash(); @@ -162,37 +165,39 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml) if (xml.readNext() == QXmlStreamReader::EndElement) { break; } + xmlName = xml.name().toString(); + // variable - assigned properties - if (xml.name() == "name") { + if (xmlName == "name") { name = xml.readElementText(QXmlStreamReader::IncludeChildElements); - } else if (xml.name() == "text") { + } else if (xmlName == "text") { text = xml.readElementText(QXmlStreamReader::IncludeChildElements); - } else if (xml.name() == "color") { + } else if (xmlName == "color") { colors.append(xml.readElementText(QXmlStreamReader::IncludeChildElements)); - } else if (xml.name() == "token") { + } else if (xmlName == "token") { isToken = static_cast(xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt()); // generic properties - } else if (xml.name() == "manacost") { + } else if (xmlName == "manacost") { properties.insert("manacost", xml.readElementText(QXmlStreamReader::IncludeChildElements)); - } else if (xml.name() == "cmc") { + } else if (xmlName == "cmc") { properties.insert("cmc", xml.readElementText(QXmlStreamReader::IncludeChildElements)); - } else if (xml.name() == "type") { + } else if (xmlName == "type") { QString type = xml.readElementText(QXmlStreamReader::IncludeChildElements); properties.insert("type", type); properties.insert("maintype", getMainCardType(type)); - } else if (xml.name() == "pt") { + } else if (xmlName == "pt") { properties.insert("pt", xml.readElementText(QXmlStreamReader::IncludeChildElements)); - } else if (xml.name() == "loyalty") { + } else if (xmlName == "loyalty") { properties.insert("loyalty", xml.readElementText(QXmlStreamReader::IncludeChildElements)); // positioning info - } else if (xml.name() == "tablerow") { + } else if (xmlName == "tablerow") { tableRow = xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt(); - } else if (xml.name() == "cipt") { + } else if (xmlName == "cipt") { cipt = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1"); - } else if (xml.name() == "upsidedown") { + } else if (xmlName == "upsidedown") { upsideDown = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1"); // sets - } else if (xml.name() == "set") { + } else if (xmlName == "set") { // NOTE: attributes must be read before readElementText() QXmlStreamAttributes attrs = xml.attributes(); QString setName = xml.readElementText(QXmlStreamReader::IncludeChildElements); @@ -217,8 +222,8 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml) setInfo.setProperty("rarity", attrs.value("rarity").toString()); } sets.insert(setName, setInfo); - // relatd cards - } else if (xml.name() == "related" || xml.name() == "reverse-related") { + // related cards + } else if (xmlName == "related" || xmlName == "reverse-related") { bool attach = false; bool exclude = false; bool variable = false; @@ -249,13 +254,13 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml) } auto *relation = new CardRelation(cardName, attach, exclude, variable, count); - if (xml.name() == "reverse-related") { + if (xmlName == "reverse-related") { reverseRelatedCards << relation; } else { relatedCards << relation; } - } else if (xml.name() != "") { - qDebug() << "[CockatriceXml3Parser] Unknown card property" << xml.name() + } else if (!xmlName.isEmpty()) { + qDebug() << "[CockatriceXml3Parser] Unknown card property" << xmlName << ", trying to continue anyway"; xml.skipCurrentElement(); } diff --git a/cockatrice/src/carddbparser/cockatricexml4.cpp b/cockatrice/src/carddbparser/cockatricexml4.cpp index e9bb4adb5..281e63d0e 100644 --- a/cockatrice/src/carddbparser/cockatricexml4.cpp +++ b/cockatrice/src/carddbparser/cockatricexml4.cpp @@ -23,7 +23,7 @@ bool CockatriceXml4Parser::getCanParseFile(const QString &fileName, QIODevice &d QXmlStreamReader xml(&device); while (!xml.atEnd()) { if (xml.readNext() == QXmlStreamReader::StartElement) { - if (xml.name() == COCKATRICE_XML4_TAGNAME) { + if (xml.name().toString() == COCKATRICE_XML4_TAGNAME) { int version = xml.attributes().value("version").toString().toInt(); if (version == COCKATRICE_XML4_TAGVER) { return true; @@ -52,12 +52,13 @@ void CockatriceXml4Parser::parseFile(QIODevice &device) break; } - if (xml.name() == "sets") { + auto xmlName = xml.name().toString(); + if (xmlName == "sets") { loadSetsFromXml(xml); - } else if (xml.name() == "cards") { + } else if (xmlName == "cards") { loadCardsFromXml(xml); - } else if (xml.name() != "") { - qDebug() << "[CockatriceXml4Parser] Unknown item" << xml.name() << ", trying to continue anyway"; + } else if (!xmlName.isEmpty()) { + qDebug() << "[CockatriceXml4Parser] Unknown item" << xmlName << ", trying to continue anyway"; xml.skipCurrentElement(); } } @@ -72,25 +73,27 @@ void CockatriceXml4Parser::loadSetsFromXml(QXmlStreamReader &xml) break; } - if (xml.name() == "set") { + auto xmlName = xml.name().toString(); + if (xmlName == "set") { QString shortName, longName, setType; QDate releaseDate; while (!xml.atEnd()) { if (xml.readNext() == QXmlStreamReader::EndElement) { break; } + xmlName = xml.name().toString(); - if (xml.name() == "name") { + if (xmlName == "name") { shortName = xml.readElementText(QXmlStreamReader::IncludeChildElements); - } else if (xml.name() == "longname") { + } else if (xmlName == "longname") { longName = xml.readElementText(QXmlStreamReader::IncludeChildElements); - } else if (xml.name() == "settype") { + } else if (xmlName == "settype") { setType = xml.readElementText(QXmlStreamReader::IncludeChildElements); - } else if (xml.name() == "releasedate") { + } else if (xmlName == "releasedate") { releaseDate = QDate::fromString(xml.readElementText(QXmlStreamReader::IncludeChildElements), Qt::ISODate); - } else if (xml.name() != "") { - qDebug() << "[CockatriceXml4Parser] Unknown set property" << xml.name() + } else if (!xmlName.isEmpty()) { + qDebug() << "[CockatriceXml4Parser] Unknown set property" << xmlName << ", trying to continue anyway"; xml.skipCurrentElement(); } @@ -109,8 +112,9 @@ QVariantHash CockatriceXml4Parser::loadCardPropertiesFromXml(QXmlStreamReader &x break; } - if (xml.name() != "") { - properties.insert(xml.name().toString(), xml.readElementText(QXmlStreamReader::IncludeChildElements)); + auto xmlName = xml.name().toString(); + if (!xmlName.isEmpty()) { + properties.insert(xmlName, xml.readElementText(QXmlStreamReader::IncludeChildElements)); } } return properties; @@ -123,7 +127,9 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml) break; } - if (xml.name() == "card") { + auto xmlName = xml.name().toString(); + + if (xmlName == "card") { QString name = QString(""); QString text = QString(""); QVariantHash properties = QVariantHash(); @@ -138,25 +144,28 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml) if (xml.readNext() == QXmlStreamReader::EndElement) { break; } + + xmlName = xml.name().toString(); + // variable - assigned properties - if (xml.name() == "name") { + if (xmlName == "name") { name = xml.readElementText(QXmlStreamReader::IncludeChildElements); - } else if (xml.name() == "text") { + } else if (xmlName == "text") { text = xml.readElementText(QXmlStreamReader::IncludeChildElements); - } else if (xml.name() == "token") { + } else if (xmlName == "token") { isToken = static_cast(xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt()); // generic properties - } else if (xml.name() == "prop") { + } else if (xmlName == "prop") { properties = loadCardPropertiesFromXml(xml); // positioning info - } else if (xml.name() == "tablerow") { + } else if (xmlName == "tablerow") { tableRow = xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt(); - } else if (xml.name() == "cipt") { + } else if (xmlName == "cipt") { cipt = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1"); - } else if (xml.name() == "upsidedown") { + } else if (xmlName == "upsidedown") { upsideDown = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1"); // sets - } else if (xml.name() == "set") { + } else if (xmlName == "set") { // NOTE: attributes but be read before readElementText() QXmlStreamAttributes attrs = xml.attributes(); QString setName = xml.readElementText(QXmlStreamReader::IncludeChildElements); @@ -171,8 +180,8 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml) } sets.insert(setName, setInfo); } - // relatd cards - } else if (xml.name() == "related" || xml.name() == "reverse-related") { + // related cards + } else if (xmlName == "related" || xmlName == "reverse-related") { bool attach = false; bool exclude = false; bool variable = false; @@ -203,13 +212,13 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml) } auto *relation = new CardRelation(cardName, attach, exclude, variable, count); - if (xml.name() == "reverse-related") { + if (xmlName == "reverse-related") { reverseRelatedCards << relation; } else { relatedCards << relation; } - } else if (xml.name() != "") { - qDebug() << "[CockatriceXml4Parser] Unknown card property" << xml.name() + } else if (!xmlName.isEmpty()) { + qDebug() << "[CockatriceXml4Parser] Unknown card property" << xmlName << ", trying to continue anyway"; xml.skipCurrentElement(); } diff --git a/cockatrice/src/cardinfowidget.cpp b/cockatrice/src/cardinfowidget.cpp index 4c0d16717..4578c8b66 100644 --- a/cockatrice/src/cardinfowidget.cpp +++ b/cockatrice/src/cardinfowidget.cpp @@ -6,13 +6,9 @@ #include "main.h" #include -#include -#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) #include -#else -#include -#endif #include +#include CardInfoWidget::CardInfoWidget(const QString &cardName, QWidget *parent, Qt::WindowFlags flags) : QFrame(parent, flags), aspectRatio((qreal)CARD_HEIGHT / (qreal)CARD_WIDTH), info(nullptr) @@ -34,12 +30,7 @@ CardInfoWidget::CardInfoWidget(const QString &cardName, QWidget *parent, Qt::Win setFrameStyle(QFrame::Panel | QFrame::Raised); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) - int pixmapHeight = qApp->primaryScreen()->geometry().height() / 3; -#else - QDesktopWidget desktopWidget; - int pixmapHeight = desktopWidget.screenGeometry().height() / 3; -#endif + int pixmapHeight = QGuiApplication::primaryScreen()->geometry().height() / 3; int pixmapWidth = static_cast(pixmapHeight / aspectRatio); pic->setFixedWidth(pixmapWidth); pic->setFixedHeight(pixmapHeight); diff --git a/cockatrice/src/carditem.h b/cockatrice/src/carditem.h index c480f6a05..5ef2856fe 100644 --- a/cockatrice/src/carditem.h +++ b/cockatrice/src/carditem.h @@ -2,6 +2,7 @@ #define CARDITEM_H #include "abstractcarditem.h" +#include "server_card.h" class CardDatabase; class CardDragItem; diff --git a/cockatrice/src/chatview/chatview.cpp b/cockatrice/src/chatview/chatview.cpp index d323495c9..2b205c31b 100644 --- a/cockatrice/src/chatview/chatview.cpp +++ b/cockatrice/src/chatview/chatview.cpp @@ -321,7 +321,7 @@ void ChatView::checkTag(QTextCursor &cursor, QString &message) void ChatView::checkMention(QTextCursor &cursor, QString &message, const QString &userName, UserLevelFlags userLevel) { - const QRegExp notALetterOrNumber = QRegExp("[^a-zA-Z0-9]"); + const static auto notALetterOrNumber = QRegularExpression("[^a-zA-Z0-9]"); int firstSpace = message.indexOf(' '); QString fullMentionUpToSpaceOrEnd = (firstSpace == -1) ? message.mid(1) : message.mid(1, firstSpace - 1); @@ -510,7 +510,11 @@ void ChatView::redactMessages(const QString &userName, int amount) } } +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +void ChatView::enterEvent(QEnterEvent * /*event*/) +#else void ChatView::enterEvent(QEvent * /*event*/) +#endif { setMouseTracking(true); } @@ -566,7 +570,11 @@ void ChatView::mousePressEvent(QMouseEvent *event) switch (hoveredItemType) { case HoveredCard: { if ((event->button() == Qt::MiddleButton) || (event->button() == Qt::LeftButton)) +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + emit showCardInfoPopup(event->globalPosition().toPoint(), hoveredContent); +#else emit showCardInfoPopup(event->globalPos(), hoveredContent); +#endif break; } case HoveredUser: { @@ -576,7 +584,11 @@ void ChatView::mousePressEvent(QMouseEvent *event) switch (event->button()) { case Qt::RightButton: { UserLevelFlags userLevel(hoveredContent.left(delimiterIndex).toInt()); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + userContextMenu->showContextMenu(event->globalPosition().toPoint(), userName, userLevel, this); +#else userContextMenu->showContextMenu(event->globalPos(), userName, userLevel, this); +#endif break; } case Qt::LeftButton: { diff --git a/cockatrice/src/chatview/chatview.h b/cockatrice/src/chatview/chatview.h index 236599591..3b50f78db 100644 --- a/cockatrice/src/chatview/chatview.h +++ b/cockatrice/src/chatview/chatview.h @@ -103,7 +103,11 @@ public: void redactMessages(const QString &userName, int amount); protected: +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + void enterEvent(QEnterEvent *event); +#else void enterEvent(QEvent *event); +#endif void leaveEvent(QEvent *event); void mouseMoveEvent(QMouseEvent *event); void mousePressEvent(QMouseEvent *event); diff --git a/cockatrice/src/deck_loader.cpp b/cockatrice/src/deck_loader.cpp index 2967e0181..44ea30872 100644 --- a/cockatrice/src/deck_loader.cpp +++ b/cockatrice/src/deck_loader.cpp @@ -6,6 +6,7 @@ #include #include +#include #include const QStringList DeckLoader::fileNameFilters = QStringList() @@ -208,7 +209,7 @@ void DeckLoader::saveToStream_DeckHeader(QTextStream &out) } if (!getComments().isEmpty()) { - QStringList commentRows = getComments().split(QRegExp("\n|\r\n|\r")); + QStringList commentRows = getComments().split(QRegularExpression("\n|\r\n|\r")); foreach (QString row, commentRows) { out << "// " << row << "\n"; } diff --git a/cockatrice/src/deckview.cpp b/cockatrice/src/deckview.cpp index 15a677fd6..1d9c41f54 100644 --- a/cockatrice/src/deckview.cpp +++ b/cockatrice/src/deckview.cpp @@ -9,8 +9,8 @@ #include #include #include +#include #include -#include DeckViewCardDragItem::DeckViewCardDragItem(DeckViewCard *_item, const QPointF &_hotSpot, @@ -238,11 +238,9 @@ int DeckViewCardContainer::getCardTypeTextWidth() const QFontMetrics fm(f); int maxCardTypeWidth = 0; - QMapIterator i(cardsByType); - while (i.hasNext()) { - int cardTypeWidth = fm.size(Qt::TextSingleLine, i.next().key()).width(); - if (cardTypeWidth > maxCardTypeWidth) - maxCardTypeWidth = cardTypeWidth; + for (const auto &key : cardsByType.keys()) { + int cardTypeWidth = fm.size(Qt::TextSingleLine, key).width(); + maxCardTypeWidth = qMax(maxCardTypeWidth, cardTypeWidth); } return maxCardTypeWidth + 15; @@ -440,7 +438,7 @@ void DeckViewScene::rearrangeItems() const int maxRows = rowsAndColsList[maxIndex1][maxIndex2].first; const int maxCardCount = cardCountList[maxIndex1][maxIndex2]; rowsAndColsList[maxIndex1][maxIndex2] = - QPair(maxRows + 1, (int)ceil((qreal)maxCardCount / (qreal)(maxRows + 1))); + QPair(maxRows + 1, (int)qCeil((qreal)maxCardCount / (qreal)(maxRows + 1))); } totalHeight = -spacing; diff --git a/cockatrice/src/dlg_connect.cpp b/cockatrice/src/dlg_connect.cpp index e63a7a87c..508085f13 100644 --- a/cockatrice/src/dlg_connect.cpp +++ b/cockatrice/src/dlg_connect.cpp @@ -162,8 +162,7 @@ DlgConnect::DlgConnect(QWidget *parent) : QDialog(parent) previousHostButton->setChecked(true); - connect(previousHosts, SIGNAL(currentIndexChanged(const QString &)), this, - SLOT(updateDisplayInfo(const QString &))); + connect(previousHosts, SIGNAL(currentTextChanged(const QString &)), this, SLOT(updateDisplayInfo(const QString &))); playernameEdit->setFocus(); } diff --git a/cockatrice/src/dlg_manage_sets.cpp b/cockatrice/src/dlg_manage_sets.cpp index 496431c1e..81542c75d 100644 --- a/cockatrice/src/dlg_manage_sets.cpp +++ b/cockatrice/src/dlg_manage_sets.cpp @@ -121,7 +121,12 @@ WndSets::WndSets(QWidget *parent) : QMainWindow(parent) connect(disableSomeButton, SIGNAL(clicked()), this, SLOT(actDisableSome())); connect(view->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), this, SLOT(actToggleButtons(const QItemSelection &, const QItemSelection &))); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) + connect(searchField, SIGNAL(textChanged(const QString &)), displayModel, + SLOT(setFilterRegularExpression(const QString &))); +#else connect(searchField, SIGNAL(textChanged(const QString &)), displayModel, SLOT(setFilterRegExp(const QString &))); +#endif connect(view->header(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)), this, SLOT(actDisableSortButtons(int))); connect(searchField, SIGNAL(textChanged(const QString &)), this, SLOT(actDisableResetButton(const QString &))); @@ -139,7 +144,6 @@ WndSets::WndSets(QWidget *parent) : QMainWindow(parent) tr("How to use custom card art") + "")); QGridLayout *hintsGrid = new QGridLayout; - hintsGrid->setMargin(2); hintsGrid->addWidget(labNotes, 0, 0); hintsGroupBox = new QGroupBox(tr("Hints")); hintsGroupBox->setLayout(hintsGrid); diff --git a/cockatrice/src/dlg_settings.cpp b/cockatrice/src/dlg_settings.cpp index 894e5fba6..c2015372a 100644 --- a/cockatrice/src/dlg_settings.cpp +++ b/cockatrice/src/dlg_settings.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -44,18 +43,22 @@ GeneralSettingsPage::GeneralSettingsPage() { - SettingsCache &settings = SettingsCache::instance(); - QString setLanguage = settings.getLang(); - QStringList qmFiles = findQmFiles(); - for (int i = 0; i < qmFiles.size(); i++) { - QString langName = languageName(qmFiles[i]); - languageBox.addItem(langName, qmFiles[i]); - if ((qmFiles[i] == setLanguage) || - (setLanguage.isEmpty() && langName == QCoreApplication::translate("i18n", DEFAULT_LANG_NAME))) - languageBox.setCurrentIndex(i); + QStringList languageCodes = findQmFiles(); + for (const QString &code : languageCodes) { + QString langName = languageName(code); + languageBox.addItem(langName, code); + } + + QString setLanguage = QCoreApplication::translate("i18n", DEFAULT_LANG_NAME); + int index = languageBox.findText(setLanguage, Qt::MatchExactly); + if (index == -1) { + qWarning() << "could not find language" << setLanguage; + } else { + languageBox.setCurrentIndex(index); } // updates + SettingsCache &settings = SettingsCache::instance(); QList channels = settings.getUpdateReleaseChannels(); foreach (ReleaseChannel *chan, channels) { updateReleaseChannelBox.insertItem(chan->getIndex(), tr(chan->getName().toUtf8())); @@ -186,15 +189,20 @@ QStringList GeneralSettingsPage::findQmFiles() { QDir dir(translationPath); QStringList fileNames = dir.entryList(QStringList(translationPrefix + "_*.qm"), QDir::Files, QDir::Name); - fileNames.replaceInStrings(QRegExp(translationPrefix + "_(.*)\\.qm"), "\\1"); - fileNames.removeOne("en@source"); + fileNames.replaceInStrings(QRegularExpression(translationPrefix + "_(.*)\\.qm"), "\\1"); return fileNames; } -QString GeneralSettingsPage::languageName(const QString &qmFile) +QString GeneralSettingsPage::languageName(const QString &lang) { QTranslator qTranslator; - qTranslator.load(translationPrefix + "_" + qmFile + ".qm", translationPath); + + QString appNameHint = translationPrefix + "_" + lang; + bool appTranslationLoaded = qTranslator.load(appNameHint, translationPath); + if (!appTranslationLoaded) { + qDebug() << "Unable to load" << translationPrefix << "translation" << appNameHint << "at" << translationPath; + } + return qTranslator.translate("i18n", DEFAULT_LANG_NAME); } @@ -1235,11 +1243,7 @@ void ShortcutSettingsPage::retranslateUi() DlgSettings::DlgSettings(QWidget *parent) : QDialog(parent) { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) - auto rec = qApp->primaryScreen()->availableGeometry(); -#else - auto rec = QApplication::desktop()->availableGeometry(); -#endif + auto rec = QGuiApplication::primaryScreen()->availableGeometry(); this->setMinimumSize(qMin(700, rec.width()), qMin(700, rec.height())); connect(&SettingsCache::instance(), SIGNAL(langChanged()), this, SLOT(updateLanguage())); diff --git a/cockatrice/src/dlg_settings.h b/cockatrice/src/dlg_settings.h index 36ea93ab1..1bcbc1e3b 100644 --- a/cockatrice/src/dlg_settings.h +++ b/cockatrice/src/dlg_settings.h @@ -49,7 +49,7 @@ private slots: private: QStringList findQmFiles(); - QString languageName(const QString &qmFile); + QString languageName(const QString &lang); QLineEdit *deckPathEdit; QLineEdit *replaysPathEdit; QLineEdit *picsPathEdit; diff --git a/cockatrice/src/filter_string.cpp b/cockatrice/src/filter_string.cpp index 637583714..e412ae5a3 100644 --- a/cockatrice/src/filter_string.cpp +++ b/cockatrice/src/filter_string.cpp @@ -4,7 +4,6 @@ #include #include -#include #include peg::parser search(R"( diff --git a/cockatrice/src/gamescene.cpp b/cockatrice/src/gamescene.cpp index 815e35c55..b1be6661d 100644 --- a/cockatrice/src/gamescene.cpp +++ b/cockatrice/src/gamescene.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include GameScene::GameScene(PhasesToolbar *_phasesToolbar, QObject *parent) : QGraphicsScene(parent), phasesToolbar(_phasesToolbar), viewSize(QSize()), playerRotation(0) @@ -97,7 +97,7 @@ void GameScene::rearrange() const int playersCount = playersPlaying.size(); const int columns = playersCount < SettingsCache::instance().getMinPlayersForMultiColumnLayout() ? 1 : 2; - const int rows = ceil((qreal)playersCount / columns); + const int rows = qCeil((qreal)playersCount / columns); qreal sceneHeight = 0, sceneWidth = -playerAreaSpacing; QList columnWidth; diff --git a/cockatrice/src/gamesmodel.cpp b/cockatrice/src/gamesmodel.cpp index 092bf18b3..6d8e9dafc 100644 --- a/cockatrice/src/gamesmodel.cpp +++ b/cockatrice/src/gamesmodel.cpp @@ -93,11 +93,7 @@ QVariant GamesModel::data(const QModelIndex &index, int role) const case CREATED: { switch (role) { case Qt::DisplayRole: { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)) QDateTime then = QDateTime::fromSecsSinceEpoch(gameentry.start_time(), Qt::UTC); -#else - QDateTime then = QDateTime::fromTime_t(gameentry.start_time(), Qt::UTC); -#endif int secs = then.secsTo(QDateTime::currentDateTimeUtc()); return getGameCreatedString(secs); } diff --git a/cockatrice/src/handzone.cpp b/cockatrice/src/handzone.cpp index 91d2e89e6..57c8b48bf 100644 --- a/cockatrice/src/handzone.cpp +++ b/cockatrice/src/handzone.cpp @@ -24,8 +24,10 @@ void HandZone::updateBg() void HandZone::addCardImpl(CardItem *card, int x, int /*y*/) { - if (x == -1) + // if x is negative set it to add at end + if (x < 0 || x >= cards.size()) { x = cards.size(); + } cards.insert(x, card); if (!cards.getContentsKnown()) { diff --git a/cockatrice/src/localserver.cpp b/cockatrice/src/localserver.cpp index cf3b01d0c..0a338a6ac 100644 --- a/cockatrice/src/localserver.cpp +++ b/cockatrice/src/localserver.cpp @@ -12,8 +12,9 @@ LocalServer::LocalServer(QObject *parent) : Server(parent) LocalServer::~LocalServer() { // LocalServer is single threaded so it doesn't need locks on this - while (!clients.isEmpty()) - clients.first()->prepareDestroy(); + for (auto *client : clients) { + client->prepareDestroy(); + } prepareDestroy(); } diff --git a/cockatrice/src/localserver.h b/cockatrice/src/localserver.h index a48abdca4..d3242548e 100644 --- a/cockatrice/src/localserver.h +++ b/cockatrice/src/localserver.h @@ -27,6 +27,7 @@ protected: public: LocalServer_DatabaseInterface(LocalServer *_localServer); + ~LocalServer_DatabaseInterface() = default; AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, diff --git a/cockatrice/src/main.cpp b/cockatrice/src/main.cpp index 69003a3da..7b22d2484 100644 --- a/cockatrice/src/main.cpp +++ b/cockatrice/src/main.cpp @@ -67,11 +67,29 @@ void installNewTranslator() { QString lang = SettingsCache::instance().getLang(); - qtTranslator->load("qt_" + lang, QLibraryInfo::location(QLibraryInfo::TranslationsPath)); + QString qtNameHint = "qt_" + lang; +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QString qtTranslationPath = QLibraryInfo::path(QLibraryInfo::TranslationsPath); +#else + QString qtTranslationPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath); +#endif + + bool qtTranslationLoaded = qtTranslator->load(qtNameHint, qtTranslationPath); + if (!qtTranslationLoaded) { + qDebug() << "Unable to load qt translation" << qtNameHint << "at" << qtTranslationPath; + } else { + qDebug() << "Loaded qt translation" << qtNameHint << "at" << qtTranslationPath; + } qApp->installTranslator(qtTranslator); - translator->load(translationPrefix + "_" + lang, translationPath); + + QString appNameHint = translationPrefix + "_" + lang; + bool appTranslationLoaded = qtTranslator->load(appNameHint, translationPath); + if (!appTranslationLoaded) { + qDebug() << "Unable to load" << translationPrefix << "translation" << appNameHint << "at" << translationPath; + } else { + qDebug() << "Loaded" << translationPrefix << "translation" << appNameHint << "at" << translationPath; + } qApp->installTranslator(translator); - qDebug() << "Language changed:" << lang; } QString const generateClientID() @@ -165,7 +183,9 @@ int main(int argc, char *argv[]) ui.show(); qDebug("main(): ui.show() finished"); +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) app.setAttribute(Qt::AA_UseHighDpiPixmaps); +#endif app.exec(); qDebug("Event loop finished, terminating..."); diff --git a/cockatrice/src/pilezone.cpp b/cockatrice/src/pilezone.cpp index 271c713e9..770cdf8d1 100644 --- a/cockatrice/src/pilezone.cpp +++ b/cockatrice/src/pilezone.cpp @@ -44,6 +44,10 @@ void PileZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*optio void PileZone::addCardImpl(CardItem *card, int x, int /*y*/) { connect(card, SIGNAL(sigPixmapUpdated()), this, SLOT(callUpdate())); + // if x is negative set it to add at end + if (x < 0 || x >= cards.size()) { + x = cards.size(); + } cards.insert(x, card); card->setPos(0, 0); if (!contentsKnown()) { diff --git a/cockatrice/src/pixmapgenerator.cpp b/cockatrice/src/pixmapgenerator.cpp index 93d7add5b..66d9283bb 100644 --- a/cockatrice/src/pixmapgenerator.cpp +++ b/cockatrice/src/pixmapgenerator.cpp @@ -99,7 +99,7 @@ QMap CountryPixmapGenerator::pmCache; QPixmap UserLevelPixmapGenerator::generatePixmap(int height, UserLevelFlags userLevel, bool isBuddy, QString privLevel) { - QString key = QString::number(height * 10000) + ":" + (int)userLevel + ":" + (int)isBuddy + ":" + privLevel; + QString key = QString::number(height * 10000) + ":" + (short)userLevel + ":" + (short)isBuddy + ":" + privLevel; if (pmCache.contains(key)) return pmCache.value(key); diff --git a/cockatrice/src/player.cpp b/cockatrice/src/player.cpp index 123b0e541..03c22d81d 100644 --- a/cockatrice/src/player.cpp +++ b/cockatrice/src/player.cpp @@ -66,6 +66,7 @@ #include #include +#include #include #include #include @@ -3066,7 +3067,11 @@ void Player::actSetPT() const auto oldpt = parsePT(card->getPT()); int ptIter = 0; for (const auto &item : ptList) { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + if (item.typeId() == QMetaType::Type::Int) { +#else if (item.type() == QVariant::Int) { +#endif int oldItem = ptIter < oldpt.size() ? oldpt.at(ptIter).toInt() : 0; newpt += '/' + QString::number(oldItem + item.toInt()); } else { diff --git a/cockatrice/src/playerlistwidget.cpp b/cockatrice/src/playerlistwidget.cpp index c4c706d99..4388679f2 100644 --- a/cockatrice/src/playerlistwidget.cpp +++ b/cockatrice/src/playerlistwidget.cpp @@ -28,7 +28,11 @@ bool PlayerListItemDelegate::editorEvent(QEvent *event, if ((event->type() == QEvent::MouseButtonPress) && index.isValid()) { QMouseEvent *const mouseEvent = static_cast(event); if (mouseEvent->button() == Qt::RightButton) { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + static_cast(parent())->showContextMenu(mouseEvent->globalPosition().toPoint(), index); +#else static_cast(parent())->showContextMenu(mouseEvent->globalPos(), index); +#endif return true; } } diff --git a/cockatrice/src/releasechannel.cpp b/cockatrice/src/releasechannel.cpp index 0ce28839a..97b22e95c 100644 --- a/cockatrice/src/releasechannel.cpp +++ b/cockatrice/src/releasechannel.cpp @@ -23,10 +23,9 @@ int ReleaseChannel::sharedIndex = 0; -ReleaseChannel::ReleaseChannel() : response(nullptr), lastRelease(nullptr) +ReleaseChannel::ReleaseChannel() : netMan(new QNetworkAccessManager(this)), response(nullptr), lastRelease(nullptr) { index = sharedIndex++; - netMan = new QNetworkAccessManager(this); } ReleaseChannel::~ReleaseChannel() diff --git a/cockatrice/src/releasechannel.h b/cockatrice/src/releasechannel.h index 8ec4d04c2..f9507b858 100644 --- a/cockatrice/src/releasechannel.h +++ b/cockatrice/src/releasechannel.h @@ -78,7 +78,7 @@ class ReleaseChannel : public QObject { Q_OBJECT public: - ReleaseChannel(); + explicit ReleaseChannel(); ~ReleaseChannel() override; protected: @@ -117,7 +117,7 @@ class StableReleaseChannel : public ReleaseChannel { Q_OBJECT public: - StableReleaseChannel() = default; + explicit StableReleaseChannel() = default; ~StableReleaseChannel() override = default; QString getManualDownloadUrl() const override; diff --git a/cockatrice/src/remoteclient.cpp b/cockatrice/src/remoteclient.cpp index 53037bfb7..4ade3b082 100644 --- a/cockatrice/src/remoteclient.cpp +++ b/cockatrice/src/remoteclient.cpp @@ -41,8 +41,13 @@ RemoteClient::RemoteClient(QObject *parent) socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); connect(socket, SIGNAL(connected()), this, SLOT(slotConnected())); connect(socket, SIGNAL(readyRead()), this, SLOT(readData())); + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + connect(socket, &QTcpSocket::errorOccurred, this, &RemoteClient::slotSocketError); +#else connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(slotSocketError(QAbstractSocket::SocketError))); +#endif websocket = new QWebSocket(QString(), QWebSocketProtocol::VersionLatest, this); connect(websocket, &QWebSocket::binaryMessageReceived, this, &RemoteClient::websocketMessageReceived); diff --git a/cockatrice/src/remotedecklist_treewidget.cpp b/cockatrice/src/remotedecklist_treewidget.cpp index 2df691a9a..d681d0a0a 100644 --- a/cockatrice/src/remotedecklist_treewidget.cpp +++ b/cockatrice/src/remotedecklist_treewidget.cpp @@ -221,7 +221,7 @@ void RemoteDeckList_TreeModel::addFileToTree(const ServerInfo_DeckStorage_TreeIt { const ServerInfo_DeckStorage_File &fileInfo = file.file(); QDateTime time; - time.setTime_t(fileInfo.creation_time()); + time.setSecsSinceEpoch(fileInfo.creation_time()); beginInsertRows(nodeToIndex(parent), parent->size(), parent->size()); parent->append(new FileNode(QString::fromStdString(file.name()), file.id(), time, parent)); diff --git a/cockatrice/src/remotereplaylist_treewidget.cpp b/cockatrice/src/remotereplaylist_treewidget.cpp index b81191a06..3ae2546bd 100644 --- a/cockatrice/src/remotereplaylist_treewidget.cpp +++ b/cockatrice/src/remotereplaylist_treewidget.cpp @@ -111,7 +111,7 @@ QVariant RemoteReplayList_TreeModel::data(const QModelIndex &index, int role) co return playerList.join(", "); } case 4: - return QDateTime::fromTime_t(matchInfo.time_started()); + return QDateTime::fromSecsSinceEpoch(matchInfo.time_started()); case 5: return matchInfo.length(); default: diff --git a/cockatrice/src/replay_timeline_widget.cpp b/cockatrice/src/replay_timeline_widget.cpp index 55abcaf0b..8a0d1d5bc 100644 --- a/cockatrice/src/replay_timeline_widget.cpp +++ b/cockatrice/src/replay_timeline_widget.cpp @@ -62,7 +62,11 @@ void ReplayTimelineWidget::paintEvent(QPaintEvent * /* event */) void ReplayTimelineWidget::mousePressEvent(QMouseEvent *event) { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + int newTime = static_cast((qint64)maxTime * (qint64)event->position().x() / width()); +#else int newTime = static_cast((qint64)maxTime * (qint64)event->x() / width()); +#endif newTime -= newTime % 200; // Time should always be a multiple of 200 if (newTime < currentTime) { currentTime = 0; diff --git a/cockatrice/src/setsmodel.cpp b/cockatrice/src/setsmodel.cpp index bd18d7ada..b4a684176 100644 --- a/cockatrice/src/setsmodel.cpp +++ b/cockatrice/src/setsmodel.cpp @@ -1,5 +1,7 @@ #include "setsmodel.h" +#include + SetsModel::SetsModel(CardDatabase *_db, QObject *parent) : QAbstractTableModel(parent), sets(_db->getSetList()) { sets.sortByKey(); @@ -274,9 +276,15 @@ bool SetsDisplayModel::filterAcceptsRow(int sourceRow, const QModelIndex &source auto nameIndex = sourceModel()->index(sourceRow, SetsModel::LongNameCol, sourceParent); auto shortNameIndex = sourceModel()->index(sourceRow, SetsModel::ShortNameCol, sourceParent); - return (sourceModel()->data(typeIndex).toString().contains(filterRegExp()) || - sourceModel()->data(nameIndex).toString().contains(filterRegExp()) || - sourceModel()->data(shortNameIndex).toString().contains(filterRegExp())); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) + const auto filter = filterRegularExpression(); +#else + const auto filter = filterRegExp(); +#endif + + return (sourceModel()->data(typeIndex).toString().contains(filter) || + sourceModel()->data(nameIndex).toString().contains(filter) || + sourceModel()->data(shortNameIndex).toString().contains(filter)); } bool SetsDisplayModel::lessThan(const QModelIndex &left, const QModelIndex &right) const diff --git a/cockatrice/src/settingscache.cpp b/cockatrice/src/settingscache.cpp index 1f82ab605..5293c3c31 100644 --- a/cockatrice/src/settingscache.cpp +++ b/cockatrice/src/settingscache.cpp @@ -18,7 +18,7 @@ QString SettingsCache::getDataPath() if (isPortableBuild) return qApp->applicationDirPath() + "/data"; else - return QStandardPaths::writableLocation(QStandardPaths::DataLocation); + return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); } QString SettingsCache::getSettingsPath() diff --git a/cockatrice/src/soundengine.cpp b/cockatrice/src/soundengine.cpp index d581800e2..fb821e69f 100644 --- a/cockatrice/src/soundengine.cpp +++ b/cockatrice/src/soundengine.cpp @@ -2,21 +2,15 @@ #include "settingscache.h" -#include #include -#include -#include -#include -#include -#include +#include +#include #define DEFAULT_THEME_NAME "Default" #define TEST_SOUND_FILENAME "player_join" SoundEngine::SoundEngine(QObject *parent) : QObject(parent), player(0) { - inputBuffer = new QBuffer(this); - ensureThemeDirectoryExists(); connect(&SettingsCache::instance(), SIGNAL(soundThemeChanged()), this, SLOT(themeChangedSlot())); connect(&SettingsCache::instance(), SIGNAL(soundEnabledChanged()), this, SLOT(soundEnabledChanged())); @@ -31,8 +25,6 @@ SoundEngine::~SoundEngine() player->deleteLater(); player = 0; } - - inputBuffer->deleteLater(); } void SoundEngine::soundEnabledChanged() @@ -40,14 +32,11 @@ void SoundEngine::soundEnabledChanged() if (SettingsCache::instance().getSoundEnabled()) { qDebug("SoundEngine: enabling sound"); if (!player) { - QAudioFormat format; - format.setSampleRate(44100); - format.setChannelCount(1); - format.setSampleSize(16); - format.setCodec("audio/pcm"); - format.setByteOrder(QAudioFormat::LittleEndian); - format.setSampleType(QAudioFormat::SignedInt); - player = new QAudioOutput(format, this); + player = new QMediaPlayer; +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + auto qAudioOutput = new QAudioOutput; + player->setAudioOutput(qAudioOutput); +#endif } } else { qDebug("SoundEngine: disabling sound"); @@ -61,25 +50,35 @@ void SoundEngine::soundEnabledChanged() void SoundEngine::playSound(QString fileName) { - if (!player) + if (!player) { return; + } // still playing the previous sound? - if (player->state() == QAudio::ActiveState) +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + if (player->playbackState() == QMediaPlayer::PlaybackState::PlayingState) { +#else + if (player->state() == QMediaPlayer::PlayingState) { +#endif return; + } - if (!audioData.contains(fileName)) + if (!audioData.contains(fileName)) { return; + } qDebug() << "playing" << fileName; - inputBuffer->close(); - inputBuffer->setData(audioData[fileName]); - inputBuffer->open(QIODevice::ReadOnly); - - player->setVolume(SettingsCache::instance().getMasterVolume() / 100.0); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + player->audioOutput()->setVolume(SettingsCache::instance().getMasterVolume()); player->stop(); - player->start(inputBuffer); + player->setSource(QUrl::fromLocalFile(audioData[fileName])); +#else + player->setVolume(SettingsCache::instance().getMasterVolume()); + player->stop(); + player->setMedia(QUrl::fromLocalFile(audioData[fileName])); +#endif + player->play(); } void SoundEngine::testSound() @@ -105,7 +104,7 @@ QStringMap &SoundEngine::getAvailableThemes() dir.setPath(SettingsCache::instance().getDataPath() + "/sounds"); - foreach (QString themeName, dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name)) { + for (const QString &themeName : dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name)) { if (!availableThemes.contains(themeName)) availableThemes.insert(themeName, dir.absoluteFilePath(themeName)); } @@ -121,7 +120,7 @@ QStringMap &SoundEngine::getAvailableThemes() #endif ); - foreach (QString themeName, dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name)) { + for (const QString &themeName : dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name)) { if (!availableThemes.contains(themeName)) availableThemes.insert(themeName, dir.absoluteFilePath(themeName)); } @@ -181,10 +180,7 @@ void SoundEngine::themeChangedSlot() continue; QFile file(dir.filePath(fileNames[i] + ".wav")); - file.open(QIODevice::ReadOnly); - // 44 = length of wav header - audioData.insert(fileNames[i], file.readAll().mid(44)); - file.close(); + audioData.insert(fileNames[i], file.fileName()); } soundEnabledChanged(); diff --git a/cockatrice/src/soundengine.h b/cockatrice/src/soundengine.h index 726c583f3..a386cea8d 100644 --- a/cockatrice/src/soundengine.h +++ b/cockatrice/src/soundengine.h @@ -1,8 +1,8 @@ #ifndef SOUNDENGINE_H #define SOUNDENGINE_H -#include #include +#include #include #include @@ -21,10 +21,9 @@ public: QStringMap &getAvailableThemes(); private: - QMap audioData; - QBuffer *inputBuffer; - QAudioOutput *player; QStringMap availableThemes; + QMap audioData; + QMediaPlayer *player; protected: void ensureThemeDirectoryExists(); diff --git a/cockatrice/src/spoilerbackgroundupdater.cpp b/cockatrice/src/spoilerbackgroundupdater.cpp index 7521b9ee9..7fb55f167 100644 --- a/cockatrice/src/spoilerbackgroundupdater.cpp +++ b/cockatrice/src/spoilerbackgroundupdater.cpp @@ -114,7 +114,7 @@ void SpoilerBackgroundUpdater::actCheckIfSpoilerSeasonEnabled() emit spoilerCheckerDone(); } else { if (trayIcon) { - trayIcon->showMessage(tr("Spoilers download failed"), tr("Error") + " " + errorCode); + trayIcon->showMessage(tr("Spoilers download failed"), tr("Error") + " " + (short)errorCode); } qDebug() << "Spoiler download failed with reason" << errorCode; @@ -159,7 +159,7 @@ bool SpoilerBackgroundUpdater::saveDownloadedFile(QByteArray data) // Data written, so reload the card database qDebug() << "Spoiler Service Data Written"; - QtConcurrent::run(db, &CardDatabase::loadCardDatabases); + const auto reloadOk = QtConcurrent::run([] { db->loadCardDatabases(); }); // If the user has notifications enabled, let them know // when the database was last updated diff --git a/cockatrice/src/stackzone.cpp b/cockatrice/src/stackzone.cpp index 26f19d557..648ed7bcb 100644 --- a/cockatrice/src/stackzone.cpp +++ b/cockatrice/src/stackzone.cpp @@ -26,8 +26,10 @@ void StackZone::updateBg() void StackZone::addCardImpl(CardItem *card, int x, int /*y*/) { - if (x == -1) + // if x is negative set it to add at end + if (x < 0 || x >= cards.size()) { x = cards.size(); + } cards.insert(x, card); if (!cards.getContentsKnown()) { diff --git a/cockatrice/src/tab.cpp b/cockatrice/src/tab.cpp index 3eaaaebc7..06cf377c8 100644 --- a/cockatrice/src/tab.cpp +++ b/cockatrice/src/tab.cpp @@ -4,7 +4,8 @@ #include #include -#include +#include + Tab::Tab(TabSupervisor *_tabSupervisor, QWidget *parent) : QMainWindow(parent), tabSupervisor(_tabSupervisor), contentsChanged(false), infoPopup(0) { @@ -20,7 +21,8 @@ void Tab::showCardInfoPopup(const QPoint &pos, const QString &cardName) infoPopup = new CardInfoWidget( cardName, 0, Qt::Widget | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint | Qt::WindowStaysOnTopHint); infoPopup->setAttribute(Qt::WA_TransparentForMouseEvents); - QRect screenRect = qApp->desktop()->screenGeometry(this); + + auto screenRect = qApp->primaryScreen()->geometry(); infoPopup->move(qMax(screenRect.left(), qMin(pos.x() - infoPopup->width() / 2, screenRect.left() + screenRect.width() - infoPopup->width())), qMax(screenRect.top(), qMin(pos.y() - infoPopup->height() / 2, diff --git a/cockatrice/src/tab_room.cpp b/cockatrice/src/tab_room.cpp index f7e2bbf9a..51bfc3e50 100644 --- a/cockatrice/src/tab_room.cpp +++ b/cockatrice/src/tab_room.cpp @@ -52,7 +52,7 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, SIGNAL(openMessageDialog(const QString &, bool))); chatView = new ChatView(tabSupervisor, tabSupervisor, nullptr, true, this); - connect(chatView, SIGNAL(showMentionPopup(QString &)), this, SLOT(actShowMentionPopup(QString &))); + connect(chatView, SIGNAL(showMentionPopup(const QString &)), this, SLOT(actShowMentionPopup(const QString &))); connect(chatView, SIGNAL(messageClickedSignal()), this, SLOT(focusTab())); connect(chatView, SIGNAL(openMessageDialog(QString, bool)), this, SIGNAL(openMessageDialog(QString, bool))); connect(chatView, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString))); @@ -160,7 +160,7 @@ void TabRoom::focusTab() emit maximizeClient(); } -void TabRoom::actShowMentionPopup(QString &sender) +void TabRoom::actShowMentionPopup(const QString &sender) { this->actShowPopup(sender + tr(" mentioned you.")); } diff --git a/cockatrice/src/tab_room.h b/cockatrice/src/tab_room.h index d048f26ed..d9e6d9246 100644 --- a/cockatrice/src/tab_room.h +++ b/cockatrice/src/tab_room.h @@ -75,7 +75,7 @@ private slots: void actOpenChatSettings(); void addMentionTag(QString mentionTag); void focusTab(); - void actShowMentionPopup(QString &sender); + void actShowMentionPopup(const QString &sender); void actShowPopup(const QString &message); void actCompleterChanged(); diff --git a/cockatrice/src/tab_supervisor.cpp b/cockatrice/src/tab_supervisor.cpp index f5a199cc6..e87a8592b 100644 --- a/cockatrice/src/tab_supervisor.cpp +++ b/cockatrice/src/tab_supervisor.cpp @@ -58,7 +58,11 @@ QSize CloseButton::sizeHint() const return QSize(width, height); } +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +void CloseButton::enterEvent(QEnterEvent *event) +#else void CloseButton::enterEvent(QEvent *event) +#endif { update(); QAbstractButton::enterEvent(event); @@ -74,7 +78,7 @@ void CloseButton::paintEvent(QPaintEvent * /*event*/) { QPainter p(this); QStyleOption opt; - opt.init(this); + opt.initFrom(this); opt.state |= QStyle::State_AutoRaise; if (isEnabled() && underMouse() && !isChecked() && !isDown()) opt.state |= QStyle::State_Raised; diff --git a/cockatrice/src/tab_supervisor.h b/cockatrice/src/tab_supervisor.h index b67375570..7045e4f9a 100644 --- a/cockatrice/src/tab_supervisor.h +++ b/cockatrice/src/tab_supervisor.h @@ -52,7 +52,11 @@ public: } protected: +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + void enterEvent(QEnterEvent *event); +#else void enterEvent(QEvent *event); +#endif void leaveEvent(QEvent *event); void paintEvent(QPaintEvent *event); }; diff --git a/cockatrice/src/tappedout_interface.cpp b/cockatrice/src/tappedout_interface.cpp index 95f58d395..0397c7f9f 100644 --- a/cockatrice/src/tappedout_interface.cpp +++ b/cockatrice/src/tappedout_interface.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include TappedOutInterface::TappedOutInterface(CardDatabase &_cardDatabase, QObject *parent) @@ -53,7 +54,7 @@ void TappedOutInterface::queryFinished(QNetworkReply *reply) int captures = rx2.captureCount(); for (int i = 1; i <= captures; i++) { - errorMessage += QString("\n") + rx2.cap(i).remove(QRegExp("<[^>]*>")).simplified(); + errorMessage += QString("\n") + rx2.cap(i).remove(QRegularExpression("<[^>]*>")).simplified(); } } diff --git a/cockatrice/src/tip_of_the_day.cpp b/cockatrice/src/tip_of_the_day.cpp index 876b6db33..94996e975 100644 --- a/cockatrice/src/tip_of_the_day.cpp +++ b/cockatrice/src/tip_of_the_day.cpp @@ -36,7 +36,9 @@ TipsOfTheDay::TipsOfTheDay(QString xmlPath, QObject *parent) : QAbstractListMode break; } - if (reader.name() == "tip") { + auto readerName = reader.name().toString(); + + if (readerName == "tip") { QString title, content, imagePath; QDate date; reader.readNext(); @@ -45,16 +47,18 @@ TipsOfTheDay::TipsOfTheDay(QString xmlPath, QObject *parent) : QAbstractListMode break; } - if (reader.name() == "title") { + readerName = reader.name().toString(); + + if (readerName == "title") { title = reader.readElementText(); - } else if (reader.name() == "text") { + } else if (readerName == "text") { content = reader.readElementText(); - } else if (reader.name() == "image") { + } else if (readerName == "image") { imagePath = "theme:tips/images/" + reader.readElementText(); - } else if (reader.name() == "date") { + } else if (readerName == "date") { date = QDate::fromString(reader.readElementText(), Qt::ISODate); } else { - // unkown element, do nothing + // unknown element, do nothing } } tipList->append(TipOfTheDay(title, content, imagePath, date)); diff --git a/cockatrice/src/userinfobox.cpp b/cockatrice/src/userinfobox.cpp index d9850e8da..369e08fba 100644 --- a/cockatrice/src/userinfobox.cpp +++ b/cockatrice/src/userinfobox.cpp @@ -137,7 +137,7 @@ QString UserInfoBox::getAgeString(int ageSeconds) if (ageSeconds == 0) return accountAgeString; - auto date = QDateTime::fromTime_t(QDateTime::currentSecsSinceEpoch() - ageSeconds).date(); + auto date = QDateTime::fromSecsSinceEpoch(QDateTime::currentSecsSinceEpoch() - ageSeconds).date(); if (!date.isValid()) return accountAgeString; diff --git a/cockatrice/src/userlist.cpp b/cockatrice/src/userlist.cpp index 80b621743..dc93a1f63 100644 --- a/cockatrice/src/userlist.cpp +++ b/cockatrice/src/userlist.cpp @@ -296,7 +296,11 @@ bool UserListItemDelegate::editorEvent(QEvent *event, if ((event->type() == QEvent::MouseButtonPress) && index.isValid()) { QMouseEvent *const mouseEvent = static_cast(event); if (mouseEvent->button() == Qt::RightButton) { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + static_cast(parent())->showContextMenu(mouseEvent->globalPosition().toPoint(), index); +#else static_cast(parent())->showContextMenu(mouseEvent->globalPos(), index); +#endif return true; } } diff --git a/cockatrice/src/window_main.cpp b/cockatrice/src/window_main.cpp index 4098f77bc..b101aeb39 100644 --- a/cockatrice/src/window_main.cpp +++ b/cockatrice/src/window_main.cpp @@ -101,8 +101,8 @@ void MainWindow::processConnectionClosedEvent(const Event_ConnectionClosed &even case Event_ConnectionClosed::BANNED: { reasonStr = tr("Banned by moderator"); if (event.has_end_time()) - reasonStr.append("\n" + - tr("Expected end time: %1").arg(QDateTime::fromTime_t(event.end_time()).toString())); + reasonStr.append( + "\n" + tr("Expected end time: %1").arg(QDateTime::fromSecsSinceEpoch(event.end_time()).toString())); else reasonStr.append("\n" + tr("This ban lasts indefinitely.")); if (event.has_reason_str()) @@ -393,7 +393,7 @@ void MainWindow::loginError(Response::ResponseCode r, case Response::RespUserIsBanned: { QString bannedStr; if (endTime) - bannedStr = tr("You are banned until %1.").arg(QDateTime::fromTime_t(endTime).toString()); + bannedStr = tr("You are banned until %1.").arg(QDateTime::fromSecsSinceEpoch(endTime).toString()); else bannedStr = tr("You are banned indefinitely."); if (!reasonStr.isEmpty()) @@ -530,7 +530,7 @@ void MainWindow::registerError(Response::ResponseCode r, QString reasonStr, quin case Response::RespUserIsBanned: { QString bannedStr; if (endTime) - bannedStr = tr("You are banned until %1.").arg(QDateTime::fromTime_t(endTime).toString()); + bannedStr = tr("You are banned until %1.").arg(QDateTime::fromSecsSinceEpoch(endTime).toString()); else bannedStr = tr("You are banned indefinitely."); if (!reasonStr.isEmpty()) @@ -865,13 +865,13 @@ void MainWindow::startupConfigCheck() if (SettingsCache::instance().getNotifyAboutNewVersion()) { alertForcedOracleRun(VERSION_STRING, true); } else { - QtConcurrent::run(db, &CardDatabase::loadCardDatabases); + const auto reloadOk0 = QtConcurrent::run([] { db->loadCardDatabases(); }); } SettingsCache::instance().setClientVersion(VERSION_STRING); } else { // previous config from this version found qDebug() << "Startup: found config with current version"; - QtConcurrent::run(db, &CardDatabase::loadCardDatabases); + const auto reloadOk1 = QtConcurrent::run([] { db->loadCardDatabases(); }); // Run the tips dialog only on subsequent startups. // On the first run after an install/update the startup is already crowded enough @@ -932,9 +932,9 @@ void MainWindow::createTrayIcon() void MainWindow::iconActivated(QSystemTrayIcon::ActivationReason reason) { if (reason == QSystemTrayIcon::DoubleClick) { - if (windowState() != Qt::WindowMinimized && windowState() != Qt::WindowMinimized + Qt::WindowMaximized) + if ((windowState() & Qt::WindowMinimized) == 0) { showMinimized(); - else { + } else { showNormal(); QApplication::setActiveWindow(this); } @@ -1058,7 +1058,7 @@ void MainWindow::cardDatabaseNewSetsFound(int numUnknownSets, QStringList unknow if (msgBox.clickedButton() == yesButton) { db->enableAllUnknownSets(); - QtConcurrent::run(db, &CardDatabase::loadCardDatabases); + const auto reloadOk1 = QtConcurrent::run([] { db->loadCardDatabases(); }); } else if (msgBox.clickedButton() == noButton) { db->markAllSetsAsKnown(); } else if (msgBox.clickedButton() == settingsButton) { @@ -1086,8 +1086,9 @@ void MainWindow::actCheckCardUpdates() } cardUpdateProcess = new QProcess(this); - connect(cardUpdateProcess, SIGNAL(error(QProcess::ProcessError)), this, - SLOT(cardUpdateError(QProcess::ProcessError))); + + connect(cardUpdateProcess, &QProcess::errorOccurred, this, &MainWindow::cardUpdateError); + connect(cardUpdateProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(cardUpdateFinished(int, QProcess::ExitStatus))); @@ -1148,7 +1149,7 @@ void MainWindow::exitCardDatabaseUpdate() cardUpdateProcess->deleteLater(); cardUpdateProcess = nullptr; - QtConcurrent::run(db, &CardDatabase::loadCardDatabases); + const auto reloadOk1 = QtConcurrent::run([] { db->loadCardDatabases(); }); } void MainWindow::cardUpdateError(QProcess::ProcessError err) @@ -1278,7 +1279,7 @@ void MainWindow::actAddCustomSet() QMessageBox::information( this, tr("Load sets/cards"), tr("The new sets/cards have been added successfully.\nCockatrice will now reload the card database.")); - QtConcurrent::run(db, &CardDatabase::loadCardDatabases); + const auto reloadOk1 = QtConcurrent::run([] { db->loadCardDatabases(); }); } else { QMessageBox::warning(this, tr("Load sets/cards"), tr("Sets/cards failed to import.")); } diff --git a/cockatrice/src/zoneviewzone.cpp b/cockatrice/src/zoneviewzone.cpp index 87d561d76..299ed9efa 100644 --- a/cockatrice/src/zoneviewzone.cpp +++ b/cockatrice/src/zoneviewzone.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include ZoneViewZone::ZoneViewZone(Player *_p, CardZone *_origZone, @@ -103,17 +103,17 @@ void ZoneViewZone::reorganizeCards() for (int i = 0; i < cardCount; ++i) cards[i]->setId(i); - int cols = floor(sqrt((double)cardCount / 2)); + int cols = qFloor(qSqrt((double)cardCount / 2)); if (cols > 7) cols = 7; - int rows = ceil((double)cardCount / cols); + int rows = qCeil((double)cardCount / cols); if (rows < 1) rows = 1; if (minRows == 0) minRows = rows; else if (rows < minRows) { rows = minRows; - cols = ceil((double)cardCount / minRows); + cols = qCeil((double)cardCount / minRows); } if (cols < 2) cols = 2; @@ -193,6 +193,10 @@ void ZoneViewZone::setPileView(int _pileView) void ZoneViewZone::addCardImpl(CardItem *card, int x, int /*y*/) { + // if x is negative set it to add at end + if (x < 0 || x >= cards.size()) { + x = cards.size(); + } cards.insert(x, card); card->setParentItem(this); card->update(); diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 5d2e60972..e8338c35f 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -36,8 +36,8 @@ set(ORACLE_LIBS) INCLUDE_DIRECTORIES(pb) INCLUDE_DIRECTORIES(sfmt) INCLUDE_DIRECTORIES(${PROTOBUF_INCLUDE_DIR}) -include_directories(${Qt5Core_INCLUDE_DIRS}) +include_directories(${${COCKATRICE_QT_VERSION_NAME}Core_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) add_library(cockatrice_common ${common_SOURCES} ${common_MOC_SRCS}) -target_link_libraries(cockatrice_common cockatrice_protocol) +target_link_libraries(cockatrice_common PUBLIC cockatrice_protocol) diff --git a/common/decklist.cpp b/common/decklist.cpp index a1463d0cc..09ceb2b76 100644 --- a/common/decklist.cpp +++ b/common/decklist.cpp @@ -292,7 +292,7 @@ bool AbstractDecklistCardNode::readElement(QXmlStreamReader *xml) { while (!xml->atEnd()) { xml->readNext(); - if (xml->isEndElement() && xml->name() == "card") + if (xml->isEndElement() && xml->name().toString() == "card") return false; } return true; @@ -435,7 +435,7 @@ bool DeckList::loadFromXml(QXmlStreamReader *xml) while (!xml->atEnd()) { xml->readNext(); if (xml->isStartElement()) { - if (xml->name() != "cockatrice_deck") + if (xml->name().toString() != "cockatrice_deck") return false; while (!xml->atEnd()) { xml->readNext(); @@ -602,7 +602,7 @@ bool DeckList::loadFromStream_Plain(QTextStream &in) int amount = 1; match = reMultiplier.match(cardName); if (match.hasMatch()) { - amount = match.capturedRef(1).toInt(); + amount = match.captured(1).toInt(); cardName = match.captured(2); } diff --git a/common/expression.cpp b/common/expression.cpp index 370a3864e..cc3a393b9 100644 --- a/common/expression.cpp +++ b/common/expression.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include peg::parser math(R"( @@ -32,19 +32,17 @@ Expression::Expression(double initial) : value(initial) { if (default_functions == nullptr) { default_functions = new QMap>(); - default_functions->insert("sin", [](double a) { return sin(a); }); - default_functions->insert("cos", [](double a) { return cos(a); }); - default_functions->insert("tan", [](double a) { return tan(a); }); - default_functions->insert("sqrt", [](double a) { return sqrt(a); }); - default_functions->insert("log", [](double a) { return log(a); }); - default_functions->insert("log10", [](double a) { return log(a); }); - default_functions->insert("trunc", [](double a) { return trunc(a); }); - default_functions->insert("abs", [](double a) { return fabs(a); }); - - default_functions->insert("floor", [](double a) { return floor(a); }); - default_functions->insert("ceil", [](double a) { return ceil(a); }); - default_functions->insert("round", [](double a) { return round(a); }); - default_functions->insert("trunc", [](double a) { return trunc(a); }); + default_functions->insert("abs", [](double a) { return qFabs(a); }); + default_functions->insert("ceil", [](double a) { return qCeil(a); }); + default_functions->insert("cos", [](double a) { return qCos(a); }); + default_functions->insert("floor", [](double a) { return qFloor(a); }); + default_functions->insert("log", [](double a) { return qLn(a); }); + default_functions->insert("log10", [](double a) { return qLn(a); }); + default_functions->insert("round", [](double a) { return qRound(a); }); + default_functions->insert("sin", [](double a) { return qSin(a); }); + default_functions->insert("sqrt", [](double a) { return qSqrt(a); }); + default_functions->insert("tan", [](double a) { return qTan(a); }); + default_functions->insert("trunc", [](double a) { return std::trunc(a); }); } fns = QMap>(*default_functions); } @@ -80,7 +78,7 @@ double Expression::eval(const peg::Ast &ast) result /= arg; break; case '^': - result = pow(result, arg); + result = qPow(result, arg); break; default: result = 0; diff --git a/common/rng_sfmt.cpp b/common/rng_sfmt.cpp index 88c5be82c..30d0ab638 100644 --- a/common/rng_sfmt.cpp +++ b/common/rng_sfmt.cpp @@ -14,7 +14,7 @@ RNG_SFMT::RNG_SFMT(QObject *parent) : RNG_Abstract(parent) { // initialize the random number generator with a 32bit integer seed (timestamp) - sfmt_init_gen_rand(&sfmt, QDateTime::currentDateTime().toTime_t()); + sfmt_init_gen_rand(&sfmt, QDateTime::currentDateTime().toSecsSinceEpoch()); } /** diff --git a/common/server.cpp b/common/server.cpp index 7a2aa3f82..5f0fe11eb 100644 --- a/common/server.cpp +++ b/common/server.cpp @@ -118,7 +118,7 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, Event_ConnectionClosed event; event.set_reason(Event_ConnectionClosed::LOGGEDINELSEWERE); event.set_reason_str("You have been logged out due to logging in at another location."); - event.set_end_time(QDateTime::currentDateTime().toTime_t()); + event.set_end_time(QDateTime::currentDateTime().toSecsSinceEpoch()); SessionEvent *se = users.value(name)->prepareSessionEvent(event); users.value(name)->sendProtocolItem(*se); @@ -230,6 +230,11 @@ void Server::addClient(Server_ProtocolHandler *client) void Server::removeClient(Server_ProtocolHandler *client) { + int clientIndex = clients.indexOf(client); + if (clientIndex == -1) { + qWarning() << "tried to remove non existing client"; + return; + } if (client->getConnectionType() == "tcp") tcpUserCount--; @@ -238,7 +243,7 @@ void Server::removeClient(Server_ProtocolHandler *client) webSocketUserCount--; QWriteLocker locker(&clientsLock); - clients.removeAt(clients.indexOf(client)); + clients.removeAt(clientIndex); ServerInfo_User *data = client->getUserInfo(); if (data) { Event_UserLeft event; diff --git a/common/server.h b/common/server.h index dc7220420..16cbf4086 100644 --- a/common/server.h +++ b/common/server.h @@ -57,8 +57,8 @@ private slots: public: mutable QReadWriteLock clientsLock, roomsLock; // locking order: roomsLock before clientsLock - Server(QObject *parent = nullptr); - ~Server() = default; + explicit Server(QObject *parent = nullptr); + virtual ~Server() = default; AuthenticationResult loginUser(Server_ProtocolHandler *session, QString &name, const QString &password, diff --git a/common/server_card.h b/common/server_card.h index e43997af0..14f09fba9 100644 --- a/common/server_card.h +++ b/common/server_card.h @@ -21,13 +21,13 @@ #define SERVER_CARD_H #include "pb/card_attributes.pb.h" +#include "pb/serverinfo_card.pb.h" #include "server_arrowtarget.h" #include #include class Server_CardZone; -class ServerInfo_Card; class Server_Card : public Server_ArrowTarget { @@ -52,7 +52,7 @@ private: public: Server_Card(QString _name, int _id, int _coord_x, int _coord_y, Server_CardZone *_zone = 0); - ~Server_Card(); + ~Server_Card() override; Server_CardZone *getZone() const { diff --git a/common/server_cardzone.cpp b/common/server_cardzone.cpp index 90b11695a..f34e24e3d 100644 --- a/common/server_cardzone.cpp +++ b/common/server_cardzone.cpp @@ -292,10 +292,10 @@ void Server_CardZone::insertCard(Server_Card *card, int x, int y) insertCardIntoCoordMap(card, x, y); } else { card->setCoords(0, 0); - if (x == -1) { - cards.append(card); - } else { + if (0 <= x && x < cards.length()) { cards.insert(x, card); + } else { + cards.append(card); } } card->setZone(this); diff --git a/common/server_database_interface.h b/common/server_database_interface.h index 4f404f7ff..80578cc60 100644 --- a/common/server_database_interface.h +++ b/common/server_database_interface.h @@ -147,7 +147,12 @@ public: LogMessage_TargetType /* targetType */, const int /* targetId */, const QString & /* targetName */){}; - bool checkUserIsBanned(Server_ProtocolHandler *session, QString &banReason, int &banSecondsRemaining); + virtual bool checkUserIsBanned(Server_ProtocolHandler * /* session */, + QString & /* banReason */, + int & /* banSecondsRemaining */) + { + return false; + }; virtual int checkNumberOfUserAccounts(const QString & /* email */) { return 0; diff --git a/common/server_game.cpp b/common/server_game.cpp index 46c67f8ea..631e3503a 100644 --- a/common/server_game.cpp +++ b/common/server_game.cpp @@ -69,6 +69,7 @@ Server_Game::Server_Game(const ServerInfo_User &_creatorInfo, spectatorsNeedPassword(_spectatorsNeedPassword), spectatorsCanTalk(_spectatorsCanTalk), spectatorsSeeEverything(_spectatorsSeeEverything), inactivityCounter(0), startTimeOfThisGame(0), secondsElapsed(0), firstGameStarted(false), turnOrderReversed(false), startTime(QDateTime::currentDateTime()), + pingClock(nullptr), #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) gameMutex() #else @@ -97,7 +98,7 @@ Server_Game::~Server_Game() gameClosed = true; sendGameEventContainer(prepareGameEvent(Event_GameClosed(), -1)); - for (Server_Player *player : players.values()) { + for (auto *player : players.values()) { player->prepareDestroy(); } players.clear(); @@ -112,10 +113,22 @@ Server_Game::~Server_Game() replayList.append(currentReplay); storeGameInformation(); - for (int i = 0; i < replayList.size(); ++i) - delete replayList[i]; + for (auto *replay : replayList) { + delete replay; + } + replayList.clear(); + + room = nullptr; + currentReplay = nullptr; + creatorInfo = nullptr; + + if (pingClock) { + delete pingClock; + pingClock = nullptr; + } qDebug() << "Server_Game destructor: gameId=" << gameId; + deleteLater(); } void Server_Game::storeGameInformation() @@ -126,7 +139,7 @@ void Server_Game::storeGameInformation() ServerInfo_ReplayMatch *replayMatchInfo = replayEvent.mutable_match_info(); replayMatchInfo->set_game_id(gameInfo.game_id()); replayMatchInfo->set_room_name(room->getName().toStdString()); - replayMatchInfo->set_time_started(QDateTime::currentDateTime().addSecs(-secondsElapsed).toTime_t()); + replayMatchInfo->set_time_started(QDateTime::currentDateTime().addSecs(-secondsElapsed).toSecsSinceEpoch()); replayMatchInfo->set_length(secondsElapsed); replayMatchInfo->set_game_name(gameInfo.description()); @@ -769,6 +782,6 @@ void Server_Game::getInfo(ServerInfo_Game &result) const result.set_spectators_can_chat(spectatorsCanTalk); result.set_spectators_omniscient(spectatorsSeeEverything); result.set_spectators_count(getSpectatorCount()); - result.set_start_time(startTime.toTime_t()); + result.set_start_time(startTime.toSecsSinceEpoch()); } } diff --git a/common/server_player.cpp b/common/server_player.cpp index d2cd8f053..ee78a7fba 100644 --- a/common/server_player.cpp +++ b/common/server_player.cpp @@ -120,6 +120,7 @@ Server_Player::~Server_Player() = default; void Server_Player::prepareDestroy() { delete deck; + deck = nullptr; playerMutex.lock(); if (userInterface) { @@ -1234,7 +1235,6 @@ Server_Player::cmdAttachCard(const Command_AttachCard &cmd, ResponseContainer & return Response::RespContextError; } - // Get all arrows pointing to or originating from the card being attached and delete them. QMapIterator playerIterator(game->getPlayers()); while (playerIterator.hasNext()) { Server_Player *p = playerIterator.next().value(); diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index 6db545471..b5273b9af 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -25,8 +25,8 @@ #include #include +#include #include -#include Server_ProtocolHandler::Server_ProtocolHandler(Server *_server, Server_DatabaseInterface *_databaseInterface, @@ -44,6 +44,7 @@ Server_ProtocolHandler::~Server_ProtocolHandler() } // This function must only be called from the thread this object lives in. +// Except when the server is shutting down. // The thread must not hold any server locks when calling this (e.g. clientsLock, roomsLock). void Server_ProtocolHandler::prepareDestroy() { @@ -412,7 +413,7 @@ void Server_ProtocolHandler::pingClockTimeout() } } - if (((timeRunning - lastActionReceived) >= ceil(server->getIdleClientTimeout() * .9)) && + if (((timeRunning - lastActionReceived) >= qCeil(server->getIdleClientTimeout() * .9)) && (!idleClientWarningSent) && (server->getIdleClientTimeout() > 0)) { Event_NotifyUser event; event.set_type(Event_NotifyUser::IDLEWARNING); @@ -489,7 +490,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd Response_Login *re = new Response_Login; re->set_denied_reason_str(reasonStr.toStdString()); if (banSecondsLeft != 0) - re->set_denied_end_time(QDateTime::currentDateTime().addSecs(banSecondsLeft).toTime_t()); + re->set_denied_end_time(QDateTime::currentDateTime().addSecs(banSecondsLeft).toSecsSinceEpoch()); rc.setResponseExtension(re); return Response::RespUserIsBanned; } diff --git a/common/server_protocolhandler.h b/common/server_protocolhandler.h index 760ca2730..b80e08c7e 100644 --- a/common/server_protocolhandler.h +++ b/common/server_protocolhandler.h @@ -63,7 +63,6 @@ protected: private: QList messageSizeOverTime, messageCountOverTime, commandCountOverTime; int timeRunning, lastDataReceived, lastActionReceived; - QTimer *pingClock; virtual void transmitProtocolItem(const ServerMessage &item) = 0; diff --git a/common/serverinfo_user_container.cpp b/common/serverinfo_user_container.cpp index 1c261040c..597ebd01a 100644 --- a/common/serverinfo_user_container.cpp +++ b/common/serverinfo_user_container.cpp @@ -16,7 +16,7 @@ ServerInfo_User_Container::ServerInfo_User_Container(const ServerInfo_User_Conta if (other.userInfo) userInfo = new ServerInfo_User(*other.userInfo); else - userInfo = 0; + userInfo = nullptr; } ServerInfo_User_Container::~ServerInfo_User_Container() diff --git a/common/serverinfo_user_container.h b/common/serverinfo_user_container.h index 16135d65f..3a6484661 100644 --- a/common/serverinfo_user_container.h +++ b/common/serverinfo_user_container.h @@ -9,7 +9,7 @@ protected: ServerInfo_User *userInfo; public: - ServerInfo_User_Container(ServerInfo_User *_userInfo = 0); + ServerInfo_User_Container(ServerInfo_User *_userInfo = nullptr); ServerInfo_User_Container(const ServerInfo_User &_userInfo); ServerInfo_User_Container(const ServerInfo_User_Container &other); ServerInfo_User_Container &operator=(const ServerInfo_User_Container &other) = default; diff --git a/common/stringsizes.h b/common/stringsizes.h index 56fba626e..d8015ccf0 100644 --- a/common/stringsizes.h +++ b/common/stringsizes.h @@ -3,7 +3,6 @@ #define STRINGSIZES_H #include -#include // max size for short strings, like names and things that are generally a single phrase constexpr int MAX_NAME_LENGTH = 0xff; diff --git a/dbconverter/CMakeLists.txt b/dbconverter/CMakeLists.txt index 5e631b9a0..b3696ae35 100644 --- a/dbconverter/CMakeLists.txt +++ b/dbconverter/CMakeLists.txt @@ -13,24 +13,25 @@ SET(dbconverter_SOURCES ${VERSION_STRING_CPP} ) -# Qt5 -find_package(Qt5 COMPONENTS Network Widgets REQUIRED) -set(dbconverter_QT_MODULES Qt5::Core Qt5::Network Qt5::Widgets) + SET(QT_DONT_USE_QTGUI TRUE) -QT5_WRAP_CPP(dbconverter_SOURCES - ../cockatrice/src/settingscache.h - ../cockatrice/src/settings/carddatabasesettings.h -) +IF(Qt6_FOUND) + Qt6_WRAP_CPP(dbconverter_SOURCES + ../cockatrice/src/settingscache.h + ../cockatrice/src/settings/carddatabasesettings.h + ) +ELSEIF(Qt5_FOUND) + Qt5_WRAP_CPP(dbconverter_SOURCES + ../cockatrice/src/settingscache.h + ../cockatrice/src/settings/carddatabasesettings.h + ) +ENDIF() # Build servatrice binary and link it ADD_EXECUTABLE(dbconverter MACOSX_BUNDLE ${dbconverter_SOURCES} ${dbconverter_MOC_SRCS}) -if(MSVC) - TARGET_LINK_LIBRARIES(dbconverter ${dbconverter_QT_MODULES} Qt5::WinMain) -else() - TARGET_LINK_LIBRARIES(dbconverter ${dbconverter_QT_MODULES}) -endif() +TARGET_LINK_LIBRARIES(dbconverter ${DB_CONVERTER_QT_MODULES}) # install rules if(UNIX) @@ -55,9 +56,8 @@ if(APPLE) # these needs to be relative to CMAKE_INSTALL_PREFIX set(plugin_dest_dir dbconverter.app/Contents/Plugins) set(qtconf_dest_dir dbconverter.app/Contents/Resources) - get_filename_component(QT_LIBRARY_DIR "${QT_LIBRARY_DIR}/.." ABSOLUTE) - # qt5 plugins: platforms, sqldrivers/mysql + # Qt plugins: platforms install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime FILES_MATCHING PATTERN "*.dSYM" EXCLUDE @@ -87,10 +87,12 @@ if(WIN32) install(DIRECTORY "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${CMAKE_BUILD_TYPE}/" DESTINATION ./ FILES_MATCHING PATTERN "*.dll") - # qt5 plugins: platforms, sqldrivers/mysql - install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime - FILES_MATCHING REGEX "(platforms/.*)\\.dll" - REGEX ".*d\\.dll" EXCLUDE) + # Qt plugins: platforms + install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime FILES_MATCHING + PATTERN "platforms/qdirect2d.dll" + PATTERN "platforms/qminimal.dll" + PATTERN "platforms/qoffscreen.dll" + PATTERN "platforms/qwindows.dll") install(CODE " file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths] diff --git a/oracle/CMakeLists.txt b/oracle/CMakeLists.txt index 3c201decb..c77d6ac8e 100644 --- a/oracle/CMakeLists.txt +++ b/oracle/CMakeLists.txt @@ -2,12 +2,12 @@ # # provides the oracle binary -PROJECT(Oracle VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") +project(Oracle VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") # paths set(DESKTOPDIR share/applications CACHE STRING "path to .desktop files") -SET(oracle_SOURCES +set(oracle_SOURCES src/main.cpp src/oraclewizard.cpp src/oracleimporter.cpp @@ -34,13 +34,13 @@ SET(oracle_SOURCES set(oracle_RESOURCES oracle.qrc) -IF(UPDATE_TRANSLATIONS) +if(UPDATE_TRANSLATIONS) FILE(GLOB_RECURSE translate_oracle_SRCS src/*.cpp src/*.h ../cockatrice/src/settingscache.cpp) SET(translate_SRCS ${translate_oracle_SRCS}) - SET(oracle_TS "${CMAKE_CURRENT_SOURCE_DIR}/translations/oracle_en@source.ts") -ELSE() + SET(oracle_TS "${CMAKE_CURRENT_SOURCE_DIR}/oracle_en@source.ts") +else() FILE(GLOB oracle_TS "${CMAKE_CURRENT_SOURCE_DIR}/translations/*.ts") -ENDIF(UPDATE_TRANSLATIONS) +endif(UPDATE_TRANSLATIONS) if(WIN32) set(oracle_SOURCES ${oracle_SOURCES} oracle.rc) @@ -50,38 +50,19 @@ if(APPLE) set(MACOSX_BUNDLE_ICON_FILE appicon.icns) set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) set(oracle_SOURCES ${oracle_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns) -ENDIF(APPLE) +endif(APPLE) -# Qt5 -find_package(Qt5 COMPONENTS Concurrent Network Svg Widgets REQUIRED) -set(ORACLE_QT_MODULES Qt5::Concurrent Qt5::Network Qt5::Svg Qt5::Widgets) - -# Qt5LinguistTools -find_package(Qt5LinguistTools) -if(Qt5LinguistTools_FOUND) - list(APPEND ORACLE_LIBS Qt5::LinguistTools) - - if(NOT Qt5_LRELEASE_EXECUTABLE) - MESSAGE(WARNING "Qt's lrelease not found.") - endif() - - if(UPDATE_TRANSLATIONS) - if(NOT Qt5_LUPDATE_EXECUTABLE) - MESSAGE(WARNING "Qt's lupdate not found.") - endif() - QT5_CREATE_TRANSLATION(oracle_QM ${translate_SRCS} ${oracle_TS}) - else() - QT5_ADD_TRANSLATION(oracle_QM ${oracle_TS}) - endif() +if(Qt6_FOUND) + Qt6_ADD_RESOURCES(oracle_RESOURCES_RCC ${oracle_RESOURCES}) +elseif(Qt5_FOUND) + Qt5_ADD_RESOURCES(oracle_RESOURCES_RCC ${oracle_RESOURCES}) endif() -QT5_ADD_RESOURCES(oracle_RESOURCES_RCC ${oracle_RESOURCES}) - INCLUDE_DIRECTORIES(../cockatrice/src) # Libz is required to support zipped files FIND_PACKAGE(ZLIB) -IF(ZLIB_FOUND) +if(ZLIB_FOUND) INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS}) ADD_DEFINITIONS("-DHAS_ZLIB") @@ -89,34 +70,60 @@ IF(ZLIB_FOUND) src/zip/unzip.cpp src/zip/zipglobal.cpp ) -ELSE() +else() MESSAGE(STATUS "Oracle: zlib not found; ZIP support disabled") -ENDIF() +endif() # LibLZMA is required to support xz files FIND_PACKAGE(LibLZMA) -IF(LIBLZMA_FOUND) +if(LIBLZMA_FOUND) INCLUDE_DIRECTORIES(${LIBLZMA_INCLUDE_DIRS}) ADD_DEFINITIONS("-DHAS_LZMA") set(oracle_SOURCES ${oracle_SOURCES} src/lzma/decompress.cpp ) -ELSE() +else() MESSAGE(STATUS "Oracle: LibLZMA not found; xz support disabled") -ENDIF() +endif() -# Build oracle binary and link it -ADD_EXECUTABLE(oracle WIN32 MACOSX_BUNDLE ${oracle_SOURCES} ${oracle_QM} ${oracle_RESOURCES_RCC} ${oracle_MOC_SRCS}) -TARGET_LINK_LIBRARIES(oracle ${ORACLE_QT_MODULES}) +set(ORACLE_MAC_QM_INSTALL_DIR "oracle.app/Contents/Resources/translations") +set(ORACLE_UNIX_QM_INSTALL_DIR "share/oracle/translations") +set(ORACLE_WIN32_QM_INSTALL_DIR "translations") -IF(ZLIB_FOUND) - TARGET_LINK_LIBRARIES(oracle ${ZLIB_LIBRARIES}) -ENDIF() +if(Qt6_FOUND) + # Qt6 Translations are linked after the executable is created in manual mode + qt6_add_executable(oracle WIN32 MACOSX_BUNDLE ${oracle_SOURCES} ${oracle_RESOURCES_RCC} ${oracle_MOC_SRCS} MANUAL_FINALIZATION) +elseif(Qt5_FOUND) + # Qt5 Translations need to be linked at executable creation time + if(Qt5LinguistTools_FOUND) + if(UPDATE_TRANSLATIONS) + qt5_create_translation(oracle_QM ${translate_SRCS} ${oracle_TS}) + else() + qt5_add_translation(oracle_QM ${oracle_TS}) + endif() + endif() + add_executable(oracle WIN32 MACOSX_BUNDLE ${oracle_SOURCES} ${oracle_QM} ${oracle_RESOURCES_RCC} ${oracle_MOC_SRCS}) + if(UNIX) + if(APPLE) + install(FILES ${oracle_QM} DESTINATION ${ORACLE_MAC_QM_INSTALL_DIR}) + else() + install(FILES ${oracle_QM} DESTINATION ${ORACLE_UNIX_QM_INSTALL_DIR}) + endif() + elseif(WIN32) + install(FILES ${oracle_QM} DESTINATION ${ORACLE_WIN32_QM_INSTALL_DIR}) + endif() +endif() -IF(LIBLZMA_FOUND) - TARGET_LINK_LIBRARIES(oracle ${LIBLZMA_LIBRARIES}) -ENDIF() +TARGET_LINK_LIBRARIES(oracle PUBLIC ${ORACLE_QT_MODULES}) + +if(ZLIB_FOUND) + TARGET_LINK_LIBRARIES(oracle PUBLIC ${ZLIB_LIBRARIES}) +endif() + +if(LIBLZMA_FOUND) + TARGET_LINK_LIBRARIES(oracle PUBLIC ${LIBLZMA_LIBRARIES}) +endif() if(UNIX) if(APPLE) @@ -129,17 +136,14 @@ if(UNIX) set_target_properties(oracle PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/Info.plist) INSTALL(TARGETS oracle BUNDLE DESTINATION ./) - INSTALL(FILES ${oracle_QM} DESTINATION ./oracle.app/Contents/Resources/translations) else() # Assume linux INSTALL(TARGETS oracle RUNTIME DESTINATION bin/) INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/oracle.png DESTINATION ${ICONDIR}/hicolor/48x48/apps) INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/oracle.svg DESTINATION ${ICONDIR}/hicolor/scalable/apps) - INSTALL(FILES ${oracle_QM} DESTINATION share/oracle/translations) endif() elseif(WIN32) INSTALL(TARGETS oracle RUNTIME DESTINATION ./) - INSTALL(FILES ${oracle_QM} DESTINATION ./translations) endif() IF (NOT WIN32 AND NOT APPLE) @@ -150,9 +154,8 @@ if(APPLE) # these needs to be relative to CMAKE_INSTALL_PREFIX set(plugin_dest_dir oracle.app/Contents/Plugins) set(qtconf_dest_dir oracle.app/Contents/Resources) - get_filename_component(QT_LIBRARY_DIR "${QT_LIBRARY_DIR}/.." ABSOLUTE) - # qt5 plugins: iconengines, platforms + # Qt plugins: iconengines, platforms, styles, tls (Qt6) install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime FILES_MATCHING PATTERN "*.dSYM" EXCLUDE @@ -160,6 +163,7 @@ if(APPLE) PATTERN "iconengines/*.dylib" PATTERN "platforms/*.dylib" PATTERN "styles/*.dylib" + PATTERN "tls/*.dylib" ) install(CODE " @@ -177,7 +181,7 @@ Translations = Resources/translations\") " COMPONENT Runtime) endif() -IF(WIN32) +if(WIN32) # these needs to be relative to CMAKE_INSTALL_PREFIX set(plugin_dest_dir Plugins) set(qtconf_dest_dir .) @@ -185,10 +189,20 @@ IF(WIN32) install(DIRECTORY "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${CMAKE_BUILD_TYPE}/" DESTINATION ./ FILES_MATCHING PATTERN "*.dll") - # qt5 plugins: iconengines, platforms - - install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime - FILES_MATCHING REGEX "(iconengines|platforms|styles)/.*[^d]\\.dll") + # Qt plugins: iconengines, platforms, styles, tls (Qt6) + install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime FILES_MATCHING + PATTERN "iconengines/qsvgicon.dll" + PATTERN "platforms/qdirect2d.dll" + PATTERN "platforms/qminimal.dll" + PATTERN "platforms/qoffscreen.dll" + PATTERN "platforms/qwindows.dll" + PATTERN "styles/qcertonlybackend.dll" + PATTERN "styles/qopensslbackend.dll" + PATTERN "styles/qschannelbackend.dll" + PATTERN "styles/qwindowsvistastyle.dll" + PATTERN "tls/qcertonlybackend.dll" + PATTERN "tls/qopensslbackend.dll" + PATTERN "tls/qschannelbackend.dll") install(CODE " file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths] @@ -204,3 +218,26 @@ Translations = Resources/translations\") fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/Oracle.exe\" \"\${QTPLUGINS}\" \"${libSearchDirs}\") " COMPONENT Runtime) endif() + +if(Qt6LinguistTools_FOUND) + #Qt6 Translations happen after the executable is built up + if(UPDATE_TRANSLATIONS) + qt6_add_translations(oracle TS_FILES ${oracle_TS} SOURCES ${translate_SRCS} QM_FILES_OUTPUT_VARIABLE oracle_QM) + else() + qt6_add_translations(oracle TS_FILES ${oracle_TS} QM_FILES_OUTPUT_VARIABLE oracle_QM) + endif() + + if(UNIX) + if(APPLE) + install(FILES ${oracle_QM} DESTINATION ${ORACLE_MAC_QM_INSTALL_DIR}) + else() + install(FILES ${oracle_QM} DESTINATION ${ORACLE_UNIX_QM_INSTALL_DIR}) + endif() + elseif(WIN32) + install(FILES ${oracle_QM} DESTINATION ${ORACLE_WIN32_QM_INSTALL_DIR}) + endif() +endif() + +if(Qt6_FOUND) + qt6_finalize_target(oracle) +endif() diff --git a/oracle/translations/oracle_en@source.ts b/oracle/oracle_en@source.ts similarity index 100% rename from oracle/translations/oracle_en@source.ts rename to oracle/oracle_en@source.ts diff --git a/oracle/src/main.cpp b/oracle/src/main.cpp index 6cae0b6d6..934286a2b 100644 --- a/oracle/src/main.cpp +++ b/oracle/src/main.cpp @@ -22,9 +22,28 @@ void installNewTranslator() { QString lang = SettingsCache::instance().getLang(); - qtTranslator->load("qt_" + lang, QLibraryInfo::location(QLibraryInfo::TranslationsPath)); + QString qtNameHint = "qt_" + lang; +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QString qtTranslationPath = QLibraryInfo::path(QLibraryInfo::TranslationsPath); +#else + QString qtTranslationPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath); +#endif + + bool qtTranslationLoaded = qtTranslator->load(qtNameHint, qtTranslationPath); + if (!qtTranslationLoaded) { + qDebug() << "Unable to load qt translation" << qtNameHint << "at" << qtTranslationPath; + } else { + qDebug() << "Loaded qt translation" << qtNameHint << "at" << qtTranslationPath; + } qApp->installTranslator(qtTranslator); - translator->load(translationPrefix + "_" + lang, translationPath); + + QString appNameHint = translationPrefix + "_" + lang; + bool appTranslationLoaded = qtTranslator->load(appNameHint, translationPath); + if (!appTranslationLoaded) { + qDebug() << "Unable to load" << translationPrefix << "translation" << appNameHint << "at" << translationPath; + } else { + qDebug() << "Loaded" << translationPrefix << "translation" << appNameHint << "at" << translationPath; + } qApp->installTranslator(translator); } @@ -62,10 +81,8 @@ int main(int argc, char *argv[]) QIcon icon("theme:appicon.svg"); wizard.setWindowIcon(icon); -#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) // set name of the app desktop file; used by wayland to load the window icon QGuiApplication::setDesktopFileName("oracle"); -#endif wizard.show(); diff --git a/oracle/src/oracleimporter.cpp b/oracle/src/oracleimporter.cpp index 785f54700..9ee88baa7 100644 --- a/oracle/src/oracleimporter.cpp +++ b/oracle/src/oracleimporter.cpp @@ -116,7 +116,7 @@ CardInfoPtr OracleImporter::addCard(QString name, QStringList symbols = manacost.split("}"); QString formattedCardCost; for (QString symbol : symbols) { - if (symbol.contains(QRegExp("[0-9WUBGRP]/[0-9WUBGRP]"))) { + if (symbol.contains(QRegularExpression("[0-9WUBGRP]/[0-9WUBGRP]"))) { symbol.append("}"); } else { symbol.remove(QChar('{')); diff --git a/oracle/src/oraclewizard.cpp b/oracle/src/oraclewizard.cpp index 957546fd8..6a480b737 100644 --- a/oracle/src/oraclewizard.cpp +++ b/oracle/src/oraclewizard.cpp @@ -143,16 +143,19 @@ IntroPage::IntroPage(QWidget *parent) : OracleWizardPage(parent) languageLabel = new QLabel(this); versionLabel = new QLabel(this); languageBox = new QComboBox(this); - QString setLanguage = SettingsCache::instance().getLang(); - QStringList qmFiles = findQmFiles(); - for (int i = 0; i < qmFiles.size(); i++) { - QString langName = languageName(qmFiles[i]); - languageBox->addItem(langName, qmFiles[i]); - if ((qmFiles[i] == setLanguage) || - (setLanguage.isEmpty() && langName == QCoreApplication::translate("i18n", DEFAULT_LANG_NAME))) { - languageBox->setCurrentIndex(i); - } + QStringList languageCodes = findQmFiles(); + for (const QString &code : languageCodes) { + QString langName = languageName(code); + languageBox->addItem(langName, code); + } + + QString setLanguage = QCoreApplication::translate("i18n", DEFAULT_LANG_NAME); + int index = languageBox->findText(setLanguage, Qt::MatchExactly); + if (index == -1) { + qWarning() << "could not find language" << setLanguage; + } else { + languageBox->setCurrentIndex(index); } connect(languageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(languageBoxChanged(int))); @@ -170,15 +173,20 @@ QStringList IntroPage::findQmFiles() { QDir dir(translationPath); QStringList fileNames = dir.entryList(QStringList(translationPrefix + "_*.qm"), QDir::Files, QDir::Name); - fileNames.replaceInStrings(QRegExp(translationPrefix + "_(.*)\\.qm"), "\\1"); - fileNames.removeOne("en@source"); + fileNames.replaceInStrings(QRegularExpression(translationPrefix + "_(.*)\\.qm"), "\\1"); return fileNames; } -QString IntroPage::languageName(const QString &qmFile) +QString IntroPage::languageName(const QString &lang) { QTranslator qTranslator; - qTranslator.load(translationPrefix + "_" + qmFile + ".qm", translationPath); + + QString appNameHint = translationPrefix + "_" + lang; + bool appTranslationLoaded = qTranslator.load(appNameHint, translationPath); + if (!appTranslationLoaded) { + qDebug() << "Unable to load" << translationPrefix << "translation" << appNameHint << "at" << translationPath; + } + return qTranslator.translate("i18n", DEFAULT_LANG_NAME); } @@ -454,8 +462,10 @@ void LoadSetsPage::readSetsFromByteArray(QByteArray data) zipDownloadFailed(tr("Xz extraction failed.")); return; } + const auto &outBufferData = outBuffer->data(); - future = QtConcurrent::run(wizard()->importer, &OracleImporter::readSetsFromByteArray, outBuffer->data()); + future = QtConcurrent::run( + [this, &outBufferData] { return wizard()->importer->readSetsFromByteArray(outBufferData); }); watcher.setFuture(future); return; #else @@ -495,8 +505,10 @@ void LoadSetsPage::readSetsFromByteArray(QByteArray data) uz.closeArchive(); return; } + const auto &outBufferData = outBuffer->data(); - future = QtConcurrent::run(wizard()->importer, &OracleImporter::readSetsFromByteArray, outBuffer->data()); + future = QtConcurrent::run( + [this, &outBufferData] { return wizard()->importer->readSetsFromByteArray(outBufferData); }); watcher.setFuture(future); return; #else @@ -510,7 +522,7 @@ void LoadSetsPage::readSetsFromByteArray(QByteArray data) #endif } // Start the computation. - future = QtConcurrent::run(wizard()->importer, &OracleImporter::readSetsFromByteArray, data); + future = QtConcurrent::run([this, &data] { return wizard()->importer->readSetsFromByteArray(data); }); watcher.setFuture(future); } diff --git a/oracle/src/oraclewizard.h b/oracle/src/oraclewizard.h index 362c19def..f660be32a 100644 --- a/oracle/src/oraclewizard.h +++ b/oracle/src/oraclewizard.h @@ -82,7 +82,7 @@ public: private: QStringList findQmFiles(); - QString languageName(const QString &qmFile); + QString languageName(const QString &lang); private: QLabel *label, *languageLabel, *versionLabel; @@ -189,4 +189,4 @@ protected: QString getFileType() override; }; -#endif \ No newline at end of file +#endif diff --git a/oracle/src/qt-json/json.cpp b/oracle/src/qt-json/json.cpp index a5dd69c20..418f9c6b4 100644 --- a/oracle/src/qt-json/json.cpp +++ b/oracle/src/qt-json/json.cpp @@ -31,36 +31,35 @@ */ #include "json.h" + +#include #include namespace QtJson { - static QString sanitizeString(QString str) { - str.replace(QLatin1String("\\"), QLatin1String("\\\\")); - str.replace(QLatin1String("\""), QLatin1String("\\\"")); - str.replace(QLatin1String("\b"), QLatin1String("\\b")); - str.replace(QLatin1String("\f"), QLatin1String("\\f")); - str.replace(QLatin1String("\n"), QLatin1String("\\n")); - str.replace(QLatin1String("\r"), QLatin1String("\\r")); - str.replace(QLatin1String("\t"), QLatin1String("\\t")); - return QString(QLatin1String("\"%1\"")).arg(str); + str.replace(QLatin1String("\\"), QLatin1String("\\\\")); + str.replace(QLatin1String("\""), QLatin1String("\\\"")); + str.replace(QLatin1String("\b"), QLatin1String("\\b")); + str.replace(QLatin1String("\f"), QLatin1String("\\f")); + str.replace(QLatin1String("\n"), QLatin1String("\\n")); + str.replace(QLatin1String("\r"), QLatin1String("\\r")); + str.replace(QLatin1String("\t"), QLatin1String("\\t")); + return QString(QLatin1String("\"%1\"")).arg(str); } static QByteArray join(const QList &list, const QByteArray &sep) { - QByteArray res; - Q_FOREACH(const QByteArray &i, list) - { - if(!res.isEmpty()) - { - res += sep; - } - res += i; + QByteArray res; + Q_FOREACH (const QByteArray &i, list) { + if (!res.isEmpty()) { + res += sep; } - return res; + res += i; + } + return res; } /** @@ -68,8 +67,8 @@ static QByteArray join(const QList &list, const QByteArray &sep) */ QVariant Json::parse(const QString &json) { - bool success = true; - return Json::parse(json, success); + bool success = true; + return Json::parse(json, success); } /** @@ -77,149 +76,160 @@ QVariant Json::parse(const QString &json) */ QVariant Json::parse(const QString &json, bool &success) { - success = true; + success = true; - //Return an empty QVariant if the JSON data is either null or empty - if(!json.isNull() || !json.isEmpty()) - { - QString data = json; - //We'll start from index 0 - int index = 0; + // Return an empty QVariant if the JSON data is either null or empty + if (!json.isNull() || !json.isEmpty()) { + QString data = json; + // We'll start from index 0 + int index = 0; - //Parse the first value - QVariant value = Json::parseValue(data, index, success); + // Parse the first value + QVariant value = Json::parseValue(data, index, success); - //Return the parsed value - return value; - } - else - { - //Return the empty QVariant - return QVariant(); - } + // Return the parsed value + return value; + } else { + // Return the empty QVariant + return QVariant(); + } } QByteArray Json::serialize(const QVariant &data) { - bool success = true; - return Json::serialize(data, success); + bool success = true; + return Json::serialize(data, success); } QByteArray Json::serialize(const QVariant &data, bool &success) { - QByteArray str; - success = true; + QByteArray str; + success = true; - if(!data.isValid()) // invalid or null? - { - str = "null"; + if (!data.isValid()) // invalid or null? + { + str = "null"; + } +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + else if ((data.typeId() == QMetaType::Type::QVariantList) || + (data.typeId() == QMetaType::Type::QStringList)) // variant is a list? +#else + else if ((data.type() == QVariant::List) || (data.type() == QVariant::StringList)) // variant is a list? +#endif + { + QList values; + const QVariantList list = data.toList(); + Q_FOREACH (const QVariant &v, list) { + QByteArray serializedValue = serialize(v); + if (serializedValue.isNull()) { + success = false; + break; + } + values << serializedValue; } - else if((data.type() == QVariant::List) || (data.type() == QVariant::StringList)) // variant is a list? - { - QList values; - const QVariantList list = data.toList(); - Q_FOREACH(const QVariant& v, list) - { - QByteArray serializedValue = serialize(v); - if(serializedValue.isNull()) - { - success = false; - break; - } - values << serializedValue; - } - str = "[ " + join( values, ", " ) + " ]"; - } - else if(data.type() == QVariant::Hash) // variant is a hash? - { - const QVariantHash vhash = data.toHash(); - QHashIterator it( vhash ); - str = "{ "; - QList pairs; + str = "[ " + join(values, ", ") + " ]"; + } +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + else if ((data.typeId() == QMetaType::Type::QVariantHash)) // variant is a list? +#else + else if (data.type() == QVariant::Hash) // variant is a hash? +#endif + { + const QVariantHash vhash = data.toHash(); + QHashIterator it(vhash); + str = "{ "; + QList pairs; - while(it.hasNext()) - { - it.next(); - QByteArray serializedValue = serialize(it.value()); + while (it.hasNext()) { + it.next(); + QByteArray serializedValue = serialize(it.value()); - if(serializedValue.isNull()) - { - success = false; - break; - } - - pairs << sanitizeString(it.key()).toUtf8() + " : " + serializedValue; + if (serializedValue.isNull()) { + success = false; + break; } - str += join(pairs, ", "); - str += " }"; + pairs << sanitizeString(it.key()).toUtf8() + " : " + serializedValue; } - else if(data.type() == QVariant::Map) // variant is a map? - { - const QVariantMap vmap = data.toMap(); - QMapIterator it( vmap ); - str = "{ "; - QList pairs; - while(it.hasNext()) - { - it.next(); - QByteArray serializedValue = serialize(it.value()); - if(serializedValue.isNull()) - { - success = false; - break; - } - pairs << sanitizeString(it.key()).toUtf8() + " : " + serializedValue; - } - str += join(pairs, ", "); - str += " }"; - } - else if((data.type() == QVariant::String) || (data.type() == QVariant::ByteArray)) // a string or a byte array? - { - str = sanitizeString(data.toString()).toUtf8(); - } - else if(data.type() == QVariant::Double) // double? - { - str = QByteArray::number(data.toDouble(), 'g', 20); - if(!str.contains(".") && ! str.contains("e")) - { - str += ".0"; - } - } - else if (data.type() == QVariant::Bool) // boolean value? - { - str = data.toBool() ? "true" : "false"; - } - else if (data.type() == QVariant::ULongLong) // large unsigned number? - { - str = QByteArray::number(data.value()); - } - else if ( data.canConvert() ) // any signed number? - { - str = QByteArray::number(data.value()); - } - else if (data.canConvert()) - { - str = QString::number(data.value()).toUtf8(); - } - else if (data.canConvert()) // can value be converted to string? - { - // this will catch QDate, QDateTime, QUrl, ... - str = sanitizeString(data.toString()).toUtf8(); - } - else - { + + str += join(pairs, ", "); + str += " }"; + } +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + else if ((data.typeId() == QMetaType::Type::QVariantMap)) // variant is a list? +#else + else if (data.type() == QVariant::Map) // variant is a map? +#endif + { + const QVariantMap vmap = data.toMap(); + QMapIterator it(vmap); + str = "{ "; + QList pairs; + while (it.hasNext()) { + it.next(); + QByteArray serializedValue = serialize(it.value()); + if (serializedValue.isNull()) { success = false; + break; + } + pairs << sanitizeString(it.key()).toUtf8() + " : " + serializedValue; } - if (success) - { - return str; - } - else - { - return QByteArray(); + str += join(pairs, ", "); + str += " }"; + } +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + else if ((data.typeId() == QMetaType::Type::QString) || + (data.typeId() == QMetaType::Type::QByteArray)) // variant is a list? +#else + else if ((data.type() == QVariant::String) || (data.type() == QVariant::ByteArray)) // a string or a byte array? +#endif + { + str = sanitizeString(data.toString()).toUtf8(); + } +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + else if (data.typeId() == QMetaType::Type::Double) +#else + else if (data.type() == QVariant::Double) // double? +#endif + { + str = QByteArray::number(data.toDouble(), 'g', 20); + if (!str.contains(".") && !str.contains("e")) { + str += ".0"; } + } +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + else if (data.typeId() == QMetaType::Type::Bool) +#else + else if (data.type() == QVariant::Bool) // boolean value? +#endif + { + str = data.toBool() ? "true" : "false"; + } +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + else if (data.typeId() == QMetaType::Type::ULongLong) +#else + else if (data.type() == QVariant::ULongLong) // large unsigned number? +#endif + { + str = QByteArray::number(data.value()); + } else if (data.canConvert()) // any signed number? + { + str = QByteArray::number(data.value()); + } else if (data.canConvert()) { + str = QString::number(data.value()).toUtf8(); + } else if (data.canConvert()) // can value be converted to string? + { + // this will catch QDate, QDateTime, QUrl, ... + str = sanitizeString(data.toString()).toUtf8(); + } else { + success = false; + } + if (success) { + return str; + } else { + return QByteArray(); + } } /** @@ -227,34 +237,33 @@ QByteArray Json::serialize(const QVariant &data, bool &success) */ QVariant Json::parseValue(const QString &json, int &index, bool &success) { - //Determine what kind of data we should parse by - //checking out the upcoming token - switch(Json::lookAhead(json, index)) - { - case JsonTokenString: - return Json::parseString(json, index, success); - case JsonTokenNumber: - return Json::parseNumber(json, index); - case JsonTokenCurlyOpen: - return Json::parseObject(json, index, success); - case JsonTokenSquaredOpen: - return Json::parseArray(json, index, success); - case JsonTokenTrue: - Json::nextToken(json, index); - return QVariant(true); - case JsonTokenFalse: - Json::nextToken(json, index); - return QVariant(false); - case JsonTokenNull: - Json::nextToken(json, index); - return QVariant(); - case JsonTokenNone: - break; - } + // Determine what kind of data we should parse by + // checking out the upcoming token + switch (Json::lookAhead(json, index)) { + case JsonTokenString: + return Json::parseString(json, index, success); + case JsonTokenNumber: + return Json::parseNumber(json, index); + case JsonTokenCurlyOpen: + return Json::parseObject(json, index, success); + case JsonTokenSquaredOpen: + return Json::parseArray(json, index, success); + case JsonTokenTrue: + Json::nextToken(json, index); + return QVariant(true); + case JsonTokenFalse: + Json::nextToken(json, index); + return QVariant(false); + case JsonTokenNull: + Json::nextToken(json, index); + return QVariant(); + case JsonTokenNone: + break; + } - //If there were no tokens, flag the failure and return an empty QVariant - success = false; - return QVariant(); + // If there were no tokens, flag the failure and return an empty QVariant + success = false; + return QVariant(); } /** @@ -262,69 +271,58 @@ QVariant Json::parseValue(const QString &json, int &index, bool &success) */ QVariant Json::parseObject(const QString &json, int &index, bool &success) { - QVariantMap map; - int token; + QVariantMap map; + int token; - //Get rid of the whitespace and increment index - Json::nextToken(json, index); + // Get rid of the whitespace and increment index + Json::nextToken(json, index); - //Loop through all of the key/value pairs of the object - bool done = false; - while(!done) - { - //Get the upcoming token - token = Json::lookAhead(json, index); + // Loop through all of the key/value pairs of the object + bool done = false; + while (!done) { + // Get the upcoming token + token = Json::lookAhead(json, index); - if(token == JsonTokenNone) - { - success = false; - return QVariantMap(); - } - else if(token == JsonTokenComma) - { - Json::nextToken(json, index); - } - else if(token == JsonTokenCurlyClose) - { - Json::nextToken(json, index); - return map; - } - else - { - //Parse the key/value pair's name - QString name = Json::parseString(json, index, success).toString(); + if (token == JsonTokenNone) { + success = false; + return QVariantMap(); + } else if (token == JsonTokenComma) { + Json::nextToken(json, index); + } else if (token == JsonTokenCurlyClose) { + Json::nextToken(json, index); + return map; + } else { + // Parse the key/value pair's name + QString name = Json::parseString(json, index, success).toString(); - if(!success) - { - return QVariantMap(); - } + if (!success) { + return QVariantMap(); + } - //Get the next token - token = Json::nextToken(json, index); + // Get the next token + token = Json::nextToken(json, index); - //If the next token is not a colon, flag the failure - //return an empty QVariant - if(token != JsonTokenColon) - { - success = false; - return QVariant(QVariantMap()); - } + // If the next token is not a colon, flag the failure + // return an empty QVariant + if (token != JsonTokenColon) { + success = false; + return QVariant(QVariantMap()); + } - //Parse the key/value pair's value - QVariant value = Json::parseValue(json, index, success); + // Parse the key/value pair's value + QVariant value = Json::parseValue(json, index, success); - if(!success) - { - return QVariantMap(); - } + if (!success) { + return QVariantMap(); + } - //Assign the value to the key in the map - map[name] = value; - } + // Assign the value to the key in the map + map[name] = value; } + } - //Return the map successfully - return QVariant(map); + // Return the map successfully + return QVariant(map); } /** @@ -332,43 +330,34 @@ QVariant Json::parseObject(const QString &json, int &index, bool &success) */ QVariant Json::parseArray(const QString &json, int &index, bool &success) { - QVariantList list; + QVariantList list; - Json::nextToken(json, index); + Json::nextToken(json, index); - bool done = false; - while(!done) - { - int token = Json::lookAhead(json, index); + bool done = false; + while (!done) { + int token = Json::lookAhead(json, index); - if(token == JsonTokenNone) - { - success = false; - return QVariantList(); - } - else if(token == JsonTokenComma) - { - Json::nextToken(json, index); - } - else if(token == JsonTokenSquaredClose) - { - Json::nextToken(json, index); - break; - } - else - { - QVariant value = Json::parseValue(json, index, success); + if (token == JsonTokenNone) { + success = false; + return QVariantList(); + } else if (token == JsonTokenComma) { + Json::nextToken(json, index); + } else if (token == JsonTokenSquaredClose) { + Json::nextToken(json, index); + break; + } else { + QVariant value = Json::parseValue(json, index, success); - if(!success) - { - return QVariantList(); - } + if (!success) { + return QVariantList(); + } - list.push_back(value); - } + list.push_back(value); } + } - return QVariant(list); + return QVariant(list); } /** @@ -376,102 +365,73 @@ QVariant Json::parseArray(const QString &json, int &index, bool &success) */ QVariant Json::parseString(const QString &json, int &index, bool &success) { - QString s; - QChar c; + QString s; + QChar c; - Json::eatWhitespace(json, index); + Json::eatWhitespace(json, index); + + c = json[index++]; + + bool complete = false; + while (!complete) { + if (index == json.size()) { + break; + } c = json[index++]; - bool complete = false; - while(!complete) - { - if(index == json.size()) - { - break; - } - - c = json[index++]; - - if(c == '\"') - { - complete = true; - break; - } - else if(c == '\\') - { - if(index == json.size()) - { - break; - } - - c = json[index++]; - - if(c == '\"') - { - s.append('\"'); - } - else if(c == '\\') - { - s.append('\\'); - } - else if(c == '/') - { - s.append('/'); - } - else if(c == 'b') - { - s.append('\b'); - } - else if(c == 'f') - { - s.append('\f'); - } - else if(c == 'n') - { - s.append('\n'); - } - else if(c == 'r') - { - s.append('\r'); - } - else if(c == 't') - { - s.append('\t'); - } - else if(c == 'u') - { - int remainingLength = json.size() - index; - - if(remainingLength >= 4) - { - QString unicodeStr = json.mid(index, 4); - - int symbol = unicodeStr.toInt(0, 16); - - s.append(QChar(symbol)); - - index += 4; - } - else - { - break; - } - } - } - else - { - s.append(c); + if (c == '\"') { + complete = true; + break; + } else if (c == '\\') { + if (index == json.size()) { + break; + } + + c = json[index++]; + + if (c == '\"') { + s.append('\"'); + } else if (c == '\\') { + s.append('\\'); + } else if (c == '/') { + s.append('/'); + } else if (c == 'b') { + s.append('\b'); + } else if (c == 'f') { + s.append('\f'); + } else if (c == 'n') { + s.append('\n'); + } else if (c == 'r') { + s.append('\r'); + } else if (c == 't') { + s.append('\t'); + } else if (c == 'u') { + int remainingLength = json.size() - index; + + if (remainingLength >= 4) { + QString unicodeStr = json.mid(index, 4); + + int symbol = unicodeStr.toInt(0, 16); + + s.append(QChar(symbol)); + + index += 4; + } else { + break; } + } + } else { + s.append(c); } + } - if(!complete) - { - success = false; - return QVariant(); - } + if (!complete) { + success = false; + return QVariant(); + } - return QVariant(s); + return QVariant(s); } /** @@ -479,23 +439,23 @@ QVariant Json::parseString(const QString &json, int &index, bool &success) */ QVariant Json::parseNumber(const QString &json, int &index) { - Json::eatWhitespace(json, index); + Json::eatWhitespace(json, index); - int lastIndex = Json::lastIndexOfNumber(json, index); - int charLength = (lastIndex - index) + 1; - QString numberStr; + int lastIndex = Json::lastIndexOfNumber(json, index); + int charLength = (lastIndex - index) + 1; + QString numberStr; - numberStr = json.mid(index, charLength); + numberStr = json.mid(index, charLength); - index = lastIndex + 1; + index = lastIndex + 1; - if (numberStr.contains('.')) { - return QVariant(numberStr.toDouble(NULL)); - } else if (numberStr.startsWith('-')) { - return QVariant(numberStr.toLongLong(NULL)); - } else { - return QVariant(numberStr.toULongLong(NULL)); - } + if (numberStr.contains('.')) { + return QVariant(numberStr.toDouble(NULL)); + } else if (numberStr.startsWith('-')) { + return QVariant(numberStr.toLongLong(NULL)); + } else { + return QVariant(numberStr.toULongLong(NULL)); + } } /** @@ -503,18 +463,16 @@ QVariant Json::parseNumber(const QString &json, int &index) */ int Json::lastIndexOfNumber(const QString &json, int index) { - static const QString numericCharacters("0123456789+-.eE"); - int lastIndex; + static const QString numericCharacters("0123456789+-.eE"); + int lastIndex; - for(lastIndex = index; lastIndex < json.size(); lastIndex++) - { - if(numericCharacters.indexOf(json[lastIndex]) == -1) - { - break; - } + for (lastIndex = index; lastIndex < json.size(); lastIndex++) { + if (numericCharacters.indexOf(json[lastIndex]) == -1) { + break; } + } - return lastIndex -1; + return lastIndex - 1; } /** @@ -522,14 +480,12 @@ int Json::lastIndexOfNumber(const QString &json, int index) */ void Json::eatWhitespace(const QString &json, int &index) { - static const QString whitespaceChars(" \t\n\r"); - for(; index < json.size(); index++) - { - if(whitespaceChars.indexOf(json[index]) == -1) - { - break; - } + static const QString whitespaceChars(" \t\n\r"); + for (; index < json.size(); index++) { + if (whitespaceChars.indexOf(json[index]) == -1) { + break; } + } } /** @@ -537,8 +493,8 @@ void Json::eatWhitespace(const QString &json, int &index) */ int Json::lookAhead(const QString &json, int index) { - int saveIndex = index; - return Json::nextToken(json, saveIndex); + int saveIndex = index; + return Json::nextToken(json, saveIndex); } /** @@ -546,69 +502,73 @@ int Json::lookAhead(const QString &json, int index) */ int Json::nextToken(const QString &json, int &index) { - Json::eatWhitespace(json, index); - - if(index == json.size()) - { - return JsonTokenNone; - } - - QChar c = json[index]; - index++; - switch(c.toLatin1()) - { - case '{': return JsonTokenCurlyOpen; - case '}': return JsonTokenCurlyClose; - case '[': return JsonTokenSquaredOpen; - case ']': return JsonTokenSquaredClose; - case ',': return JsonTokenComma; - case '"': return JsonTokenString; - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - case '-': return JsonTokenNumber; - case ':': return JsonTokenColon; - } - - index--; - - int remainingLength = json.size() - index; - - //True - if(remainingLength >= 4) - { - if (json[index] == 't' && json[index + 1] == 'r' && - json[index + 2] == 'u' && json[index + 3] == 'e') - { - index += 4; - return JsonTokenTrue; - } - } - - //False - if (remainingLength >= 5) - { - if (json[index] == 'f' && json[index + 1] == 'a' && - json[index + 2] == 'l' && json[index + 3] == 's' && - json[index + 4] == 'e') - { - index += 5; - return JsonTokenFalse; - } - } - - //Null - if (remainingLength >= 4) - { - if (json[index] == 'n' && json[index + 1] == 'u' && - json[index + 2] == 'l' && json[index + 3] == 'l') - { - index += 4; - return JsonTokenNull; - } - } + Json::eatWhitespace(json, index); + if (index == json.size()) { return JsonTokenNone; + } + + QChar c = json[index]; + index++; + switch (c.toLatin1()) { + case '{': + return JsonTokenCurlyOpen; + case '}': + return JsonTokenCurlyClose; + case '[': + return JsonTokenSquaredOpen; + case ']': + return JsonTokenSquaredClose; + case ',': + return JsonTokenComma; + case '"': + return JsonTokenString; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + return JsonTokenNumber; + case ':': + return JsonTokenColon; + } + + index--; + + int remainingLength = json.size() - index; + + // True + if (remainingLength >= 4) { + if (json[index] == 't' && json[index + 1] == 'r' && json[index + 2] == 'u' && json[index + 3] == 'e') { + index += 4; + return JsonTokenTrue; + } + } + + // False + if (remainingLength >= 5) { + if (json[index] == 'f' && json[index + 1] == 'a' && json[index + 2] == 'l' && json[index + 3] == 's' && + json[index + 4] == 'e') { + index += 5; + return JsonTokenFalse; + } + } + + // Null + if (remainingLength >= 4) { + if (json[index] == 'n' && json[index + 1] == 'u' && json[index + 2] == 'l' && json[index + 3] == 'l') { + index += 4; + return JsonTokenNull; + } + } + + return JsonTokenNone; } - -} //end namespace +} // namespace QtJson diff --git a/oracle/src/zip/unzip.h b/oracle/src/zip/unzip.h old mode 100755 new mode 100644 index 7171c8b8f..ab57fdc36 --- a/oracle/src/zip/unzip.h +++ b/oracle/src/zip/unzip.h @@ -33,14 +33,12 @@ #include #include #include - #include class QDir; class QFile; class QIODevice; class QString; -class QStringList; OSDAB_BEGIN_NAMESPACE(Zip) @@ -49,100 +47,105 @@ class UnzipPrivate; class OSDAB_ZIP_EXPORT UnZip { public: - enum ErrorCode - { - Ok, - ZlibInit, - ZlibError, - OpenFailed, - PartiallyCorrupted, - Corrupted, - WrongPassword, - NoOpenArchive, - FileNotFound, - ReadFailed, - WriteFailed, - SeekFailed, - CreateDirFailed, - InvalidDevice, - InvalidArchive, - HeaderConsistencyError, + enum ErrorCode + { + Ok, + ZlibInit, + ZlibError, + OpenFailed, + PartiallyCorrupted, + Corrupted, + WrongPassword, + NoOpenArchive, + FileNotFound, + ReadFailed, + WriteFailed, + SeekFailed, + CreateDirFailed, + InvalidDevice, + InvalidArchive, + HeaderConsistencyError, - Skip, SkipAll // internal use only - }; + Skip, + SkipAll // internal use only + }; - enum ExtractionOption + enum ExtractionOption { ExtractPaths = 0x0001, SkipPaths = 0x0002, VerifyOnly = 0x0004, NoSilentDirectoryCreation = 0x0008 - }; - Q_DECLARE_FLAGS(ExtractionOptions, ExtractionOption) + }; + Q_DECLARE_FLAGS(ExtractionOptions, ExtractionOption) - enum CompressionMethod - { - NoCompression, Deflated, UnknownCompression - }; + enum CompressionMethod + { + NoCompression, + Deflated, + UnknownCompression + }; - enum FileType - { - File, Directory - }; + enum FileType + { + File, + Directory + }; - struct ZipEntry - { - ZipEntry(); + struct ZipEntry + { + ZipEntry(); - QString filename; - QString comment; + QString filename; + QString comment; - quint32 compressedSize; - quint32 uncompressedSize; - quint32 crc32; + quint32 compressedSize; + quint32 uncompressedSize; + quint32 crc32; - QDateTime lastModified; + QDateTime lastModified; - CompressionMethod compression; - FileType type; + CompressionMethod compression; + FileType type; - bool encrypted; - }; + bool encrypted; + }; - UnZip(); - virtual ~UnZip(); + UnZip(); + virtual ~UnZip(); - bool isOpen() const; + bool isOpen() const; - ErrorCode openArchive(const QString& filename); - ErrorCode openArchive(QIODevice* device); - void closeArchive(); + ErrorCode openArchive(const QString &filename); + ErrorCode openArchive(QIODevice *device); + void closeArchive(); - QString archiveComment() const; + QString archiveComment() const; - QString formatError(UnZip::ErrorCode c) const; + QString formatError(UnZip::ErrorCode c) const; - bool contains(const QString& file) const; + bool contains(const QString &file) const; - QStringList fileList() const; - QList entryList() const; + QStringList fileList() const; + QList entryList() const; ErrorCode verifyArchive(); - ErrorCode extractAll(const QString& dirname, ExtractionOptions options = ExtractPaths); - ErrorCode extractAll(const QDir& dir, ExtractionOptions options = ExtractPaths); + ErrorCode extractAll(const QString &dirname, ExtractionOptions options = ExtractPaths); + ErrorCode extractAll(const QDir &dir, ExtractionOptions options = ExtractPaths); - ErrorCode extractFile(const QString& filename, const QString& dirname, ExtractionOptions options = ExtractPaths); - ErrorCode extractFile(const QString& filename, const QDir& dir, ExtractionOptions options = ExtractPaths); - ErrorCode extractFile(const QString& filename, QIODevice* device, ExtractionOptions options = ExtractPaths); + ErrorCode extractFile(const QString &filename, const QString &dirname, ExtractionOptions options = ExtractPaths); + ErrorCode extractFile(const QString &filename, const QDir &dir, ExtractionOptions options = ExtractPaths); + ErrorCode extractFile(const QString &filename, QIODevice *device, ExtractionOptions options = ExtractPaths); - ErrorCode extractFiles(const QStringList& filenames, const QString& dirname, ExtractionOptions options = ExtractPaths); - ErrorCode extractFiles(const QStringList& filenames, const QDir& dir, ExtractionOptions options = ExtractPaths); + ErrorCode + extractFiles(const QStringList &filenames, const QString &dirname, ExtractionOptions options = ExtractPaths); + ErrorCode extractFiles(const QStringList &filenames, const QDir &dir, ExtractionOptions options = ExtractPaths); - void setPassword(const QString& pwd); + void setPassword(const QString &pwd); private: - UnzipPrivate* d; + UnzipPrivate *d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(UnZip::ExtractionOptions) diff --git a/oracle/src/zip/zipglobal.cpp b/oracle/src/zip/zipglobal.cpp old mode 100755 new mode 100644 index aed1ee0e7..4a855ed81 --- a/oracle/src/zip/zipglobal.cpp +++ b/oracle/src/zip/zipglobal.cpp @@ -27,7 +27,7 @@ #include "zipglobal.h" -#if defined(Q_OS_WIN) || defined(Q_OS_WINCE) || defined(Q_OS_LINUX) || defined (Q_OS_MACX) +#if defined(Q_OS_WIN) || defined(Q_OS_WINCE) || defined(Q_OS_LINUX) || defined(Q_OS_MACX) #define OSDAB_ZIP_HAS_UTC #include #else @@ -55,9 +55,9 @@ int OSDAB_ZIP_MANGLE(currentUtcOffset)() #if defined Q_OS_WIN struct tm _tm_struct; - struct tm* tm_struct = &_tm_struct; + struct tm *tm_struct = &_tm_struct; #else - struct tm* tm_struct = 0; + struct tm *tm_struct = 0; #endif #if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) @@ -92,14 +92,14 @@ int OSDAB_ZIP_MANGLE(currentUtcOffset)() const time_t local_time_t = mktime(tm_struct); - const int utcOffset = - qRound(difftime(global_time_t, local_time_t)); + const int utcOffset = -qRound(difftime(global_time_t, local_time_t)); return tm_struct->tm_isdst > 0 ? utcOffset + 3600 : utcOffset; #endif // No UTC return 0; } -QDateTime OSDAB_ZIP_MANGLE(fromFileTimestamp)(const QDateTime& dateTime) +QDateTime OSDAB_ZIP_MANGLE(fromFileTimestamp)(const QDateTime &dateTime) { #if !defined OSDAB_ZIP_NO_UTC && defined OSDAB_ZIP_HAS_UTC const int utc = OSDAB_ZIP_MANGLE(currentUtcOffset)(); @@ -109,14 +109,14 @@ QDateTime OSDAB_ZIP_MANGLE(fromFileTimestamp)(const QDateTime& dateTime) #endif // OSDAB_ZIP_NO_UTC } -bool OSDAB_ZIP_MANGLE(setFileTimestamp)(const QString& fileName, const QDateTime& dateTime) +bool OSDAB_ZIP_MANGLE(setFileTimestamp)(const QString &fileName, const QDateTime &dateTime) { if (fileName.isEmpty()) return true; #ifdef Q_OS_WIN - HANDLE hFile = CreateFileW(fileName.toStdWString().c_str(), - GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); + HANDLE hFile = + CreateFileW(fileName.toStdWString().c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); if (hFile == INVALID_HANDLE_VALUE) { return false; } @@ -143,7 +143,7 @@ bool OSDAB_ZIP_MANGLE(setFileTimestamp)(const QString& fileName, const QDateTime #elif defined(Q_OS_LINUX) || defined(Q_OS_MACX) struct utimbuf t_buffer; - t_buffer.actime = t_buffer.modtime = dateTime.toTime_t(); + t_buffer.actime = t_buffer.modtime = dateTime.toSecsSinceEpoch(); return utime(fileName.toLocal8Bit().constData(), &t_buffer) == 0; #endif diff --git a/servatrice/CMakeLists.txt b/servatrice/CMakeLists.txt index 569958674..99ce2f8c0 100644 --- a/servatrice/CMakeLists.txt +++ b/servatrice/CMakeLists.txt @@ -41,11 +41,12 @@ if(APPLE) set(servatrice_SOURCES ${servatrice_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns) ENDIF(APPLE) -# Qt5 -find_package(Qt5 COMPONENTS Network Sql WebSockets REQUIRED) -set(SERVATRICE_QT_MODULES Qt5::Core Qt5::Network Qt5::Sql Qt5::WebSockets) +IF(Qt6_FOUND) + Qt6_ADD_RESOURCES(servatrice_RESOURCES_RCC ${servatrice_RESOURCES}) +ELSEIF(Qt5_FOUND) + Qt5_ADD_RESOURCES(servatrice_RESOURCES_RCC ${servatrice_RESOURCES}) +ENDIF() -QT5_ADD_RESOURCES(servatrice_RESOURCES_RCC ${servatrice_RESOURCES}) SET(QT_DONT_USE_QTGUI TRUE) # Mysql connector @@ -59,15 +60,15 @@ elseif(WIN32) SET(MYSQLCLIENT_DEFAULT_PATHS "C:\\Program Files\\MySQL\\MySQL Server 5.7\\lib" "C:\\Program Files (x86)\\MySQL\\MySQL Server 5.7\\lib") endif() -find_library(MYSQLCLIENT_LIBRARIES NAMES mysqlclient PATHS ${MYSQLCLIENT_DEFAULT_PATHS} PATH_SUFFIXES mysql mariadb) -if(${MYSQLCLIENT_LIBRARIES} MATCHES "NOTFOUND") +find_library(MYSQL_CLIENT_LIBRARIES NAMES mysqlclient PATHS ${MYSQLCLIENT_DEFAULT_PATHS} PATH_SUFFIXES mysql mariadb) +if(${MYSQL_CLIENT_LIBRARIES} MATCHES "NOTFOUND") set(MYSQLCLIENT_FOUND FALSE CACHE INTERNAL "") - MESSAGE(STATUS "Mysql connector NOT FOUND: servatrice won't be able to connect to a mysql server") - unset(MYSQLCLIENT_LIBRARIES) + MESSAGE(STATUS "MySQL connector NOT FOUND: Servatrice won't be able to connect to a MySQL server") + unset(MYSQL_CLIENT_LIBRARIES) else() set(MYSQLCLIENT_FOUND TRUE CACHE INTERNAL "") - get_filename_component(MYSQLCLIENT_LIBRARY_DIR ${MYSQLCLIENT_LIBRARIES} PATH) - MESSAGE(STATUS "Mysql connector found at: ${MYSQLCLIENT_LIBRARY_DIR}") + get_filename_component(MYSQLCLIENT_LIBRARY_DIR ${MYSQL_CLIENT_LIBRARIES} PATH) + MESSAGE(STATUS "Found MySQL connector at: ${MYSQL_CLIENT_LIBRARIES}") endif() # Declare path variables @@ -83,9 +84,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) # Build servatrice binary and link it ADD_EXECUTABLE(servatrice MACOSX_BUNDLE ${servatrice_SOURCES} ${servatrice_RESOURCES_RCC} ${servatrice_MOC_SRCS}) -if(MSVC) - TARGET_LINK_LIBRARIES(servatrice cockatrice_common Threads::Threads ${SERVATRICE_QT_MODULES} Qt5::WinMain) -elseif(CMAKE_HOST_SYSTEM MATCHES "FreeBSD") +if(CMAKE_HOST_SYSTEM MATCHES "FreeBSD") TARGET_LINK_LIBRARIES(servatrice cockatrice_common Threads::Threads ${SERVATRICE_QT_MODULES} ${LIBEXECINFO_LIBRARY}) else() TARGET_LINK_LIBRARIES(servatrice cockatrice_common Threads::Threads ${SERVATRICE_QT_MODULES}) @@ -124,15 +123,15 @@ if(APPLE) # these needs to be relative to CMAKE_INSTALL_PREFIX set(plugin_dest_dir servatrice.app/Contents/Plugins) set(qtconf_dest_dir servatrice.app/Contents/Resources) - get_filename_component(QT_LIBRARY_DIR "${QT_LIBRARY_DIR}/.." ABSOLUTE) - # qt5 plugins: platforms, sqldrivers/mysql + # Qt plugins: platforms, sqldrivers/mysql, tls (Qt6) install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime FILES_MATCHING PATTERN "*.dSYM" EXCLUDE PATTERN "*_debug.dylib" EXCLUDE PATTERN "platforms/*.dylib" PATTERN "sqldrivers/libqsqlmysql*.dylib" + PATTERN "tls/*.dylib" ) install(CODE " @@ -157,10 +156,18 @@ if(WIN32) install(DIRECTORY "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${CMAKE_BUILD_TYPE}/" DESTINATION ./ FILES_MATCHING PATTERN "*.dll") - # qt5 plugins: platforms, sqldrivers/mysql - install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime - FILES_MATCHING REGEX "(platforms/.*|sqldrivers/qsqlmysql)\\.dll" - REGEX ".*d\\.dll" EXCLUDE) + # Qt plugins: platforms, sqldrivers, tls (Qt6) + install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime FILES_MATCHING + PATTERN "platforms/qdirect2d.dll" + PATTERN "platforms/qminimal.dll" + PATTERN "platforms/qoffscreen.dll" + PATTERN "platforms/qwindows.dll" + PATTERN "tls/qcertonlybackend.dll" + PATTERN "tls/qopensslbackend.dll" + PATTERN "tls/qschannelbackend.dll" + PATTERN "sqldrivers/qsqlite.dll" + PATTERN "sqldrivers/qsqlodbc.dll" + PATTERN "sqldrivers/qsqlpsql.dll") install(CODE " file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths] diff --git a/servatrice/src/main.cpp b/servatrice/src/main.cpp index de34755a2..63e85fa18 100644 --- a/servatrice/src/main.cpp +++ b/servatrice/src/main.cpp @@ -34,7 +34,6 @@ #include #include #include -#include #include RNG_Abstract *rng; @@ -70,17 +69,17 @@ void testRNG() } for (int i = 0; i <= maxMax - min; ++i) { std::cerr << (min + i); - for (int j = 0; j < numbers.size(); ++j) { - if (i < numbers[j].size()) - std::cerr << "\t" << numbers[j][i]; + for (auto &number : numbers) { + if (i < number.size()) + std::cerr << "\t" << number[i]; else std::cerr << "\t"; } std::cerr << std::endl; } std::cerr << std::endl << "Chi^2 ="; - for (int j = 0; j < chisq.size(); ++j) - std::cerr << "\t" << QString::number(chisq[j], 'f', 3).toStdString(); + for (double j : chisq) + std::cerr << "\t" << QString::number(j, 'f', 3).toStdString(); std::cerr << std::endl << "k ="; for (int j = 0; j < chisq.size(); ++j) std::cerr << "\t" << (j - min + minMax); @@ -112,9 +111,9 @@ void myMessageOutput2(QtMsgType /*type*/, const QMessageLogContext &, const QStr int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); - app.setOrganizationName("Cockatrice"); - app.setApplicationName("Servatrice"); - app.setApplicationVersion(VERSION_STRING); + QCoreApplication::setOrganizationName("Cockatrice"); + QCoreApplication::setApplicationName("Servatrice"); + QCoreApplication::setApplicationVersion(VERSION_STRING); QCommandLineParser parser; parser.addHelpOption(); @@ -183,7 +182,7 @@ int main(int argc, char *argv[]) smtpClient = new SmtpClient(); - Servatrice *server = new Servatrice(); + auto *server = new Servatrice(); QObject::connect(server, SIGNAL(destroyed()), &app, SLOT(quit()), Qt::QueuedConnection); int retval = 0; if (server->initServer()) { @@ -192,7 +191,7 @@ int main(int argc, char *argv[]) qInstallMessageHandler(myMessageOutput); - retval = app.exec(); + retval = QCoreApplication::exec(); std::cerr << "Server quit." << std::endl; std::cerr << "-------------------------" << std::endl; @@ -210,5 +209,6 @@ int main(int argc, char *argv[]) // Delete all global objects allocated by libprotobuf. google::protobuf::ShutdownProtobufLibrary(); + QCoreApplication::quit(); return retval; } diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index 48b438481..4e16ff244 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -76,6 +76,7 @@ Servatrice_GameServer::~Servatrice_GameServer() QThread *poolThread = connectionPools[i]->thread(); connectionPools[i]->deleteLater(); // pool destructor calls thread()->quit() poolThread->wait(); + poolThread->deleteLater(); } } @@ -84,6 +85,7 @@ void Servatrice_GameServer::incomingConnection(qintptr socketDescriptor) Servatrice_ConnectionPool *pool = findLeastUsedConnectionPool(); auto ssi = new TcpServerSocketInterface(server, pool->getDatabaseInterface()); + connect(ssi, SIGNAL(incTxBytes), this, SLOT(incTxBytes)); ssi->moveToThread(pool->thread()); pool->addClient(); connect(ssi, SIGNAL(destroyed()), pool, SLOT(removeClient())); @@ -144,6 +146,7 @@ Servatrice_WebsocketGameServer::~Servatrice_WebsocketGameServer() QThread *poolThread = connectionPools[i]->thread(); connectionPools[i]->deleteLater(); // pool destructor calls thread()->quit() poolThread->wait(); + poolThread->deleteLater(); } } @@ -152,6 +155,7 @@ void Servatrice_WebsocketGameServer::onNewConnection() Servatrice_ConnectionPool *pool = findLeastUsedConnectionPool(); auto ssi = new WebsocketServerSocketInterface(server, pool->getDatabaseInterface()); + connect(ssi, SIGNAL(incTxBytes), this, SLOT(incTxBytes)); /* * Due to a Qt limitation, websockets can't be moved to another thread. * This will hopefully change in Qt6 if QtWebSocket will be integrated in QtNetwork @@ -195,7 +199,7 @@ void Servatrice_IslServer::incomingConnection(qintptr socketDescriptor) Servatrice::Servatrice(QObject *parent) : Server(parent), authenticationMethod(AuthenticationNone), uptime(0), txBytes(0), rxBytes(0), - shutdownTimer(nullptr), isFirstShutdownMessage(true) + shutdownTimer(nullptr) { qRegisterMetaType("QSqlDatabase"); } @@ -204,21 +208,16 @@ Servatrice::~Servatrice() { gameServer->close(); - // clients live in other threads, we need to lock them - clientsLock.lockForRead(); + // we are destroying the clients outside their thread! for (auto *client : clients) { - QMetaObject::invokeMethod(client, "prepareDestroy", Qt::QueuedConnection); - } - clientsLock.unlock(); - - // client destruction is asynchronous, wait for all clients to be gone - for (;;) { - QThread::usleep(10); - QReadLocker locker(&clientsLock); - if (clients.isEmpty()) - break; + client->prepareDestroy(); } + if (shutdownTimer) { + shutdownTimer->deleteLater(); + } + + servatriceDatabaseInterface->deleteLater(); prepareDestroy(); } @@ -559,7 +558,7 @@ void Servatrice::updateLoginMessage() } } -void Servatrice::setRequiredFeatures(const QString featureList) +void Servatrice::setRequiredFeatures(const QString &featureList) { FeatureSet features; serverRequiredFeatureList.clear(); @@ -570,8 +569,9 @@ void Servatrice::setRequiredFeatures(const QString featureList) QStringList listReqFeatures = featureList.split(",", QString::SkipEmptyParts); #endif if (!listReqFeatures.isEmpty()) - foreach (QString reqFeature, listReqFeatures) + for (const QString &reqFeature : listReqFeatures) { features.enableRequiredFeature(serverRequiredFeatureList, reqFeature); + } qDebug() << "Set required client features to:" << serverRequiredFeatureList; } @@ -715,8 +715,9 @@ void Servatrice::shutdownTimeout() clientsLock.unlock(); delete se; - if (!shutdownMinutes) + if (!shutdownMinutes) { deleteLater(); + } } shutdownMinutes--; } diff --git a/servatrice/src/servatrice.h b/servatrice/src/servatrice.h index 8456bf7d7..d21410560 100644 --- a/servatrice/src/servatrice.h +++ b/servatrice/src/servatrice.h @@ -96,9 +96,9 @@ private: public: Servatrice_IslServer(Servatrice *_server, const QSslCertificate &_cert, - const QSslKey &_privateKey, + QSslKey _privateKey, QObject *parent = nullptr) - : QTcpServer(parent), server(_server), cert(_cert), privateKey(_privateKey) + : QTcpServer(parent), server(_server), cert(_cert), privateKey(std::move(_privateKey)) { } @@ -143,7 +143,7 @@ private slots: void shutdownTimeout(); protected: - void doSendIslMessage(const IslMessage &msg, int serverId) override; + void doSendIslMessage(const IslMessage &msg, int islServerId) override; private: enum DatabaseType @@ -160,7 +160,6 @@ private: mutable QMutex loginMessageMutex; QString loginMessage; QString dbPrefix; - QString requiredFeatures; QMap serverRequiredFeatureList; QString officialWarnings; Servatrice_DatabaseInterface *servatriceDatabaseInterface; @@ -173,7 +172,6 @@ private: int shutdownMinutes; int nextShutdownMessageMinutes; QTimer *shutdownTimer; - bool isFirstShutdownMessage; mutable QMutex serverListMutex; QList serverList; @@ -203,7 +201,7 @@ private: public slots: void scheduleShutdown(const QString &reason, int minutes); void updateLoginMessage(); - void setRequiredFeatures(QString featureList); + void setRequiredFeatures(const QString &featureList); public: explicit Servatrice(QObject *parent = nullptr); @@ -213,10 +211,6 @@ public: { return serverRequiredFeatureList; } - QString getOfficialWarningsList() const - { - return officialWarnings; - } QString getServerName() const; QString getLoginMessage() const override { @@ -282,9 +276,9 @@ public: void incRxBytes(quint64 num); void addDatabaseInterface(QThread *thread, Servatrice_DatabaseInterface *databaseInterface); - bool islConnectionExists(int serverId) const; - void addIslInterface(int serverId, IslInterface *interface); - void removeIslInterface(int serverId); + bool islConnectionExists(int islServerId) const; + void addIslInterface(int islServerId, IslInterface *interface); + void removeIslInterface(int islServerId); QReadWriteLock islLock; QList getServerList() const; diff --git a/servatrice/src/servatrice_connection_pool.h b/servatrice/src/servatrice_connection_pool.h index 2814089e2..7479122cf 100644 --- a/servatrice/src/servatrice_connection_pool.h +++ b/servatrice/src/servatrice_connection_pool.h @@ -17,8 +17,8 @@ private: int clientCount; public: - Servatrice_ConnectionPool(Servatrice_DatabaseInterface *_databaseInterface); - ~Servatrice_ConnectionPool(); + explicit Servatrice_ConnectionPool(Servatrice_DatabaseInterface *_databaseInterface); + ~Servatrice_ConnectionPool() override; Servatrice_DatabaseInterface *getDatabaseInterface() const { diff --git a/servatrice/src/servatrice_database_interface.cpp b/servatrice/src/servatrice_database_interface.cpp index 4e261da8a..1c691e9b8 100644 --- a/servatrice/src/servatrice_database_interface.cpp +++ b/servatrice/src/servatrice_database_interface.cpp @@ -113,12 +113,13 @@ bool Servatrice_DatabaseInterface::checkSql() QSqlQuery *Servatrice_DatabaseInterface::prepareQuery(const QString &queryText) { - if (preparedStatements.contains(queryText)) + if (preparedStatements.contains(queryText)) { return preparedStatements.value(queryText); + } QString prefixedQueryText = queryText; prefixedQueryText.replace("{prefix}", server->getDbPrefix()); - QSqlQuery *query = new QSqlQuery(sqlDatabase); + auto *query = new QSqlQuery(sqlDatabase); query->prepare(prefixedQueryText); preparedStatements.insert(queryText, query); diff --git a/servatrice/src/servatrice_database_interface.h b/servatrice/src/servatrice_database_interface.h index c867b8075..f4bbf5dfc 100644 --- a/servatrice/src/servatrice_database_interface.h +++ b/servatrice/src/servatrice_database_interface.h @@ -36,14 +36,14 @@ protected: const QString &clientId, QString &reasonStr, int &banSecondsLeft, - bool passwordNeedsHash); + bool passwordNeedsHash) override; public slots: void initDatabase(const QSqlDatabase &_sqlDatabase); public: - Servatrice_DatabaseInterface(int _instanceId, Servatrice *_server); - ~Servatrice_DatabaseInterface(); + explicit Servatrice_DatabaseInterface(int _instanceId, Servatrice *_server); + ~Servatrice_DatabaseInterface() override; bool initDatabase(const QString &type, const QString &hostName, const QString &databaseName, @@ -58,66 +58,66 @@ public: return sqlDatabase; } - bool activeUserExists(const QString &user); - bool userExists(const QString &user); - QString getUserSalt(const QString &user); + bool activeUserExists(const QString &user) override; + bool userExists(const QString &user) override; + QString getUserSalt(const QString &user) override; int getUserIdInDB(const QString &name); - QMap getBuddyList(const QString &name); - QMap getIgnoreList(const QString &name); - bool isInBuddyList(const QString &whoseList, const QString &who); - bool isInIgnoreList(const QString &whoseList, const QString &who); - ServerInfo_User getUserData(const QString &name, bool withId = false); + QMap getBuddyList(const QString &name) override; + QMap getIgnoreList(const QString &name) override; + bool isInBuddyList(const QString &whoseList, const QString &who) override; + bool isInIgnoreList(const QString &whoseList, const QString &who) override; + ServerInfo_User getUserData(const QString &name, bool withId = false) override; void storeGameInformation(const QString &roomName, const QStringList &roomGameTypes, const ServerInfo_Game &gameInfo, const QSet &allPlayersEver, const QSet &allSpectatorsEver, - const QList &replayList); - DeckList *getDeckFromDatabase(int deckId, int userId); + const QList &replayList) override; + DeckList *getDeckFromDatabase(int deckId, int userId) override; - int getNextGameId(); - int getNextReplayId(); - int getActiveUserCount(QString connectionType = QString()); + int getNextGameId() override; + int getNextReplayId() override; + int getActiveUserCount(QString connectionType = QString()) override; qint64 startSession(const QString &userName, const QString &address, const QString &clientId, - const QString &connectionType); - void endSession(qint64 sessionId); - void clearSessionTables(); - void lockSessionTables(); - void unlockSessionTables(); - bool userSessionExists(const QString &userName); - bool usernameIsValid(const QString &user, QString &error); + const QString &connectionType) override; + void endSession(qint64 sessionId) override; + void clearSessionTables() override; + void lockSessionTables() override; + void unlockSessionTables() override; + bool userSessionExists(const QString &userName) override; + bool usernameIsValid(const QString &user, QString &error) override; bool checkUserIsBanned(const QString &ipAddress, const QString &userName, const QString &clientId, QString &banReason, - int &banSecondsRemaining); - int checkNumberOfUserAccounts(const QString &email); + int &banSecondsRemaining) override; + int checkNumberOfUserAccounts(const QString &email) override; bool registerUser(const QString &userName, const QString &realName, const QString &password, bool passwordNeedsHash, const QString &emailAddress, const QString &country, - bool active = false); - bool activateUser(const QString &userName, const QString &token); - void updateUsersClientID(const QString &userName, const QString &userClientID); - void updateUsersLastLoginData(const QString &userName, const QString &clientVersion); + bool active = false) override; + bool activateUser(const QString &userName, const QString &token) override; + void updateUsersClientID(const QString &userName, const QString &userClientID) override; + void updateUsersLastLoginData(const QString &userName, const QString &clientVersion) override; void logMessage(const int senderId, const QString &senderName, const QString &senderIp, const QString &logMessage, LogMessage_TargetType targetType, const int targetId, - const QString &targetName); - bool changeUserPassword(const QString &user, const QString &password, bool passwordNeedsHash); + const QString &targetName) override; + bool changeUserPassword(const QString &user, const QString &password, bool passwordNeedsHash) override; bool changeUserPassword(const QString &user, const QString &oldPassword, bool oldPasswordNeedsHash, const QString &newPassword, - bool newPasswordNeedsHash); + bool newPasswordNeedsHash) override; QList getUserBanHistory(const QString userName); bool addWarning(const QString userName, const QString adminName, const QString warningReason, const QString clientID); @@ -133,7 +133,7 @@ public: int &range, int &maxresults); bool addForgotPassword(const QString &user); - bool removeForgotPassword(const QString &user); + bool removeForgotPassword(const QString &user) override; bool doesForgotPasswordExist(const QString &user); bool updateUserToken(const QString &token, const QString &user); bool validateTableColumnStringData(const QString &table, diff --git a/servatrice/src/server_logger.cpp b/servatrice/src/server_logger.cpp index 31553cadd..4453757b6 100644 --- a/servatrice/src/server_logger.cpp +++ b/servatrice/src/server_logger.cpp @@ -45,7 +45,7 @@ void ServerLogger::startLog(const QString &logFileName) connect(this, SIGNAL(sigFlushBuffer()), this, SLOT(flushBuffer()), Qt::QueuedConnection); } -void ServerLogger::logMessage(QString message, void *caller) +void ServerLogger::logMessage(const QString &message, void *caller) { if (!logFile) return; diff --git a/servatrice/src/server_logger.h b/servatrice/src/server_logger.h index 0e11d23fb..0ad092b66 100644 --- a/servatrice/src/server_logger.h +++ b/servatrice/src/server_logger.h @@ -18,7 +18,7 @@ public: ~ServerLogger(); public slots: void startLog(const QString &logFileName); - void logMessage(QString message, void *caller = 0); + void logMessage(const QString &message, void *caller = 0); void rotateLogs(); private slots: void flushBuffer(); diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index ce1770fb5..ee6fa6c89 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -74,7 +74,6 @@ #include #include #include -#include #include #include @@ -388,7 +387,7 @@ bool AbstractServerSocketInterface::deckListHelper(int folderId, ServerInfo_Deck newItem->set_name(query->value(1).toString().toStdString()); ServerInfo_DeckStorage_File *newFile = newItem->mutable_file(); - newFile->set_creation_time(query->value(2).toDateTime().toTime_t()); + newFile->set_creation_time(query->value(2).toDateTime().toSecsSinceEpoch()); } return true; @@ -551,7 +550,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdDeckUpload(const Comman ServerInfo_DeckStorage_TreeItem *fileInfo = re->mutable_new_file(); fileInfo->set_id(query->lastInsertId().toInt()); fileInfo->set_name(deckName.toStdString()); - fileInfo->mutable_file()->set_creation_time(QDateTime::currentDateTime().toTime_t()); + fileInfo->mutable_file()->set_creation_time(QDateTime::currentDateTime().toSecsSinceEpoch()); rc.setResponseExtension(re); } else if (cmd.has_deck_id()) { QSqlQuery *query = @@ -570,7 +569,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdDeckUpload(const Comman ServerInfo_DeckStorage_TreeItem *fileInfo = re->mutable_new_file(); fileInfo->set_id(cmd.deck_id()); fileInfo->set_name(deckName.toStdString()); - fileInfo->mutable_file()->set_creation_time(QDateTime::currentDateTime().toTime_t()); + fileInfo->mutable_file()->set_creation_time(QDateTime::currentDateTime().toSecsSinceEpoch()); rc.setResponseExtension(re); } else return Response::RespInvalidData; @@ -619,8 +618,8 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReplayList(const Comman const int gameId = query1->value(0).toInt(); matchInfo->set_game_id(gameId); matchInfo->set_room_name(query1->value(2).toString().toStdString()); - const int timeStarted = query1->value(3).toDateTime().toTime_t(); - const int timeFinished = query1->value(4).toDateTime().toTime_t(); + const int timeStarted = query1->value(3).toDateTime().toSecsSinceEpoch(); + const int timeFinished = query1->value(4).toDateTime().toSecsSinceEpoch(); matchInfo->set_time_started(timeStarted); matchInfo->set_length(timeFinished - timeStarted); matchInfo->set_game_name(query1->value(5).toString().toStdString()); @@ -977,7 +976,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdBanFromServer(const Com if (cmd.has_visible_reason()) event.set_reason_str(visibleReason.toStdString()); if (minutes) - event.set_end_time(QDateTime::currentDateTime().addSecs(60 * minutes).toTime_t()); + event.set_end_time(QDateTime::currentDateTime().addSecs(60 * minutes).toSecsSinceEpoch()); for (int i = 0; i < userList.size(); ++i) { SessionEvent *se = userList[i]->prepareSessionEvent(event); userList[i]->sendProtocolItem(*se); @@ -1148,7 +1147,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdRegisterAccount(const C Response_Register *re = new Response_Register; re->set_denied_reason_str(banReason.toStdString()); if (banSecondsRemaining != 0) - re->set_denied_end_time(QDateTime::currentDateTime().addSecs(banSecondsRemaining).toTime_t()); + re->set_denied_end_time(QDateTime::currentDateTime().addSecs(banSecondsRemaining).toSecsSinceEpoch()); rc.setResponseExtension(re); return Response::RespUserIsBanned; } @@ -1649,7 +1648,7 @@ bool AbstractServerSocketInterface::removeAdminFlagFromUser(const QString &userN Event_ConnectionClosed event; event.set_reason(Event_ConnectionClosed::DEMOTED); event.set_reason_str("Your moderator and/or judge status has been revoked."); - event.set_end_time(QDateTime::currentDateTime().toTime_t()); + event.set_end_time(QDateTime::currentDateTime().toSecsSinceEpoch()); SessionEvent *se = user->prepareSessionEvent(event); user->sendProtocolItem(*se); @@ -1765,7 +1764,7 @@ void TcpServerSocketInterface::flushOutputQueue() locker.relock(); } locker.unlock(); - servatrice->incTxBytes(totalBytes); + emit incTxBytes(totalBytes); // see above wrt mutex flushSocket(); } @@ -1859,7 +1858,7 @@ bool TcpServerSocketInterface::initTcpSession() WebsocketServerSocketInterface::WebsocketServerSocketInterface(Servatrice *_server, Servatrice_DatabaseInterface *_databaseInterface, QObject *parent) - : AbstractServerSocketInterface(_server, _databaseInterface, parent) + : AbstractServerSocketInterface(_server, _databaseInterface, parent), socket(nullptr) { } @@ -1885,17 +1884,12 @@ void WebsocketServerSocketInterface::initConnection(void *_socket) address = socket->peerAddress(); QByteArray websocketIPHeader = settingsCache->value("server/web_socket_ip_header", "").toByteArray(); - if (websocketIPHeader.length() > 0) { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) - if (socket->request().hasRawHeader(websocketIPHeader)) { - QString header(socket->request().rawHeader(websocketIPHeader)); - QHostAddress parsed(header); - if (!parsed.isNull()) - address = parsed; + if (websocketIPHeader.length() > 0 && socket->request().hasRawHeader(websocketIPHeader)) { + QString header(socket->request().rawHeader(websocketIPHeader)); + QHostAddress parsed(header); + if (!parsed.isNull()) { + address = parsed; } -#else - logger->logMessage(QString("Reading the websocket IP header is unsupported on this version of QT.")); -#endif } connect(socket, SIGNAL(binaryMessageReceived(const QByteArray &)), this, @@ -1905,7 +1899,7 @@ void WebsocketServerSocketInterface::initConnection(void *_socket) connect(socket, SIGNAL(disconnected()), this, SLOT(catchSocketDisconnected())); // Add this object to the server's list of connections before it can receive socket events. - // Otherwise, in case a of a socket error, it could be removed from the list before it is added. + // Otherwise, in case of a socket error, it could be removed from the list before it is added. server->addClient(this); logger->logMessage( @@ -1949,7 +1943,7 @@ void WebsocketServerSocketInterface::flushOutputQueue() if (outputQueue.isEmpty()) return; - int totalBytes = 0; + qint64 totalBytes = 0; while (!outputQueue.isEmpty()) { ServerMessage item = outputQueue.takeFirst(); locker.unlock(); @@ -1969,7 +1963,7 @@ void WebsocketServerSocketInterface::flushOutputQueue() locker.relock(); } locker.unlock(); - servatrice->incTxBytes(totalBytes); + emit incTxBytes(totalBytes); // see above wrt mutex flushSocket(); } diff --git a/servatrice/src/serversocketinterface.h b/servatrice/src/serversocketinterface.h index 55d452eac..b2ee2e279 100644 --- a/servatrice/src/serversocketinterface.h +++ b/servatrice/src/serversocketinterface.h @@ -63,6 +63,7 @@ protected slots: virtual void flushOutputQueue() = 0; signals: void outputQueueChanged(); + void incTxBytes(qint64 amount); protected: void logDebugMessage(const QString &message); diff --git a/servatrice/src/settingscache.cpp b/servatrice/src/settingscache.cpp index 8c33cdba6..e606cc2ce 100644 --- a/servatrice/src/settingscache.cpp +++ b/servatrice/src/settingscache.cpp @@ -45,6 +45,6 @@ QString SettingsCache::guessConfigurationPath() return guessFileName; #endif - guessFileName = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + fileName; + guessFileName = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/" + fileName; return guessFileName; } diff --git a/servatrice/src/smtp/qxtmail_p.h b/servatrice/src/smtp/qxtmail_p.h index a9a9dc901..96d22c0d3 100644 --- a/servatrice/src/smtp/qxtmail_p.h +++ b/servatrice/src/smtp/qxtmail_p.h @@ -26,9 +26,12 @@ #define QXTMAIL_P_H #include +#include #define QXT_MUST_QP(x) (x < char(32) || x > char(126) || x == '=' || x == '?') -QByteArray qxt_fold_mime_header(const QString& key, const QString& value, QTextCodec* latin1, - const QByteArray& prefix = QByteArray()); +QByteArray qxt_fold_mime_header(const QString &key, + const QString &value, + QTextCodec *latin1, + const QByteArray &prefix = QByteArray()); #endif // QXTMAIL_P_H diff --git a/servatrice/src/smtp/qxtmailmessage.cpp b/servatrice/src/smtp/qxtmailmessage.cpp index 45452aede..a1c5f4409 100644 --- a/servatrice/src/smtp/qxtmailmessage.cpp +++ b/servatrice/src/smtp/qxtmailmessage.cpp @@ -379,6 +379,8 @@ QByteArray QxtMailMessage::rfc2822() const line = line + ' ' + word; word = ""; } + if (line.isEmpty()) + continue; if (line[0] == '.') rv += "."; rv += line + "\r\n"; diff --git a/servatrice/src/smtp/qxtsmtp.cpp b/servatrice/src/smtp/qxtsmtp.cpp index f9afd8c57..7f46234e0 100644 --- a/servatrice/src/smtp/qxtsmtp.cpp +++ b/servatrice/src/smtp/qxtsmtp.cpp @@ -23,39 +23,44 @@ ** ****************************************************************************/ - /*! * \class QxtSmtp * \inmodule QxtNetwork * \brief The QxtSmtp class implements the SMTP protocol for sending email */ - - #include "qxtsmtp.h" -#include "qxtsmtp_p.h" + #include "qxthmac.h" -#include -#include +#include "qxtsmtp_p.h" + #include #include +#include +#include QxtSmtpPrivate::QxtSmtpPrivate() : QObject(0) { // empty ctor } -QxtSmtp::QxtSmtp(QObject* parent) : QObject(parent) +QxtSmtp::QxtSmtp(QObject *parent) : QObject(parent) { QXT_INIT_PRIVATE(QxtSmtp); qxt_d().state = QxtSmtpPrivate::Disconnected; qxt_d().nextID = 0; qxt_d().socket = new QSslSocket(this); QObject::connect(socket(), SIGNAL(encrypted()), this, SIGNAL(encrypted())); - //QObject::connect(socket(), SIGNAL(encrypted()), &qxt_d(), SLOT(ehlo())); + // QObject::connect(socket(), SIGNAL(encrypted()), &qxt_d(), SLOT(ehlo())); QObject::connect(socket(), SIGNAL(connected()), this, SIGNAL(connected())); QObject::connect(socket(), SIGNAL(disconnected()), this, SIGNAL(disconnected())); - QObject::connect(socket(), SIGNAL(error(QAbstractSocket::SocketError)), &qxt_d(), SLOT(socketError(QAbstractSocket::SocketError))); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QObject::connect(socket(), SIGNAL(errorOccurred(QAbstractSocket::SocketError)), &qxt_d(), + SLOT(socketError(QAbstractSocket::SocketError))); +#else + QObject::connect(socket(), SIGNAL(error(QAbstractSocket::SocketError)), &qxt_d(), + SLOT(socketError(QAbstractSocket::SocketError))); +#endif QObject::connect(this, SIGNAL(authenticated()), &qxt_d(), SLOT(sendNext())); QObject::connect(socket(), SIGNAL(readyRead()), &qxt_d(), SLOT(socketRead())); } @@ -65,7 +70,7 @@ QByteArray QxtSmtp::username() const return qxt_d().username; } -void QxtSmtp::setUsername(const QByteArray& username) +void QxtSmtp::setUsername(const QByteArray &username) { qxt_d().username = username; } @@ -75,12 +80,12 @@ QByteArray QxtSmtp::password() const return qxt_d().password; } -void QxtSmtp::setPassword(const QByteArray& password) +void QxtSmtp::setPassword(const QByteArray &password) { qxt_d().password = password; } -int QxtSmtp::send(const QxtMailMessage& message) +int QxtSmtp::send(const QxtMailMessage &message) { int messageID = ++qxt_d().nextID; qxt_d().pending.append(qMakePair(messageID, message)); @@ -94,19 +99,19 @@ int QxtSmtp::pendingMessages() const return qxt_d().pending.count(); } -QTcpSocket* QxtSmtp::socket() const +QTcpSocket *QxtSmtp::socket() const { return qxt_d().socket; } -void QxtSmtp::connectToHost(const QString& hostName, quint16 port) +void QxtSmtp::connectToHost(const QString &hostName, quint16 port) { qxt_d().useSecure = false; qxt_d().state = QxtSmtpPrivate::StartState; socket()->connectToHost(hostName, port); } -void QxtSmtp::connectToHost(const QHostAddress& address, quint16 port) +void QxtSmtp::connectToHost(const QHostAddress &address, quint16 port) { connectToHost(address.toString(), port); } @@ -126,157 +131,141 @@ void QxtSmtp::setStartTlsDisabled(bool disable) qxt_d().disableStartTLS = disable; } -QSslSocket* QxtSmtp::sslSocket() const +QSslSocket *QxtSmtp::sslSocket() const { return qxt_d().socket; } -void QxtSmtp::connectToSecureHost(const QString& hostName, quint16 port) +void QxtSmtp::connectToSecureHost(const QString &hostName, quint16 port) { qxt_d().useSecure = true; qxt_d().state = QxtSmtpPrivate::StartState; sslSocket()->connectToHostEncrypted(hostName, port); } -void QxtSmtp::connectToSecureHost(const QHostAddress& address, quint16 port) +void QxtSmtp::connectToSecureHost(const QHostAddress &address, quint16 port) { connectToSecureHost(address.toString(), port); } -bool QxtSmtp::hasExtension(const QString& extension) +bool QxtSmtp::hasExtension(const QString &extension) { return qxt_d().extensions.contains(extension); } -QString QxtSmtp::extensionData(const QString& extension) +QString QxtSmtp::extensionData(const QString &extension) { return qxt_d().extensions[extension]; } void QxtSmtpPrivate::socketError(QAbstractSocket::SocketError err) { - if (err == QAbstractSocket::SslHandshakeFailedError) - { + if (err == QAbstractSocket::SslHandshakeFailedError) { emit qxt_p().encryptionFailed(); - emit qxt_p().encryptionFailed( socket->errorString().toLatin1() ); - } - else if (state == StartState) - { + emit qxt_p().encryptionFailed(socket->errorString().toLatin1()); + } else if (state == StartState) { emit qxt_p().connectionFailed(); - emit qxt_p().connectionFailed( socket->errorString().toLatin1() ); + emit qxt_p().connectionFailed(socket->errorString().toLatin1()); } } void QxtSmtpPrivate::socketRead() { buffer += socket->readAll(); - while (true) - { + while (true) { int pos = buffer.indexOf("\r\n"); - if (pos < 0) return; + if (pos < 0) + return; QByteArray line = buffer.left(pos); buffer = buffer.mid(pos + 2); QByteArray code = line.left(3); - switch (state) - { - case StartState: - if (code[0] != '2') - { - socket->disconnectFromHost(); - } - else - { - ehlo(); - } - break; - case HeloSent: - case EhloSent: - case EhloGreetReceived: - parseEhlo(code, (line[3] != ' '), line.mid(4)); - break; - case StartTLSSent: - if (code == "220") - { - socket->startClientEncryption(); - ehlo(); - } - else - { - authenticate(); - } - break; - case AuthRequestSent: - case AuthUsernameSent: - if (authType == AuthPlain) authPlain(); - else if (authType == AuthLogin) authLogin(); - else authCramMD5(line.mid(4)); - break; - case AuthSent: - if (code[0] == '2') - { - state = Authenticated; - emit qxt_p().authenticated(); - } - else - { - state = Disconnected; - emit qxt_p().authenticationFailed(); - emit qxt_p().authenticationFailed( line ); - emit socket->disconnectFromHost(); - } - break; - case MailToSent: - case RcptAckPending: - if (code[0] != '2') { - emit qxt_p().mailFailed( pending.first().first, code.toInt() ); - emit qxt_p().mailFailed(pending.first().first, code.toInt(), line); - // pending.removeFirst(); - // DO NOT remove it, the body sent state needs this message to assigned the next mail failed message that will - // the sendNext - // a reset will be sent to clear things out + switch (state) { + case StartState: + if (code[0] != '2') { + socket->disconnectFromHost(); + } else { + ehlo(); + } + break; + case HeloSent: + case EhloSent: + case EhloGreetReceived: + parseEhlo(code, (line[3] != ' '), line.mid(4)); + break; + case StartTLSSent: + if (code == "220") { + socket->startClientEncryption(); + ehlo(); + } else { + authenticate(); + } + break; + case AuthRequestSent: + case AuthUsernameSent: + if (authType == AuthPlain) + authPlain(); + else if (authType == AuthLogin) + authLogin(); + else + authCramMD5(line.mid(4)); + break; + case AuthSent: + if (code[0] == '2') { + state = Authenticated; + emit qxt_p().authenticated(); + } else { + state = Disconnected; + emit qxt_p().authenticationFailed(); + emit qxt_p().authenticationFailed(line); + emit socket->disconnectFromHost(); + } + break; + case MailToSent: + case RcptAckPending: + if (code[0] != '2') { + emit qxt_p().mailFailed(pending.first().first, code.toInt()); + emit qxt_p().mailFailed(pending.first().first, code.toInt(), line); + // pending.removeFirst(); + // DO NOT remove it, the body sent state needs this message to assigned the next mail failed message + // that will the sendNext a reset will be sent to clear things out + sendNext(); + state = BodySent; + } else + sendNextRcpt(code, line); + break; + case SendingBody: + sendBody(code, line); + break; + case BodySent: + if (pending.count()) { + // if you removeFirst in RcpActpending/MailToSent on an error, and the queue is now empty, + // you will get into this state and then crash because no check is done. CHeck added but shouldnt + // be necessary since I commented out the removeFirst + if (code[0] != '2') { + emit qxt_p().mailFailed(pending.first().first, code.toInt()); + emit qxt_p().mailFailed(pending.first().first, code.toInt(), line); + } else + emit qxt_p().mailSent(pending.first().first); + pending.removeFirst(); + } sendNext(); - state = BodySent; - } - else - sendNextRcpt(code, line); - break; - case SendingBody: - sendBody(code, line); - break; - case BodySent: - if ( pending.count() ) - { - // if you removeFirst in RcpActpending/MailToSent on an error, and the queue is now empty, - // you will get into this state and then crash because no check is done. CHeck added but shouldnt - // be necessary since I commented out the removeFirst - if (code[0] != '2') - { - emit qxt_p().mailFailed(pending.first().first, code.toInt() ); - emit qxt_p().mailFailed(pending.first().first, code.toInt(), line); - } - else - emit qxt_p().mailSent(pending.first().first); - pending.removeFirst(); - } - sendNext(); - break; - case Resetting: - if (code[0] != '2') { - emit qxt_p().connectionFailed(); - emit qxt_p().connectionFailed( line ); - } - else { - state = Waiting; - sendNext(); - } - break; - case Disconnected: - case EhloExtensionsReceived: - case EhloDone: - case Authenticated: - case Waiting: - // only to make compiler happy - break; + break; + case Resetting: + if (code[0] != '2') { + emit qxt_p().connectionFailed(); + emit qxt_p().connectionFailed(line); + } else { + state = Waiting; + sendNext(); + } + break; + case Disconnected: + case EhloExtensionsReceived: + case EhloDone: + case Authenticated: + case Waiting: + // only to make compiler happy + break; } } } @@ -284,8 +273,7 @@ void QxtSmtpPrivate::socketRead() void QxtSmtpPrivate::ehlo() { QByteArray address = "127.0.0.1"; - foreach(const QHostAddress& addr, QNetworkInterface::allAddresses()) - { + foreach (const QHostAddress &addr, QNetworkInterface::allAddresses()) { if (addr == QHostAddress::LocalHost || addr == QHostAddress::LocalHostIPv6) continue; address = addr.toString().toLatin1(); @@ -296,53 +284,40 @@ void QxtSmtpPrivate::ehlo() state = EhloSent; } -void QxtSmtpPrivate::parseEhlo(const QByteArray& code, bool cont, const QString& line) +void QxtSmtpPrivate::parseEhlo(const QByteArray &code, bool cont, const QString &line) { - if (code != "250") - { + if (code != "250") { // error! - if (state != HeloSent) - { + if (state != HeloSent) { // maybe let's try HELO socket->write("helo\r\n"); state = HeloSent; - } - else - { + } else { // nope socket->write("QUIT\r\n"); socket->flush(); socket->disconnectFromHost(); } return; - } - else if (state != EhloGreetReceived) - { - if (!cont) - { + } else if (state != EhloGreetReceived) { + if (!cont) { // greeting only, no extensions state = EhloDone; - } - else - { + } else { // greeting followed by extensions state = EhloGreetReceived; return; } - } - else - { + } else { extensions[line.section(' ', 0, 0).toUpper()] = line.section(' ', 1); if (!cont) state = EhloDone; } - if (state != EhloDone) return; - if (extensions.contains("STARTTLS") && !disableStartTLS) - { + if (state != EhloDone) + return; + if (extensions.contains("STARTTLS") && !disableStartTLS) { startTLS(); - } - else - { + } else { authenticate(); } } @@ -355,48 +330,35 @@ void QxtSmtpPrivate::startTLS() void QxtSmtpPrivate::authenticate() { - if (!extensions.contains("AUTH") || username.isEmpty() || password.isEmpty()) - { + if (!extensions.contains("AUTH") || username.isEmpty() || password.isEmpty()) { state = Authenticated; emit qxt_p().authenticated(); - } - else - { + } else { #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QStringList auth = extensions["AUTH"].toUpper().split(' ', Qt::SkipEmptyParts); #else QStringList auth = extensions["AUTH"].toUpper().split(' ', QString::SkipEmptyParts); #endif - if (auth.contains("CRAM-MD5")) - { + if (auth.contains("CRAM-MD5")) { authCramMD5(); - } - else if (auth.contains("PLAIN")) - { + } else if (auth.contains("PLAIN")) { authPlain(); - } - else if (auth.contains("LOGIN")) - { + } else if (auth.contains("LOGIN")) { authLogin(); - } - else - { + } else { state = Authenticated; emit qxt_p().authenticated(); } } } -void QxtSmtpPrivate::authCramMD5(const QByteArray& challenge) +void QxtSmtpPrivate::authCramMD5(const QByteArray &challenge) { - if (state != AuthRequestSent) - { + if (state != AuthRequestSent) { socket->write("auth cram-md5\r\n"); authType = AuthCramMD5; state = AuthRequestSent; - } - else - { + } else { QxtHmac hmac(QCryptographicHash::Md5); hmac.setKey(password); hmac.addData(QByteArray::fromBase64(challenge)); @@ -408,14 +370,11 @@ void QxtSmtpPrivate::authCramMD5(const QByteArray& challenge) void QxtSmtpPrivate::authPlain() { - if (state != AuthRequestSent) - { + if (state != AuthRequestSent) { socket->write("auth plain\r\n"); authType = AuthPlain; state = AuthRequestSent; - } - else - { + } else { QByteArray auth; auth += '\0'; auth += username; @@ -428,60 +387,44 @@ void QxtSmtpPrivate::authPlain() void QxtSmtpPrivate::authLogin() { - if (state != AuthRequestSent && state != AuthUsernameSent) - { + if (state != AuthRequestSent && state != AuthUsernameSent) { socket->write("auth login\r\n"); authType = AuthLogin; state = AuthRequestSent; - } - else if (state == AuthRequestSent) - { + } else if (state == AuthRequestSent) { socket->write(username.toBase64() + "\r\n"); state = AuthUsernameSent; - } - else - { + } else { socket->write(password.toBase64() + "\r\n"); state = AuthSent; } } -static QByteArray qxt_extract_address(const QString& address) +static QByteArray qxt_extract_address(const QString &address) { int parenDepth = 0; int addrStart = -1; bool inQuote = false; int ct = address.length(); - for (int i = 0; i < ct; i++) - { + for (int i = 0; i < ct; i++) { QChar ch = address[i]; - if (inQuote) - { + if (inQuote) { if (ch == '"') inQuote = false; - } - else if (addrStart != -1) - { + } else if (addrStart != -1) { if (ch == '>') return address.mid(addrStart, (i - addrStart)).toLatin1(); - } - else if (ch == '(') - { + } else if (ch == '(') { parenDepth++; - } - else if (ch == ')') - { + } else if (ch == ')') { parenDepth--; - if (parenDepth < 0) parenDepth = 0; - } - else if (ch == '"') - { + if (parenDepth < 0) + parenDepth = 0; + } else if (ch == '"') { if (parenDepth == 0) inQuote = true; - } - else if (ch == '<') - { + } else if (ch == '<') { if (!inQuote && parenDepth == 0) addrStart = i + 1; } @@ -491,35 +434,31 @@ static QByteArray qxt_extract_address(const QString& address) void QxtSmtpPrivate::sendNext() { - if (state == Disconnected) - { + if (state == Disconnected) { // leave the mail in the queue if not ready to send return; } - if (pending.isEmpty()) - { + if (pending.isEmpty()) { // if there are no additional mails to send, finish up state = Waiting; emit qxt_p().finished(); return; } - if(state != Waiting) { + if (state != Waiting) { state = Resetting; socket->write("rset\r\n"); return; } - const QxtMailMessage& msg = pending.first().second; + const QxtMailMessage &msg = pending.first().second; rcptNumber = rcptAck = mailAck = 0; - recipients = msg.recipients(QxtMailMessage::To) + - msg.recipients(QxtMailMessage::Cc) + - msg.recipients(QxtMailMessage::Bcc); - if (recipients.count() == 0) - { + recipients = + msg.recipients(QxtMailMessage::To) + msg.recipients(QxtMailMessage::Cc) + msg.recipients(QxtMailMessage::Bcc); + if (recipients.count() == 0) { // can't send an e-mail with no recipients - emit qxt_p().mailFailed(pending.first().first, QxtSmtp::NoRecipients ); - emit qxt_p().mailFailed(pending.first().first, QxtSmtp::NoRecipients, QByteArray( "e-mail has no recipients" ) ); + emit qxt_p().mailFailed(pending.first().first, QxtSmtp::NoRecipients); + emit qxt_p().mailFailed(pending.first().first, QxtSmtp::NoRecipients, QByteArray("e-mail has no recipients")); pending.removeFirst(); sendNext(); return; @@ -528,87 +467,67 @@ void QxtSmtpPrivate::sendNext() // interprets any string starting with an uppercase R as a request // to renegotiate the SSL connection. socket->write("mail from:<" + qxt_extract_address(msg.sender()) + ">\r\n"); - if (extensions.contains("PIPELINING")) // almost all do nowadays + if (extensions.contains("PIPELINING")) // almost all do nowadays { - foreach(const QString& rcpt, recipients) - { + foreach (const QString &rcpt, recipients) { socket->write("rcpt to:<" + qxt_extract_address(rcpt) + ">\r\n"); } state = RcptAckPending; - } - else - { + } else { state = MailToSent; } } -void QxtSmtpPrivate::sendNextRcpt(const QByteArray& code, const QByteArray&line) +void QxtSmtpPrivate::sendNextRcpt(const QByteArray &code, const QByteArray &line) { int messageID = pending.first().first; - const QxtMailMessage& msg = pending.first().second; + const QxtMailMessage &msg = pending.first().second; - if (code[0] != '2') - { + if (code[0] != '2') { // on failure, emit a warning signal - if (!mailAck) - { + if (!mailAck) { emit qxt_p().senderRejected(messageID, msg.sender()); - emit qxt_p().senderRejected(messageID, msg.sender(), line ); - } - else - { + emit qxt_p().senderRejected(messageID, msg.sender(), line); + } else { emit qxt_p().recipientRejected(messageID, msg.sender()); emit qxt_p().recipientRejected(messageID, msg.sender(), line); } - } - else if (!mailAck) - { + } else if (!mailAck) { mailAck = true; - } - else - { + } else { rcptAck++; } - if (rcptNumber == recipients.count()) - { + if (rcptNumber == recipients.count()) { // all recipients have been sent - if (rcptAck == 0) - { + if (rcptAck == 0) { // no recipients were considered valid - emit qxt_p().mailFailed(messageID, code.toInt() ); + emit qxt_p().mailFailed(messageID, code.toInt()); emit qxt_p().mailFailed(messageID, code.toInt(), line); pending.removeFirst(); sendNext(); - } - else - { + } else { // at least one recipient was acknowledged, send mail body socket->write("data\r\n"); state = SendingBody; } - } - else if (state != RcptAckPending) - { + } else if (state != RcptAckPending) { // send the next recipient unless we're only waiting on acks socket->write("rcpt to:<" + qxt_extract_address(recipients[rcptNumber]) + ">\r\n"); rcptNumber++; - } - else - { + } else { // If we're only waiting on acks, just count them rcptNumber++; } } -void QxtSmtpPrivate::sendBody(const QByteArray& code, const QByteArray & line) +void QxtSmtpPrivate::sendBody(const QByteArray &code, const QByteArray &line) { int messageID = pending.first().first; - const QxtMailMessage& msg = pending.first().second; + const QxtMailMessage &msg = pending.first().second; - if (code[0] != '3') - { - emit qxt_p().mailFailed(messageID, code.toInt() ); + if (code[0] != '3') { + emit qxt_p().mailFailed(messageID, code.toInt()); emit qxt_p().mailFailed(messageID, code.toInt(), line); pending.removeFirst(); sendNext(); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9ca0bfeba..bce3afebe 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -44,10 +44,6 @@ if(NOT GTEST_FOUND) add_dependencies(password_hash_test gtest) endif() -find_package(Qt5 COMPONENTS Widgets REQUIRED) -set(TEST_QT_MODULES Qt5::Widgets) - - include_directories(${GTEST_INCLUDE_DIRS}) target_link_libraries(dummy_test Threads::Threads ${GTEST_BOTH_LIBRARIES}) target_link_libraries(expression_test cockatrice_common Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES}) diff --git a/tests/carddatabase/CMakeLists.txt b/tests/carddatabase/CMakeLists.txt index 6aac4ebd3..ee9605fa6 100644 --- a/tests/carddatabase/CMakeLists.txt +++ b/tests/carddatabase/CMakeLists.txt @@ -1,11 +1,18 @@ ADD_DEFINITIONS("-DCARDDB_DATADIR=\"${CMAKE_CURRENT_SOURCE_DIR}/data/\"") -find_package(Qt5 COMPONENTS Concurrent Network Widgets Svg REQUIRED) +SET(TEST_QT_MODULES ${COCKATRICE_QT_VERSION_NAME}::Concurrent ${COCKATRICE_QT_VERSION_NAME}::Network ${COCKATRICE_QT_VERSION_NAME}::Widgets ${COCKATRICE_QT_VERSION_NAME}::Svg) -QT5_WRAP_CPP(MOCKS_SOURCES - ../../cockatrice/src/settingscache.h - ../../cockatrice/src/settings/carddatabasesettings.h -) +IF(Qt6_FOUND) + QT6_WRAP_CPP(MOCKS_SOURCES + ../../cockatrice/src/settingscache.h + ../../cockatrice/src/settings/carddatabasesettings.h + ) +ELSEIF(Qt5_FOUND) + QT5_WRAP_CPP(MOCKS_SOURCES + ../../cockatrice/src/settingscache.h + ../../cockatrice/src/settings/carddatabasesettings.h + ) +ENDIF() add_executable(carddatabase_test carddatabase_test.cpp @@ -37,8 +44,6 @@ if(NOT GTEST_FOUND) add_dependencies(filter_string_test gtest) endif() -set(TEST_QT_MODULES Qt5::Concurrent Qt5::Network Qt5::Widgets Qt5::Svg) - target_link_libraries(carddatabase_test Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES}) target_link_libraries(filter_string_test Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES}) diff --git a/tests/carddatabase/filter_string_test.cpp b/tests/carddatabase/filter_string_test.cpp index f76f27b61..07824087b 100644 --- a/tests/carddatabase/filter_string_test.cpp +++ b/tests/carddatabase/filter_string_test.cpp @@ -2,7 +2,6 @@ #include "mocks.h" #include "gtest/gtest.h" -#include CardDatabase *db; diff --git a/tests/expression_test.cpp b/tests/expression_test.cpp index 30569a196..51c4039a4 100644 --- a/tests/expression_test.cpp +++ b/tests/expression_test.cpp @@ -1,7 +1,7 @@ #include "../common/expression.h" #include "gtest/gtest.h" -#include +#include #define TEST_EXPR(name, a, b) \ TEST(ExpressionTest, name) \ @@ -18,7 +18,7 @@ TEST_EXPR(Multiply, "2*2", 4) TEST_EXPR(Whitespace, "3 * 3", 9) TEST_EXPR(Powers, "2^8", 256) TEST_EXPR(OrderOfOperations, "2+2*2", 6) -TEST_EXPR(Fn, "2*cos(1)", 2 * cos(1)) +TEST_EXPR(Fn, "2*cos(1)", 2 * qCos(1)) TEST_EXPR(Variable, "x / 2", 4) TEST_EXPR(Negative, "-2 * 2", -4) TEST_EXPR(UnknownFnReturnsZero, "blah(22)", 0) diff --git a/tests/loading_from_clipboard/CMakeLists.txt b/tests/loading_from_clipboard/CMakeLists.txt index c778fb7b3..efb36a083 100644 --- a/tests/loading_from_clipboard/CMakeLists.txt +++ b/tests/loading_from_clipboard/CMakeLists.txt @@ -9,8 +9,7 @@ if(NOT GTEST_FOUND) add_dependencies(loading_from_clipboard_test gtest) endif() -find_package(Qt5 COMPONENTS Concurrent Network Widgets REQUIRED) -set(TEST_QT_MODULES Qt5::Concurrent Qt5::Network Qt5::Widgets) +SET(TEST_QT_MODULES ${COCKATRICE_QT_VERSION_NAME}::Concurrent ${COCKATRICE_QT_VERSION_NAME}::Network ${COCKATRICE_QT_VERSION_NAME}::Widgets) target_link_libraries(loading_from_clipboard_test cockatrice_common Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES}) add_test(NAME loading_from_clipboard_test COMMAND loading_from_clipboard_test) diff --git a/vcpkg b/vcpkg index a863c8481..b98afc9f1 160000 --- a/vcpkg +++ b/vcpkg @@ -1 +1 @@ -Subproject commit a863c84812089836a3c0f2f215ab3e2579fc8acf +Subproject commit b98afc9f1192becb2f447cee485ce36ba111f9f6 diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 000000000..7d2c1a485 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,11 @@ +{ + "name": "cockatrice", + "version-string": "1.0.0", + "dependencies": [ + "gtest", + "liblzma", + "protobuf", + "pthreads", + "zlib" + ] +} \ No newline at end of file diff --git a/vcpkg.txt b/vcpkg.txt deleted file mode 100644 index 359f420d6..000000000 --- a/vcpkg.txt +++ /dev/null @@ -1,4 +0,0 @@ -protobuf -liblzma -zlib -gtest \ No newline at end of file