Compare commits

...

340 Commits

Author SHA1 Message Date
Zach H
b7fbc12ac0 Allow Judges to see all information, regardless of room settings (#5053) 2024-06-16 19:12:37 -04:00
Zach H
e2ab8db958 Add some sessions (#5052)
* Add AccountEdit

* Add PasswordChange

* Cleanup

* Add SessionService.accountImage

* Add SessionService.message

* Add SessionService.getUserInfo

* Lint
2024-06-14 23:06:50 -04:00
Zach H
34d70980e8 Webatrice admin commands (#5051)
* Add AdminCommand.updateServerMessage

* Add AdminCommand.shutdownServer

* Add AdminCommand.reloadConfig

* Cleanup

* Add AdminCommand.adjustMod

* Lint

* Lint
2024-06-13 02:52:40 +00:00
Zach H
e45c4042fe Webatrice: Add all ModeratorCommands (#5049)
* Move viewLogHistory to Moderator commands

* Add Moderator.banFromServer

* Add Moderator.getBanHistory

* Add Moderator.getWarnHistory

* Add Moderator.warnUser

* Add Moderator.getWarnList
2024-06-13 02:15:14 +00:00
Zach H
ce8092318e Allow up to 100 dice to be rolled at a time (#5047)
* Allow up to 100 dice to be rolled at a time
- Fix #4276
2024-06-12 08:37:04 -04:00
transifex-integration[bot]
c95cc1dd9d Translate webclient/src/i18n-default.json in it (#5045)
100% translated source file: 'webclient/src/i18n-default.json'
on 'it'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-06 23:53:15 +00:00
Zach H
1f72877728 Drop MacOS 10.15/11 Support :( (#5033) 2024-05-31 09:10:54 -04:00
tooomm
93b40343d9 EnableCIServerMode (#5034) 2024-05-25 12:40:40 -04:00
transifex-integration[bot]
ba10108207 Translate cockatrice/cockatrice_en@source.ts in de (#5038) 2024-05-24 19:05:11 +02:00
github-actions[bot]
c28f66d673 Update translation source strings (#4973)
Co-authored-by: github-actions <github-actions@github.com>
2024-05-18 22:11:07 +02:00
Zach H
59f327f97a Pin XCode versions for Mac Builds (#5032) 2024-05-13 18:57:33 -04:00
tooomm
872c92a244 CI: Use windows-2022 image with Visual Studio 17 2022 (#4999)
Co-authored-by: ZeldaZach <zahalpern+github@gmail.com>
2024-05-13 17:41:50 -04:00
dependabot[bot]
2303880b87 Bump ejs from 3.1.8 to 3.1.10 in /webclient (#5027)
Bumps [ejs](https://github.com/mde/ejs) from 3.1.8 to 3.1.10.
- [Release notes](https://github.com/mde/ejs/releases)
- [Commits](https://github.com/mde/ejs/compare/v3.1.8...v3.1.10)

---
updated-dependencies:
- dependency-name: ejs
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-02 11:48:57 +00:00
github-actions[bot]
0e97cc1712 Update translation files (#5021)
Co-authored-by: github-actions <github-actions@github.com>
2024-04-24 19:37:31 -04:00
SlightlyCircuitous
d550e42441 Remove Fedora 38 Build and Add Fedora 40 Build (#5024)
* Remove Fedora 38 docker image

* Add Fedora 40 Dockerfile

* Remove Fedora 38, Add Fedora 40 to release template

* Remove Fedora 38, add Fedora 40 to desktop-build
2024-04-24 19:37:23 -04:00
SlightlyCircuitous
4279753030 Add Ubuntu 24.04 "Noble Numbat" Build (#5023)
* Create Ubuntu Noble Numbat dockerfile

* Add Noble Numbat to desktop_build

* Add Noble Numbat to release_template
2024-04-24 15:13:20 +02:00
Jeremy Letto
2f6c018b7a untangle updateStatus (#5018)
* untangle updateStatus

* fix test
2024-04-05 04:56:12 +00:00
Jeremy Letto
be5d42baba WebClient: refactor protobuf method structure (#5014) 2024-04-01 17:32:08 +00:00
ebbit1q
f174614496 assign new arrow id when arrow is moved to transformed card (#5012)
* add timeout to settingscache

* assign new arrow id when arrow is moved to transformed card

fixes bug introduced in #4907
fixes #5008
2024-03-27 14:47:00 +01:00
dependabot[bot]
e8c7fba8b0 Bump peter-evans/create-pull-request from 5 to 6 (#4997) 2024-03-19 19:42:08 +01:00
SlightlyCircuitous
5c49283023 Remove Ubuntu 23.04 Lunar Lobster Build (#5002)
* Delete .ci/UbuntuLunar directory

EOL

* Update release_template.md

Lunar is EOL

* Update desktop-build.yml

Lunar is EOL
2024-02-27 21:07:18 +01:00
ebbit1q
ad56b431a3 increase version number so we can create a beta (#5001) 2024-02-27 20:55:28 +01:00
SlightlyCircuitous
b0d8a33d5f Remove Fedora 37 Build, Add Fedora 39 Build (#5003) 2024-02-26 08:37:28 +01:00
dependabot[bot]
1715bcb216 Bump microsoft/setup-msbuild from 1 to 2 (#4996)
Bumps [microsoft/setup-msbuild](https://github.com/microsoft/setup-msbuild) from 1 to 2.
- [Release notes](https://github.com/microsoft/setup-msbuild/releases)
- [Changelog](https://github.com/microsoft/setup-msbuild/blob/main/building-release.md)
- [Commits](https://github.com/microsoft/setup-msbuild/compare/v1...v2)

---
updated-dependencies:
- dependency-name: microsoft/setup-msbuild
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-10 15:31:13 -05:00
tooomm
96caeaca72 Enable MTT over parallel flag (#4998) 2024-02-10 15:30:43 -05:00
tooomm
c8723ae935 Remove Gitter chat (#4995) 2024-02-01 18:25:04 +01:00
tooomm
94e39c044c update mv mapping (#4896) 2024-01-30 00:27:45 -05:00
ebbit1q
9776ea53c9 make translation update script more specific in line selection (#4993) 2024-01-25 23:28:23 +01:00
github-actions[bot]
675d07dac0 Update translation files (#4991)
Co-authored-by: github-actions <github-actions@github.com>
2024-01-23 22:29:40 +01:00
tooomm
1217820288 CI: GitHub Job Summary for Translation PRs (#4992) 2024-01-23 22:21:35 +01:00
tooomm
90e1a3cb76 Utilize new CPUs with more cores (#4988) 2024-01-22 23:15:20 +01:00
tooomm
7c1095ea50 CI: Fix ignore pattern & highlight status of translation automations (#4977)
* Add result of run to GHA summary

* Fix `paths-ignore`
2024-01-22 22:07:21 +01:00
dependabot[bot]
203e916a07 Bump actions/cache from 3 to 4 (#4990) 2024-01-22 19:32:27 +01:00
github-actions[bot]
7201e34b38 Update translation files (#4984)
Co-authored-by: github-actions <github-actions@github.com>
2024-01-18 20:50:28 +01:00
Basile Clement
6d032c378f Improve drag & drop behavior (#4963)
* Improve drag & drop behavior

This patch tweaks the drag & drop behavior (in particular, the grid
placement) to be more intuitive. More precisely, with this patch the
drag & drop will:

 - Only use the "hot spot" (i.e. position of the cursor on the card)
   for zones where the card is actually displayed around the cursor (in
   particular, not on the table where the card snaps to the grid).

 - Use better boundaries computed with respect to the center of the
   card (rather than its top left corner) for determining which grid
   cell a card should go to

 - Align behavior of the preview and the actual effect when overflow of
   the 3-card stacks occurs

 - Avoid visual glitches where the cursor ends up outside of the card or
   at incorrect offsets when moving the mouse too fast (which translates
   to overflows of the hot spot computation)

* Address review comments

 - Use simpler computation for restricting hotSpot range
 - Prevent dropping cards onto full 3-card slots
2024-01-01 16:51:36 -05:00
dependabot[bot]
badd8952f5 Bump @babel/traverse from 7.20.0 to 7.23.2 in /webclient (#4917)
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.20.0 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-20 21:31:12 -05:00
dependabot[bot]
7209eddb2d Bump crypto-js from 4.1.1 to 4.2.0 in /webclient (#4926)
Bumps [crypto-js](https://github.com/brix/crypto-js) from 4.1.1 to 4.2.0.
- [Commits](https://github.com/brix/crypto-js/compare/4.1.1...4.2.0)

---
updated-dependencies:
- dependency-name: crypto-js
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-20 21:31:04 -05:00
dependabot[bot]
a7ffd43b29 Bump actions/upload-artifact from 3 to 4 (#4969)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-18 23:58:37 +01:00
tooomm
724ba69483 macOS 12 --> 13 (#4966) 2023-12-17 13:39:06 -05:00
ebbit1q
1716801437 make package on macos 13 (#4961) 2023-12-15 21:15:57 -05:00
ebbit1q
fa727524dc make cards on the stack slightly overlap to stress order (#4930)
* make cards on the stack slightly overlap to stress order

dragging cards to the stack now places the card at the location it is
dropped

* make default play action append to stack

* add vertical overlap to settings and vertical hand

* Update cockatrice/src/dlg_settings.cpp

Co-authored-by: tooomm <tooomm@users.noreply.github.com>

* Fix format

---------

Co-authored-by: tooomm <tooomm@users.noreply.github.com>
Co-authored-by: ZeldaZach <zahalpern+github@gmail.com>
2023-12-15 14:00:58 -05:00
ebbit1q
28f80e18a0 add ctrl enter as shortcut for ok when setting annotation (#4929) 2023-12-15 13:55:11 -05:00
ebbit1q
4acc8bfe80 put cards on top in a random order (#4960) 2023-12-15 13:51:21 -05:00
Zach H
9f86ed7887 Fix Fedora Builds (#4964) 2023-12-15 13:49:32 -05:00
Basile Clement
cb18a55338 Support fractional scaling when scaling card images (#4962)
Fixes #4880
2023-12-14 14:24:35 +01:00
dependabot[bot]
78a928464c Bump @adobe/css-tools from 4.0.1 to 4.3.2 in /webclient (#4953)
Bumps [@adobe/css-tools](https://github.com/adobe/css-tools) from 4.0.1 to 4.3.2.
- [Changelog](https://github.com/adobe/css-tools/blob/main/History.md)
- [Commits](https://github.com/adobe/css-tools/commits)

---
updated-dependencies:
- dependency-name: "@adobe/css-tools"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-09 00:53:10 -05:00
Zach H
07a8cd0a5f Fix #4903: Parse Email Addresses whenever used (#4932) 2023-12-09 00:52:47 -05:00
Zach H
b73ef58567 Support WebP format for Card Images (#4950)
- Fix #4939
2023-12-09 00:52:14 -05:00
Zach H
519531f3a0 Support escaping single and double quotes in Deck Editor Search Regex Strings (#4948)
- Fix #4946
2023-12-09 00:51:54 -05:00
tooomm
4b8e47d079 Update default theme name (#4944) 2023-11-20 01:01:42 +01:00
transifex-integration[bot]
ed170f7e07 Updates for project Cockatrice and language de (#4942)
* Translate cockatrice/cockatrice_en@source.ts in de

100% translated source file: 'cockatrice/cockatrice_en@source.ts'
on 'de'.

* Translate oracle/oracle_en@source.ts in de

100% translated source file: 'oracle/oracle_en@source.ts'
on 'de'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-11-18 12:10:11 +01:00
transifex-integration[bot]
6bb559874c Translate cockatrice/cockatrice_en@source.ts in it (#4941)
100% translated source file: 'cockatrice/cockatrice_en@source.ts'
on 'it'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-11-18 08:44:10 +01:00
transifex-integration[bot]
9cd68e25b3 Translate cockatrice/cockatrice_en@source.ts in de (#4937)
100% translated source file: 'cockatrice/cockatrice_en@source.ts'
on 'de'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-11-12 19:22:58 +01:00
transifex-integration[bot]
72ac441598 Updates for project Cockatrice and language de (#4934)
* Translate cockatrice/cockatrice_en@source.ts in de

100% translated source file: 'cockatrice/cockatrice_en@source.ts'
on 'de'.

* Translate cockatrice/cockatrice_en@source.ts in de

100% translated source file: 'cockatrice/cockatrice_en@source.ts'
on 'de'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-11-05 19:15:48 +01:00
dependabot[bot]
f5fe56c85d Bump actions/setup-node from 3 to 4 (#4928)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3 to 4.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-31 00:48:39 +01:00
tooomm
d4fc1be2cc CI: Cleanup & updates (#4921)
* simplify ci after 64bit only

* hint on macos 10.15 with qt6.2

* Update desktop-build.yml

* Update desktop-build.yml

* update xcode

* 14.3 finds 14.3.1, but 14.0 doesn't find 14.0.1

* Update desktop-build.yml
2023-10-28 14:36:50 -04:00
ebbit1q
7b3617a273 clean up #4904 for consistency (#4927)
note, this is not a racetime issue, see #4907
2023-10-28 14:35:15 -04:00
tooomm
3e8adae3de Rename "stack until found" feature (#4871)
* Rename "stack until found" feature

* lint
2023-10-25 18:43:57 +02:00
tooomm
9943133d6d TOTD: Exchange Gitter for Discord, Fix date format (#4920)
* Update tips_of_the_day.xml

* Delete cockatrice/resources/tips/images/gitter.png

* Add Discord icon

* Update cockatrice.qrc

* date format
2023-10-24 14:26:38 +02:00
tooomm
a5706a47af Include missing strings for translation (#4919) 2023-10-19 21:58:38 +02:00
github-actions[bot]
b3b911c64d Update translation files (#4913)
Co-authored-by: github-actions <github-actions@github.com>
2023-10-18 20:41:14 +02:00
github-actions[bot]
05beb4fcaf Update translation source strings (#4918) 2023-10-18 00:29:56 +02:00
tooomm
324b50e381 Improve Transifex pull action (#4916) 2023-10-16 22:51:08 +02:00
Zach H
186f4289e9 Address /W4 compiler warnings for Windows (#4910) 2023-10-15 20:31:13 -04:00
Zach H
cb90a8356b Use proto21 on macos11 (#4914) 2023-10-15 20:30:50 -04:00
tooomm
e9c502ab32 CI: Add action for pulling new translations (#4911)
* Update and rename translations.yml to translations-push.yml

* Rename update_translations.sh to update_translation_source_strings.sh

* Update and rename update_translations_template.md to update_translation_source_strings_template.md

* Add translations-pull.yml

* Update config

* Update desktop-lint.yml

* Update desktop-build.yml

* correct env var naming

* names
2023-10-15 18:47:15 -04:00
tooomm
f728520e97 Update release_template.md (#4909) 2023-10-13 22:52:08 -04:00
Zach H
c1b0d50237 Handle Qt6.6 Deprecations (#4908) 2023-10-13 20:53:47 -04:00
tooomm
b9cfc29059 CI: Use concurrency group (#4902) 2023-10-13 19:01:37 -04:00
ebbit1q
6bf7c79891 copy arrows on transform (#4907) 2023-10-13 19:01:08 -04:00
Zach H
2bd0e58354 HotFix: Prevent crashing if a Zone is null with an arrow while a player concedes race time (#4904) 2023-10-13 19:00:53 -04:00
Zach H
ee674cb0cf Support MacOS 12 & 13. Support Protobuf 23. Deprecate MacOS 11. (#4884) 2023-10-13 14:45:22 -04:00
transifex-integration[bot]
dd1b354d48 Translate webclient/src/i18n-default.json in de (#4906)
100% translated source file: 'webclient/src/i18n-default.json'
on 'de'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-10-13 14:26:05 -04:00
tooomm
d3e96f4a99 Switch to rolling release (#4905) 2023-10-13 14:25:40 -04:00
tooomm
90e2eb3db9 Webclient: Fix translation file names (#4897) 2023-10-09 18:05:43 +02:00
transifex-integration[bot]
102be6a350 Translate cockatrice_en@source.ts in en_US [Manual Sync] (#4893)
98% of minimum 80% translated source file: 'cockatrice_en@source.ts'
on 'en_US'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-10-06 22:56:03 +02:00
transifex-integration[bot]
be6152948c Updates for project Cockatrice and lanuage es on branch master (#4886)
* Translate i18n-default.json in es [Manual Sync]

99% of minimum 80% translated source file: 'i18n-default.json'
on 'es'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

* Translate cockatrice_en@source.ts in es [Manual Sync]

98% of minimum 80% translated source file: 'cockatrice_en@source.ts'
on 'es'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

* Translate i18n-default.json in es [Manual Sync]

99% of minimum 95% translated source file: 'i18n-default.json'
on 'es'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

* Translate cockatrice_en@source.ts in es [Manual Sync]

98% of minimum 95% translated source file: 'cockatrice_en@source.ts'
on 'es'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-10-06 22:52:37 +02:00
transifex-integration[bot]
f14bf4b205 Updates for project Cockatrice and lanuage it on branch master (#4887)
* Translate i18n-default.json in it [Manual Sync]

99% of minimum 80% translated source file: 'i18n-default.json'
on 'it'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

* Translate cockatrice_en@source.ts in it [Manual Sync]

98% of minimum 80% translated source file: 'cockatrice_en@source.ts'
on 'it'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

* Translate i18n-default.json in it [Manual Sync]

99% of minimum 95% translated source file: 'i18n-default.json'
on 'it'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-10-06 21:37:59 +02:00
transifex-integration[bot]
123ac2ca25 Updates for project Cockatrice and lanuage fr on branch master (#4888)
* Translate cockatrice_en@source.ts in fr [Manual Sync]

98% of minimum 80% translated source file: 'cockatrice_en@source.ts'
on 'fr'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

* Translate i18n-default.json in fr [Manual Sync]

99% of minimum 80% translated source file: 'i18n-default.json'
on 'fr'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

* Translate i18n-default.json in fr [Manual Sync]

99% of minimum 95% translated source file: 'i18n-default.json'
on 'fr'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-10-06 21:36:56 +02:00
transifex-integration[bot]
7216b976ec Updates for project Cockatrice and lanuage pt_BR on branch master (#4890)
* Translate cockatrice_en@source.ts in pt_BR [Manual Sync]

98% of minimum 80% translated source file: 'cockatrice_en@source.ts'
on 'pt_BR'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

* Translate webclient/src/i18n-default.json in pt_BR

100% translated source file: 'webclient/src/i18n-default.json'
on 'pt_BR'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-10-06 21:36:16 +02:00
ebbit1q
7fb698cfbf update release template with #4883 deprecating 32 bit (#4885) 2023-10-02 14:09:19 -04:00
Zach H
b0470ab678 Move to OpenSSLv3 (& Drop 32-bit) (#4883) 2023-10-01 17:19:31 -04:00
Zach H
0deb037035 Address connect errors in logs (#4882) 2023-10-01 15:30:54 -04:00
ZeldaZach
064b362d60 Bump to 2.9.0 :) 2023-09-14 22:14:22 -04:00
tooomm
6bbe228a84 README: Cleanup translations widget and links (#4870)
* Fix translations widget and links

* Name webatrice
2023-09-11 22:13:52 -04:00
transifex-integration[bot]
a8ba8b6ab5 Updates for project Cockatrice and lanuage de on branch master (#4867)
* Translate cockatrice/cockatrice_en@source.ts in de

100% translated source file: 'cockatrice/cockatrice_en@source.ts'
on 'de'.

* Translate cockatrice/cockatrice_en@source.ts in de

100% translated source file: 'cockatrice/cockatrice_en@source.ts'
on 'de'.

* Translate cockatrice/cockatrice_en@source.ts in de

100% translated source file: 'cockatrice/cockatrice_en@source.ts'
on 'de'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-09-11 21:24:57 +02:00
dependabot[bot]
e850f6c2a5 Bump actions/checkout from 3 to 4 (#4866) 2023-09-07 23:11:19 +02:00
github-actions[bot]
e9eb7d6db1 Update translation source strings (#4865)
Co-authored-by: github-actions <github-actions@github.com>
2023-09-01 20:20:19 -04:00
tooomm
56d21321d0 remove translation string (#4860) 2023-08-18 12:34:54 -04:00
transifex-integration[bot]
3888a74212 Translate oracle/oracle_en@source.ts in de (#4862)
100% translated source file: 'oracle/oracle_en@source.ts'
on 'de'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-08-18 12:34:39 -04:00
transifex-integration[bot]
0035e29f9e Translate webclient/src/i18n-default.json in pt_BR (#4863)
100% translated source file: 'webclient/src/i18n-default.json'
on 'pt_BR'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-08-18 12:34:30 -04:00
ebbit1q
90679d5669 fix issues with #4648 (#4864) 2023-08-18 12:34:17 -04:00
transifex-integration[bot]
ac5dc2578a Translate cockatrice/cockatrice_en@source.ts in de (#4861)
100% translated source file: 'cockatrice/cockatrice_en@source.ts'
on 'de'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-08-13 20:51:02 +02:00
tooomm
671e6823be cleanup vcpkg.json (#4859) 2023-08-11 13:35:59 +02:00
github-actions[bot]
0d76662311 Update translation source strings (#4843)
Co-authored-by: github-actions <github-actions@github.com>
2023-08-10 22:37:17 +02:00
tooomm
8dd59cf3cf CI: Bump GitHub actions + submodule (#4852)
* Bump used actions

* Update vcpkg submodule (#4857)

* Pause npm updates for webclient (#4853)

* Bump peter-evans/create-pull-request from 4 to 5 (#4846)

Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4 to 5.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](https://github.com/peter-evans/create-pull-request/compare/v4...v5)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Update vcpkg

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump vcpkg action to v11

* Update vcpkg cache setting

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-09 12:28:16 +02:00
dependabot[bot]
332d25dc00 Bump peter-evans/create-pull-request from 4 to 5 (#4846)
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4 to 5.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](https://github.com/peter-evans/create-pull-request/compare/v4...v5)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-08 23:21:26 +02:00
tooomm
0fa81a77dc Pause npm updates for webclient (#4853) 2023-08-08 13:17:28 +02:00
tooomm
9a74d8f72d update link (#4845) 2023-08-06 22:46:02 -04:00
Zach H
8c539351e3 Fix Mac Builds (#4844) 2023-08-06 22:45:45 -04:00
ebbit1q
e3552fc0ae add more default shortcuts (#4349)
* add more default shortcuts

replace the ctrl a look at top of library shortcut with ctrl shift n
use ctl a for draw arrow
replace ctrl shift b for toggle sideboard lock
use ctrl b for move card to bottom of library
use ctrl shift l for start local game
add keyboard shortcuts for all 3 counter colors using , . / keys
use ctrl [ ] \ for the "other" counter
add ctrl = as an easy alternative to ctrl + for people without keypads
( on linux ctrl alt keypad + is a special key that is reserved in x
  it produces the XF86_Next_VMode keyboard event which isn't bindable )
use alt u for toggling untapping
use alt l for peeking at cards
use ctrl alt u for unattaching cards
use alt n for set annotation
use alt y for milling one card

* use alt f for flipping cards
2023-08-06 17:56:24 -04:00
tooomm
ca308636c3 CI: Add automatic PR creation for source string updates (#4544)
* wording

* add pr creation

* Update translations.yml

* Update translations.yml

* update translation workflow

* Update CONTRIBUTING.md

* Update CONTRIBUTING.md

* Update CONTRIBUTING.md

* skip ci update

* skip ci update

* update conditions

* remove empty line

* typo

* tee test

* cleanup

* pass data between steps

* opt for step output over env variable

* remove space

* create script

* wording

* fix fork protection, re-add pr run

* updates

* Update translations.yml

* adjust for new source paths

* update comment

* wording

Co-authored-by: ebbit1q <ebbit1q@gmail.com>

* wording

* reorder

* reorder

* fix deprecation of set-output

* fix version

---------

Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2023-08-06 17:55:50 -04:00
ebbit1q
244cb847fb replace trayicon activation with menu actions (#4632) 2023-08-06 17:55:02 -04:00
tooomm
176c52daf2 Enable Dependabot (#4795)
* add dependabot file

* disable submodules for now
2023-08-06 17:54:17 -04:00
tooomm
ee3525ec64 Webclient: Brazilian translations are doubled (#4809)
* cleanup old brazilian translations

* pt-br --> pt_br

* pt-br --> pt_br

* pt-br --> pt_br
2023-08-06 17:53:55 -04:00
dependabot[bot]
adce921be7 Bump protobufjs from 7.1.2 to 7.2.4 in /webclient (#4827)
Bumps [protobufjs](https://github.com/protobufjs/protobuf.js) from 7.1.2 to 7.2.4.
- [Release notes](https://github.com/protobufjs/protobuf.js/releases)
- [Changelog](https://github.com/protobufjs/protobuf.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/protobufjs/protobuf.js/compare/protobufjs-v7.1.2...protobufjs-v7.2.4)

---
updated-dependencies:
- dependency-name: protobufjs
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-06 17:53:41 -04:00
dependabot[bot]
20ceb1c284 Bump semver from 6.3.0 to 6.3.1 in /webclient (#4829)
Bumps [semver](https://github.com/npm/node-semver) from 6.3.0 to 6.3.1.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v6.3.1/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v6.3.0...v6.3.1)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-06 17:53:32 -04:00
dependabot[bot]
db4364b8f8 Bump tough-cookie from 4.1.2 to 4.1.3 in /webclient (#4828)
Bumps [tough-cookie](https://github.com/salesforce/tough-cookie) from 4.1.2 to 4.1.3.
- [Release notes](https://github.com/salesforce/tough-cookie/releases)
- [Changelog](https://github.com/salesforce/tough-cookie/blob/master/CHANGELOG.md)
- [Commits](https://github.com/salesforce/tough-cookie/compare/v4.1.2...v4.1.3)

---
updated-dependencies:
- dependency-name: tough-cookie
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-06 17:53:25 -04:00
dependabot[bot]
48d6435e09 Bump word-wrap from 1.2.3 to 1.2.4 in /webclient (#4831)
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-06 17:53:18 -04:00
ebbit1q
7c20e9ab34 add move cards from top of library until dialog (#4648)
a bit of a hack, the client will use the play top card action and then
compare it with the propmpted expression, as if you were cascading
normally but really fast

the new keybind for this is ctrl shift y

I have ratelimited the action to 10 cards a second
2023-08-06 17:53:07 -04:00
ebbit1q
cb52605928 use a regex to remove all reserved characters from file names (#4804) 2023-08-06 17:46:22 -04:00
Guangcong Luo
bd3100dcda Update macOS Monterey app icon (#4805) 2023-08-06 17:46:03 -04:00
ebbit1q
afb7c35cfd add a way to replace the user facing list of disallowed words (#4807) 2023-08-06 17:45:37 -04:00
tooomm
5b694a55d2 CI: Remove fedora 36 (#4799)
* remove fedora 36

* remove fedora 36

* remove fedora 36
2023-08-04 02:02:44 +02:00
SlightlyCircuitous
f750a4cd72 Remove Ubuntu 22.10 Kinetic Kudu Build (#4826) 2023-07-25 21:34:18 -04:00
SlightlyCircuitous
eddeaaf52a Add Debian 12 "Bookworm" Build (#4812) 2023-06-14 20:28:12 -04:00
tooomm
2b42bee424 Webclient: lint (#4810)
* lint

* lint
2023-05-14 00:09:40 +02:00
SlightlyCircuitous
b9706c0cc1 Add indentation (#4806)
Improves readability
2023-05-09 17:08:06 -04:00
transifex-integration[bot]
800b21b000 Apply translations in pt_BR (#4801)
100% translated for the source file 'cockatrice/cockatrice_en@source.ts'
on the 'pt_BR' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-05-09 17:07:23 -04:00
transifex-integration[bot]
d1736a25bb Translate webclient/src/i18n-default.json in pt_BR (#4800)
100% translated for the source file 'webclient/src/i18n-default.json'
on the 'pt_BR' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-05-09 17:06:40 -04:00
ebbit1q
f269e5fe58 add fedora 38, scheduled to release next week (#4785) 2023-04-30 14:21:13 +02:00
tooomm
45a5296013 CI: Fix versions before deprecation (#4798)
* fix deprecation warning

* fix deprecation warning
2023-04-30 13:51:09 +02:00
SlightlyCircuitous
5f0ab2a177 CPack DMG Installer with Background Image (#4787)
* Set some CPack variables

WIP for DMG background image

* Use a .tif for background

* Add Background image and DS_Store script

Script is from https://www.kitware.com//creating-mac-os-x-packages-with-cmake/ with cmake.app changed to cockatrice.app

* Add position for all icons

* Adjust background size and icon position

This draws dbconverter off the screen as a hack to hide it, so hopefully it doesn't break.

* Add files via upload

* Change Icon Position

Icon position is probably relative to the window edge, not the edge of the screen so the numbers should be lower.

* Adjust Icon Position and Window Boundaries

Applescript seems to use the center of the icon rather than the left corner so the coordinates need to be adjusted by 64 px. 

The finder window counts the 22 px of the title bar in the window size so 22 must be added to the fourth coord of position to fit the entire image.

* Fix spelling of Servatrice

* Satifsfy Code Style

* Use vector format (eps)

* Delete background.tif

* Use vector format (eps)

* Use the right file extention in the script

* Point to svg

* Use svg

* Add svg for background image

* Delete background.eps

* Use plain SVG, adjust text

* Move applescript to /cmake

* Move background to /cmake

* Point CPack to /cmake

background and applescript now reside there

* Revert to .tif

* Use compressed .tif

* Use more specific file name

* Script expects .background:background.tif
2023-04-29 23:13:01 -04:00
ebbit1q
70ab02987a save sets dialog size (#4791)
* save sets dialog size

* reset sorting when restoring

* add to gitignore
2023-04-29 23:10:59 -04:00
ebbit1q
421da882d8 qt 6.5 compatibility (#4796)
* remove metatypes definitions

* deprecation of QApplication::setActiveWindow
2023-04-29 23:09:26 -04:00
SlightlyCircuitous
1a40102f71 Remove Debian 10 "Buster" Build (#4789)
* Drop Debian 10 Support

EOL September 10 2022

* Drop Debian 10 Support

EOL September 10, 2022

* Drop Debian 10 Support

EOL September 10, 2022
2023-04-19 23:04:35 +02:00
SlightlyCircuitous
1fbc10cd77 Add Ubuntu 23.04 Lunar Lobster Build (#4784)
* Create Lunar Dockerfile

Note that qt6-svg-dev and qt6-websockets-dev are renamed replacements for libqt6svg6-dev and libqt6websockets6-dev, respectively.

* Add Ubuntu Lunar

* Add Ubuntu Lunar to Template
2023-04-12 22:31:39 +02:00
skwerlman
87462398d8 show deck hash even when its invalid (#4595)
* show deck hash even when its invalid

* remove invalid deck hashes entirely

---------

Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2023-04-10 22:29:29 +02:00
dependabot[bot]
b33246b29f Bump loader-utils from 2.0.3 to 2.0.4 in /webclient (#4707)
Bumps [loader-utils](https://github.com/webpack/loader-utils) from 2.0.3 to 2.0.4.
- [Release notes](https://github.com/webpack/loader-utils/releases)
- [Changelog](https://github.com/webpack/loader-utils/blob/v2.0.4/CHANGELOG.md)
- [Commits](https://github.com/webpack/loader-utils/compare/v2.0.3...v2.0.4)

---
updated-dependencies:
- dependency-name: loader-utils
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-01 22:31:44 -04:00
transifex-integration[bot]
dbffe30f63 Translate /webclient/src/i18n-default.json in es (#4657)
translation completed for the source file '/webclient/src/i18n-default.json'
on the 'es' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-04-01 22:30:59 -04:00
ebbit1q
9ce450d0b0 add _substr_[pos]_[len] to picture download templates (#4762) 2023-04-01 22:29:57 -04:00
ebbit1q
f5f8acf1fd save downloaded xmls (#4736) 2023-04-01 22:19:57 -04:00
tooomm
304ed3cd60 UI: User info dialog updates (#4533) 2023-04-01 21:50:49 -04:00
dependabot[bot]
07248692ce Bump webpack from 5.74.0 to 5.76.1 in /webclient (#4774)
Bumps [webpack](https://github.com/webpack/webpack) from 5.74.0 to 5.76.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.74.0...v5.76.1)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-15 23:46:50 -04:00
Brent Clark
cef99cba71 Add left nav (#4705)
* Automated translation update ( bf08a04cda )

* Add Layout component wip

* finish layout implementation

* convert header to left nav

* better nav item spacing

* return source files to original glory

* lint fix

* Remove height limit on login screen

* fix top spacing on 3-panel layout

---------

Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Brent Clark <brent@backboneiq.com>
2023-03-15 23:45:55 -04:00
SlightlyCircuitous
cab5f29b57 Add 'persistent' as valid attribute of related (#4771) 2023-03-10 02:36:17 +01:00
Basile Clement
55a2f75d16 Make cards rounded (#4765)
* Make cards rounded

Magic cards have rounded corners, and playing cards tend to have rounded
corners as well, but Cockatrice currently displays rectangular cards.

This can cause visual glitches when using image scans where the border
does not extend in the corner, and for this reason Cockatrice always
draws a (rectangular) border around the card to try and make it look a
bit better.

In this patch I take a different approach: rather than try to make
rounded pegs, er, cards, go into a square hole, the hole is now rounded.
More precisely, the AbstractCardItem now has a rounded rectangular shape
(with a corner of 5% of the width of the card, identical to that of
modern M:TG physical cards).

As a side effect, the card drawing gets a bit simplified by getting rid
of transformPainter() when drawing the card outline and using the
QPainter::drawPixmap overloads that takes a target QRectF instead.  This
means we no longer have to bother about card rotation when painting
since that's taken care of by the Graphics View framework (which
transformPainter() undoes).

* format

* Also give PileZone rounded corners

* Forgot untap status + bits of CardDragItem

* fix deckviewcard calculations

* Rounded CardInfoPicture
2023-03-07 01:41:08 +01:00
ebbit1q
a416ee8f2b set target to sha in release creation (#4763) 2023-03-03 11:40:16 -05:00
Basile Clement
c14936c63c PictureLoader: Replace downloadedPics cache with QNetworkCache (#4756)
* PictureLoader: Replace downloadedPics cache with QNetworkCache

Currently when the "Download card pictures on the fly" option is
enabled, Cockatrice stores downloaded pictures into a downloadedPics
sub-folder, keyed on set and card name. If a picture is found in that
folder, we never try to download a picture for that card ever again
(until it is reprinted in a more recent set, I guess).

This has the unfortunate consequence that if you change the URLs for
downloading card images, the changes are not applied to cards that
already have their picture downloaded. In particular, if you use
localized card pictures (through !sflang!), you get a mix of cards in
different languages depending on the currently configured language at
the time each card was downloaded.

This patch removes that mechanism in favor of setting a
QNetworkDiskCache on the QNetworkAccessManager used by the PictureLoader
to download pictures. The QNetworkDiskCache caches the picture keyed on
their URL: if the URL changes, a new request will be made. In
particular, if you use picture URLs with !sflang! and change the
language, pictures for the current language will be downloaded even for
cards that already have a picture. The QNetworkDiskCache is configured
with a maximum size of 4GB, which should be enough to hold one
high-quality JPEG for each M:TG card in existence.

Note that this does not affect the existing mechanism for defining
custom card art, either through the CUSTOM directory or the set-based
one.  Cockatrice will still read existing cards in the downloadedPics
directory as before, it will just no longer write into that directory
(since pictures are cached by the QNetworkDiskCache instead). To fully
switch to the new cache, users should use the "Delete Downloaded Images"
button in the settings: it will clear the QNetworkDiskCache but also
remove the downloadedPics directory.

* Do not use system cache dir for portable installs

* Add settings for network cache size

* Delete corrupted cache entries

* Use old-style connect() syntax (Qt5 build failure)

* Add setNetworkCacheSizeInMB to test mocks

* setTransferTimeout was added in Qt 5.15

* Improve logging messages

We now have the following messages

 - "Trying to download picture from url: URL" before loading a picture
   when picture download is enabled
 - "Trying to load picture from cache: URL" before loading a picture
   when picture download is disabled (i.e. cache-only offline mode)
 - "Removing corrupted cache file for url URL and retrying (ERR)" when
   when we fail to load a picture from the cache. Usually, this should
   be due to the timeout, in which case ERR will be "Operation
   Canceled".
 - "Download failed for url URL (ERR)" when there was an error
   downloading a picture from the network (ERR is the error message)
 - "Following redirect to URL" and "Following cached redirect to URL"
   when following a redirect (from network/from cache)
 - "Image successfully downloaded from URL" and "Image successfully
   loaded from cached url at URL" on success
 - "Possible cached/downloaded picture at URL could not be loaded" on
   ImageReader error

* Clarify that network cache is on disk

Also migrate "Delete Downloaded Image" to a "Clear" button right next to
the network cache size.

* Remove qPrintable

* Move pixmap cache settings to card sources

* qDebug().nospace()

* some formatting on debug messages

* format

* inverted condition

---------

Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2023-03-03 16:55:17 +01:00
Basile Clement
42e7a8b423 Better support Double-Faced Cards (#4753)
* Better support Double-Faced Cards

This patch allows cards to be (virtually) transformed into other cards
while preserving their state, essentially implemeting the MTG mechanic
of the same name.

On the server side, this is implemented by allowing cards to be "stashed
away". A card that is stashed away is not in any zone, but is instead
owned by another card. When a token is destroyed due to a zone change,
if it had a card stashed away, that card is placed in the target zone
instead of the token.

On the database side, `attach="transform"` is used on `<reverse>` and
`<reverse-related>` to indicate that the created token should be
transformed this way.

Old servers ignore the new field in `Command_CreateToken` and will
perform a regular attachment, as currently.

* Address review comments

* Prevent tokens from being stashed

* format.sh
2023-03-03 16:54:51 +01:00
ebbit1q
4558b1c7ef set the target in the created release (#4758)
the target needs to be the current short commit hash because it is being
compared to by the updater, the default is "master" which breaks the
updater.
2023-02-23 01:11:41 +01:00
SlightlyCircuitous
f444ba9665 Corrected edition search syntax (#4752)
* Replaced "e:lea,leb" with "e:lea or e:leb"
* Removed "e:lea,leb -(e:lea e:leb) (Cards that appear in Alpha or Beta but not in both editions)" as this does not produce results in Cockatrice (even when using "or" instead of "," as above)
2023-02-16 12:08:00 +01:00
ebbit1q
787c8d740b replace github release actions with the gh tool in bash (#4746)
* replace github release actions with the gh tool in bash

* set macos 10.15 qt version to 6.2 LTS
2023-02-10 03:33:48 -05:00
Basile Clement
ef38a8bb2b Re-add missing '/' separator in <pt> after b282df2e27 (#4747)
In b282df2e27 (#4728) the logic for
creating <pt> values was updated to avoid adding a final slash after an
existing power value and missing toughness value. This works by setting
the ptSeparator to an empty string when either the power or the
toughness is undefined. However, due to the ptSeparator variable being
scoped out of the `for` loop, this causes all remaining cards to have an
empty string as a separator, ending up with <pt> values of e.g. 21
instead of 2/1.

Moreover, the implementation from #4728 is ambiguous in the case of a
card having a toughness value but no power value: in that situation, it
creates a <pt> entry with the toughness value and no separator, which is
not a good idea since it is not possible to know if <pt>2</pt> means
power 2 and no toughness, or no power and toughness 2 (Cockatrice takes
the first interpretation).

To avoid ambiguities, the <pt> value is now one of:

 1. A regular P/T value when the card has power and toughness
 2. A simplified P value when the card has power but no toughness
 3. A simplified /T value when the card has toughness but no power
 4. Absent when the card has neither power nor toughness

Note that, as far as I can tell, Cockatrice seems to (incorrectly, IMO)
ignore the initial slash if present in Player::parsePT, and treat /T as
a power value. However that is a separate issue: this patch is concerned
with Oracle and ensuring proper values in cards.xml, not with how
Cockatrice interprets those values.
2023-02-08 19:59:14 +01:00
Jeremy Letto
b5d35d346a Add architecture image to webclient README (#4745) 2023-02-08 18:04:23 +01:00
Basile Clement
9a7b15d19b Allow revealing specific cards from hand and library (#4743)
Currently Cockatrice allows revealing the whole hand, or one card at
random from the hand. Sometimes, a player needs to reveal a specific
card from their hand instead, which is not supported. To achieve a
similar effect, players usually move the corresponding card (or cards)
to a public zone, then back to their hand. While this works, it is
unsatisfactory (compared to a regular reveal, you can't keep the
"revealed" window around, for one) and somewhat unintuitive.

This patch adds a "Reveal to..." menu to cards and card selections in
the player's hand or in custom zones (this includes looking at the
player's library). This menu allows revealing a card or set of cards to
any given player, or to all players.

To implement this functionality at the protocol level, the existing
RevealCards command is extended to support revealing multiple specific
cards. This is done by making `card_id` a non-packed repeated field in
the `Command_RevealCards` and `Event_RevealCards` protobufs.  Using a
non-packed repeated fields allows maintaining backwards compatibility:
an empty optional field is encoded the same way as an empty non-packed
list, an optional field with a value is encoded the same way as a
one-element non-packed list, and when decoding a multi-elements
non-packed list as an optional, only the last item in the list is read.

Since the RevealCards command already exists, and due to the compatible
encodings, a new client connecting to an old server can reveal a single
specific card from their hand. When trying to reveal multiple cards at
once, the old server will only see the request for one of the cards to
be revealed, and the player will have to reveal each card separately.

On the other hand, `Event_RevealedCards` already has an explicit list of
cards revealed by the server, and the `card_id` field is only used when
exactly one card has been revealed: thus, old and new clients will
behave identically when receiving a new `Event_RevealedCards`. In
particular, if a player using a new client reveals multiple cards from
their hand on a new server, another player using an old client will
correctly see all the revealed cards.

The approach used to build the "Reveal to..." menu is slightly different
from the approach used to build other player selection menus. Because
the "Reveal to..." menu is specific to each card, but must also be
updated whenever a player is added to or removed from the game, I chose
to re-create it on the fly whenever a card is clicked, as that seemed
the safest way to avoid both memory leaks and inconsistent state given
my understanding of the code.
2023-02-07 17:12:04 -05:00
Zach H
ba35a11e82 Find OpenSSL on Windows (#4730) 2023-02-07 16:47:50 -05:00
Basile Clement
00c9efe541 Enable buttons for current game when receiving server response (#4737)
* Enable buttons for current game when receiving server response

Previously, upon joining a game, we were unconditionally re-enabling the
"Join" button in the lobby, even if it was not enabled in the first
place, causing #4698. This could also lead to issues where if the user
selects a different game after joining (which they can do in case of
e.g. network connectivity issues), the "Join as spectator" button could
get incorrectly disabled.

This fixes #4698 by re-enabling the buttons based on the state of the
currently selected game at the time the response is received. This also
avoids inconsistencies if a different game has been selected in between
joining and receiving a response from the server.

* Typo: enable gameButton in enableButtons

The "create game" button was incorrectly being disabled in enableButtons
whereas (as the name indicates) it should have been enabled

* Remove misleading comment about race conditions
2023-02-06 13:49:45 +01:00
cajun
44d1ab348b Add Oracle support for persistent & Fix persistent on reverse-related (#4742)
* fix persistent reverse-related

* create relations from spellbook property

* run format.sh
2023-02-06 07:00:54 -05:00
Zach H
f25e4785ae FIX #4665: Address missing sound on Qt5 Builds (#4733)
* FIX #4665: Address missing sound on Qt5 Builds

* FIX #4665: Address missing sound on Qt5 Builds

* Include both engines
2023-02-05 22:05:47 -05:00
Zach H
4c290aec57 Fix #4706: don't replace ampersands when loading from plain text (#4734)
* Fix #4706: Exit linting early if a card with the exact name is found first

* Remove ampersand conversion

* put back

* Update tests

* Format

* don't use qsizetype

---------

Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2023-01-30 00:37:28 +01:00
tooomm
42d1d66d9b CI: macOS-10.15 environment is deprecated (#4664)
* macOS-10.15 environment is deprecated

Build for 10.14 has to be dropped.
Build for 10.15 can be preserved via 11.

* update xcode versions

* Xcode 13.0 doesn't work for us on Big Sur

* [skip ci] update list of binaries
2023-01-29 12:47:42 -05:00
ebbit1q
06c25301a5 update build dockerfiles (#4732) 2023-01-23 18:14:35 -05:00
transifex-integration[bot]
43dbb45cc6 Translate /webclient/src/i18n-default.json in it (#4718)
translation completed updated for the source file '/webclient/src/i18n-default.json'
on the 'it' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2023-01-23 02:26:24 -05:00
dependabot[bot]
9fb62de5cb Bump json5 from 1.0.1 to 1.0.2 in /webclient (#4729)
Bumps [json5](https://github.com/json5/json5) from 1.0.1 to 1.0.2.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v1.0.1...v1.0.2)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-23 02:26:12 -05:00
PakhomCh
b282df2e27 Fixed ptSeparation oracle issue (#4728)
* Fixed psTeparation oracle issue

* Update oracle/src/oracleimporter.cpp

Co-authored-by: PakhomCh <pakhomch@gmail.com>
2023-01-16 13:30:19 -05:00
ebbit1q
da8f57f397 remove link to google doc roadmap from readme (#4727) 2023-01-02 22:07:18 -05:00
SlightlyCircuitous
e9f1992c7f Add URL to explain message macros (#4712)
* Add URL Link to Explain Message Macros

* Add URL Link to Explain Message Macros

* Revert custom shortcuts wiki link

* Conform to formatting guidelines
2022-12-11 23:54:06 +01:00
transifex-integration[bot]
2c94a6a64e Apply translations in it (#4714)
translation completed for the source file '/cockatrice/cockatrice_en@source.ts'
on the 'it' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-12-11 23:42:51 +01:00
SlightlyCircuitous
6a5e0a8501 Display Welcome Message as Most Recent Message (#4720)
* Move Join Message Block

- Moves Join Message code block to after the for loop that gets old chat message, which makes the Join Message the most recent message in the chat box instead of the oldest
-Only the rc.enqueuePostResponseItem() line really needs to move for functionality, but I have moved the whole block for readability

* Comply with formatting guide

-Remove offending white space
2022-12-11 23:40:47 +01:00
Jeremy Letto
26d7fe2ff0 Webatrice: update deps (#4700)
* save work

* fix reset styling

* fix toast reducer

* update non-react deps

* update react libraries

* remove jquery, use sanitize-html instead

* add missing change

* fix deps and dev deps

* update workflow to target Node 16

* run @mui/codemod to remove @mui/styles

* add default body font size

* update react 17 to 18

* declare enum before use

* add rel attr to links

* fix font sizing issue

* trailing commas

* refactor deep destructuring

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-11-01 12:41:42 -05:00
ebbit1q
5854a635ca fix deprecated usage of set-output (#4699) 2022-10-31 23:26:26 +01:00
ebbit1q
3d4858b840 use qt6 in arch builds (#4691)
* use qt6 in arch builds

* fix 6.4.0 deprecations
2022-10-31 23:26:13 +01:00
ebbit1q
dec2a252fa remove dependency on deprecated qt5 libraries for qt6 (#4692)
* remove dependency on deprecated qt5 libraries for qt6

removes the use of qt6-5compat for builds
replaces use of QRegExp with QRegularExpression
fixes incorrect usage of QRegExp
removes use of QTextCodec
fixes incorrect usage of QTextCodec
sets qtlinguist as a required component for qt6

* fix anchoredPattern not existing in qt 5.11
2022-10-31 23:24:11 +01:00
Jeremy Letto
f619ef23fd Upgrade to MUI 5 (#4606)
* save work

* fix perf issue on i18n rollup

* fix reset styling

* move body line-height from reset

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-10-29 20:17:03 -05:00
Antoine Dahan
72743e834e Selecting game already open in a tab brings user to that tab. (#4653)
* When trying to join a game from GameSelector that's already been joined by you, navigate to its game tab.

* return immediately, do not change button states

Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2022-10-29 15:46:50 +02:00
Tobyclark
8e4ddf366c Added hint when drawing 0 cards (#4697)
* Logging a player drawing 0 cards will now result in the message "player had no cards left to draw."

* Added hint when drawing when deck is empty

* Added hint when drawing when deck is empty

* Added hint when drawing when deck is empty

* Update cockatrice/src/messagelogwidget.cpp

update log message to present tense

Co-authored-by: ebbit1q <ebbit1q@gmail.com>

* added deckIsEmpty parameter to messagelogwidget::logDrawCards

* run format

Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2022-10-29 15:46:29 +02:00
ebbit1q
b99bd0176a update deprecated workflow actions (#4690) 2022-10-17 23:55:07 -04:00
ebbit1q
a68b98b245 update vcpkg submodule (#4689) 2022-10-17 18:23:39 -04:00
transifex-integration[bot]
a69d6ff1b4 Translate /oracle/oracle_en@source.ts in it (#4688)
translation completed for the source file '/oracle/oracle_en@source.ts'
on the 'it' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-10-17 16:43:31 -04:00
transifex-integration[bot]
ec679e95fd Translate /webclient/src/i18n-default.json in fr (#4678)
translation completed updated for the source file '/webclient/src/i18n-default.json'
on the 'fr' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-10-17 16:43:14 -04:00
ebbit1q
3f78235a74 fix updater with changes in release name (#4666)
* fix updater with changes in release name

* clangify
2022-10-17 16:42:08 -04:00
ebbit1q
c8a2fd78b0 fix crash when right clicking a user's name in a replay (#4681)
this happened when viewing a replay with the "view replay" option in the
top menu, instead of using the replays tab while connected to a server.
this uses the local game player instead of the online one which does not
initialize the player info of the local spectating player, this causes a
crash when opening the context menu on another player in the replay from
one of their chat messages as it tries to check if you're a registered
user and could add them as a friend etc.
it now regards the uninitialized player info as an unregistered user and
will not show these options.
2022-10-17 16:40:27 -04:00
ebbit1q
45cf08111a fix crash when a cardmenu becomes an orphan (#4682)
* fix crash when a cardmenu becomes an orphan

when a cardmenu is closed the cursor on that card reverts to the open
hand, this crashed the client when that card would be destroyed or moved
the act of reverting to the open hand now happens as an emitted signal,
this way it just doesn't exist anymore when the card is deleted.

* simplify fix
2022-10-17 16:38:44 -04:00
ebbit1q
527ac36129 update card menu immediately on card counter event (#4686)
fixes #4658
2022-10-17 16:37:32 -04:00
ebbit1q
a7232513a7 remove cards being looked at from the count on drawing (#4671) 2022-10-17 16:35:54 -04:00
ebbit1q
90f187e885 fix 4679 (#4680) 2022-10-02 13:23:35 -04:00
ebbit1q
235adbbdf1 fix sound slider on qt6 (#4642) 2022-09-01 19:01:20 +02:00
cajun
40c88fe385 Conjured tokens xml attribute (#4646)
* Conjured xml attr

Add conjured attribute to related xml tags that makes those cards not be destroyed when they leave the battlefield.

* fix build errors, add sarkhan to test

* update oracle importer to support spellbooks from json

* debugging

* fix weird spacing

* fix oracle spacing too

* simplify if/else

Co-authored-by: Zach H <zahalpern+github@gmail.com>

* rename, remove oracle update

* remove extra linebreak

* run format.sh again
2022-09-01 08:45:04 +02:00
tooomm
54b7943d17 CI: Update Qt setup on Windows (#4654)
* use new internal caching

* Update desktop-build.yml

* Update desktop-build.yml

* Update desktop-build.yml

* use newest patch version

jurplel/install-qt-action#version

* python not needed

* Update desktop-build.yml

* Update desktop-build.yml

* Update desktop-build.yml

* Update desktop-build.yml

* Update desktop-build.yml

* install openssl via tools

* add $RUNNER_WORKSPACE location

Source: https://www.theserverside.com/blog/Coffee-Talk-Java-News-Stories-and-Opinions/environment-variables-full-list-github-actions

* Update FindWin32SslRuntime.cmake

* Update FindWin32SslRuntime.cmake

* Update FindWin32SslRuntime.cmake

* Update FindWin32SslRuntime.cmake

* Delete download_openssl.sh

* cleanup

* [skip ci] improve short-circuit evaluation comment

* restructure
2022-09-01 02:38:25 +02:00
ebbit1q
2f100f2ba3 fix keepalive being multiplied in server timeout (#4663)
* fix keepalive being multiplied in server timeout

a timeout happened after the client not receiving anything for
keepalive * keepalive * maxtimeout (5 * 5 * 10) seconds instead of what
you'd expect, it now only uses keepalive once instead of twice this
means it should now take 50 seconds to time out when disconnected

* change timeout to 15 seconds instead

change time between pings to 3 from 5 seconds
change timout to 5 from 10 repeats
2022-09-01 02:38:10 +02:00
cajun
b5305aa5e4 Attach and Unattach apply to entire selection (#4651)
* (un)attach applies to entire selection

* additional code formatting, just because it's nicer

Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2022-08-25 21:26:29 -04:00
Gian Furrer
a2624e36f3 fix broken link in readme (#4659) 2022-08-11 23:21:30 +02:00
transifex-integration[bot]
540511befd Apply translations in es (#4656)
translation completed for the source file '/cockatrice/cockatrice_en@source.ts'
on the 'es' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-08-10 16:35:09 +02:00
tooomm
014e73d569 CI: Remove EOL builds (Fedora 34 and Ubuntu 21.10) (#4628)
* Fedora 34 EOL

* Delete .ci/Fedora34 directory

* update fedora

* Ubuntu 21.10 EOL

* Ubuntu 21.10 EOL

* Delete .ci/UbuntuImpish directory
2022-08-09 20:56:27 +02:00
ebbit1q
e2c256db5b fix typo in format.sh (#4647) 2022-07-05 15:49:19 -04:00
ebbit1q
28aa473362 rework formatting with cmake-format (#4627)
* merge clangify and cmakify into format.sh

update desktop lint workflow to 22.04

print cmake-format version as well

um, rename things?

add extra examples to format.sh --help
add option to not run clang-format
fix version display in .ci/lint_cpp.sh
fix relative paths in format.sh

fix formatting dirs

* run ./format.sh --cmake --branch ""

* revert formatting of cmake comments
2022-06-08 21:14:26 -04:00
ebbit1q
b79506fbcf add a windows 7 specific build to ci (#4639) 2022-06-08 21:09:35 -04:00
ebbit1q
afbd7252ac remove stopping sounds from interrupting each other (#4640) 2022-06-08 21:06:44 -04:00
ebbit1q
3e5b7cd392 remove unused variables (#4636) 2022-06-08 00:32:11 +02:00
Dawid Skórzewski
77622095d5 Fixed typos in Qt6_FOUND function reference (#4638) 2022-06-04 22:33:02 -04:00
dependabot[bot]
8ee71300a2 Bump protobufjs from 6.11.2 to 6.11.3 in /webclient (#4637)
Bumps [protobufjs](https://github.com/protobufjs/protobuf.js) from 6.11.2 to 6.11.3.
- [Release notes](https://github.com/protobufjs/protobuf.js/releases)
- [Changelog](https://github.com/protobufjs/protobuf.js/blob/v6.11.3/CHANGELOG.md)
- [Commits](https://github.com/protobufjs/protobuf.js/compare/v6.11.2...v6.11.3)

---
updated-dependencies:
- dependency-name: protobufjs
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-03 01:21:37 -04:00
dependabot[bot]
d79971edbc Bump dexie from 3.2.1 to 3.2.2 in /webclient (#4629)
Bumps [dexie](https://github.com/dfahlander/Dexie.js) from 3.2.1 to 3.2.2.
- [Release notes](https://github.com/dfahlander/Dexie.js/releases)
- [Commits](https://github.com/dfahlander/Dexie.js/compare/v3.2.1...v3.2.2)

---
updated-dependencies:
- dependency-name: dexie
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-23 17:32:23 -04:00
Impyrical
273ebb22e4 Tackling #4041 (#4625)
Copy contents of selected cell from card database when ctrl-c is
pressed, mimicking the behavior of ctrl-c in the deck editor.
2022-05-21 01:22:08 +02:00
Zach H
6b86e4d463 Update configuration path to be backwards compatible (#4620)
* Update configuration path to be backwards compatible
Windows users have used AppData/Local/Cockatrice, whereas the new system was using AppData/Roaming/Cockatrice. This reverts the behavior in a Qt5/6 way.
2022-05-12 18:13:49 -04:00
ZeldaZach
a8e1dc3b18 Re-Run cmakeify 2022-05-09 18:01:30 -04:00
Zach H
a95b338c80 Add cmake format (#4618)
* Support CMakeify operation

* Run Cmakeify

* Update cmakeify.sh

Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2022-05-08 21:22:43 +02:00
Zach H
3e90f109a2 Prevent bad access potential for removals by checking bounds (#4617)
* Prevent bad access potential for removals by checking bounds
Fix #4616

Switch to removeOne instead of bound checking removeAt

* Revert server cardzone check
2022-05-08 20:26:50 +02:00
ZeldaZach
b02adccf87 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
2022-05-06 17:31:08 -04:00
dependabot[bot]
accd5e4df7 Bump async from 2.6.3 to 2.6.4 in /webclient (#4614)
Bumps [async](https://github.com/caolan/async) from 2.6.3 to 2.6.4.
- [Release notes](https://github.com/caolan/async/releases)
- [Changelog](https://github.com/caolan/async/blob/v2.6.4/CHANGELOG.md)
- [Commits](https://github.com/caolan/async/compare/v2.6.3...v2.6.4)

---
updated-dependencies:
- dependency-name: async
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-06 17:31:08 -04:00
dependabot[bot]
d007196059 Bump ejs from 3.1.6 to 3.1.7 in /webclient (#4613)
Bumps [ejs](https://github.com/mde/ejs) from 3.1.6 to 3.1.7.
- [Release notes](https://github.com/mde/ejs/releases)
- [Changelog](https://github.com/mde/ejs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mde/ejs/compare/v3.1.6...v3.1.7)

---
updated-dependencies:
- dependency-name: ejs
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-06 17:31:08 -04:00
ebbit1q
79501a4af7 fix the uid and gid of the user in the docker container (#4610)
* fix the uid and gid of the user in the container

this fixes this error:
unsafe repository ('/src' is owned by someone else)
this caused the hash to go missing in the version number

* add --interactive option to .ci/docker.sh

* add --dir to .ci/compile.sh

* fix up the comments on the ci scripts

* add extra comment to docker.sh
2022-04-18 19:04:49 -04:00
ebbit1q
64c6611ea5 env vars don't go into docker containers like that (#4609)
reverts a bit of #4580 92ed53e13a
2022-04-17 22:11:55 -04:00
Zach H
a532a63403 Change actions to use Windows 2019
Windows 2022 isn't stable yet with the Qt installer, and we need to cut releases so this unblocks us
2022-04-02 00:55:05 -04:00
tooomm
c10c69d0a9 fix if condition on ci translations (#4603) 2022-04-01 11:24:51 +02:00
dependabot[bot]
191d5a83a9 Bump minimist from 1.2.5 to 1.2.6 in /webclient (#4601)
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-26 21:50:52 -04:00
dependabot[bot]
de69e2c41f Bump node-forge from 1.2.1 to 1.3.0 in /webclient (#4600)
Bumps [node-forge](https://github.com/digitalbazaar/forge) from 1.2.1 to 1.3.0.
- [Release notes](https://github.com/digitalbazaar/forge/releases)
- [Changelog](https://github.com/digitalbazaar/forge/blob/main/CHANGELOG.md)
- [Commits](https://github.com/digitalbazaar/forge/compare/v1.2.1...v1.3.0)

---
updated-dependencies:
- dependency-name: node-forge
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-26 21:49:27 -04:00
Jeremy Letto
6d200d17b7 close previous testConnect attempts (#4598)
* close previous testConnect attempts

* remove onerror handler when canceling previous attempt

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-03-22 17:42:58 -05:00
Jeremy Letto
4899b6cfef add kosovo flag (#4597)
* add kosovo flag

* add xk and eu flags to cockatrice

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2022-03-19 20:07:40 -05:00
Jeremy Letto
0ff59e6d1e test connection UI (#4596)
* test connection UI

* cleanup

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-03-19 19:22:00 -05:00
Jeremy Letto
00a2a8ab71 update pr-bt translation file (#4593)
Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-03-13 15:59:36 -04:00
Jeremy Letto
2c702d3579 Webatrice: husky (#4591) 2022-03-13 13:44:51 -04:00
ebbit1q
b464fa8d99 actualise country names (#4592)
see wikipedia here:
https://en.m.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements
2022-03-07 21:46:09 -06:00
ZeldaZach
2b330940e1 ncu update 2022-03-07 13:46:43 -05:00
Jeremy Letto
0d0337f091 Webatrice: update package.json (#4590)
* update package.json

* cleanup

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-03-07 13:43:01 -05:00
Jeremy Letto
533045445a Webatrice: improve language dropdown (#4589)
* useLocaleSort hook, translate language dropdown

* add pt-BR translation

* fix pt-BR flag

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-03-06 20:12:27 -06:00
Jeremy Letto
21f7dd5eba add fr and nl translations (#4587)
* add fr and nl translations

* update fr

* fix password label translation

* translate country strings

* fix double accents

* fix Ivory Coast

* sort countries

* use more performant Collator over localeCompare

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-03-06 17:19:51 -06:00
Jeremy Letto
f5b973e15c Webatrice: i18n login screen (#4584)
* i18n: login container and form

* i18n: activate, host, and register forms

* i18n: reset password forms

* i18n: login dialogs, ICU formatting

* i18n: login containers and components

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-03-02 22:34:57 -06:00
ebbit1q
baaf261116 use utf8 instead of the system encoding (#4582)
* use utf8 instead of the system preference

* found another toLocal8Bit
2022-02-27 22:33:36 +01:00
ebbit1q
92ed53e13a update workflow to use windows 2022 image (#4580)
* update workflow to use windows 2022 image

* return the version of the run vcpkg action

the action has been changed to now use a vcpkg.json file instead of the
txt file we use now, we should try to find a way to update it to the new
workflow in case the current one becomes obsolete

* clean up a bit for consistency

* run ctest directly instead of relying on the makefile

* set -C flag for ctest

* set config option for cmake --build

this option is ignored for other platforms
2022-02-27 22:32:54 +01:00
Jeremy Letto
2a54e9d7d1 Webatrice: fix saved password (#4563)
* fix saved label, and fix using hashedPassword when Save is unchecked

* update host only after successful login

* cleanup

* fix ability to deselect saved password on successful login

* cleanup

* clear options after connection

* fix registration saved username

* cleanup

* change label

* fix tests

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-02-27 10:12:38 -06:00
Jeremy Letto
9577ada171 Webatrice: i18n (#4562)
* implement i18n capability

* reset package.lock file

* remove custom fallback

* fix relative path for i18n files

* check for language support before fetch request

* add LanguageDropdown component, es translation file to prove functionality

* remove boilerplate

* bundle default english translation with app

* add missing file

* rollup component-level i18n files

* cleanup

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-02-26 21:36:53 -06:00
ebbit1q
217dc09c0f fix image downscaling (#4567) 2022-02-23 23:46:53 +01:00
ebbit1q
7108eb42c8 implement custom protobuf debug string creation (#4532)
* implement custom protobuf debug log string creation

* add version guards

* add missing header

* debian10 repository misses headers

* clean up logging messages a bit

* fix some more formatting on debug messages
2022-02-23 23:46:23 +01:00
ebbit1q
eb3ce1fd7e hide revealed cards when they are shuffled (#4570) 2022-02-23 23:46:07 +01:00
dependabot[bot]
c88d44e16c Bump url-parse from 1.5.3 to 1.5.7 in /webclient (#4578)
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.3 to 1.5.7.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.5.3...1.5.7)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-22 23:44:27 -05:00
dependabot[bot]
ec2ad4c713 Bump follow-redirects from 1.14.7 to 1.14.8 in /webclient (#4574)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.7 to 1.14.8.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.7...v1.14.8)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-22 23:44:16 -05:00
Brent Clark
4c04b4ef5a Webatrice: Registration toasts (#4566)
* wip

* Registration Success Toast

* remove debugging code

* remove unused field

* Show toast on successful password reset

* Toast on account activation success

* lint and PR feedback

* Rework interface names to avoid collision

* Move CssBaseline to sibling of ToastProvider

Co-authored-by: Brent Clark <brent@backboneiq.com>
2022-02-16 02:40:30 +01:00
Jeremy Letto
88b861d632 Webatrice: improve prebuild steps and add .env configs (#4564)
* create .env file for server configuration

* render client version

* automate env file

* add prestart command

* create server-props.json instead of using .env

* automate master proto file

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-02-13 00:58:47 +01:00
ebbit1q
408a13c937 remove unused include in pb file (#4572)
shows as warning on compile:
session_commands.proto:2:1: warning: Import serverinfo_user.proto is unused.
2022-02-12 23:40:47 +01:00
ebbit1q
7d0a255a49 add database migration from blob to mediumblob (#4568) 2022-02-09 20:11:13 +01:00
ebbit1q
252883f67e set rx and txBytes to zero on initialization (#4569) 2022-02-09 17:57:35 +01:00
Jeremy Letto
bf08a04cda Webatrice: tech debt (#4560)
* turn autocomplete off by default on inputs

* trim input fields onSubmit

* move trim to form submit

* cleanup

* remove dead code

* protect trim against null values

* make password optional on Login for servers that allow unregisted logins

* cleanup

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-02-06 17:41:02 +01:00
Jeremy Letto
6928a2bd98 Webatrice: show loading screen until protobuf initializes (#4559)
* show loading screen until protobuf initializes

* cleanup

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-02-04 17:03:39 -05:00
Jeremy Letto
bb16ae09ef Webatrice: fix login bugs (#4557)
* fix login after failed connection attempts, limit connection attempt time

* fix register hashed password and salt

* add feature detection and Unsupported Browser screen

* nit

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-02-04 14:07:15 -05:00
Brent Clark
81d031ca0f Webclient: Add Toasts component and show on known host CUD operations (#4556)
* Add Toasts component and show on known host CUD operations

* add slide transition

* NIT

Co-authored-by: Brent Clark <brent@backboneiq.com>
2022-02-01 12:08:05 -06:00
Jeremy Letto
8203a2fdeb fix failed saltRequest (#4554)
* fix failed saltRequest

* improve requestSalt error handling

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-02-01 04:03:31 +01:00
tooomm
75f0d60dff Webclient: Update script (#4553)
* Update package.json

* introduce linting error

* Revert "introduce linting error"

This reverts commit 0a622bcb2e.
2022-01-31 15:14:22 +01:00
Jeremy Letto
992e28797f Webatrice: support hashed passwords in register and resetPassword (#4549)
* support hashed passwords in register and resetPassword

* lint

* support hashedPasswords for accountActivation

* use salt in post-register login step

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-01-30 22:09:16 -06:00
Jeremy Letto
92f941a54c renable login after fail attempt (#4552)
Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-01-30 22:06:30 -06:00
Jeremy Letto
4c31527832 implement password length requirements (#4551)
Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-01-31 03:51:01 +01:00
Jeremy Letto
febe029ed4 use CompanyDropdown component in registration form (#4548)
Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-01-30 19:47:10 -06:00
Jeremy Letto
1d780058c8 Webatrice: Add account validation dialog/form (#4547)
* Add account validation dialog/form

* clean up

* close registration dialog on token request

* remove dupe code

* add subtitle styling

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-01-30 19:42:34 -06:00
Brent Clark
513fcb0908 Webclient: Handle firing an event once (#4499)
* draft: handle firing an event once

* lint

* Prevent rapid double-click on sending messages

* no rest spread on single primative when sibling components exist

* clear message instead of using a fireOnce handler.

* fix tests

* remove unnecessary validate mock
2022-01-30 12:14:28 -05:00
dependabot[bot]
4bb13677c8 Bump nanoid from 3.1.30 to 3.2.0 in /webclient (#4542)
Bumps [nanoid](https://github.com/ai/nanoid) from 3.1.30 to 3.2.0.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.1.30...3.2.0)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-24 00:15:46 -05:00
ebbit1q
a5baf4303c create workflow for automatically updating the translation sources (#4543)
* create workflow for automatically updating the translation sources

* change to once per month
2022-01-23 23:14:48 +01:00
ZeldaZach
7aba404f2e Add i18n for Oracle and fix Transifex 2022-01-22 00:17:29 -05:00
Zach H
1b7e8f3a16 Re-add handling of i18n for Oracle (since it was manual before) (#4541)
Removes en@source from options menu intentionally
2022-01-21 23:35:04 -05:00
ZeldaZach
5cf93ad61c Remove empty languages 2022-01-21 22:59:47 -05:00
ZeldaZach
5a52e085a7 Translation Dump! 2022-01-21 22:28:07 -05:00
ebbit1q
5d31b70406 [WIP] add english translation (#4120)
* move en.ts to en@source.ts

* run lupdate

Co-authored-by: ZeldaZach <zahalpern+github@gmail.com>
2022-01-21 21:35:26 -05:00
tooomm
2885f93fdf run ci not only on pr's to master (#4537) 2022-01-20 18:27:26 +01:00
tooomm
d225f55e5a CI: Uniform job ordering and naming (#4534)
* remove xcode string in file name

* alphabetical ordering + newest to the top

* remove not needed entries

* append -bit to name

* chronological

* spaces
2022-01-20 02:51:42 +01:00
tooomm
69edc73585 cleanup (#4530) 2022-01-18 16:40:05 +01:00
tooomm
ead1143f2e Prettier settings dialog (#4357)
* prevent stretched layout in settings

* restore layout of settings pages with already expanding elements

* Support full screen resolution and set a minimum that works well no matter the screen size

Co-authored-by: ebbit1q <ebbit1q@gmail.com>
Co-authored-by: ZeldaZach <zahalpern+github@gmail.com>
2022-01-16 21:06:52 -05:00
ebbit1q
2fc85e0c08 use hashed passwords in all commands (#4493)
* protocol changes

* server changes

* client changes for password reset and registration

* add hashed password to change password in client

* always use hashed password to log in

* add warning to client when using plain text password

* require real password for changing email on server

this is backwards compatible as users logged in with a real password on
older clients will not need this, only users logged in with a hashed
password

* implement password dialog when changing email

* require min password length

* use qstringlist to build query instead

* use clear instead of = ""

* add max to password dialog

* use proper const ness in abstractclient

* reject too long passwords instead of trimming
2022-01-16 20:32:30 -05:00
ebbit1q
fcafcb340a remove all instances of the type long (#4519)
the long type has different sizes across operating systems and should
not be used

in the timeline an overflow could occur if the width in pixels
multiplied by the total amount of milliseconds in the replay is larger
than 2^31 which is easy enough considering with only 500 pixels width
you'll reach this number with only 1.2 hours of replay (about 4 million
millis), note that this would be windows exclusive as *nix uses 64 bits

~~qt-json's own repo suggests using qt5's implementation instead, testing
revealed this is quite a bit faster, contrary to #3480~~ testing proved
this to not be compatible with older qt versions

servatrice uses the qthread usleep function which used to be protected
but is now public

cockatrice is not compatible with qt4 and hasn't been for a while
2022-01-16 18:05:24 -05:00
ebbit1q
ae9b8b8f34 miscellaneous refactors (#4521) 2022-01-16 17:58:53 -05:00
ebbit1q
994704d353 implement max lengths for input dialogs that are sent to the server (#4522)
* implement max lengths for input dialogs that are sent to the server

* missed a double setMaxLength

* implement max string lengths server side

* add custom getText dialog with max length

* fix deck storage tab and miscellaneous server side

* add max size for deck uploads

* final pass on client side limits
2022-01-16 17:57:01 -05:00
Zach H
d61c604bf4 Address macOS issue where right-clicking a username in the main chat (#4523)
* Address macOS issue where right-clicking a username in the main chat (or game chat) areas would pop up a seemingly empty user profile. This is because the resize event is overridden and doesn't actually attempt to resize based on the size hint of the dialog. Now that we're explicit with the call, this resize should be forced and have comparable results to popping up user profile from the user list.

* use datetime for calculating account age (#4526)

* use datetime for calculating account age

make translating easier by using tr multiples
automatically account for leap days

Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2022-01-16 16:51:13 -05:00
tooomm
baaf22d0c4 UI: Improve alignment in user info (#4524)
* fix alignment

* tweaking

* lint

* limit flag to one column

* lint

* cleanup

* Update userinfobox.cpp

* re-add manual window resizing

* Update cockatrice/src/userinfobox.cpp

Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2022-01-16 22:49:41 +01:00
tooomm
368ff1793f CI: Add Debian 11 (#4525)
* add debian 11

* rename debian10

* fix space

* Update Dockerfile
2022-01-16 16:46:04 -05:00
dependabot[bot]
3253ad64fd Bump follow-redirects from 1.14.5 to 1.14.7 in /webclient (#4527)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.5 to 1.14.7.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.5...v1.14.7)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-16 16:32:22 -05:00
ebbit1q
1e70989f38 add password hash test (#4528)
* clangify tests

* add password hash test

* properly use googletest semantics
2022-01-16 16:32:11 -05:00
ebbit1q
f6634de18d replace fixed size of criteria in log tab (#4515)
it's now a max size with expanding policy, looks fine I guess
fixes #4510
2022-01-11 21:35:18 -05:00
ebbit1q
7903cd520a perform restartLayout conditionally (#4513)
* Revert "Fixed layout on Deck Editor not using last layout. It was reseting layout on ctor. (#4420)"

This reverts commit 3bc90003b3.

* restart layout on fresh installs
2022-01-08 16:03:53 -05:00
ebbit1q
26d1fcc944 simplify search string (#4516) 2022-01-08 16:02:54 -05:00
ebbit1q
59d4e64a8d use multiline input dialog for annotations (#4517) 2022-01-08 16:02:25 -05:00
ebbit1q
1347d88ddb also forgot this in #4496 (#4514) 2022-01-08 16:01:54 -05:00
ebbit1q
6981cca2ae use qt round for better cross platform consistency (#4518)
* use qt round for better cross platform consistency

* remove unnecessary casts
2022-01-08 16:01:15 -05:00
ebbit1q
4d6c9ede8c missed this in #4496 (#4512) 2022-01-07 00:34:56 -05:00
ebbit1q
e845c95816 remove ccache from mac builds (#4505) 2021-12-27 22:23:11 -05:00
ebbit1q
a9f2fc427b allow servatrice to exit early based on commandline options (#4504) 2021-12-26 16:47:37 +01:00
ebbit1q
07e6aadbbe deprecate the gender property from the protocol entirely (#4496)
* deprecate the gender property from the protocol entirely

* use obsolete instead of deprecated

* add the database migration

* update internal database version as well
2021-12-14 01:51:57 -05:00
tooomm
86881bbbc3 CI: Little tweaks to web (#4488) 2021-12-07 23:01:46 -05:00
Jeremy Letto
1f15445c69 connect reset password to login view (#4489) 2021-12-07 22:57:12 -05:00
ebbit1q
811ee54c76 Fix move (#4491)
* wip fix card moving on server

* fix flipped cards being moved as -1

* fix cards from hand being moved as -1
2021-12-07 22:56:58 -05:00
ebbit1q
d1a40fd36e fix regression in local games (#4490) 2021-12-07 22:54:36 -05:00
ebbit1q
a3d3aaaca8 fix server crash on receiving email without @ (#4492) 2021-11-30 19:44:20 -08:00
tooomm
c5aaa0bc2e CI: Add webclient (#4478) 2021-11-26 16:55:53 -05:00
Jeremy Letto
6dc9f004ce fix tests, add golden command (#4486)
Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2021-11-26 16:55:12 -05:00
Jeremy Letto
6ce346af4a Webatrice: KnownHosts component (#4456)
* refactor dexie services for future schema updates

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2021-11-25 22:12:23 -05:00
ZeldaZach
37879c4255 Re-lint on linux 2021-11-23 02:49:11 -05:00
Zach H
0683d1aced Support Server requests for MFA, Render failed UI statuses to user, C… (#4483)
* Support Server requests for MFA, Render failed UI statuses to user, Connect to KnownHosts component
2021-11-23 02:45:08 -05:00
Joseph Chamish
73c5956ece Dev/jchamish/forgotpassword (#4481)
* Implementation of Forgotten Password Reset

* Update webclient/src/hooks/useReduxEffect.tsx

Co-authored-by: Zach H <zahalpern+github@gmail.com>
2021-11-19 21:00:05 -05:00
Zach H
7c27e955d5 Support all OS development for linters and prevent linting while in dev mode (#4480) 2021-11-19 20:59:55 -05:00
Jeremy Letto
6ef394000b fix file line returns (#4482)
Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2021-11-18 19:46:49 -05:00
Aren Kasner
755a09bd83 country dropdown (#4479) 2021-11-16 16:55:30 -05:00
ZeldaZach
8b1daa21ef Revert "fix card moving on server (#4413)"
This reverts commit c25bf491e4.
2021-11-15 00:54:21 -05:00
ebbit1q
691bcb9338 comment out new feature from feature list (#4477)
we don't have an official release for a version that has this feature
yet
2021-11-15 00:44:16 -05:00
Joseph Chamish
911a303326 Fix the additional line endings (#4476) 2021-11-14 22:16:13 -05:00
Johannes
5652b56b45 Respect device pixel ratio when scaling card imgs (#4467) 2021-11-13 17:01:27 -05:00
Zach H
26acfd5102 Update packages & cleanup (#4475) 2021-11-13 15:30:49 -05:00
Zach H
f789e02096 Add ESLint & Run it against the system (#4470) 2021-11-13 14:56:15 -05:00
Zach H
43eee6b32e Support HashedPassword workflow for logins (#4469)
* Support HashedPassword workflow for logins

* Address comments in PR
2021-11-13 10:37:13 -06:00
ebbit1q
45d86e7ab7 allow login using hashed passwords (#4464)
* Support getting a user's password salt via initial websocket connection (added to Event_ServerIdentification)

* Nonsense stuff to figure out later

* move passwordhasher to correct location

* protobuf changes

* add ext to protobuf

* implement request password salt server side

* add supportspasswordhash to server identification

* check backwards compatibility

* reset some changes to master

* implement get password salt client side

* implement checking hashed passwords on server login

* check for registration requirement on getting password salt

* properly check password salt response and show errors

* remove unused property

* add password salt to list of response types

Co-authored-by: ZeldaZach <zahalpern+github@gmail.com>
2021-11-09 20:00:41 -05:00
ebbit1q
b0845837c2 add extra null check to set active card (#4460) 2021-11-04 22:20:20 -04:00
ebbit1q
a10226d096 finalise fedora support (#4461) 2021-11-04 22:20:00 -04:00
ParkTandem
5e3a524401 add eslint config (#4457) 2021-11-02 19:01:06 -05:00
gus
b095d9b82f Fixed issue #4332 - changed error message for replay folder download (#4455) 2021-11-02 02:08:41 +01:00
Jeremy Letto
4cb7240f9a small improvements (#4452)
Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2021-10-31 22:21:24 -04:00
Zach H
a87c66885c Webatrice: Account Registration form (pt2) (#4454)
Co-authored-by: ParkTandem <93353951+ParkTandem@users.noreply.github.com>
2021-10-31 22:15:51 -04:00
Zach H
ac300b0b6d Support password reset workflow on Webatrice (#4445)
* Support password reset workflow. Also fix issue where a user would be disconnected "randomly" if they had a failed login, then successful one. Refactored a bit on Status Labels since they weren't really necessary and added complexity.

* Disconnect in default cases where we don't know what to do, but shouldn't stay connected to the server
2021-10-31 22:03:38 -04:00
ebbit1q
013bb8269f set fedora 35 to be allowed failure (#4448)
it currently doesn't build at all yet
2021-10-31 17:55:10 -04:00
ebbit1q
7712862036 add ubuntu 21.10 to ci (#4446)
* add ubuntu 21.10 to ci

remove ubuntu 20.10

* forgot to update dockerfile

* add googletest from repos

* update downloaded gtest

ideally this should just grab the master version

* fix hash

* fix cmake issue
2021-10-30 22:15:50 -04:00
tooomm
656e3230de CI: Xcode update, fix Big Sur build (#4449)
* xcode update

* 12.5.1 --> 13.0

* Revert "12.5.1 --> 13.0"

This reverts commit 671ee2afe4.
2021-10-30 16:00:52 +02:00
ebbit1q
915c14f6cf add fedora 35 to ci (#4447)
remove fedora 33
2021-10-29 22:15:08 -04:00
Aren Kasner
ed32e72dc1 login page created (#4444)
login page html and css created with Seavor
2021-10-25 23:36:20 -04:00
Jeremy Letto
d684a9c5fc new login design (#4442)
* new login design

* remove effects file (wrong direction)

* add Known Hosts dropdown component

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2021-10-25 14:28:43 -04:00
ebbit1q
6f360374cc change the order in which the password challenge is performed (#4439)
this will force the user to always perform the challenge, meaning no
information on the account is leaked in case of failures
2021-10-23 20:18:08 -04:00
ebbit1q
bbbf3e2a65 don't reset pt if there is nothing to reset (#4438)
* don't reset pt if there is nothing to reset

when the client resets the pt of a card it intentionally does not
include cards that already have the correct pt, this can lead to the
client sending an empty command to the server, which will be rejected

* clangify
2021-10-23 20:04:52 -04:00
Zach H
b1ef8220ee Support Registration on Webatrice with a baseline of handling. (#4436)
* Support Registration on Webatrice with a baseline of handling. Still needs to support activation tokens & unit testing.

* Add support for account activation with token

* Activate Account refactor

* Fix typo

* Add Unit Testing for Commands/Events

* Changes based on review feedback
2021-10-20 21:07:35 -05:00
Jeremy Letto
ebebb9c4bb add Material UI theme support (#4437)
* add Material UI theme support

* add primary color palette

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2021-10-20 20:03:05 -04:00
Jeremy Letto
586f23cfa9 Webatrice websocket refactor (#4435)
* add unit tests for websocket events

* add unit tests for KeepAliveService, clean up keepAlive termination flow

* put keepAlive command in protobuf service and expose thru webClient

* secure wss

* rename files tsx to ts

* add localhost support for ws/wss connection

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2021-10-17 20:52:59 -04:00
Jeremy Letto
f75ff2a7c8 cleanup and unit tests (#4434)
* put socket.updateHistory behind SessionCommand

* rename /websocket files from .tsx to .ts

* add unit tests to websocket commands

* complete unit tests for webClient commands

* secure wss

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2021-10-17 16:15:09 -04:00
Jeremy Letto
e9ba195d7d Refactor websocket into separate services, clean up socket status communication (#4433)
* Refactor websocket into separate services, clean up socket status communication

* cleanup

* add EOF lines

* fix keepalive logged in check

* undo change

* fix keepalive connection check

* cleanup

* add typings

* secure connection

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2021-10-17 01:07:30 -04:00
Jeremy Letto
19333c53f6 secure webclient socket (#4432)
Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2021-10-14 21:58:34 -04:00
Jeremy Letto
36e5a399d5 Webatrice: card import wizard (#4397) 2021-10-14 21:42:35 -04:00
Bruno Mendes
dde0f568d9 Show country flag in user list of webclient (#4431) 2021-10-14 20:59:06 -04:00
Rafael Ritzel Tischler
3bc90003b3 Fixed layout on Deck Editor not using last layout. It was reseting layout on ctor. (#4420) 2021-09-14 16:41:04 -04:00
Danny Piper
689f65b38a Fix for poor performance with large decks (#4347)
* Fix for #4284

-> The menus have the update menu thing emitted when they get triggered.
| -> works surprising well https://youtu.be/KOOmhxvHA2c is a demo on a 10000ish card deck

* changed my comment to make sense

* fix to the issues that @ebbit1q found

what caued them idk

* Revert "fix to the issues that @ebbit1q found"

This reverts commit 20b1ad9f7a.

* actual fix for the issues @ebbit1q found

* its dirty but works

* fix cards in zoneviews not having a menu

* deleted isempty check as it is updated after the check

* key binds should work now

-> menus updated on zone change/attach/retranslate UI if selected

* clangify

* remove updateCardMenu from carditem entirely

updateCardMenu is done by the player and having it in carditem led to it
often being run multiple times, I've opted to instead run it in the
player and remove the signal entirely

the new logic updates the cardMenu every time a card is set as the
activeCard in the game tab

additionally a cardmenu can change while selected if the selected card:
moves zone, is flipped, or is attached (it receives the unattach action)

this is done in the player instead now, checking if the card is the
activeCard

this however exposes a flaw in the selection management where if you
unselect a card the activeCard is set to nullptr, this was an existing
bug and causes the action on selected cards to suddenly disappear, even
if there are other cards selected!

* revert null test of aCardMenu

Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2021-09-14 16:35:47 -04:00
tooomm
154e1084ba CI: Separate C++ linting (#4400)
* separate cpp lint

* made executable
2021-09-14 16:11:56 -04:00
tooomm
6df5cece04 CI: Update Qt version (Windows) (#4407)
* qt patch update

* qt 5.15

* fix qtdir export

* cleanup
2021-09-14 16:11:29 -04:00
ebbit1q
c8bb8b0aae add reset paths button to settings (#4384) 2021-09-14 16:10:40 -04:00
ebbit1q
1e995cd97c add option to delete a user's messages (#4362)
* add option to delete a user's messages

add optional parameter remove_messages to the ban and warn commands
add event for clients to redact messages
implement server side command and message handling
implement server history removal
todo: client side implementation

add option to remove messages to moderator action dialogs

add storage of message beginnings to chatview

add redactMessage command
handle Event_RemoveMessages on rooms

this approach is favored over parsing the chatroom after the fact but
will use additional memory to store the block indexes

this also leaves a problem in that user messages from the chat backlog
are not removed in the same way because they don't have a user
associated with them

add workaround for old qt versions

add action for users to remove messages from users in chats

add chat history to userMessagePositions with regex

proper const usage for userName

allow removing the messages of unregistered users

add menus to usernames in chat history

this allows you to remove user messages on chat history as well
this also allows moderators to take actions on users in chat history

Apply suggestions from code review

* readd missing call to handler
2021-09-14 16:05:20 -04:00
ebbit1q
c25bf491e4 fix card moving on server (#4413)
* wip fix card moving on server

* fix flipped cards being moved as -1
2021-09-14 15:48:46 -04:00
ZeldaZach
affc288144 Use gmail over googlemail in all cases, as they're the same alias 2021-08-27 23:17:26 -04:00
Zach H
051be37419 Server Config Whitelist Email Providers (#4416)
* Support registration domain whitelist (registration/emailproviderwhitelist) that, if set, will require a user to have an email with one of the specified domain providers. Will require client updates to see the Whitelist message, otherwise they'll be greeted with a default alert.

This also works to remove the pain of Google Email addresses and their infinite combination of usernames for the same account (i.e. remove periods and everything after the first plus sign).

* Make blacklist response show custom dialog
2021-08-18 21:18:53 -04:00
Aren Kasner
c0bd49cf13 Fixed VCPKG folder for windows (#4406)
update vcpkg submodule
2021-08-04 03:23:42 +02:00
tooomm
2fe572c398 CI: Update Xcode versions (#4403) 2021-07-27 18:42:54 +02:00
Michael Stanaszak
904e740460 #4316: Wording change: CMC --> MV (#4388)
Co-authored-by: tooomm <tooomm@users.noreply.github.com>
Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2021-07-27 17:30:55 +02:00
ebbit1q
00add2a527 add gtest from arch repos so it doesn't have to download it (#4378) 2021-07-27 16:42:28 +02:00
Zach H
bbe43d4246 Prevent users from being able to upload super large files via Sockets, which could inadvertently deny access to the server (#4398) 2021-07-25 20:28:14 -04:00
ebbit1q
0280fea3e6 apply chat flood prevention in games next to rooms (#4387)
* apply chat flood prevention in games next to rooms

* add limit to private messages as well
2021-06-28 01:57:46 -04:00
Joseph Chamish
a65ce8694c selection of known hosts to form (#4379) 2021-06-15 03:12:17 -04:00
Jeremy Letto
c9ddd042fc Webatrice: Update nav (#4380)
* wip: subnav debug

* nav redesign

* remove unnecessary code

* remove subnav

* add leaveRoom button

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2021-06-15 03:12:04 -04:00
ebbit1q
da9222929b add werror flags when making a debug build using llvm (#4344)
* add werror flags when making a debug build using llvm

this would get bugs like
https://github.com/Cockatrice/Cockatrice/pull/4337
get signalled earlier to us

* fix error: 'Servatrice_DatabaseInterface::registerUser' hides overloaded virtual function

* remove unused field

* mac machines have 3 cores

see
https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources

* typo
2021-06-01 21:57:37 -04:00
marsnicholas
b858e36183 Closes all library views when shuffled (#4261) (#4324) 2021-06-01 21:53:43 -04:00
ebbit1q
ebe2c494aa remove the stop dump zone command from the protocol (#4326)
the stop dump zone command was implemented as a courtesy to other
players in order to take into account when they would stop looking at
unknown information

however, this can be abused, a malicious client can send this command
whenever they would like

cockatrice is not a physical tabletop nor does it aim to be, if you can
take a screenshot of your deck and then close the view, you are not
cheating as you have been given this information

in order to prevent anyone from abusing this we should remove the
command from the protocol, this means servers will ignore this message
and clients will get a little invalid command reply in their debug log

the extension id will remain reserved

shuffling your deck will always invalidate any card view looking at
those cards

if players wish to signal that they stopped looking at their deck for
whatever reason they should just use the chat instead, optionally using
one of the chat macros
2021-06-01 21:52:20 -04:00
Jeremy Letto
fac7bfaa92 Webatrice: Nav Update (#4367) 2021-06-01 20:47:19 -04:00
685 changed files with 136574 additions and 90991 deletions

View File

@@ -5,11 +5,14 @@ RUN pacman --sync --refresh --sysupgrade --needed --noconfirm \
ccache \ ccache \
cmake \ cmake \
git \ git \
gtest \
mariadb-libs \ mariadb-libs \
protobuf \ protobuf \
qt5-base \ qt6-base \
qt5-multimedia \ qt6-imageformats \
qt5-svg \ qt6-multimedia \
qt5-tools \ qt6-svg \
qt5-websockets \ qt6-tools \
qt6-translations \
qt6-websockets \
&& pacman --sync --clean --clean --noconfirm && pacman --sync --clean --clean --noconfirm

View File

@@ -1,4 +1,4 @@
FROM ubuntu:hirsute FROM debian:11
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 \
@@ -17,6 +17,7 @@ RUN apt-get update && \
libqt5svg5-dev \ libqt5svg5-dev \
libqt5websockets5-dev \ libqt5websockets5-dev \
protobuf-compiler \ protobuf-compiler \
qt5-image-formats-plugins \
qtmultimedia5-dev \ qtmultimedia5-dev \
qttools5-dev \ qttools5-dev \
qttools5-dev-tools \ qttools5-dev-tools \

View File

@@ -1,4 +1,4 @@
FROM ubuntu:groovy FROM debian:12
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,17 +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 \ libqt6multimedia6 \
libqt5sql5-mysql \ libqt6sql6-mysql \
libqt5svg5-dev \ qt6-svg-dev \
libqt5websockets5-dev \ qt6-websockets-dev \
protobuf-compiler \ protobuf-compiler \
qt5-default \ qt6-image-formats-plugins \
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

@@ -1,24 +0,0 @@
FROM debian:buster
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
ccache \
clang-format \
cmake \
file \
g++ \
git \
liblzma-dev \
libmariadb-dev-compat \
libprotobuf-dev \
libqt5multimedia5-plugins \
libqt5sql5-mysql \
libqt5svg5-dev \
libqt5websockets5-dev \
protobuf-compiler \
qt5-default \
qtbase5-dev \
qtmultimedia5-dev \
qttools5-dev-tools \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

View File

@@ -1,21 +0,0 @@
FROM fedora:33
RUN dnf install -y \
@development-tools \
ccache \
cmake \
desktop-file-utils \
file \
gcc-c++ \
git \
hicolor-icon-theme \
libappstream-glib \
mariadb-devel \
protobuf-devel \
qt5-{qttools,qtsvg,qtmultimedia,qtwebsockets}-devel \
rpm-build \
sqlite-devel \
wget \
xz-devel \
zlib-devel \
&& dnf clean all

View File

@@ -1,21 +0,0 @@
FROM fedora:34
RUN dnf install -y \
@development-tools \
ccache \
cmake \
desktop-file-utils \
file \
gcc-c++ \
git \
hicolor-icon-theme \
libappstream-glib \
mariadb-devel \
protobuf-devel \
qt5-{qttools,qtsvg,qtmultimedia,qtwebsockets}-devel \
rpm-build \
sqlite-devel \
wget \
xz-devel \
zlib-devel \
&& dnf clean all

15
.ci/Fedora39/Dockerfile Normal file
View File

@@ -0,0 +1,15 @@
FROM fedora:39
RUN dnf install -y \
ccache \
cmake \
gcc-c++ \
git \
mariadb-devel \
protobuf-devel \
qt6-{qttools,qtsvg,qtmultimedia,qtwebsockets}-devel \
qt6-qtimageformats \
rpm-build \
xz-devel \
zlib-devel \
&& dnf clean all

15
.ci/Fedora40/Dockerfile Normal file
View File

@@ -0,0 +1,15 @@
FROM fedora:40
RUN dnf install -y \
ccache \
cmake \
gcc-c++ \
git \
mariadb-devel \
protobuf-devel \
qt6-{qttools,qtsvg,qtmultimedia,qtwebsockets}-devel \
qt6-qtimageformats \
rpm-build \
xz-devel \
zlib-devel \
&& dnf clean all

View File

@@ -17,6 +17,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
libqt5websockets5-dev \ libqt5websockets5-dev \
protobuf-compiler \ protobuf-compiler \
qt5-default \ qt5-default \
qt5-image-formats-plugins \
qtmultimedia5-dev \ qtmultimedia5-dev \
qttools5-dev \ qttools5-dev \
qttools5-dev-tools \ qttools5-dev-tools \

View File

@@ -18,6 +18,7 @@ RUN apt-get update && \
libqt5websockets5-dev \ libqt5websockets5-dev \
protobuf-compiler \ protobuf-compiler \
qt5-default \ qt5-default \
qt5-image-formats-plugins \
qtmultimedia5-dev \ qtmultimedia5-dev \
qttools5-dev \ qttools5-dev \
qttools5-dev-tools \ qttools5-dev-tools \

View File

@@ -0,0 +1,27 @@
FROM ubuntu:jammy
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
build-essential \
ccache \
clang-format \
cmake \
file \
g++ \
git \
libgl-dev \
liblzma-dev \
libmariadb-dev-compat \
libprotobuf-dev \
libqt6multimedia6 \
libqt6sql6-mysql \
libqt6svg6-dev \
libqt6websockets6-dev \
protobuf-compiler \
qt6-image-formats-plugins \
qt6-l10n-tools \
qt6-multimedia-dev \
qt6-tools-dev \
qt6-tools-dev-tools \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

View File

@@ -0,0 +1,27 @@
FROM ubuntu:noble
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
build-essential \
ccache \
clang-format \
cmake \
file \
g++ \
git \
libgl-dev \
liblzma-dev \
libmariadb-dev-compat \
libprotobuf-dev \
libqt6multimedia6 \
libqt6sql6-mysql \
qt6-svg-dev \
qt6-websockets-dev \
protobuf-compiler \
qt6-image-formats-plugins \
qt6-l10n-tools \
qt6-multimedia-dev \
qt6-tools-dev \
qt6-tools-dev-tools \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

View File

@@ -2,16 +2,26 @@
# 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
# --install runs make install
# --package [<package type>] runs make package, optionally force the type
# --suffix <suffix> renames package with this suffix, requires arg
# --server compiles servatrice
# --test runs tests
# --debug or --release sets the build type ie CMAKE_BUILD_TYPE
# --ccache [<size>] uses ccache and shows stats, optionally provide size
# --dir <dir> sets the name of the build dir, default is "build"
# --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
# Read arguments # Read arguments
while [[ "$@" ]]; do while [[ $# != 0 ]]; do
case "$1" in case "$1" in
'--') '--')
shift shift
;; ;;
'--format')
CHECK_FORMAT=1
shift
;;
'--install') '--install')
MAKE_INSTALL=1 MAKE_INSTALL=1
shift shift
@@ -19,7 +29,7 @@ while [[ "$@" ]]; 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
@@ -28,7 +38,7 @@ while [[ "$@" ]]; do
shift shift
if [[ $# == 0 ]]; then if [[ $# == 0 ]]; then
echo "::error file=$0::--suffix expects an argument" echo "::error file=$0::--suffix expects an argument"
exit 1 exit 3
fi fi
PACKAGE_SUFFIX="$1" PACKAGE_SUFFIX="$1"
shift shift
@@ -49,94 +59,143 @@ while [[ "$@" ]]; do
BUILDTYPE="Release" BUILDTYPE="Release"
shift shift
;; ;;
*) '--ccache')
if [[ $1 == -* ]]; then USE_CCACHE=1
echo "::error file=$0::unrecognized option: $1" shift
if [[ $# != 0 && ${1:0:1} != - ]]; then
CCACHE_SIZE="$1"
shift
fi
;;
'--dir')
shift
if [[ $# == 0 ]]; then
echo "::error file=$0::--dir expects an argument"
exit 3 exit 3
fi fi
BUILDTYPE="$1" BUILD_DIR="$1"
shift shift
;; ;;
'--parallel')
shift
if [[ $# == 0 ]]; then
echo "::error file=$0::--parallel expects an argument"
exit 3
fi
PARALLEL_COUNT="$1"
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 ./.ci/lint.sh
echo "::endgroup::"
fi
set -e set -e
# Setup # Setup
./servatrice/check_schema_version.sh ./servatrice/check_schema_version.sh
mkdir -p build if [[ ! $BUILDTYPE ]]; then
cd build BUILDTYPE=Release
if [[ ! $CMAKE_BUILD_PARALLEL_LEVEL ]]; then
CMAKE_BUILD_PARALLEL_LEVEL=2 # default machines have 2 cores
fi fi
if [[ ! $BUILD_DIR ]]; then
BUILD_DIR="build"
fi
mkdir -p "$BUILD_DIR"
cd "$BUILD_DIR"
# Add cmake flags # Add cmake 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
flags+=" -DCMAKE_BUILD_TYPE=$BUILDTYPE" flags+=("-DUSE_CCACHE=1")
if [[ $CCACHE_SIZE ]]; then
# note, this setting persists after running the script
ccache --max-size "$CCACHE_SIZE"
fi
fi fi
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
# prepend ccache compiler binaries to path buildflags=(--config "$BUILDTYPE")
PATH="/usr/local/opt/ccache/libexec:$PATH" if [[ $PARALLEL_COUNT ]]; then
# Add qt install location when using homebrew if [[ $(cmake --build /not_a_dir --parallel 2>&1 | head -1) =~ parallel ]]; then
flags+=" -DCMAKE_PREFIX_PATH=/usr/local/opt/qt5/" # 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 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
echo "::group::Show ccache stats" if [[ $USE_CCACHE ]]; then
ccache --show-stats echo "::group::Show ccache stats"
echo "::endgroup::" ccachestatsverbose
echo "::endgroup::"
fi
echo "::group::Configure cmake" echo "::group::Configure cmake"
cmake --version cmake --version
cmake .. $flags cmake .. "${flags[@]}"
echo "::endgroup::" echo "::endgroup::"
echo "::group::Build project" echo "::group::Build project"
cmake --build . if [[ $RUNNER_OS == Windows ]]; then
# Enable MTT, see https://devblogs.microsoft.com/cppblog/improved-parallelism-in-msbuild/
# and https://devblogs.microsoft.com/cppblog/cpp-build-throughput-investigation-and-tune-up/#multitooltask-mtt
cmake --build . "${buildflags[@]}" -- -p:UseMultiToolTask=true -p:EnableClServerMode=true
else
cmake --build . "${buildflags[@]}"
fi
echo "::endgroup::" echo "::endgroup::"
echo "::group::Show ccache stats again" if [[ $USE_CCACHE ]]; then
ccache --show-stats echo "::group::Show ccache stats again"
echo "::endgroup::" ccachestatsverbose
echo "::endgroup::"
fi
if [[ $MAKE_TEST ]]; then if [[ $MAKE_TEST ]]; then
echo "::group::Run tests" echo "::group::Run tests"
cmake --build . --target test ctest -C "$BUILDTYPE" --output-on-failure
echo "::endgroup::" echo "::endgroup::"
fi fi
if [[ $MAKE_INSTALL ]]; then if [[ $MAKE_INSTALL ]]; then
echo "::group::Install" echo "::group::Install"
cmake --build . --target install cmake --build . --target install --config "$BUILDTYPE"
echo "::endgroup::" echo "::endgroup::"
fi fi
if [[ $MAKE_PACKAGE ]]; then if [[ $MAKE_PACKAGE ]]; then
echo "::group::Create package" echo "::group::Create package"
cmake --build . --target package cmake --build . --target package --config "$BUILDTYPE"
echo "::endgroup::" echo "::endgroup::"
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

@@ -3,11 +3,15 @@
# 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.
# Creates or loads docker images to use in compilation, creates RUN function to start compilation on the docker image. # Creates or loads docker images to use in compilation, creates RUN function to start compilation on the docker image.
# <arg> sets the name of the docker image, these correspond to directories in .ci
# --get loads the image from a previously saved image cache, will build if no image is found # --get loads the image from a previously saved image cache, will build if no image is found
# --build builds the image from the Dockerfile in .ci/$NAME # --build builds the image from the Dockerfile in .ci/$NAME
# --save stores the image, if an image was loaded it will not be stored # --save stores the image, if an image was loaded it will not be stored
# --interactive immediately starts the image interactively for debugging
# --set-cache <location> sets the location to cache the image or for ccache
# requires: docker # requires: docker
# uses env: NAME CACHE BUILD GET SAVE (correspond to args: <name> --set-cache <cache> --build --get --save) # 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"
@@ -18,7 +22,7 @@ image_cache="image"
ccache_cache=".ccache" ccache_cache=".ccache"
# Read arguments # Read arguments
while [[ "$@" ]]; do while [[ $# != 0 ]]; do
case "$1" in case "$1" in
'--build') '--build')
BUILD=1 BUILD=1
@@ -28,6 +32,10 @@ while [[ "$@" ]]; do
GET=1 GET=1
shift shift
;; ;;
'--interactive')
INTERACTIVE=1
shift
;;
'--save') '--save')
SAVE=1 SAVE=1
shift shift
@@ -36,12 +44,12 @@ while [[ "$@" ]]; do
CACHE=$2 CACHE=$2
if ! [[ -d $CACHE ]]; then if ! [[ -d $CACHE ]]; then
echo "could not find cache path: $CACHE" >&2 echo "could not find cache path: $CACHE" >&2
exit 3 return 3
fi fi
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
@@ -67,27 +75,27 @@ 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)"
if ! [[ -d $CACHE ]]; then echo "set cache dir to $CACHE" >&2
echo "could not find cache dir: $CACHE" >&2 fi
mkdir -p $CACHE if ! [[ -d $CACHE ]]; then
unset GET # the dir is empty echo "could not find cache dir: $CACHE" >&2
fi mkdir -p "$CACHE"
if [[ $GET || $SAVE ]]; then unset GET # the dir is empty
img_dir="$CACHE/$image_cache" fi
img_save="$img_dir/$IMAGE_NAME$save_extension" if [[ $GET || $SAVE ]]; then
if ! [[ -d $img_dir ]]; then img_dir="$CACHE/$image_cache"
echo "could not find image dir: $img_dir" >&2 img_save="$img_dir/$IMAGE_NAME$save_extension"
mkdir -p "$img_dir" if ! [[ -d $img_dir ]]; then
fi echo "could not find image dir: $img_dir" >&2
fi mkdir -p "$img_dir"
export CCACHE_DIR="$CACHE/$ccache_cache"
if ! [[ -d $CCACHE_DIR ]]; then
echo "could not find ccache dir: $CCACHE_DIR" >&2
mkdir -p "$CCACHE_DIR"
fi fi
fi fi
export CCACHE_DIR="$CACHE/$ccache_cache"
if ! [[ -d $CCACHE_DIR ]]; then
echo "could not find ccache dir: $CCACHE_DIR" >&2
mkdir -p "$CCACHE_DIR"
fi
# Get the docker image from previously stored save # Get the docker image from previously stored save
if [[ $GET ]]; then if [[ $GET ]]; then
@@ -132,15 +140,26 @@ fi
function RUN () function RUN ()
{ {
echo "running image:" echo "running image:"
if docker images | grep "$IMAGE_NAME"; then if [[ $(docker images) =~ "$IMAGE_NAME" ]]; then
args="--mount type=bind,source=$PWD,target=/src -w=/src" local args=(--mount "type=bind,source=$PWD,target=/src")
args+=(--workdir "/src")
args+=(--user "$(id -u):$(id -g)")
if [[ $CCACHE_DIR ]]; then if [[ $CCACHE_DIR ]]; then
args+=" --mount type=bind,source=$CCACHE_DIR,target=/.ccache -e CCACHE_DIR=/.ccache" args+=(--mount "type=bind,source=$CCACHE_DIR,target=/.ccache")
args+=(--env "CCACHE_DIR=/.ccache")
fi fi
docker run $args $RUN_ARGS "$IMAGE_NAME" bash "$BUILD_SCRIPT" $RUN_OPTS $@ docker run "${args[@]}" $RUN_ARGS "$IMAGE_NAME" bash "$BUILD_SCRIPT" $RUN_OPTS "$@"
return $? return $?
else else
echo "could not find docker image: $IMAGE_NAME" >&2 echo "could not find docker image: $IMAGE_NAME" >&2
return 3 return 3
fi fi
} }
# for debugging, start the docker image interactively instead of building
# starts immediately, does not require sourcing or RUN
if [[ $INTERACTIVE ]]; then
export BUILD_SCRIPT="-i"
export RUN_ARGS="$RUN_ARGS -it"
RUN
fi

View File

@@ -10,10 +10,10 @@ if ! git merge-base origin/master HEAD; then
git fetch --unshallow git fetch --unshallow
fi fi
# Check formatting using clangify # Check formatting using format.sh
echo "Checking your code using clang-format..." echo "Checking your code using clang-format/cmake-format..."
diff="$(./clangify.sh --diff --cf-version --branch origin/master)" diff="$(./format.sh --diff --cmake --cf-version --branch origin/master)"
err=$? err=$?
case $err in case $err in
@@ -24,7 +24,7 @@ case $err in
*** *** *** ***
*** Your code does not comply with our style guide. *** *** Your code does not comply with our style guide. ***
*** *** *** ***
*** Please correct it or run the "clangify.sh" script. *** *** Please correct it or run the "format.sh" script. ***
*** Then commit and push those changes to this branch. *** *** Then commit and push those changes to this branch. ***
*** Check our CONTRIBUTING.md file for more details. *** *** Check our CONTRIBUTING.md file for more details. ***
*** *** *** ***
@@ -32,21 +32,23 @@ case $err in
*** *** *** ***
*********************************************************** ***********************************************************
Used clang-format version: Used version:
${diff%% ${diff%%
----------
*} *}
The following changes should be made: The following changes should be made:
${diff#* ${diff#*
----------
} }
Exiting... Exiting...
EOM EOM
exit 2 exit 2
;; ;;
0) 0)
cat <<EOM cat <<EOM
*********************************************************** ***********************************************************
*** *** *** ***
@@ -58,12 +60,10 @@ EOM
Exiting... Exiting...
EOM EOM
exit 0 exit 0
;; ;;
*) *)
echo "" echo "Something went wrong in our formatting checks: format.sh returned $err" >&2
echo "Something went wrong in our formatting checks: clangify returned $err" >&2 ;;
echo "" esac
;;
esac

View File

@@ -3,8 +3,9 @@
# 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="." # adds output to GITHUB_OUTPUT
builddir="${BUILD_DIR:=.}"
findrx="Cockatrice-*.*" findrx="Cockatrice-*.*"
if [[ $1 ]]; then if [[ $1 ]]; then
@@ -27,6 +28,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 +47,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"
echo "::set-output name=name::$filename" relative_path="$path/$filename"
ls -l "$relative_path"
echo "path=$relative_path" >>"$GITHUB_OUTPUT"
echo "name=$filename" >>"$GITHUB_OUTPUT"

View File

@@ -4,6 +4,7 @@
# the releases are first made as drafts and will be vetted by a human # the releases are first made as drafts and will be vetted by a human
# it just has to provide a template # it just has to provide a template
# this requires the repo to be unshallowed # this requires the repo to be unshallowed
# adds output to GITHUB_OUTPUT
template_path=".ci/release_template.md" template_path=".ci/release_template.md"
body_path="/tmp/release.md" body_path="/tmp/release.md"
beta_regex='beta' beta_regex='beta'
@@ -22,22 +23,22 @@ fi
# create title # create title
if [[ $TAG =~ $beta_regex ]]; then if [[ $TAG =~ $beta_regex ]]; then
echo "::set-output name=is_beta::yes" echo "is_beta=yes" >>"$GITHUB_OUTPUT"
title="$TAG" title="$TAG"
echo "creating beta release '$title'" echo "creating beta release '$title'"
elif [[ ! $(cat CMakeLists.txt) =~ $name_regex ]]; then elif [[ ! $(cat CMakeLists.txt) =~ $name_regex ]]; then
echo "::error file=$0::could not find releasename in CMakeLists.txt" echo "::error file=$0::could not find releasename in CMakeLists.txt"
exit 1 exit 1
else else
echo "::set-output name=is_beta::no" echo "is_beta=no" >>"$GITHUB_OUTPUT"
name="${BASH_REMATCH[1]}" name="${BASH_REMATCH[1]}"
version="${TAG##*-}" version="${TAG##*-}"
title="Cockatrice $version: $name" title="Cockatrice $version: $name"
no_beta=1 no_beta=1
echo "::set-output name=friendly_name::$name" echo "friendly_name=$name" >>"$GITHUB_OUTPUT"
echo "creating full release '$title'" echo "creating full release '$title'"
fi fi
echo "::set-output name=title::$title" echo "title=$title" >>"$GITHUB_OUTPUT"
# add release notes template # add release notes template
if [[ $no_beta ]]; then if [[ $no_beta ]]; then
@@ -60,9 +61,9 @@ fi
all_tags=" all_tags="
$(git tag)" # tags are ordered alphabetically $(git tag)" # tags are ordered alphabetically
before="${all_tags%% before="${all_tags%%
$TAG*}" # strip line with current tag an all lines after it "$TAG"*}" # strip line with current tag an all lines after it
# note the extra newlines are needed to always have a last line # note the extra newlines are needed to always have a last line
if [[ $all_tags == $before ]]; then if [[ $all_tags == "$before" ]]; then
echo "::warning file=$0::could not find current tag" echo "::warning file=$0::could not find current tag"
else else
while while
@@ -74,7 +75,7 @@ else
beta_list+=" $previous" # add to list of skipped betas beta_list+=" $previous" # add to list of skipped betas
next_before="${before% next_before="${before%
*}" # strip the last line *}" # strip the last line
if [[ $next_before == $before ]]; then if [[ $next_before == "$before" ]]; then
unset previous unset previous
break break
fi fi
@@ -108,5 +109,5 @@ else
fi fi
# write to file # write to file
echo "::set-output name=body_path::$body_path" echo "body_path=$body_path" >>"$GITHUB_OUTPUT"
echo "$body" >"$body_path" echo "$body" >"$body_path"

View File

@@ -8,26 +8,25 @@ 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+</kbd>
- <kbd>Windows 7/8/10 (64-bit)</kbd></i> - <kbd>Windows 10+</kbd>
- <kbd>macOS 10.14</kbd> ("Mojave")</i> - <kbd>macOS 10.15+</kbd> ("Catalina")
- <kbd>macOS 10.15</kbd> ("Catalina")</i> - <kbd>macOS 13+</kbd> ("Ventura")
- <kbd>macOS 11.0</kbd> ("Big Sur")</i> - <kbd>Ubuntu 18.04 LTS</kbd> ("Bionic Beaver")
- <kbd>Ubuntu 18.04</kbd> ("Bionic Beaver")</i> - <kbd>Ubuntu 20.04 LTS</kbd> ("Focal Fossa")
- <kbd>Ubuntu 20.04</kbd> ("Focal Fossa")</i> - <kbd>Ubuntu 22.04 LTS</kbd> ("Jammy Jellyfish")
- <kbd>Ubuntu 20.10</kbd> ("Groovy Gorilla")</i> - <kbd>Ubuntu 24.04 LTS</kbd> ("Noble Numbat")
- <kbd>Ubuntu 21.04</kbd> ("Hirsute Hippo")</i> - <kbd>Debian 11</kbd> ("Bullseye")
- <kbd>Debian 10</kbd> ("Buster")</i> - <kbd>Debian 12</kbd> ("Bookworm")
- <kbd>Fedora 33</kbd></i> - <kbd>Fedora 39</kbd>
- <kbd>Fedora 34</kbd></i> - <kbd>Fedora 40</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>
## General Notes ## General Notes
<!-- --REPLACE-WITH-RELEASE-TITLE-- should be placed here by the ci -->
We're pleased to announce the newest official release: <kbd>--REPLACE-WITH-RELEASE-TITLE--</kbd> We're pleased to announce the newest official release: <kbd>--REPLACE-WITH-RELEASE-TITLE--</kbd>
We hope you enjoy the changes made and we have listed all changes, with their corresponding tickets, since the last version of Cockatrice was released for your convenience. We hope you enjoy the changes made and we have listed all changes, with their corresponding tickets, since the last version of Cockatrice was released for your convenience.
@@ -44,7 +43,7 @@ If you'd like to help contribute to Cockatrice in any way, check out our [README
> ⚠️ **With this release, we no longer provide a ready-to-install binary for:** > ⚠️ **With this release, we no longer provide a ready-to-install binary for:**
> --DEPRECATED-OS-HERE-- > --DEPRECATED-OS-HERE--
--> -->
- Run the internal software updater: <kbd>Help → Check for Client Updates</kbd> - Run the internal software updater: <kbd>Help → Check for Client Updates</kbd>
Don't forget to update your card database right after! (<kbd>Help → Check for Card Updates...</kbd>) Don't forget to update your card database right after! (<kbd>Help → Check for Card Updates...</kbd>)

View File

@@ -0,0 +1,58 @@
#!/bin/bash
# ci script to update translation files
# usage:
# $0 cockatrice/cockatrice_en@source.ts cockatrice/src common
# or
# FILE="cockatrice/cockatrice_en@source.ts"
# DIRS="cockatrice/src common"
# $0
# note: directories can't contain spaces
# check parameters
if [[ ! $FILE ]]; then
FILE="$1"
shift
fi
if [[ ! $FILE ]]; then
echo "no output file selected" >&2
exit 2;
fi
if [[ ! $DIRS ]]; then
DIRS="$*"
fi
if [[ ! $DIRS ]]; then
echo "no source directories selected to translate" >&2
exit 2;
fi
if [[ ! -e $FILE ]]; then
echo "output file does not exist at: $FILE" >&2
exit 3;
fi
# print version
if ! lupdate -version; then
echo "failed to run lupdate" >&2
exit 4;
fi
# run lupdate, duplicating the output in stderr and saving it
# for convenience we ignore that $DIRS will be split on spaces
# shellcheck disable=SC2086
if ! got="$(lupdate $DIRS -ts "$FILE" | tee /dev/stderr)"; then
echo "failed to update $FILE with $DIRS" >&2
exit 4;
fi
# trim output
# the line we are interested in is:
# Found xxx source text(s) (x new and xxx already existing)
output="${got##* source text(s) (}" # get stuff in between brackets
output="${output%%)*}" # trim everything after first )
if [[ $output == "$got" ]]; then
echo "could not parse generated output" >&2
exit 4;
fi
# write output to ci environment file
echo "output=$output" >> "$GITHUB_OUTPUT"

View File

@@ -0,0 +1,14 @@
Updated source strings for translations:
- {{ .cockatrice_output }} (Cockatrice)
- {{ .oracle_output }} (Oracle)
<br>
Last changes are based on commit {{ .commit }}.
---
*This PR is automatically generated and updated by the workflow at `.github/workflows/translations-push.yml`. Review [action runs][2].*<br>
*After merging, all changes to the source language are available for translation at [Transifex][1] shortly.*
[1]: https://app.transifex.com/cockatrice/cockatrice/
[2]: https://github.com/Cockatrice/Cockatrice/actions/workflows/translations-push.yml?query=branch%3Amaster

109
.cmake-format.json Normal file
View File

@@ -0,0 +1,109 @@
{
"format": {
"_help_line_width": [
"How wide to allow formatted cmake files"
],
"line_width": 120,
"_help_tab_size": [
"How many spaces to tab for indent"
],
"tab_size": 2,
"_help_max_subgroups_hwrap": [
"If an argument group contains more than this many sub-groups",
"(parg or kwarg groups) then force it to a vertical layout."
],
"max_subgroups_hwrap": 2,
"_help_max_pargs_hwrap": [
"If a positional argument group contains more than this many",
"arguments, then force it to a vertical layout."
],
"max_pargs_hwrap": 6,
"_help_max_rows_cmdline": [
"If a cmdline positional group consumes more than this many",
"lines without nesting, then invalidate the layout (and nest)"
],
"max_rows_cmdline": 5,
"_help_separate_ctrl_name_with_space": [
"If true, separate flow control names from their parentheses",
"with a space"
],
"separate_ctrl_name_with_space": false,
"_help_separate_fn_name_with_space": [
"If true, separate function names from parentheses with a",
"space"
],
"separate_fn_name_with_space": false,
"_help_dangle_parens": [
"If a statement is wrapped to more than one line, than dangle",
"the closing parenthesis on its own line."
],
"dangle_parens": true,
"_help_dangle_align": [
"If the trailing parenthesis must be 'dangled' on its on",
"line, then align it to this reference: `prefix`: the start",
"of the statement, `prefix-indent`: the start of the",
"statement, plus one indentation level, `child`: align to",
"the column of the arguments"
],
"dangle_align": "prefix",
"_help_min_prefix_chars": [
"If the statement spelling length (including space and",
"parenthesis) is smaller than this amount, then force reject",
"nested layouts."
],
"min_prefix_chars": 4,
"_help_max_prefix_chars": [
"If the statement spelling length (including space and",
"parenthesis) is larger than the tab width by more than this",
"amount, then force reject un-nested layouts."
],
"max_prefix_chars": 10,
"_help_max_lines_hwrap": [
"If a candidate layout is wrapped horizontally but it exceeds",
"this many lines, then reject the layout."
],
"max_lines_hwrap": 2,
"_help_line_ending": [
"What style line endings to use in the output."
],
"line_ending": "auto",
"_help_command_case": [
"Format command names consistently as 'lower' or 'upper' case"
],
"command_case": "lower",
"_help_keyword_case": [
"Format keywords consistently as 'lower' or 'upper' case"
],
"keyword_case": "upper",
"_help_always_wrap": [
"A list of command names which should always be wrapped"
],
"always_wrap": [],
"_help_enable_sort": [
"If true, the argument lists which are known to be sortable",
"will be sorted lexicographically"
],
"enable_sort": true,
"_help_autosort": [
"If true, the parsers may infer whether or not an argument",
"list is sortable (without annotation)."
],
"autosort": true,
"_help_require_valid_layout": [
"By default, if cmake-format cannot successfully fit",
"everything into the desired line-width it will apply the",
"last, most aggressive attempt that it made. If this flag is",
"True, however, cmake-format will print error, exit with non-",
"zero status code, and write-out nothing"
],
"require_valid_layout": false,
"_help_layout_passes": [
"A dictionary mapping layout nodes to a list of wrap",
"decisions. See the documentation for more information."
],
"layout_passes": {}
},
"markup": {
"enable_markup": false
}
}

View File

@@ -41,8 +41,8 @@ albeit slightly less active.
We use a separate job on the CI to check your code for formatting issues. If We use a separate job on the CI to check your code for formatting issues. If
your pull request failed the test, you can check the output on the checks tab. your pull request failed the test, you can check the output on the checks tab.
It's the first job called "linter", you can click the "Run clangify" step to It's the first job called "linter", you can click the "Check code formatting"
see the output of the test. step to see the output of the test.
The message will look like this: The message will look like this:
``` ```
@@ -50,7 +50,7 @@ The message will look like this:
*** *** *** ***
*** Your code does not comply with our style guide. *** *** Your code does not comply with our style guide. ***
*** *** *** ***
*** Please correct it or run the "clangify.sh" script. *** *** Please correct it or run the "format.sh" script. ***
*** Then commit and push those changes to this branch. *** *** Then commit and push those changes to this branch. ***
*** Check our CONTRIBUTING.md file for more details. *** *** Check our CONTRIBUTING.md file for more details. ***
*** *** *** ***
@@ -81,9 +81,9 @@ The handy tool `clang-format` can format your code for you, it is available for
almost any environment. A special `.clang-format` configuration file is almost any environment. A special `.clang-format` configuration file is
included in the project and is used to format your code. included in the project and is used to format your code.
We've also included a bash script, `clangify.sh`, that will use clang-format to We've also included a bash script, `format.sh`, that will use clang-format to
format all files in your pr in one go. Use `./clangify.sh --help` to show a format all files in your pr in one go. Use `./format.sh --help` to show a full
full help page. help page.
To run clang-format on a single source file simply use the command To run clang-format on a single source file simply use the command
`clang-format -i <filename>` to format it in place. (Some systems install `clang-format -i <filename>` to format it in place. (Some systems install
@@ -290,20 +290,21 @@ be included in the next release 👍
Basic workflow for translations: Basic workflow for translations:
1. Developer adds a `tr("foo")` string in the code; 1. Developer adds a `tr("foo")` string in the code;
2. Every few days, a maintainer updates the `*_en.ts files` with the new strings; 2. CI updates the `*_en@source.ts files` regularly and creates a PR automatically;
3. Transifex picks up the new files from GitHub every 24 hours; 3. Maintainer verifies and merges the change;
4. Translators translate the new untranslated strings on Transifex; 4. Transifex picks up the new files from GitHub automatically;
5. Before a release, a maintainer fetches the updated translations from Transifex. 5. Translators translate the new untranslated strings on Transifex;
6. Before a release, a maintainer fetches the updated translations from Transifex.
### Using Translations (for developers) ### ### Using Translations (for developers) ###
All the user-interface strings inside Cockatrice's source code must be written All user interface strings inside Cockatrice's source code must be written
in English(US). in English (US).
Translations to other languages are managed using [Transifex]( Translations to other languages are managed using [Transifex](
https://www.transifex.com/projects/p/cockatrice/). https://www.transifex.com/projects/p/cockatrice/).
Adding a new string to translate is as easy as adding the string in the Adding a new string to translate is as easy as adding the string in the
'tr("")' function, the string will be picked up as translatable automatically `tr("")` function, the string will be picked up as translatable automatically
and translated as needed. and translated as needed.
For example, setting the text of a label in the way that the string For example, setting the text of a label in the way that the string
`"My name is:"` can be translated: `"My name is:"` can be translated:
@@ -312,7 +313,7 @@ nameLabel.setText(tr("My name is:"));
``` ```
To translate a string that would have plural forms you can add the amount to To translate a string that would have plural forms you can add the amount to
the tr call, also you can add an extra string as a hint for translators: the tr() call, also you can add an extra string as a hint for translators:
```c++ ```c++
QString message = tr("Everyone draws %n cards", "pop up message", amount); QString message = tr("Everyone draws %n cards", "pop up message", amount);
``` ```
@@ -321,20 +322,46 @@ https://doc.qt.io/qt-5/i18n-source-translation.html#handling-plurals)
If you're about to propose a change that adds or modifies any translatable If you're about to propose a change that adds or modifies any translatable
string in the code, you don't need to take care of adding the new strings to string in the code, you don't need to take care of adding the new strings to
the translation files. the translation files.<br>
Every few days, or when a lot of new strings have been added, someone from the We have an automated process to update our language source files on a schedule
development team will take care of extracting all the new strings and adding and provide the translators on Transifex with the new contents.<br>
them to the english translation files and making them available to translators Maintainers can also manually trigger this on demand.
on Transifex.
### Maintaining Translations (for maintainers) ### ### Maintaining Translations (for maintainers) ###
When new translatable strings have been added to the code, a maintainer should When new translatable strings have been added to the code, a maintainer has to
make them available to translators on Transifex. Every few days, or when a lot make them available to translators on Transifex.
of new strings have been added, a maintainer should take care of extracting all
the new strings and add them to the english translation files.
To update the english translation files, re-run cmake enabling the appropriate To help with that, we have an automated CI workflow, that regularly looks at the
code in the master branch, extracts all strings and updates dedicated source string
files with any changes. These updates are not commited right away, the CI creates a
PR for reviewing instead.<br>
After approval, our translation tool automatically picks the changes up and deploys
them to our translators. Be mindful when merging only a few changes!
Once a release is planned, or when a lot of strings have been added or changed, a
maintainer can manually trigger a CI run to extract all strings on demand.
<details>
<summary><b>Manually trigger CI run (Workflow Dispatch)</b></summary>
Maintainers can always request the CI to run on demand if it's required.
Go to the `Actions` tab and select our dedicated translation workflow:
https://github.com/Cockatrice/Cockatrice/actions/workflows/translations.yml
You see a "This workflow has a workflow_dispatch event trigger." hint at the top of
the list.<br>
Select `Run workflow` on the right and trigger a run from master branch.
The CI will now check for changed strings and create a PR if there are any updates.
</details>
<details>
<summary><b>Manually update source strings locally</b></summary>
To update the english source files for translation, re-run cmake enabling the appropriate
parameter and then run make: parameter and then run make:
```sh ```sh
cd cockatrice/build cd cockatrice/build
@@ -344,24 +371,26 @@ make
If the parameter has been enabled correctly, when running "make" you should see If the parameter has been enabled correctly, when running "make" you should see
a line similar to this one (the numbers may vary): a line similar to this one (the numbers may vary):
```sh ```sh
[ 76%] Generating ../../cockatrice/translations/cockatrice_en.ts [ 76%] Generating ../../cockatrice/translations/cockatrice_en@source.ts
Updating '../../cockatrice/translations/cockatrice_en.ts'... Updating '../../cockatrice/translations/cockatrice_en@source.ts'...
Found 857 source text(s) (8 new and 849 already existing) Found 857 source text(s) (8 new and 849 already existing)
``` ```
You should then notice that the following files have uncommitted changes: You should then notice that the following files have uncommitted changes:
cockatrice/translations/cockatrice_en.ts cockatrice/translations/cockatrice_en@source.ts
oracle/translations/oracle_en.ts oracle/translations/oracle_en@source.ts
It is recommended to disable the parameter afterwards using: It is recommended to disable the parameter afterwards using:
```sh ```sh
cmake .. -DUPDATE_TRANSLATIONS=OFF cmake .. -DUPDATE_TRANSLATIONS=OFF
``` ```
Now you are ready to propose your change. Now you are ready to commit your changes and open a PR.
Once your change gets merged, Transifex will pick up the modified files </details>
automatically (checked every 24 hours) and update the interface where
translators will be able to translate the new strings. Once the changes get merged, Transifex will pick up the modified files
automatically (checked every few hours) and update their online editor where
translators will be able to translate the new strings right in the browser.
### Releasing Translations (for maintainers) ### ### Releasing Translations (for maintainers) ###

49
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,49 @@
# Configuration options: https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
# # Enable version updates for git submodules
# Not yet possible to bump only on tags or releases, see:
# https://github.com/dependabot/dependabot-core/issues/1639
# https://github.com/dependabot/dependabot-core/issues/2192
# Alternative: Action that updates submodule and can be manually run on demand (workflow_dispatch)
# - package-ecosystem: "gitsubmodule"
# # Look for `.gitmodules` in the `root` directory
# directory: "/"
# # Check for updates once a month
# schedule:
# interval: "monthly"
# # Limit the amout of open PR's (default = 5, disabled = 0, security updates are not impacted)
# open-pull-requests-limit: 1
# # Enable version updates for Docker
# Not yet possible to bump from one LTS version to the next and skip others, see:
# https://github.com/dependabot/dependabot-core/issues/2247
# - package-ecosystem: "docker"
# # Look for a `Dockerfile` in the `root` directory
# directory: "/"
# # Check for updates once a week
# schedule:
# interval: "weekly"
# # Limit the amout of open PR's (default = 5, disabled = 0, security updates are not impacted)
# open-pull-requests-limit: 1
# Enable version updates for GitHub Actions
- package-ecosystem: "github-actions"
# Directory must be set to "/" to check for workflow files in .github/workflows
directory: "/"
# Check for updates to GitHub Actions once a week
schedule:
interval: "weekly"
# Limit the amout of open PR's (default = 5, disabled = 0, security updates are not impacted)
open-pull-requests-limit: 2
# # Enable version updates for npm
# - package-ecosystem: "npm"
# # Look for `package.json` and `lock` files in the `webclient` subdirectory
# directory: "/webclient"
# # Check the npm registry for updates once a week
# schedule:
# interval: "weekly"
# # Limit the amout of open PR's (default = 5, disabled = 0, security updates are not impacted)
# open-pull-requests-limit: 5

View File

@@ -1,407 +0,0 @@
name: Build
on:
push:
branches:
- master
paths-ignore:
- '**.md'
- 'webclient/**'
tags:
- '*'
pull_request:
branches:
- master
paths-ignore:
- '**.md'
- 'webclient/**'
jobs:
configure:
name: Configure
runs-on: ubuntu-latest
outputs:
tag: ${{steps.configure.outputs.tag}}
sha: ${{steps.configure.outputs.sha}}
upload_url: ${{steps.create_release.outputs.upload_url}}
steps:
- name: Cancel previous runs
uses: styfle/cancel-workflow-action@0.6.0
with:
access_token: ${{github.token}} # needs other token https://github.com/styfle/cancel-workflow-action/issues/7
- name: Configure
id: configure
shell: bash
run: |
tag_regex='^refs/tags/'
if [[ $GITHUB_EVENT_NAME == pull-request ]]; then # pull request
sha="${{github.event.pull_request.head.sha}}"
elif [[ $GITHUB_REF =~ $tag_regex ]]; then # release
sha="$GITHUB_SHA"
tag="${GITHUB_REF/refs\/tags\//}"
echo "::set-output name=tag::$tag"
else # push to branch
sha="$GITHUB_SHA"
fi
echo "::set-output name=sha::$sha"
- name: Checkout
if: steps.configure.outputs.tag != null
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Prepare release paramaters
id: prepare
if: steps.configure.outputs.tag != null
shell: bash
env:
TAG: ${{steps.configure.outputs.tag}}
run: .ci/prep_release.sh
- name: Create release
if: steps.configure.outputs.tag != null
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{github.token}}
with:
tag_name: ${{github.ref}}
release_name: ${{steps.prepare.outputs.title}}
body_path: ${{steps.prepare.outputs.body_path}}
draft: true
prerelease: ${{steps.prepare.outputs.is_beta == 'yes'}}
build-linux:
strategy:
fail-fast: false
matrix:
# these names correspond to the files in .ci/$distro
include:
- distro: UbuntuHirsute
package: DEB
- distro: UbuntuGroovy
package: DEB
test: skip
- distro: UbuntuFocal
package: DEB
test: skip # UbuntuFocal has a broken qt for debug builds
- distro: UbuntuBionic
package: DEB
- distro: ArchLinux
package: skip # we are packaged in arch already
allow-failure: yes
- distro: DebianBuster
package: DEB
- distro: Fedora33
package: RPM
test: skip # Fedora is our slowest build
- distro: Fedora34
package: RPM
test: skip # gtest does not compile for some reason
name: ${{matrix.distro}}
needs: configure
runs-on: ubuntu-latest
continue-on-error: ${{matrix.allow-failure == 'yes'}}
env:
NAME: ${{matrix.distro}}
CACHE: /tmp/${{matrix.distro}}-cache # ${{runner.temp}} does not work?
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Get cache timestamp
id: cache_timestamp
shell: bash
run: echo "::set-output name=timestamp::$(date -u '+%Y%m%d%H%M%S')"
- name: Restore cache
uses: actions/cache@v2
env:
timestamp: ${{steps.cache_timestamp.outputs.timestamp}}
with:
path: ${{env.CACHE}}
key: docker-${{matrix.distro}}-cache-${{env.timestamp}}
restore-keys: |
docker-${{matrix.distro}}-cache-
- name: Build ${{matrix.distro}} Docker image
shell: bash
run: source .ci/docker.sh --build
- name: Build debug and test
if: matrix.test != 'skip'
shell: bash
run: |
source .ci/docker.sh
RUN --server --debug --test
- name: Build release package
id: package
if: matrix.package != 'skip'
shell: bash
env:
suffix: '-${{matrix.distro}}'
type: '${{matrix.package}}'
run: |
source .ci/docker.sh
RUN --server --release --package "$type" --suffix "$suffix"
- name: Upload artifact
if: matrix.package != 'skip'
uses: actions/upload-artifact@v2
with:
name: ${{matrix.distro}}-package
path: ./build/${{steps.package.outputs.name}}
if-no-files-found: error
- name: Upload to release
if: matrix.package != 'skip' && needs.configure.outputs.tag != null
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{github.token}}
with:
upload_url: ${{needs.configure.outputs.upload_url}}
asset_path: ./build/${{steps.package.outputs.name}}
asset_name: ${{steps.package.outputs.name}}
asset_content_type: application/octet-stream
build-macos:
strategy:
fail-fast: false
matrix:
target:
- Debug
- 10.14_Mojave
- 10.15_Catalina
- 11.0_Big_Sur
include:
- target: Debug # tests only
os: macos-latest
xcode: 11.7
type: Debug
do_tests: 0 # tests do not work yet on mac
make_package: false
- target: 10.14_Mojave
os: macos-10.15 # runs on Catalina
xcode: 10.3 # allows compatibility with macos 10.14
type: Release
do_tests: 0
make_package: true
- target: 10.15_Catalina
os: macos-10.15
xcode: 11.7
type: Release
do_tests: 0
make_package: true
- target: 11.0_Big_Sur
os: macos-11.0
xcode: 12.2
type: Release
do_tests: 0
make_package: true
name: macOS ${{matrix.target}}
needs: configure
runs-on: ${{matrix.os}}
continue-on-error: ${{matrix.allow-failure == 'yes'}}
env:
CCACHE_DIR: ~/.ccache
DEVELOPER_DIR:
/Applications/Xcode_${{matrix.xcode}}.app/Contents/Developer
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install dependencies using homebrew
shell: bash
# cmake cannot find the mysql connector
# neither of these works: mariadb-connector-c mysql-connector-c++
run: brew install ccache protobuf
- name: Install QT using homebrew
id: brew_install_qt
continue-on-error: true
shell: bash
run: brew install qt@5 --force-bottle
- name: Install QT using actions
if: steps.brew_install_qt.outcome != 'success'
uses: jurplel/install-qt-action@v2
- name: Get ccache timestamp
id: ccache_timestamp
shell: bash
run: echo "::set-output name=timestamp::$(date -u '+%Y%m%d%H%M%S')"
- name: Restore ccache cache
uses: actions/cache@v2
env:
timestamp: ${{steps.ccache_timestamp.outputs.timestamp}}
with:
path: ${{env.CCACHE_DIR}}
key: ${{runner.os}}-xcode-${{matrix.xcode}}-ccache-${{env.timestamp}}
restore-keys: |
${{runner.os}}-xcode-${{matrix.xcode}}-ccache-
- name: Build on Xcode ${{matrix.xcode}}
shell: bash
run: .ci/compile.sh ${{matrix.type}} --server
- name: Test
if: matrix.do_tests == 1
shell: bash
working-directory: build
run: cmake --build . --target test
- name: Package for ${{matrix.target}}
id: package
if: matrix.make_package
shell: bash
working-directory: build
run: |
cmake --build . --target package
../.ci/name_build.sh "-macOS-${{matrix.target}}"
- name: Upload artifact
if: matrix.make_package
uses: actions/upload-artifact@v2
with:
name: macOS-${{matrix.target}}-xcode-${{matrix.xcode}}-dmg
path: ${{steps.package.outputs.path}}
if-no-files-found: error
- name: Upload to release
if: matrix.make_package && needs.configure.outputs.tag != null
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{github.token}}
with:
upload_url: ${{needs.configure.outputs.upload_url}}
asset_path: ${{steps.package.outputs.path}}
asset_name: ${{steps.package.outputs.name}}
asset_content_type: application/octet-stream
build-windows:
strategy:
fail-fast: false
matrix:
arch:
- 64
- 32
include:
- arch: 64
triplet: x64
cmake: x64
append: _64
- arch: 32
triplet: x86
cmake: Win32
name: Windows ${{matrix.arch}}
needs: configure
runs-on: windows-latest
env:
QT_VERSION: '5.12.9'
QT_ARCH: msvc2017${{matrix.append}}
CMAKE_GENERATOR: 'Visual Studio 16 2019'
steps:
- name: Add msbuild to PATH
id: add-msbuild
uses: microsoft/setup-msbuild@v1.0.2
- name: Checkout
uses: actions/checkout@v2
with:
submodules: recursive
- name: Restore Qt ${{env.QT_VERSION}} ${{matrix.arch}}-bit from cache
id: cache-qt
uses: actions/cache@v2
with:
key: ${{runner.os}}-QtCache-${{env.QT_VERSION}}-${{matrix.arch}}
path: ${{runner.workspace}}/Qt
- name: Install ${{matrix.arch}}-bit Qt
uses: jurplel/install-qt-action@v2
with:
cached: ${{steps.cache-qt.outputs.cache-hit}}
version: ${{env.QT_VERSION}}
arch: win${{matrix.arch}}_${{env.QT_ARCH}}
- name: Restore or setup vcpkg
uses: lukka/run-vcpkg@v6
with:
vcpkgArguments: '@${{github.workspace}}/vcpkg.txt'
vcpkgDirectory: ${{github.workspace}}/vcpkg
appendedCacheKey: ${{hashFiles('**/vcpkg.txt')}}
vcpkgTriplet: ${{matrix.triplet}}-windows
- name: Configure Cockatrice ${{matrix.arch}}-bit
shell: bash
run: |
mkdir -p build
cd build
export QTDIR="${{runner.workspace}}/Qt/$QT_VERSION/$QT_ARCH"
cmake .. -G "${{env.CMAKE_GENERATOR}}" -A "${{matrix.cmake}}" -DCMAKE_BUILD_TYPE="Release" -DWITH_SERVER=1 -DTEST=1
- name: Build Cockatrice ${{matrix.arch}}-bit
id: package
shell: bash
working-directory: build
run: |
cmake --build . --target package --config Release
../.ci/name_build.sh "-win${{matrix.arch}}"
- name: Run tests
shell: bash
working-directory: build
run: ctest -T Test -C Release
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: Windows-${{matrix.arch}}bit-installer
path: ./build/${{steps.package.outputs.name}}
if-no-files-found: error
- name: Upload to release
if: needs.configure.outputs.tag != null
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{github.token}}
with:
upload_url: ${{needs.configure.outputs.upload_url}}
asset_path: ./build/${{steps.package.outputs.name}}
asset_name: ${{steps.package.outputs.name}}
asset_content_type: application/octet-stream

View File

@@ -1,28 +0,0 @@
name: Clangify
on:
pull_request:
branches:
- master
paths-ignore:
- '**.md'
jobs:
linter:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 20 # should be enough to find merge base
- name: Install clang-format
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends clang-format
- name: Run clangify
shell: bash
run: ./.ci/lint.sh

350
.github/workflows/desktop-build.yml vendored Normal file
View File

@@ -0,0 +1,350 @@
name: Build Desktop
on:
push:
branches:
- master
paths-ignore:
- '**.md'
- 'webclient/**'
- '.github/workflows/web-*.yml'
- '.github/workflows/translations-*.yml'
tags:
- '*'
pull_request:
paths-ignore:
- '**.md'
- 'webclient/**'
- '.github/workflows/web-*.yml'
- '.github/workflows/translations-*.yml'
# Cancel earlier, unfinished runs of this workflow on the same branch (unless on master)
concurrency:
group: "${{ github.workflow }} @ ${{ github.ref_name }}"
cancel-in-progress: ${{ github.ref_name != 'master' }}
jobs:
configure:
name: Configure
runs-on: ubuntu-latest
outputs:
tag: ${{steps.configure.outputs.tag}}
sha: ${{steps.configure.outputs.sha}}
steps:
- name: Configure
id: configure
shell: bash
run: |
tag_regex='^refs/tags/'
if [[ $GITHUB_EVENT_NAME == pull-request ]]; then # pull request
sha="${{github.event.pull_request.head.sha}}"
elif [[ $GITHUB_REF =~ $tag_regex ]]; then # release
sha="$GITHUB_SHA"
tag="${GITHUB_REF/refs\/tags\//}"
echo "tag=$tag" >>"$GITHUB_OUTPUT"
else # push to branch
sha="$GITHUB_SHA"
fi
echo "sha=$sha" >>"$GITHUB_OUTPUT"
- name: Checkout
if: steps.configure.outputs.tag != null
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Prepare release parameters
id: prepare
if: steps.configure.outputs.tag != null
shell: bash
env:
TAG: ${{steps.configure.outputs.tag}}
run: .ci/prep_release.sh
- name: Create release
if: steps.configure.outputs.tag != null
id: create_release
shell: bash
env:
GH_TOKEN: ${{github.token}}
tag_name: ${{steps.configure.outputs.tag}}
target: ${{steps.configure.outputs.sha}}
release_name: ${{steps.prepare.outputs.title}}
body_path: ${{steps.prepare.outputs.body_path}}
prerelease: ${{steps.prepare.outputs.is_beta}}
run: |
if [[ $prerelease == yes ]]; then
args="--prerelease"
fi
gh release create "$tag_name" --draft --verify-tag $args \
--target "$target" --title "$release_name" \
--notes-file "$body_path"
build-linux:
strategy:
fail-fast: false
matrix:
# these names correspond to the files in .ci/$distro
include:
- distro: ArchLinux
package: skip # we are packaged in arch already
allow-failure: yes
- distro: Debian11
package: DEB
- distro: Debian12
package: DEB
- distro: Fedora39
package: RPM
- distro: Fedora40
package: RPM
- distro: UbuntuBionic
package: DEB
- distro: UbuntuFocal
package: DEB
test: skip # UbuntuFocal has a broken qt for debug builds
- distro: UbuntuJammy
package: DEB
test: skip # running tests on all distros is superfluous
- distro: UbuntuNoble
package: DEB
name: ${{matrix.distro}}
needs: configure
runs-on: ubuntu-latest
continue-on-error: ${{matrix.allow-failure == 'yes'}}
env:
NAME: ${{matrix.distro}}
CACHE: /tmp/${{matrix.distro}}-cache # ${{runner.temp}} does not work?
# cache size over the entire repo is 10Gi link:
# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy
CCACHE_SIZE: 200M
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Get cache timestamp
id: cache_timestamp
shell: bash
run: echo "timestamp=$(date -u '+%Y%m%d%H%M%S')" >>"$GITHUB_OUTPUT"
- name: Restore cache
uses: actions/cache@v4
env:
timestamp: ${{steps.cache_timestamp.outputs.timestamp}}
with:
path: ${{env.CACHE}}
key: docker-${{matrix.distro}}-cache-${{env.timestamp}}
restore-keys: |
docker-${{matrix.distro}}-cache-
- name: Build ${{matrix.distro}} Docker image
shell: bash
run: source .ci/docker.sh --build
- name: Build debug and test
if: matrix.test != 'skip'
shell: bash
env:
distro: '${{matrix.distro}}'
run: |
source .ci/docker.sh
RUN --server --debug --test --ccache "$CCACHE_SIZE" --parallel 4
- name: Build release package
id: build
if: matrix.package != 'skip'
shell: bash
env:
BUILD_DIR: build
SUFFIX: '-${{matrix.distro}}'
distro: '${{matrix.distro}}'
type: '${{matrix.package}}'
run: |
source .ci/docker.sh
RUN --server --release --package "$type" --dir "$BUILD_DIR" \
--ccache "$CCACHE_SIZE" --parallel 4
.ci/name_build.sh
- name: Upload artifact
if: matrix.package != 'skip'
uses: actions/upload-artifact@v4
with:
name: ${{matrix.distro}}-package
path: ${{steps.build.outputs.path}}
if-no-files-found: error
- name: Upload to release
if: matrix.package != 'skip' && needs.configure.outputs.tag != null
shell: bash
env:
GH_TOKEN: ${{github.token}}
tag_name: ${{needs.configure.outputs.tag}}
asset_path: ${{steps.build.outputs.path}}
asset_name: ${{steps.build.outputs.name}}
run: gh release upload "$tag_name" "$asset_path#$asset_name"
build-macos:
strategy:
fail-fast: false
matrix:
include:
- target: 12_Monterey_and_13_Ventura
os: macos-12
xcode: "14.0.1"
type: Release
make_package: 1
- target: 14_Sonoma
os: macos-14
xcode: "15.4"
type: Release
make_package: 1
- target: 14_Sonoma_Debug
os: macos-14
xcode: "15.4"
type: Debug
name: macOS${{matrix.target}}
needs: configure
runs-on: ${{matrix.os}}
continue-on-error: ${{matrix.allow-failure == 'yes'}}
env:
DEVELOPER_DIR:
/Applications/Xcode_${{matrix.xcode}}.app/Contents/Developer
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install dependencies using Homebrew
shell: bash
# cmake cannot find the mysql connector
# neither of these works: mariadb-connector-c mysql-connector-c++
env:
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
run: |
brew update
brew install protobuf qt --force-bottle
- name: Build on Xcode ${{matrix.xcode}}
shell: bash
id: build
env:
BUILDTYPE: '${{matrix.type}}'
MAKE_TEST: 1
MAKE_PACKAGE: '${{matrix.make_package}}'
PACKAGE_SUFFIX: '-macOS-${{matrix.target}}'
# macOS runner actually have only 3 cores
# See https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources
run: .ci/compile.sh --server --parallel 3
- name: Upload artifact
if: matrix.make_package
uses: actions/upload-artifact@v4
with:
name: macOS-${{matrix.target}}-dmg
path: ${{steps.build.outputs.path}}
if-no-files-found: error
- name: Upload to release
if: matrix.package != 'skip' && needs.configure.outputs.tag != null
shell: bash
env:
GH_TOKEN: ${{github.token}}
tag_name: ${{needs.configure.outputs.tag}}
asset_path: ${{steps.build.outputs.path}}
asset_name: ${{steps.build.outputs.name}}
run: gh release upload "$tag_name" "$asset_path#$asset_name"
build-windows:
strategy:
fail-fast: false
matrix:
include:
- target: 7
qt_version: 5.15.*
qt_arch: msvc2019_64
qt_tools: "tools_opensslv3_x64"
- target: 10
qt_version: 6.5.*
qt_arch: msvc2019_64
qt_tools: "tools_opensslv3_x64"
qt_modules: "qtmultimedia qtwebsockets"
name: Windows ${{matrix.target}}
needs: configure
runs-on: windows-2022
env:
CMAKE_GENERATOR: 'Visual Studio 17 2022'
steps:
- name: Add msbuild to PATH
id: add-msbuild
uses: microsoft/setup-msbuild@v2
with:
msbuild-architecture: x64
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Qt ${{matrix.qt_version}}
uses: jurplel/install-qt-action@v3
with:
cache: true
setup-python: false
version: ${{matrix.qt_version}}
arch: win64_${{matrix.qt_arch}}
tools: ${{matrix.qt_tools}}
modules: ${{matrix.qt_modules}}
- name: Run vcpkg
uses: lukka/run-vcpkg@v11
with:
runVcpkgInstall: true
doNotCache: false
env:
VCPKG_DEFAULT_TRIPLET: 'x64-windows'
VCPKG_DISABLE_METRICS: 1
- name: Build Cockatrice
id: build
shell: bash
env:
PACKAGE_SUFFIX: '-Win${{matrix.target}}'
CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}'
CMAKE_GENERATOR_PLATFORM: 'x64'
QTDIR: '${{github.workspace}}\Qt\${{matrix.qt_version}}\win64_${{matrix.qt_arch}}'
# No need for --parallel flag, MTT is added in the compile script to let cmake/msbuild manage core count,
# project and process parallelism: https://devblogs.microsoft.com/cppblog/improved-parallelism-in-msbuild/
run: .ci/compile.sh --server --release --test --package
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: Windows${{matrix.target}}-installer
path: ${{steps.build.outputs.path}}
if-no-files-found: error
- name: Upload to release
if: matrix.package != 'skip' && needs.configure.outputs.tag != null
shell: bash
env:
GH_TOKEN: ${{github.token}}
tag_name: ${{needs.configure.outputs.tag}}
asset_path: ${{steps.build.outputs.path}}
asset_name: ${{steps.build.outputs.name}}
run: gh release upload "$tag_name" "$asset_path#$asset_name"

29
.github/workflows/desktop-lint.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: Code Style (C++)
on:
pull_request:
paths-ignore:
- '**.md'
- 'webclient/**'
- '.github/workflows/web-*.yml'
- '.github/workflows/translations-*.yml'
jobs:
format:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 20 # should be enough to find merge base
- name: Install dependencies
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends clang-format cmake-format
- name: Check code formatting
shell: bash
run: ./.ci/lint_cpp.sh

72
.github/workflows/translations-pull.yml vendored Normal file
View File

@@ -0,0 +1,72 @@
name: Update Translations
on:
workflow_dispatch:
schedule:
# runs in the middle of each month starting a quarter (UTC) = two weeks after new strings are built
- cron: '0 0 15 1,4,7,10 *'
pull_request:
paths:
- '.github/workflows/translations-pull.yml'
jobs:
translations:
# Do not run the scheduled workflow on forks
if: github.event_name != 'schedule' || github.repository_owner == 'Cockatrice'
name: Pull languages
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Pull translated strings from Transifex
uses: transifex/cli-action@v2
with:
# used config file: https://github.com/Cockatrice/Cockatrice/blob/master/.tx/config
# https://github.com/transifex/cli#pulling-files-from-transifex
token: ${{ secrets.TX_TOKEN }}
args: pull --force --all
- name: Create pull request
if: github.event_name != 'pull_request'
id: create_pr
uses: peter-evans/create-pull-request@v6
with:
add-paths: |
cockatrice/translations/*.ts
oracle/translations/*.ts
webclient/public/locales/*/translation.json
commit-message: Update translation files
# author is the owner of the commit
author: github-actions <github-actions@github.com>
branch: ci-update_translations
delete-branch: true
title: 'Update translations'
body: |
Pulled all translated strings from [Transifex][1].
---
*This PR is automatically generated and updated by the workflow at `.github/workflows/translations-pull.yml`. Review [action runs][2].*<br>
*After merging, all new languages and translations are available in the next build.*
[1]: https://app.transifex.com/cockatrice/cockatrice/
[2]: https://github.com/Cockatrice/Cockatrice/actions/workflows/translations-pull.yml?query=branch%3Amaster
labels: |
CI
Translation
draft: false
- name: PR Status
if: github.event_name != 'pull_request'
shell: bash
env:
STATUS: ${{ steps.create_pr.outputs.pull-request-operation }}
run: |
if [[ "$STATUS" == "" ]]; then
echo "PR #${{ steps.create_pr.outputs.pull-request-number }} unchanged!" >> $GITHUB_STEP_SUMMARY
else
echo "PR #${{ steps.create_pr.outputs.pull-request-number }} $STATUS!" >> $GITHUB_STEP_SUMMARY
fi
echo "URL: ${{ steps.create_pr.outputs.pull-request-url }}" >> $GITHUB_STEP_SUMMARY

87
.github/workflows/translations-push.yml vendored Normal file
View File

@@ -0,0 +1,87 @@
name: Update Translation Source
on:
workflow_dispatch:
schedule:
# runs at the start of each quarter (UTC)
- cron: '0 0 1 1,4,7,10 *'
pull_request:
paths:
- '.github/workflows/translations-push.yml'
jobs:
translations:
# Do not run the scheduled workflow on forks
if: github.event_name != 'schedule' || github.repository_owner == 'Cockatrice'
name: Push strings
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Install lupdate
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends qttools5-dev-tools
- name: Update Cockatrice translation source
id: cockatrice
shell: bash
env:
FILE: 'cockatrice/cockatrice_en@source.ts'
DIRS: 'cockatrice/src common'
run: .ci/update_translation_source_strings.sh
- name: Update Oracle translation source
id: oracle
shell: bash
env:
FILE: 'oracle/oracle_en@source.ts'
DIRS: 'oracle/src'
run: .ci/update_translation_source_strings.sh
- name: Render template
id: template
uses: chuhlomin/render-template@v1
with:
template: .ci/update_translation_source_strings_template.md
vars: |
cockatrice_output: ${{ steps.cockatrice.outputs.output }}
oracle_output: ${{ steps.oracle.outputs.output }}
commit: ${{ github.sha }}
- name: Create pull request
if: github.event_name != 'pull_request'
id: create_pr
uses: peter-evans/create-pull-request@v6
with:
add-paths: |
cockatrice/cockatrice_en@source.ts
oracle/oracle_en@source.ts
commit-message: Update translation source strings
# author is the owner of the commit
author: github-actions <github-actions@github.com>
branch: ci-update_translation_source
delete-branch: true
title: 'Update source strings'
body: ${{ steps.template.outputs.result }}
labels: |
CI
Translation
draft: false
- name: PR Status
if: github.event_name != 'pull_request'
shell: bash
env:
STATUS: ${{ steps.create_pr.outputs.pull-request-operation }}
run: |
if [[ "$STATUS" == "" ]]; then
echo "PR #${{ steps.create_pr.outputs.pull-request-number }} unchanged!" >> $GITHUB_STEP_SUMMARY
else
echo "PR #${{ steps.create_pr.outputs.pull-request-number }} $STATUS!" >> $GITHUB_STEP_SUMMARY
fi
echo "URL: ${{ steps.create_pr.outputs.pull-request-url }}" >> $GITHUB_STEP_SUMMARY

52
.github/workflows/web-build.yml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: Build Web
on:
push:
branches:
- master
paths:
- '.github/workflows/web-*.yml'
- 'webclient/**'
- '!**.md'
pull_request:
paths:
- '.github/workflows/web-*.yml'
- 'webclient/**'
- '!**.md'
jobs:
build-web:
name: React (Node ${{matrix.node_version}})
runs-on: ubuntu-latest
defaults:
run:
working-directory: webclient
strategy:
fail-fast: false
matrix:
node_version:
- 16
- lts/*
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{matrix.node_version}}
cache: 'npm'
cache-dependency-path: 'webclient/package-lock.json'
- name: Install dependencies
run: npm clean-install
- name: Build app
run: npm run build
- name: Test app
run: npm run test

32
.github/workflows/web-lint.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: Code Style (TypeScript)
on:
pull_request:
paths:
- '.github/workflows/web-*.yml'
- 'webclient/**'
- '!**.md'
jobs:
ESLint:
runs-on: ubuntu-latest
defaults:
run:
working-directory: webclient
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: 'npm'
cache-dependency-path: 'webclient/package-lock.json'
- name: Install ESLint
run: npm clean-install --ignore-scripts
- name: Run ESLint
run: npm run lint

7
.gitignore vendored
View File

@@ -6,5 +6,10 @@ mysql.cnf
.DS_Store .DS_Store
.idea/ .idea/
*.aps *.aps
cmake-build-debug/ cmake-build-debug*
preferences preferences
compile_commands.json
.vs/
.vscode/
.cache
.gdb_history

7
.husky/pre-commit Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
cd webclient
npm run translate
git add src/i18n-default.json

View File

@@ -1,13 +1,26 @@
[main] [main]
host = https://www.transifex.com host = https://app.transifex.com
[cockatrice.cockatrice] [o:cockatrice:p:cockatrice:r:cockatrice-cockatrice-en-source-ts--master]
resource_name = Cockatrice
source_lang = en
source_file = cockatrice/cockatrice_en@source.ts
file_filter = cockatrice/translations/cockatrice_<lang>.ts file_filter = cockatrice/translations/cockatrice_<lang>.ts
source_file = cockatrice/translations/cockatrice_en.ts type = QT
source_lang = en minimum_perc = 10
[cockatrice.oracle] [o:cockatrice:p:cockatrice:r:oracle-oracle-en-source-ts--master]
resource_name = Oracle
source_lang = en
source_file = oracle/oracle_en@source.ts
file_filter = oracle/translations/oracle_<lang>.ts file_filter = oracle/translations/oracle_<lang>.ts
source_file = oracle/translations/oracle_en.ts type = QT
source_lang = en minimum_perc = 10
[o:cockatrice:p:cockatrice:r:webclient-src-i18n-default-json--master]
resource_name = Webclient
source_lang = en
source_file = webclient/src/i18n-default.json
file_filter = webclient/public/locales/<lang>/translation.json
type = KEYVALUEJSON
minimum_perc = 10

View File

@@ -5,58 +5,95 @@
# 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)
SET(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "Type of build") set(CMAKE_BUILD_TYPE
ELSE() ${CMAKE_BUILD_TYPE}
SET(CMAKE_BUILD_TYPE Release CACHE STRING "Type of build") CACHE STRING "Type of build"
ENDIF() )
else()
set(CMAKE_BUILD_TYPE
Release
CACHE STRING "Type of build"
)
endif()
# Early detect ccache
OPTION(USE_CCACHE "Cache the build results with ccache" ON)
if(USE_CCACHE) if(USE_CCACHE)
find_program(CCACHE_PROGRAM ccache) find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM) if(CCACHE_PROGRAM)
# Support Unix Makefiles and Ninja # Support Unix Makefiles and Ninja
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}") set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
MESSAGE(STATUS "Found CCache ${CCACHE_PROGRAM}") message(STATUS "Found CCache ${CCACHE_PROGRAM}")
endif() endif()
endif() endif()
if(WIN32) if(WIN32)
# Use vcpkg toolchain on Windows # Use vcpkg toolchain on Windows
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake set(CMAKE_TOOLCHAIN_FILE
CACHE STRING "Vcpkg toolchain file") ${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake
# Qt path set by user or env var CACHE STRING "Vcpkg toolchain file"
if (QTDIR OR DEFINED ENV{QTDIR} OR DEFINED ENV{QTDIR32} OR DEFINED ENV{QTDIR64}) )
else() # Qt path set by user or env var
set(QTDIR "" CACHE PATH "Path to Qt (e.g. C:/Qt/5.7/msvc2015_64)") if(QTDIR
message(WARNING "QTDIR variable is missing. Please set this variable to specify path to Qt (e.g. C:/Qt/5.7/msvc2015_64)") OR DEFINED ENV{QTDIR}
endif() OR DEFINED ENV{QTDIR32}
OR DEFINED ENV{QTDIR64}
)
else()
set(QTDIR
""
CACHE PATH "Path to Qt (e.g. C:/Qt/5.7/msvc2015_64)"
)
message(
WARNING "QTDIR variable is missing. Please set this variable to specify path to Qt (e.g. C:/Qt/5.7/msvc2015_64)"
)
endif()
endif() 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.9.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 "Rings of the Wild")
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)
@@ -65,148 +102,150 @@ include(createversionfile)
# Define a proper install path # Define a proper install path
if(UNIX) if(UNIX)
if(APPLE) if(APPLE)
# macOS # macOS
# Due to the special bundle structure ignore # Due to the special bundle structure ignore
# the prefix eventually set by the user. # the prefix eventually set by the user.
set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/release) set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/release)
# Force ccache usage if available # Force ccache usage if available
get_property(RULE_LAUNCH_COMPILE GLOBAL PROPERTY RULE_LAUNCH_COMPILE) get_property(RULE_LAUNCH_COMPILE GLOBAL PROPERTY RULE_LAUNCH_COMPILE)
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-cxx")
"${CMAKE_BINARY_DIR}/launch-c"
"${CMAKE_BINARY_DIR}/launch-cxx")
# Set Xcode project attributes to route compilation through our scripts # Set Xcode project attributes to route compilation through our scripts
set(CMAKE_XCODE_ATTRIBUTE_CC "${CMAKE_BINARY_DIR}/launch-c") set(CMAKE_XCODE_ATTRIBUTE_CC "${CMAKE_BINARY_DIR}/launch-c")
set(CMAKE_XCODE_ATTRIBUTE_CXX "${CMAKE_BINARY_DIR}/launch-cxx") set(CMAKE_XCODE_ATTRIBUTE_CXX "${CMAKE_BINARY_DIR}/launch-cxx")
set(CMAKE_XCODE_ATTRIBUTE_LD "${CMAKE_BINARY_DIR}/launch-c") set(CMAKE_XCODE_ATTRIBUTE_LD "${CMAKE_BINARY_DIR}/launch-c")
set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS "${CMAKE_BINARY_DIR}/launch-cxx") set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS "${CMAKE_BINARY_DIR}/launch-cxx")
endif()
else()
# Linux / BSD
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
#fix package build
if(PREFIX)
set(CMAKE_INSTALL_PREFIX ${PREFIX})
else()
set(CMAKE_INSTALL_PREFIX /usr/local)
endif()
endif()
endif() endif()
else()
# Linux / BSD
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
#fix package build
if(PREFIX)
set(CMAKE_INSTALL_PREFIX ${PREFIX})
else()
set(CMAKE_INSTALL_PREFIX /usr/local)
endif()
endif()
endif()
elseif(WIN32) 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- /W4")
# 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)
# linux/gcc, bsd/gcc, windows/mingw # linux/gcc, bsd/gcc, windows/mingw
include(CheckCXXCompilerFlag) include(CheckCXXCompilerFlag)
set(CMAKE_CXX_FLAGS_RELEASE "-s -O2") set(CMAKE_CXX_FLAGS_RELEASE "-s -O2")
if(WARNING_AS_ERROR) if(WARNING_AS_ERROR)
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0 -Wall -Wextra -Werror") set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0 -Wall -Wextra -Werror")
else() else()
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0 -Wall -Wextra") set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0 -Wall -Wextra")
endif()
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17")
endif()
set(ADDITIONAL_DEBUG_FLAGS
-Wcast-align
-Wmissing-declarations
-Wno-long-long
-Wno-error=extra
-Wno-error=delete-non-virtual-dtor
-Wno-error=sign-compare
-Wno-error=missing-declarations
)
foreach(FLAG ${ADDITIONAL_DEBUG_FLAGS})
check_cxx_compiler_flag("${FLAG}" CXX_HAS_WARNING_${FLAG})
if(CXX_HAS_WARNING_${FLAG})
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${FLAG}")
endif() endif()
endforeach()
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) else()
# other: osx/llvm, bsd/llvm
FOREACH(FLAG ${ADDITIONAL_DEBUG_FLAGS}) set(CMAKE_CXX_FLAGS_RELEASE "-O2")
CHECK_CXX_COMPILER_FLAG("${FLAG}" CXX_HAS_WARNING_${FLAG}) if(WARNING_AS_ERROR)
IF(CXX_HAS_WARNING_${FLAG}) set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -Wall -Wextra -Werror -Wno-unused-parameter")
SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${FLAG}") else()
ENDIF() set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -Wall -Wextra")
ENDFOREACH() endif()
ELSE() endif()
# other: osx/llvm, bsd/llvm
set(CMAKE_CXX_FLAGS_RELEASE "-O2")
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0")
ENDIF()
# GNU systems need to define the Mersenne exponent for the RNG to compile w/o warning # GNU systems need to define the Mersenne exponent for the RNG to compile w/o warning
IF(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
ADD_DEFINITIONS("-DSFMT_MEXP=19937") add_definitions("-DSFMT_MEXP=19937")
ENDIF() 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()
set(_lib_suffix 32) set(_lib_suffix 32)
endif() endif()
if(DEFINED QTDIR${_lib_suffix}) if(DEFINED QTDIR${_lib_suffix})
list(APPEND CMAKE_PREFIX_PATH "${QTDIR${_lib_suffix}}") list(APPEND CMAKE_PREFIX_PATH "${QTDIR${_lib_suffix}}")
elseif(DEFINED QTDIR) elseif(DEFINED QTDIR)
list(APPEND CMAKE_PREFIX_PATH "${QTDIR}") list(APPEND CMAKE_PREFIX_PATH "${QTDIR}")
elseif(DEFINED ENV{QTDIR${_lib_suffix}}) elseif(DEFINED ENV{QTDIR${_lib_suffix}})
list(APPEND CMAKE_PREFIX_PATH "$ENV{QTDIR${_lib_suffix}}") list(APPEND CMAKE_PREFIX_PATH "$ENV{QTDIR${_lib_suffix}}")
elseif(DEFINED ENV{QTDIR}) 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)
# Find other needed libraries # Find other needed libraries
FIND_PACKAGE(Protobuf REQUIRED) find_package(Protobuf CONFIG)
IF(NOT EXISTS "${Protobuf_PROTOC_EXECUTABLE}") if(NOT Protobuf_FOUND)
MESSAGE(FATAL_ERROR "No protoc command found!") find_package(Protobuf REQUIRED)
ELSE() endif()
MESSAGE(STATUS "Protoc version ${Protobuf_VERSION} found!")
ENDIF() if(${Protobuf_VERSION} VERSION_LESS "3.21.0.0" AND NOT EXISTS "${Protobuf_PROTOC_EXECUTABLE}")
message(FATAL_ERROR "No protoc command found!")
endif()
#Find OpenSSL #Find OpenSSL
IF(WIN32) if(WIN32)
FIND_PACKAGE(Win32SslRuntime) find_package(OpenSSL REQUIRED)
ENDIF() if(OPENSSL_FOUND)
include_directories(${OPENSSL_INCLUDE_DIRS})
else()
message(
WARNING
"Could not find OpenSSL runtime libraries. They are not required for compiling, but needs to be available at runtime."
)
endif()
endif()
#Find VCredist #Find VCredist
IF(MSVC) if(MSVC)
FIND_PACKAGE(VCredistRuntime) find_package(VCredistRuntime)
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")
@@ -216,82 +255,95 @@ set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}")
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_VERSION_FILENAME}") set(CPACK_PACKAGE_FILE_NAME "${PROJECT_VERSION_FILENAME}")
if(UNIX) if(UNIX)
if(APPLE) if(APPLE)
set(CPACK_GENERATOR DragNDrop ${CPACK_GENERATOR}) set(CPACK_GENERATOR DragNDrop ${CPACK_GENERATOR})
set(CPACK_GENERATOR "DragNDrop") set(CPACK_GENERATOR "DragNDrop")
set(CPACK_DMG_FORMAT "UDBZ") set(CPACK_DMG_FORMAT "UDBZ")
set(CPACK_DMG_VOLUME_NAME "${PROJECT_NAME}") set(CPACK_DMG_VOLUME_NAME "${PROJECT_NAME}")
set(CPACK_SYSTEM_NAME "OSX") set(CPACK_SYSTEM_NAME "OSX")
set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/cockatrice/resources/appicon.icns") set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/cockatrice/resources/appicon.icns")
set(CPACK_DMG_DS_STORE_SETUP_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/cmake/CMakeDMGSetup.script")
set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/dmgBackground.tif")
else()
# linux
if(CPACK_GENERATOR STREQUAL "RPM")
set(CPACK_RPM_PACKAGE_LICENSE "GPLv2")
set(CPACK_RPM_MAIN_COMPONENT "cockatrice")
if(Qt6_FOUND)
set(CPACK_RPM_PACKAGE_REQUIRES "protobuf, qt6-qttools, qt6-qtsvg, qt6-qtmultimedia")
elseif(Qt5_FOUND)
set(CPACK_RPM_PACKAGE_REQUIRES "protobuf, qt5-qttools, qt5-qtsvg, qt5-qtmultimedia")
endif()
set(CPACK_RPM_PACKAGE_GROUP "Amusements/Games")
set(CPACK_RPM_PACKAGE_URL "http://github.com/Cockatrice/Cockatrice")
# stop directories from making package conflicts
set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION
/usr/share/applications
/usr/share/icons
/usr/share/icons/hicolor
/usr/share/icons/hicolor/48x48
/usr/share/icons/hicolor/48x48/apps
/usr/share/icons/hicolor/scalable
/usr/share/icons/hicolor/scalable/apps
)
else() else()
# linux set(CPACK_GENERATOR DEB)
IF(CPACK_GENERATOR STREQUAL "RPM") set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
set(CPACK_RPM_PACKAGE_LICENSE "GPLv2") set(CPACK_DEBIAN_PACKAGE_SECTION "games")
set(CPACK_RPM_PACKAGE_REQUIRES "protobuf, qt5-qttools, qt5-qtsvg, qt5-qtmultimedia") set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://github.com/Cockatrice/Cockatrice")
set(CPACK_RPM_PACKAGE_GROUP "Amusements/Games") if(Qt6_FOUND)
set(CPACK_RPM_PACKAGE_URL "http://github.com/Cockatrice/Cockatrice") set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt6multimedia6, libqt6svg6, qt6-qpa-plugins")
ELSE() elseif(Qt5_FOUND)
set(CPACK_GENERATOR DEB) set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5multimedia5-plugins, libqt5svg5")
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) endif()
set(CPACK_DEBIAN_PACKAGE_SECTION "games")
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://github.com/Cockatrice/Cockatrice")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5multimedia5-plugins, libqt5svg5")
ENDIF()
endif() endif()
endif()
elseif(WIN32) elseif(WIN32)
set(CPACK_GENERATOR NSIS ${CPACK_GENERATOR}) set(CPACK_GENERATOR NSIS ${CPACK_GENERATOR})
if("${CMAKE_GENERATOR_PLATFORM}" MATCHES "(x64)") if("${CMAKE_GENERATOR_PLATFORM}" MATCHES "(x64)")
set(TRICE_IS_64_BIT 1) set(TRICE_IS_64_BIT 1)
else() else()
set(TRICE_IS_64_BIT 0) set(TRICE_IS_64_BIT 0)
endif() endif()
# Configure file with custom definitions for NSIS. # Configure file with custom definitions for NSIS.
configure_file( configure_file("${COCKATRICE_CMAKE_PATH}/NSIS.definitions.nsh.in" "${PROJECT_BINARY_DIR}/NSIS.definitions.nsh")
${CMAKE_MODULE_PATH}/NSIS.definitions.nsh.in
${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
if(VCREDISTRUNTIME_FOUND) if(VCREDISTRUNTIME_FOUND)
INSTALL(FILES "${VCREDISTRUNTIME_FILE}" DESTINATION ./) install(FILES "${VCREDISTRUNTIME_FILE}" DESTINATION ./)
endif() endif()
endif() 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()
if(Qt6_FOUND AND Qt6_VERSION_MINOR GREATER_EQUAL 3)
# Qt6.3+ requires project finalization to support translations
qt6_finalize_project()
endif() endif()

View File

@@ -8,7 +8,7 @@
<a href="#get-involved--">Get Involved</a> <b>|</b> <a href="#get-involved--">Get Involved</a> <b>|</b>
<a href="#community-resources">Community</a> <b>|</b> <a href="#community-resources">Community</a> <b>|</b>
<a href="#translations-">Translations</a> <b>|</b> <a href="#translations-">Translations</a> <b>|</b>
<a href="#build-">Build</a> <b>|</b> <a href="#build--">Build</a> <b>|</b>
<a href="#run">Run</a> <b>|</b> <a href="#run">Run</a> <b>|</b>
<a href="#license-">License</a> <a href="#license-">License</a>
</p> </p>
@@ -18,7 +18,7 @@
<br><pre> <br><pre>
<b>To get started, &#8674; [view our webpage](https://cockatrice.github.io/)</b><br> <b>To get started, &#8674; [view our webpage](https://cockatrice.github.io/)</b><br>
<b>To get support or suggest changes &#8674; [file an issue](https://github.com/Cockatrice/Cockatrice/issues) ([How?](https://github.com/Cockatrice/Cockatrice/wiki/How-to-Create-a-GitHub-Ticket-Regarding-Cockatrice))</b> <b>To get support or suggest changes &#8674; [file an issue](https://github.com/Cockatrice/Cockatrice/issues) ([How?](https://github.com/Cockatrice/Cockatrice/wiki/How-to-Create-a-GitHub-Ticket-Regarding-Cockatrice))</b>
<b>To help with development, see how to [get involved](#get-involved-)</b> <b>To help with development, see how to [get involved](#get-involved--)</b>
</pre><br> </pre><br>
@@ -40,7 +40,7 @@ Downloads are available for full releases and the current beta version in develo
- To be a Cockatrice Beta Tester, use this version. Find more information [here](https://github.com/Cockatrice/Cockatrice/wiki/Release-Channels)! - To be a Cockatrice Beta Tester, use this version. Find more information [here](https://github.com/Cockatrice/Cockatrice/wiki/Release-Channels)!
# Get Involved [![Discord](https://img.shields.io/discord/314987288398659595?label=Discord&logo=discord&logoColor=white)](https://discord.gg/3Z9yzmA) [![Gitter Chat](https://img.shields.io/gitter/room/Cockatrice/Cockatrice)](https://gitter.im/Cockatrice/Cockatrice) # Get Involved [![Discord](https://img.shields.io/discord/314987288398659595?label=Discord&logo=discord&logoColor=white)](https://discord.gg/3Z9yzmA)
Join our [Discord community](https://discord.gg/3Z9yzmA) to connect with the project or fellow users of the app. The Cockatrice developers are also available on [Gitter](https://gitter.im/Cockatrice/Cockatrice). Come here to talk about the application, features, or just to hang out.<br> Join our [Discord community](https://discord.gg/3Z9yzmA) to connect with the project or fellow users of the app. The Cockatrice developers are also available on [Gitter](https://gitter.im/Cockatrice/Cockatrice). Come here to talk about the application, features, or just to hang out.<br>
For support regarding specific servers, please contact that server's admin or forum for support rather than asking here.<br> For support regarding specific servers, please contact that server's admin or forum for support rather than asking here.<br>
@@ -52,8 +52,6 @@ We maintain two tags for contributors to find issues to work on:
For both tags, we're willing to provide help to contributors in showing them where and how they can make changes, as well as code review for changes they submit. For both tags, we're willing to provide help to contributors in showing them where and how they can make changes, as well as code review for changes they submit.
Read the long-term project **roadmap** to see planned edits and milestones [here](https://docs.google.com/document/d/1Ewe5uSaRE2nR2pNPMaGmP6gVZdqgFbBgwSscGqIr4W0/edit).
We try to be responsive to new issues. We'll provide advice on how best to implement a feature; alternately, we can show you where the codebase is doing something similar before you get too far along. We try to be responsive to new issues. We'll provide advice on how best to implement a feature; alternately, we can show you where the codebase is doing something similar before you get too far along.
Cockatrice uses the [Google Developer Documentation Style Guide](https://developers.google.com/style/) to ensure consistent documentation. We encourage you to improve the documentation by suggesting edits based on this guide. Cockatrice uses the [Google Developer Documentation Style Guide](https://developers.google.com/style/) to ensure consistent documentation. We encourage you to improve the documentation by suggesting edits based on this guide.
@@ -67,18 +65,14 @@ Cockatrice uses the [Google Developer Documentation Style Guide](https://develop
- [reddit r/Cockatrice](https://reddit.com/r/cockatrice) - [reddit r/Cockatrice](https://reddit.com/r/cockatrice)
# Translations [![Transifex Project](https://img.shields.io/badge/translate-on%20transifex-brightgreen)](https://www.transifex.com/projects/p/cockatrice/) # Translations [![Transifex Project](https://img.shields.io/badge/translate-on%20transifex-brightgreen)](https://transifex.com/cockatrice/cockatrice/)
Cockatrice uses Transifex for translations. You can help us bring Cockatrice and Oracle to your language or just edit single wordings right from within your browser by visiting our [Transifex project page](https://www.transifex.com/projects/p/cockatrice/).<br> Cockatrice uses Transifex for translations. You can help us bring Cockatrice, Oracle and Webatrice to your language or just adjust single wordings right from within your browser by visiting our [Transifex project page](https://transifex.com/cockatrice/cockatrice/).<br>
| Cockatrice | Oracle |
|:-:|:-:|
| [![Cockatrice Translation Status](https://www.transifex.com/projects/p/cockatrice/resource/cockatrice/chart/image_png/)](https://www.transifex.com/projects/p/cockatrice/) | [![Oracle Translation Status](https://www.transifex.com/projects/p/cockatrice/resource/oracle/chart/image_png/)](https://www.transifex.com/projects/p/cockatrice/) |
Check out our [Translator FAQ](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ) for more information about contributing!<br> Check out our [Translator FAQ](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ) for more information about contributing!<br>
# Build [![CI Builds](https://github.com/Cockatrice/Cockatrice/actions/workflows/ci-builds.yml/badge.svg?branch=master&event=push)](https://github.com/Cockatrice/Cockatrice/actions/workflows/ci-builds.yml?query=branch%3Amaster+event%3Apush) # Build [![CI Desktop](https://github.com/Cockatrice/Cockatrice/actions/workflows/desktop-build.yml/badge.svg?branch=master&event=push)](https://github.com/Cockatrice/Cockatrice/actions/workflows/desktop-build.yml?query=branch%3Amaster+event%3Apush) [![CI Web](https://github.com/Cockatrice/Cockatrice/actions/workflows/web-build.yml/badge.svg?branch=master&event=push)](https://github.com/Cockatrice/Cockatrice/actions/workflows/web-build.yml?query=branch%3Amaster+event%3Apush)
**Detailed compiling instructions can be found on the Cockatrice wiki under [Compiling Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/Compiling-Cockatrice)** **Detailed compiling instructions can be found on the Cockatrice wiki under [Compiling Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/Compiling-Cockatrice)**
@@ -117,6 +111,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

View File

@@ -0,0 +1,55 @@
on run argv
set image_name to item 1 of argv
tell application "Finder"
tell disk image_name
-- wait for the image to finish mounting
set open_attempts to 0
repeat while open_attempts < 4
try
open
delay 1
set open_attempts to 5
close
on error errStr number errorNumber
set open_attempts to open_attempts + 1
delay 10
end try
end repeat
delay 5
-- open the image the first time and save a DS_Store with just
-- background and icon setup
open
set current view of container window to icon view
set theViewOptions to the icon view options of container window
set background picture of theViewOptions to file ".background:background.tif"
set arrangement of theViewOptions to not arranged
set icon size of theViewOptions to 128
delay 5
close
-- next setup the position of the app and Applications symlink
-- plus hide all the window decoration
open
update without registering applications
tell container window
set sidebar width to 0
set statusbar visible to false
set toolbar visible to false
set the bounds to { 400, 100, 1400, 922 }
set position of item "Cockatrice.app" to { 139, 214 }
set position of item "Oracle.app" to { 139, 414 }
set position of item "Servatrice.app" to { 139, 614 }
set position of item "dbconverter.app" to { 1400, 1400 }
set position of item "Applications" to { 861, 414 }
end tell
update without registering applications
delay 5
close
end tell
delay 1
end tell
end run

View File

@@ -1,18 +1,18 @@
# Find the LibExecinfo library - FreeBSD only # Find the LibExecinfo library - FreeBSD only
FIND_PATH(LIBEXECINFO_INCLUDE_DIR execinfo.h) find_path(LIBEXECINFO_INCLUDE_DIR execinfo.h)
FIND_LIBRARY(LIBEXECINFO_LIBRARY NAMES execinfo) find_library(LIBEXECINFO_LIBRARY NAMES execinfo)
IF(LIBEXECINFO_INCLUDE_DIR AND LIBEXECINFO_LIBRARY) if(LIBEXECINFO_INCLUDE_DIR AND LIBEXECINFO_LIBRARY)
SET(LIBEXECINFO_FOUND TRUE) set(LIBEXECINFO_FOUND TRUE)
ENDIF() endif()
IF(LIBEXECINFO_FOUND) if(LIBEXECINFO_FOUND)
IF(NOT LIBEXECINFO_FIND_QUIETLY) if(NOT LIBEXECINFO_FIND_QUIETLY)
MESSAGE(STATUS "Found LibExecinfo: ${EXECINFO_LIBRARY}") message(STATUS "Found LibExecinfo: ${EXECINFO_LIBRARY}")
ENDIF() endif()
ELSE() else()
IF(LIBEXECINFO_FIND_REQUIRED) if(LIBEXECINFO_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find LibExecinfo") message(FATAL_ERROR "Could not find LibExecinfo")
ENDIF() endif()
ENDIF() endif()

117
cmake/FindQtRuntime.cmake Normal file
View File

@@ -0,0 +1,117 @@
# 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
WebSockets
Widgets
)
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)
# Linguist is now a component in Qt6 instead of an external package
find_package(
Qt6 6.2.3
COMPONENTS ${REQUIRED_QT_COMPONENTS} Linguist
QUIET HINTS ${Qt6_DIR}
)
endif()
if(Qt6_FOUND)
set(COCKATRICE_QT_VERSION_NAME Qt6)
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()
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}")
message(STATUS "Found Qt ${${COCKATRICE_QT_VERSION_NAME}_VERSION} at: ${${COCKATRICE_QT_VERSION_NAME}_DIR}")

View File

@@ -1,36 +1,42 @@
# Find the MS Visual Studio VC redistributable package # Find the MS Visual Studio VC redistributable package
if (WIN32) if(WIN32)
set(VCREDISTRUNTIME_FOUND "NO") set(VCREDISTRUNTIME_FOUND "NO")
if(CMAKE_SIZEOF_VOID_P EQUAL 8) # 64-bit if(CMAKE_SIZEOF_VOID_P EQUAL 8) # 64-bit
set(REDIST_ARCH x64) set(REDIST_ARCH x64)
else() else()
set(REDIST_ARCH x86) set(REDIST_ARCH x86)
endif() endif()
set(REDIST_FILE vcredist_${REDIST_ARCH}.exe) # VS 2017 uses vcredist_ARCH.exe, VS 2022 uses vc_redist.ARCH.exe
set(REDIST_FILE_NAMES vcredist_${REDIST_ARCH}.exe vcredist.${REDIST_ARCH}.exe vc_redist.${REDIST_ARCH}.exe)
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE) set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
include(InstallRequiredSystemLibraries) include(InstallRequiredSystemLibraries)
# Check if the list contains minimum one element, to get the path from # Check if the list contains minimum one element, to get the path from
list(LENGTH CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS libsCount) list(LENGTH CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS libsCount)
if (libsCount GREATER 0) if(libsCount GREATER 0)
list(GET CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS 0 _path) list(GET CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS 0 _path)
get_filename_component(_path ${_path} DIRECTORY) get_filename_component(_path ${_path} DIRECTORY)
get_filename_component(_path ${_path}/../../ ABSOLUTE) get_filename_component(_path ${_path}/../../ ABSOLUTE)
if (EXISTS "${_path}/${REDIST_FILE}") # VS 2017 foreach(redist_file ${REDIST_FILE_NAMES})
set(VCREDISTRUNTIME_FOUND "YES") if(EXISTS "${_path}/${redist_file}")
set(VCREDISTRUNTIME_FILE ${_path}/${REDIST_FILE}) set(VCREDISTRUNTIME_FOUND "YES")
endif() set(VCREDISTRUNTIME_FILE ${_path}/${redist_file})
endif() break()
endif()
endforeach()
endif()
if(VCREDISTRUNTIME_FOUND) if(VCREDISTRUNTIME_FOUND)
message(STATUS "Found VCredist ${VCREDISTRUNTIME_FILE}") message(STATUS "Found VCredist ${VCREDISTRUNTIME_FILE}")
else() else()
message(WARNING "Could not find VCredist package. It's not required for compiling, but needs to be available at runtime.") message(
endif() WARNING "Could not find VCredist package. It's not required for compiling, but needs to be available at runtime."
)
endif()
endif() endif()

View File

@@ -1,77 +0,0 @@
# Find the OpenSSL runtime libraries (.dll) for Windows that
# will be needed by Qt in order to access https urls.
if (WIN32)
# Get standard installation paths for OpenSSL under Windows
# http://www.slproweb.com/products/Win32OpenSSL.html
if( CMAKE_SIZEOF_VOID_P EQUAL 8 )
# target win64
set(_OPENSSL_ROOT_HINTS
${OPENSSL_ROOT_DIR}
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]"
ENV OPENSSL_ROOT_DIR
)
file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles)
set(_OPENSSL_ROOT_PATHS
"C:/Tools/vcpkg/installed/x64-windows/bin"
"${_programfiles}/OpenSSL-Win64"
"C:/OpenSSL-Win64/"
)
unset(_programfiles)
else( CMAKE_SIZEOF_VOID_P EQUAL 8 )
# target win32
set(_OPENSSL_ROOT_HINTS
${OPENSSL_ROOT_DIR}
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]"
ENV OPENSSL_ROOT_DIR
)
file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles)
set(_OPENSSL_ROOT_PATHS
"C:/Tools/vcpkg/installed/x86-windows/bin"
"${_programfiles}/OpenSSL"
"${_programfiles}/OpenSSL-Win32"
"C:/OpenSSL/"
"C:/OpenSSL-Win32/"
)
unset(_programfiles)
endif( CMAKE_SIZEOF_VOID_P EQUAL 8 )
else ()
set(_OPENSSL_ROOT_HINTS
${OPENSSL_ROOT_DIR}
ENV OPENSSL_ROOT_DIR
)
endif ()
set(_OPENSSL_ROOT_HINTS_AND_PATHS
HINTS ${_OPENSSL_ROOT_HINTS}
PATHS ${_OPENSSL_ROOT_PATHS}
)
# For OpenSSL < 1.1, they are named libeay32 and ssleay32 and even if the dll is 64bit, it's still suffixed as *32.dll
# For OpenSSL >= 1.1, they are named libcrypto and libssl with no suffix
if( CMAKE_SIZEOF_VOID_P EQUAL 8 )
# target win64
FIND_FILE(WIN32SSLRUNTIME_LIBEAY NAMES libcrypto-1_1-x64.dll libcrypto.dll libeay32.dll ${_OPENSSL_ROOT_HINTS_AND_PATHS})
FIND_FILE(WIN32SSLRUNTIME_SSLEAY NAMES libssl-1_1-x64.dll libssl.dll ssleay32.dll ${_OPENSSL_ROOT_HINTS_AND_PATHS})
else( CMAKE_SIZEOF_VOID_P EQUAL 8 )
# target win32
FIND_FILE(WIN32SSLRUNTIME_LIBEAY NAMES libcrypto-1_1.dll libcrypto.dll libeay32.dll ${_OPENSSL_ROOT_HINTS_AND_PATHS})
FIND_FILE(WIN32SSLRUNTIME_SSLEAY NAMES libssl-1_1.dll libssl.dll ssleay32.dll ${_OPENSSL_ROOT_HINTS_AND_PATHS})
endif( CMAKE_SIZEOF_VOID_P EQUAL 8 )
IF(WIN32SSLRUNTIME_LIBEAY AND WIN32SSLRUNTIME_SSLEAY)
SET(WIN32SSLRUNTIME_LIBRARIES "${WIN32SSLRUNTIME_LIBEAY}" "${WIN32SSLRUNTIME_SSLEAY}")
SET(WIN32SSLRUNTIME_FOUND "YES")
message(STATUS "Found OpenSSL ${WIN32SSLRUNTIME_LIBRARIES}")
ELSE()
SET(WIN32SSLRUNTIME_FOUND "NO")
message(WARNING "Could not find OpenSSL runtime libraries. They are not required for compiling, but needs to be available at runtime.")
ENDIF()
MARK_AS_ADVANCED(
WIN32SSLRUNTIME_LIBEAY
WIN32SSLRUNTIME_SSLEAY
)

View File

@@ -248,20 +248,20 @@ ${If} $PortableMode = 0
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "VersionMajor" "@CPACK_PACKAGE_VERSION_MAJOR@" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "VersionMajor" "@CPACK_PACKAGE_VERSION_MAJOR@"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "VersionMinor" "@CPACK_PACKAGE_VERSION_MINOR@" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "VersionMinor" "@CPACK_PACKAGE_VERSION_MINOR@"
IfFileExists "$INSTDIR\vcredist_x86.exe" VcRedist86Exists PastVcRedist86Check IfFileExists "$INSTDIR\vc_redist.x86.exe" VcRedist86Exists PastVcRedist86Check
VcRedist86Exists: VcRedist86Exists:
ExecWait '"$INSTDIR\vcredist_x86.exe" /passive /norestart' ExecWait '"$INSTDIR\vc_redist.x86.exe" /passive /norestart'
DetailPrint "Wait to ensure unlock of vc_redist file after installation..." DetailPrint "Wait to ensure unlock of vc_redist file after installation..."
Sleep 3000 Sleep 3000
Delete "$INSTDIR\vcredist_x86.exe" Delete "$INSTDIR\vc_redist.x86.exe"
PastVcRedist86Check: PastVcRedist86Check:
IfFileExists "$INSTDIR\vcredist_x64.exe" VcRedist64Exists PastVcRedist64Check IfFileExists "$INSTDIR\vc_redist.x64.exe" VcRedist64Exists PastVcRedist64Check
VcRedist64Exists: VcRedist64Exists:
ExecWait '"$INSTDIR\vcredist_x64.exe" /passive /norestart' ExecWait '"$INSTDIR\vc_redist.x64.exe" /passive /norestart'
DetailPrint "Sleep to ensure unlock of vc_redist file after installation..." DetailPrint "Sleep to ensure unlock of vc_redist file after installation..."
Sleep 3000 Sleep 3000
Delete "$INSTDIR\vcredist_x64.exe" Delete "$INSTDIR\vc_redist.x64.exe"
PastVcRedist64Check: PastVcRedist64Check:
${Else} ${Else}

View File

@@ -1,21 +1,24 @@
set(VERSION_STRING_CPP "${PROJECT_BINARY_DIR}/version_string.cpp") set(VERSION_STRING_CPP "${PROJECT_BINARY_DIR}/version_string.cpp")
set(VERSION_STRING_H "${PROJECT_BINARY_DIR}/version_string.h") set(VERSION_STRING_H "${PROJECT_BINARY_DIR}/version_string.h")
INCLUDE_DIRECTORIES(${PROJECT_BINARY_DIR}) include_directories(${PROJECT_BINARY_DIR})
set( hstring "extern const char *VERSION_STRING\; set(hstring
"extern const char *VERSION_STRING\;
extern const char *VERSION_COMMIT\; extern const char *VERSION_COMMIT\;
extern const char *VERSION_DATE\;\n" ) extern const char *VERSION_DATE\;\n"
set( cppstring "const char *VERSION_STRING = \"${PROJECT_VERSION_FRIENDLY}\"\; )
set(cppstring
"const char *VERSION_STRING = \"${PROJECT_VERSION_FRIENDLY}\"\;
const char *VERSION_COMMIT = \"${GIT_COMMIT_ID}\"\; const char *VERSION_COMMIT = \"${GIT_COMMIT_ID}\"\;
const char *VERSION_DATE = \"${GIT_COMMIT_DATE_FRIENDLY}\"\;\n") const char *VERSION_DATE = \"${GIT_COMMIT_DATE_FRIENDLY}\"\;\n"
file(WRITE ${PROJECT_BINARY_DIR}/version_string.cpp.txt ${cppstring} )
file(WRITE ${PROJECT_BINARY_DIR}/version_string.h.txt ${hstring} )
execute_process(
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PROJECT_BINARY_DIR}/version_string.h.txt ${VERSION_STRING_H}
)
execute_process(
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PROJECT_BINARY_DIR}/version_string.cpp.txt ${VERSION_STRING_CPP}
) )
file(WRITE ${PROJECT_BINARY_DIR}/version_string.cpp.txt ${cppstring})
file(WRITE ${PROJECT_BINARY_DIR}/version_string.h.txt ${hstring})
execute_process(
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PROJECT_BINARY_DIR}/version_string.h.txt ${VERSION_STRING_H}
)
execute_process(
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PROJECT_BINARY_DIR}/version_string.cpp.txt ${VERSION_STRING_CPP}
)

BIN
cmake/dmgBackground.tif Normal file

Binary file not shown.

View File

@@ -1,154 +1,187 @@
# HELPER FUNCTIONS # HELPER FUNCTIONS
function(get_commit_id) function(get_commit_id)
# get last commit hash # get last commit hash
execute_process( execute_process(
COMMAND ${GIT_EXECUTABLE} log -1 --abbrev=7 --date=short "--pretty=%h" COMMAND ${GIT_EXECUTABLE} log -1 --abbrev=7 --date=short "--pretty=%h"
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
RESULT_VARIABLE res_var RESULT_VARIABLE res_var
OUTPUT_VARIABLE GIT_COM_ID OUTPUT_VARIABLE GIT_COM_ID
) )
if(NOT ${res_var} EQUAL 0) if(NOT ${res_var} EQUAL 0)
message(WARNING "Git failed (not a repo, or no tags). Build will not contain git revision info.") message(WARNING "Git failed (not a repo, or no tags). Build will not contain git revision info.")
return() return()
endif() endif()
string(REPLACE "\n" "" GIT_COM_ID "${GIT_COM_ID}") string(REPLACE "\n" "" GIT_COM_ID "${GIT_COM_ID}")
set(GIT_COMMIT_ID "${GIT_COM_ID}" PARENT_SCOPE) set(GIT_COMMIT_ID
set(PROJECT_VERSION_LABEL "custom(${GIT_COM_ID})" PARENT_SCOPE) "${GIT_COM_ID}"
PARENT_SCOPE
)
set(PROJECT_VERSION_LABEL
"custom(${GIT_COM_ID})"
PARENT_SCOPE
)
endfunction() endfunction()
function(get_commit_date) function(get_commit_date)
# get last commit date # get last commit date
execute_process( execute_process(
COMMAND ${GIT_EXECUTABLE} log -1 --date=short "--pretty=%cd" COMMAND ${GIT_EXECUTABLE} log -1 --date=short "--pretty=%cd"
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
RESULT_VARIABLE res_var RESULT_VARIABLE res_var
OUTPUT_VARIABLE GIT_COM_DATE OUTPUT_VARIABLE GIT_COM_DATE
) )
if(NOT ${res_var} EQUAL 0) if(NOT ${res_var} EQUAL 0)
message(WARNING "Git failed (not a repo, or no tags). Build will not contain git revision info.") message(WARNING "Git failed (not a repo, or no tags). Build will not contain git revision info.")
return() return()
endif() endif()
string(REPLACE "\n" "" GIT_COM_DATE "${GIT_COM_DATE}") string(REPLACE "\n" "" GIT_COM_DATE "${GIT_COM_DATE}")
set(GIT_COMMIT_DATE_FRIENDLY "${GIT_COM_DATE}" PARENT_SCOPE) set(GIT_COMMIT_DATE_FRIENDLY
"${GIT_COM_DATE}"
PARENT_SCOPE
)
string(REPLACE "-" "" GIT_COM_DATE "${GIT_COM_DATE}") string(REPLACE "-" "" GIT_COM_DATE "${GIT_COM_DATE}")
set(GIT_COMMIT_DATE "${GIT_COM_DATE}" PARENT_SCOPE) set(GIT_COMMIT_DATE
"${GIT_COM_DATE}"
PARENT_SCOPE
)
endfunction() endfunction()
function(get_tag_name commit) function(get_tag_name commit)
if(${commit} STREQUAL "unknown") if(${commit} STREQUAL "unknown")
return() return()
endif() endif()
execute_process( execute_process(
COMMAND ${GIT_EXECUTABLE} describe --exact-match --tags ${commit} COMMAND ${GIT_EXECUTABLE} describe --exact-match --tags ${commit}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
RESULT_VARIABLE res_var RESULT_VARIABLE res_var
OUTPUT_VARIABLE GIT_TAG OUTPUT_VARIABLE GIT_TAG
ERROR_VARIABLE GIT_TAG_ERR ERROR_VARIABLE GIT_TAG_ERR
) )
if((NOT ${res_var} EQUAL 0) OR (${GIT_TAG_ERR} MATCHES "fatal: no tag exactly matches.*")) if((NOT ${res_var} EQUAL 0) OR (${GIT_TAG_ERR} MATCHES "fatal: no tag exactly matches.*"))
message(STATUS "Commit is not a release or prerelease (no git tag found)") message(STATUS "Commit is not a release or prerelease (no git tag found)")
return() return()
endif() endif()
string(REPLACE "\n" "" GIT_TAG "${GIT_TAG}") string(REPLACE "\n" "" GIT_TAG "${GIT_TAG}")
message(STATUS "Commit is a release or prerelease, git tag: ${GIT_TAG}") message(STATUS "Commit is a release or prerelease, git tag: ${GIT_TAG}")
# Extract information from tag: # Extract information from tag:
# YYYY-MM-DD-Release-MAJ.MIN.PATCH # YYYY-MM-DD-Release-MAJ.MIN.PATCH
# YYYY-MM-DD-Development-MAJ.MIN.PATCH-beta.X # YYYY-MM-DD-Development-MAJ.MIN.PATCH-beta.X
string(REPLACE "-" ";" GIT_TAG_EXPLODED "${GIT_TAG}") string(REPLACE "-" ";" GIT_TAG_EXPLODED "${GIT_TAG}")
string(REPLACE "." ";" GIT_TAG_EXPLODED "${GIT_TAG_EXPLODED}") string(REPLACE "." ";" GIT_TAG_EXPLODED "${GIT_TAG_EXPLODED}")
# Sanity checks: length # Sanity checks: length
list(LENGTH GIT_TAG_EXPLODED GIT_TAG_LISTCOUNT) list(LENGTH GIT_TAG_EXPLODED GIT_TAG_LISTCOUNT)
if(${GIT_TAG_LISTCOUNT} LESS 7 OR ${GIT_TAG_LISTCOUNT} GREATER 9) if(${GIT_TAG_LISTCOUNT} LESS 7 OR ${GIT_TAG_LISTCOUNT} GREATER 9)
message(WARNING "Invalid tag format, got ${GIT_TAG_LISTCOUNT} tokens") message(WARNING "Invalid tag format, got ${GIT_TAG_LISTCOUNT} tokens")
return() return()
endif() endif()
# Year # Year
list(GET GIT_TAG_EXPLODED 0 GIT_TAG_YEAR) list(GET GIT_TAG_EXPLODED 0 GIT_TAG_YEAR)
if(${GIT_TAG_YEAR} LESS 2017 OR ${GIT_TAG_LISTCOUNT} GREATER 2100) if(${GIT_TAG_YEAR} LESS 2017 OR ${GIT_TAG_LISTCOUNT} GREATER 2100)
message(WARNING "Invalid tag year ${GIT_TAG_YEAR}") message(WARNING "Invalid tag year ${GIT_TAG_YEAR}")
return() return()
endif() endif()
# Month # Month
list(GET GIT_TAG_EXPLODED 1 GIT_TAG_MONTH) list(GET GIT_TAG_EXPLODED 1 GIT_TAG_MONTH)
if(${GIT_TAG_MONTH} LESS 1 OR ${GIT_TAG_MONTH} GREATER 12) if(${GIT_TAG_MONTH} LESS 1 OR ${GIT_TAG_MONTH} GREATER 12)
message(WARNING "Invalid tag month ${GIT_TAG_MONTH}") message(WARNING "Invalid tag month ${GIT_TAG_MONTH}")
return() return()
endif() endif()
# Day # Day
list(GET GIT_TAG_EXPLODED 2 GIT_TAG_DAY) list(GET GIT_TAG_EXPLODED 2 GIT_TAG_DAY)
if(${GIT_TAG_DAY} LESS 1 OR ${GIT_TAG_DAY} GREATER 31) if(${GIT_TAG_DAY} LESS 1 OR ${GIT_TAG_DAY} GREATER 31)
message(WARNING "Invalid tag day ${GIT_TAG_DAY}") message(WARNING "Invalid tag day ${GIT_TAG_DAY}")
return() return()
endif() endif()
# Type # Type
list(GET GIT_TAG_EXPLODED 3 GIT_TAG_TYPE) list(GET GIT_TAG_EXPLODED 3 GIT_TAG_TYPE)
if(NOT(${GIT_TAG_TYPE} STREQUAL "Release" OR ${GIT_TAG_TYPE} STREQUAL "Development")) if(NOT (${GIT_TAG_TYPE} STREQUAL "Release" OR ${GIT_TAG_TYPE} STREQUAL "Development"))
message(WARNING "Invalid tag type ${GIT_TAG_TYPE}") message(WARNING "Invalid tag type ${GIT_TAG_TYPE}")
return() return()
endif() endif()
# Major # Major
list(GET GIT_TAG_EXPLODED 4 GIT_TAG_MAJOR) list(GET GIT_TAG_EXPLODED 4 GIT_TAG_MAJOR)
if(${GIT_TAG_MAJOR} LESS 0 OR ${GIT_TAG_MAJOR} GREATER 99) if(${GIT_TAG_MAJOR} LESS 0 OR ${GIT_TAG_MAJOR} GREATER 99)
message(WARNING "Invalid tag major version ${GIT_TAG_MAJOR}") message(WARNING "Invalid tag major version ${GIT_TAG_MAJOR}")
return() return()
endif() endif()
# Minor # Minor
list(GET GIT_TAG_EXPLODED 5 GIT_TAG_MINOR) list(GET GIT_TAG_EXPLODED 5 GIT_TAG_MINOR)
if(${GIT_TAG_MINOR} LESS 0 OR ${GIT_TAG_MINOR} GREATER 99) if(${GIT_TAG_MINOR} LESS 0 OR ${GIT_TAG_MINOR} GREATER 99)
message(WARNING "Invalid tag minor version ${GIT_TAG_MINOR}") message(WARNING "Invalid tag minor version ${GIT_TAG_MINOR}")
return() return()
endif() endif()
# Patch # Patch
list(GET GIT_TAG_EXPLODED 6 GIT_TAG_PATCH) list(GET GIT_TAG_EXPLODED 6 GIT_TAG_PATCH)
if(${GIT_TAG_PATCH} LESS 0 OR ${GIT_TAG_PATCH} GREATER 99) if(${GIT_TAG_PATCH} LESS 0 OR ${GIT_TAG_PATCH} GREATER 99)
message(WARNING "Invalid tag patch version ${GIT_TAG_PATCH}") message(WARNING "Invalid tag patch version ${GIT_TAG_PATCH}")
return() return()
endif() endif()
# Label # Label
# 7 = Stable release # 7 = Stable release
# 8 = Dev release, first beta so only "beta" attached # 8 = Dev release, first beta so only "beta" attached
# 9 = Dev release, subsequent beta so "beta.N" attached (N>=2) # 9 = Dev release, subsequent beta so "beta.N" attached (N>=2)
if(${GIT_TAG_LISTCOUNT} EQUAL 8) if(${GIT_TAG_LISTCOUNT} EQUAL 8)
list(GET GIT_TAG_EXPLODED 7 GIT_TAG_LABEL) list(GET GIT_TAG_EXPLODED 7 GIT_TAG_LABEL)
elseif(${GIT_TAG_LISTCOUNT} EQUAL 9) elseif(${GIT_TAG_LISTCOUNT} EQUAL 9)
list(GET GIT_TAG_EXPLODED 7 GIT_TAG_LABEL) list(GET GIT_TAG_EXPLODED 7 GIT_TAG_LABEL)
list(GET GIT_TAG_EXPLODED 8 GIT_TAG_LABEL_NUM) list(GET GIT_TAG_EXPLODED 8 GIT_TAG_LABEL_NUM)
set(GIT_TAG_LABEL ${GIT_TAG_LABEL} ${GIT_TAG_LABEL_NUM}) set(GIT_TAG_LABEL ${GIT_TAG_LABEL} ${GIT_TAG_LABEL_NUM})
string(REPLACE ";" "." GIT_TAG_LABEL "${GIT_TAG_LABEL}") string(REPLACE ";" "." GIT_TAG_LABEL "${GIT_TAG_LABEL}")
else() else()
SET(GIT_TAG_LABEL "") set(GIT_TAG_LABEL "")
endif() endif()
# Override hardcoded version with the informations from the tag # Override hardcoded version with the informations from the tag
set(PROJECT_VERSION_MAJOR ${GIT_TAG_MAJOR} PARENT_SCOPE) set(PROJECT_VERSION_MAJOR
set(PROJECT_VERSION_MINOR ${GIT_TAG_MINOR} PARENT_SCOPE) ${GIT_TAG_MAJOR}
set(PROJECT_VERSION_PATCH ${GIT_TAG_PATCH} PARENT_SCOPE) PARENT_SCOPE
set(PROJECT_VERSION_LABEL ${GIT_TAG_LABEL} PARENT_SCOPE) )
set(PROJECT_VERSION_MINOR
${GIT_TAG_MINOR}
PARENT_SCOPE
)
set(PROJECT_VERSION_PATCH
${GIT_TAG_PATCH}
PARENT_SCOPE
)
set(PROJECT_VERSION_LABEL
${GIT_TAG_LABEL}
PARENT_SCOPE
)
if(${GIT_TAG_TYPE} STREQUAL "Development") if(${GIT_TAG_TYPE} STREQUAL "Development")
set(PROJECT_VERSION_LABEL ${GIT_TAG_LABEL} PARENT_SCOPE) set(PROJECT_VERSION_LABEL
elseif(${GIT_TAG_TYPE} STREQUAL "Release") ${GIT_TAG_LABEL}
set(PROJECT_VERSION_LABEL "" PARENT_SCOPE) PARENT_SCOPE
# set release name from env var )
set(PROJECT_VERSION_RELEASENAME "${GIT_TAG_RELEASENAME}" PARENT_SCOPE) elseif(${GIT_TAG_TYPE} STREQUAL "Release")
endif() set(PROJECT_VERSION_LABEL
""
PARENT_SCOPE
)
# set release name from env var
set(PROJECT_VERSION_RELEASENAME
"${GIT_TAG_RELEASENAME}"
PARENT_SCOPE
)
endif()
endfunction() endfunction()
@@ -163,16 +196,16 @@ set(PROJECT_VERSION_RELEASENAME "")
find_package(Git) find_package(Git)
if(GIT_FOUND) if(GIT_FOUND)
get_commit_id() get_commit_id()
get_commit_date() get_commit_date()
get_tag_name(${GIT_COMMIT_ID}) get_tag_name(${GIT_COMMIT_ID})
else() else()
message( WARNING "Git not found. Build will not contain git revision info." ) message(WARNING "Git not found. Build will not contain git revision info.")
endif() endif()
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
if(PROJECT_VERSION_LABEL) if(PROJECT_VERSION_LABEL)
set(PROJECT_VERSION "${PROJECT_VERSION}-${PROJECT_VERSION_LABEL}") set(PROJECT_VERSION "${PROJECT_VERSION}-${PROJECT_VERSION_LABEL}")
endif() endif()
set(PROJECT_VERSION_FRIENDLY "${PROJECT_VERSION} (${GIT_COMMIT_DATE_FRIENDLY})") set(PROJECT_VERSION_FRIENDLY "${PROJECT_VERSION} (${GIT_COMMIT_DATE_FRIENDLY})")
@@ -180,7 +213,7 @@ set(PROJECT_VERSION_FRIENDLY "${PROJECT_VERSION} (${GIT_COMMIT_DATE_FRIENDLY})")
# Format: <program name>[-ReleaseName]-MAJ.MIN.PATCH[-prerelease_label] # Format: <program name>[-ReleaseName]-MAJ.MIN.PATCH[-prerelease_label]
set(PROJECT_VERSION_FILENAME "${PROJECT_NAME}") set(PROJECT_VERSION_FILENAME "${PROJECT_NAME}")
if(PROJECT_VERSION_RELEASENAME) if(PROJECT_VERSION_RELEASENAME)
set(PROJECT_VERSION_FILENAME "${PROJECT_VERSION_FILENAME}-${PROJECT_VERSION_RELEASENAME}") set(PROJECT_VERSION_FILENAME "${PROJECT_VERSION_FILENAME}-${PROJECT_VERSION_RELEASENAME}")
endif() endif()
set(PROJECT_VERSION_FILENAME "${PROJECT_VERSION_FILENAME}-${PROJECT_VERSION}") set(PROJECT_VERSION_FILENAME "${PROJECT_VERSION_FILENAME}-${PROJECT_VERSION}")

View File

@@ -4,12 +4,12 @@ project(gtest-download LANGUAGES NONE)
include(ExternalProject) include(ExternalProject)
ExternalProject_Add(googletest ExternalProject_Add(googletest
URL https://github.com/google/googletest/archive/release-1.7.0.zip URL https://github.com/google/googletest/archive/release-1.11.0.zip
URL_HASH SHA1=f89bc9f55477df2fde082481e2d709bfafdb057b URL_HASH SHA1=9ffb7b5923f4a8fcdabf2f42c6540cce299f44c0
SOURCE_DIR "${CMAKE_BINARY_DIR}/gtest-src" SOURCE_DIR "${CMAKE_BINARY_DIR}/gtest-src"
BINARY_DIR "${CMAKE_BINARY_DIR}/gtest-build" BINARY_DIR "${CMAKE_BINARY_DIR}/gtest-build"
CONFIGURE_COMMAND "" CONFIGURE_COMMAND ""
BUILD_COMMAND "" BUILD_COMMAND ""
INSTALL_COMMAND "" INSTALL_COMMAND ""
TEST_COMMAND "" TEST_COMMAND ""
) )

View File

@@ -2,284 +2,386 @@
# #
# provides the cockatrice binary # provides the cockatrice binary
PROJECT(Cockatrice VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") project(Cockatrice VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
SET(cockatrice_SOURCES set(cockatrice_SOURCES
src/abstractcarddragitem.cpp
src/abstractcarditem.cpp
src/abstractclient.cpp
src/abstractcounter.cpp src/abstractcounter.cpp
src/abstractgraphicsitem.cpp
src/arrowitem.cpp
src/arrowtarget.cpp
src/carddatabase.cpp
src/carddatabasemodel.cpp
src/carddbparser/carddatabaseparser.cpp
src/carddbparser/cockatricexml3.cpp
src/carddbparser/cockatricexml4.cpp
src/carddragitem.cpp
src/cardfilter.cpp
src/cardframe.cpp
src/cardinfopicture.cpp
src/cardinfotext.cpp
src/cardinfowidget.cpp
src/carditem.cpp
src/cardlist.cpp
src/cardzone.cpp
src/chatview/chatview.cpp
src/counter_general.cpp src/counter_general.cpp
src/dlg_creategame.cpp src/customlineedit.cpp
src/dlg_filter_games.cpp src/deck_loader.cpp
src/decklistmodel.cpp
src/deckstats_interface.cpp
src/deckview.cpp
src/dlg_connect.cpp src/dlg_connect.cpp
src/dlg_create_token.cpp src/dlg_create_token.cpp
src/dlg_creategame.cpp
src/dlg_edit_avatar.cpp src/dlg_edit_avatar.cpp
src/dlg_edit_password.cpp src/dlg_edit_password.cpp
src/dlg_edit_tokens.cpp src/dlg_edit_tokens.cpp
src/dlg_edit_user.cpp src/dlg_edit_user.cpp
src/dlg_filter_games.cpp
src/dlg_forgotpasswordchallenge.cpp
src/dlg_forgotpasswordrequest.cpp src/dlg_forgotpasswordrequest.cpp
src/dlg_forgotpasswordreset.cpp src/dlg_forgotpasswordreset.cpp
src/dlg_forgotpasswordchallenge.cpp
src/dlg_manage_sets.cpp
src/dlg_register.cpp
src/dlg_tip_of_the_day.cpp
src/tip_of_the_day.cpp
src/dlg_update.cpp
src/dlg_viewlog.cpp
src/abstractclient.cpp
src/remoteclient.cpp
src/main.cpp
src/window_main.cpp
src/gamesmodel.cpp
src/player.cpp
src/playertarget.cpp
src/cardzone.cpp
src/selectzone.cpp
src/cardlist.cpp
src/abstractcarditem.cpp
src/carditem.cpp
src/tablezone.cpp
src/handzone.cpp
src/handcounter.cpp
src/carddatabase.cpp
src/keysignals.cpp
src/gameview.cpp
src/gameselector.cpp
src/decklistmodel.cpp
src/deck_loader.cpp
src/dlg_load_deck_from_clipboard.cpp src/dlg_load_deck_from_clipboard.cpp
src/dlg_load_remote_deck.cpp src/dlg_load_remote_deck.cpp
src/cardinfowidget.cpp src/dlg_manage_sets.cpp
src/cardframe.cpp src/dlg_register.cpp
src/cardinfopicture.cpp src/dlg_roll_dice.cpp
src/cardinfotext.cpp
src/filterbuilder.cpp
src/cardfilter.cpp
src/filtertreemodel.cpp
src/filtertree.cpp
src/messagelogwidget.cpp
src/zoneviewzone.cpp
src/zoneviewwidget.cpp
src/pilezone.cpp
src/stackzone.cpp
src/carddragitem.cpp
src/carddatabasemodel.cpp
src/setsmodel.cpp
src/abstractgraphicsitem.cpp
src/abstractcarddragitem.cpp
src/dlg_settings.cpp src/dlg_settings.cpp
src/phasestoolbar.cpp src/dlg_tip_of_the_day.cpp
src/dlg_update.cpp
src/dlg_viewlog.cpp
src/filter_string.cpp
src/filterbuilder.cpp
src/filtertree.cpp
src/filtertreemodel.cpp
src/gamescene.cpp src/gamescene.cpp
src/arrowitem.cpp src/gameselector.cpp
src/arrowtarget.cpp src/gamesmodel.cpp
src/tab.cpp src/gameview.cpp
src/tab_server.cpp src/gettextwithmax.cpp
src/tab_room.cpp src/handcounter.cpp
src/tab_message.cpp src/handle_public_servers.cpp
src/tab_game.cpp src/handzone.cpp
src/tab_deck_storage.cpp src/keysignals.cpp
src/tab_replays.cpp src/lineeditcompleter.cpp
src/tab_supervisor.cpp src/localclient.cpp
src/tab_admin.cpp
src/tab_account.cpp
src/tab_deck_editor.cpp
src/tab_logs.cpp
src/replay_timeline_widget.cpp
src/deckstats_interface.cpp
src/tappedout_interface.cpp
src/chatview/chatview.cpp
src/userlist.cpp
src/userinfobox.cpp
src/user_context_menu.cpp
src/remotedecklist_treewidget.cpp
src/remotereplaylist_treewidget.cpp
src/deckview.cpp
src/playerlistwidget.cpp
src/pixmapgenerator.cpp
src/settingscache.cpp
src/thememanager.cpp
src/localserver.cpp src/localserver.cpp
src/localserverinterface.cpp src/localserverinterface.cpp
src/localclient.cpp src/logger.cpp
src/soundengine.cpp src/main.cpp
src/messagelogwidget.cpp
src/pending_command.cpp src/pending_command.cpp
src/phase.cpp
src/phasestoolbar.cpp
src/pictureloader.cpp src/pictureloader.cpp
src/shortcutssettings.cpp src/pilezone.cpp
src/pixmapgenerator.cpp
src/player.cpp
src/playerlistwidget.cpp
src/playertarget.cpp
src/releasechannel.cpp
src/remoteclient.cpp
src/remotedecklist_treewidget.cpp
src/remotereplaylist_treewidget.cpp
src/replay_timeline_widget.cpp
src/selectzone.cpp
src/sequenceEdit/sequenceedit.cpp src/sequenceEdit/sequenceedit.cpp
src/lineeditcompleter.cpp src/setsmodel.cpp
src/settings/settingsmanager.cpp
src/settings/carddatabasesettings.cpp src/settings/carddatabasesettings.cpp
src/settings/serverssettings.cpp src/settings/downloadsettings.cpp
src/settings/messagesettings.cpp
src/settings/gamefilterssettings.cpp src/settings/gamefilterssettings.cpp
src/settings/layoutssettings.cpp src/settings/layoutssettings.cpp
src/settings/downloadsettings.cpp src/settings/messagesettings.cpp
src/update_downloader.cpp src/settings/serverssettings.cpp
src/logger.cpp src/settings/settingsmanager.cpp
src/releasechannel.cpp src/settingscache.cpp
src/userconnection_information.cpp src/shortcutssettings.cpp
src/soundengine.cpp
src/spoilerbackgroundupdater.cpp src/spoilerbackgroundupdater.cpp
src/handle_public_servers.cpp src/stackzone.cpp
src/carddbparser/carddatabaseparser.cpp src/tab.cpp
src/carddbparser/cockatricexml3.cpp src/tab_account.cpp
src/carddbparser/cockatricexml4.cpp src/tab_admin.cpp
src/filter_string.cpp src/tab_deck_editor.cpp
src/phase.cpp src/tab_deck_storage.cpp
src/customlineedit.cpp src/tab_game.cpp
src/tab_logs.cpp
src/tab_message.cpp
src/tab_replays.cpp
src/tab_room.cpp
src/tab_server.cpp
src/tab_supervisor.cpp
src/tablezone.cpp
src/tappedout_interface.cpp
src/thememanager.cpp
src/tip_of_the_day.cpp
src/translatecountername.cpp src/translatecountername.cpp
src/update_downloader.cpp
src/user_context_menu.cpp
src/userconnection_information.cpp
src/userinfobox.cpp
src/userlist.cpp
src/window_main.cpp
src/zoneviewwidget.cpp
src/zoneviewzone.cpp
${VERSION_STRING_CPP} ${VERSION_STRING_CPP}
) )
add_subdirectory(sounds) add_subdirectory(sounds)
add_subdirectory(themes) add_subdirectory(themes)
set(cockatrice_RESOURCES cockatrice.qrc) set(cockatrice_RESOURCES cockatrice.qrc)
IF(UPDATE_TRANSLATIONS) 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
FILE(GLOB_RECURSE translate_common_SRCS ${CMAKE_SOURCE_DIR}/common/*.cpp ${CMAKE_SOURCE_DIR}/common/*.h) ${CMAKE_SOURCE_DIR}/cockatrice/src/*.h
SET(translate_SRCS ${translate_cockatrice_SRCS} ${translate_common_SRCS}) )
SET(cockatrice_TS "${CMAKE_CURRENT_SOURCE_DIR}/translations/cockatrice_en.ts") file(GLOB_RECURSE translate_common_SRCS ${CMAKE_SOURCE_DIR}/common/*.cpp ${CMAKE_SOURCE_DIR}/common/*.h)
ELSE() set(translate_SRCS ${translate_cockatrice_SRCS} ${translate_common_SRCS})
FILE(GLOB cockatrice_TS "${CMAKE_CURRENT_SOURCE_DIR}/translations/*.ts") set(cockatrice_TS "${CMAKE_CURRENT_SOURCE_DIR}/cockatrice_en@source.ts")
ENDIF(UPDATE_TRANSLATIONS) else()
file(GLOB cockatrice_TS "${CMAKE_CURRENT_SOURCE_DIR}/translations/*.ts")
endif(UPDATE_TRANSLATIONS)
if(WIN32) if(WIN32)
set(cockatrice_SOURCES ${cockatrice_SOURCES} cockatrice.rc) set(cockatrice_SOURCES ${cockatrice_SOURCES} cockatrice.rc)
endif(WIN32) endif(WIN32)
if(APPLE) 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(
set(cockatrice_SOURCES ${cockatrice_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns) ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources
ENDIF(APPLE) )
set(cockatrice_SOURCES ${cockatrice_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns)
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
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() 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
set(DESKTOPDIR share/applications CACHE STRING "desktop file destination") share/icons
CACHE STRING "icon dir"
)
set(DESKTOPDIR
share/applications
CACHE STRING "desktop file destination"
)
# Include directories # Include directories
INCLUDE_DIRECTORIES(../common) include_directories(../common)
INCLUDE_DIRECTORIES(${PROTOBUF_INCLUDE_DIR}) 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_MOC_SRCS} ${cockatrice_QM} ${cockatrice_RESOURCES_RCC}
${cockatrice_SOURCES}
)
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)
set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME}") set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME}")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.cockatrice.${PROJECT_NAME}") set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.cockatrice.${PROJECT_NAME}")
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME}-${PROJECT_VERSION}") set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME}-${PROJECT_VERSION}")
set(MACOSX_BUNDLE_BUNDLE_NAME "${PROJECT_NAME}") set(MACOSX_BUNDLE_BUNDLE_NAME "${PROJECT_NAME}")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION}) set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION})
set(MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}) set(MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION})
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}) endif()
INSTALL(FILES ${cockatrice_QM} DESTINATION share/cockatrice/translations)
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(
FILES_MATCHING DIRECTORY "${QT_PLUGINS_DIR}/"
PATTERN "*.dSYM" EXCLUDE DESTINATION ${plugin_dest_dir}
PATTERN "*_debug.dylib" EXCLUDE COMPONENT Runtime
PATTERN "audio/*.dylib" FILES_MATCHING
PATTERN "iconengines/*.dylib" PATTERN "*.dSYM" EXCLUDE
PATTERN "imageformats/*.dylib" PATTERN "*_debug.dylib" EXCLUDE
PATTERN "platforms/*.dylib" PATTERN "audio/*.dylib"
PATTERN "printsupport/*.dylib" PATTERN "iconengines/*.dylib"
PATTERN "styles/*.dylib" PATTERN "imageformats/*.dylib"
) PATTERN "platforms/*.dylib"
PATTERN "printsupport/*.dylib"
PATTERN "styles/*.dylib"
PATTERN "tls/*.dylib"
)
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]
Plugins = Plugins Plugins = Plugins
Translations = Resources/translations Translations = Resources/translations
Data = Resources\") Data = Resources\")
" COMPONENT Runtime) "
COMPONENT Runtime
)
install(CODE " install(
CODE "
file(GLOB_RECURSE QTPLUGINS file(GLOB_RECURSE QTPLUGINS
\"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dylib\") \"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dylib\")
set(BU_CHMOD_BUNDLE_ITEMS ON) set(BU_CHMOD_BUNDLE_ITEMS ON)
include(BundleUtilities) include(BundleUtilities)
fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/cockatrice.app\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\") fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/cockatrice.app\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\")
" 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 .)
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(
FILES_MATCHING REGEX "(audio|iconengines|imageformats|platforms|printsupport|styles)/.*[^d]\\.dll") DIRECTORY "${QT_PLUGINS_DIR}/"
DESTINATION ${plugin_dest_dir}
COMPONENT Runtime
FILES_MATCHING
PATTERN "audio/qtaudio_wasapi.dll"
PATTERN "audio/qtaudio_windows.dll"
PATTERN "iconengines/qsvgicon.dll"
PATTERN "imageformats/*.dll"
PATTERN "mediaservice/dsengine.dll"
PATTERN "mediaservice/wmfengine.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]
Plugins = Plugins Plugins = Plugins
Translations = Resources/translations Translations = Resources/translations
Data = Resources\") Data = Resources\")
" COMPONENT Runtime) "
COMPONENT Runtime
)
install(CODE " install(
CODE "
file(GLOB_RECURSE QTPLUGINS file(GLOB_RECURSE QTPLUGINS
\"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dll\") \"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dll\")
set(BU_CHMOD_BUNDLE_ITEMS ON) set(BU_CHMOD_BUNDLE_ITEMS ON)
include(BundleUtilities) include(BundleUtilities)
fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/Cockatrice.exe\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\") fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/Cockatrice.exe\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\")
" COMPONENT Runtime) "
COMPONENT Runtime
)
if(WIN32SSLRUNTIME_FOUND) if(OPENSSL_FOUND)
install(FILES ${WIN32SSLRUNTIME_LIBRARIES} DESTINATION ./) install(FILES ${OPENSSL_INCLUDE_DIRS} DESTINATION ./)
endif() endif()
endif()
if(Qt6_FOUND AND 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() endif()

View File

@@ -126,6 +126,7 @@
<file>resources/countries/er.svg</file> <file>resources/countries/er.svg</file>
<file>resources/countries/es.svg</file> <file>resources/countries/es.svg</file>
<file>resources/countries/et.svg</file> <file>resources/countries/et.svg</file>
<file>resources/countries/eu.svg</file>
<file>resources/countries/fi.svg</file> <file>resources/countries/fi.svg</file>
<file>resources/countries/fj.svg</file> <file>resources/countries/fj.svg</file>
<file>resources/countries/fk.svg</file> <file>resources/countries/fk.svg</file>
@@ -301,16 +302,13 @@
<file>resources/countries/vu.svg</file> <file>resources/countries/vu.svg</file>
<file>resources/countries/wf.svg</file> <file>resources/countries/wf.svg</file>
<file>resources/countries/ws.svg</file> <file>resources/countries/ws.svg</file>
<file>resources/countries/xk.svg</file>
<file>resources/countries/ye.svg</file> <file>resources/countries/ye.svg</file>
<file>resources/countries/yt.svg</file> <file>resources/countries/yt.svg</file>
<file>resources/countries/za.svg</file> <file>resources/countries/za.svg</file>
<file>resources/countries/zm.svg</file> <file>resources/countries/zm.svg</file>
<file>resources/countries/zw.svg</file> <file>resources/countries/zw.svg</file>
<file>resources/genders/male.svg</file>
<file>resources/genders/female.svg</file>
<file>resources/genders/unknown.svg</file>
<file>resources/phases/untap.svg</file> <file>resources/phases/untap.svg</file>
<file>resources/phases/upkeep.svg</file> <file>resources/phases/upkeep.svg</file>
<file>resources/phases/draw.svg</file> <file>resources/phases/draw.svg</file>
@@ -353,10 +351,10 @@
<file>resources/tips/images/cockatrice_wiki.png</file> <file>resources/tips/images/cockatrice_wiki.png</file>
<file>resources/tips/images/coin_flip.png</file> <file>resources/tips/images/coin_flip.png</file>
<file>resources/tips/images/counter_expression.png</file> <file>resources/tips/images/counter_expression.png</file>
<file>resources/tips/images/discord.png</file>
<file>resources/tips/images/face_down.png</file> <file>resources/tips/images/face_down.png</file>
<file>resources/tips/images/filter_games.png</file> <file>resources/tips/images/filter_games.png</file>
<file>resources/tips/images/github_logo.png</file> <file>resources/tips/images/github_logo.png</file>
<file>resources/tips/images/gitter.png</file>
<file>resources/tips/images/setpt.png</file> <file>resources/tips/images/setpt.png</file>
<file>resources/tips/images/shortcuts.png</file> <file>resources/tips/images/shortcuts.png</file>
<file>resources/tips/images/themes.png</file> <file>resources/tips/images/themes.png</file>

Binary file not shown.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

@@ -1,33 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.0"
width="75"
height="75"
id="svg34864">
<defs
id="defs34866" />
<g
transform="translate(-348.7552,-478.0905)"
id="layer1">
<g
transform="matrix(1.071197,0,0,1.075147,-13.30677,-36.99488)"
id="g3773">
<path
d="M 176 33 A 11 11 0 1 1 154,33 A 11 11 0 1 1 176 33 z"
transform="matrix(1.540096,0,0,1.5384,118.8893,454.0543)"
style="color:black;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:black;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
id="path3939" />
<path
d="M 373.00525,521.74399 L 373.00525,543.28159"
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:4.61774349;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path3941" />
<path
d="M 363.76467,534.05119 L 382.24582,534.05119"
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:black;stroke-width:4.61774349;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4816" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.0" width="75" height="75" id="svg34864">
<defs id="defs34866"/>
<g transform="translate(-348.755, -478.091)" id="layer1">
<g transform="matrix(1.94812, 0, 0, 1.93731, -342.43, -460.01)" id="g1872">
<path d="M 387.95009,489.60348 L 378.66214,498.89143" style="opacity: 1; color: black; fill: none; fill-opacity: 0.75; fill-rule: evenodd; stroke: black; stroke-width: 3; stroke-linecap: butt; stroke-linejoin: miter; marker: none; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 0pt; stroke-opacity: 1; visibility: visible; display: inline; overflow: visible;" id="path26867"/>
<path d="M 49.396475 36.70454 A 15.623922 16.319134 0 1 1 18.14863,36.70454 A 15.623922 16.319134 0 1 1 49.396475 36.70454 z" transform="matrix(0.48802, 0.48802, -0.467594, 0.467594, 371.609, 473.136)" style="opacity: 1; color: black; fill: none; fill-opacity: 1; fill-rule: evenodd; stroke: black; stroke-width: 4.44072; stroke-linecap: butt; stroke-linejoin: miter; marker: none; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 0pt; stroke-opacity: 1; visibility: visible; display: inline; overflow: visible;" id="path26871"/>
<path d="M 379.92823,489.70212 C 387.842,489.70212 387.842,489.70212 387.842,489.70212 L 387.842,497.61589" style="fill: none; fill-rule: evenodd; stroke: black; stroke-width: 3; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-dasharray: none; stroke-opacity: 1;" id="path27759"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Intersexual/Transgendered symbol by Gregory Maxwell. Copyright 2005. GFDL-1.2 only -->
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="210"
height="280">
<path d="M 188,38 v 20 l 14,-8 v -42 h -42 l -8,14 h 20 l -37,37 a 79,79 0 1,0 -59,141 v 22 h -27 v 23 h 27 v 27 h 23 v -27 h 27 v -23 h -27 v -22 a 79,79 0 0,0 52,-125 zm -100,27 a 57,57 0 1,1 0,114 a 57,57 0 1,1 0,-114 z"/>
<!-- 88,65 // -63,-10 -->
</svg>

Before

Width:  |  Height:  |  Size: 500 B

View File

@@ -1,6 +1,7 @@
## Syntax Help ## Search Syntax Help
----- -----
The search bar recognizes a set of special commands similar to some other card databases. Here is a list with examples. Each entry can be clicked to test the query and has a small explanation. Note that all searches are case insensitive. The search bar recognizes a set of special commands similar to some other card databases.<br>
In this list of examples below, each entry has an explanation and can be clicked to test the query. Note that all searches are case insensitive.
<dl> <dl>
<dt>Name:</dt> <dt>Name:</dt>
<dd>[birds of paradise](#birds of paradise) <small>(Any card name containing the words birds, of, and paradise)</small></dd> <dd>[birds of paradise](#birds of paradise) <small>(Any card name containing the words birds, of, and paradise)</small></dd>
@@ -30,10 +31,10 @@ The search bar recognizes a set of special commands similar to some other card d
<dd>[c:c](#c:c) <small>(Any colorless card)</small></dd> <dd>[c:c](#c:c) <small>(Any colorless card)</small></dd>
<dd>[ci:w](#ci:w) <small>(Any card that has white in it's color identity)</small></dd> <dd>[ci:w](#ci:w) <small>(Any card that has white in it's color identity)</small></dd>
<dt><u>Pow</u>er, <u>Tou</u>ghness, <u>C</u>onverted <u>M</u>ana <u>C</u>ost:</dt> <dt><u>Pow</u>er, <u>Tou</u>ghness, <u>M</u>ana <u>V</u>alue:</dt>
<dd>[tou:1](#tou:1) <small>(Any card with a toughness of 1)</small></dd> <dd>[tou:1](#tou:1) <small>(Any card with a toughness of 1)</small></dd>
<dd>[pow>=8](#pow>=8) <small>(Any card with a power greater than or equal to 8)</small></dd> <dd>[pow>=8](#pow>=8) <small>(Any card with a power greater than or equal to 8)</small></dd>
<dd>[cmc=7](#cmc=7) <small>(Any card with a converted mana cost equal to 7)</small></dd> <dd>[mv=7](#mv=7) <small>(Any card with a mana value equal to 7)</small></dd>
<dt><u>R</u>arity:</dt> <dt><u>R</u>arity:</dt>
<dd>[r:mythic](#r:mythic) <small>(Any card that has the mythic-rare rarity)</small></dd> <dd>[r:mythic](#r:mythic) <small>(Any card that has the mythic-rare rarity)</small></dd>
@@ -46,8 +47,7 @@ The search bar recognizes a set of special commands similar to some other card d
<dt><u>E</u>dition:</dt> <dt><u>E</u>dition:</dt>
<dd>[set:lea](#set:lea) <small>(Cards that appear in Alpha, which has the set code LEA)</small></dd> <dd>[set:lea](#set:lea) <small>(Cards that appear in Alpha, which has the set code LEA)</small></dd>
<dd>[e:lea,leb](#e:lea,leb) <small>(Cards that appear in Alpha or Beta)</small></dd> <dd>[e:lea or e:leb](#e:lea or e:leb) <small>(Cards that appear in Alpha or Beta)</small></dd>
<dd><a href="#e:lea,leb -(e:lea e:leb)">e:lea,leb -(e:lea e:leb)</a> <small>(Cards that appear in Alpha or Beta but not in both editions)</small></dd>
<dt>Negate:</dt> <dt>Negate:</dt>
<dd>[c:wu -c:m](#c:wu -c:m) <small>(Any card that is white or blue, but not multicolored)</small></dd> <dd>[c:wu -c:m](#c:wu -c:m) <small>(Any card that is white or blue, but not multicolored)</small></dd>

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

View File

@@ -7,9 +7,9 @@
</tip> </tip>
<tip> <tip>
<title>Suggesting New Tips</title> <title>Suggesting New Tips</title>
<text>You can suggest new Tips of the Day by reaching out to the development team on &lt;a href="https://gitter.im/cockatrice/cockatrice"&gt;Gitter&lt;/a&gt;!</text> <text>You can suggest new Tips of the Day by reaching out to the development team on &lt;a href="https://discord.gg/3Z9yzmA"&gt;Discord&lt;/a&gt;!</text>
<image>gitter.png</image> <image>discord.png</image>
<date>2018-03-01</date> <date>2023-10-18</date>
</tip> </tip>
<tip> <tip>
<title>Reporting Bugs</title> <title>Reporting Bugs</title>

View File

@@ -2,18 +2,15 @@
# #
# add sounds subfolders # add sounds subfolders
SET(defsounds set(defsounds Default Legacy)
Default
Legacy
)
if(UNIX) if(UNIX)
if(APPLE) if(APPLE)
INSTALL(DIRECTORY ${defsounds} DESTINATION Cockatrice.app/Contents/Resources/sounds/) install(DIRECTORY ${defsounds} DESTINATION Cockatrice.app/Contents/Resources/sounds/)
else() else()
# Assume linux # Assume linux
INSTALL(DIRECTORY ${defsounds} DESTINATION share/cockatrice/sounds/) install(DIRECTORY ${defsounds} DESTINATION share/cockatrice/sounds/)
endif() endif()
elseif(WIN32) elseif(WIN32)
INSTALL(DIRECTORY ${defsounds} DESTINATION sounds/) install(DIRECTORY ${defsounds} DESTINATION sounds/)
endif() endif()

View File

@@ -20,13 +20,8 @@ AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item,
parentDrag->addChildDrag(this); parentDrag->addChildDrag(this);
setZValue(2000000007 + hotSpot.x() * 1000000 + hotSpot.y() * 1000 + 1000); setZValue(2000000007 + hotSpot.x() * 1000000 + hotSpot.y() * 1000 + 1000);
} else { } else {
if ((hotSpot.x() < 0) || (hotSpot.y() < 0)) { hotSpot = QPointF{qBound(0.0, hotSpot.x(), static_cast<qreal>(CARD_WIDTH - 1)),
qDebug() << "CardDragItem: coordinate overflow: x =" << hotSpot.x() << ", y =" << hotSpot.y(); qBound(0.0, hotSpot.y(), static_cast<qreal>(CARD_HEIGHT - 1))};
hotSpot = QPointF();
} else if ((hotSpot.x() > CARD_WIDTH) || (hotSpot.y() > CARD_HEIGHT)) {
qDebug() << "CardDragItem: coordinate overflow: x =" << hotSpot.x() << ", y =" << hotSpot.y();
hotSpot = QPointF(CARD_WIDTH, CARD_HEIGHT);
}
setCursor(Qt::ClosedHandCursor); setCursor(Qt::ClosedHandCursor);
setZValue(2000000007); setZValue(2000000007);
} }
@@ -45,12 +40,19 @@ AbstractCardDragItem::~AbstractCardDragItem()
delete childDrags[i]; delete childDrags[i];
} }
QPainterPath AbstractCardDragItem::shape() const
{
QPainterPath shape;
shape.addRoundedRect(boundingRect(), 0.05 * CARD_WIDTH, 0.05 * CARD_WIDTH);
return shape;
}
void AbstractCardDragItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) void AbstractCardDragItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{ {
item->paint(painter, option, widget); item->paint(painter, option, widget);
// adds a mask to the card so it looks like the card hasnt been placed yet // adds a mask to the card so it looks like the card hasnt been placed yet
painter->fillRect(boundingRect(), GHOST_MASK); painter->fillPath(shape(), GHOST_MASK);
} }
void AbstractCardDragItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) void AbstractCardDragItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)

View File

@@ -21,17 +21,18 @@ public:
{ {
Type = typeCardDrag Type = typeCardDrag
}; };
int type() const int type() const override
{ {
return Type; return Type;
} }
AbstractCardDragItem(AbstractCardItem *_item, const QPointF &_hotSpot, AbstractCardDragItem *parentDrag = 0); AbstractCardDragItem(AbstractCardItem *_item, const QPointF &_hotSpot, AbstractCardDragItem *parentDrag = 0);
~AbstractCardDragItem(); ~AbstractCardDragItem();
QRectF boundingRect() const QRectF boundingRect() const override
{ {
return QRectF(0, 0, CARD_WIDTH, CARD_HEIGHT); return QRectF(0, 0, CARD_WIDTH, CARD_HEIGHT);
} }
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); QPainterPath shape() const override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
AbstractCardItem *getItem() const AbstractCardItem *getItem() const
{ {
return item; return item;
@@ -44,7 +45,7 @@ public:
virtual void updatePosition(const QPointF &cursorScenePos) = 0; virtual void updatePosition(const QPointF &cursorScenePos) = 0;
protected: protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event); void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
}; };
#endif #endif

View File

@@ -1,19 +1,17 @@
#include <QCursor>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include <algorithm>
#include <cmath>
#ifdef _WIN32
#include "round.h"
#endif /* _WIN32 */
#include "abstractcarditem.h" #include "abstractcarditem.h"
#include "carddatabase.h" #include "carddatabase.h"
#include "gamescene.h" #include "gamescene.h"
#include "main.h" #include "main.h"
#include "pictureloader.h" #include "pictureloader.h"
#include "settingscache.h" #include "settingscache.h"
#include <QCursor>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include <algorithm>
AbstractCardItem::AbstractCardItem(const QString &_name, Player *_owner, int _id, QGraphicsItem *parent) AbstractCardItem::AbstractCardItem(const QString &_name, Player *_owner, int _id, QGraphicsItem *parent)
: ArrowTarget(_owner, parent), id(_id), name(_name), tapped(false), facedown(false), tapAngle(0), : ArrowTarget(_owner, parent), id(_id), name(_name), tapped(false), facedown(false), tapAngle(0),
bgColor(Qt::transparent), isHovered(false), realZValue(0) bgColor(Qt::transparent), isHovered(false), realZValue(0)
@@ -36,6 +34,13 @@ QRectF AbstractCardItem::boundingRect() const
return QRectF(0, 0, CARD_WIDTH, CARD_HEIGHT); return QRectF(0, 0, CARD_WIDTH, CARD_HEIGHT);
} }
QPainterPath AbstractCardItem::shape() const
{
QPainterPath shape;
shape.addRoundedRect(boundingRect(), 0.05 * CARD_WIDTH, 0.05 * CARD_WIDTH);
return shape;
}
void AbstractCardItem::pixmapUpdated() void AbstractCardItem::pixmapUpdated()
{ {
update(); update();
@@ -115,24 +120,14 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS
if (paintImage) { if (paintImage) {
painter->save(); painter->save();
transformPainter(painter, translatedSize, angle); painter->setClipPath(shape());
painter->drawPixmap(QPointF(1, 1), translatedPixmap); painter->drawPixmap(boundingRect(), translatedPixmap, QRectF({0, 0}, translatedPixmap.size()));
painter->restore(); painter->restore();
} else { } else {
painter->setBrush(bgColor); painter->setBrush(bgColor);
painter->drawPath(shape());
} }
QPen pen(Qt::black);
pen.setJoinStyle(Qt::MiterJoin);
const int penWidth = 2;
pen.setWidth(penWidth);
painter->setPen(pen);
if (!angle)
painter->drawRect(QRectF(0, 0, CARD_WIDTH - 1, CARD_HEIGHT - penWidth));
else
painter->drawRect(QRectF(1, 1, CARD_WIDTH - 2, CARD_HEIGHT - 1.5));
if (translatedPixmap.isNull() || SettingsCache::instance().getDisplayCardNames() || facedown) { if (translatedPixmap.isNull() || SettingsCache::instance().getDisplayCardNames() || facedown) {
painter->save(); painter->save();
transformPainter(painter, translatedSize, angle); transformPainter(painter, translatedSize, angle);
@@ -160,9 +155,7 @@ void AbstractCardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *
QSizeF translatedSize = getTranslatedSize(painter); QSizeF translatedSize = getTranslatedSize(painter);
paintPicture(painter, translatedSize, tapAngle); paintPicture(painter, translatedSize, tapAngle);
painter->save();
painter->setRenderHint(QPainter::Antialiasing, false); painter->setRenderHint(QPainter::Antialiasing, false);
transformPainter(painter, translatedSize, tapAngle);
if (isSelected() || isHovered) { if (isSelected() || isHovered) {
QPen pen; QPen pen;
@@ -170,15 +163,12 @@ void AbstractCardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *
pen.setColor(Qt::yellow); pen.setColor(Qt::yellow);
if (isSelected()) if (isSelected())
pen.setColor(Qt::red); pen.setColor(Qt::red);
const int penWidth = 1; pen.setWidth(0); // Cosmetic pen
pen.setWidth(penWidth);
painter->setPen(pen); painter->setPen(pen);
painter->drawRect(QRectF(0, 0, translatedSize.width() + penWidth, translatedSize.height() - penWidth)); painter->drawPath(shape());
} }
painter->restore(); painter->restore();
painter->restore();
} }
void AbstractCardItem::setName(const QString &_name) void AbstractCardItem::setName(const QString &_name)
@@ -272,7 +262,6 @@ void AbstractCardItem::setFaceDown(bool _facedown)
{ {
facedown = _facedown; facedown = _facedown;
update(); update();
emit updateCardMenu(this);
} }
void AbstractCardItem::mousePressEvent(QGraphicsSceneMouseEvent *event) void AbstractCardItem::mousePressEvent(QGraphicsSceneMouseEvent *event)

View File

@@ -36,7 +36,6 @@ signals:
void hovered(AbstractCardItem *card); void hovered(AbstractCardItem *card);
void showCardInfoPopup(QPoint pos, QString cardName); void showCardInfoPopup(QPoint pos, QString cardName);
void deleteCardInfoPopup(QString cardName); void deleteCardInfoPopup(QString cardName);
void updateCardMenu(AbstractCardItem *card);
void sigPixmapUpdated(); void sigPixmapUpdated();
void cardShiftClicked(QString cardName); void cardShiftClicked(QString cardName);
@@ -45,7 +44,7 @@ public:
{ {
Type = typeCard Type = typeCard
}; };
int type() const int type() const override
{ {
return Type; return Type;
} }
@@ -54,10 +53,11 @@ public:
int _id = -1, int _id = -1,
QGraphicsItem *parent = nullptr); QGraphicsItem *parent = nullptr);
~AbstractCardItem(); ~AbstractCardItem();
QRectF boundingRect() const; QRectF boundingRect() const override;
QPainterPath shape() const override;
QSizeF getTranslatedSize(QPainter *painter) const; QSizeF getTranslatedSize(QPainter *painter) const;
void paintPicture(QPainter *painter, const QSizeF &translatedSize, int angle); void paintPicture(QPainter *painter, const QSizeF &translatedSize, int angle);
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
CardInfoPtr getInfo() const CardInfoPtr getInfo() const
{ {
return info; return info;
@@ -104,9 +104,9 @@ public:
protected: protected:
void transformPainter(QPainter *painter, const QSizeF &translatedSize, int angle); void transformPainter(QPainter *painter, const QSizeF &translatedSize, int angle);
void mousePressEvent(QGraphicsSceneMouseEvent *event); void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value); QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;
void cacheBgColor(); void cacheBgColor();
}; };

View File

@@ -1,6 +1,5 @@
#include "abstractclient.h" #include "abstractclient.h"
#include "client_metatypes.h"
#include "featureset.h" #include "featureset.h"
#include "get_pb_extension.h" #include "get_pb_extension.h"
#include "pb/commands.pb.h" #include "pb/commands.pb.h"
@@ -22,7 +21,8 @@
#include <google/protobuf/descriptor.h> #include <google/protobuf/descriptor.h>
AbstractClient::AbstractClient(QObject *parent) : QObject(parent), nextCmdId(0), status(StatusDisconnected) AbstractClient::AbstractClient(QObject *parent)
: QObject(parent), nextCmdId(0), status(StatusDisconnected), serverSupportsPasswordHash(false)
{ {
qRegisterMetaType<QVariant>("QVariant"); qRegisterMetaType<QVariant>("QVariant");
qRegisterMetaType<CommandContainer>("CommandContainer"); qRegisterMetaType<CommandContainer>("CommandContainer");
@@ -47,6 +47,7 @@ AbstractClient::AbstractClient(QObject *parent) : QObject(parent), nextCmdId(0),
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

@@ -40,6 +40,7 @@ enum ClientStatus
StatusRequestingForgotPassword, StatusRequestingForgotPassword,
StatusSubmitForgotPasswordReset, StatusSubmitForgotPasswordReset,
StatusSubmitForgotPasswordChallenge, StatusSubmitForgotPasswordChallenge,
StatusGettingPasswordSalt,
}; };
class AbstractClient : public QObject class AbstractClient : public QObject
@@ -87,7 +88,7 @@ protected slots:
protected: protected:
QMap<int, PendingCommand *> pendingCommands; QMap<int, PendingCommand *> pendingCommands;
QString userName, password, email, country, realName, token; QString userName, password, email, country, realName, token;
int gender; bool serverSupportsPasswordHash;
void setStatus(ClientStatus _status); void setStatus(ClientStatus _status);
int getNewCmdId() int getNewCmdId()
{ {
@@ -107,7 +108,11 @@ public:
void sendCommand(const CommandContainer &cont); void sendCommand(const CommandContainer &cont);
void sendCommand(PendingCommand *pend); void sendCommand(PendingCommand *pend);
const QString getUserName() bool getServerSupportsPasswordHash() const
{
return serverSupportsPasswordHash;
}
const QString &getUserName() const
{ {
return userName; return userName;
} }

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);
@@ -138,8 +138,8 @@ void ArrowItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
} }
QList<QGraphicsItem *> colliding = scene()->items(event->scenePos()); QList<QGraphicsItem *> colliding = scene()->items(event->scenePos());
for (int i = 0; i < colliding.size(); ++i) { for (QGraphicsItem *item : colliding) {
if (qgraphicsitem_cast<CardItem *>(colliding[i])) { if (qgraphicsitem_cast<CardItem *>(item)) {
event->ignore(); event->ignore();
return; return;
} }
@@ -205,8 +205,8 @@ void ArrowDragItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
} }
update(); update();
for (int i = 0; i < childArrows.size(); ++i) { for (ArrowDragItem *child : childArrows) {
childArrows[i]->mouseMoveEvent(event); child->mouseMoveEvent(event);
} }
} }
@@ -251,8 +251,9 @@ void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
} }
delArrow(); delArrow();
for (int i = 0; i < childArrows.size(); ++i) for (ArrowDragItem *child : childArrows) {
childArrows[i]->mouseReleaseEvent(event); child->mouseReleaseEvent(event);
}
} }
ArrowAttachItem::ArrowAttachItem(ArrowTarget *_startItem) ArrowAttachItem::ArrowAttachItem(ArrowTarget *_startItem)
@@ -260,6 +261,11 @@ ArrowAttachItem::ArrowAttachItem(ArrowTarget *_startItem)
{ {
} }
void ArrowAttachItem::addChildArrow(ArrowAttachItem *childArrow)
{
childArrows.append(childArrow);
}
void ArrowAttachItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) void ArrowAttachItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{ {
if (!startItem) if (!startItem)
@@ -295,9 +301,13 @@ void ArrowAttachItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
updatePath(); updatePath();
} }
update(); update();
for (ArrowAttachItem *child : childArrows) {
child->mouseMoveEvent(event);
}
} }
void ArrowAttachItem::mouseReleaseEvent(QGraphicsSceneMouseEvent * /*event*/) void ArrowAttachItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{ {
if (!startItem) if (!startItem)
return; return;
@@ -319,4 +329,8 @@ void ArrowAttachItem::mouseReleaseEvent(QGraphicsSceneMouseEvent * /*event*/)
} }
delArrow(); delArrow();
for (ArrowAttachItem *child : childArrows) {
child->mouseReleaseEvent(event);
}
} }

View File

@@ -85,8 +85,12 @@ protected:
class ArrowAttachItem : public ArrowItem class ArrowAttachItem : public ArrowItem
{ {
Q_OBJECT Q_OBJECT
private:
QList<ArrowAttachItem *> childArrows;
public: public:
ArrowAttachItem(ArrowTarget *_startItem); ArrowAttachItem(ArrowTarget *_startItem);
void addChildArrow(ArrowAttachItem *childArrow);
protected: protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event); void mouseMoveEvent(QGraphicsSceneMouseEvent *event);

View File

@@ -43,9 +43,8 @@ public:
} }
void removeArrowFrom(ArrowItem *arrow) void removeArrowFrom(ArrowItem *arrow)
{ {
arrowsFrom.removeAt(arrowsFrom.indexOf(arrow)); arrowsFrom.removeOne(arrow);
} }
const QList<ArrowItem *> &getArrowsTo() const const QList<ArrowItem *> &getArrowsTo() const
{ {
return arrowsTo; return arrowsTo;
@@ -56,8 +55,7 @@ public:
} }
void removeArrowTo(ArrowItem *arrow) void removeArrowTo(ArrowItem *arrow)
{ {
arrowsTo.removeAt(arrowsTo.indexOf(arrow)); arrowsTo.removeOne(arrow);
} }
}; };
#endif #endif

View File

@@ -266,9 +266,14 @@ CardInfoPtr CardInfo::newInstance(const QString &_name,
QString CardInfo::getCorrectedName() const QString CardInfo::getCorrectedName() const
{ {
// remove all the characters reserved in windows file paths,
// other oses only disallow a subset of these so it covers all
static const QRegularExpression rmrx(R"(( // |[*<>:"\\?\x00-\x08\x10-\x1f]))");
static const QRegularExpression spacerx(R"([/\x09-\x0f])");
static const QString space(' ');
QString result = name; QString result = name;
// Fire // Ice, Circle of Protection: Red, "Ach! Hans, Run!", Who/What/When/Where/Why, Question Elemental? // Fire // Ice, Circle of Protection: Red, "Ach! Hans, Run!", Who/What/When/Where/Why, Question Elemental?
return result.remove(" // ").remove(':').remove('"').remove('?').replace('/', ' '); return result.remove(rmrx).replace(spacerx, space);
} }
void CardInfo::addToSet(const CardSetPtr &_set, const CardInfoPerSet _info) void CardInfo::addToSet(const CardSetPtr &_set, const CardInfoPerSet _info)
@@ -587,9 +592,9 @@ void CardDatabase::refreshCachedReverseRelatedCards()
continue; continue;
} }
auto *newCardRelation = new CardRelation(card->getName(), cardRelation->getDoesAttach(), auto *newCardRelation = new CardRelation(
cardRelation->getIsCreateAllExclusion(), card->getName(), cardRelation->getAttachType(), cardRelation->getIsCreateAllExclusion(),
cardRelation->getIsVariable(), cardRelation->getDefaultCount()); cardRelation->getIsVariable(), cardRelation->getDefaultCount(), cardRelation->getIsPersistent());
cards.value(targetCard)->addReverseRelatedCards2Me(newCardRelation); cards.value(targetCard)->addReverseRelatedCards2Me(newCardRelation);
} }
} }
@@ -607,22 +612,22 @@ QStringList CardDatabase::getAllMainCardTypes() const
void CardDatabase::checkUnknownSets() void CardDatabase::checkUnknownSets()
{ {
SetList sets = getSetList(); auto _sets = getSetList();
if (sets.getEnabledSetsNum()) { if (_sets.getEnabledSetsNum()) {
// if some sets are first found on this run, ask the user // if some sets are first found on this run, ask the user
int numUnknownSets = sets.getUnknownSetsNum(); int numUnknownSets = _sets.getUnknownSetsNum();
QStringList unknownSetNames = sets.getUnknownSetsNames(); QStringList unknownSetNames = _sets.getUnknownSetsNames();
if (numUnknownSets > 0) { if (numUnknownSets > 0) {
emit cardDatabaseNewSetsFound(numUnknownSets, unknownSetNames); emit cardDatabaseNewSetsFound(numUnknownSets, unknownSetNames);
} else { } else {
sets.markAllAsKnown(); _sets.markAllAsKnown();
} }
} else { } else {
// No set enabled. Probably this is the first time running trice // No set enabled. Probably this is the first time running trice
sets.guessSortKeys(); _sets.guessSortKeys();
sets.sortByKey(); _sets.sortByKey();
sets.enableAll(); _sets.enableAll();
notifyEnabledSetsChanged(); notifyEnabledSetsChanged();
emit cardDatabaseAllNewSetsEnabled(); emit cardDatabaseAllNewSetsEnabled();
@@ -631,14 +636,14 @@ void CardDatabase::checkUnknownSets()
void CardDatabase::enableAllUnknownSets() void CardDatabase::enableAllUnknownSets()
{ {
SetList sets = getSetList(); auto _sets = getSetList();
sets.enableAllUnknown(); _sets.enableAllUnknown();
} }
void CardDatabase::markAllSetsAsKnown() void CardDatabase::markAllSetsAsKnown()
{ {
SetList sets = getSetList(); auto _sets = getSetList();
sets.markAllAsKnown(); _sets.markAllAsKnown();
} }
void CardDatabase::notifyEnabledSetsChanged() void CardDatabase::notifyEnabledSetsChanged()
@@ -672,12 +677,13 @@ bool CardDatabase::saveCustomTokensToFile()
} }
CardRelation::CardRelation(const QString &_name, CardRelation::CardRelation(const QString &_name,
bool _doesAttach, AttachType _attachType,
bool _isCreateAllExclusion, bool _isCreateAllExclusion,
bool _isVariableCount, bool _isVariableCount,
int _defaultCount) int _defaultCount,
: name(_name), doesAttach(_doesAttach), isCreateAllExclusion(_isCreateAllExclusion), bool _isPersistent)
isVariableCount(_isVariableCount), defaultCount(_defaultCount) : name(_name), attachType(_attachType), isCreateAllExclusion(_isCreateAllExclusion),
isVariableCount(_isVariableCount), defaultCount(_defaultCount), isPersistent(_isPersistent)
{ {
} }

View File

@@ -452,31 +452,60 @@ signals:
class CardRelation : public QObject class CardRelation : public QObject
{ {
Q_OBJECT Q_OBJECT
public:
enum AttachType
{
DoesNotAttach = 0,
AttachTo = 1,
TransformInto = 2,
};
private: private:
QString name; QString name;
bool doesAttach; AttachType attachType;
bool isCreateAllExclusion; bool isCreateAllExclusion;
bool isVariableCount; bool isVariableCount;
int defaultCount; int defaultCount;
bool isPersistent;
public: public:
explicit CardRelation(const QString &_name = QString(), explicit CardRelation(const QString &_name = QString(),
bool _doesAttach = false, AttachType _attachType = DoesNotAttach,
bool _isCreateAllExclusion = false, bool _isCreateAllExclusion = false,
bool _isVariableCount = false, bool _isVariableCount = false,
int _defaultCount = 1); int _defaultCount = 1,
bool _isPersistent = false);
inline const QString &getName() const inline const QString &getName() const
{ {
return name; return name;
} }
AttachType getAttachType() const
{
return attachType;
}
bool getDoesAttach() const bool getDoesAttach() const
{ {
return doesAttach; return attachType != DoesNotAttach;
}
bool getDoesTransform() const
{
return attachType == TransformInto;
}
QString getAttachTypeAsString() const
{
switch (attachType) {
case AttachTo:
return "attach";
case TransformInto:
return "transform";
default:
return "";
}
} }
bool getCanCreateAnother() const bool getCanCreateAnother() const
{ {
return !doesAttach; return !getDoesAttach();
} }
bool getIsCreateAllExclusion() const bool getIsCreateAllExclusion() const
{ {
@@ -490,5 +519,9 @@ public:
{ {
return defaultCount; return defaultCount;
} }
bool getIsPersistent() const
{
return isPersistent;
}
}; };
#endif #endif

View File

@@ -326,12 +326,12 @@ void CardDatabaseDisplayModel::clearFilterAll()
invalidateFilter(); invalidateFilter();
} }
void CardDatabaseDisplayModel::setFilterTree(FilterTree *filterTree) void CardDatabaseDisplayModel::setFilterTree(FilterTree *_filterTree)
{ {
if (this->filterTree != nullptr) if (this->filterTree != nullptr)
disconnect(this->filterTree, nullptr, this, nullptr); disconnect(this->filterTree, nullptr, this, nullptr);
this->filterTree = filterTree; this->filterTree = _filterTree;
connect(this->filterTree, SIGNAL(changed()), this, SLOT(filterTreeChanged())); connect(this->filterTree, SIGNAL(changed()), this, SLOT(filterTreeChanged()));
invalidate(); invalidate();
} }

View File

@@ -82,7 +82,7 @@ private:
public: public:
explicit CardDatabaseDisplayModel(QObject *parent = nullptr); explicit CardDatabaseDisplayModel(QObject *parent = nullptr);
void setFilterTree(FilterTree *filterTree); void setFilterTree(FilterTree *_filterTree);
void setIsToken(FilterBool _isToken) void setIsToken(FilterBool _isToken)
{ {
isToken = _isToken; isToken = _isToken;

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,13 +148,14 @@ 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();
QString colors = QString(""); QString colors = QString("");
QList<CardRelation *> relatedCards, reverseRelatedCards; QList<CardRelation *> relatedCards, reverseRelatedCards;
CardInfoPerSetMap sets = CardInfoPerSetMap(); auto _sets = CardInfoPerSetMap();
int tableRow = 0; int tableRow = 0;
bool cipt = false; bool cipt = false;
bool isToken = false; bool isToken = false;
@@ -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);
@@ -216,10 +221,10 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml)
if (attrs.hasAttribute("rarity")) { if (attrs.hasAttribute("rarity")) {
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; CardRelation::AttachType attach = CardRelation::DoesNotAttach;
bool exclude = false; bool exclude = false;
bool variable = false; bool variable = false;
int count = 1; int count = 1;
@@ -241,7 +246,7 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml)
} }
if (attrs.hasAttribute("attach")) { if (attrs.hasAttribute("attach")) {
attach = true; attach = CardRelation::AttachTo;
} }
if (attrs.hasAttribute("exclude")) { if (attrs.hasAttribute("exclude")) {
@@ -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();
} }
@@ -263,7 +268,7 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml)
properties.insert("colors", colors); properties.insert("colors", colors);
CardInfoPtr newCard = CardInfo::newInstance(name, text, isToken, properties, relatedCards, CardInfoPtr newCard = CardInfo::newInstance(name, text, isToken, properties, relatedCards,
reverseRelatedCards, sets, cipt, tableRow, upsideDown); reverseRelatedCards, _sets, cipt, tableRow, upsideDown);
emit addCard(newCard); emit addCard(newCard);
} }
} }
@@ -407,7 +412,7 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in
return xml; return xml;
} }
bool CockatriceXml3Parser::saveToFile(SetNameMap sets, bool CockatriceXml3Parser::saveToFile(SetNameMap _sets,
CardNameMap cards, CardNameMap cards,
const QString &fileName, const QString &fileName,
const QString &sourceUrl, const QString &sourceUrl,
@@ -434,9 +439,9 @@ bool CockatriceXml3Parser::saveToFile(SetNameMap sets,
xml.writeTextElement("sourceVersion", sourceVersion); xml.writeTextElement("sourceVersion", sourceVersion);
xml.writeEndElement(); xml.writeEndElement();
if (sets.count() > 0) { if (_sets.count() > 0) {
xml.writeStartElement("sets"); xml.writeStartElement("sets");
for (CardSetPtr set : sets) { for (CardSetPtr set : _sets) {
xml << set; xml << set;
} }
xml.writeEndElement(); xml.writeEndElement();
@@ -454,4 +459,4 @@ bool CockatriceXml3Parser::saveToFile(SetNameMap sets,
xml.writeEndDocument(); xml.writeEndDocument();
return true; return true;
} }

View File

@@ -14,7 +14,7 @@ public:
~CockatriceXml3Parser() override = default; ~CockatriceXml3Parser() override = default;
bool getCanParseFile(const QString &name, QIODevice &device) override; bool getCanParseFile(const QString &name, QIODevice &device) override;
void parseFile(QIODevice &device) override; void parseFile(QIODevice &device) override;
bool saveToFile(SetNameMap sets, bool saveToFile(SetNameMap _sets,
CardNameMap cards, CardNameMap cards,
const QString &fileName, const QString &fileName,
const QString &sourceUrl = "unknown", const QString &sourceUrl = "unknown",

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,12 +127,14 @@ 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();
QList<CardRelation *> relatedCards, reverseRelatedCards; QList<CardRelation *> relatedCards, reverseRelatedCards;
CardInfoPerSetMap sets = CardInfoPerSetMap(); auto _sets = CardInfoPerSetMap();
int tableRow = 0; int tableRow = 0;
bool cipt = false; bool cipt = false;
bool isToken = false; bool isToken = false;
@@ -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);
@@ -169,13 +178,14 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml)
attrName = "picurl"; attrName = "picurl";
setInfo.setProperty(attrName, attr.value().toString()); setInfo.setProperty(attrName, attr.value().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; CardRelation::AttachType attachType = CardRelation::DoesNotAttach;
bool exclude = false; bool exclude = false;
bool variable = false; bool variable = false;
bool persistent = false;
int count = 1; int count = 1;
QXmlStreamAttributes attrs = xml.attributes(); QXmlStreamAttributes attrs = xml.attributes();
QString cardName = xml.readElementText(QXmlStreamReader::IncludeChildElements); QString cardName = xml.readElementText(QXmlStreamReader::IncludeChildElements);
@@ -195,28 +205,33 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml)
} }
if (attrs.hasAttribute("attach")) { if (attrs.hasAttribute("attach")) {
attach = true; attachType = attrs.value("attach").toString() == "transform" ? CardRelation::TransformInto
: CardRelation::AttachTo;
} }
if (attrs.hasAttribute("exclude")) { if (attrs.hasAttribute("exclude")) {
exclude = true; exclude = true;
} }
auto *relation = new CardRelation(cardName, attach, exclude, variable, count); if (attrs.hasAttribute("persistent")) {
if (xml.name() == "reverse-related") { persistent = true;
}
auto *relation = new CardRelation(cardName, attachType, exclude, variable, count, persistent);
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();
} }
} }
CardInfoPtr newCard = CardInfo::newInstance(name, text, isToken, properties, relatedCards, CardInfoPtr newCard = CardInfo::newInstance(name, text, isToken, properties, relatedCards,
reverseRelatedCards, sets, cipt, tableRow, upsideDown); reverseRelatedCards, _sets, cipt, tableRow, upsideDown);
emit addCard(newCard); emit addCard(newCard);
} }
} }
@@ -280,12 +295,14 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in
for (auto i : related) { for (auto i : related) {
xml.writeStartElement("related"); xml.writeStartElement("related");
if (i->getDoesAttach()) { if (i->getDoesAttach()) {
xml.writeAttribute("attach", "attach"); xml.writeAttribute("attach", i->getAttachTypeAsString());
} }
if (i->getIsCreateAllExclusion()) { if (i->getIsCreateAllExclusion()) {
xml.writeAttribute("exclude", "exclude"); xml.writeAttribute("exclude", "exclude");
} }
if (i->getIsPersistent()) {
xml.writeAttribute("persistent", "persistent");
}
if (i->getIsVariable()) { if (i->getIsVariable()) {
if (1 == i->getDefaultCount()) { if (1 == i->getDefaultCount()) {
xml.writeAttribute("count", "x"); xml.writeAttribute("count", "x");
@@ -302,13 +319,16 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in
for (auto i : reverseRelated) { for (auto i : reverseRelated) {
xml.writeStartElement("reverse-related"); xml.writeStartElement("reverse-related");
if (i->getDoesAttach()) { if (i->getDoesAttach()) {
xml.writeAttribute("attach", "attach"); xml.writeAttribute("attach", i->getAttachTypeAsString());
} }
if (i->getIsCreateAllExclusion()) { if (i->getIsCreateAllExclusion()) {
xml.writeAttribute("exclude", "exclude"); xml.writeAttribute("exclude", "exclude");
} }
if (i->getIsPersistent()) {
xml.writeAttribute("persistent", "persistent");
}
if (i->getIsVariable()) { if (i->getIsVariable()) {
if (1 == i->getDefaultCount()) { if (1 == i->getDefaultCount()) {
xml.writeAttribute("count", "x"); xml.writeAttribute("count", "x");
@@ -336,7 +356,7 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in
return xml; return xml;
} }
bool CockatriceXml4Parser::saveToFile(SetNameMap sets, bool CockatriceXml4Parser::saveToFile(SetNameMap _sets,
CardNameMap cards, CardNameMap cards,
const QString &fileName, const QString &fileName,
const QString &sourceUrl, const QString &sourceUrl,
@@ -363,9 +383,9 @@ bool CockatriceXml4Parser::saveToFile(SetNameMap sets,
xml.writeTextElement("sourceVersion", sourceVersion); xml.writeTextElement("sourceVersion", sourceVersion);
xml.writeEndElement(); xml.writeEndElement();
if (sets.count() > 0) { if (_sets.count() > 0) {
xml.writeStartElement("sets"); xml.writeStartElement("sets");
for (CardSetPtr set : sets) { for (CardSetPtr set : _sets) {
xml << set; xml << set;
} }
xml.writeEndElement(); xml.writeEndElement();

View File

@@ -14,7 +14,7 @@ public:
~CockatriceXml4Parser() override = default; ~CockatriceXml4Parser() override = default;
bool getCanParseFile(const QString &name, QIODevice &device) override; bool getCanParseFile(const QString &name, QIODevice &device) override;
void parseFile(QIODevice &device) override; void parseFile(QIODevice &device) override;
bool saveToFile(SetNameMap sets, bool saveToFile(SetNameMap _sets,
CardNameMap cards, CardNameMap cards,
const QString &fileName, const QString &fileName,
const QString &sourceUrl = "unknown", const QString &sourceUrl = "unknown",

View File

@@ -24,7 +24,7 @@ void CardDragItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti
AbstractCardDragItem::paint(painter, option, widget); AbstractCardDragItem::paint(painter, option, widget);
if (occupied) if (occupied)
painter->fillRect(boundingRect(), QColor(200, 0, 0, 100)); painter->fillPath(shape(), QColor(200, 0, 0, 100));
} }
void CardDragItem::updatePosition(const QPointF &cursorScenePos) void CardDragItem::updatePosition(const QPointF &cursorScenePos)
@@ -53,8 +53,20 @@ void CardDragItem::updatePosition(const QPointF &cursorScenePos)
QPointF zonePos = currentZone->scenePos(); QPointF zonePos = currentZone->scenePos();
QPointF cursorPosInZone = cursorScenePos - zonePos; QPointF cursorPosInZone = cursorScenePos - zonePos;
QPointF cardTopLeft = cursorPosInZone - hotSpot;
QPointF closestGridPoint = cursorZone->closestGridPoint(cardTopLeft); // If we are on a Table, we center the card around the cursor, because we
// snap it into place and no longer see it being dragged.
//
// For other zones (where we do display the card under the cursor), we use
// the hotspot to feel like the card was dragged at the corresponding
// position.
TableZone *tableZone = qobject_cast<TableZone *>(cursorZone);
QPointF closestGridPoint;
if (tableZone)
closestGridPoint = tableZone->closestGridPoint(cursorPosInZone);
else
closestGridPoint = cursorPosInZone - hotSpot;
QPointF newPos = zonePos + closestGridPoint; QPointF newPos = zonePos + closestGridPoint;
if (newPos != pos()) { if (newPos != pos()) {
@@ -83,7 +95,7 @@ void CardDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
QList<CardDragItem *> dragItemList; QList<CardDragItem *> dragItemList;
CardZone *startZone = static_cast<CardItem *>(item)->getZone(); CardZone *startZone = static_cast<CardItem *>(item)->getZone();
if (currentZone && !(static_cast<CardItem *>(item)->getAttachedTo() && (startZone == currentZone))) { if (currentZone && !(static_cast<CardItem *>(item)->getAttachedTo() && (startZone == currentZone)) && !occupied) {
dragItemList.append(this); dragItemList.append(this);
for (int i = 0; i < childDrags.size(); i++) { for (int i = 0; i < childDrags.size(); i++) {
CardDragItem *c = static_cast<CardDragItem *>(childDrags[i]); CardDragItem *c = static_cast<CardDragItem *>(childDrags[i]);

View File

@@ -32,7 +32,7 @@ const QString CardFilter::attrName(Attr a)
case AttrManaCost: case AttrManaCost:
return tr("Mana Cost"); return tr("Mana Cost");
case AttrCmc: case AttrCmc:
return tr("CMC"); return tr("Mana Value");
case AttrRarity: case AttrRarity:
return tr("Rarity"); return tr("Rarity");
case AttrPow: case AttrPow:

View File

@@ -4,8 +4,7 @@
#include "main.h" #include "main.h"
#include "pictureloader.h" #include "pictureloader.h"
#include <QPainter> #include <QStylePainter>
#include <QStyle>
#include <QWidget> #include <QWidget>
CardInfoPicture::CardInfoPicture(QWidget *parent) : QWidget(parent), info(nullptr), pixmapDirty(true) CardInfoPicture::CardInfoPicture(QWidget *parent) : QWidget(parent), info(nullptr), pixmapDirty(true)
@@ -55,6 +54,13 @@ void CardInfoPicture::paintEvent(QPaintEvent *)
if (pixmapDirty) if (pixmapDirty)
loadPixmap(); loadPixmap();
QPainter painter(this); QSize scaledSize = resizedPixmap.size().scaled(size(), Qt::KeepAspectRatio);
style()->drawItemPixmap(&painter, rect(), Qt::AlignHCenter, resizedPixmap); QPoint topLeft{(width() - scaledSize.width()) / 2, (height() - scaledSize.height()) / 2};
qreal radius = 0.05 * scaledSize.width();
QStylePainter painter(this);
QPainterPath shape;
shape.addRoundedRect(QRect(topLeft, scaledSize), radius, radius);
painter.setClipPath(shape);
painter.drawItemPixmap(QRect(topLeft, scaledSize), Qt::AlignCenter, resizedPixmap);
} }

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

@@ -23,8 +23,7 @@ CardItem::CardItem(Player *_owner,
int _cardid, int _cardid,
bool _revealedCard, bool _revealedCard,
QGraphicsItem *parent, QGraphicsItem *parent,
CardZone *_zone, CardZone *_zone)
bool updateMenu)
: AbstractCardItem(_name, _owner, _cardid, parent), zone(_zone), revealedCard(_revealedCard), attacking(false), : AbstractCardItem(_name, _owner, _cardid, parent), zone(_zone), revealedCard(_revealedCard), attacking(false),
destroyOnZoneChange(false), doesntUntap(false), dragItem(nullptr), attachedTo(nullptr) destroyOnZoneChange(false), doesntUntap(false), dragItem(nullptr), attachedTo(nullptr)
{ {
@@ -35,9 +34,6 @@ CardItem::CardItem(Player *_owner,
moveMenu = new QMenu; moveMenu = new QMenu;
retranslateUi(); retranslateUi();
if (updateMenu) { // avoid updating card menu too often
emit updateCardMenu(this);
}
} }
CardItem::~CardItem() CardItem::~CardItem()
@@ -84,7 +80,6 @@ void CardItem::deleteLater()
void CardItem::setZone(CardZone *_zone) void CardItem::setZone(CardZone *_zone)
{ {
zone = _zone; zone = _zone;
emit updateCardMenu(this);
} }
void CardItem::retranslateUi() void CardItem::retranslateUi()
@@ -102,10 +97,10 @@ void CardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QMapIterator<int, int> counterIterator(counters); QMapIterator<int, int> counterIterator(counters);
while (counterIterator.hasNext()) { while (counterIterator.hasNext()) {
counterIterator.next(); counterIterator.next();
QColor color; QColor _color;
color.setHsv(counterIterator.key() * 60, 150, 255); _color.setHsv(counterIterator.key() * 60, 150, 255);
paintNumberEllipse(counterIterator.value(), 14, color, i, counters.size(), painter); paintNumberEllipse(counterIterator.value(), 14, _color, i, counters.size(), painter);
++i; ++i;
} }
@@ -146,21 +141,19 @@ void CardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
} }
if (getBeingPointedAt()) { if (getBeingPointedAt()) {
painter->fillRect(boundingRect(), QBrush(QColor(255, 0, 0, 100))); painter->fillPath(shape(), QBrush(QColor(255, 0, 0, 100)));
} }
if (doesntUntap) { if (doesntUntap) {
painter->save(); painter->save();
painter->setRenderHint(QPainter::Antialiasing, false); painter->setRenderHint(QPainter::Antialiasing, false);
transformPainter(painter, translatedSize, tapAngle);
QPen pen; QPen pen;
pen.setColor(Qt::magenta); pen.setColor(Qt::magenta);
const int penWidth = 1; pen.setWidth(0); // Cosmetic pen
pen.setWidth(penWidth);
painter->setPen(pen); painter->setPen(pen);
painter->drawRect(QRectF(0, 0, translatedSize.width() + penWidth, translatedSize.height() - penWidth)); painter->drawPath(shape());
painter->restore(); painter->restore();
} }
@@ -203,23 +196,25 @@ void CardItem::setPT(const QString &_pt)
void CardItem::setAttachedTo(CardItem *_attachedTo) void CardItem::setAttachedTo(CardItem *_attachedTo)
{ {
if (attachedTo) if (attachedTo != nullptr) {
attachedTo->removeAttachedCard(this); attachedTo->removeAttachedCard(this);
}
gridPoint.setX(-1); gridPoint.setX(-1);
attachedTo = _attachedTo; attachedTo = _attachedTo;
if (attachedTo) { if (attachedTo != nullptr) {
setParentItem(attachedTo->getZone()); setParentItem(attachedTo->getZone());
attachedTo->addAttachedCard(this); attachedTo->addAttachedCard(this);
if (zone != attachedTo->getZone()) if (zone != attachedTo->getZone()) {
attachedTo->getZone()->reorganizeCards(); attachedTo->getZone()->reorganizeCards();
} else }
} else {
setParentItem(zone); setParentItem(zone);
}
if (zone) if (zone != nullptr) {
zone->reorganizeCards(); zone->reorganizeCards();
}
emit updateCardMenu(this);
} }
void CardItem::resetState() void CardItem::resetState()
@@ -238,25 +233,25 @@ void CardItem::resetState()
update(); update();
} }
void CardItem::processCardInfo(const ServerInfo_Card &info) void CardItem::processCardInfo(const ServerInfo_Card &_info)
{ {
counters.clear(); counters.clear();
const int counterListSize = info.counter_list_size(); const int counterListSize = _info.counter_list_size();
for (int i = 0; i < counterListSize; ++i) { for (int i = 0; i < counterListSize; ++i) {
const ServerInfo_CardCounter &counterInfo = info.counter_list(i); const ServerInfo_CardCounter &counterInfo = _info.counter_list(i);
counters.insert(counterInfo.id(), counterInfo.value()); counters.insert(counterInfo.id(), counterInfo.value());
} }
setId(info.id()); setId(_info.id());
setName(QString::fromStdString(info.name())); setName(QString::fromStdString(_info.name()));
setAttacking(info.attacking()); setAttacking(_info.attacking());
setFaceDown(info.face_down()); setFaceDown(_info.face_down());
setPT(QString::fromStdString(info.pt())); setPT(QString::fromStdString(_info.pt()));
setAnnotation(QString::fromStdString(info.annotation())); setAnnotation(QString::fromStdString(_info.annotation()));
setColor(QString::fromStdString(info.color())); setColor(QString::fromStdString(_info.color()));
setTapped(info.tapped()); setTapped(_info.tapped());
setDestroyOnZoneChange(info.destroy_on_zone_change()); setDestroyOnZoneChange(_info.destroy_on_zone_change());
setDoesntUntap(info.doesnt_untap()); setDoesntUntap(_info.doesnt_untap());
} }
CardDragItem *CardItem::createDragItem(int _id, const QPointF &_pos, const QPointF &_scenePos, bool faceDown) CardDragItem *CardItem::createDragItem(int _id, const QPointF &_pos, const QPointF &_scenePos, bool faceDown)
@@ -289,15 +284,36 @@ void CardItem::drawArrow(const QColor &arrowColor)
scene()->addItem(arrow); scene()->addItem(arrow);
arrow->grabMouse(); arrow->grabMouse();
QListIterator<QGraphicsItem *> itemIterator(scene()->selectedItems()); for (const auto &item : scene()->selectedItems()) {
while (itemIterator.hasNext()) { CardItem *card = qgraphicsitem_cast<CardItem *>(item);
CardItem *c = qgraphicsitem_cast<CardItem *>(itemIterator.next()); if (card == nullptr || card == this)
if (!c || (c == this))
continue; continue;
if (c->getZone() != zone) if (card->getZone() != zone)
continue; continue;
ArrowDragItem *childArrow = new ArrowDragItem(arrowOwner, c, arrowColor); ArrowDragItem *childArrow = new ArrowDragItem(arrowOwner, card, arrowColor);
scene()->addItem(childArrow);
arrow->addChildArrow(childArrow);
}
}
void CardItem::drawAttachArrow()
{
if (static_cast<TabGame *>(owner->parent())->getSpectator())
return;
auto *arrow = new ArrowAttachItem(this);
scene()->addItem(arrow);
arrow->grabMouse();
for (const auto &item : scene()->selectedItems()) {
CardItem *card = qgraphicsitem_cast<CardItem *>(item);
if (card == nullptr)
continue;
if (card->getZone() != zone)
continue;
ArrowAttachItem *childArrow = new ArrowAttachItem(card);
scene()->addItem(childArrow); scene()->addItem(childArrow);
arrow->addChildArrow(childArrow); arrow->addChildArrow(childArrow);
} }
@@ -332,22 +348,24 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
bool forceFaceDown = event->modifiers().testFlag(Qt::ShiftModifier); bool forceFaceDown = event->modifiers().testFlag(Qt::ShiftModifier);
createDragItem(id, event->pos(), event->scenePos(), facedown || forceFaceDown); // Use the buttonDownPos to align the hot spot with the position when
// the user originally clicked
createDragItem(id, event->buttonDownPos(Qt::LeftButton), event->scenePos(), facedown || forceFaceDown);
dragItem->grabMouse(); dragItem->grabMouse();
QList<QGraphicsItem *> sel = scene()->selectedItems(); int childIndex = 0;
int j = 0; for (const auto &item : scene()->selectedItems()) {
for (int i = 0; i < sel.size(); i++) { CardItem *card = static_cast<CardItem *>(item);
CardItem *c = static_cast<CardItem *>(sel.at(i)); if ((card == this) || (card->getZone() != zone))
if ((c == this) || (c->getZone() != zone))
continue; continue;
++j; ++childIndex;
QPointF childPos; QPointF childPos;
if (zone->getHasCardAttr()) if (zone->getHasCardAttr())
childPos = c->pos() - pos(); childPos = card->pos() - pos();
else else
childPos = QPointF(j * CARD_WIDTH / 2, 0); childPos = QPointF(childIndex * CARD_WIDTH / 2, 0);
CardDragItem *drag = new CardDragItem(c, c->getId(), childPos, c->getFaceDown() || forceFaceDown, dragItem); CardDragItem *drag =
new CardDragItem(card, card->getId(), childPos, card->getFaceDown() || forceFaceDown, dragItem);
drag->setPos(dragItem->pos() + childPos); drag->setPos(dragItem->pos() + childPos);
scene()->addItem(drag); scene()->addItem(drag);
} }
@@ -371,9 +389,9 @@ void CardItem::playCard(bool faceDown)
void CardItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) void CardItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{ {
if (event->button() == Qt::RightButton) { if (event->button() == Qt::RightButton) {
if (cardMenu && !cardMenu->isEmpty() && owner != nullptr) { if (cardMenu != nullptr && !cardMenu->isEmpty() && owner != nullptr) {
owner->updateCardMenu(this); cardMenu->popup(event->screenPos());
cardMenu->exec(event->screenPos()); return;
} }
} else if ((event->modifiers() != Qt::AltModifier) && (event->button() == Qt::LeftButton) && } else if ((event->modifiers() != Qt::AltModifier) && (event->button() == Qt::LeftButton) &&
(!SettingsCache::instance().getDoubleClickToPlay())) { (!SettingsCache::instance().getDoubleClickToPlay())) {
@@ -437,13 +455,13 @@ bool CardItem::animationEvent()
QVariant CardItem::itemChange(GraphicsItemChange change, const QVariant &value) QVariant CardItem::itemChange(GraphicsItemChange change, const QVariant &value)
{ {
if ((change == ItemSelectedHasChanged) && owner) { if ((change == ItemSelectedHasChanged) && owner != nullptr) {
if (value == true) { if (value == true) {
owner->setCardMenu(cardMenu); owner->setCardMenu(cardMenu);
owner->getGame()->setActiveCard(this); owner->getGame()->setActiveCard(this);
} else if (owner->getCardMenu() == cardMenu) { } else if (owner->getCardMenu() == cardMenu) {
owner->setCardMenu(0); owner->setCardMenu(nullptr);
owner->getGame()->setActiveCard(0); owner->getGame()->setActiveCard(nullptr);
} }
} }
return QGraphicsItem::itemChange(change, value); return QGraphicsItem::itemChange(change, value);

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;
@@ -53,8 +54,7 @@ public:
int _cardid = -1, int _cardid = -1,
bool revealedCard = false, bool revealedCard = false,
QGraphicsItem *parent = nullptr, QGraphicsItem *parent = nullptr,
CardZone *_zone = nullptr, CardZone *_zone = nullptr);
bool updateMenu = false);
~CardItem(); ~CardItem();
void retranslateUi(); void retranslateUi();
CardZone *getZone() const CardZone *getZone() const
@@ -131,14 +131,14 @@ public:
} }
void removeAttachedCard(CardItem *card) void removeAttachedCard(CardItem *card)
{ {
attachedCards.removeAt(attachedCards.indexOf(card)); attachedCards.removeOne(card);
} }
const QList<CardItem *> &getAttachedCards() const const QList<CardItem *> &getAttachedCards() const
{ {
return attachedCards; return attachedCards;
} }
void resetState(); void resetState();
void processCardInfo(const ServerInfo_Card &info); void processCardInfo(const ServerInfo_Card &_info);
QMenu *getCardMenu() const QMenu *getCardMenu() const
{ {
@@ -157,6 +157,7 @@ public:
CardDragItem *createDragItem(int _id, const QPointF &_pos, const QPointF &_scenePos, bool faceDown); CardDragItem *createDragItem(int _id, const QPointF &_pos, const QPointF &_scenePos, bool faceDown);
void deleteDragItem(); void deleteDragItem();
void drawArrow(const QColor &arrowColor); void drawArrow(const QColor &arrowColor);
void drawAttachArrow();
void playCard(bool faceDown); void playCard(bool faceDown);
protected: protected:

View File

@@ -187,7 +187,7 @@ CardItem *CardZone::takeCard(int position, int cardId, bool /*canResize*/)
void CardZone::removeCard(CardItem *card) void CardZone::removeCard(CardItem *card)
{ {
cards.removeAt(cards.indexOf(card)); cards.removeOne(card);
reorganizeCards(); reorganizeCards();
emit cardCountChanged(); emit cardCountChanged();
player->deleteCard(card); player->deleteCard(card);

View File

@@ -12,11 +12,16 @@
#include <QDesktopServices> #include <QDesktopServices>
#include <QMouseEvent> #include <QMouseEvent>
#include <QScrollBar> #include <QScrollBar>
#include <QTextEdit>
const QColor DEFAULT_MENTION_COLOR = QColor(194, 31, 47); const QColor DEFAULT_MENTION_COLOR = QColor(194, 31, 47);
ChatView::ChatView(const TabSupervisor *_tabSupervisor, UserMessagePosition::UserMessagePosition(QTextCursor &cursor)
{
block = cursor.block();
relativePosition = cursor.position() - block.position();
}
ChatView::ChatView(TabSupervisor *_tabSupervisor,
const UserlistProxy *_userlistProxy, const UserlistProxy *_userlistProxy,
TabGame *_game, TabGame *_game,
bool _showTimestamps, bool _showTimestamps,
@@ -43,8 +48,8 @@ ChatView::ChatView(const TabSupervisor *_tabSupervisor,
userContextMenu = new UserContextMenu(tabSupervisor, this, game); userContextMenu = new UserContextMenu(tabSupervisor, this, game);
connect(userContextMenu, SIGNAL(openMessageDialog(QString, bool)), this, SIGNAL(openMessageDialog(QString, bool))); connect(userContextMenu, SIGNAL(openMessageDialog(QString, bool)), this, SIGNAL(openMessageDialog(QString, bool)));
userName = userlistProxy->getOwnUsername(); ownUserName = userlistProxy->getOwnUsername();
mention = "@" + userName; mention = "@" + ownUserName;
mentionFormat.setFontWeight(QFont::Bold); mentionFormat.setFontWeight(QFont::Bold);
@@ -68,7 +73,7 @@ QTextCursor ChatView::prepareBlock(bool same)
{ {
lastSender.clear(); lastSender.clear();
QTextCursor cursor(document()->lastBlock()); QTextCursor cursor(document());
cursor.movePosition(QTextCursor::End); cursor.movePosition(QTextCursor::End);
if (same) { if (same) {
cursor.insertHtml("<br>"); cursor.insertHtml("<br>");
@@ -145,76 +150,86 @@ void ChatView::appendUrlTag(QTextCursor &cursor, QString url)
void ChatView::appendMessage(QString message, void ChatView::appendMessage(QString message,
RoomMessageTypeFlags messageType, RoomMessageTypeFlags messageType,
QString sender, const QString &userName,
UserLevelFlags userLevel, UserLevelFlags userLevel,
QString UserPrivLevel, QString UserPrivLevel,
bool playerBold) bool playerBold)
{ {
bool atBottom = verticalScrollBar()->value() >= verticalScrollBar()->maximum(); bool atBottom = verticalScrollBar()->value() >= verticalScrollBar()->maximum();
bool sameSender = (sender == lastSender) && !lastSender.isEmpty(); // messageType should be Event_RoomSay::UserMessage though we don't actually check
bool isUserMessage = !(userName.toLower() == "servatrice" || userName.isEmpty());
bool sameSender = isUserMessage && userName == lastSender;
QTextCursor cursor = prepareBlock(sameSender); QTextCursor cursor = prepareBlock(sameSender);
lastSender = sender; lastSender = userName;
// timestamp // timestamp
if (showTimestamps && (!sameSender || sender.toLower() == "servatrice") && !sender.isEmpty()) { if (showTimestamps && ((!sameSender && isUserMessage) || userName.toLower() == "servatrice")) {
QTextCharFormat timeFormat; QTextCharFormat timeFormat;
timeFormat.setForeground(serverMessageColor); timeFormat.setForeground(serverMessageColor);
if (sender.isEmpty()) timeFormat.setFontWeight(QFont::Bold);
timeFormat.setFontWeight(QFont::Bold);
cursor.setCharFormat(timeFormat); cursor.setCharFormat(timeFormat);
cursor.insertText(QDateTime::currentDateTime().toString("[hh:mm:ss] ")); cursor.insertText(QDateTime::currentDateTime().toString("[hh:mm:ss] "));
} }
// nickname // nickname
if (sender.toLower() != "servatrice") { if (isUserMessage) {
QTextCharFormat senderFormat; QTextCharFormat senderFormat;
if (sender == userName) { if (userName == ownUserName) {
senderFormat.setForeground(QBrush(getCustomMentionColor())); senderFormat.setForeground(QBrush(getCustomMentionColor()));
senderFormat.setFontWeight(QFont::Bold); senderFormat.setFontWeight(QFont::Bold);
} else { } else {
senderFormat.setForeground(QBrush(otherUserColor)); senderFormat.setForeground(QBrush(otherUserColor));
if (playerBold) if (playerBold) {
senderFormat.setFontWeight(QFont::Bold); senderFormat.setFontWeight(QFont::Bold);
}
} }
senderFormat.setAnchor(true); senderFormat.setAnchor(true);
senderFormat.setAnchorHref("user://" + QString::number(userLevel) + "_" + sender); senderFormat.setAnchorHref("user://" + QString::number(userLevel) + "_" + userName);
if (sameSender) { if (sameSender) {
cursor.insertText(" "); cursor.insertText(" ");
} else { } else {
if (!sender.isEmpty()) { const int pixelSize = QFontInfo(cursor.charFormat().font()).pixelSize();
const int pixelSize = QFontInfo(cursor.charFormat().font()).pixelSize(); bool isBuddy = userlistProxy->isUserBuddy(userName);
bool isBuddy = userlistProxy->isUserBuddy(sender); cursor.insertImage(
cursor.insertImage( UserLevelPixmapGenerator::generatePixmap(pixelSize, userLevel, isBuddy, UserPrivLevel).toImage());
UserLevelPixmapGenerator::generatePixmap(pixelSize, userLevel, isBuddy, UserPrivLevel).toImage()); cursor.insertText(" ");
cursor.insertText(" ");
}
cursor.setCharFormat(senderFormat); cursor.setCharFormat(senderFormat);
if (!sender.isEmpty()) cursor.insertText(userName);
sender.append(": "); cursor.insertText(": ");
cursor.insertText(sender); userMessagePositions[userName].append(cursor);
} }
} }
// use different color for server messages // use different color for server messages
defaultFormat = QTextCharFormat(); defaultFormat = QTextCharFormat();
if (sender.isEmpty()) { if (!isUserMessage) {
switch (messageType) { if (messageType == Event_RoomSay::ChatHistory) {
case Event_RoomSay::Welcome: defaultFormat.setForeground(Qt::gray); // FIXME : hardcoded color
defaultFormat.setForeground(Qt::darkGreen); defaultFormat.setFontWeight(QFont::Light);
defaultFormat.setFontWeight(QFont::Bold); defaultFormat.setFontItalic(true);
break; static const QRegularExpression userNameRegex("^(\\[[^\\]]*\\]\\s)(\\S+):\\s");
case Event_RoomSay::ChatHistory: auto match = userNameRegex.match(message);
defaultFormat.setForeground(Qt::gray); if (match.hasMatch()) {
defaultFormat.setFontWeight(QFont::Light); cursor.setCharFormat(defaultFormat);
defaultFormat.setFontItalic(true); UserMessagePosition pos(cursor);
break; pos.relativePosition = match.captured(0).length(); // set message start
default: auto before = match.captured(1);
defaultFormat.setForeground(Qt::darkGreen); auto sentBy = match.captured(2);
defaultFormat.setFontWeight(QFont::Bold); cursor.insertText(before); // add message timestamp
QTextCharFormat senderFormat(defaultFormat);
senderFormat.setAnchor(true);
// this underscore is important, it is used to add the user level, but in this case the level is
// unknown, if the name contains an underscore it would split up the name
senderFormat.setAnchorHref("user://_" + sentBy);
cursor.setCharFormat(senderFormat);
cursor.insertText(sentBy); // add username with href so it shows the menu
userMessagePositions[sentBy].append(pos); // save message position
message.remove(0, pos.relativePosition - 2); // do not remove semicolon
}
} else {
defaultFormat.setForeground(Qt::darkGreen); // FIXME : hardcoded color
defaultFormat.setFontWeight(QFont::Bold);
} }
} else if (sender.toLower() == "servatrice") {
defaultFormat.setForeground(Qt::darkGreen);
defaultFormat.setFontWeight(QFont::Bold);
} }
cursor.setCharFormat(defaultFormat); cursor.setCharFormat(defaultFormat);
@@ -234,7 +249,7 @@ void ChatView::appendMessage(QString message,
break; break;
case '@': case '@':
if (mentionEnabled) { if (mentionEnabled) {
checkMention(cursor, message, sender, userLevel); checkMention(cursor, message, userName, userLevel);
} else { } else {
cursor.insertText(c, defaultFormat); cursor.insertText(c, defaultFormat);
message = message.mid(1); message = message.mid(1);
@@ -304,9 +319,9 @@ void ChatView::checkTag(QTextCursor &cursor, QString &message)
checkWord(cursor, message); checkWord(cursor, message);
} }
void ChatView::checkMention(QTextCursor &cursor, QString &message, QString &sender, 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);
@@ -316,7 +331,7 @@ void ChatView::checkMention(QTextCursor &cursor, QString &message, QString &send
const ServerInfo_User *onlineUser = userlistProxy->getOnlineUser(fullMentionUpToSpaceOrEnd); const ServerInfo_User *onlineUser = userlistProxy->getOnlineUser(fullMentionUpToSpaceOrEnd);
if (onlineUser) // Is there a user online named this? if (onlineUser) // Is there a user online named this?
{ {
if (userName.toLower() == fullMentionUpToSpaceOrEnd.toLower()) // Is this user you? if (ownUserName.toLower() == fullMentionUpToSpaceOrEnd.toLower()) // Is this user you?
{ {
// You have received a valid mention!! // You have received a valid mention!!
soundEngine->playSound("chat_mention"); soundEngine->playSound("chat_mention");
@@ -325,7 +340,7 @@ void ChatView::checkMention(QTextCursor &cursor, QString &message, QString &send
: QBrush(Qt::black)); : QBrush(Qt::black));
cursor.insertText(mention, mentionFormat); cursor.insertText(mention, mentionFormat);
message = message.mid(mention.size()); message = message.mid(mention.size());
showSystemPopup(sender); showSystemPopup(userName);
} else { } else {
QString correctUserName = QString::fromStdString(onlineUser->name()); QString correctUserName = QString::fromStdString(onlineUser->name());
mentionFormatOtherUser.setAnchorHref("user://" + QString::number(onlineUser->user_level()) + "_" + mentionFormatOtherUser.setAnchorHref("user://" + QString::number(onlineUser->user_level()) + "_" +
@@ -347,7 +362,7 @@ void ChatView::checkMention(QTextCursor &cursor, QString &message, QString &send
: QBrush(Qt::black)); : QBrush(Qt::black));
cursor.insertText("@" + fullMentionUpToSpaceOrEnd, mentionFormat); cursor.insertText("@" + fullMentionUpToSpaceOrEnd, mentionFormat);
message = message.mid(fullMentionUpToSpaceOrEnd.size() + 1); message = message.mid(fullMentionUpToSpaceOrEnd.size() + 1);
showSystemPopup(sender); showSystemPopup(userName);
cursor.setCharFormat(defaultFormat); cursor.setCharFormat(defaultFormat);
return; return;
@@ -445,26 +460,33 @@ void ChatView::actMessageClicked()
emit messageClickedSignal(); emit messageClickedSignal();
} }
void ChatView::showSystemPopup(QString &sender) void ChatView::showSystemPopup(const QString &userName)
{ {
QApplication::alert(this); QApplication::alert(this);
if (SettingsCache::instance().getShowMentionPopup()) { if (SettingsCache::instance().getShowMentionPopup()) {
QString ref = sender.left(sender.length() - 2); emit showMentionPopup(userName);
emit showMentionPopup(ref);
} }
} }
QColor ChatView::getCustomMentionColor() QColor ChatView::getCustomMentionColor()
{ {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
QColor customColor = QColor::fromString("#" + SettingsCache::instance().getChatMentionColor());
#else
QColor customColor; QColor customColor;
customColor.setNamedColor("#" + SettingsCache::instance().getChatMentionColor()); customColor.setNamedColor("#" + SettingsCache::instance().getChatMentionColor());
#endif
return customColor.isValid() ? customColor : DEFAULT_MENTION_COLOR; return customColor.isValid() ? customColor : DEFAULT_MENTION_COLOR;
} }
QColor ChatView::getCustomHighlightColor() QColor ChatView::getCustomHighlightColor()
{ {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
QColor customColor = QColor::fromString("#" + SettingsCache::instance().getChatMentionColor());
#else
QColor customColor; QColor customColor;
customColor.setNamedColor("#" + SettingsCache::instance().getChatHighlightColor()); customColor.setNamedColor("#" + SettingsCache::instance().getChatMentionColor());
#endif
return customColor.isValid() ? customColor : DEFAULT_MENTION_COLOR; return customColor.isValid() ? customColor : DEFAULT_MENTION_COLOR;
} }
@@ -474,7 +496,33 @@ void ChatView::clearChat()
lastSender = ""; lastSender = "";
} }
void ChatView::redactMessages(const QString &userName, int amount)
{
auto &messagePositions = userMessagePositions[userName];
bool removedLastMessage = false;
QTextCursor cursor(document());
for (; !messagePositions.isEmpty() && amount != 0; --amount) {
auto position = messagePositions.takeLast(); // go backwards from last message
cursor.setPosition(position.block.position()); // move to start of block, then continue to start of message
cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, position.relativePosition);
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); // select until end of block
cursor.removeSelectedText();
// if the cursor is at the end of the text it is possible to add text to this block still
removedLastMessage |= cursor.atEnd();
// we will readd this position later
}
if (removedLastMessage) {
cursor.movePosition(QTextCursor::End);
messagePositions.append(cursor);
// note that this message might stay empty, this is not harmful as it will simply remove nothing the next time
}
}
#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);
} }
@@ -530,7 +578,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: {
@@ -540,7 +592,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());
userContextMenu->showContextMenu(event->globalPos(), userName, userLevel); #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
userContextMenu->showContextMenu(event->globalPosition().toPoint(), userName, userLevel, this);
#else
userContextMenu->showContextMenu(event->globalPos(), userName, userLevel, this);
#endif
break; break;
} }
case Qt::LeftButton: { case Qt::LeftButton: {

View File

@@ -18,11 +18,22 @@ class QMouseEvent;
class UserContextMenu; class UserContextMenu;
class TabGame; class TabGame;
class UserMessagePosition
{
public:
#if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0))
UserMessagePosition() = default; // older qt versions require a default constructor to use in containers
#endif
UserMessagePosition(QTextCursor &cursor);
int relativePosition;
QTextBlock block;
};
class ChatView : public QTextBrowser class ChatView : public QTextBrowser
{ {
Q_OBJECT Q_OBJECT
protected: protected:
const TabSupervisor *const tabSupervisor; TabSupervisor *const tabSupervisor;
TabGame *const game; TabGame *const game;
private: private:
@@ -36,7 +47,7 @@ private:
const UserlistProxy *const userlistProxy; const UserlistProxy *const userlistProxy;
UserContextMenu *userContextMenu; UserContextMenu *userContextMenu;
QString lastSender; QString lastSender;
QString userName; QString ownUserName;
QString mention; QString mention;
QTextCharFormat mentionFormat; QTextCharFormat mentionFormat;
QTextCharFormat highlightFormat; QTextCharFormat highlightFormat;
@@ -48,16 +59,18 @@ private:
HoveredItemType hoveredItemType; HoveredItemType hoveredItemType;
QString hoveredContent; QString hoveredContent;
QAction *messageClicked; QAction *messageClicked;
QMap<QString, QVector<UserMessagePosition>> userMessagePositions;
QTextFragment getFragmentUnderMouse(const QPoint &pos) const; QTextFragment getFragmentUnderMouse(const QPoint &pos) const;
QTextCursor prepareBlock(bool same = false); QTextCursor prepareBlock(bool same = false);
void appendCardTag(QTextCursor &cursor, const QString &cardName); void appendCardTag(QTextCursor &cursor, const QString &cardName);
void appendUrlTag(QTextCursor &cursor, QString url); void appendUrlTag(QTextCursor &cursor, QString url);
QColor getCustomMentionColor(); static QColor getCustomMentionColor();
QColor getCustomHighlightColor(); static QColor getCustomHighlightColor();
void showSystemPopup(QString &sender); void showSystemPopup(const QString &userName);
bool isModeratorSendingGlobal(QFlags<ServerInfo_User::UserLevelFlag> userLevelFlag, QString message); bool isModeratorSendingGlobal(QFlags<ServerInfo_User::UserLevelFlag> userLevelFlag, QString message);
void checkTag(QTextCursor &cursor, QString &message); void checkTag(QTextCursor &cursor, QString &message);
void checkMention(QTextCursor &cursor, QString &message, QString &sender, UserLevelFlags userLevel); void checkMention(QTextCursor &cursor, QString &message, const QString &userName, UserLevelFlags userLevel);
void checkWord(QTextCursor &cursor, QString &message); void checkWord(QTextCursor &cursor, QString &message);
QString extractNextWord(QString &message, QString &rest); QString extractNextWord(QString &message, QString &rest);
@@ -70,7 +83,7 @@ private slots:
void actMessageClicked(); void actMessageClicked();
public: public:
ChatView(const TabSupervisor *_tabSupervisor, ChatView(TabSupervisor *_tabSupervisor,
const UserlistProxy *_userlistProxy, const UserlistProxy *_userlistProxy,
TabGame *_game, TabGame *_game,
bool _showTimestamps, bool _showTimestamps,
@@ -82,14 +95,19 @@ public:
QString optionalFontColor = QString()); QString optionalFontColor = QString());
void appendMessage(QString message, void appendMessage(QString message,
RoomMessageTypeFlags messageType = {}, RoomMessageTypeFlags messageType = {},
QString sender = QString(), const QString &userName = QString(),
UserLevelFlags userLevel = UserLevelFlags(), UserLevelFlags userLevel = UserLevelFlags(),
QString UserPrivLevel = "NONE", QString UserPrivLevel = "NONE",
bool playerBold = false); bool playerBold = false);
void clearChat(); void clearChat();
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);
@@ -101,7 +119,7 @@ signals:
void deleteCardInfoPopup(QString cardName); void deleteCardInfoPopup(QString cardName);
void addMentionTag(QString mentionTag); void addMentionTag(QString mentionTag);
void messageClickedSignal(); void messageClickedSignal();
void showMentionPopup(QString &sender); void showMentionPopup(const QString &userName);
}; };
#endif #endif

View File

@@ -1,29 +0,0 @@
#ifndef CLIENT_METATYPES_H
#define CLIENT_METATYPES_H
#include <QMetaType>
Q_DECLARE_METATYPE(QVariant)
Q_DECLARE_METATYPE(CommandContainer)
Q_DECLARE_METATYPE(Response)
Q_DECLARE_METATYPE(Response::ResponseCode)
Q_DECLARE_METATYPE(ClientStatus)
Q_DECLARE_METATYPE(RoomEvent)
Q_DECLARE_METATYPE(GameEventContainer)
Q_DECLARE_METATYPE(Event_ServerIdentification)
Q_DECLARE_METATYPE(Event_ConnectionClosed)
Q_DECLARE_METATYPE(Event_ServerShutdown)
Q_DECLARE_METATYPE(Event_AddToList)
Q_DECLARE_METATYPE(Event_RemoveFromList)
Q_DECLARE_METATYPE(Event_UserJoined)
Q_DECLARE_METATYPE(Event_UserLeft)
Q_DECLARE_METATYPE(Event_ServerMessage)
Q_DECLARE_METATYPE(Event_ListRooms)
Q_DECLARE_METATYPE(Event_GameJoined)
Q_DECLARE_METATYPE(Event_UserMessage)
Q_DECLARE_METATYPE(ServerInfo_User)
Q_DECLARE_METATYPE(QList<ServerInfo_User>)
Q_DECLARE_METATYPE(Event_ReplayAdded)
Q_DECLARE_METATYPE(QList<QString>)
#endif

View File

@@ -1,4 +1,3 @@
#include "customlineedit.h" #include "customlineedit.h"
#include "settingscache.h" #include "settingscache.h"
@@ -38,8 +37,8 @@ bool LineEditUnfocusable::isUnfocusShortcut(QKeyEvent *event)
QKeySequence key(modifier + keyNoMod); QKeySequence key(modifier + keyNoMod);
QList<QKeySequence> unfocusShortcut = SettingsCache::instance().shortcuts().getShortcut("Textbox/unfocusTextBox"); QList<QKeySequence> unfocusShortcut = SettingsCache::instance().shortcuts().getShortcut("Textbox/unfocusTextBox");
for (QList<QKeySequence>::iterator i = unfocusShortcut.begin(); i != unfocusShortcut.end(); ++i) { for (const auto &unfocusKey : unfocusShortcut) {
if (key.matches(*i) == QKeySequence::ExactMatch) if (key.matches(unfocusKey) == QKeySequence::ExactMatch)
return true; return true;
} }
return false; return false;

View File

@@ -12,12 +12,13 @@ class QString;
// shortcuts and other shortcuts // shortcuts and other shortcuts
class LineEditUnfocusable : public QLineEdit class LineEditUnfocusable : public QLineEdit
{ {
Q_OBJECT
public: public:
LineEditUnfocusable(QWidget *parent = nullptr); explicit LineEditUnfocusable(QWidget *parent = nullptr);
LineEditUnfocusable(const QString &contents, QWidget *parent = nullptr); explicit LineEditUnfocusable(const QString &contents, QWidget *parent = nullptr);
private: private:
bool isUnfocusShortcut(QKeyEvent *key); static bool isUnfocusShortcut(QKeyEvent *key);
protected: protected:
void keyPressEvent(QKeyEvent *event) override; void keyPressEvent(QKeyEvent *event) override;

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";
} }
@@ -288,7 +289,7 @@ QString DeckLoader::getCardZoneFromName(QString cardName, QString currentZoneNam
return currentZoneName; return currentZoneName;
} }
QString DeckLoader::getCompleteCardName(const QString cardName) const QString DeckLoader::getCompleteCardName(const QString &cardName) const
{ {
if (db) { if (db) {
CardInfoPtr temp = db->guessCard(cardName); CardInfoPtr temp = db->guessCard(cardName);

View File

@@ -57,8 +57,8 @@ protected:
const InnerDecklistNode *zoneNode, const InnerDecklistNode *zoneNode,
QList<DecklistCardNode *> cards, QList<DecklistCardNode *> cards,
bool addComments = true); bool addComments = true);
virtual QString getCardZoneFromName(QString cardName, QString currentZoneName); [[nodiscard]] QString getCardZoneFromName(QString cardName, QString currentZoneName) override;
virtual QString getCompleteCardName(const QString cardName) const; [[nodiscard]] QString getCompleteCardName(const QString &cardName) const override;
}; };
#endif #endif

View File

@@ -7,7 +7,7 @@
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QNetworkReply> #include <QNetworkReply>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QRegExp> #include <QRegularExpression>
#include <QUrlQuery> #include <QUrlQuery>
DeckStatsInterface::DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent) DeckStatsInterface::DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent)
@@ -20,7 +20,7 @@ DeckStatsInterface::DeckStatsInterface(CardDatabase &_cardDatabase, QObject *par
void DeckStatsInterface::queryFinished(QNetworkReply *reply) void DeckStatsInterface::queryFinished(QNetworkReply *reply)
{ {
if (reply->error() != QNetworkReply::NoError) { if (reply->error() != QNetworkReply::NoError) {
QMessageBox::critical(0, tr("Error"), reply->errorString()); QMessageBox::critical(nullptr, tr("Error"), reply->errorString());
reply->deleteLater(); reply->deleteLater();
deleteLater(); deleteLater();
return; return;
@@ -29,14 +29,15 @@ void DeckStatsInterface::queryFinished(QNetworkReply *reply)
QString data(reply->readAll()); QString data(reply->readAll());
reply->deleteLater(); reply->deleteLater();
QRegExp rx("<meta property=\"og:url\" content=\"([^\"]+)\""); static const QRegularExpression rx("<meta property=\"og:url\" content=\"([^\"]+)\"");
if (-1 == rx.indexIn(data)) { auto match = rx.match(data);
QMessageBox::critical(0, tr("Error"), tr("The reply from the server could not be parsed.")); if (!match.hasMatch()) {
QMessageBox::critical(nullptr, tr("Error"), tr("The reply from the server could not be parsed."));
deleteLater(); deleteLater();
return; return;
} }
QString deckUrl = rx.cap(1); QString deckUrl = match.captured(1);
QDesktopServices::openUrl(deckUrl); QDesktopServices::openUrl(deckUrl);
deleteLater(); deleteLater();

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,
@@ -89,7 +89,8 @@ void DeckViewCard::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti
pen.setJoinStyle(Qt::MiterJoin); pen.setJoinStyle(Qt::MiterJoin);
pen.setColor(originZone == DECK_ZONE_MAIN ? Qt::green : Qt::red); pen.setColor(originZone == DECK_ZONE_MAIN ? Qt::green : Qt::red);
painter->setPen(pen); painter->setPen(pen);
painter->drawRect(QRectF(1, 1, CARD_WIDTH - 2, CARD_HEIGHT - 2.5)); qreal cardRadius = 0.05 * (CARD_WIDTH - 3);
painter->drawRoundedRect(QRectF(1.5, 1.5, CARD_WIDTH - 3., CARD_HEIGHT - 3.), cardRadius, cardRadius);
painter->restore(); painter->restore();
} }
@@ -216,7 +217,7 @@ void DeckViewCardContainer::addCard(DeckViewCard *card)
void DeckViewCardContainer::removeCard(DeckViewCard *card) void DeckViewCardContainer::removeCard(DeckViewCard *card)
{ {
cards.removeAt(cards.indexOf(card)); cards.removeOne(card);
cardsByType.remove(card->getInfo() ? card->getInfo()->getMainCardType() : "", card); cardsByType.remove(card->getInfo() ? card->getInfo()->getMainCardType() : "", card);
} }
@@ -238,11 +239,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;
@@ -274,16 +273,11 @@ void DeckViewCardContainer::rearrangeItems(const QList<QPair<int, int>> &rowsAnd
{ {
currentRowsAndCols = rowsAndCols; currentRowsAndCols = rowsAndCols;
int totalCols = 0, totalRows = 0;
qreal yUntilNow = separatorY + paddingY; qreal yUntilNow = separatorY + paddingY;
qreal x = (qreal)getCardTypeTextWidth(); qreal x = (qreal)getCardTypeTextWidth();
for (int i = 0; i < rowsAndCols.size(); ++i) { for (int i = 0; i < rowsAndCols.size(); ++i) {
const int tempRows = rowsAndCols[i].first; const int tempRows = rowsAndCols[i].first;
const int tempCols = rowsAndCols[i].second; const int tempCols = rowsAndCols[i].second;
totalRows += tempRows;
if (tempCols > totalCols)
totalCols = tempCols;
QList<QString> cardTypeList = cardsByType.uniqueKeys(); QList<QString> cardTypeList = cardsByType.uniqueKeys();
QList<DeckViewCard *> row = cardsByType.values(cardTypeList[i]); QList<DeckViewCard *> row = cardsByType.values(cardTypeList[i]);
std::sort(row.begin(), row.end(), DeckViewCardContainer::sortCardsByName); std::sort(row.begin(), row.end(), DeckViewCardContainer::sortCardsByName);
@@ -440,7 +434,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

@@ -1,6 +1,7 @@
#include "dlg_connect.h" #include "dlg_connect.h"
#include "settingscache.h" #include "settingscache.h"
#include "trice_limits.h"
#include "userconnection_information.h" #include "userconnection_information.h"
#include <QCheckBox> #include <QCheckBox>
@@ -39,22 +40,27 @@ DlgConnect::DlgConnect(QWidget *parent) : QDialog(parent)
saveLabel = new QLabel(tr("Name:")); saveLabel = new QLabel(tr("Name:"));
saveEdit = new QLineEdit; saveEdit = new QLineEdit;
saveEdit->setMaxLength(MAX_NAME_LENGTH);
saveLabel->setBuddy(saveEdit); saveLabel->setBuddy(saveEdit);
hostLabel = new QLabel(tr("&Host:")); hostLabel = new QLabel(tr("&Host:"));
hostEdit = new QLineEdit; hostEdit = new QLineEdit;
hostEdit->setMaxLength(MAX_NAME_LENGTH);
hostLabel->setBuddy(hostEdit); hostLabel->setBuddy(hostEdit);
portLabel = new QLabel(tr("&Port:")); portLabel = new QLabel(tr("&Port:"));
portEdit = new QLineEdit; portEdit = new QLineEdit;
portEdit->setValidator(new QIntValidator(0, 0xffff, portEdit));
portLabel->setBuddy(portEdit); portLabel->setBuddy(portEdit);
playernameLabel = new QLabel(tr("Player &name:")); playernameLabel = new QLabel(tr("Player &name:"));
playernameEdit = new QLineEdit; playernameEdit = new QLineEdit;
playernameEdit->setMaxLength(MAX_NAME_LENGTH);
playernameLabel->setBuddy(playernameEdit); playernameLabel->setBuddy(playernameEdit);
passwordLabel = new QLabel(tr("P&assword:")); passwordLabel = new QLabel(tr("P&assword:"));
passwordEdit = new QLineEdit; passwordEdit = new QLineEdit;
passwordEdit->setMaxLength(MAX_NAME_LENGTH);
passwordLabel->setBuddy(passwordEdit); passwordLabel->setBuddy(passwordEdit);
passwordEdit->setEchoMode(QLineEdit::Password); passwordEdit->setEchoMode(QLineEdit::Password);
@@ -156,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();
} }
@@ -240,22 +245,22 @@ void DlgConnect::updateDisplayInfo(const QString &saveName)
} }
UserConnection_Information uci; UserConnection_Information uci;
QStringList data = uci.getServerInfo(saveName); QStringList _data = uci.getServerInfo(saveName);
bool savePasswordStatus = (data.at(5) == "1"); bool savePasswordStatus = (_data.at(5) == "1");
saveEdit->setText(data.at(0)); saveEdit->setText(_data.at(0));
hostEdit->setText(data.at(1)); hostEdit->setText(_data.at(1));
portEdit->setText(data.at(2)); portEdit->setText(_data.at(2));
playernameEdit->setText(data.at(3)); playernameEdit->setText(_data.at(3));
savePasswordCheckBox->setChecked(savePasswordStatus); savePasswordCheckBox->setChecked(savePasswordStatus);
if (savePasswordStatus) { if (savePasswordStatus) {
passwordEdit->setText(data.at(4)); passwordEdit->setText(_data.at(4));
} }
if (!data.at(6).isEmpty()) { if (!_data.at(6).isEmpty()) {
QString formattedLink = "<a href=\"" + data.at(6) + "\">" + data.at(6) + "</a>"; QString formattedLink = "<a href=\"" + _data.at(6) + "\">" + _data.at(6) + "</a>";
serverContactLabel->setText(tr("Webpage") + ":"); serverContactLabel->setText(tr("Webpage") + ":");
serverContactLink->setText(formattedLink); serverContactLink->setText(formattedLink);
} else { } else {

View File

@@ -5,6 +5,7 @@
#include "decklist.h" #include "decklist.h"
#include "main.h" #include "main.h"
#include "settingscache.h" #include "settingscache.h"
#include "trice_limits.h"
#include <QCheckBox> #include <QCheckBox>
#include <QCloseEvent> #include <QCloseEvent>
@@ -28,6 +29,7 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
nameLabel = new QLabel(tr("&Name:")); nameLabel = new QLabel(tr("&Name:"));
nameEdit = new QLineEdit(tr("Token")); nameEdit = new QLineEdit(tr("Token"));
nameEdit->setMaxLength(MAX_NAME_LENGTH);
nameEdit->selectAll(); nameEdit->selectAll();
connect(nameEdit, SIGNAL(textChanged(const QString &)), this, SLOT(updateSearch(const QString &))); connect(nameEdit, SIGNAL(textChanged(const QString &)), this, SLOT(updateSearch(const QString &)));
nameLabel->setBuddy(nameEdit); nameLabel->setBuddy(nameEdit);
@@ -45,10 +47,12 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
ptLabel = new QLabel(tr("&P/T:")); ptLabel = new QLabel(tr("&P/T:"));
ptEdit = new QLineEdit; ptEdit = new QLineEdit;
ptEdit->setMaxLength(MAX_NAME_LENGTH);
ptLabel->setBuddy(ptEdit); ptLabel->setBuddy(ptEdit);
annotationLabel = new QLabel(tr("&Annotation:")); annotationLabel = new QLabel(tr("&Annotation:"));
annotationEdit = new QLineEdit; annotationEdit = new QLineEdit;
annotationEdit->setMaxLength(MAX_NAME_LENGTH);
annotationLabel->setBuddy(annotationEdit); annotationLabel->setBuddy(annotationEdit);
destroyCheckBox = new QCheckBox(tr("&Destroy token when it leaves the table")); destroyCheckBox = new QCheckBox(tr("&Destroy token when it leaves the table"));

View File

@@ -4,6 +4,7 @@
#include "pending_command.h" #include "pending_command.h"
#include "settingscache.h" #include "settingscache.h"
#include "tab_room.h" #include "tab_room.h"
#include "trice_limits.h"
#include <QApplication> #include <QApplication>
#include <QCheckBox> #include <QCheckBox>
@@ -24,8 +25,8 @@ void DlgCreateGame::sharedCtor()
rememberGameSettings = new QCheckBox(tr("Re&member settings")); rememberGameSettings = new QCheckBox(tr("Re&member settings"));
descriptionLabel = new QLabel(tr("&Description:")); descriptionLabel = new QLabel(tr("&Description:"));
descriptionEdit = new QLineEdit; descriptionEdit = new QLineEdit;
descriptionEdit->setMaxLength(MAX_NAME_LENGTH);
descriptionLabel->setBuddy(descriptionEdit); descriptionLabel->setBuddy(descriptionEdit);
descriptionEdit->setMaxLength(60);
maxPlayersLabel = new QLabel(tr("P&layers:")); maxPlayersLabel = new QLabel(tr("P&layers:"));
maxPlayersEdit = new QSpinBox(); maxPlayersEdit = new QSpinBox();
@@ -57,6 +58,7 @@ void DlgCreateGame::sharedCtor()
passwordLabel = new QLabel(tr("&Password:")); passwordLabel = new QLabel(tr("&Password:"));
passwordEdit = new QLineEdit; passwordEdit = new QLineEdit;
passwordEdit->setMaxLength(MAX_NAME_LENGTH);
passwordLabel->setBuddy(passwordEdit); passwordLabel->setBuddy(passwordEdit);
onlyBuddiesCheckBox = new QCheckBox(tr("Only &buddies can join")); onlyBuddiesCheckBox = new QCheckBox(tr("Only &buddies can join"));
@@ -233,13 +235,13 @@ void DlgCreateGame::actOK()
cmd.set_join_as_judge(QApplication::keyboardModifiers() & Qt::ShiftModifier); cmd.set_join_as_judge(QApplication::keyboardModifiers() & Qt::ShiftModifier);
cmd.set_join_as_spectator(createGameAsSpectatorCheckBox->isChecked()); cmd.set_join_as_spectator(createGameAsSpectatorCheckBox->isChecked());
QString gameTypes = QString(); QString _gameTypes = QString();
QMapIterator<int, QRadioButton *> gameTypeCheckBoxIterator(gameTypeCheckBoxes); QMapIterator<int, QRadioButton *> gameTypeCheckBoxIterator(gameTypeCheckBoxes);
while (gameTypeCheckBoxIterator.hasNext()) { while (gameTypeCheckBoxIterator.hasNext()) {
gameTypeCheckBoxIterator.next(); gameTypeCheckBoxIterator.next();
if (gameTypeCheckBoxIterator.value()->isChecked()) { if (gameTypeCheckBoxIterator.value()->isChecked()) {
cmd.add_game_type_ids(gameTypeCheckBoxIterator.key()); cmd.add_game_type_ids(gameTypeCheckBoxIterator.key());
gameTypes += gameTypeCheckBoxIterator.value()->text() + ", "; _gameTypes += gameTypeCheckBoxIterator.value()->text() + ", ";
} }
} }
@@ -254,7 +256,7 @@ void DlgCreateGame::actOK()
SettingsCache::instance().setSpectatorsCanTalk(spectatorsCanTalkCheckBox->isChecked()); SettingsCache::instance().setSpectatorsCanTalk(spectatorsCanTalkCheckBox->isChecked());
SettingsCache::instance().setSpectatorsCanSeeEverything(spectatorsSeeEverythingCheckBox->isChecked()); SettingsCache::instance().setSpectatorsCanSeeEverything(spectatorsSeeEverythingCheckBox->isChecked());
SettingsCache::instance().setCreateGameAsSpectator(createGameAsSpectatorCheckBox->isChecked()); SettingsCache::instance().setCreateGameAsSpectator(createGameAsSpectatorCheckBox->isChecked());
SettingsCache::instance().setGameTypes(gameTypes); SettingsCache::instance().setGameTypes(_gameTypes);
} }
PendingCommand *pend = room->prepareRoomCommand(cmd); PendingCommand *pend = room->prepareRoomCommand(cmd);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(checkResponse(Response))); connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(checkResponse(Response)));

View File

@@ -1,5 +1,7 @@
#include "dlg_edit_avatar.h" #include "dlg_edit_avatar.h"
#include "trice_limits.h"
#include <QBuffer> #include <QBuffer>
#include <QDebug> #include <QDebug>
#include <QDialogButtonBox> #include <QDialogButtonBox>
@@ -72,9 +74,15 @@ QByteArray DlgEditAvatar::getImage()
return QByteArray(); return QByteArray();
} }
QByteArray ba; for (;;) {
QBuffer buffer(&ba); QByteArray ba;
buffer.open(QIODevice::WriteOnly); QBuffer buffer(&ba);
image.save(&buffer, "JPG"); buffer.open(QIODevice::WriteOnly);
return ba; image.save(&buffer, "JPG");
if (ba.length() > MAX_FILE_LENGTH) {
image = image.scaledToWidth(image.width() / 2); // divide the amount of pixels in four to get the size down
} else {
return ba;
}
}
} }

View File

@@ -1,6 +1,7 @@
#include "dlg_edit_password.h" #include "dlg_edit_password.h"
#include "settingscache.h" #include "settingscache.h"
#include "trice_limits.h"
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QGridLayout> #include <QGridLayout>
@@ -12,6 +13,7 @@ DlgEditPassword::DlgEditPassword(QWidget *parent) : QDialog(parent)
{ {
oldPasswordLabel = new QLabel(tr("Old password:")); oldPasswordLabel = new QLabel(tr("Old password:"));
oldPasswordEdit = new QLineEdit(); oldPasswordEdit = new QLineEdit();
oldPasswordEdit->setMaxLength(MAX_NAME_LENGTH);
auto &servers = SettingsCache::instance().servers(); auto &servers = SettingsCache::instance().servers();
if (servers.getSavePassword()) { if (servers.getSavePassword()) {
@@ -23,11 +25,13 @@ DlgEditPassword::DlgEditPassword(QWidget *parent) : QDialog(parent)
newPasswordLabel = new QLabel(tr("New password:")); newPasswordLabel = new QLabel(tr("New password:"));
newPasswordEdit = new QLineEdit(); newPasswordEdit = new QLineEdit();
newPasswordEdit->setMaxLength(MAX_NAME_LENGTH);
newPasswordLabel->setBuddy(newPasswordLabel); newPasswordLabel->setBuddy(newPasswordLabel);
newPasswordEdit->setEchoMode(QLineEdit::Password); newPasswordEdit->setEchoMode(QLineEdit::Password);
newPasswordLabel2 = new QLabel(tr("Confirm new password:")); newPasswordLabel2 = new QLabel(tr("Confirm new password:"));
newPasswordEdit2 = new QLineEdit(); newPasswordEdit2 = new QLineEdit();
newPasswordEdit2->setMaxLength(MAX_NAME_LENGTH);
newPasswordLabel2->setBuddy(newPasswordLabel2); newPasswordLabel2->setBuddy(newPasswordLabel2);
newPasswordEdit2->setEchoMode(QLineEdit::Password); newPasswordEdit2->setEchoMode(QLineEdit::Password);
@@ -55,7 +59,11 @@ DlgEditPassword::DlgEditPassword(QWidget *parent) : QDialog(parent)
void DlgEditPassword::actOk() void DlgEditPassword::actOk()
{ {
if (newPasswordEdit->text() != newPasswordEdit2->text()) { // TODO this stuff should be using qvalidators
if (newPasswordEdit->text().length() < 8) {
QMessageBox::critical(this, tr("Error"), tr("Your password is too short."));
return;
} else if (newPasswordEdit->text() != newPasswordEdit2->text()) {
QMessageBox::warning(this, tr("Error"), tr("The new passwords don't match.")); QMessageBox::warning(this, tr("Error"), tr("The new passwords don't match."));
return; return;
} }

View File

@@ -2,7 +2,9 @@
#include "carddatabase.h" #include "carddatabase.h"
#include "carddatabasemodel.h" #include "carddatabasemodel.h"
#include "gettextwithmax.h"
#include "main.h" #include "main.h"
#include "trice_limits.h"
#include <QAction> #include <QAction>
#include <QComboBox> #include <QComboBox>
@@ -23,6 +25,7 @@ DlgEditTokens::DlgEditTokens(QWidget *parent) : QDialog(parent), currentCard(nul
{ {
nameLabel = new QLabel(tr("&Name:")); nameLabel = new QLabel(tr("&Name:"));
nameEdit = new QLineEdit; nameEdit = new QLineEdit;
nameEdit->setMaxLength(MAX_NAME_LENGTH);
nameEdit->setEnabled(false); nameEdit->setEnabled(false);
nameLabel->setBuddy(nameEdit); nameLabel->setBuddy(nameEdit);
@@ -40,11 +43,13 @@ DlgEditTokens::DlgEditTokens(QWidget *parent) : QDialog(parent), currentCard(nul
ptLabel = new QLabel(tr("&P/T:")); ptLabel = new QLabel(tr("&P/T:"));
ptEdit = new QLineEdit; ptEdit = new QLineEdit;
ptEdit->setMaxLength(MAX_NAME_LENGTH);
ptLabel->setBuddy(ptEdit); ptLabel->setBuddy(ptEdit);
connect(ptEdit, SIGNAL(textChanged(QString)), this, SLOT(ptChanged(QString))); connect(ptEdit, SIGNAL(textChanged(QString)), this, SLOT(ptChanged(QString)));
annotationLabel = new QLabel(tr("&Annotation:")); annotationLabel = new QLabel(tr("&Annotation:"));
annotationEdit = new QLineEdit; annotationEdit = new QLineEdit;
annotationEdit->setMaxLength(MAX_NAME_LENGTH);
annotationLabel->setBuddy(annotationEdit); annotationLabel->setBuddy(annotationEdit);
connect(annotationEdit, SIGNAL(textChanged(QString)), this, SLOT(annotationChanged(QString))); connect(annotationEdit, SIGNAL(textChanged(QString)), this, SLOT(annotationChanged(QString)));
@@ -142,9 +147,8 @@ void DlgEditTokens::tokenSelectionChanged(const QModelIndex &current, const QMod
void DlgEditTokens::actAddToken() void DlgEditTokens::actAddToken()
{ {
QString name; QString name;
bool askAgain = true; for (;;) {
do { name = getTextWithMax(this, tr("Add token"), tr("Please enter the name of the token:"));
name = QInputDialog::getText(this, tr("Add token"), tr("Please enter the name of the token:"));
if (name.isEmpty()) if (name.isEmpty())
return; return;
if (databaseModel->getDatabase()->getCard(name)) { if (databaseModel->getDatabase()->getCard(name)) {
@@ -152,9 +156,9 @@ void DlgEditTokens::actAddToken()
tr("The chosen name conflicts with an existing card or token.\nMake sure to enable " tr("The chosen name conflicts with an existing card or token.\nMake sure to enable "
"the 'Token' set in the \"Manage sets\" dialog to display them correctly.")); "the 'Token' set in the \"Manage sets\" dialog to display them correctly."));
} else { } else {
askAgain = false; break;
} }
} while (askAgain); }
QString setName = CardDatabase::TOKENS_SETNAME; QString setName = CardDatabase::TOKENS_SETNAME;
CardInfoPerSetMap sets; CardInfoPerSetMap sets;

View File

@@ -1,6 +1,7 @@
#include "dlg_edit_user.h" #include "dlg_edit_user.h"
#include "settingscache.h" #include "settingscache.h"
#include "trice_limits.h"
#include <QDebug> #include <QDebug>
#include <QDialogButtonBox> #include <QDialogButtonBox>
@@ -12,6 +13,7 @@ DlgEditUser::DlgEditUser(QWidget *parent, QString email, QString country, QStrin
{ {
emailLabel = new QLabel(tr("Email:")); emailLabel = new QLabel(tr("Email:"));
emailEdit = new QLineEdit(); emailEdit = new QLineEdit();
emailEdit->setMaxLength(MAX_NAME_LENGTH);
emailLabel->setBuddy(emailEdit); emailLabel->setBuddy(emailEdit);
emailEdit->setText(email); emailEdit->setText(email);
@@ -33,6 +35,7 @@ DlgEditUser::DlgEditUser(QWidget *parent, QString email, QString country, QStrin
realnameLabel = new QLabel(tr("Real name:")); realnameLabel = new QLabel(tr("Real name:"));
realnameEdit = new QLineEdit(); realnameEdit = new QLineEdit();
realnameEdit->setMaxLength(MAX_NAME_LENGTH);
realnameLabel->setBuddy(realnameEdit); realnameLabel->setBuddy(realnameEdit);
realnameEdit->setText(realName); realnameEdit->setText(realName);

View File

@@ -21,10 +21,6 @@ public:
{ {
return emailEdit->text(); return emailEdit->text();
} }
int getGender() const
{
return -1;
} // This will return GenderUnknown for protocol purposes.
QString getCountry() const QString getCountry() const
{ {
return countryEdit->currentIndex() == 0 ? "" : countryEdit->currentText(); return countryEdit->currentIndex() == 0 ? "" : countryEdit->currentText();

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