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
This commit is contained in:
ZeldaZach 2022-03-27 19:59:37 -04:00 committed by ZeldaZach
parent accd5e4df7
commit b02adccf87
114 changed files with 1925 additions and 1603 deletions

View file

@ -19,6 +19,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
qt5-default \ qt5-default \
qtbase5-dev \ qtbase5-dev \
qtmultimedia5-dev \ qtmultimedia5-dev \
qttools5-dev \
qttools5-dev-tools \ qttools5-dev-tools \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*

14
.ci/Fedora36/Dockerfile Normal file
View file

@ -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

View file

@ -1,4 +1,4 @@
FROM ubuntu:hirsute FROM ubuntu:jammy
RUN apt-get update && \ RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
@ -9,16 +9,19 @@ RUN apt-get update && \
file \ file \
g++ \ g++ \
git \ git \
libgl-dev \
liblzma-dev \ liblzma-dev \
libmariadb-dev-compat \ libmariadb-dev-compat \
libprotobuf-dev \ libprotobuf-dev \
libqt5multimedia5-plugins \ libqt6core5compat6-dev \
libqt5sql5-mysql \ libqt6multimedia6 \
libqt5svg5-dev \ libqt6sql6-mysql \
libqt5websockets5-dev \ libqt6svg6-dev \
libqt6websockets6-dev \
protobuf-compiler \ protobuf-compiler \
qtmultimedia5-dev \ qt6-l10n-tools \
qttools5-dev \ qt6-multimedia-dev \
qttools5-dev-tools \ qt6-tools-dev \
qt6-tools-dev-tools \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*

View file

@ -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. # 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 # Compiles cockatrice inside of a ci environment
# --format runs the clang-format script first
# --install runs make install # --install runs make install
# --package [<package type>] runs make package, optionally force the type # --package [<package type>] runs make package, optionally force the type
# --suffix <suffix> renames package with this suffix, requires arg # --suffix <suffix> renames package with this suffix, requires arg
# --server compiles servatrice # --server compiles servatrice
# --test runs tests # --test runs tests
# --debug or --release or <arg> sets the build type ie CMAKE_BUILD_TYPE # --debug or --release sets the build type ie CMAKE_BUILD_TYPE
# --ccache uses ccache and shows stats # --ccache [<size>] uses ccache and shows stats, optionally provide size
# --dir <dir> sets the name of the build dir, default is "build" # --dir <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: <buildtype>/--debug/--release --format --install --package <package type> --suffix <suffix> --server --test --ccache --dir <dir>) # --parallel <core count> 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 <package type> --suffix <suffix> --server --test --ccache <ccache_size> --dir <dir> --parallel <core_count>)
# exitcode: 1 for failure, 3 for invalid arguments # exitcode: 1 for failure, 3 for invalid arguments
LINT_SCRIPT=".ci/lint_cpp.sh"
# Read arguments # Read arguments
while [[ $# != 0 ]]; do while [[ $# != 0 ]]; do
@ -22,10 +22,6 @@ while [[ $# != 0 ]]; do
'--') '--')
shift shift
;; ;;
'--format')
CHECK_FORMAT=1
shift
;;
'--install') '--install')
MAKE_INSTALL=1 MAKE_INSTALL=1
shift shift
@ -33,7 +29,7 @@ while [[ $# != 0 ]]; do
'--package') '--package')
MAKE_PACKAGE=1 MAKE_PACKAGE=1
shift shift
if [[ $# != 0 && $1 != -* ]]; then if [[ $# != 0 && ${1:0:1} != - ]]; then
PACKAGE_TYPE="$1" PACKAGE_TYPE="$1"
shift shift
fi fi
@ -66,6 +62,10 @@ while [[ $# != 0 ]]; do
'--ccache') '--ccache')
USE_CCACHE=1 USE_CCACHE=1
shift shift
if [[ $# != 0 && ${1:0:1} != - ]]; then
CCACHE_SIZE="$1"
shift
fi
;; ;;
'--dir') '--dir')
shift shift
@ -76,67 +76,81 @@ while [[ $# != 0 ]]; do
BUILD_DIR="$1" BUILD_DIR="$1"
shift shift
;; ;;
*) '--parallel')
if [[ $1 == -* ]]; then shift
echo "::error file=$0::unrecognized option: $1" if [[ $# == 0 ]]; then
echo "::error file=$0::--parallel expects an argument"
exit 3 exit 3
fi fi
BUILDTYPE="$1" PARALLEL_COUNT="$1"
shift shift
;; ;;
*)
echo "::error file=$0::unrecognized option: $1"
exit 3
;;
esac esac
done done
# Check formatting using clang-format
if [[ $CHECK_FORMAT ]]; then
echo "::group::Run linter"
source "$LINT_SCRIPT"
echo "::endgroup::"
fi
set -e set -e
# Setup # Setup
./servatrice/check_schema_version.sh ./servatrice/check_schema_version.sh
if [[ ! $BUILDTYPE ]]; then
BUILDTYPE=Release
fi
if [[ ! $BUILD_DIR ]]; then if [[ ! $BUILD_DIR ]]; then
BUILD_DIR="build" BUILD_DIR="build"
fi fi
mkdir -p "$BUILD_DIR" mkdir -p "$BUILD_DIR"
cd "$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 # Add cmake flags
flags=() flags=("-DCMAKE_BUILD_TYPE=$BUILDTYPE")
if [[ $MAKE_SERVER ]]; then if [[ $MAKE_SERVER ]]; then
flags+=("-DWITH_SERVER=1") flags+=("-DWITH_SERVER=1")
fi fi
if [[ $MAKE_TEST ]]; then if [[ $MAKE_TEST ]]; then
flags+=("-DTEST=1") flags+=("-DTEST=1")
fi fi
if [[ ! $BUILDTYPE ]]; then if [[ $USE_CCACHE ]]; then
BUILDTYPE=Release flags+=("-DUSE_CCACHE=1")
if [[ $CCACHE_SIZE ]]; then
# note, this setting persists after running the script
ccache --max-size "$CCACHE_SIZE"
fi
fi fi
flags+=("-DCMAKE_BUILD_TYPE=$BUILDTYPE")
if [[ $PACKAGE_TYPE ]]; then if [[ $PACKAGE_TYPE ]]; then
flags+=("-DCPACK_GENERATOR=$PACKAGE_TYPE") flags+=("-DCPACK_GENERATOR=$PACKAGE_TYPE")
fi fi
if [[ $(uname) == "Darwin" ]]; then # Add cmake --build flags
if [[ $USE_CCACHE ]]; then buildflags=(--config "$BUILDTYPE")
# prepend ccache compiler binaries to path if [[ $PARALLEL_COUNT ]]; then
PATH="/usr/local/opt/ccache/libexec:$PATH" 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 fi
# Add qt install location when using homebrew
flags+=("-DCMAKE_PREFIX_PATH=/usr/local/opt/qt5/")
fi 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 # Compile
if [[ $USE_CCACHE ]]; then if [[ $USE_CCACHE ]]; then
echo "::group::Show ccache stats" echo "::group::Show ccache stats"
ccache --show-stats ccachestatsverbose
echo "::endgroup::" echo "::endgroup::"
fi fi
@ -146,12 +160,12 @@ cmake .. "${flags[@]}"
echo "::endgroup::" echo "::endgroup::"
echo "::group::Build project" echo "::group::Build project"
cmake --build . --config "$BUILDTYPE" cmake --build . "${buildflags[@]}"
echo "::endgroup::" echo "::endgroup::"
if [[ $USE_CCACHE ]]; then if [[ $USE_CCACHE ]]; then
echo "::group::Show ccache stats again" echo "::group::Show ccache stats again"
ccache --show-stats ccachestatsverbose
echo "::endgroup::" echo "::endgroup::"
fi fi
@ -174,7 +188,8 @@ if [[ $MAKE_PACKAGE ]]; then
if [[ $PACKAGE_SUFFIX ]]; then if [[ $PACKAGE_SUFFIX ]]; then
echo "::group::Update package name" echo "::group::Update package name"
../.ci/name_build.sh "$PACKAGE_SUFFIX" cd ..
BUILD_DIR="$BUILD_DIR" .ci/name_build.sh "$PACKAGE_SUFFIX"
echo "::endgroup::" echo "::endgroup::"
fi fi
fi fi

View file

@ -10,7 +10,8 @@
# --interactive immediately starts the image interactively for debugging # --interactive immediately starts the image interactively for debugging
# --set-cache <location> sets the location to cache the image or for ccache # --set-cache <location> sets the location to cache the image or for ccache
# requires: docker # requires: docker
# uses env: NAME CACHE BUILD GET SAVE INTERACTIVE (correspond to args: <name> --set-cache <cache> --build --get --save --interactive) # uses env: NAME CACHE BUILD GET SAVE INTERACTIVE
# (correspond to args: <name> --set-cache <cache> --build --get --save --interactive)
# sets env: RUN CCACHE_DIR IMAGE_NAME RUN_ARGS RUN_OPTS BUILD_SCRIPT # 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 # exitcode: 1 for failure, 2 for missing dockerfile, 3 for invalid arguments
export BUILD_SCRIPT=".ci/compile.sh" export BUILD_SCRIPT=".ci/compile.sh"
@ -48,7 +49,7 @@ while [[ $# != 0 ]]; do
shift 2 shift 2
;; ;;
*) *)
if [[ $1 == -* ]]; then if [[ ${1:0:1} == - ]]; then
echo "unrecognized option: $1" echo "unrecognized option: $1"
return 3 return 3
fi fi
@ -74,7 +75,9 @@ fi
if ! [[ $CACHE ]]; then if ! [[ $CACHE ]]; then
echo "cache dir is not set!" >&2 echo "cache dir is not set!" >&2
else CACHE="$(mktemp -d)"
echo "set cache dir to $CACHE" >&2
fi
if ! [[ -d $CACHE ]]; then if ! [[ -d $CACHE ]]; then
echo "could not find cache dir: $CACHE" >&2 echo "could not find cache dir: $CACHE" >&2
mkdir -p "$CACHE" mkdir -p "$CACHE"
@ -93,7 +96,6 @@ else
echo "could not find ccache dir: $CCACHE_DIR" >&2 echo "could not find ccache dir: $CCACHE_DIR" >&2
mkdir -p "$CCACHE_DIR" mkdir -p "$CCACHE_DIR"
fi fi
fi
# Get the docker image from previously stored save # Get the docker image from previously stored save
if [[ $GET ]]; then if [[ $GET ]]; then
@ -138,7 +140,7 @@ fi
function RUN () function RUN ()
{ {
echo "running image:" 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") local args=(--mount "type=bind,source=$PWD,target=/src")
args+=(--workdir "/src") args+=(--workdir "/src")
args+=(--user "$(id -u):$(id -g)") args+=(--user "$(id -u):$(id -g)")

35
.ci/download_openssl.sh Normal file
View file

@ -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"

View file

@ -3,8 +3,8 @@
# renames the file to [original name][SUFFIX].[original extension] # renames the file to [original name][SUFFIX].[original extension]
# where SUFFIX is either available in the environment or as the first arg # where SUFFIX is either available in the environment or as the first arg
# if MAKE_ZIP is set instead a zip is made # if MAKE_ZIP is set instead a zip is made
# expected to be run in the build directory # expected to be run in the build directory unless BUILD_DIR is set
builddir="." builddir="${BUILD_DIR:=.}"
findrx="Cockatrice-*.*" findrx="Cockatrice-*.*"
if [[ $1 ]]; then if [[ $1 ]]; then
@ -27,6 +27,7 @@ if [[ ! $file ]]; then
echo "::error file=$0::could not find package" echo "::error file=$0::could not find package"
exit 1 exit 1
fi fi
oldpwd="$PWD"
if ! cd "$path"; then if ! cd "$path"; then
echo "::error file=$0::could not get file path" echo "::error file=$0::could not get file path"
exit 1 exit 1
@ -45,6 +46,9 @@ else
echo "renaming '$file' to '$filename'" echo "renaming '$file' to '$filename'"
mv "$file" "$filename" mv "$file" "$filename"
fi 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" echo "::set-output name=name::$filename"

View file

@ -8,18 +8,20 @@ git push -d origin --REPLACE-WITH-BETA-LIST--
include different targets --> include different targets -->
<pre> <pre>
<b>Pre-compiled binaries we serve:</b> <b>Pre-compiled binaries we serve:</b>
- <kbd>Windows 7/8/10 (32-bit)</kbd></i> - <kbd>Windows 7/8/10/11 (32-bit)</kbd>
- <kbd>Windows 7/8/10 (64-bit)</kbd></i> - <kbd>Windows 7/8/10/11 (64-bit)</kbd>
- <kbd>macOS 10.14</kbd> ("Mojave")</i> - <kbd>macOS 10.14</kbd> ("Mojave")
- <kbd>macOS 10.15</kbd> ("Catalina")</i> - <kbd>macOS 10.15</kbd> ("Catalina")
- <kbd>macOS 11.0</kbd> ("Big Sur")</i> - <kbd>macOS 11.0</kbd> ("Big Sur")
- <kbd>Ubuntu 18.04</kbd> ("Bionic Beaver")</i> - <kbd>Ubuntu 18.04</kbd> ("Bionic Beaver")
- <kbd>Ubuntu 20.04</kbd> ("Focal Fossa")</i> - <kbd>Ubuntu 20.04</kbd> ("Focal Fossa")
- <kbd>Ubuntu 20.10</kbd> ("Groovy Gorilla")</i> - <kbd>Ubuntu 21.10</kbd> ("Impish Indri")
- <kbd>Ubuntu 21.04</kbd> ("Hirsute Hippo")</i> - <kbd>Ubuntu 22.04</kbd> ("Jammy Jellyfish")
- <kbd>Debian 10</kbd> ("Buster")</i> - <kbd>Debian 10</kbd> ("Buster")
- <kbd>Fedora 33</kbd></i> - <kbd>Debian 11</kbd> ("Bullseye")
- <kbd>Fedora 34</kbd></i> - <kbd>Fedora 34</kbd>
- <kbd>Fedora 35</kbd>
- <kbd>Fedora 36</kbd>
<kbd>We are also packaged in Arch Linux's official community repository, courtesy of @FFY00</kbd></i> <kbd>We are also packaged in Arch Linux's official community repository, courtesy of @FFY00</kbd></i>
<kbd>General linux support is available via a flatpak package (Flathub)</kbd></i> <kbd>General linux support is available via a flatpak package (Flathub)</kbd></i>
</pre> </pre>

View file

@ -19,9 +19,7 @@ on:
jobs: jobs:
configure: configure:
name: Configure name: Configure
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
tag: ${{steps.configure.outputs.tag}} tag: ${{steps.configure.outputs.tag}}
sha: ${{steps.configure.outputs.sha}} sha: ${{steps.configure.outputs.sha}}
@ -55,7 +53,7 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Prepare release paramaters - name: Prepare release parameters
id: prepare id: prepare
if: steps.configure.outputs.tag != null if: steps.configure.outputs.tag != null
shell: bash shell: bash
@ -88,6 +86,7 @@ jobs:
- distro: Debian10 - distro: Debian10
package: DEB package: DEB
test: skip # running tests on all distros is superfluous
- distro: Debian11 - distro: Debian11
package: DEB package: DEB
@ -98,6 +97,10 @@ jobs:
- distro: Fedora35 - distro: Fedora35
package: RPM package: RPM
test: skip
- distro: Fedora36
package: RPM
- distro: UbuntuBionic - distro: UbuntuBionic
package: DEB package: DEB
@ -106,24 +109,23 @@ jobs:
package: DEB package: DEB
test: skip # UbuntuFocal has a broken qt for debug builds test: skip # UbuntuFocal has a broken qt for debug builds
- distro: UbuntuHirsute - distro: UbuntuImpish
package: DEB package: DEB
test: skip test: skip
- distro: UbuntuImpish - distro: UbuntuJammy
package: DEB package: DEB
name: ${{matrix.distro}} name: ${{matrix.distro}}
needs: configure needs: configure
runs-on: ubuntu-latest runs-on: ubuntu-latest
continue-on-error: ${{matrix.allow-failure == 'yes'}} continue-on-error: ${{matrix.allow-failure == 'yes'}}
env: env:
NAME: ${{matrix.distro}} NAME: ${{matrix.distro}}
CACHE: /tmp/${{matrix.distro}}-cache # ${{runner.temp}} does not work? 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: steps:
- name: Checkout - name: Checkout
@ -151,9 +153,11 @@ jobs:
- name: Build debug and test - name: Build debug and test
if: matrix.test != 'skip' if: matrix.test != 'skip'
shell: bash shell: bash
env:
distro: '${{matrix.distro}}'
run: | run: |
source .ci/docker.sh source .ci/docker.sh
RUN --server --debug --test --ccache RUN --server --debug --test --ccache "$CCACHE_SIZE" --parallel 2
- name: Build release package - name: Build release package
id: package id: package
@ -162,16 +166,18 @@ jobs:
env: env:
suffix: '-${{matrix.distro}}' suffix: '-${{matrix.distro}}'
type: '${{matrix.package}}' type: '${{matrix.package}}'
distro: '${{matrix.distro}}'
run: | run: |
source .ci/docker.sh 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 - name: Upload artifact
if: matrix.package != 'skip' if: matrix.package != 'skip'
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
with: with:
name: ${{matrix.distro}}-package name: ${{matrix.distro}}-package
path: ./build/${{steps.package.outputs.name}} path: ${{steps.package.outputs.path}}
if-no-files-found: error if-no-files-found: error
- name: Upload to release - name: Upload to release
@ -181,7 +187,7 @@ jobs:
GITHUB_TOKEN: ${{github.token}} GITHUB_TOKEN: ${{github.token}}
with: with:
upload_url: ${{needs.configure.outputs.upload_url}} 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_name: ${{steps.package.outputs.name}}
asset_content_type: application/octet-stream asset_content_type: application/octet-stream
@ -193,39 +199,46 @@ jobs:
- target: Debug # tests only - target: Debug # tests only
os: macos-latest os: macos-latest
xcode: 12.5.1 xcode: 12.5.1
qt_version: 6
type: Debug type: Debug
do_tests: 0 # tests do not work yet on mac do_tests: 1
make_package: false
- target: 10.14_Mojave - target: 10.14_Mojave
os: macos-10.15 # runs on Catalina os: macos-10.15 # runs on Catalina
xcode: 10.3 # allows compatibility with macOS 10.14 xcode: 10.3 # allows compatibility with macOS 10.14
qt_version: 5
type: Release type: Release
do_tests: 0 # do_tests: 1 # tests do not work on qt5?
make_package: true make_package: 1
- target: 10.15_Catalina - target: 10.15_Catalina
os: macos-10.15 os: macos-10.15
xcode: 12.1 xcode: 12.4
qt_version: 6
type: Release type: Release
do_tests: 0 do_tests: 1
make_package: true make_package: 1
- target: 11_Big_Sur - target: 11_Big_Sur
os: macos-11 os: macos-11
xcode: 12.5.1 xcode: 13.2
qt_version: 6
type: Release type: Release
do_tests: 0 do_tests: 1
make_package: true 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}} name: macOS ${{matrix.target}}
needs: configure needs: configure
runs-on: ${{matrix.os}} runs-on: ${{matrix.os}}
continue-on-error: ${{matrix.allow-failure == 'yes'}} continue-on-error: ${{matrix.allow-failure == 'yes'}}
env: env:
DEVELOPER_DIR: DEVELOPER_DIR:
/Applications/Xcode_${{matrix.xcode}}.app/Contents/Developer /Applications/Xcode_${{matrix.xcode}}.app/Contents/Developer
@ -238,45 +251,32 @@ jobs:
shell: bash shell: bash
# cmake cannot find the mysql connector # cmake cannot find the mysql connector
# neither of these works: mariadb-connector-c mysql-connector-c++ # neither of these works: mariadb-connector-c mysql-connector-c++
run: brew update && brew install protobuf env:
qt_version: 'qt@${{matrix.qt_version}}'
- name: Install QT using homebrew run: |
id: brew_install_qt brew update
continue-on-error: true brew install protobuf
shell: bash brew install "$qt_version" --force-bottle
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
- name: Build on Xcode ${{matrix.xcode}} - name: Build on Xcode ${{matrix.xcode}}
shell: bash shell: bash
id: build
env: env:
CMAKE_BUILD_PARALLEL_LEVEL: 3 # mac machines actually have 3 cores BUILDTYPE: '${{matrix.type}}'
run: .ci/compile.sh ${{matrix.type}} --server MAKE_TEST: '${{matrix.do_tests}}'
MAKE_PACKAGE: '${{matrix.make_package}}'
- name: Test PACKAGE_SUFFIX: '-macOS-${{matrix.target}}'
if: matrix.do_tests == 1 # set QTDIR to find qt5, qt6 can be found without this
shell: bash QTDIR: '/usr/local/opt/qt5'
working-directory: build # Mac machines actually have 3 cores
run: cmake --build . --target test run: .ci/compile.sh --server --parallel 3
- 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}}"
- name: Upload artifact - name: Upload artifact
if: matrix.make_package if: matrix.make_package
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
with: with:
name: macOS-${{matrix.target}}-dmg name: macOS-${{matrix.target}}-dmg
path: ${{steps.package.outputs.path}} path: ${{steps.build.outputs.path}}
if-no-files-found: error if-no-files-found: error
- name: Upload to release - name: Upload to release
@ -286,8 +286,8 @@ jobs:
GITHUB_TOKEN: ${{github.token}} GITHUB_TOKEN: ${{github.token}}
with: with:
upload_url: ${{needs.configure.outputs.upload_url}} upload_url: ${{needs.configure.outputs.upload_url}}
asset_path: ${{steps.package.outputs.path}} asset_path: ${{steps.build.outputs.path}}
asset_name: ${{steps.package.outputs.name}} asset_name: ${{steps.build.outputs.name}}
asset_content_type: application/octet-stream asset_content_type: application/octet-stream
build-windows: build-windows:
@ -296,24 +296,22 @@ jobs:
matrix: matrix:
include: include:
- arch: 32 - arch: 32
triplet: x86 vcpkg_default_triplet: x86
cmake: Win32 qt_version: '5.15.2'
cmake_generator_platform: Win32
qt_arch: msvc2019
- arch: 64 - arch: 64
triplet: x64 vcpkg_default_triplet: x64
cmake: x64 qt_version: '6.3.0'
append: _64 cmake_generator_platform: x64
qt_arch: msvc2019_64
name: Windows ${{matrix.arch}}-bit qt_modules: "qt5compat qtmultimedia qtwebsockets"
name: Windows (${{matrix.arch}}-bit)
needs: configure needs: configure
runs-on: windows-2019 runs-on: windows-2019
env: env:
QT_VERSION: '5.15.2' CMAKE_GENERATOR: 'Visual Studio 16 2019'
QT_ARCH: msvc2022${{matrix.append}}
CMAKE_GENERATOR: 'Visual Studio 17 2022'
steps: steps:
- name: Add msbuild to PATH - name: Add msbuild to PATH
@ -321,46 +319,57 @@ jobs:
uses: microsoft/setup-msbuild@v1.1 uses: microsoft/setup-msbuild@v1.1
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
submodules: recursive 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 id: cache-qt
uses: actions/cache@v2 uses: actions/cache@v1 # Intentionally v1, based on jurplel documentation
with: with:
key: ${{runner.os}}-QtCache-${{env.QT_VERSION}}-${{matrix.arch}} key: ${{runner.os}}-QtCache-${{matrix.qt_version}}-${{matrix.arch}}
path: ${{runner.workspace}}/Qt path: '${{github.workspace}}/../Qt'
- name: Install ${{matrix.arch}}-bit Qt - name: Install Qt ${{matrix.qt_version}} (${{matrix.arch}}-bit)
uses: jurplel/install-qt-action@v2 uses: jurplel/install-qt-action@v3
with: with:
cached: ${{steps.cache-qt.outputs.cache-hit}} cached: ${{steps.cache-qt.outputs.cache-hit}}
version: ${{env.QT_VERSION}} version: ${{matrix.qt_version}}
arch: win${{matrix.arch}}_${{env.QT_ARCH}} arch: win${{matrix.arch}}_${{matrix.qt_arch}}
modules: ${{matrix.qt_modules}}
- name: Restore or setup vcpkg - name: Run vcpkg
uses: lukka/run-vcpkg@v6 uses: lukka/run-vcpkg@v10.2
with: with:
vcpkgArguments: '@${{github.workspace}}/vcpkg.txt' runVcpkgInstall: true
vcpkgDirectory: ${{github.workspace}}/vcpkg appendedCacheKey: ${{matrix.arch}}-bit
appendedCacheKey: ${{hashFiles('**/vcpkg.txt')}} env:
vcpkgTriplet: ${{matrix.triplet}}-windows 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 id: build
shell: bash shell: bash
env: env:
PACKAGE_SUFFIX: '-win${{matrix.arch}}' PACKAGE_SUFFIX: '-win${{matrix.arch}}'
CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}' CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}'
CMAKE_GENERATOR_PLATFORM: '${{matrix.cmake}}' CMAKE_GENERATOR_PLATFORM: '${{matrix.cmake_generator_platform}}'
run: ./.ci/compile.sh --server --release --test --package 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 - name: Upload artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: Windows-${{matrix.arch}}bit-installer name: Windows-${{matrix.arch}}bit-installer
path: ./build/${{steps.build.outputs.name}} path: ${{steps.build.outputs.path}}
if-no-files-found: error if-no-files-found: error
- name: Upload to release - name: Upload to release
@ -370,6 +379,6 @@ jobs:
GITHUB_TOKEN: ${{github.token}} GITHUB_TOKEN: ${{github.token}}
with: with:
upload_url: ${{needs.configure.outputs.upload_url}} 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_name: ${{steps.build.outputs.name}}
asset_content_type: application/octet-stream asset_content_type: application/octet-stream

2
.gitignore vendored
View file

@ -6,7 +6,7 @@ mysql.cnf
.DS_Store .DS_Store
.idea/ .idea/
*.aps *.aps
cmake-build-debug/ cmake-build-debug*
preferences preferences
compile_commands.json compile_commands.json
.vs/ .vs/

View file

@ -5,9 +5,26 @@
# This file sets all the variables shared between the projects # This file sets all the variables shared between the projects
# like the installation path, compilation flags etc.. # 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) 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 # Default to "Release" build type
# User-provided value for CMAKE_BUILD_TYPE must be checked before the PROJECT() call # User-provided value for CMAKE_BUILD_TYPE must be checked before the PROJECT() call
IF(DEFINED CMAKE_BUILD_TYPE) IF(DEFINED CMAKE_BUILD_TYPE)
@ -16,8 +33,6 @@ ELSE()
SET(CMAKE_BUILD_TYPE Release CACHE STRING "Type of build") SET(CMAKE_BUILD_TYPE Release CACHE STRING "Type of build")
ENDIF() ENDIF()
# Early detect ccache
OPTION(USE_CCACHE "Cache the build results with ccache" ON)
if(USE_CCACHE) if(USE_CCACHE)
find_program(CCACHE_PROGRAM ccache) find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM) if(CCACHE_PROGRAM)
@ -41,22 +56,23 @@ endif()
# A project name is needed for CPack # A project name is needed for CPack
# Version can be overriden by git tags, see cmake/getversion.cmake # 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 # Set release name if not provided via env/cmake var
if(NOT DEFINED GIT_TAG_RELEASENAME) if(NOT DEFINED GIT_TAG_RELEASENAME)
set(GIT_TAG_RELEASENAME "Prismatic Bridge") set(GIT_TAG_RELEASENAME "Prismatic Bridge")
endif() endif()
# Use c++11 for all targets # Use c++17 for all targets
set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ ISO Standard") set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ ISO Standard")
set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_CXX_STANDARD_REQUIRED True)
# Set conventional loops # Set conventional loops
set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true) set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true)
# Search path for cmake modules # 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) include(getversion)
@ -76,8 +92,8 @@ if(UNIX)
if(RULE_LAUNCH_COMPILE) if(RULE_LAUNCH_COMPILE)
MESSAGE(STATUS "Force enabling CCache usage under macOS") MESSAGE(STATUS "Force enabling CCache usage under macOS")
# Set up wrapper scripts # Set up wrapper scripts
configure_file(${CMAKE_MODULE_PATH}/launch-c.in launch-c) configure_file("${COCKATRICE_CMAKE_PATH}/launch-c.in" launch-c)
configure_file(${CMAKE_MODULE_PATH}/launch-cxx.in launch-cxx) configure_file("${COCKATRICE_CMAKE_PATH}/launch-cxx.in" launch-cxx)
execute_process(COMMAND chmod a+rx execute_process(COMMAND chmod a+rx
"${CMAKE_BINARY_DIR}/launch-c" "${CMAKE_BINARY_DIR}/launch-c"
"${CMAKE_BINARY_DIR}/launch-cxx") "${CMAKE_BINARY_DIR}/launch-cxx")
@ -103,13 +119,10 @@ elseif(WIN32)
set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/rundir/${CMAKE_BUILD_TYPE}) set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/rundir/${CMAKE_BUILD_TYPE})
endif() endif()
# Treat warnings as errors (Debug builds only)
option(WARNING_AS_ERROR "Treat warnings as errors in debug builds" ON)
# Define proper compilation flags # Define proper compilation flags
IF(MSVC) IF(MSVC)
# Visual Studio: Maximum optimization, disable warning C4251 # Visual Studio: Maximum optimization, disable warning C4251, establish C++17 compatibility
set(CMAKE_CXX_FLAGS_RELEASE "/Ox /MD /wd4251 ") SET(CMAKE_CXX_FLAGS_RELEASE "/Ox /MD /wd4251 /Zc:__cplusplus /std:c++17 /permissive-")
# Generate complete debugging information # Generate complete debugging information
#set(CMAKE_CXX_FLAGS_DEBUG "/Zi") #set(CMAKE_CXX_FLAGS_DEBUG "/Zi")
ELSEIF (CMAKE_COMPILER_IS_GNUCXX) ELSEIF (CMAKE_COMPILER_IS_GNUCXX)
@ -123,6 +136,10 @@ ELSEIF (CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0 -Wall -Wextra") set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0 -Wall -Wextra")
endif() 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) 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}) FOREACH(FLAG ${ADDITIONAL_DEBUG_FLAGS})
@ -148,7 +165,7 @@ ENDIF()
FIND_PACKAGE(Threads REQUIRED) FIND_PACKAGE(Threads REQUIRED)
# Find Qt5 # Determine 32 or 64 bit build
if(CMAKE_SIZEOF_VOID_P EQUAL 8) if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(_lib_suffix 64) set(_lib_suffix 64)
else() else()
@ -165,28 +182,9 @@ elseif(DEFINED ENV{QTDIR})
list(APPEND CMAKE_PREFIX_PATH "$ENV{QTDIR}") list(APPEND CMAKE_PREFIX_PATH "$ENV{QTDIR}")
endif() endif()
FIND_PACKAGE(Qt5Core 5.5.0 REQUIRED) MESSAGE(STATUS "Update Translations: ${UPDATE_TRANSLATIONS}")
IF(Qt5Core_FOUND) include(FindQtRuntime)
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}")
set(CMAKE_AUTOMOC TRUE) set(CMAKE_AUTOMOC TRUE)
@ -195,7 +193,7 @@ FIND_PACKAGE(Protobuf REQUIRED)
IF(NOT EXISTS "${Protobuf_PROTOC_EXECUTABLE}") IF(NOT EXISTS "${Protobuf_PROTOC_EXECUTABLE}")
MESSAGE(FATAL_ERROR "No protoc command found!") MESSAGE(FATAL_ERROR "No protoc command found!")
ELSE() ELSE()
MESSAGE(STATUS "Protoc version ${Protobuf_VERSION} found!") MESSAGE(STATUS "Found Protobuf ${Protobuf_VERSION} at: ${Protobuf_LIBRARIES}")
ENDIF() ENDIF()
#Find OpenSSL #Find OpenSSL
@ -209,8 +207,8 @@ IF(MSVC)
ENDIF() ENDIF()
# Package builder # Package builder
set(CPACK_PACKAGE_CONTACT "Zach Halpern <zahalpern+github@gmail.com>") set(CPACK_PACKAGE_CONTACT "Zach Halpern <zach@cockatrice.us>")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${PROJECT_NAME}) set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PROJECT_NAME}")
set(CPACK_PACKAGE_VENDOR "Cockatrice Development Team") set(CPACK_PACKAGE_VENDOR "Cockatrice Development Team")
set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/README.md") set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/README.md")
set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
@ -231,16 +229,34 @@ if(UNIX)
# linux # linux
IF(CPACK_GENERATOR STREQUAL "RPM") IF(CPACK_GENERATOR STREQUAL "RPM")
set(CPACK_RPM_PACKAGE_LICENSE "GPLv2") 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_GROUP "Amusements/Games")
set(CPACK_RPM_PACKAGE_URL "http://github.com/Cockatrice/Cockatrice") 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() ELSE()
set(CPACK_GENERATOR DEB) set(CPACK_GENERATOR DEB)
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
set(CPACK_DEBIAN_PACKAGE_SECTION "games") set(CPACK_DEBIAN_PACKAGE_SECTION "games")
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://github.com/Cockatrice/Cockatrice") set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://github.com/Cockatrice/Cockatrice")
IF(Qt6_FOUND)
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt6multimedia6, libqt6svg6, qt6-qpa-plugins")
ELSEIF(Qt5_FOUND)
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5multimedia5-plugins, libqt5svg5") set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5multimedia5-plugins, libqt5svg5")
ENDIF() ENDIF()
ENDIF()
endif() endif()
elseif(WIN32) elseif(WIN32)
set(CPACK_GENERATOR NSIS ${CPACK_GENERATOR}) set(CPACK_GENERATOR NSIS ${CPACK_GENERATOR})
@ -252,8 +268,8 @@ elseif(WIN32)
# Configure file with custom definitions for NSIS. # Configure file with custom definitions for NSIS.
configure_file( configure_file(
${CMAKE_MODULE_PATH}/NSIS.definitions.nsh.in "${COCKATRICE_CMAKE_PATH}/NSIS.definitions.nsh.in"
${PROJECT_BINARY_DIR}/NSIS.definitions.nsh "${PROJECT_BINARY_DIR}/NSIS.definitions.nsh"
) )
# include vcredist into the package; NSIS will take care of running it # include vcredist into the package; NSIS will take care of running it
@ -264,38 +280,33 @@ endif()
include(CPack) include(CPack)
# Compile servatrice (default off)
option(WITH_SERVER "build servatrice" OFF)
add_subdirectory(common) add_subdirectory(common)
if(WITH_SERVER) if(WITH_SERVER)
add_subdirectory(servatrice) add_subdirectory(servatrice)
SET(CPACK_INSTALL_CMAKE_PROJECTS "Servatrice;Servatrice;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS}) SET(CPACK_INSTALL_CMAKE_PROJECTS "Servatrice;Servatrice;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS})
endif() endif()
# Compile cockatrice (default on)
option(WITH_CLIENT "build cockatrice" ON)
if(WITH_CLIENT) if(WITH_CLIENT)
add_subdirectory(cockatrice) add_subdirectory(cockatrice)
SET(CPACK_INSTALL_CMAKE_PROJECTS "Cockatrice;Cockatrice;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS}) SET(CPACK_INSTALL_CMAKE_PROJECTS "Cockatrice;Cockatrice;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS})
endif() endif()
# Compile oracle (default on)
option(WITH_ORACLE "build oracle" ON)
if(WITH_ORACLE) if(WITH_ORACLE)
add_subdirectory(oracle) add_subdirectory(oracle)
SET(CPACK_INSTALL_CMAKE_PROJECTS "Oracle;Oracle;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS}) SET(CPACK_INSTALL_CMAKE_PROJECTS "Oracle;Oracle;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS})
endif() endif()
# Compile dbconverter (default on)
option(WITH_DBCONVERTER "build dbconverter" ON)
if(WITH_DBCONVERTER) if(WITH_DBCONVERTER)
add_subdirectory(dbconverter) add_subdirectory(dbconverter)
SET(CPACK_INSTALL_CMAKE_PROJECTS "Dbconverter;Dbconverter;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS}) SET(CPACK_INSTALL_CMAKE_PROJECTS "Dbconverter;Dbconverter;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS})
endif() endif()
# Compile tests (default off)
option(TEST "build tests" OFF)
if(TEST) if(TEST)
include(CTest) include(CTest)
add_subdirectory(tests) add_subdirectory(tests)
endif() endif()
if(Qt6Found AND Qt6_VERSION_MINOR GREATER_EQUAL 3)
# Qt6.3+ requires project finalization to support translations
qt6_finalize_project()
endif()

View file

@ -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). - `-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). - `-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```. - `-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 # Run

118
cmake/FindQtRuntime.cmake Normal file
View file

@ -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}")

View file

@ -1,66 +1,44 @@
# Find the OpenSSL runtime libraries (.dll) for Windows that # Find the OpenSSL runtime libraries (.dll) for Windows that
# will be needed by Qt in order to access https urls. # 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) if("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "x64")
# Get standard installation paths for OpenSSL under Windows message(STATUS "Looking for OpenSSL for ${CMAKE_GENERATOR_PLATFORM}")
# 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) file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles)
set(_OPENSSL_ROOT_PATHS 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" "C:/Tools/vcpkg/installed/x64-windows/bin"
"${_programfiles}/OpenSSL-Win64" "${_programfiles}/OpenSSL-Win64"
"C:/OpenSSL-Win64/"
) )
unset(_programfiles) unset(_programfiles)
else( CMAKE_SIZEOF_VOID_P EQUAL 8 ) elseif("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "Win32")
# target win32 message(STATUS "Looking for OpenSSL for ${CMAKE_GENERATOR_PLATFORM}")
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) file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles)
set(_OPENSSL_ROOT_PATHS 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" "C:/Tools/vcpkg/installed/x86-windows/bin"
"${_programfiles}/OpenSSL" "${_programfiles}/OpenSSL"
"${_programfiles}/OpenSSL-Win32" "${_programfiles}/OpenSSL-Win32"
"C:/OpenSSL/"
"C:/OpenSSL-Win32/"
) )
unset(_programfiles) unset(_programfiles)
endif( CMAKE_SIZEOF_VOID_P EQUAL 8 )
else ()
set(_OPENSSL_ROOT_HINTS
${OPENSSL_ROOT_DIR}
ENV OPENSSL_ROOT_DIR
)
endif() endif()
set(_OPENSSL_ROOT_HINTS_AND_PATHS message(STATUS "Looking for OpenSSL @ $ENV{CMAKE_GENERATOR_PLATFORM} in ${_OPENSSL_ROOT_PATHS}")
HINTS ${_OPENSSL_ROOT_HINTS} if("$ENV{CMAKE_GENERATOR_PLATFORM}" STREQUAL "x64")
PATHS ${_OPENSSL_ROOT_PATHS} 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")
# For OpenSSL < 1.1, they are named libeay32 and ssleay32 and even if the dll is 64bit, it's still suffixed as *32.dll FIND_FILE(WIN32SSLRUNTIME_LIBEAY NAMES libcrypto-1_1.dll libcrypto.dll PATHS ${_OPENSSL_ROOT_PATHS} NO_DEFAULT_PATH)
# For OpenSSL >= 1.1, they are named libcrypto and libssl with no suffix FIND_FILE(WIN32SSLRUNTIME_SSLEAY NAMES libssl-1_1.dll libssl.dll PATHS ${_OPENSSL_ROOT_PATHS} NO_DEFAULT_PATH)
if( CMAKE_SIZEOF_VOID_P EQUAL 8 ) endif()
# 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 )
IF(WIN32SSLRUNTIME_LIBEAY AND WIN32SSLRUNTIME_SSLEAY) IF(WIN32SSLRUNTIME_LIBEAY AND WIN32SSLRUNTIME_SSLEAY)
SET(WIN32SSLRUNTIME_LIBRARIES "${WIN32SSLRUNTIME_LIBEAY}" "${WIN32SSLRUNTIME_SSLEAY}") SET(WIN32SSLRUNTIME_LIBRARIES "${WIN32SSLRUNTIME_LIBEAY}" "${WIN32SSLRUNTIME_SSLEAY}")

View file

@ -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_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) 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(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() ELSE()
FILE(GLOB cockatrice_TS "${CMAKE_CURRENT_SOURCE_DIR}/translations/*.ts") FILE(GLOB cockatrice_TS "${CMAKE_CURRENT_SOURCE_DIR}/translations/*.ts")
ENDIF(UPDATE_TRANSLATIONS) ENDIF(UPDATE_TRANSLATIONS)
@ -153,30 +153,11 @@ if(APPLE)
set(cockatrice_SOURCES ${cockatrice_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns) set(cockatrice_SOURCES ${cockatrice_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns)
ENDIF(APPLE) ENDIF(APPLE)
# Qt5 IF(Qt6_FOUND)
find_package(Qt5 COMPONENTS Concurrent Multimedia Network PrintSupport Svg WebSockets Widgets REQUIRED) Qt6_ADD_RESOURCES(cockatrice_RESOURCES_RCC ${cockatrice_RESOURCES})
set(COCKATRICE_QT_MODULES Qt5::Concurrent Qt5::Multimedia Qt5::Network Qt5::PrintSupport Qt5::Svg Qt5::Widgets Qt5::WebSockets) ELSEIF(Qt5_FOUND)
Qt5_ADD_RESOURCES(cockatrice_RESOURCES_RCC ${cockatrice_RESOURCES})
# Qt5LinguistTools ENDIF()
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})
# Declare path variables # Declare path variables
set(ICONDIR share/icons CACHE STRING "icon dir") 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_BINARY_DIR}/common)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
# Build cockatrice binary and link it set(COCKATRICE_MAC_QM_INSTALL_DIR "cockatrice.app/Contents/Resources/translations")
ADD_EXECUTABLE(cockatrice WIN32 MACOSX_BUNDLE ${cockatrice_SOURCES} ${cockatrice_QM} ${cockatrice_RESOURCES_RCC} ${cockatrice_MOC_SRCS}) 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(UNIX)
if(APPLE) if(APPLE)
@ -205,27 +214,23 @@ if(UNIX)
set_target_properties(cockatrice PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/Info.plist) set_target_properties(cockatrice PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/Info.plist)
INSTALL(TARGETS cockatrice BUNDLE DESTINATION ./) INSTALL(TARGETS cockatrice BUNDLE DESTINATION ./)
INSTALL(FILES ${cockatrice_QM} DESTINATION ./cockatrice.app/Contents/Resources/translations)
else() else()
# Assume linux # Assume linux
INSTALL(TARGETS cockatrice RUNTIME DESTINATION bin/) 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.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}/resources/cockatrice.svg DESTINATION ${ICONDIR}/hicolor/scalable/apps)
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/cockatrice.desktop DESTINATION ${DESKTOPDIR}) INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/cockatrice.desktop DESTINATION ${DESKTOPDIR})
INSTALL(FILES ${cockatrice_QM} DESTINATION share/cockatrice/translations)
endif() endif()
elseif(WIN32) elseif(WIN32)
INSTALL(TARGETS cockatrice RUNTIME DESTINATION ./) INSTALL(TARGETS cockatrice RUNTIME DESTINATION ./)
INSTALL(FILES ${cockatrice_QM} DESTINATION ./translations)
endif() endif()
if(APPLE) if(APPLE)
# these needs to be relative to CMAKE_INSTALL_PREFIX # these needs to be relative to CMAKE_INSTALL_PREFIX
set(plugin_dest_dir cockatrice.app/Contents/Plugins) set(plugin_dest_dir cockatrice.app/Contents/Plugins)
set(qtconf_dest_dir cockatrice.app/Contents/Resources) 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 install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime
FILES_MATCHING FILES_MATCHING
PATTERN "*.dSYM" EXCLUDE PATTERN "*.dSYM" EXCLUDE
@ -236,6 +241,7 @@ if(APPLE)
PATTERN "platforms/*.dylib" PATTERN "platforms/*.dylib"
PATTERN "printsupport/*.dylib" PATTERN "printsupport/*.dylib"
PATTERN "styles/*.dylib" PATTERN "styles/*.dylib"
PATTERN "tls/*.dylib"
) )
install(CODE " install(CODE "
@ -261,9 +267,32 @@ if(WIN32)
install(DIRECTORY "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${CMAKE_BUILD_TYPE}/" DESTINATION ./ FILES_MATCHING PATTERN "*.dll") install(DIRECTORY "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${CMAKE_BUILD_TYPE}/" DESTINATION ./ FILES_MATCHING PATTERN "*.dll")
# 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 install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime FILES_MATCHING
FILES_MATCHING REGEX "(audio|iconengines|imageformats|platforms|printsupport|styles)/.*[^d]\\.dll") 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 " install(CODE "
file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths] file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths]
@ -284,3 +313,26 @@ Data = Resources\")
install(FILES ${WIN32SSLRUNTIME_LIBRARIES} DESTINATION ./) install(FILES ${WIN32SSLRUNTIME_LIBRARIES} DESTINATION ./)
endif() endif()
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()

View file

@ -48,6 +48,7 @@ AbstractClient::AbstractClient(QObject *parent)
qRegisterMetaType<QList<ServerInfo_User>>("QList<ServerInfo_User>"); qRegisterMetaType<QList<ServerInfo_User>>("QList<ServerInfo_User>");
qRegisterMetaType<Event_ReplayAdded>("Event_ReplayAdded"); qRegisterMetaType<Event_ReplayAdded>("Event_ReplayAdded");
qRegisterMetaType<QList<QString>>("missingFeatures"); qRegisterMetaType<QList<QString>>("missingFeatures");
qRegisterMetaType<PendingCommand *>("pendingCommand");
FeatureSet features; FeatureSet features;
features.initalizeFeatureList(clientFeatures); features.initalizeFeatureList(clientFeatures);

View file

@ -16,7 +16,7 @@
#include <QGraphicsScene> #include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent> #include <QGraphicsSceneMouseEvent>
#include <QPainter> #include <QPainter>
#include <cmath> #include <QtMath>
ArrowItem::ArrowItem(Player *_player, int _id, ArrowTarget *_startItem, ArrowTarget *_targetItem, const QColor &_color) ArrowItem::ArrowItem(Player *_player, int _id, ArrowTarget *_startItem, ArrowTarget *_targetItem, const QColor &_color)
: QGraphicsItem(), player(_player), id(_id), startItem(_startItem), targetItem(_targetItem), color(_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 arrowWidth = 15.0;
const double headWidth = 40.0; const double headWidth = 40.0;
const double headLength = 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; const double phi = 15;
if (!startItem) if (!startItem)
@ -86,7 +86,7 @@ void ArrowItem::updatePath(const QPointF &endPoint)
if (lineLength < 30) if (lineLength < 30)
path = QPainterPath(); path = QPainterPath();
else { else {
QPointF c(lineLength / 2, tan(phi * M_PI / 180) * lineLength); QPointF c(lineLength / 2, qTan(phi * M_PI / 180) * lineLength);
QPainterPath centerLine; QPainterPath centerLine;
centerLine.moveTo(0, 0); centerLine.moveTo(0, 0);
@ -97,22 +97,22 @@ void ArrowItem::updatePath(const QPointF &endPoint)
QLineF testLine(arrowBodyEndPoint, centerLine.pointAtPercent(percentage + 0.001)); QLineF testLine(arrowBodyEndPoint, centerLine.pointAtPercent(percentage + 0.001));
qreal alpha = testLine.angle() - 90; qreal alpha = testLine.angle() - 90;
QPointF endPoint1 = 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 = 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 = 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 = 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.quadTo(c, endPoint1);
path.lineTo(point1); path.lineTo(point1);
path.lineTo(QPointF(lineLength, 0)); path.lineTo(QPointF(lineLength, 0));
path.lineTo(point2); path.lineTo(point2);
path.lineTo(endPoint2); path.lineTo(endPoint2);
path.quadTo(c, 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(cos((phi - 90) * M_PI / 180), sin((phi - 90) * M_PI / 180))); path.lineTo(-arrowWidth / 2 * QPointF(qCos((phi - 90) * M_PI / 180), qSin((phi - 90) * M_PI / 180)));
} }
setPos(startPoint); setPos(startPoint);

View file

@ -23,7 +23,7 @@ bool CockatriceXml3Parser::getCanParseFile(const QString &fileName, QIODevice &d
QXmlStreamReader xml(&device); QXmlStreamReader xml(&device);
while (!xml.atEnd()) { while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::StartElement) { 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(); int version = xml.attributes().value("version").toString().toInt();
if (version == COCKATRICE_XML3_TAGVER) { if (version == COCKATRICE_XML3_TAGVER) {
return true; return true;
@ -52,12 +52,13 @@ void CockatriceXml3Parser::parseFile(QIODevice &device)
break; break;
} }
if (xml.name() == "sets") { auto name = xml.name().toString();
if (name == "sets") {
loadSetsFromXml(xml); loadSetsFromXml(xml);
} else if (xml.name() == "cards") { } else if (name == "cards") {
loadCardsFromXml(xml); loadCardsFromXml(xml);
} else if (xml.name() != "") { } else if (!name.isEmpty()) {
qDebug() << "[CockatriceXml3Parser] Unknown item" << xml.name() << ", trying to continue anyway"; qDebug() << "[CockatriceXml3Parser] Unknown item" << name << ", trying to continue anyway";
xml.skipCurrentElement(); xml.skipCurrentElement();
} }
} }
@ -72,26 +73,27 @@ void CockatriceXml3Parser::loadSetsFromXml(QXmlStreamReader &xml)
break; break;
} }
if (xml.name() == "set") { auto name = xml.name().toString();
if (name == "set") {
QString shortName, longName, setType; QString shortName, longName, setType;
QDate releaseDate; QDate releaseDate;
while (!xml.atEnd()) { while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement) { if (xml.readNext() == QXmlStreamReader::EndElement) {
break; break;
} }
name = xml.name().toString();
if (xml.name() == "name") { if (name == "name") {
shortName = xml.readElementText(QXmlStreamReader::IncludeChildElements); shortName = xml.readElementText(QXmlStreamReader::IncludeChildElements);
} else if (xml.name() == "longname") { } else if (name == "longname") {
longName = xml.readElementText(QXmlStreamReader::IncludeChildElements); longName = xml.readElementText(QXmlStreamReader::IncludeChildElements);
} else if (xml.name() == "settype") { } else if (name == "settype") {
setType = xml.readElementText(QXmlStreamReader::IncludeChildElements); setType = xml.readElementText(QXmlStreamReader::IncludeChildElements);
} else if (xml.name() == "releasedate") { } else if (name == "releasedate") {
releaseDate = releaseDate =
QDate::fromString(xml.readElementText(QXmlStreamReader::IncludeChildElements), Qt::ISODate); QDate::fromString(xml.readElementText(QXmlStreamReader::IncludeChildElements), Qt::ISODate);
} else if (xml.name() != "") { } else if (!name.isEmpty()) {
qDebug() << "[CockatriceXml3Parser] Unknown set property" << xml.name() qDebug() << "[CockatriceXml3Parser] Unknown set property" << name << ", trying to continue anyway";
<< ", trying to continue anyway";
xml.skipCurrentElement(); xml.skipCurrentElement();
} }
} }
@ -146,7 +148,8 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml)
break; break;
} }
if (xml.name() == "card") { auto xmlName = xml.name().toString();
if (xmlName == "card") {
QString name = QString(""); QString name = QString("");
QString text = QString(""); QString text = QString("");
QVariantHash properties = QVariantHash(); QVariantHash properties = QVariantHash();
@ -162,37 +165,39 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml)
if (xml.readNext() == QXmlStreamReader::EndElement) { if (xml.readNext() == QXmlStreamReader::EndElement) {
break; break;
} }
xmlName = xml.name().toString();
// variable - assigned properties // variable - assigned properties
if (xml.name() == "name") { if (xmlName == "name") {
name = xml.readElementText(QXmlStreamReader::IncludeChildElements); name = xml.readElementText(QXmlStreamReader::IncludeChildElements);
} else if (xml.name() == "text") { } else if (xmlName == "text") {
text = xml.readElementText(QXmlStreamReader::IncludeChildElements); text = xml.readElementText(QXmlStreamReader::IncludeChildElements);
} else if (xml.name() == "color") { } else if (xmlName == "color") {
colors.append(xml.readElementText(QXmlStreamReader::IncludeChildElements)); colors.append(xml.readElementText(QXmlStreamReader::IncludeChildElements));
} else if (xml.name() == "token") { } else if (xmlName == "token") {
isToken = static_cast<bool>(xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt()); isToken = static_cast<bool>(xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt());
// generic properties // generic properties
} else if (xml.name() == "manacost") { } else if (xmlName == "manacost") {
properties.insert("manacost", xml.readElementText(QXmlStreamReader::IncludeChildElements)); properties.insert("manacost", xml.readElementText(QXmlStreamReader::IncludeChildElements));
} else if (xml.name() == "cmc") { } else if (xmlName == "cmc") {
properties.insert("cmc", xml.readElementText(QXmlStreamReader::IncludeChildElements)); properties.insert("cmc", xml.readElementText(QXmlStreamReader::IncludeChildElements));
} else if (xml.name() == "type") { } else if (xmlName == "type") {
QString type = xml.readElementText(QXmlStreamReader::IncludeChildElements); QString type = xml.readElementText(QXmlStreamReader::IncludeChildElements);
properties.insert("type", type); properties.insert("type", type);
properties.insert("maintype", getMainCardType(type)); properties.insert("maintype", getMainCardType(type));
} else if (xml.name() == "pt") { } else if (xmlName == "pt") {
properties.insert("pt", xml.readElementText(QXmlStreamReader::IncludeChildElements)); properties.insert("pt", xml.readElementText(QXmlStreamReader::IncludeChildElements));
} else if (xml.name() == "loyalty") { } else if (xmlName == "loyalty") {
properties.insert("loyalty", xml.readElementText(QXmlStreamReader::IncludeChildElements)); properties.insert("loyalty", xml.readElementText(QXmlStreamReader::IncludeChildElements));
// positioning info // positioning info
} else if (xml.name() == "tablerow") { } else if (xmlName == "tablerow") {
tableRow = xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt(); tableRow = xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt();
} else if (xml.name() == "cipt") { } else if (xmlName == "cipt") {
cipt = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1"); cipt = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1");
} else if (xml.name() == "upsidedown") { } else if (xmlName == "upsidedown") {
upsideDown = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1"); upsideDown = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1");
// sets // sets
} else if (xml.name() == "set") { } else if (xmlName == "set") {
// NOTE: attributes must be read before readElementText() // NOTE: attributes must be read before readElementText()
QXmlStreamAttributes attrs = xml.attributes(); QXmlStreamAttributes attrs = xml.attributes();
QString setName = xml.readElementText(QXmlStreamReader::IncludeChildElements); QString setName = xml.readElementText(QXmlStreamReader::IncludeChildElements);
@ -217,8 +222,8 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml)
setInfo.setProperty("rarity", attrs.value("rarity").toString()); setInfo.setProperty("rarity", attrs.value("rarity").toString());
} }
sets.insert(setName, setInfo); sets.insert(setName, setInfo);
// relatd cards // related cards
} else if (xml.name() == "related" || xml.name() == "reverse-related") { } else if (xmlName == "related" || xmlName == "reverse-related") {
bool attach = false; bool attach = false;
bool exclude = false; bool exclude = false;
bool variable = false; bool variable = false;
@ -249,13 +254,13 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml)
} }
auto *relation = new CardRelation(cardName, attach, exclude, variable, count); auto *relation = new CardRelation(cardName, attach, exclude, variable, count);
if (xml.name() == "reverse-related") { if (xmlName == "reverse-related") {
reverseRelatedCards << relation; reverseRelatedCards << relation;
} else { } else {
relatedCards << relation; relatedCards << relation;
} }
} else if (xml.name() != "") { } else if (!xmlName.isEmpty()) {
qDebug() << "[CockatriceXml3Parser] Unknown card property" << xml.name() qDebug() << "[CockatriceXml3Parser] Unknown card property" << xmlName
<< ", trying to continue anyway"; << ", trying to continue anyway";
xml.skipCurrentElement(); xml.skipCurrentElement();
} }

View file

@ -23,7 +23,7 @@ bool CockatriceXml4Parser::getCanParseFile(const QString &fileName, QIODevice &d
QXmlStreamReader xml(&device); QXmlStreamReader xml(&device);
while (!xml.atEnd()) { while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::StartElement) { 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(); int version = xml.attributes().value("version").toString().toInt();
if (version == COCKATRICE_XML4_TAGVER) { if (version == COCKATRICE_XML4_TAGVER) {
return true; return true;
@ -52,12 +52,13 @@ void CockatriceXml4Parser::parseFile(QIODevice &device)
break; break;
} }
if (xml.name() == "sets") { auto xmlName = xml.name().toString();
if (xmlName == "sets") {
loadSetsFromXml(xml); loadSetsFromXml(xml);
} else if (xml.name() == "cards") { } else if (xmlName == "cards") {
loadCardsFromXml(xml); loadCardsFromXml(xml);
} else if (xml.name() != "") { } else if (!xmlName.isEmpty()) {
qDebug() << "[CockatriceXml4Parser] Unknown item" << xml.name() << ", trying to continue anyway"; qDebug() << "[CockatriceXml4Parser] Unknown item" << xmlName << ", trying to continue anyway";
xml.skipCurrentElement(); xml.skipCurrentElement();
} }
} }
@ -72,25 +73,27 @@ void CockatriceXml4Parser::loadSetsFromXml(QXmlStreamReader &xml)
break; break;
} }
if (xml.name() == "set") { auto xmlName = xml.name().toString();
if (xmlName == "set") {
QString shortName, longName, setType; QString shortName, longName, setType;
QDate releaseDate; QDate releaseDate;
while (!xml.atEnd()) { while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement) { if (xml.readNext() == QXmlStreamReader::EndElement) {
break; break;
} }
xmlName = xml.name().toString();
if (xml.name() == "name") { if (xmlName == "name") {
shortName = xml.readElementText(QXmlStreamReader::IncludeChildElements); shortName = xml.readElementText(QXmlStreamReader::IncludeChildElements);
} else if (xml.name() == "longname") { } else if (xmlName == "longname") {
longName = xml.readElementText(QXmlStreamReader::IncludeChildElements); longName = xml.readElementText(QXmlStreamReader::IncludeChildElements);
} else if (xml.name() == "settype") { } else if (xmlName == "settype") {
setType = xml.readElementText(QXmlStreamReader::IncludeChildElements); setType = xml.readElementText(QXmlStreamReader::IncludeChildElements);
} else if (xml.name() == "releasedate") { } else if (xmlName == "releasedate") {
releaseDate = releaseDate =
QDate::fromString(xml.readElementText(QXmlStreamReader::IncludeChildElements), Qt::ISODate); QDate::fromString(xml.readElementText(QXmlStreamReader::IncludeChildElements), Qt::ISODate);
} else if (xml.name() != "") { } else if (!xmlName.isEmpty()) {
qDebug() << "[CockatriceXml4Parser] Unknown set property" << xml.name() qDebug() << "[CockatriceXml4Parser] Unknown set property" << xmlName
<< ", trying to continue anyway"; << ", trying to continue anyway";
xml.skipCurrentElement(); xml.skipCurrentElement();
} }
@ -109,8 +112,9 @@ QVariantHash CockatriceXml4Parser::loadCardPropertiesFromXml(QXmlStreamReader &x
break; break;
} }
if (xml.name() != "") { auto xmlName = xml.name().toString();
properties.insert(xml.name().toString(), xml.readElementText(QXmlStreamReader::IncludeChildElements)); if (!xmlName.isEmpty()) {
properties.insert(xmlName, xml.readElementText(QXmlStreamReader::IncludeChildElements));
} }
} }
return properties; return properties;
@ -123,7 +127,9 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml)
break; break;
} }
if (xml.name() == "card") { auto xmlName = xml.name().toString();
if (xmlName == "card") {
QString name = QString(""); QString name = QString("");
QString text = QString(""); QString text = QString("");
QVariantHash properties = QVariantHash(); QVariantHash properties = QVariantHash();
@ -138,25 +144,28 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml)
if (xml.readNext() == QXmlStreamReader::EndElement) { if (xml.readNext() == QXmlStreamReader::EndElement) {
break; break;
} }
xmlName = xml.name().toString();
// variable - assigned properties // variable - assigned properties
if (xml.name() == "name") { if (xmlName == "name") {
name = xml.readElementText(QXmlStreamReader::IncludeChildElements); name = xml.readElementText(QXmlStreamReader::IncludeChildElements);
} else if (xml.name() == "text") { } else if (xmlName == "text") {
text = xml.readElementText(QXmlStreamReader::IncludeChildElements); text = xml.readElementText(QXmlStreamReader::IncludeChildElements);
} else if (xml.name() == "token") { } else if (xmlName == "token") {
isToken = static_cast<bool>(xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt()); isToken = static_cast<bool>(xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt());
// generic properties // generic properties
} else if (xml.name() == "prop") { } else if (xmlName == "prop") {
properties = loadCardPropertiesFromXml(xml); properties = loadCardPropertiesFromXml(xml);
// positioning info // positioning info
} else if (xml.name() == "tablerow") { } else if (xmlName == "tablerow") {
tableRow = xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt(); tableRow = xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt();
} else if (xml.name() == "cipt") { } else if (xmlName == "cipt") {
cipt = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1"); cipt = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1");
} else if (xml.name() == "upsidedown") { } else if (xmlName == "upsidedown") {
upsideDown = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1"); upsideDown = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1");
// sets // sets
} else if (xml.name() == "set") { } else if (xmlName == "set") {
// NOTE: attributes but be read before readElementText() // NOTE: attributes but be read before readElementText()
QXmlStreamAttributes attrs = xml.attributes(); QXmlStreamAttributes attrs = xml.attributes();
QString setName = xml.readElementText(QXmlStreamReader::IncludeChildElements); QString setName = xml.readElementText(QXmlStreamReader::IncludeChildElements);
@ -171,8 +180,8 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml)
} }
sets.insert(setName, setInfo); sets.insert(setName, setInfo);
} }
// relatd cards // related cards
} else if (xml.name() == "related" || xml.name() == "reverse-related") { } else if (xmlName == "related" || xmlName == "reverse-related") {
bool attach = false; bool attach = false;
bool exclude = false; bool exclude = false;
bool variable = false; bool variable = false;
@ -203,13 +212,13 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml)
} }
auto *relation = new CardRelation(cardName, attach, exclude, variable, count); auto *relation = new CardRelation(cardName, attach, exclude, variable, count);
if (xml.name() == "reverse-related") { if (xmlName == "reverse-related") {
reverseRelatedCards << relation; reverseRelatedCards << relation;
} else { } else {
relatedCards << relation; relatedCards << relation;
} }
} else if (xml.name() != "") { } else if (!xmlName.isEmpty()) {
qDebug() << "[CockatriceXml4Parser] Unknown card property" << xml.name() qDebug() << "[CockatriceXml4Parser] Unknown card property" << xmlName
<< ", trying to continue anyway"; << ", trying to continue anyway";
xml.skipCurrentElement(); xml.skipCurrentElement();
} }

View file

@ -6,13 +6,9 @@
#include "main.h" #include "main.h"
#include <QApplication> #include <QApplication>
#include <utility>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0))
#include <QScreen> #include <QScreen>
#else
#include <QDesktopWidget>
#endif
#include <QVBoxLayout> #include <QVBoxLayout>
#include <utility>
CardInfoWidget::CardInfoWidget(const QString &cardName, QWidget *parent, Qt::WindowFlags flags) CardInfoWidget::CardInfoWidget(const QString &cardName, QWidget *parent, Qt::WindowFlags flags)
: QFrame(parent, flags), aspectRatio((qreal)CARD_HEIGHT / (qreal)CARD_WIDTH), info(nullptr) : 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); setFrameStyle(QFrame::Panel | QFrame::Raised);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) int pixmapHeight = QGuiApplication::primaryScreen()->geometry().height() / 3;
int pixmapHeight = qApp->primaryScreen()->geometry().height() / 3;
#else
QDesktopWidget desktopWidget;
int pixmapHeight = desktopWidget.screenGeometry().height() / 3;
#endif
int pixmapWidth = static_cast<int>(pixmapHeight / aspectRatio); int pixmapWidth = static_cast<int>(pixmapHeight / aspectRatio);
pic->setFixedWidth(pixmapWidth); pic->setFixedWidth(pixmapWidth);
pic->setFixedHeight(pixmapHeight); pic->setFixedHeight(pixmapHeight);

View file

@ -2,6 +2,7 @@
#define CARDITEM_H #define CARDITEM_H
#include "abstractcarditem.h" #include "abstractcarditem.h"
#include "server_card.h"
class CardDatabase; class CardDatabase;
class CardDragItem; class CardDragItem;

View file

@ -321,7 +321,7 @@ void ChatView::checkTag(QTextCursor &cursor, QString &message)
void ChatView::checkMention(QTextCursor &cursor, QString &message, const QString &userName, UserLevelFlags userLevel) 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(' '); int firstSpace = message.indexOf(' ');
QString fullMentionUpToSpaceOrEnd = (firstSpace == -1) ? message.mid(1) : message.mid(1, firstSpace - 1); 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*/) void ChatView::enterEvent(QEvent * /*event*/)
#endif
{ {
setMouseTracking(true); setMouseTracking(true);
} }
@ -566,7 +570,11 @@ void ChatView::mousePressEvent(QMouseEvent *event)
switch (hoveredItemType) { switch (hoveredItemType) {
case HoveredCard: { case HoveredCard: {
if ((event->button() == Qt::MiddleButton) || (event->button() == Qt::LeftButton)) 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); emit showCardInfoPopup(event->globalPos(), hoveredContent);
#endif
break; break;
} }
case HoveredUser: { case HoveredUser: {
@ -576,7 +584,11 @@ void ChatView::mousePressEvent(QMouseEvent *event)
switch (event->button()) { switch (event->button()) {
case Qt::RightButton: { case Qt::RightButton: {
UserLevelFlags userLevel(hoveredContent.left(delimiterIndex).toInt()); 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); userContextMenu->showContextMenu(event->globalPos(), userName, userLevel, this);
#endif
break; break;
} }
case Qt::LeftButton: { case Qt::LeftButton: {

View file

@ -103,7 +103,11 @@ public:
void redactMessages(const QString &userName, int amount); void redactMessages(const QString &userName, int amount);
protected: protected:
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
void enterEvent(QEnterEvent *event);
#else
void enterEvent(QEvent *event); void enterEvent(QEvent *event);
#endif
void leaveEvent(QEvent *event); void leaveEvent(QEvent *event);
void mouseMoveEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event); void mousePressEvent(QMouseEvent *event);

View file

@ -6,6 +6,7 @@
#include <QDebug> #include <QDebug>
#include <QFile> #include <QFile>
#include <QRegularExpression>
#include <QStringList> #include <QStringList>
const QStringList DeckLoader::fileNameFilters = QStringList() const QStringList DeckLoader::fileNameFilters = QStringList()
@ -208,7 +209,7 @@ void DeckLoader::saveToStream_DeckHeader(QTextStream &out)
} }
if (!getComments().isEmpty()) { 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) { foreach (QString row, commentRows) {
out << "// " << row << "\n"; out << "// " << row << "\n";
} }

View file

@ -9,8 +9,8 @@
#include <QApplication> #include <QApplication>
#include <QGraphicsSceneMouseEvent> #include <QGraphicsSceneMouseEvent>
#include <QMouseEvent> #include <QMouseEvent>
#include <QtMath>
#include <algorithm> #include <algorithm>
#include <math.h>
DeckViewCardDragItem::DeckViewCardDragItem(DeckViewCard *_item, DeckViewCardDragItem::DeckViewCardDragItem(DeckViewCard *_item,
const QPointF &_hotSpot, const QPointF &_hotSpot,
@ -238,11 +238,9 @@ int DeckViewCardContainer::getCardTypeTextWidth() const
QFontMetrics fm(f); QFontMetrics fm(f);
int maxCardTypeWidth = 0; int maxCardTypeWidth = 0;
QMapIterator<QString, DeckViewCard *> i(cardsByType); for (const auto &key : cardsByType.keys()) {
while (i.hasNext()) { int cardTypeWidth = fm.size(Qt::TextSingleLine, key).width();
int cardTypeWidth = fm.size(Qt::TextSingleLine, i.next().key()).width(); maxCardTypeWidth = qMax(maxCardTypeWidth, cardTypeWidth);
if (cardTypeWidth > maxCardTypeWidth)
maxCardTypeWidth = cardTypeWidth;
} }
return maxCardTypeWidth + 15; return maxCardTypeWidth + 15;
@ -440,7 +438,7 @@ void DeckViewScene::rearrangeItems()
const int maxRows = rowsAndColsList[maxIndex1][maxIndex2].first; const int maxRows = rowsAndColsList[maxIndex1][maxIndex2].first;
const int maxCardCount = cardCountList[maxIndex1][maxIndex2]; const int maxCardCount = cardCountList[maxIndex1][maxIndex2];
rowsAndColsList[maxIndex1][maxIndex2] = rowsAndColsList[maxIndex1][maxIndex2] =
QPair<int, int>(maxRows + 1, (int)ceil((qreal)maxCardCount / (qreal)(maxRows + 1))); QPair<int, int>(maxRows + 1, (int)qCeil((qreal)maxCardCount / (qreal)(maxRows + 1)));
} }
totalHeight = -spacing; totalHeight = -spacing;

View file

@ -162,8 +162,7 @@ DlgConnect::DlgConnect(QWidget *parent) : QDialog(parent)
previousHostButton->setChecked(true); previousHostButton->setChecked(true);
connect(previousHosts, SIGNAL(currentIndexChanged(const QString &)), this, connect(previousHosts, SIGNAL(currentTextChanged(const QString &)), this, SLOT(updateDisplayInfo(const QString &)));
SLOT(updateDisplayInfo(const QString &)));
playernameEdit->setFocus(); playernameEdit->setFocus();
} }

View file

@ -121,7 +121,12 @@ WndSets::WndSets(QWidget *parent) : QMainWindow(parent)
connect(disableSomeButton, SIGNAL(clicked()), this, SLOT(actDisableSome())); connect(disableSomeButton, SIGNAL(clicked()), this, SLOT(actDisableSome()));
connect(view->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), this, connect(view->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), this,
SLOT(actToggleButtons(const QItemSelection &, const QItemSelection &))); 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 &))); 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(view->header(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)), this, SLOT(actDisableSortButtons(int)));
connect(searchField, SIGNAL(textChanged(const QString &)), this, SLOT(actDisableResetButton(const QString &))); 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") + "</a>")); tr("How to use custom card art") + "</a>"));
QGridLayout *hintsGrid = new QGridLayout; QGridLayout *hintsGrid = new QGridLayout;
hintsGrid->setMargin(2);
hintsGrid->addWidget(labNotes, 0, 0); hintsGrid->addWidget(labNotes, 0, 0);
hintsGroupBox = new QGroupBox(tr("Hints")); hintsGroupBox = new QGroupBox(tr("Hints"));
hintsGroupBox->setLayout(hintsGrid); hintsGroupBox->setLayout(hintsGrid);

View file

@ -17,7 +17,6 @@
#include <QComboBox> #include <QComboBox>
#include <QDebug> #include <QDebug>
#include <QDesktopServices> #include <QDesktopServices>
#include <QDesktopWidget>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QFileDialog> #include <QFileDialog>
#include <QGridLayout> #include <QGridLayout>
@ -44,18 +43,22 @@
GeneralSettingsPage::GeneralSettingsPage() GeneralSettingsPage::GeneralSettingsPage()
{ {
SettingsCache &settings = SettingsCache::instance(); QStringList languageCodes = findQmFiles();
QString setLanguage = settings.getLang(); for (const QString &code : languageCodes) {
QStringList qmFiles = findQmFiles(); QString langName = languageName(code);
for (int i = 0; i < qmFiles.size(); i++) { languageBox.addItem(langName, code);
QString langName = languageName(qmFiles[i]); }
languageBox.addItem(langName, qmFiles[i]);
if ((qmFiles[i] == setLanguage) || QString setLanguage = QCoreApplication::translate("i18n", DEFAULT_LANG_NAME);
(setLanguage.isEmpty() && langName == QCoreApplication::translate("i18n", DEFAULT_LANG_NAME))) int index = languageBox.findText(setLanguage, Qt::MatchExactly);
languageBox.setCurrentIndex(i); if (index == -1) {
qWarning() << "could not find language" << setLanguage;
} else {
languageBox.setCurrentIndex(index);
} }
// updates // updates
SettingsCache &settings = SettingsCache::instance();
QList<ReleaseChannel *> channels = settings.getUpdateReleaseChannels(); QList<ReleaseChannel *> channels = settings.getUpdateReleaseChannels();
foreach (ReleaseChannel *chan, channels) { foreach (ReleaseChannel *chan, channels) {
updateReleaseChannelBox.insertItem(chan->getIndex(), tr(chan->getName().toUtf8())); updateReleaseChannelBox.insertItem(chan->getIndex(), tr(chan->getName().toUtf8()));
@ -186,15 +189,20 @@ QStringList GeneralSettingsPage::findQmFiles()
{ {
QDir dir(translationPath); QDir dir(translationPath);
QStringList fileNames = dir.entryList(QStringList(translationPrefix + "_*.qm"), QDir::Files, QDir::Name); QStringList fileNames = dir.entryList(QStringList(translationPrefix + "_*.qm"), QDir::Files, QDir::Name);
fileNames.replaceInStrings(QRegExp(translationPrefix + "_(.*)\\.qm"), "\\1"); fileNames.replaceInStrings(QRegularExpression(translationPrefix + "_(.*)\\.qm"), "\\1");
fileNames.removeOne("en@source");
return fileNames; return fileNames;
} }
QString GeneralSettingsPage::languageName(const QString &qmFile) QString GeneralSettingsPage::languageName(const QString &lang)
{ {
QTranslator qTranslator; 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); return qTranslator.translate("i18n", DEFAULT_LANG_NAME);
} }
@ -1235,11 +1243,7 @@ void ShortcutSettingsPage::retranslateUi()
DlgSettings::DlgSettings(QWidget *parent) : QDialog(parent) DlgSettings::DlgSettings(QWidget *parent) : QDialog(parent)
{ {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) auto rec = QGuiApplication::primaryScreen()->availableGeometry();
auto rec = qApp->primaryScreen()->availableGeometry();
#else
auto rec = QApplication::desktop()->availableGeometry();
#endif
this->setMinimumSize(qMin(700, rec.width()), qMin(700, rec.height())); this->setMinimumSize(qMin(700, rec.width()), qMin(700, rec.height()));
connect(&SettingsCache::instance(), SIGNAL(langChanged()), this, SLOT(updateLanguage())); connect(&SettingsCache::instance(), SIGNAL(langChanged()), this, SLOT(updateLanguage()));

View file

@ -49,7 +49,7 @@ private slots:
private: private:
QStringList findQmFiles(); QStringList findQmFiles();
QString languageName(const QString &qmFile); QString languageName(const QString &lang);
QLineEdit *deckPathEdit; QLineEdit *deckPathEdit;
QLineEdit *replaysPathEdit; QLineEdit *replaysPathEdit;
QLineEdit *picsPathEdit; QLineEdit *picsPathEdit;

View file

@ -4,7 +4,6 @@
#include <QByteArray> #include <QByteArray>
#include <QString> #include <QString>
#include <cmath>
#include <functional> #include <functional>
peg::parser search(R"( peg::parser search(R"(

View file

@ -13,7 +13,7 @@
#include <QGraphicsSceneMouseEvent> #include <QGraphicsSceneMouseEvent>
#include <QGraphicsView> #include <QGraphicsView>
#include <QSet> #include <QSet>
#include <math.h> #include <QtMath>
GameScene::GameScene(PhasesToolbar *_phasesToolbar, QObject *parent) GameScene::GameScene(PhasesToolbar *_phasesToolbar, QObject *parent)
: QGraphicsScene(parent), phasesToolbar(_phasesToolbar), viewSize(QSize()), playerRotation(0) : QGraphicsScene(parent), phasesToolbar(_phasesToolbar), viewSize(QSize()), playerRotation(0)
@ -97,7 +97,7 @@ void GameScene::rearrange()
const int playersCount = playersPlaying.size(); const int playersCount = playersPlaying.size();
const int columns = playersCount < SettingsCache::instance().getMinPlayersForMultiColumnLayout() ? 1 : 2; 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; qreal sceneHeight = 0, sceneWidth = -playerAreaSpacing;
QList<int> columnWidth; QList<int> columnWidth;

View file

@ -93,11 +93,7 @@ QVariant GamesModel::data(const QModelIndex &index, int role) const
case CREATED: { case CREATED: {
switch (role) { switch (role) {
case Qt::DisplayRole: { case Qt::DisplayRole: {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
QDateTime then = QDateTime::fromSecsSinceEpoch(gameentry.start_time(), Qt::UTC); 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()); int secs = then.secsTo(QDateTime::currentDateTimeUtc());
return getGameCreatedString(secs); return getGameCreatedString(secs);
} }

View file

@ -24,8 +24,10 @@ void HandZone::updateBg()
void HandZone::addCardImpl(CardItem *card, int x, int /*y*/) 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(); x = cards.size();
}
cards.insert(x, card); cards.insert(x, card);
if (!cards.getContentsKnown()) { if (!cards.getContentsKnown()) {

View file

@ -12,8 +12,9 @@ LocalServer::LocalServer(QObject *parent) : Server(parent)
LocalServer::~LocalServer() LocalServer::~LocalServer()
{ {
// LocalServer is single threaded so it doesn't need locks on this // LocalServer is single threaded so it doesn't need locks on this
while (!clients.isEmpty()) for (auto *client : clients) {
clients.first()->prepareDestroy(); client->prepareDestroy();
}
prepareDestroy(); prepareDestroy();
} }

View file

@ -27,6 +27,7 @@ protected:
public: public:
LocalServer_DatabaseInterface(LocalServer *_localServer); LocalServer_DatabaseInterface(LocalServer *_localServer);
~LocalServer_DatabaseInterface() = default;
AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler,
const QString &user, const QString &user,
const QString &password, const QString &password,

View file

@ -67,11 +67,29 @@ void installNewTranslator()
{ {
QString lang = SettingsCache::instance().getLang(); 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); 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); qApp->installTranslator(translator);
qDebug() << "Language changed:" << lang;
} }
QString const generateClientID() QString const generateClientID()
@ -165,7 +183,9 @@ int main(int argc, char *argv[])
ui.show(); ui.show();
qDebug("main(): ui.show() finished"); qDebug("main(): ui.show() finished");
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
app.setAttribute(Qt::AA_UseHighDpiPixmaps); app.setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
app.exec(); app.exec();
qDebug("Event loop finished, terminating..."); qDebug("Event loop finished, terminating...");

View file

@ -44,6 +44,10 @@ void PileZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*optio
void PileZone::addCardImpl(CardItem *card, int x, int /*y*/) void PileZone::addCardImpl(CardItem *card, int x, int /*y*/)
{ {
connect(card, SIGNAL(sigPixmapUpdated()), this, SLOT(callUpdate())); 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); cards.insert(x, card);
card->setPos(0, 0); card->setPos(0, 0);
if (!contentsKnown()) { if (!contentsKnown()) {

View file

@ -99,7 +99,7 @@ QMap<QString, QPixmap> CountryPixmapGenerator::pmCache;
QPixmap UserLevelPixmapGenerator::generatePixmap(int height, UserLevelFlags userLevel, bool isBuddy, QString privLevel) 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)) if (pmCache.contains(key))
return pmCache.value(key); return pmCache.value(key);

View file

@ -66,6 +66,7 @@
#include <QDebug> #include <QDebug>
#include <QMenu> #include <QMenu>
#include <QMetaType>
#include <QPainter> #include <QPainter>
#include <QRegularExpression> #include <QRegularExpression>
#include <QRegularExpressionMatch> #include <QRegularExpressionMatch>
@ -3066,7 +3067,11 @@ void Player::actSetPT()
const auto oldpt = parsePT(card->getPT()); const auto oldpt = parsePT(card->getPT());
int ptIter = 0; int ptIter = 0;
for (const auto &item : ptList) { 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) { if (item.type() == QVariant::Int) {
#endif
int oldItem = ptIter < oldpt.size() ? oldpt.at(ptIter).toInt() : 0; int oldItem = ptIter < oldpt.size() ? oldpt.at(ptIter).toInt() : 0;
newpt += '/' + QString::number(oldItem + item.toInt()); newpt += '/' + QString::number(oldItem + item.toInt());
} else { } else {

View file

@ -28,7 +28,11 @@ bool PlayerListItemDelegate::editorEvent(QEvent *event,
if ((event->type() == QEvent::MouseButtonPress) && index.isValid()) { if ((event->type() == QEvent::MouseButtonPress) && index.isValid()) {
QMouseEvent *const mouseEvent = static_cast<QMouseEvent *>(event); QMouseEvent *const mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent->button() == Qt::RightButton) { if (mouseEvent->button() == Qt::RightButton) {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
static_cast<PlayerListWidget *>(parent())->showContextMenu(mouseEvent->globalPosition().toPoint(), index);
#else
static_cast<PlayerListWidget *>(parent())->showContextMenu(mouseEvent->globalPos(), index); static_cast<PlayerListWidget *>(parent())->showContextMenu(mouseEvent->globalPos(), index);
#endif
return true; return true;
} }
} }

View file

@ -23,10 +23,9 @@
int ReleaseChannel::sharedIndex = 0; int ReleaseChannel::sharedIndex = 0;
ReleaseChannel::ReleaseChannel() : response(nullptr), lastRelease(nullptr) ReleaseChannel::ReleaseChannel() : netMan(new QNetworkAccessManager(this)), response(nullptr), lastRelease(nullptr)
{ {
index = sharedIndex++; index = sharedIndex++;
netMan = new QNetworkAccessManager(this);
} }
ReleaseChannel::~ReleaseChannel() ReleaseChannel::~ReleaseChannel()

View file

@ -78,7 +78,7 @@ class ReleaseChannel : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
ReleaseChannel(); explicit ReleaseChannel();
~ReleaseChannel() override; ~ReleaseChannel() override;
protected: protected:
@ -117,7 +117,7 @@ class StableReleaseChannel : public ReleaseChannel
{ {
Q_OBJECT Q_OBJECT
public: public:
StableReleaseChannel() = default; explicit StableReleaseChannel() = default;
~StableReleaseChannel() override = default; ~StableReleaseChannel() override = default;
QString getManualDownloadUrl() const override; QString getManualDownloadUrl() const override;

View file

@ -41,8 +41,13 @@ RemoteClient::RemoteClient(QObject *parent)
socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
connect(socket, SIGNAL(connected()), this, SLOT(slotConnected())); connect(socket, SIGNAL(connected()), this, SLOT(slotConnected()));
connect(socket, SIGNAL(readyRead()), this, SLOT(readData())); 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, connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this,
SLOT(slotSocketError(QAbstractSocket::SocketError))); SLOT(slotSocketError(QAbstractSocket::SocketError)));
#endif
websocket = new QWebSocket(QString(), QWebSocketProtocol::VersionLatest, this); websocket = new QWebSocket(QString(), QWebSocketProtocol::VersionLatest, this);
connect(websocket, &QWebSocket::binaryMessageReceived, this, &RemoteClient::websocketMessageReceived); connect(websocket, &QWebSocket::binaryMessageReceived, this, &RemoteClient::websocketMessageReceived);

View file

@ -221,7 +221,7 @@ void RemoteDeckList_TreeModel::addFileToTree(const ServerInfo_DeckStorage_TreeIt
{ {
const ServerInfo_DeckStorage_File &fileInfo = file.file(); const ServerInfo_DeckStorage_File &fileInfo = file.file();
QDateTime time; QDateTime time;
time.setTime_t(fileInfo.creation_time()); time.setSecsSinceEpoch(fileInfo.creation_time());
beginInsertRows(nodeToIndex(parent), parent->size(), parent->size()); beginInsertRows(nodeToIndex(parent), parent->size(), parent->size());
parent->append(new FileNode(QString::fromStdString(file.name()), file.id(), time, parent)); parent->append(new FileNode(QString::fromStdString(file.name()), file.id(), time, parent));

View file

@ -111,7 +111,7 @@ QVariant RemoteReplayList_TreeModel::data(const QModelIndex &index, int role) co
return playerList.join(", "); return playerList.join(", ");
} }
case 4: case 4:
return QDateTime::fromTime_t(matchInfo.time_started()); return QDateTime::fromSecsSinceEpoch(matchInfo.time_started());
case 5: case 5:
return matchInfo.length(); return matchInfo.length();
default: default:

View file

@ -62,7 +62,11 @@ void ReplayTimelineWidget::paintEvent(QPaintEvent * /* event */)
void ReplayTimelineWidget::mousePressEvent(QMouseEvent *event) void ReplayTimelineWidget::mousePressEvent(QMouseEvent *event)
{ {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
int newTime = static_cast<int>((qint64)maxTime * (qint64)event->position().x() / width());
#else
int newTime = static_cast<int>((qint64)maxTime * (qint64)event->x() / width()); int newTime = static_cast<int>((qint64)maxTime * (qint64)event->x() / width());
#endif
newTime -= newTime % 200; // Time should always be a multiple of 200 newTime -= newTime % 200; // Time should always be a multiple of 200
if (newTime < currentTime) { if (newTime < currentTime) {
currentTime = 0; currentTime = 0;

View file

@ -1,5 +1,7 @@
#include "setsmodel.h" #include "setsmodel.h"
#include <QSortFilterProxyModel>
SetsModel::SetsModel(CardDatabase *_db, QObject *parent) : QAbstractTableModel(parent), sets(_db->getSetList()) SetsModel::SetsModel(CardDatabase *_db, QObject *parent) : QAbstractTableModel(parent), sets(_db->getSetList())
{ {
sets.sortByKey(); sets.sortByKey();
@ -274,9 +276,15 @@ bool SetsDisplayModel::filterAcceptsRow(int sourceRow, const QModelIndex &source
auto nameIndex = sourceModel()->index(sourceRow, SetsModel::LongNameCol, sourceParent); auto nameIndex = sourceModel()->index(sourceRow, SetsModel::LongNameCol, sourceParent);
auto shortNameIndex = sourceModel()->index(sourceRow, SetsModel::ShortNameCol, sourceParent); auto shortNameIndex = sourceModel()->index(sourceRow, SetsModel::ShortNameCol, sourceParent);
return (sourceModel()->data(typeIndex).toString().contains(filterRegExp()) || #if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
sourceModel()->data(nameIndex).toString().contains(filterRegExp()) || const auto filter = filterRegularExpression();
sourceModel()->data(shortNameIndex).toString().contains(filterRegExp())); #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 bool SetsDisplayModel::lessThan(const QModelIndex &left, const QModelIndex &right) const

View file

@ -18,7 +18,7 @@ QString SettingsCache::getDataPath()
if (isPortableBuild) if (isPortableBuild)
return qApp->applicationDirPath() + "/data"; return qApp->applicationDirPath() + "/data";
else else
return QStandardPaths::writableLocation(QStandardPaths::DataLocation); return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
} }
QString SettingsCache::getSettingsPath() QString SettingsCache::getSettingsPath()

View file

@ -2,21 +2,15 @@
#include "settingscache.h" #include "settingscache.h"
#include <QApplication>
#include <QAudioOutput> #include <QAudioOutput>
#include <QBuffer> #include <QDir>
#include <QDebug> #include <QMediaPlayer>
#include <QFileInfo>
#include <QLibraryInfo>
#include <QStandardPaths>
#define DEFAULT_THEME_NAME "Default" #define DEFAULT_THEME_NAME "Default"
#define TEST_SOUND_FILENAME "player_join" #define TEST_SOUND_FILENAME "player_join"
SoundEngine::SoundEngine(QObject *parent) : QObject(parent), player(0) SoundEngine::SoundEngine(QObject *parent) : QObject(parent), player(0)
{ {
inputBuffer = new QBuffer(this);
ensureThemeDirectoryExists(); ensureThemeDirectoryExists();
connect(&SettingsCache::instance(), SIGNAL(soundThemeChanged()), this, SLOT(themeChangedSlot())); connect(&SettingsCache::instance(), SIGNAL(soundThemeChanged()), this, SLOT(themeChangedSlot()));
connect(&SettingsCache::instance(), SIGNAL(soundEnabledChanged()), this, SLOT(soundEnabledChanged())); connect(&SettingsCache::instance(), SIGNAL(soundEnabledChanged()), this, SLOT(soundEnabledChanged()));
@ -31,8 +25,6 @@ SoundEngine::~SoundEngine()
player->deleteLater(); player->deleteLater();
player = 0; player = 0;
} }
inputBuffer->deleteLater();
} }
void SoundEngine::soundEnabledChanged() void SoundEngine::soundEnabledChanged()
@ -40,14 +32,11 @@ void SoundEngine::soundEnabledChanged()
if (SettingsCache::instance().getSoundEnabled()) { if (SettingsCache::instance().getSoundEnabled()) {
qDebug("SoundEngine: enabling sound"); qDebug("SoundEngine: enabling sound");
if (!player) { if (!player) {
QAudioFormat format; player = new QMediaPlayer;
format.setSampleRate(44100); #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
format.setChannelCount(1); auto qAudioOutput = new QAudioOutput;
format.setSampleSize(16); player->setAudioOutput(qAudioOutput);
format.setCodec("audio/pcm"); #endif
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::SignedInt);
player = new QAudioOutput(format, this);
} }
} else { } else {
qDebug("SoundEngine: disabling sound"); qDebug("SoundEngine: disabling sound");
@ -61,25 +50,35 @@ void SoundEngine::soundEnabledChanged()
void SoundEngine::playSound(QString fileName) void SoundEngine::playSound(QString fileName)
{ {
if (!player) if (!player) {
return; return;
}
// still playing the previous sound? // 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; return;
}
if (!audioData.contains(fileName)) if (!audioData.contains(fileName)) {
return; return;
}
qDebug() << "playing" << fileName; qDebug() << "playing" << fileName;
inputBuffer->close(); #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
inputBuffer->setData(audioData[fileName]); player->audioOutput()->setVolume(SettingsCache::instance().getMasterVolume());
inputBuffer->open(QIODevice::ReadOnly);
player->setVolume(SettingsCache::instance().getMasterVolume() / 100.0);
player->stop(); 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() void SoundEngine::testSound()
@ -105,7 +104,7 @@ QStringMap &SoundEngine::getAvailableThemes()
dir.setPath(SettingsCache::instance().getDataPath() + "/sounds"); 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)) if (!availableThemes.contains(themeName))
availableThemes.insert(themeName, dir.absoluteFilePath(themeName)); availableThemes.insert(themeName, dir.absoluteFilePath(themeName));
} }
@ -121,7 +120,7 @@ QStringMap &SoundEngine::getAvailableThemes()
#endif #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)) if (!availableThemes.contains(themeName))
availableThemes.insert(themeName, dir.absoluteFilePath(themeName)); availableThemes.insert(themeName, dir.absoluteFilePath(themeName));
} }
@ -181,10 +180,7 @@ void SoundEngine::themeChangedSlot()
continue; continue;
QFile file(dir.filePath(fileNames[i] + ".wav")); QFile file(dir.filePath(fileNames[i] + ".wav"));
file.open(QIODevice::ReadOnly); audioData.insert(fileNames[i], file.fileName());
// 44 = length of wav header
audioData.insert(fileNames[i], file.readAll().mid(44));
file.close();
} }
soundEnabledChanged(); soundEnabledChanged();

View file

@ -1,8 +1,8 @@
#ifndef SOUNDENGINE_H #ifndef SOUNDENGINE_H
#define SOUNDENGINE_H #define SOUNDENGINE_H
#include <QDir>
#include <QMap> #include <QMap>
#include <QMediaPlayer>
#include <QObject> #include <QObject>
#include <QString> #include <QString>
@ -21,10 +21,9 @@ public:
QStringMap &getAvailableThemes(); QStringMap &getAvailableThemes();
private: private:
QMap<QString, QByteArray> audioData;
QBuffer *inputBuffer;
QAudioOutput *player;
QStringMap availableThemes; QStringMap availableThemes;
QMap<QString, QString> audioData;
QMediaPlayer *player;
protected: protected:
void ensureThemeDirectoryExists(); void ensureThemeDirectoryExists();

View file

@ -114,7 +114,7 @@ void SpoilerBackgroundUpdater::actCheckIfSpoilerSeasonEnabled()
emit spoilerCheckerDone(); emit spoilerCheckerDone();
} else { } else {
if (trayIcon) { 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; qDebug() << "Spoiler download failed with reason" << errorCode;
@ -159,7 +159,7 @@ bool SpoilerBackgroundUpdater::saveDownloadedFile(QByteArray data)
// Data written, so reload the card database // Data written, so reload the card database
qDebug() << "Spoiler Service Data Written"; 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 // If the user has notifications enabled, let them know
// when the database was last updated // when the database was last updated

View file

@ -26,8 +26,10 @@ void StackZone::updateBg()
void StackZone::addCardImpl(CardItem *card, int x, int /*y*/) 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(); x = cards.size();
}
cards.insert(x, card); cards.insert(x, card);
if (!cards.getContentsKnown()) { if (!cards.getContentsKnown()) {

View file

@ -4,7 +4,8 @@
#include <QApplication> #include <QApplication>
#include <QDebug> #include <QDebug>
#include <QDesktopWidget> #include <QScreen>
Tab::Tab(TabSupervisor *_tabSupervisor, QWidget *parent) Tab::Tab(TabSupervisor *_tabSupervisor, QWidget *parent)
: QMainWindow(parent), tabSupervisor(_tabSupervisor), contentsChanged(false), infoPopup(0) : QMainWindow(parent), tabSupervisor(_tabSupervisor), contentsChanged(false), infoPopup(0)
{ {
@ -20,7 +21,8 @@ void Tab::showCardInfoPopup(const QPoint &pos, const QString &cardName)
infoPopup = new CardInfoWidget( infoPopup = new CardInfoWidget(
cardName, 0, Qt::Widget | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint | Qt::WindowStaysOnTopHint); cardName, 0, Qt::Widget | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint | Qt::WindowStaysOnTopHint);
infoPopup->setAttribute(Qt::WA_TransparentForMouseEvents); 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, infoPopup->move(qMax(screenRect.left(), qMin(pos.x() - infoPopup->width() / 2,
screenRect.left() + screenRect.width() - infoPopup->width())), screenRect.left() + screenRect.width() - infoPopup->width())),
qMax(screenRect.top(), qMin(pos.y() - infoPopup->height() / 2, qMax(screenRect.top(), qMin(pos.y() - infoPopup->height() / 2,

View file

@ -52,7 +52,7 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor,
SIGNAL(openMessageDialog(const QString &, bool))); SIGNAL(openMessageDialog(const QString &, bool)));
chatView = new ChatView(tabSupervisor, tabSupervisor, nullptr, true, this); 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(messageClickedSignal()), this, SLOT(focusTab()));
connect(chatView, SIGNAL(openMessageDialog(QString, bool)), this, SIGNAL(openMessageDialog(QString, bool))); connect(chatView, SIGNAL(openMessageDialog(QString, bool)), this, SIGNAL(openMessageDialog(QString, bool)));
connect(chatView, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString))); connect(chatView, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString)));
@ -160,7 +160,7 @@ void TabRoom::focusTab()
emit maximizeClient(); emit maximizeClient();
} }
void TabRoom::actShowMentionPopup(QString &sender) void TabRoom::actShowMentionPopup(const QString &sender)
{ {
this->actShowPopup(sender + tr(" mentioned you.")); this->actShowPopup(sender + tr(" mentioned you."));
} }

View file

@ -75,7 +75,7 @@ private slots:
void actOpenChatSettings(); void actOpenChatSettings();
void addMentionTag(QString mentionTag); void addMentionTag(QString mentionTag);
void focusTab(); void focusTab();
void actShowMentionPopup(QString &sender); void actShowMentionPopup(const QString &sender);
void actShowPopup(const QString &message); void actShowPopup(const QString &message);
void actCompleterChanged(); void actCompleterChanged();

View file

@ -58,7 +58,11 @@ QSize CloseButton::sizeHint() const
return QSize(width, height); return QSize(width, height);
} }
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
void CloseButton::enterEvent(QEnterEvent *event)
#else
void CloseButton::enterEvent(QEvent *event) void CloseButton::enterEvent(QEvent *event)
#endif
{ {
update(); update();
QAbstractButton::enterEvent(event); QAbstractButton::enterEvent(event);
@ -74,7 +78,7 @@ void CloseButton::paintEvent(QPaintEvent * /*event*/)
{ {
QPainter p(this); QPainter p(this);
QStyleOption opt; QStyleOption opt;
opt.init(this); opt.initFrom(this);
opt.state |= QStyle::State_AutoRaise; opt.state |= QStyle::State_AutoRaise;
if (isEnabled() && underMouse() && !isChecked() && !isDown()) if (isEnabled() && underMouse() && !isChecked() && !isDown())
opt.state |= QStyle::State_Raised; opt.state |= QStyle::State_Raised;

View file

@ -52,7 +52,11 @@ public:
} }
protected: protected:
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
void enterEvent(QEnterEvent *event);
#else
void enterEvent(QEvent *event); void enterEvent(QEvent *event);
#endif
void leaveEvent(QEvent *event); void leaveEvent(QEvent *event);
void paintEvent(QPaintEvent *event); void paintEvent(QPaintEvent *event);
}; };

View file

@ -8,6 +8,7 @@
#include <QNetworkReply> #include <QNetworkReply>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QRegExp> #include <QRegExp>
#include <QRegularExpression>
#include <QUrlQuery> #include <QUrlQuery>
TappedOutInterface::TappedOutInterface(CardDatabase &_cardDatabase, QObject *parent) TappedOutInterface::TappedOutInterface(CardDatabase &_cardDatabase, QObject *parent)
@ -53,7 +54,7 @@ void TappedOutInterface::queryFinished(QNetworkReply *reply)
int captures = rx2.captureCount(); int captures = rx2.captureCount();
for (int i = 1; i <= captures; i++) { 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();
} }
} }

View file

@ -36,7 +36,9 @@ TipsOfTheDay::TipsOfTheDay(QString xmlPath, QObject *parent) : QAbstractListMode
break; break;
} }
if (reader.name() == "tip") { auto readerName = reader.name().toString();
if (readerName == "tip") {
QString title, content, imagePath; QString title, content, imagePath;
QDate date; QDate date;
reader.readNext(); reader.readNext();
@ -45,16 +47,18 @@ TipsOfTheDay::TipsOfTheDay(QString xmlPath, QObject *parent) : QAbstractListMode
break; break;
} }
if (reader.name() == "title") { readerName = reader.name().toString();
if (readerName == "title") {
title = reader.readElementText(); title = reader.readElementText();
} else if (reader.name() == "text") { } else if (readerName == "text") {
content = reader.readElementText(); content = reader.readElementText();
} else if (reader.name() == "image") { } else if (readerName == "image") {
imagePath = "theme:tips/images/" + reader.readElementText(); imagePath = "theme:tips/images/" + reader.readElementText();
} else if (reader.name() == "date") { } else if (readerName == "date") {
date = QDate::fromString(reader.readElementText(), Qt::ISODate); date = QDate::fromString(reader.readElementText(), Qt::ISODate);
} else { } else {
// unkown element, do nothing // unknown element, do nothing
} }
} }
tipList->append(TipOfTheDay(title, content, imagePath, date)); tipList->append(TipOfTheDay(title, content, imagePath, date));

View file

@ -137,7 +137,7 @@ QString UserInfoBox::getAgeString(int ageSeconds)
if (ageSeconds == 0) if (ageSeconds == 0)
return accountAgeString; return accountAgeString;
auto date = QDateTime::fromTime_t(QDateTime::currentSecsSinceEpoch() - ageSeconds).date(); auto date = QDateTime::fromSecsSinceEpoch(QDateTime::currentSecsSinceEpoch() - ageSeconds).date();
if (!date.isValid()) if (!date.isValid())
return accountAgeString; return accountAgeString;

View file

@ -296,7 +296,11 @@ bool UserListItemDelegate::editorEvent(QEvent *event,
if ((event->type() == QEvent::MouseButtonPress) && index.isValid()) { if ((event->type() == QEvent::MouseButtonPress) && index.isValid()) {
QMouseEvent *const mouseEvent = static_cast<QMouseEvent *>(event); QMouseEvent *const mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent->button() == Qt::RightButton) { if (mouseEvent->button() == Qt::RightButton) {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
static_cast<UserList *>(parent())->showContextMenu(mouseEvent->globalPosition().toPoint(), index);
#else
static_cast<UserList *>(parent())->showContextMenu(mouseEvent->globalPos(), index); static_cast<UserList *>(parent())->showContextMenu(mouseEvent->globalPos(), index);
#endif
return true; return true;
} }
} }

View file

@ -101,8 +101,8 @@ void MainWindow::processConnectionClosedEvent(const Event_ConnectionClosed &even
case Event_ConnectionClosed::BANNED: { case Event_ConnectionClosed::BANNED: {
reasonStr = tr("Banned by moderator"); reasonStr = tr("Banned by moderator");
if (event.has_end_time()) if (event.has_end_time())
reasonStr.append("\n" + reasonStr.append(
tr("Expected end time: %1").arg(QDateTime::fromTime_t(event.end_time()).toString())); "\n" + tr("Expected end time: %1").arg(QDateTime::fromSecsSinceEpoch(event.end_time()).toString()));
else else
reasonStr.append("\n" + tr("This ban lasts indefinitely.")); reasonStr.append("\n" + tr("This ban lasts indefinitely."));
if (event.has_reason_str()) if (event.has_reason_str())
@ -393,7 +393,7 @@ void MainWindow::loginError(Response::ResponseCode r,
case Response::RespUserIsBanned: { case Response::RespUserIsBanned: {
QString bannedStr; QString bannedStr;
if (endTime) 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 else
bannedStr = tr("You are banned indefinitely."); bannedStr = tr("You are banned indefinitely.");
if (!reasonStr.isEmpty()) if (!reasonStr.isEmpty())
@ -530,7 +530,7 @@ void MainWindow::registerError(Response::ResponseCode r, QString reasonStr, quin
case Response::RespUserIsBanned: { case Response::RespUserIsBanned: {
QString bannedStr; QString bannedStr;
if (endTime) 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 else
bannedStr = tr("You are banned indefinitely."); bannedStr = tr("You are banned indefinitely.");
if (!reasonStr.isEmpty()) if (!reasonStr.isEmpty())
@ -865,13 +865,13 @@ void MainWindow::startupConfigCheck()
if (SettingsCache::instance().getNotifyAboutNewVersion()) { if (SettingsCache::instance().getNotifyAboutNewVersion()) {
alertForcedOracleRun(VERSION_STRING, true); alertForcedOracleRun(VERSION_STRING, true);
} else { } else {
QtConcurrent::run(db, &CardDatabase::loadCardDatabases); const auto reloadOk0 = QtConcurrent::run([] { db->loadCardDatabases(); });
} }
SettingsCache::instance().setClientVersion(VERSION_STRING); SettingsCache::instance().setClientVersion(VERSION_STRING);
} else { } else {
// previous config from this version found // previous config from this version found
qDebug() << "Startup: found config with current version"; 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. // Run the tips dialog only on subsequent startups.
// On the first run after an install/update the startup is already crowded enough // 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) void MainWindow::iconActivated(QSystemTrayIcon::ActivationReason reason)
{ {
if (reason == QSystemTrayIcon::DoubleClick) { if (reason == QSystemTrayIcon::DoubleClick) {
if (windowState() != Qt::WindowMinimized && windowState() != Qt::WindowMinimized + Qt::WindowMaximized) if ((windowState() & Qt::WindowMinimized) == 0) {
showMinimized(); showMinimized();
else { } else {
showNormal(); showNormal();
QApplication::setActiveWindow(this); QApplication::setActiveWindow(this);
} }
@ -1058,7 +1058,7 @@ void MainWindow::cardDatabaseNewSetsFound(int numUnknownSets, QStringList unknow
if (msgBox.clickedButton() == yesButton) { if (msgBox.clickedButton() == yesButton) {
db->enableAllUnknownSets(); db->enableAllUnknownSets();
QtConcurrent::run(db, &CardDatabase::loadCardDatabases); const auto reloadOk1 = QtConcurrent::run([] { db->loadCardDatabases(); });
} else if (msgBox.clickedButton() == noButton) { } else if (msgBox.clickedButton() == noButton) {
db->markAllSetsAsKnown(); db->markAllSetsAsKnown();
} else if (msgBox.clickedButton() == settingsButton) { } else if (msgBox.clickedButton() == settingsButton) {
@ -1086,8 +1086,9 @@ void MainWindow::actCheckCardUpdates()
} }
cardUpdateProcess = new QProcess(this); 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, connect(cardUpdateProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this,
SLOT(cardUpdateFinished(int, QProcess::ExitStatus))); SLOT(cardUpdateFinished(int, QProcess::ExitStatus)));
@ -1148,7 +1149,7 @@ void MainWindow::exitCardDatabaseUpdate()
cardUpdateProcess->deleteLater(); cardUpdateProcess->deleteLater();
cardUpdateProcess = nullptr; cardUpdateProcess = nullptr;
QtConcurrent::run(db, &CardDatabase::loadCardDatabases); const auto reloadOk1 = QtConcurrent::run([] { db->loadCardDatabases(); });
} }
void MainWindow::cardUpdateError(QProcess::ProcessError err) void MainWindow::cardUpdateError(QProcess::ProcessError err)
@ -1278,7 +1279,7 @@ void MainWindow::actAddCustomSet()
QMessageBox::information( QMessageBox::information(
this, tr("Load sets/cards"), this, tr("Load sets/cards"),
tr("The new sets/cards have been added successfully.\nCockatrice will now reload the card database.")); 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 { } else {
QMessageBox::warning(this, tr("Load sets/cards"), tr("Sets/cards failed to import.")); QMessageBox::warning(this, tr("Load sets/cards"), tr("Sets/cards failed to import."));
} }

View file

@ -14,7 +14,7 @@
#include <QDebug> #include <QDebug>
#include <QGraphicsSceneWheelEvent> #include <QGraphicsSceneWheelEvent>
#include <QPainter> #include <QPainter>
#include <math.h> #include <QtMath>
ZoneViewZone::ZoneViewZone(Player *_p, ZoneViewZone::ZoneViewZone(Player *_p,
CardZone *_origZone, CardZone *_origZone,
@ -103,17 +103,17 @@ void ZoneViewZone::reorganizeCards()
for (int i = 0; i < cardCount; ++i) for (int i = 0; i < cardCount; ++i)
cards[i]->setId(i); cards[i]->setId(i);
int cols = floor(sqrt((double)cardCount / 2)); int cols = qFloor(qSqrt((double)cardCount / 2));
if (cols > 7) if (cols > 7)
cols = 7; cols = 7;
int rows = ceil((double)cardCount / cols); int rows = qCeil((double)cardCount / cols);
if (rows < 1) if (rows < 1)
rows = 1; rows = 1;
if (minRows == 0) if (minRows == 0)
minRows = rows; minRows = rows;
else if (rows < minRows) { else if (rows < minRows) {
rows = minRows; rows = minRows;
cols = ceil((double)cardCount / minRows); cols = qCeil((double)cardCount / minRows);
} }
if (cols < 2) if (cols < 2)
cols = 2; cols = 2;
@ -193,6 +193,10 @@ void ZoneViewZone::setPileView(int _pileView)
void ZoneViewZone::addCardImpl(CardItem *card, int x, int /*y*/) 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); cards.insert(x, card);
card->setParentItem(this); card->setParentItem(this);
card->update(); card->update();

View file

@ -36,8 +36,8 @@ set(ORACLE_LIBS)
INCLUDE_DIRECTORIES(pb) INCLUDE_DIRECTORIES(pb)
INCLUDE_DIRECTORIES(sfmt) INCLUDE_DIRECTORIES(sfmt)
INCLUDE_DIRECTORIES(${PROTOBUF_INCLUDE_DIR}) 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}) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
add_library(cockatrice_common ${common_SOURCES} ${common_MOC_SRCS}) add_library(cockatrice_common ${common_SOURCES} ${common_MOC_SRCS})
target_link_libraries(cockatrice_common cockatrice_protocol) target_link_libraries(cockatrice_common PUBLIC cockatrice_protocol)

View file

@ -292,7 +292,7 @@ bool AbstractDecklistCardNode::readElement(QXmlStreamReader *xml)
{ {
while (!xml->atEnd()) { while (!xml->atEnd()) {
xml->readNext(); xml->readNext();
if (xml->isEndElement() && xml->name() == "card") if (xml->isEndElement() && xml->name().toString() == "card")
return false; return false;
} }
return true; return true;
@ -435,7 +435,7 @@ bool DeckList::loadFromXml(QXmlStreamReader *xml)
while (!xml->atEnd()) { while (!xml->atEnd()) {
xml->readNext(); xml->readNext();
if (xml->isStartElement()) { if (xml->isStartElement()) {
if (xml->name() != "cockatrice_deck") if (xml->name().toString() != "cockatrice_deck")
return false; return false;
while (!xml->atEnd()) { while (!xml->atEnd()) {
xml->readNext(); xml->readNext();
@ -602,7 +602,7 @@ bool DeckList::loadFromStream_Plain(QTextStream &in)
int amount = 1; int amount = 1;
match = reMultiplier.match(cardName); match = reMultiplier.match(cardName);
if (match.hasMatch()) { if (match.hasMatch()) {
amount = match.capturedRef(1).toInt(); amount = match.captured(1).toInt();
cardName = match.captured(2); cardName = match.captured(2);
} }

View file

@ -4,7 +4,7 @@
#include <QByteArray> #include <QByteArray>
#include <QString> #include <QString>
#include <cmath> #include <QtMath>
#include <functional> #include <functional>
peg::parser math(R"( peg::parser math(R"(
@ -32,19 +32,17 @@ Expression::Expression(double initial) : value(initial)
{ {
if (default_functions == nullptr) { if (default_functions == nullptr) {
default_functions = new QMap<QString, std::function<double(double)>>(); default_functions = new QMap<QString, std::function<double(double)>>();
default_functions->insert("sin", [](double a) { return sin(a); }); default_functions->insert("abs", [](double a) { return qFabs(a); });
default_functions->insert("cos", [](double a) { return cos(a); }); default_functions->insert("ceil", [](double a) { return qCeil(a); });
default_functions->insert("tan", [](double a) { return tan(a); }); default_functions->insert("cos", [](double a) { return qCos(a); });
default_functions->insert("sqrt", [](double a) { return sqrt(a); }); default_functions->insert("floor", [](double a) { return qFloor(a); });
default_functions->insert("log", [](double a) { return log(a); }); default_functions->insert("log", [](double a) { return qLn(a); });
default_functions->insert("log10", [](double a) { return log(a); }); default_functions->insert("log10", [](double a) { return qLn(a); });
default_functions->insert("trunc", [](double a) { return trunc(a); }); default_functions->insert("round", [](double a) { return qRound(a); });
default_functions->insert("abs", [](double a) { return fabs(a); }); default_functions->insert("sin", [](double a) { return qSin(a); });
default_functions->insert("sqrt", [](double a) { return qSqrt(a); });
default_functions->insert("floor", [](double a) { return floor(a); }); default_functions->insert("tan", [](double a) { return qTan(a); });
default_functions->insert("ceil", [](double a) { return ceil(a); }); default_functions->insert("trunc", [](double a) { return std::trunc(a); });
default_functions->insert("round", [](double a) { return round(a); });
default_functions->insert("trunc", [](double a) { return trunc(a); });
} }
fns = QMap<QString, std::function<double(double)>>(*default_functions); fns = QMap<QString, std::function<double(double)>>(*default_functions);
} }
@ -80,7 +78,7 @@ double Expression::eval(const peg::Ast &ast)
result /= arg; result /= arg;
break; break;
case '^': case '^':
result = pow(result, arg); result = qPow(result, arg);
break; break;
default: default:
result = 0; result = 0;

View file

@ -14,7 +14,7 @@
RNG_SFMT::RNG_SFMT(QObject *parent) : RNG_Abstract(parent) RNG_SFMT::RNG_SFMT(QObject *parent) : RNG_Abstract(parent)
{ {
// initialize the random number generator with a 32bit integer seed (timestamp) // 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());
} }
/** /**

View file

@ -118,7 +118,7 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session,
Event_ConnectionClosed event; Event_ConnectionClosed event;
event.set_reason(Event_ConnectionClosed::LOGGEDINELSEWERE); event.set_reason(Event_ConnectionClosed::LOGGEDINELSEWERE);
event.set_reason_str("You have been logged out due to logging in at another location."); 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); SessionEvent *se = users.value(name)->prepareSessionEvent(event);
users.value(name)->sendProtocolItem(*se); users.value(name)->sendProtocolItem(*se);
@ -230,6 +230,11 @@ void Server::addClient(Server_ProtocolHandler *client)
void Server::removeClient(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") if (client->getConnectionType() == "tcp")
tcpUserCount--; tcpUserCount--;
@ -238,7 +243,7 @@ void Server::removeClient(Server_ProtocolHandler *client)
webSocketUserCount--; webSocketUserCount--;
QWriteLocker locker(&clientsLock); QWriteLocker locker(&clientsLock);
clients.removeAt(clients.indexOf(client)); clients.removeAt(clientIndex);
ServerInfo_User *data = client->getUserInfo(); ServerInfo_User *data = client->getUserInfo();
if (data) { if (data) {
Event_UserLeft event; Event_UserLeft event;

View file

@ -57,8 +57,8 @@ private slots:
public: public:
mutable QReadWriteLock clientsLock, roomsLock; // locking order: roomsLock before clientsLock mutable QReadWriteLock clientsLock, roomsLock; // locking order: roomsLock before clientsLock
Server(QObject *parent = nullptr); explicit Server(QObject *parent = nullptr);
~Server() = default; virtual ~Server() = default;
AuthenticationResult loginUser(Server_ProtocolHandler *session, AuthenticationResult loginUser(Server_ProtocolHandler *session,
QString &name, QString &name,
const QString &password, const QString &password,

View file

@ -21,13 +21,13 @@
#define SERVER_CARD_H #define SERVER_CARD_H
#include "pb/card_attributes.pb.h" #include "pb/card_attributes.pb.h"
#include "pb/serverinfo_card.pb.h"
#include "server_arrowtarget.h" #include "server_arrowtarget.h"
#include <QMap> #include <QMap>
#include <QString> #include <QString>
class Server_CardZone; class Server_CardZone;
class ServerInfo_Card;
class Server_Card : public Server_ArrowTarget class Server_Card : public Server_ArrowTarget
{ {
@ -52,7 +52,7 @@ private:
public: public:
Server_Card(QString _name, int _id, int _coord_x, int _coord_y, Server_CardZone *_zone = 0); 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 Server_CardZone *getZone() const
{ {

View file

@ -292,10 +292,10 @@ void Server_CardZone::insertCard(Server_Card *card, int x, int y)
insertCardIntoCoordMap(card, x, y); insertCardIntoCoordMap(card, x, y);
} else { } else {
card->setCoords(0, 0); card->setCoords(0, 0);
if (x == -1) { if (0 <= x && x < cards.length()) {
cards.append(card);
} else {
cards.insert(x, card); cards.insert(x, card);
} else {
cards.append(card);
} }
} }
card->setZone(this); card->setZone(this);

View file

@ -147,7 +147,12 @@ public:
LogMessage_TargetType /* targetType */, LogMessage_TargetType /* targetType */,
const int /* targetId */, const int /* targetId */,
const QString & /* targetName */){}; 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 */) virtual int checkNumberOfUserAccounts(const QString & /* email */)
{ {
return 0; return 0;

View file

@ -69,6 +69,7 @@ Server_Game::Server_Game(const ServerInfo_User &_creatorInfo,
spectatorsNeedPassword(_spectatorsNeedPassword), spectatorsCanTalk(_spectatorsCanTalk), spectatorsNeedPassword(_spectatorsNeedPassword), spectatorsCanTalk(_spectatorsCanTalk),
spectatorsSeeEverything(_spectatorsSeeEverything), inactivityCounter(0), startTimeOfThisGame(0), spectatorsSeeEverything(_spectatorsSeeEverything), inactivityCounter(0), startTimeOfThisGame(0),
secondsElapsed(0), firstGameStarted(false), turnOrderReversed(false), startTime(QDateTime::currentDateTime()), secondsElapsed(0), firstGameStarted(false), turnOrderReversed(false), startTime(QDateTime::currentDateTime()),
pingClock(nullptr),
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
gameMutex() gameMutex()
#else #else
@ -97,7 +98,7 @@ Server_Game::~Server_Game()
gameClosed = true; gameClosed = true;
sendGameEventContainer(prepareGameEvent(Event_GameClosed(), -1)); sendGameEventContainer(prepareGameEvent(Event_GameClosed(), -1));
for (Server_Player *player : players.values()) { for (auto *player : players.values()) {
player->prepareDestroy(); player->prepareDestroy();
} }
players.clear(); players.clear();
@ -112,10 +113,22 @@ Server_Game::~Server_Game()
replayList.append(currentReplay); replayList.append(currentReplay);
storeGameInformation(); storeGameInformation();
for (int i = 0; i < replayList.size(); ++i) for (auto *replay : replayList) {
delete replayList[i]; delete replay;
}
replayList.clear();
room = nullptr;
currentReplay = nullptr;
creatorInfo = nullptr;
if (pingClock) {
delete pingClock;
pingClock = nullptr;
}
qDebug() << "Server_Game destructor: gameId=" << gameId; qDebug() << "Server_Game destructor: gameId=" << gameId;
deleteLater();
} }
void Server_Game::storeGameInformation() void Server_Game::storeGameInformation()
@ -126,7 +139,7 @@ void Server_Game::storeGameInformation()
ServerInfo_ReplayMatch *replayMatchInfo = replayEvent.mutable_match_info(); ServerInfo_ReplayMatch *replayMatchInfo = replayEvent.mutable_match_info();
replayMatchInfo->set_game_id(gameInfo.game_id()); replayMatchInfo->set_game_id(gameInfo.game_id());
replayMatchInfo->set_room_name(room->getName().toStdString()); 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_length(secondsElapsed);
replayMatchInfo->set_game_name(gameInfo.description()); 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_can_chat(spectatorsCanTalk);
result.set_spectators_omniscient(spectatorsSeeEverything); result.set_spectators_omniscient(spectatorsSeeEverything);
result.set_spectators_count(getSpectatorCount()); result.set_spectators_count(getSpectatorCount());
result.set_start_time(startTime.toTime_t()); result.set_start_time(startTime.toSecsSinceEpoch());
} }
} }

View file

@ -120,6 +120,7 @@ Server_Player::~Server_Player() = default;
void Server_Player::prepareDestroy() void Server_Player::prepareDestroy()
{ {
delete deck; delete deck;
deck = nullptr;
playerMutex.lock(); playerMutex.lock();
if (userInterface) { if (userInterface) {
@ -1234,7 +1235,6 @@ Server_Player::cmdAttachCard(const Command_AttachCard &cmd, ResponseContainer &
return Response::RespContextError; return Response::RespContextError;
} }
// Get all arrows pointing to or originating from the card being attached and delete them.
QMapIterator<int, Server_Player *> playerIterator(game->getPlayers()); QMapIterator<int, Server_Player *> playerIterator(game->getPlayers());
while (playerIterator.hasNext()) { while (playerIterator.hasNext()) {
Server_Player *p = playerIterator.next().value(); Server_Player *p = playerIterator.next().value();

View file

@ -25,8 +25,8 @@
#include <QDateTime> #include <QDateTime>
#include <QDebug> #include <QDebug>
#include <QtMath>
#include <google/protobuf/descriptor.h> #include <google/protobuf/descriptor.h>
#include <math.h>
Server_ProtocolHandler::Server_ProtocolHandler(Server *_server, Server_ProtocolHandler::Server_ProtocolHandler(Server *_server,
Server_DatabaseInterface *_databaseInterface, Server_DatabaseInterface *_databaseInterface,
@ -44,6 +44,7 @@ Server_ProtocolHandler::~Server_ProtocolHandler()
} }
// This function must only be called from the thread this object lives in. // 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). // The thread must not hold any server locks when calling this (e.g. clientsLock, roomsLock).
void Server_ProtocolHandler::prepareDestroy() 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)) { (!idleClientWarningSent) && (server->getIdleClientTimeout() > 0)) {
Event_NotifyUser event; Event_NotifyUser event;
event.set_type(Event_NotifyUser::IDLEWARNING); 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; Response_Login *re = new Response_Login;
re->set_denied_reason_str(reasonStr.toStdString()); re->set_denied_reason_str(reasonStr.toStdString());
if (banSecondsLeft != 0) 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); rc.setResponseExtension(re);
return Response::RespUserIsBanned; return Response::RespUserIsBanned;
} }

View file

@ -63,7 +63,6 @@ protected:
private: private:
QList<int> messageSizeOverTime, messageCountOverTime, commandCountOverTime; QList<int> messageSizeOverTime, messageCountOverTime, commandCountOverTime;
int timeRunning, lastDataReceived, lastActionReceived; int timeRunning, lastDataReceived, lastActionReceived;
QTimer *pingClock;
virtual void transmitProtocolItem(const ServerMessage &item) = 0; virtual void transmitProtocolItem(const ServerMessage &item) = 0;

View file

@ -16,7 +16,7 @@ ServerInfo_User_Container::ServerInfo_User_Container(const ServerInfo_User_Conta
if (other.userInfo) if (other.userInfo)
userInfo = new ServerInfo_User(*other.userInfo); userInfo = new ServerInfo_User(*other.userInfo);
else else
userInfo = 0; userInfo = nullptr;
} }
ServerInfo_User_Container::~ServerInfo_User_Container() ServerInfo_User_Container::~ServerInfo_User_Container()

View file

@ -9,7 +9,7 @@ protected:
ServerInfo_User *userInfo; ServerInfo_User *userInfo;
public: 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 &_userInfo);
ServerInfo_User_Container(const ServerInfo_User_Container &other); ServerInfo_User_Container(const ServerInfo_User_Container &other);
ServerInfo_User_Container &operator=(const ServerInfo_User_Container &other) = default; ServerInfo_User_Container &operator=(const ServerInfo_User_Container &other) = default;

View file

@ -3,7 +3,6 @@
#define STRINGSIZES_H #define STRINGSIZES_H
#include <QString> #include <QString>
#include <QtMath>
// max size for short strings, like names and things that are generally a single phrase // max size for short strings, like names and things that are generally a single phrase
constexpr int MAX_NAME_LENGTH = 0xff; constexpr int MAX_NAME_LENGTH = 0xff;

View file

@ -13,24 +13,25 @@ SET(dbconverter_SOURCES
${VERSION_STRING_CPP} ${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) SET(QT_DONT_USE_QTGUI TRUE)
QT5_WRAP_CPP(dbconverter_SOURCES IF(Qt6_FOUND)
Qt6_WRAP_CPP(dbconverter_SOURCES
../cockatrice/src/settingscache.h ../cockatrice/src/settingscache.h
../cockatrice/src/settings/carddatabasesettings.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 # Build servatrice binary and link it
ADD_EXECUTABLE(dbconverter MACOSX_BUNDLE ${dbconverter_SOURCES} ${dbconverter_MOC_SRCS}) ADD_EXECUTABLE(dbconverter MACOSX_BUNDLE ${dbconverter_SOURCES} ${dbconverter_MOC_SRCS})
if(MSVC) TARGET_LINK_LIBRARIES(dbconverter ${DB_CONVERTER_QT_MODULES})
TARGET_LINK_LIBRARIES(dbconverter ${dbconverter_QT_MODULES} Qt5::WinMain)
else()
TARGET_LINK_LIBRARIES(dbconverter ${dbconverter_QT_MODULES})
endif()
# install rules # install rules
if(UNIX) if(UNIX)
@ -55,9 +56,8 @@ if(APPLE)
# these needs to be relative to CMAKE_INSTALL_PREFIX # these needs to be relative to CMAKE_INSTALL_PREFIX
set(plugin_dest_dir dbconverter.app/Contents/Plugins) set(plugin_dest_dir dbconverter.app/Contents/Plugins)
set(qtconf_dest_dir dbconverter.app/Contents/Resources) 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 install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime
FILES_MATCHING FILES_MATCHING
PATTERN "*.dSYM" EXCLUDE PATTERN "*.dSYM" EXCLUDE
@ -87,10 +87,12 @@ if(WIN32)
install(DIRECTORY "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${CMAKE_BUILD_TYPE}/" DESTINATION ./ FILES_MATCHING PATTERN "*.dll") install(DIRECTORY "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${CMAKE_BUILD_TYPE}/" DESTINATION ./ FILES_MATCHING PATTERN "*.dll")
# qt5 plugins: platforms, sqldrivers/mysql # Qt plugins: platforms
install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime FILES_MATCHING
FILES_MATCHING REGEX "(platforms/.*)\\.dll" PATTERN "platforms/qdirect2d.dll"
REGEX ".*d\\.dll" EXCLUDE) PATTERN "platforms/qminimal.dll"
PATTERN "platforms/qoffscreen.dll"
PATTERN "platforms/qwindows.dll")
install(CODE " install(CODE "
file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths] file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths]

View file

@ -2,12 +2,12 @@
# #
# provides the oracle binary # 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 # paths
set(DESKTOPDIR share/applications CACHE STRING "path to .desktop files") set(DESKTOPDIR share/applications CACHE STRING "path to .desktop files")
SET(oracle_SOURCES set(oracle_SOURCES
src/main.cpp src/main.cpp
src/oraclewizard.cpp src/oraclewizard.cpp
src/oracleimporter.cpp src/oracleimporter.cpp
@ -34,13 +34,13 @@ SET(oracle_SOURCES
set(oracle_RESOURCES oracle.qrc) 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) FILE(GLOB_RECURSE translate_oracle_SRCS src/*.cpp src/*.h ../cockatrice/src/settingscache.cpp)
SET(translate_SRCS ${translate_oracle_SRCS}) SET(translate_SRCS ${translate_oracle_SRCS})
SET(oracle_TS "${CMAKE_CURRENT_SOURCE_DIR}/translations/oracle_en@source.ts") SET(oracle_TS "${CMAKE_CURRENT_SOURCE_DIR}/oracle_en@source.ts")
ELSE() else()
FILE(GLOB oracle_TS "${CMAKE_CURRENT_SOURCE_DIR}/translations/*.ts") FILE(GLOB oracle_TS "${CMAKE_CURRENT_SOURCE_DIR}/translations/*.ts")
ENDIF(UPDATE_TRANSLATIONS) endif(UPDATE_TRANSLATIONS)
if(WIN32) if(WIN32)
set(oracle_SOURCES ${oracle_SOURCES} oracle.rc) set(oracle_SOURCES ${oracle_SOURCES} oracle.rc)
@ -50,38 +50,19 @@ if(APPLE)
set(MACOSX_BUNDLE_ICON_FILE appicon.icns) set(MACOSX_BUNDLE_ICON_FILE appicon.icns)
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) 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) set(oracle_SOURCES ${oracle_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns)
ENDIF(APPLE) endif(APPLE)
# Qt5 if(Qt6_FOUND)
find_package(Qt5 COMPONENTS Concurrent Network Svg Widgets REQUIRED) Qt6_ADD_RESOURCES(oracle_RESOURCES_RCC ${oracle_RESOURCES})
set(ORACLE_QT_MODULES Qt5::Concurrent Qt5::Network Qt5::Svg Qt5::Widgets) elseif(Qt5_FOUND)
Qt5_ADD_RESOURCES(oracle_RESOURCES_RCC ${oracle_RESOURCES})
# 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() 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()
endif()
QT5_ADD_RESOURCES(oracle_RESOURCES_RCC ${oracle_RESOURCES})
INCLUDE_DIRECTORIES(../cockatrice/src) INCLUDE_DIRECTORIES(../cockatrice/src)
# Libz is required to support zipped files # Libz is required to support zipped files
FIND_PACKAGE(ZLIB) FIND_PACKAGE(ZLIB)
IF(ZLIB_FOUND) if(ZLIB_FOUND)
INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS})
ADD_DEFINITIONS("-DHAS_ZLIB") ADD_DEFINITIONS("-DHAS_ZLIB")
@ -89,34 +70,60 @@ IF(ZLIB_FOUND)
src/zip/unzip.cpp src/zip/unzip.cpp
src/zip/zipglobal.cpp src/zip/zipglobal.cpp
) )
ELSE() else()
MESSAGE(STATUS "Oracle: zlib not found; ZIP support disabled") MESSAGE(STATUS "Oracle: zlib not found; ZIP support disabled")
ENDIF() endif()
# LibLZMA is required to support xz files # LibLZMA is required to support xz files
FIND_PACKAGE(LibLZMA) FIND_PACKAGE(LibLZMA)
IF(LIBLZMA_FOUND) if(LIBLZMA_FOUND)
INCLUDE_DIRECTORIES(${LIBLZMA_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(${LIBLZMA_INCLUDE_DIRS})
ADD_DEFINITIONS("-DHAS_LZMA") ADD_DEFINITIONS("-DHAS_LZMA")
set(oracle_SOURCES ${oracle_SOURCES} set(oracle_SOURCES ${oracle_SOURCES}
src/lzma/decompress.cpp src/lzma/decompress.cpp
) )
ELSE() else()
MESSAGE(STATUS "Oracle: LibLZMA not found; xz support disabled") MESSAGE(STATUS "Oracle: LibLZMA not found; xz support disabled")
ENDIF() endif()
# Build oracle binary and link it set(ORACLE_MAC_QM_INSTALL_DIR "oracle.app/Contents/Resources/translations")
ADD_EXECUTABLE(oracle WIN32 MACOSX_BUNDLE ${oracle_SOURCES} ${oracle_QM} ${oracle_RESOURCES_RCC} ${oracle_MOC_SRCS}) set(ORACLE_UNIX_QM_INSTALL_DIR "share/oracle/translations")
TARGET_LINK_LIBRARIES(oracle ${ORACLE_QT_MODULES}) set(ORACLE_WIN32_QM_INSTALL_DIR "translations")
IF(ZLIB_FOUND) if(Qt6_FOUND)
TARGET_LINK_LIBRARIES(oracle ${ZLIB_LIBRARIES}) # Qt6 Translations are linked after the executable is created in manual mode
ENDIF() 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 PUBLIC ${ORACLE_QT_MODULES})
TARGET_LINK_LIBRARIES(oracle ${LIBLZMA_LIBRARIES})
ENDIF() 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(UNIX)
if(APPLE) if(APPLE)
@ -129,17 +136,14 @@ if(UNIX)
set_target_properties(oracle PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/Info.plist) set_target_properties(oracle PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/Info.plist)
INSTALL(TARGETS oracle BUNDLE DESTINATION ./) INSTALL(TARGETS oracle BUNDLE DESTINATION ./)
INSTALL(FILES ${oracle_QM} DESTINATION ./oracle.app/Contents/Resources/translations)
else() else()
# Assume linux # Assume linux
INSTALL(TARGETS oracle RUNTIME DESTINATION bin/) 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.png DESTINATION ${ICONDIR}/hicolor/48x48/apps)
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/oracle.svg DESTINATION ${ICONDIR}/hicolor/scalable/apps) INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/oracle.svg DESTINATION ${ICONDIR}/hicolor/scalable/apps)
INSTALL(FILES ${oracle_QM} DESTINATION share/oracle/translations)
endif() endif()
elseif(WIN32) elseif(WIN32)
INSTALL(TARGETS oracle RUNTIME DESTINATION ./) INSTALL(TARGETS oracle RUNTIME DESTINATION ./)
INSTALL(FILES ${oracle_QM} DESTINATION ./translations)
endif() endif()
IF (NOT WIN32 AND NOT APPLE) IF (NOT WIN32 AND NOT APPLE)
@ -150,9 +154,8 @@ if(APPLE)
# these needs to be relative to CMAKE_INSTALL_PREFIX # these needs to be relative to CMAKE_INSTALL_PREFIX
set(plugin_dest_dir oracle.app/Contents/Plugins) set(plugin_dest_dir oracle.app/Contents/Plugins)
set(qtconf_dest_dir oracle.app/Contents/Resources) 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 install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime
FILES_MATCHING FILES_MATCHING
PATTERN "*.dSYM" EXCLUDE PATTERN "*.dSYM" EXCLUDE
@ -160,6 +163,7 @@ if(APPLE)
PATTERN "iconengines/*.dylib" PATTERN "iconengines/*.dylib"
PATTERN "platforms/*.dylib" PATTERN "platforms/*.dylib"
PATTERN "styles/*.dylib" PATTERN "styles/*.dylib"
PATTERN "tls/*.dylib"
) )
install(CODE " install(CODE "
@ -177,7 +181,7 @@ Translations = Resources/translations\")
" COMPONENT Runtime) " COMPONENT Runtime)
endif() endif()
IF(WIN32) if(WIN32)
# these needs to be relative to CMAKE_INSTALL_PREFIX # these needs to be relative to CMAKE_INSTALL_PREFIX
set(plugin_dest_dir Plugins) set(plugin_dest_dir Plugins)
set(qtconf_dest_dir .) 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") install(DIRECTORY "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${CMAKE_BUILD_TYPE}/" DESTINATION ./ FILES_MATCHING PATTERN "*.dll")
# qt5 plugins: iconengines, platforms # Qt plugins: iconengines, platforms, styles, tls (Qt6)
install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime FILES_MATCHING
install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime PATTERN "iconengines/qsvgicon.dll"
FILES_MATCHING REGEX "(iconengines|platforms|styles)/.*[^d]\\.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 " install(CODE "
file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths] 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}\") fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/Oracle.exe\" \"\${QTPLUGINS}\" \"${libSearchDirs}\")
" COMPONENT Runtime) " COMPONENT Runtime)
endif() 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()

View file

@ -22,9 +22,28 @@ void installNewTranslator()
{ {
QString lang = SettingsCache::instance().getLang(); 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); 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); qApp->installTranslator(translator);
} }
@ -62,10 +81,8 @@ int main(int argc, char *argv[])
QIcon icon("theme:appicon.svg"); QIcon icon("theme:appicon.svg");
wizard.setWindowIcon(icon); 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 // set name of the app desktop file; used by wayland to load the window icon
QGuiApplication::setDesktopFileName("oracle"); QGuiApplication::setDesktopFileName("oracle");
#endif
wizard.show(); wizard.show();

View file

@ -116,7 +116,7 @@ CardInfoPtr OracleImporter::addCard(QString name,
QStringList symbols = manacost.split("}"); QStringList symbols = manacost.split("}");
QString formattedCardCost; QString formattedCardCost;
for (QString symbol : symbols) { for (QString symbol : symbols) {
if (symbol.contains(QRegExp("[0-9WUBGRP]/[0-9WUBGRP]"))) { if (symbol.contains(QRegularExpression("[0-9WUBGRP]/[0-9WUBGRP]"))) {
symbol.append("}"); symbol.append("}");
} else { } else {
symbol.remove(QChar('{')); symbol.remove(QChar('{'));

View file

@ -143,16 +143,19 @@ IntroPage::IntroPage(QWidget *parent) : OracleWizardPage(parent)
languageLabel = new QLabel(this); languageLabel = new QLabel(this);
versionLabel = new QLabel(this); versionLabel = new QLabel(this);
languageBox = new QComboBox(this); languageBox = new QComboBox(this);
QString setLanguage = SettingsCache::instance().getLang();
QStringList qmFiles = findQmFiles(); QStringList languageCodes = findQmFiles();
for (int i = 0; i < qmFiles.size(); i++) { for (const QString &code : languageCodes) {
QString langName = languageName(qmFiles[i]); QString langName = languageName(code);
languageBox->addItem(langName, qmFiles[i]); languageBox->addItem(langName, code);
if ((qmFiles[i] == setLanguage) ||
(setLanguage.isEmpty() && langName == QCoreApplication::translate("i18n", DEFAULT_LANG_NAME))) {
languageBox->setCurrentIndex(i);
} }
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))); connect(languageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(languageBoxChanged(int)));
@ -170,15 +173,20 @@ QStringList IntroPage::findQmFiles()
{ {
QDir dir(translationPath); QDir dir(translationPath);
QStringList fileNames = dir.entryList(QStringList(translationPrefix + "_*.qm"), QDir::Files, QDir::Name); QStringList fileNames = dir.entryList(QStringList(translationPrefix + "_*.qm"), QDir::Files, QDir::Name);
fileNames.replaceInStrings(QRegExp(translationPrefix + "_(.*)\\.qm"), "\\1"); fileNames.replaceInStrings(QRegularExpression(translationPrefix + "_(.*)\\.qm"), "\\1");
fileNames.removeOne("en@source");
return fileNames; return fileNames;
} }
QString IntroPage::languageName(const QString &qmFile) QString IntroPage::languageName(const QString &lang)
{ {
QTranslator qTranslator; 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); return qTranslator.translate("i18n", DEFAULT_LANG_NAME);
} }
@ -454,8 +462,10 @@ void LoadSetsPage::readSetsFromByteArray(QByteArray data)
zipDownloadFailed(tr("Xz extraction failed.")); zipDownloadFailed(tr("Xz extraction failed."));
return; 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); watcher.setFuture(future);
return; return;
#else #else
@ -495,8 +505,10 @@ void LoadSetsPage::readSetsFromByteArray(QByteArray data)
uz.closeArchive(); uz.closeArchive();
return; 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); watcher.setFuture(future);
return; return;
#else #else
@ -510,7 +522,7 @@ void LoadSetsPage::readSetsFromByteArray(QByteArray data)
#endif #endif
} }
// Start the computation. // Start the computation.
future = QtConcurrent::run(wizard()->importer, &OracleImporter::readSetsFromByteArray, data); future = QtConcurrent::run([this, &data] { return wizard()->importer->readSetsFromByteArray(data); });
watcher.setFuture(future); watcher.setFuture(future);
} }

View file

@ -82,7 +82,7 @@ public:
private: private:
QStringList findQmFiles(); QStringList findQmFiles();
QString languageName(const QString &qmFile); QString languageName(const QString &lang);
private: private:
QLabel *label, *languageLabel, *versionLabel; QLabel *label, *languageLabel, *versionLabel;

View file

@ -31,12 +31,13 @@
*/ */
#include "json.h" #include "json.h"
#include <QMetaType>
#include <iostream> #include <iostream>
namespace QtJson namespace QtJson
{ {
static QString sanitizeString(QString str) static QString sanitizeString(QString str)
{ {
str.replace(QLatin1String("\\"), QLatin1String("\\\\")); str.replace(QLatin1String("\\"), QLatin1String("\\\\"));
@ -52,10 +53,8 @@ static QString sanitizeString(QString str)
static QByteArray join(const QList<QByteArray> &list, const QByteArray &sep) static QByteArray join(const QList<QByteArray> &list, const QByteArray &sep)
{ {
QByteArray res; QByteArray res;
Q_FOREACH(const QByteArray &i, list) Q_FOREACH (const QByteArray &i, list) {
{ if (!res.isEmpty()) {
if(!res.isEmpty())
{
res += sep; res += sep;
} }
res += i; res += i;
@ -80,8 +79,7 @@ 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 // Return an empty QVariant if the JSON data is either null or empty
if(!json.isNull() || !json.isEmpty()) if (!json.isNull() || !json.isEmpty()) {
{
QString data = json; QString data = json;
// We'll start from index 0 // We'll start from index 0
int index = 0; int index = 0;
@ -91,9 +89,7 @@ QVariant Json::parse(const QString &json, bool &success)
// Return the parsed value // Return the parsed value
return value; return value;
} } else {
else
{
// Return the empty QVariant // Return the empty QVariant
return QVariant(); return QVariant();
} }
@ -114,15 +110,18 @@ QByteArray Json::serialize(const QVariant &data, bool &success)
{ {
str = "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? else if ((data.type() == QVariant::List) || (data.type() == QVariant::StringList)) // variant is a list?
#endif
{ {
QList<QByteArray> values; QList<QByteArray> values;
const QVariantList list = data.toList(); const QVariantList list = data.toList();
Q_FOREACH(const QVariant& v, list) Q_FOREACH (const QVariant &v, list) {
{
QByteArray serializedValue = serialize(v); QByteArray serializedValue = serialize(v);
if(serializedValue.isNull()) if (serializedValue.isNull()) {
{
success = false; success = false;
break; break;
} }
@ -131,20 +130,22 @@ QByteArray Json::serialize(const QVariant &data, bool &success)
str = "[ " + join(values, ", ") + " ]"; 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? else if (data.type() == QVariant::Hash) // variant is a hash?
#endif
{ {
const QVariantHash vhash = data.toHash(); const QVariantHash vhash = data.toHash();
QHashIterator<QString, QVariant> it(vhash); QHashIterator<QString, QVariant> it(vhash);
str = "{ "; str = "{ ";
QList<QByteArray> pairs; QList<QByteArray> pairs;
while(it.hasNext()) while (it.hasNext()) {
{
it.next(); it.next();
QByteArray serializedValue = serialize(it.value()); QByteArray serializedValue = serialize(it.value());
if(serializedValue.isNull()) if (serializedValue.isNull()) {
{
success = false; success = false;
break; break;
} }
@ -155,18 +156,20 @@ QByteArray Json::serialize(const QVariant &data, bool &success)
str += join(pairs, ", "); str += join(pairs, ", ");
str += " }"; 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? else if (data.type() == QVariant::Map) // variant is a map?
#endif
{ {
const QVariantMap vmap = data.toMap(); const QVariantMap vmap = data.toMap();
QMapIterator<QString, QVariant> it(vmap); QMapIterator<QString, QVariant> it(vmap);
str = "{ "; str = "{ ";
QList<QByteArray> pairs; QList<QByteArray> pairs;
while(it.hasNext()) while (it.hasNext()) {
{
it.next(); it.next();
QByteArray serializedValue = serialize(it.value()); QByteArray serializedValue = serialize(it.value());
if(serializedValue.isNull()) if (serializedValue.isNull()) {
{
success = false; success = false;
break; break;
} }
@ -175,49 +178,56 @@ QByteArray Json::serialize(const QVariant &data, bool &success)
str += join(pairs, ", "); str += join(pairs, ", ");
str += " }"; 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? else if ((data.type() == QVariant::String) || (data.type() == QVariant::ByteArray)) // a string or a byte array?
#endif
{ {
str = sanitizeString(data.toString()).toUtf8(); 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? else if (data.type() == QVariant::Double) // double?
#endif
{ {
str = QByteArray::number(data.toDouble(), 'g', 20); str = QByteArray::number(data.toDouble(), 'g', 20);
if(!str.contains(".") && ! str.contains("e")) if (!str.contains(".") && !str.contains("e")) {
{
str += ".0"; 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? else if (data.type() == QVariant::Bool) // boolean value?
#endif
{ {
str = data.toBool() ? "true" : "false"; 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? else if (data.type() == QVariant::ULongLong) // large unsigned number?
#endif
{ {
str = QByteArray::number(data.value<qulonglong>()); str = QByteArray::number(data.value<qulonglong>());
} } else if (data.canConvert<qlonglong>()) // any signed number?
else if ( data.canConvert<qlonglong>() ) // any signed number?
{ {
str = QByteArray::number(data.value<qlonglong>()); str = QByteArray::number(data.value<qlonglong>());
} } else if (data.canConvert<long>()) {
else if (data.canConvert<long>())
{
str = QString::number(data.value<long>()).toUtf8(); str = QString::number(data.value<long>()).toUtf8();
} } else if (data.canConvert<QString>()) // can value be converted to string?
else if (data.canConvert<QString>()) // can value be converted to string?
{ {
// this will catch QDate, QDateTime, QUrl, ... // this will catch QDate, QDateTime, QUrl, ...
str = sanitizeString(data.toString()).toUtf8(); str = sanitizeString(data.toString()).toUtf8();
} } else {
else
{
success = false; success = false;
} }
if (success) if (success) {
{
return str; return str;
} } else {
else
{
return QByteArray(); return QByteArray();
} }
} }
@ -229,8 +239,7 @@ QVariant Json::parseValue(const QString &json, int &index, bool &success)
{ {
// Determine what kind of data we should parse by // Determine what kind of data we should parse by
// checking out the upcoming token // checking out the upcoming token
switch(Json::lookAhead(json, index)) switch (Json::lookAhead(json, index)) {
{
case JsonTokenString: case JsonTokenString:
return Json::parseString(json, index, success); return Json::parseString(json, index, success);
case JsonTokenNumber: case JsonTokenNumber:
@ -270,32 +279,23 @@ QVariant Json::parseObject(const QString &json, int &index, bool &success)
// Loop through all of the key/value pairs of the object // Loop through all of the key/value pairs of the object
bool done = false; bool done = false;
while(!done) while (!done) {
{
// Get the upcoming token // Get the upcoming token
token = Json::lookAhead(json, index); token = Json::lookAhead(json, index);
if(token == JsonTokenNone) if (token == JsonTokenNone) {
{
success = false; success = false;
return QVariantMap(); return QVariantMap();
} } else if (token == JsonTokenComma) {
else if(token == JsonTokenComma)
{
Json::nextToken(json, index); Json::nextToken(json, index);
} } else if (token == JsonTokenCurlyClose) {
else if(token == JsonTokenCurlyClose)
{
Json::nextToken(json, index); Json::nextToken(json, index);
return map; return map;
} } else {
else
{
// Parse the key/value pair's name // Parse the key/value pair's name
QString name = Json::parseString(json, index, success).toString(); QString name = Json::parseString(json, index, success).toString();
if(!success) if (!success) {
{
return QVariantMap(); return QVariantMap();
} }
@ -304,8 +304,7 @@ QVariant Json::parseObject(const QString &json, int &index, bool &success)
// If the next token is not a colon, flag the failure // If the next token is not a colon, flag the failure
// return an empty QVariant // return an empty QVariant
if(token != JsonTokenColon) if (token != JsonTokenColon) {
{
success = false; success = false;
return QVariant(QVariantMap()); return QVariant(QVariantMap());
} }
@ -313,8 +312,7 @@ QVariant Json::parseObject(const QString &json, int &index, bool &success)
// Parse the key/value pair's value // Parse the key/value pair's value
QVariant value = Json::parseValue(json, index, success); QVariant value = Json::parseValue(json, index, success);
if(!success) if (!success) {
{
return QVariantMap(); return QVariantMap();
} }
@ -337,30 +335,21 @@ QVariant Json::parseArray(const QString &json, int &index, bool &success)
Json::nextToken(json, index); Json::nextToken(json, index);
bool done = false; bool done = false;
while(!done) while (!done) {
{
int token = Json::lookAhead(json, index); int token = Json::lookAhead(json, index);
if(token == JsonTokenNone) if (token == JsonTokenNone) {
{
success = false; success = false;
return QVariantList(); return QVariantList();
} } else if (token == JsonTokenComma) {
else if(token == JsonTokenComma)
{
Json::nextToken(json, index); Json::nextToken(json, index);
} } else if (token == JsonTokenSquaredClose) {
else if(token == JsonTokenSquaredClose)
{
Json::nextToken(json, index); Json::nextToken(json, index);
break; break;
} } else {
else
{
QVariant value = Json::parseValue(json, index, success); QVariant value = Json::parseValue(json, index, success);
if(!success) if (!success) {
{
return QVariantList(); return QVariantList();
} }
@ -384,67 +373,43 @@ QVariant Json::parseString(const QString &json, int &index, bool &success)
c = json[index++]; c = json[index++];
bool complete = false; bool complete = false;
while(!complete) while (!complete) {
{ if (index == json.size()) {
if(index == json.size())
{
break; break;
} }
c = json[index++]; c = json[index++];
if(c == '\"') if (c == '\"') {
{
complete = true; complete = true;
break; break;
} } else if (c == '\\') {
else if(c == '\\') if (index == json.size()) {
{
if(index == json.size())
{
break; break;
} }
c = json[index++]; c = json[index++];
if(c == '\"') if (c == '\"') {
{
s.append('\"'); s.append('\"');
} } else if (c == '\\') {
else if(c == '\\')
{
s.append('\\'); s.append('\\');
} } else if (c == '/') {
else if(c == '/')
{
s.append('/'); s.append('/');
} } else if (c == 'b') {
else if(c == 'b')
{
s.append('\b'); s.append('\b');
} } else if (c == 'f') {
else if(c == 'f')
{
s.append('\f'); s.append('\f');
} } else if (c == 'n') {
else if(c == 'n')
{
s.append('\n'); s.append('\n');
} } else if (c == 'r') {
else if(c == 'r')
{
s.append('\r'); s.append('\r');
} } else if (c == 't') {
else if(c == 't')
{
s.append('\t'); s.append('\t');
} } else if (c == 'u') {
else if(c == 'u')
{
int remainingLength = json.size() - index; int remainingLength = json.size() - index;
if(remainingLength >= 4) if (remainingLength >= 4) {
{
QString unicodeStr = json.mid(index, 4); QString unicodeStr = json.mid(index, 4);
int symbol = unicodeStr.toInt(0, 16); int symbol = unicodeStr.toInt(0, 16);
@ -452,21 +417,16 @@ QVariant Json::parseString(const QString &json, int &index, bool &success)
s.append(QChar(symbol)); s.append(QChar(symbol));
index += 4; index += 4;
} } else {
else
{
break; break;
} }
} }
} } else {
else
{
s.append(c); s.append(c);
} }
} }
if(!complete) if (!complete) {
{
success = false; success = false;
return QVariant(); return QVariant();
} }
@ -506,10 +466,8 @@ int Json::lastIndexOfNumber(const QString &json, int index)
static const QString numericCharacters("0123456789+-.eE"); static const QString numericCharacters("0123456789+-.eE");
int lastIndex; int lastIndex;
for(lastIndex = index; lastIndex < json.size(); lastIndex++) for (lastIndex = index; lastIndex < json.size(); lastIndex++) {
{ if (numericCharacters.indexOf(json[lastIndex]) == -1) {
if(numericCharacters.indexOf(json[lastIndex]) == -1)
{
break; break;
} }
} }
@ -523,10 +481,8 @@ int Json::lastIndexOfNumber(const QString &json, int index)
void Json::eatWhitespace(const QString &json, int &index) void Json::eatWhitespace(const QString &json, int &index)
{ {
static const QString whitespaceChars(" \t\n\r"); static const QString whitespaceChars(" \t\n\r");
for(; index < json.size(); index++) for (; index < json.size(); index++) {
{ if (whitespaceChars.indexOf(json[index]) == -1) {
if(whitespaceChars.indexOf(json[index]) == -1)
{
break; break;
} }
} }
@ -548,25 +504,39 @@ int Json::nextToken(const QString &json, int &index)
{ {
Json::eatWhitespace(json, index); Json::eatWhitespace(json, index);
if(index == json.size()) if (index == json.size()) {
{
return JsonTokenNone; return JsonTokenNone;
} }
QChar c = json[index]; QChar c = json[index];
index++; index++;
switch(c.toLatin1()) switch (c.toLatin1()) {
{ case '{':
case '{': return JsonTokenCurlyOpen; return JsonTokenCurlyOpen;
case '}': return JsonTokenCurlyClose; case '}':
case '[': return JsonTokenSquaredOpen; return JsonTokenCurlyClose;
case ']': return JsonTokenSquaredClose; case '[':
case ',': return JsonTokenComma; return JsonTokenSquaredOpen;
case '"': return JsonTokenString; case ']':
case '0': case '1': case '2': case '3': case '4': return JsonTokenSquaredClose;
case '5': case '6': case '7': case '8': case '9': case ',':
case '-': return JsonTokenNumber; return JsonTokenComma;
case ':': return JsonTokenColon; 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--; index--;
@ -574,34 +544,25 @@ int Json::nextToken(const QString &json, int &index)
int remainingLength = json.size() - index; int remainingLength = json.size() - index;
// True // True
if(remainingLength >= 4) if (remainingLength >= 4) {
{ if (json[index] == 't' && json[index + 1] == 'r' && json[index + 2] == 'u' && json[index + 3] == 'e') {
if (json[index] == 't' && json[index + 1] == 'r' &&
json[index + 2] == 'u' && json[index + 3] == 'e')
{
index += 4; index += 4;
return JsonTokenTrue; return JsonTokenTrue;
} }
} }
// False // False
if (remainingLength >= 5) if (remainingLength >= 5) {
{ if (json[index] == 'f' && json[index + 1] == 'a' && json[index + 2] == 'l' && json[index + 3] == 's' &&
if (json[index] == 'f' && json[index + 1] == 'a' && json[index + 4] == 'e') {
json[index + 2] == 'l' && json[index + 3] == 's' &&
json[index + 4] == 'e')
{
index += 5; index += 5;
return JsonTokenFalse; return JsonTokenFalse;
} }
} }
// Null // Null
if (remainingLength >= 4) if (remainingLength >= 4) {
{ if (json[index] == 'n' && json[index + 1] == 'u' && json[index + 2] == 'l' && json[index + 3] == 'l') {
if (json[index] == 'n' && json[index + 1] == 'u' &&
json[index + 2] == 'l' && json[index + 3] == 'l')
{
index += 4; index += 4;
return JsonTokenNull; return JsonTokenNull;
} }
@ -610,5 +571,4 @@ int Json::nextToken(const QString &json, int &index)
return JsonTokenNone; return JsonTokenNone;
} }
} // namespace QtJson
} //end namespace

15
oracle/src/zip/unzip.h Executable file → Normal file
View file

@ -33,14 +33,12 @@
#include <QtCore/QDateTime> #include <QtCore/QDateTime>
#include <QtCore/QMap> #include <QtCore/QMap>
#include <QtCore/QtGlobal> #include <QtCore/QtGlobal>
#include <zlib.h> #include <zlib.h>
class QDir; class QDir;
class QFile; class QFile;
class QIODevice; class QIODevice;
class QString; class QString;
class QStringList;
OSDAB_BEGIN_NAMESPACE(Zip) OSDAB_BEGIN_NAMESPACE(Zip)
@ -68,7 +66,8 @@ public:
InvalidArchive, InvalidArchive,
HeaderConsistencyError, HeaderConsistencyError,
Skip, SkipAll // internal use only Skip,
SkipAll // internal use only
}; };
enum ExtractionOption enum ExtractionOption
@ -82,12 +81,15 @@ public:
enum CompressionMethod enum CompressionMethod
{ {
NoCompression, Deflated, UnknownCompression NoCompression,
Deflated,
UnknownCompression
}; };
enum FileType enum FileType
{ {
File, Directory File,
Directory
}; };
struct ZipEntry struct ZipEntry
@ -136,7 +138,8 @@ public:
ErrorCode extractFile(const QString &filename, const QDir &dir, 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, QIODevice *device, ExtractionOptions options = ExtractPaths);
ErrorCode extractFiles(const QStringList& filenames, const QString& dirname, 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 QDir &dir, ExtractionOptions options = ExtractPaths);
void setPassword(const QString &pwd); void setPassword(const QString &pwd);

6
oracle/src/zip/zipglobal.cpp Executable file → Normal file
View file

@ -115,8 +115,8 @@ bool OSDAB_ZIP_MANGLE(setFileTimestamp)(const QString& fileName, const QDateTime
return true; return true;
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
HANDLE hFile = CreateFileW(fileName.toStdWString().c_str(), HANDLE hFile =
GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); CreateFileW(fileName.toStdWString().c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE) { if (hFile == INVALID_HANDLE_VALUE) {
return false; 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) #elif defined(Q_OS_LINUX) || defined(Q_OS_MACX)
struct utimbuf t_buffer; 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; return utime(fileName.toLocal8Bit().constData(), &t_buffer) == 0;
#endif #endif

View file

@ -41,11 +41,12 @@ if(APPLE)
set(servatrice_SOURCES ${servatrice_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns) set(servatrice_SOURCES ${servatrice_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns)
ENDIF(APPLE) ENDIF(APPLE)
# Qt5 IF(Qt6_FOUND)
find_package(Qt5 COMPONENTS Network Sql WebSockets REQUIRED) Qt6_ADD_RESOURCES(servatrice_RESOURCES_RCC ${servatrice_RESOURCES})
set(SERVATRICE_QT_MODULES Qt5::Core Qt5::Network Qt5::Sql Qt5::WebSockets) 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) SET(QT_DONT_USE_QTGUI TRUE)
# Mysql connector # 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") SET(MYSQLCLIENT_DEFAULT_PATHS "C:\\Program Files\\MySQL\\MySQL Server 5.7\\lib" "C:\\Program Files (x86)\\MySQL\\MySQL Server 5.7\\lib")
endif() endif()
find_library(MYSQLCLIENT_LIBRARIES NAMES mysqlclient PATHS ${MYSQLCLIENT_DEFAULT_PATHS} PATH_SUFFIXES mysql mariadb) find_library(MYSQL_CLIENT_LIBRARIES NAMES mysqlclient PATHS ${MYSQLCLIENT_DEFAULT_PATHS} PATH_SUFFIXES mysql mariadb)
if(${MYSQLCLIENT_LIBRARIES} MATCHES "NOTFOUND") if(${MYSQL_CLIENT_LIBRARIES} MATCHES "NOTFOUND")
set(MYSQLCLIENT_FOUND FALSE CACHE INTERNAL "") set(MYSQLCLIENT_FOUND FALSE CACHE INTERNAL "")
MESSAGE(STATUS "Mysql connector NOT FOUND: servatrice won't be able to connect to a mysql server") MESSAGE(STATUS "MySQL connector NOT FOUND: Servatrice won't be able to connect to a MySQL server")
unset(MYSQLCLIENT_LIBRARIES) unset(MYSQL_CLIENT_LIBRARIES)
else() else()
set(MYSQLCLIENT_FOUND TRUE CACHE INTERNAL "") set(MYSQLCLIENT_FOUND TRUE CACHE INTERNAL "")
get_filename_component(MYSQLCLIENT_LIBRARY_DIR ${MYSQLCLIENT_LIBRARIES} PATH) get_filename_component(MYSQLCLIENT_LIBRARY_DIR ${MYSQL_CLIENT_LIBRARIES} PATH)
MESSAGE(STATUS "Mysql connector found at: ${MYSQLCLIENT_LIBRARY_DIR}") MESSAGE(STATUS "Found MySQL connector at: ${MYSQL_CLIENT_LIBRARIES}")
endif() endif()
# Declare path variables # Declare path variables
@ -83,9 +84,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
# Build servatrice binary and link it # Build servatrice binary and link it
ADD_EXECUTABLE(servatrice MACOSX_BUNDLE ${servatrice_SOURCES} ${servatrice_RESOURCES_RCC} ${servatrice_MOC_SRCS}) ADD_EXECUTABLE(servatrice MACOSX_BUNDLE ${servatrice_SOURCES} ${servatrice_RESOURCES_RCC} ${servatrice_MOC_SRCS})
if(MSVC) if(CMAKE_HOST_SYSTEM MATCHES "FreeBSD")
TARGET_LINK_LIBRARIES(servatrice cockatrice_common Threads::Threads ${SERVATRICE_QT_MODULES} Qt5::WinMain)
elseif(CMAKE_HOST_SYSTEM MATCHES "FreeBSD")
TARGET_LINK_LIBRARIES(servatrice cockatrice_common Threads::Threads ${SERVATRICE_QT_MODULES} ${LIBEXECINFO_LIBRARY}) TARGET_LINK_LIBRARIES(servatrice cockatrice_common Threads::Threads ${SERVATRICE_QT_MODULES} ${LIBEXECINFO_LIBRARY})
else() else()
TARGET_LINK_LIBRARIES(servatrice cockatrice_common Threads::Threads ${SERVATRICE_QT_MODULES}) 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 # these needs to be relative to CMAKE_INSTALL_PREFIX
set(plugin_dest_dir servatrice.app/Contents/Plugins) set(plugin_dest_dir servatrice.app/Contents/Plugins)
set(qtconf_dest_dir servatrice.app/Contents/Resources) 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 install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime
FILES_MATCHING FILES_MATCHING
PATTERN "*.dSYM" EXCLUDE PATTERN "*.dSYM" EXCLUDE
PATTERN "*_debug.dylib" EXCLUDE PATTERN "*_debug.dylib" EXCLUDE
PATTERN "platforms/*.dylib" PATTERN "platforms/*.dylib"
PATTERN "sqldrivers/libqsqlmysql*.dylib" PATTERN "sqldrivers/libqsqlmysql*.dylib"
PATTERN "tls/*.dylib"
) )
install(CODE " install(CODE "
@ -157,10 +156,18 @@ if(WIN32)
install(DIRECTORY "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${CMAKE_BUILD_TYPE}/" DESTINATION ./ FILES_MATCHING PATTERN "*.dll") install(DIRECTORY "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${CMAKE_BUILD_TYPE}/" DESTINATION ./ FILES_MATCHING PATTERN "*.dll")
# qt5 plugins: platforms, sqldrivers/mysql # Qt plugins: platforms, sqldrivers, tls (Qt6)
install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime FILES_MATCHING
FILES_MATCHING REGEX "(platforms/.*|sqldrivers/qsqlmysql)\\.dll" PATTERN "platforms/qdirect2d.dll"
REGEX ".*d\\.dll" EXCLUDE) 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 " install(CODE "
file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths] file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths]

View file

@ -34,7 +34,6 @@
#include <QMetaType> #include <QMetaType>
#include <QTextCodec> #include <QTextCodec>
#include <QtGlobal> #include <QtGlobal>
#include <google/protobuf/stubs/common.h>
#include <iostream> #include <iostream>
RNG_Abstract *rng; RNG_Abstract *rng;
@ -70,17 +69,17 @@ void testRNG()
} }
for (int i = 0; i <= maxMax - min; ++i) { for (int i = 0; i <= maxMax - min; ++i) {
std::cerr << (min + i); std::cerr << (min + i);
for (int j = 0; j < numbers.size(); ++j) { for (auto &number : numbers) {
if (i < numbers[j].size()) if (i < number.size())
std::cerr << "\t" << numbers[j][i]; std::cerr << "\t" << number[i];
else else
std::cerr << "\t"; std::cerr << "\t";
} }
std::cerr << std::endl; std::cerr << std::endl;
} }
std::cerr << std::endl << "Chi^2 ="; std::cerr << std::endl << "Chi^2 =";
for (int j = 0; j < chisq.size(); ++j) for (double j : chisq)
std::cerr << "\t" << QString::number(chisq[j], 'f', 3).toStdString(); std::cerr << "\t" << QString::number(j, 'f', 3).toStdString();
std::cerr << std::endl << "k ="; std::cerr << std::endl << "k =";
for (int j = 0; j < chisq.size(); ++j) for (int j = 0; j < chisq.size(); ++j)
std::cerr << "\t" << (j - min + minMax); std::cerr << "\t" << (j - min + minMax);
@ -112,9 +111,9 @@ void myMessageOutput2(QtMsgType /*type*/, const QMessageLogContext &, const QStr
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QCoreApplication app(argc, argv); QCoreApplication app(argc, argv);
app.setOrganizationName("Cockatrice"); QCoreApplication::setOrganizationName("Cockatrice");
app.setApplicationName("Servatrice"); QCoreApplication::setApplicationName("Servatrice");
app.setApplicationVersion(VERSION_STRING); QCoreApplication::setApplicationVersion(VERSION_STRING);
QCommandLineParser parser; QCommandLineParser parser;
parser.addHelpOption(); parser.addHelpOption();
@ -183,7 +182,7 @@ int main(int argc, char *argv[])
smtpClient = new SmtpClient(); smtpClient = new SmtpClient();
Servatrice *server = new Servatrice(); auto *server = new Servatrice();
QObject::connect(server, SIGNAL(destroyed()), &app, SLOT(quit()), Qt::QueuedConnection); QObject::connect(server, SIGNAL(destroyed()), &app, SLOT(quit()), Qt::QueuedConnection);
int retval = 0; int retval = 0;
if (server->initServer()) { if (server->initServer()) {
@ -192,7 +191,7 @@ int main(int argc, char *argv[])
qInstallMessageHandler(myMessageOutput); qInstallMessageHandler(myMessageOutput);
retval = app.exec(); retval = QCoreApplication::exec();
std::cerr << "Server quit." << std::endl; std::cerr << "Server quit." << std::endl;
std::cerr << "-------------------------" << std::endl; std::cerr << "-------------------------" << std::endl;
@ -210,5 +209,6 @@ int main(int argc, char *argv[])
// Delete all global objects allocated by libprotobuf. // Delete all global objects allocated by libprotobuf.
google::protobuf::ShutdownProtobufLibrary(); google::protobuf::ShutdownProtobufLibrary();
QCoreApplication::quit();
return retval; return retval;
} }

View file

@ -76,6 +76,7 @@ Servatrice_GameServer::~Servatrice_GameServer()
QThread *poolThread = connectionPools[i]->thread(); QThread *poolThread = connectionPools[i]->thread();
connectionPools[i]->deleteLater(); // pool destructor calls thread()->quit() connectionPools[i]->deleteLater(); // pool destructor calls thread()->quit()
poolThread->wait(); poolThread->wait();
poolThread->deleteLater();
} }
} }
@ -84,6 +85,7 @@ void Servatrice_GameServer::incomingConnection(qintptr socketDescriptor)
Servatrice_ConnectionPool *pool = findLeastUsedConnectionPool(); Servatrice_ConnectionPool *pool = findLeastUsedConnectionPool();
auto ssi = new TcpServerSocketInterface(server, pool->getDatabaseInterface()); auto ssi = new TcpServerSocketInterface(server, pool->getDatabaseInterface());
connect(ssi, SIGNAL(incTxBytes), this, SLOT(incTxBytes));
ssi->moveToThread(pool->thread()); ssi->moveToThread(pool->thread());
pool->addClient(); pool->addClient();
connect(ssi, SIGNAL(destroyed()), pool, SLOT(removeClient())); connect(ssi, SIGNAL(destroyed()), pool, SLOT(removeClient()));
@ -144,6 +146,7 @@ Servatrice_WebsocketGameServer::~Servatrice_WebsocketGameServer()
QThread *poolThread = connectionPools[i]->thread(); QThread *poolThread = connectionPools[i]->thread();
connectionPools[i]->deleteLater(); // pool destructor calls thread()->quit() connectionPools[i]->deleteLater(); // pool destructor calls thread()->quit()
poolThread->wait(); poolThread->wait();
poolThread->deleteLater();
} }
} }
@ -152,6 +155,7 @@ void Servatrice_WebsocketGameServer::onNewConnection()
Servatrice_ConnectionPool *pool = findLeastUsedConnectionPool(); Servatrice_ConnectionPool *pool = findLeastUsedConnectionPool();
auto ssi = new WebsocketServerSocketInterface(server, pool->getDatabaseInterface()); 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. * 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 * 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) Servatrice::Servatrice(QObject *parent)
: Server(parent), authenticationMethod(AuthenticationNone), uptime(0), txBytes(0), rxBytes(0), : Server(parent), authenticationMethod(AuthenticationNone), uptime(0), txBytes(0), rxBytes(0),
shutdownTimer(nullptr), isFirstShutdownMessage(true) shutdownTimer(nullptr)
{ {
qRegisterMetaType<QSqlDatabase>("QSqlDatabase"); qRegisterMetaType<QSqlDatabase>("QSqlDatabase");
} }
@ -204,21 +208,16 @@ Servatrice::~Servatrice()
{ {
gameServer->close(); gameServer->close();
// clients live in other threads, we need to lock them // we are destroying the clients outside their thread!
clientsLock.lockForRead();
for (auto *client : clients) { for (auto *client : clients) {
QMetaObject::invokeMethod(client, "prepareDestroy", Qt::QueuedConnection); client->prepareDestroy();
}
clientsLock.unlock();
// client destruction is asynchronous, wait for all clients to be gone
for (;;) {
QThread::usleep(10);
QReadLocker locker(&clientsLock);
if (clients.isEmpty())
break;
} }
if (shutdownTimer) {
shutdownTimer->deleteLater();
}
servatriceDatabaseInterface->deleteLater();
prepareDestroy(); prepareDestroy();
} }
@ -559,7 +558,7 @@ void Servatrice::updateLoginMessage()
} }
} }
void Servatrice::setRequiredFeatures(const QString featureList) void Servatrice::setRequiredFeatures(const QString &featureList)
{ {
FeatureSet features; FeatureSet features;
serverRequiredFeatureList.clear(); serverRequiredFeatureList.clear();
@ -570,8 +569,9 @@ void Servatrice::setRequiredFeatures(const QString featureList)
QStringList listReqFeatures = featureList.split(",", QString::SkipEmptyParts); QStringList listReqFeatures = featureList.split(",", QString::SkipEmptyParts);
#endif #endif
if (!listReqFeatures.isEmpty()) if (!listReqFeatures.isEmpty())
foreach (QString reqFeature, listReqFeatures) for (const QString &reqFeature : listReqFeatures) {
features.enableRequiredFeature(serverRequiredFeatureList, reqFeature); features.enableRequiredFeature(serverRequiredFeatureList, reqFeature);
}
qDebug() << "Set required client features to:" << serverRequiredFeatureList; qDebug() << "Set required client features to:" << serverRequiredFeatureList;
} }
@ -715,9 +715,10 @@ void Servatrice::shutdownTimeout()
clientsLock.unlock(); clientsLock.unlock();
delete se; delete se;
if (!shutdownMinutes) if (!shutdownMinutes) {
deleteLater(); deleteLater();
} }
}
shutdownMinutes--; shutdownMinutes--;
} }

View file

@ -96,9 +96,9 @@ private:
public: public:
Servatrice_IslServer(Servatrice *_server, Servatrice_IslServer(Servatrice *_server,
const QSslCertificate &_cert, const QSslCertificate &_cert,
const QSslKey &_privateKey, QSslKey _privateKey,
QObject *parent = nullptr) 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(); void shutdownTimeout();
protected: protected:
void doSendIslMessage(const IslMessage &msg, int serverId) override; void doSendIslMessage(const IslMessage &msg, int islServerId) override;
private: private:
enum DatabaseType enum DatabaseType
@ -160,7 +160,6 @@ private:
mutable QMutex loginMessageMutex; mutable QMutex loginMessageMutex;
QString loginMessage; QString loginMessage;
QString dbPrefix; QString dbPrefix;
QString requiredFeatures;
QMap<QString, bool> serverRequiredFeatureList; QMap<QString, bool> serverRequiredFeatureList;
QString officialWarnings; QString officialWarnings;
Servatrice_DatabaseInterface *servatriceDatabaseInterface; Servatrice_DatabaseInterface *servatriceDatabaseInterface;
@ -173,7 +172,6 @@ private:
int shutdownMinutes; int shutdownMinutes;
int nextShutdownMessageMinutes; int nextShutdownMessageMinutes;
QTimer *shutdownTimer; QTimer *shutdownTimer;
bool isFirstShutdownMessage;
mutable QMutex serverListMutex; mutable QMutex serverListMutex;
QList<ServerProperties> serverList; QList<ServerProperties> serverList;
@ -203,7 +201,7 @@ private:
public slots: public slots:
void scheduleShutdown(const QString &reason, int minutes); void scheduleShutdown(const QString &reason, int minutes);
void updateLoginMessage(); void updateLoginMessage();
void setRequiredFeatures(QString featureList); void setRequiredFeatures(const QString &featureList);
public: public:
explicit Servatrice(QObject *parent = nullptr); explicit Servatrice(QObject *parent = nullptr);
@ -213,10 +211,6 @@ public:
{ {
return serverRequiredFeatureList; return serverRequiredFeatureList;
} }
QString getOfficialWarningsList() const
{
return officialWarnings;
}
QString getServerName() const; QString getServerName() const;
QString getLoginMessage() const override QString getLoginMessage() const override
{ {
@ -282,9 +276,9 @@ public:
void incRxBytes(quint64 num); void incRxBytes(quint64 num);
void addDatabaseInterface(QThread *thread, Servatrice_DatabaseInterface *databaseInterface); void addDatabaseInterface(QThread *thread, Servatrice_DatabaseInterface *databaseInterface);
bool islConnectionExists(int serverId) const; bool islConnectionExists(int islServerId) const;
void addIslInterface(int serverId, IslInterface *interface); void addIslInterface(int islServerId, IslInterface *interface);
void removeIslInterface(int serverId); void removeIslInterface(int islServerId);
QReadWriteLock islLock; QReadWriteLock islLock;
QList<ServerProperties> getServerList() const; QList<ServerProperties> getServerList() const;

View file

@ -17,8 +17,8 @@ private:
int clientCount; int clientCount;
public: public:
Servatrice_ConnectionPool(Servatrice_DatabaseInterface *_databaseInterface); explicit Servatrice_ConnectionPool(Servatrice_DatabaseInterface *_databaseInterface);
~Servatrice_ConnectionPool(); ~Servatrice_ConnectionPool() override;
Servatrice_DatabaseInterface *getDatabaseInterface() const Servatrice_DatabaseInterface *getDatabaseInterface() const
{ {

View file

@ -113,12 +113,13 @@ bool Servatrice_DatabaseInterface::checkSql()
QSqlQuery *Servatrice_DatabaseInterface::prepareQuery(const QString &queryText) QSqlQuery *Servatrice_DatabaseInterface::prepareQuery(const QString &queryText)
{ {
if (preparedStatements.contains(queryText)) if (preparedStatements.contains(queryText)) {
return preparedStatements.value(queryText); return preparedStatements.value(queryText);
}
QString prefixedQueryText = queryText; QString prefixedQueryText = queryText;
prefixedQueryText.replace("{prefix}", server->getDbPrefix()); prefixedQueryText.replace("{prefix}", server->getDbPrefix());
QSqlQuery *query = new QSqlQuery(sqlDatabase); auto *query = new QSqlQuery(sqlDatabase);
query->prepare(prefixedQueryText); query->prepare(prefixedQueryText);
preparedStatements.insert(queryText, query); preparedStatements.insert(queryText, query);

View file

@ -36,14 +36,14 @@ protected:
const QString &clientId, const QString &clientId,
QString &reasonStr, QString &reasonStr,
int &banSecondsLeft, int &banSecondsLeft,
bool passwordNeedsHash); bool passwordNeedsHash) override;
public slots: public slots:
void initDatabase(const QSqlDatabase &_sqlDatabase); void initDatabase(const QSqlDatabase &_sqlDatabase);
public: public:
Servatrice_DatabaseInterface(int _instanceId, Servatrice *_server); explicit Servatrice_DatabaseInterface(int _instanceId, Servatrice *_server);
~Servatrice_DatabaseInterface(); ~Servatrice_DatabaseInterface() override;
bool initDatabase(const QString &type, bool initDatabase(const QString &type,
const QString &hostName, const QString &hostName,
const QString &databaseName, const QString &databaseName,
@ -58,66 +58,66 @@ public:
return sqlDatabase; return sqlDatabase;
} }
bool activeUserExists(const QString &user); bool activeUserExists(const QString &user) override;
bool userExists(const QString &user); bool userExists(const QString &user) override;
QString getUserSalt(const QString &user); QString getUserSalt(const QString &user) override;
int getUserIdInDB(const QString &name); int getUserIdInDB(const QString &name);
QMap<QString, ServerInfo_User> getBuddyList(const QString &name); QMap<QString, ServerInfo_User> getBuddyList(const QString &name) override;
QMap<QString, ServerInfo_User> getIgnoreList(const QString &name); QMap<QString, ServerInfo_User> getIgnoreList(const QString &name) override;
bool isInBuddyList(const QString &whoseList, const QString &who); bool isInBuddyList(const QString &whoseList, const QString &who) override;
bool isInIgnoreList(const QString &whoseList, const QString &who); bool isInIgnoreList(const QString &whoseList, const QString &who) override;
ServerInfo_User getUserData(const QString &name, bool withId = false); ServerInfo_User getUserData(const QString &name, bool withId = false) override;
void storeGameInformation(const QString &roomName, void storeGameInformation(const QString &roomName,
const QStringList &roomGameTypes, const QStringList &roomGameTypes,
const ServerInfo_Game &gameInfo, const ServerInfo_Game &gameInfo,
const QSet<QString> &allPlayersEver, const QSet<QString> &allPlayersEver,
const QSet<QString> &allSpectatorsEver, const QSet<QString> &allSpectatorsEver,
const QList<GameReplay *> &replayList); const QList<GameReplay *> &replayList) override;
DeckList *getDeckFromDatabase(int deckId, int userId); DeckList *getDeckFromDatabase(int deckId, int userId) override;
int getNextGameId(); int getNextGameId() override;
int getNextReplayId(); int getNextReplayId() override;
int getActiveUserCount(QString connectionType = QString()); int getActiveUserCount(QString connectionType = QString()) override;
qint64 startSession(const QString &userName, qint64 startSession(const QString &userName,
const QString &address, const QString &address,
const QString &clientId, const QString &clientId,
const QString &connectionType); const QString &connectionType) override;
void endSession(qint64 sessionId); void endSession(qint64 sessionId) override;
void clearSessionTables(); void clearSessionTables() override;
void lockSessionTables(); void lockSessionTables() override;
void unlockSessionTables(); void unlockSessionTables() override;
bool userSessionExists(const QString &userName); bool userSessionExists(const QString &userName) override;
bool usernameIsValid(const QString &user, QString &error); bool usernameIsValid(const QString &user, QString &error) override;
bool checkUserIsBanned(const QString &ipAddress, bool checkUserIsBanned(const QString &ipAddress,
const QString &userName, const QString &userName,
const QString &clientId, const QString &clientId,
QString &banReason, QString &banReason,
int &banSecondsRemaining); int &banSecondsRemaining) override;
int checkNumberOfUserAccounts(const QString &email); int checkNumberOfUserAccounts(const QString &email) override;
bool registerUser(const QString &userName, bool registerUser(const QString &userName,
const QString &realName, const QString &realName,
const QString &password, const QString &password,
bool passwordNeedsHash, bool passwordNeedsHash,
const QString &emailAddress, const QString &emailAddress,
const QString &country, const QString &country,
bool active = false); bool active = false) override;
bool activateUser(const QString &userName, const QString &token); bool activateUser(const QString &userName, const QString &token) override;
void updateUsersClientID(const QString &userName, const QString &userClientID); void updateUsersClientID(const QString &userName, const QString &userClientID) override;
void updateUsersLastLoginData(const QString &userName, const QString &clientVersion); void updateUsersLastLoginData(const QString &userName, const QString &clientVersion) override;
void logMessage(const int senderId, void logMessage(const int senderId,
const QString &senderName, const QString &senderName,
const QString &senderIp, const QString &senderIp,
const QString &logMessage, const QString &logMessage,
LogMessage_TargetType targetType, LogMessage_TargetType targetType,
const int targetId, const int targetId,
const QString &targetName); const QString &targetName) override;
bool changeUserPassword(const QString &user, const QString &password, bool passwordNeedsHash); bool changeUserPassword(const QString &user, const QString &password, bool passwordNeedsHash) override;
bool changeUserPassword(const QString &user, bool changeUserPassword(const QString &user,
const QString &oldPassword, const QString &oldPassword,
bool oldPasswordNeedsHash, bool oldPasswordNeedsHash,
const QString &newPassword, const QString &newPassword,
bool newPasswordNeedsHash); bool newPasswordNeedsHash) override;
QList<ServerInfo_Ban> getUserBanHistory(const QString userName); QList<ServerInfo_Ban> getUserBanHistory(const QString userName);
bool bool
addWarning(const QString userName, const QString adminName, const QString warningReason, const QString clientID); addWarning(const QString userName, const QString adminName, const QString warningReason, const QString clientID);
@ -133,7 +133,7 @@ public:
int &range, int &range,
int &maxresults); int &maxresults);
bool addForgotPassword(const QString &user); bool addForgotPassword(const QString &user);
bool removeForgotPassword(const QString &user); bool removeForgotPassword(const QString &user) override;
bool doesForgotPasswordExist(const QString &user); bool doesForgotPasswordExist(const QString &user);
bool updateUserToken(const QString &token, const QString &user); bool updateUserToken(const QString &token, const QString &user);
bool validateTableColumnStringData(const QString &table, bool validateTableColumnStringData(const QString &table,

View file

@ -45,7 +45,7 @@ void ServerLogger::startLog(const QString &logFileName)
connect(this, SIGNAL(sigFlushBuffer()), this, SLOT(flushBuffer()), Qt::QueuedConnection); 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) if (!logFile)
return; return;

View file

@ -18,7 +18,7 @@ public:
~ServerLogger(); ~ServerLogger();
public slots: public slots:
void startLog(const QString &logFileName); void startLog(const QString &logFileName);
void logMessage(QString message, void *caller = 0); void logMessage(const QString &message, void *caller = 0);
void rotateLogs(); void rotateLogs();
private slots: private slots:
void flushBuffer(); void flushBuffer();

View file

@ -74,7 +74,6 @@
#include <QSqlError> #include <QSqlError>
#include <QSqlQuery> #include <QSqlQuery>
#include <QString> #include <QString>
#include <QtMath>
#include <iostream> #include <iostream>
#include <string> #include <string>
@ -388,7 +387,7 @@ bool AbstractServerSocketInterface::deckListHelper(int folderId, ServerInfo_Deck
newItem->set_name(query->value(1).toString().toStdString()); newItem->set_name(query->value(1).toString().toStdString());
ServerInfo_DeckStorage_File *newFile = newItem->mutable_file(); 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; return true;
@ -551,7 +550,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdDeckUpload(const Comman
ServerInfo_DeckStorage_TreeItem *fileInfo = re->mutable_new_file(); ServerInfo_DeckStorage_TreeItem *fileInfo = re->mutable_new_file();
fileInfo->set_id(query->lastInsertId().toInt()); fileInfo->set_id(query->lastInsertId().toInt());
fileInfo->set_name(deckName.toStdString()); 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); rc.setResponseExtension(re);
} else if (cmd.has_deck_id()) { } else if (cmd.has_deck_id()) {
QSqlQuery *query = QSqlQuery *query =
@ -570,7 +569,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdDeckUpload(const Comman
ServerInfo_DeckStorage_TreeItem *fileInfo = re->mutable_new_file(); ServerInfo_DeckStorage_TreeItem *fileInfo = re->mutable_new_file();
fileInfo->set_id(cmd.deck_id()); fileInfo->set_id(cmd.deck_id());
fileInfo->set_name(deckName.toStdString()); 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); rc.setResponseExtension(re);
} else } else
return Response::RespInvalidData; return Response::RespInvalidData;
@ -619,8 +618,8 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReplayList(const Comman
const int gameId = query1->value(0).toInt(); const int gameId = query1->value(0).toInt();
matchInfo->set_game_id(gameId); matchInfo->set_game_id(gameId);
matchInfo->set_room_name(query1->value(2).toString().toStdString()); matchInfo->set_room_name(query1->value(2).toString().toStdString());
const int timeStarted = query1->value(3).toDateTime().toTime_t(); const int timeStarted = query1->value(3).toDateTime().toSecsSinceEpoch();
const int timeFinished = query1->value(4).toDateTime().toTime_t(); const int timeFinished = query1->value(4).toDateTime().toSecsSinceEpoch();
matchInfo->set_time_started(timeStarted); matchInfo->set_time_started(timeStarted);
matchInfo->set_length(timeFinished - timeStarted); matchInfo->set_length(timeFinished - timeStarted);
matchInfo->set_game_name(query1->value(5).toString().toStdString()); matchInfo->set_game_name(query1->value(5).toString().toStdString());
@ -977,7 +976,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdBanFromServer(const Com
if (cmd.has_visible_reason()) if (cmd.has_visible_reason())
event.set_reason_str(visibleReason.toStdString()); event.set_reason_str(visibleReason.toStdString());
if (minutes) 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) { for (int i = 0; i < userList.size(); ++i) {
SessionEvent *se = userList[i]->prepareSessionEvent(event); SessionEvent *se = userList[i]->prepareSessionEvent(event);
userList[i]->sendProtocolItem(*se); userList[i]->sendProtocolItem(*se);
@ -1148,7 +1147,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdRegisterAccount(const C
Response_Register *re = new Response_Register; Response_Register *re = new Response_Register;
re->set_denied_reason_str(banReason.toStdString()); re->set_denied_reason_str(banReason.toStdString());
if (banSecondsRemaining != 0) 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); rc.setResponseExtension(re);
return Response::RespUserIsBanned; return Response::RespUserIsBanned;
} }
@ -1649,7 +1648,7 @@ bool AbstractServerSocketInterface::removeAdminFlagFromUser(const QString &userN
Event_ConnectionClosed event; Event_ConnectionClosed event;
event.set_reason(Event_ConnectionClosed::DEMOTED); event.set_reason(Event_ConnectionClosed::DEMOTED);
event.set_reason_str("Your moderator and/or judge status has been revoked."); 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); SessionEvent *se = user->prepareSessionEvent(event);
user->sendProtocolItem(*se); user->sendProtocolItem(*se);
@ -1765,7 +1764,7 @@ void TcpServerSocketInterface::flushOutputQueue()
locker.relock(); locker.relock();
} }
locker.unlock(); locker.unlock();
servatrice->incTxBytes(totalBytes); emit incTxBytes(totalBytes);
// see above wrt mutex // see above wrt mutex
flushSocket(); flushSocket();
} }
@ -1859,7 +1858,7 @@ bool TcpServerSocketInterface::initTcpSession()
WebsocketServerSocketInterface::WebsocketServerSocketInterface(Servatrice *_server, WebsocketServerSocketInterface::WebsocketServerSocketInterface(Servatrice *_server,
Servatrice_DatabaseInterface *_databaseInterface, Servatrice_DatabaseInterface *_databaseInterface,
QObject *parent) QObject *parent)
: AbstractServerSocketInterface(_server, _databaseInterface, parent) : AbstractServerSocketInterface(_server, _databaseInterface, parent), socket(nullptr)
{ {
} }
@ -1885,17 +1884,12 @@ void WebsocketServerSocketInterface::initConnection(void *_socket)
address = socket->peerAddress(); address = socket->peerAddress();
QByteArray websocketIPHeader = settingsCache->value("server/web_socket_ip_header", "").toByteArray(); QByteArray websocketIPHeader = settingsCache->value("server/web_socket_ip_header", "").toByteArray();
if (websocketIPHeader.length() > 0) { if (websocketIPHeader.length() > 0 && socket->request().hasRawHeader(websocketIPHeader)) {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
if (socket->request().hasRawHeader(websocketIPHeader)) {
QString header(socket->request().rawHeader(websocketIPHeader)); QString header(socket->request().rawHeader(websocketIPHeader));
QHostAddress parsed(header); QHostAddress parsed(header);
if (!parsed.isNull()) if (!parsed.isNull()) {
address = parsed; 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, connect(socket, SIGNAL(binaryMessageReceived(const QByteArray &)), this,
@ -1905,7 +1899,7 @@ void WebsocketServerSocketInterface::initConnection(void *_socket)
connect(socket, SIGNAL(disconnected()), this, SLOT(catchSocketDisconnected())); connect(socket, SIGNAL(disconnected()), this, SLOT(catchSocketDisconnected()));
// Add this object to the server's list of connections before it can receive socket events. // 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); server->addClient(this);
logger->logMessage( logger->logMessage(
@ -1949,7 +1943,7 @@ void WebsocketServerSocketInterface::flushOutputQueue()
if (outputQueue.isEmpty()) if (outputQueue.isEmpty())
return; return;
int totalBytes = 0; qint64 totalBytes = 0;
while (!outputQueue.isEmpty()) { while (!outputQueue.isEmpty()) {
ServerMessage item = outputQueue.takeFirst(); ServerMessage item = outputQueue.takeFirst();
locker.unlock(); locker.unlock();
@ -1969,7 +1963,7 @@ void WebsocketServerSocketInterface::flushOutputQueue()
locker.relock(); locker.relock();
} }
locker.unlock(); locker.unlock();
servatrice->incTxBytes(totalBytes); emit incTxBytes(totalBytes);
// see above wrt mutex // see above wrt mutex
flushSocket(); flushSocket();
} }

Some files were not shown because too many files have changed in this diff Show more