[Docs] Move documentation sources to docs/ and include CONTRIBUTING (#7584)
Rename doc/ to docs/ so GitHub picks up docs/CONTRIBUTING.md and Doxygen keeps including it, and update all references to the old layout: - Doxygen output now goes to docs-output/, exposed by .gitignore - docs workflow triggers on docs/** and publishes docs-output/html - theme submodule moved to docs/doxygen/theme - schema URLs in both XML parsers point at docs/xsd/ - README, developer docs, desktop workflow filters, .dockerignore and gcovr config updated for the new paths The v3 card database schema is kept as docs/xsd/cards_v3.xsd.
270
docs/doxygen/DoxygenLayout.xml
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doxygenlayout version="2.0">
|
||||
<!-- Generated by doxygen 1.14.0 -->
|
||||
<!-- Navigation index tabs for HTML output -->
|
||||
<navindex>
|
||||
<tab type="mainpage" visible="yes" title=""/>
|
||||
<tab type="pages" visible="yes" title="" intro=""/>
|
||||
<tab type="topics" visible="yes" title="" intro=""/>
|
||||
<tab type="usergroup" title="Code Reference">
|
||||
<tab type="modules" visible="no" title="" intro="">
|
||||
<tab type="modulelist" visible="yes" title="" intro=""/>
|
||||
<tab type="modulemembers" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="namespaces" visible="yes" title="">
|
||||
<tab type="namespacelist" visible="yes" title="" intro=""/>
|
||||
<tab type="namespacemembers" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="concepts" visible="no" title=""/>
|
||||
<tab type="interfaces" visible="no" title="">
|
||||
<tab type="interfacelist" visible="yes" title="" intro=""/>
|
||||
<tab type="interfaceindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
<tab type="interfacehierarchy" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="classes" visible="yes" title="">
|
||||
<tab type="classlist" visible="yes" title="" intro=""/>
|
||||
<tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
<tab type="hierarchy" visible="yes" title="" intro=""/>
|
||||
<tab type="classmembers" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="structs" visible="no" title="">
|
||||
<tab type="structlist" visible="yes" title="" intro=""/>
|
||||
<tab type="structindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
</tab>
|
||||
<tab type="exceptions" visible="no" title="">
|
||||
<tab type="exceptionlist" visible="yes" title="" intro=""/>
|
||||
<tab type="exceptionindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
<tab type="exceptionhierarchy" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="files" visible="yes" title="">
|
||||
<tab type="filelist" visible="yes" title="" intro=""/>
|
||||
<tab type="globals" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="examples" visible="no" title="" intro=""/>
|
||||
</tab>
|
||||
</navindex>
|
||||
|
||||
<!-- Layout definition for a class page -->
|
||||
<class>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_HEADERFILE"/>
|
||||
<inheritancegraph visible="yes"/>
|
||||
<collaborationgraph visible="yes"/>
|
||||
<memberdecl>
|
||||
<nestedclasses visible="yes" title=""/>
|
||||
<publictypes visible="yes" title=""/>
|
||||
<services visible="yes" title=""/>
|
||||
<interfaces visible="yes" title=""/>
|
||||
<publicslots visible="yes" title=""/>
|
||||
<signals visible="yes" title=""/>
|
||||
<publicmethods visible="yes" title=""/>
|
||||
<publicstaticmethods visible="yes" title=""/>
|
||||
<publicattributes visible="yes" title=""/>
|
||||
<publicstaticattributes visible="yes" title=""/>
|
||||
<protectedtypes visible="yes" title=""/>
|
||||
<protectedslots visible="yes" title=""/>
|
||||
<protectedmethods visible="yes" title=""/>
|
||||
<protectedstaticmethods visible="yes" title=""/>
|
||||
<protectedattributes visible="yes" title=""/>
|
||||
<protectedstaticattributes visible="yes" title=""/>
|
||||
<packagetypes visible="yes" title=""/>
|
||||
<packagemethods visible="yes" title=""/>
|
||||
<packagestaticmethods visible="yes" title=""/>
|
||||
<packageattributes visible="yes" title=""/>
|
||||
<packagestaticattributes visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
<events visible="yes" title=""/>
|
||||
<privatetypes visible="yes" title=""/>
|
||||
<privateslots visible="yes" title=""/>
|
||||
<privatemethods visible="yes" title=""/>
|
||||
<privatestaticmethods visible="yes" title=""/>
|
||||
<privateattributes visible="yes" title=""/>
|
||||
<privatestaticattributes visible="yes" title=""/>
|
||||
<friends visible="yes" title=""/>
|
||||
<related visible="yes" title="" subtitle=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<services visible="yes" title=""/>
|
||||
<interfaces visible="yes" title=""/>
|
||||
<constructors visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<related visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
<events visible="yes" title=""/>
|
||||
</memberdef>
|
||||
<allmemberslink visible="yes"/>
|
||||
<usedfiles visible="$SHOW_USED_FILES"/>
|
||||
<authorsection visible="yes"/>
|
||||
</class>
|
||||
|
||||
<!-- Layout definition for a namespace page -->
|
||||
<namespace>
|
||||
<briefdescription visible="yes"/>
|
||||
<memberdecl>
|
||||
<nestednamespaces visible="yes" title=""/>
|
||||
<constantgroups visible="yes" title=""/>
|
||||
<interfaces visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<concepts visible="yes" title=""/>
|
||||
<structs visible="yes" title=""/>
|
||||
<exceptions visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<sequences visible="yes" title=""/>
|
||||
<dictionaries visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<sequences visible="yes" title=""/>
|
||||
<dictionaries visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
</memberdef>
|
||||
<authorsection visible="yes"/>
|
||||
</namespace>
|
||||
|
||||
<!-- Layout definition for a concept page -->
|
||||
<concept>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_HEADERFILE"/>
|
||||
<definition visible="yes" title=""/>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
<authorsection visible="yes"/>
|
||||
</concept>
|
||||
|
||||
<!-- Layout definition for a file page -->
|
||||
<file>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_INCLUDE_FILES"/>
|
||||
<includegraph visible="yes"/>
|
||||
<includedbygraph visible="yes"/>
|
||||
<sourcelink visible="yes"/>
|
||||
<memberdecl>
|
||||
<interfaces visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<structs visible="yes" title=""/>
|
||||
<exceptions visible="yes" title=""/>
|
||||
<namespaces visible="yes" title=""/>
|
||||
<concepts visible="yes" title=""/>
|
||||
<constantgroups visible="yes" title=""/>
|
||||
<defines visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<sequences visible="yes" title=""/>
|
||||
<dictionaries visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses visible="yes" title=""/>
|
||||
<defines visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<sequences visible="yes" title=""/>
|
||||
<dictionaries visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
</memberdef>
|
||||
<authorsection/>
|
||||
</file>
|
||||
|
||||
<!-- Layout definition for a group page -->
|
||||
<group>
|
||||
<briefdescription visible="yes"/>
|
||||
<groupgraph visible="yes"/>
|
||||
<memberdecl>
|
||||
<nestedgroups visible="yes" title=""/>
|
||||
<modules visible="yes" title=""/>
|
||||
<dirs visible="yes" title=""/>
|
||||
<files visible="yes" title=""/>
|
||||
<namespaces visible="yes" title=""/>
|
||||
<concepts visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<defines visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<sequences visible="yes" title=""/>
|
||||
<dictionaries visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<enumvalues visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<signals visible="yes" title=""/>
|
||||
<publicslots visible="yes" title=""/>
|
||||
<protectedslots visible="yes" title=""/>
|
||||
<privateslots visible="yes" title=""/>
|
||||
<events visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
<friends visible="yes" title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
<memberdef>
|
||||
<pagedocs/>
|
||||
<inlineclasses visible="yes" title=""/>
|
||||
<defines visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<sequences visible="yes" title=""/>
|
||||
<dictionaries visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<enumvalues visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<signals visible="yes" title=""/>
|
||||
<publicslots visible="yes" title=""/>
|
||||
<protectedslots visible="yes" title=""/>
|
||||
<privateslots visible="yes" title=""/>
|
||||
<events visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
<friends visible="yes" title=""/>
|
||||
</memberdef>
|
||||
<authorsection visible="yes"/>
|
||||
</group>
|
||||
|
||||
<!-- Layout definition for a C++20 module page -->
|
||||
<module>
|
||||
<briefdescription visible="yes"/>
|
||||
<exportedmodules visible="yes"/>
|
||||
<memberdecl>
|
||||
<concepts visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<membergroups visible="yes" title=""/>
|
||||
</memberdecl>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
<memberdecl>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
</module>
|
||||
|
||||
<!-- Layout definition for a directory page -->
|
||||
<directory>
|
||||
<briefdescription visible="yes"/>
|
||||
<directorygraph visible="yes"/>
|
||||
<memberdecl>
|
||||
<dirs visible="yes"/>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
</directory>
|
||||
</doxygenlayout>
|
||||
28
docs/doxygen/README.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
## Building Doxygen Documentation
|
||||
|
||||
We use [Doxygen](https://www.doxygen.nl) for our code documentation.
|
||||
After installing the tool, you can build a HTML webpage into the `/docs-output` folder according to our [configuration](https://github.com/Cockatrice/Cockatrice/blob/master/Doxyfile):
|
||||
```bash
|
||||
doxygen Doxyfile
|
||||
```
|
||||
Open the `index.html` file in the `/docs-output/html/` folder to browse the result locally.
|
||||
There is also an online version we host at the Cockatrice webpage: [cockatrice.github.io/docs](https://cockatrice.github.io/docs)
|
||||
|
||||
Doxygen's own documenation can be found at [www.doxygen.nl/manual](https://www.doxygen.nl/manual).
|
||||
|
||||
<br>
|
||||
|
||||
## Doxygen Documentation Theme
|
||||
|
||||
Required changes to the `Doxyfile` config from the [theme docs](https://jothepro.github.io/doxygen-awesome-css/index.html#autotoc_md16) to make [doxygen-awesome-css](https://github.com/jothepro/doxygen-awesome-css) work:
|
||||
```
|
||||
HTML_EXTRA_STYLESHEET = doxygen-awesome.css # Main CSS file of the theme
|
||||
GENERATE_TREEVIEW = YES # Optional, also works without
|
||||
HTML_COLORSTYLE = LIGHT # Required with doxygen-awesome-css theme, Auto Dark Mode will still work
|
||||
DISABLE_INDEX = NO # YES is bugged in the theme, see jothepro/doxygen-awesome-css/issues/201
|
||||
FULL_SIDEBAR = NO # Required for doxygen-awesome-css theme
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
Cockatrice dedicated color adjustments are configured in the [cockatrice_docs_style.css](https://github.com/Cockatrice/Cockatrice/blob/master/docs/doxygen/css/cockatrice_docs_style.css) file that uses & overrides the theme definitions.
|
||||
33
docs/doxygen/css/cockatrice_docs_style.css
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
|
||||
See "Awesome Doxygen CSS" theme docs:
|
||||
https://jothepro.github.io/doxygen-awesome-css/md_docs_2customization.html#autotoc_md36
|
||||
|
||||
Adjustments here are based on the css file of the theme and variables defined there.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/* Light Mode overrides */
|
||||
html {
|
||||
--primary-color: #33a946;
|
||||
--primary-dark-color: #33a946;
|
||||
--primary-light-color: #33a946;
|
||||
}
|
||||
|
||||
/* Dark Mode overrides */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html:not(.light-mode) {
|
||||
--primary-color: #33a946;
|
||||
--primary-dark-color: #33a946;
|
||||
--primary-light-color: #33a946;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark Mode overrides, defined twice to support both the dark-mode without and with doxygen-awesome-darkmode-toggle.js */
|
||||
html.dark-mode {
|
||||
color-scheme: dark;
|
||||
--primary-color: #33a946;
|
||||
--primary-dark-color: #33a946;
|
||||
--primary-light-color: #33a946;
|
||||
}
|
||||
88
docs/doxygen/css/doxygen_style_old.css
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/* === Accent Tuning for Doxygen Dark Mode === */
|
||||
|
||||
/* Main accent color */
|
||||
:root {
|
||||
--main-accent: #33a946;
|
||||
}
|
||||
|
||||
/* Links */
|
||||
a, a:visited {
|
||||
color: var(--main-accent) !important;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #4fd467 !important; /* lighter green */
|
||||
}
|
||||
|
||||
/* Project title */
|
||||
#projectname {
|
||||
color: var(--main-accent) !important;
|
||||
}
|
||||
|
||||
/* Current sidebar item */
|
||||
#side-nav .selected {
|
||||
border-left: 3px solid var(--main-accent) !important;
|
||||
}
|
||||
|
||||
/* Tabs */
|
||||
.tablist li.current {
|
||||
background: var(--main-accent) !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
/* Search box focus */
|
||||
#MSearchField:focus {
|
||||
outline: 1px solid var(--main-accent) !important;
|
||||
}
|
||||
|
||||
/* Code block border highlight */
|
||||
pre, code {
|
||||
border: 1px solid #333;
|
||||
}
|
||||
|
||||
pre.fragment {
|
||||
border-color: var(--main-accent) !important;
|
||||
}
|
||||
|
||||
/* Graph toggle (caller / callee two-button) */
|
||||
.graph-toggle {
|
||||
display: inline-flex;
|
||||
margin: 0.5em 0;
|
||||
border: 1px solid #444;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
background: #111;
|
||||
}
|
||||
|
||||
.graph-toggle button {
|
||||
flex: 1 1 50%;
|
||||
padding: 0.35em 0.8em;
|
||||
background: transparent;
|
||||
color: #aaa;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
.graph-toggle button:hover:not(:disabled) {
|
||||
background: #222;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.graph-toggle button.active {
|
||||
background: var(--main-accent);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.graph-toggle button:disabled,
|
||||
.graph-toggle button.disabled {
|
||||
opacity: 0.45;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* small accessibility focus ring */
|
||||
.graph-toggle button:focus {
|
||||
outline: 2px solid rgba(51, 169, 70, 0.3);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
10
docs/doxygen/css/hide_nav_sync.css
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
/* hide navigation sync control and icons */
|
||||
#nav-sync,
|
||||
#nav-sync * {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
div.nav-sync-icon,
|
||||
div.nav-sync-icon * {
|
||||
display: none !important;
|
||||
}
|
||||
285
docs/doxygen/filters/proto2cpp.py
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
#!/usr/bin/env python
|
||||
##
|
||||
# Doxygen filter for Google Protocol Buffers .proto files.
|
||||
# This script converts .proto files into C++ style ones
|
||||
# and prints the output to standard output.
|
||||
#
|
||||
# version 0.8-beta OSI
|
||||
#
|
||||
# How to enable this filter in Doxygen:
|
||||
# 1. Generate Doxygen configuration file with command 'doxygen -g <filename>'
|
||||
# e.g. doxygen -g doxyfile
|
||||
# 2. In the Doxygen configuration file, find FILE_PATTERNS and add *.proto
|
||||
# FILE_PATTERNS = *.proto
|
||||
# 3. In the Doxygen configuration file, find EXTENSION_MAPPING and add proto=C++
|
||||
# EXTENSION_MAPPING = proto=C++
|
||||
# 4. In the Doxygen configuration file, find INPUT_FILTER and add this script
|
||||
# INPUT_FILTER = "python proto2cpp.py"
|
||||
# 5. Run Doxygen with the modified configuration
|
||||
# doxygen doxyfile
|
||||
#
|
||||
#
|
||||
# Version 0.8 2018 Bugfix regarding long comments, remove typo
|
||||
# Version 0.7 2018 Bugfix and extensions have been made by Open Simulation Interface (OSI) Carsten Kuebler https://github.com/OpenSimulationInterface,
|
||||
# Copyright (C) 2016 Regents of the University of California https://github.com/vgteam/vg
|
||||
# Copyright (C) 2012-2015 Timo Marjoniemi https://sourceforge.net/p/proto2cpp/wiki/Home/
|
||||
# All rights reserved.
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
##
|
||||
|
||||
import fnmatch
|
||||
import inspect
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
## Class for converting Google Protocol Buffers .proto files into C++ style output to enable Doxygen usage.
|
||||
##
|
||||
## The C++ style output is printed into standard output.<br />
|
||||
## There are three different logging levels for the class:
|
||||
## <ul><li>#logNone: do not log anything</li>
|
||||
## <li>#logErrors: log errors only</li>
|
||||
## <li>#logAll: log everything</li></ul>
|
||||
## Logging level is determined by \c #logLevel.<br />
|
||||
## Error logs are written to file determined by \c #errorLogFile.<br />
|
||||
## Debug logs are written to file determined by \c #logFile.
|
||||
#
|
||||
class proto2cpp:
|
||||
|
||||
## Logging level: do not log anything.
|
||||
logNone = 0
|
||||
## Logging level: log errors only.
|
||||
logErrors = 1
|
||||
## Logging level: log everything.
|
||||
logAll = 2
|
||||
|
||||
## Constructor
|
||||
#
|
||||
def __init__(self):
|
||||
## Debug log file name.
|
||||
self.logFile = "proto2cpp.log"
|
||||
## Error log file name.
|
||||
self.errorLogFile = "proto2cpp.error.log"
|
||||
## Logging level.
|
||||
self.logLevel = self.logNone
|
||||
|
||||
## Handles a file.
|
||||
##
|
||||
## If @p fileName has .proto suffix, it is processed through parseFile().
|
||||
## Otherwise it is printed to stdout as is except for file \c proto2cpp.py without
|
||||
## path since it's the script given to python for processing.
|
||||
##
|
||||
## @param fileName Name of the file to be handled.
|
||||
#
|
||||
def handleFile(self, fileName):
|
||||
if fnmatch.fnmatch(filename, '*.proto'):
|
||||
self.log('\nXXXXXXXXXX\nXX ' + filename + '\nXXXXXXXXXX\n\n')
|
||||
# Open the file. Use try to detect whether or not we have an actual file.
|
||||
if (sys.version_info >= (3, 0)):
|
||||
try:
|
||||
with open(filename, 'r', encoding='utf8') as inputFile:
|
||||
self.parseFile(inputFile)
|
||||
pass
|
||||
except IOError as e:
|
||||
self.logError('the file ' + filename + ' could not be opened for reading')
|
||||
else:
|
||||
# Python 2 code in this block
|
||||
try:
|
||||
with open(filename, 'r') as inputFile:
|
||||
self.parseFile(inputFile)
|
||||
pass
|
||||
except IOError as e:
|
||||
self.logError('the file ' + filename + ' could not be opened for reading')
|
||||
|
||||
elif not fnmatch.fnmatch(filename, os.path.basename(inspect.getfile(inspect.currentframe()))):
|
||||
self.log('\nXXXXXXXXXX\nXX ' + filename + '\nXXXXXXXXXX\n\n')
|
||||
if (sys.version_info > (3, 0)):
|
||||
try:
|
||||
with open(filename, 'r', encoding='utf8') as theFile:
|
||||
output = ''
|
||||
for theLine in theFile:
|
||||
output += theLine
|
||||
print(output)
|
||||
self.log(output)
|
||||
pass
|
||||
except IOError as e:
|
||||
self.logError('the file ' + filename + ' could not be opened for reading')
|
||||
else:
|
||||
# Python 2 code in this block
|
||||
try:
|
||||
with open(filename, 'r') as theFile:
|
||||
output = ''
|
||||
for theLine in theFile:
|
||||
output += theLine
|
||||
print(output)
|
||||
self.log(output)
|
||||
pass
|
||||
except IOError as e:
|
||||
self.logError('the file ' + filename + ' could not be opened for reading')
|
||||
|
||||
else:
|
||||
self.log('\nXXXXXXXXXX\nXX ' + filename + ' --skipped--\nXXXXXXXXXX\n\n')
|
||||
|
||||
## Parser function.
|
||||
##
|
||||
## The function takes a .proto file object as input
|
||||
## parameter and modifies the contents into C++ style.
|
||||
## The modified data is printed into standard output.
|
||||
##
|
||||
## @param inputFile Input file object
|
||||
#
|
||||
def parseFile(self, inputFile):
|
||||
# Go through the input file line by line.
|
||||
isEnum = False
|
||||
isPackage = False
|
||||
isMultilineComment = False
|
||||
# This variable is here as a workaround for not getting extra line breaks (each line
|
||||
# ends with a line separator and print() method will add another one).
|
||||
# We will be adding lines into this var and then print the var out at the end.
|
||||
theOutput = ''
|
||||
for line in inputFile:
|
||||
# Search for comment ("//") and add one more slash character ("/") to the comment
|
||||
# block to make Doxygen detect it.
|
||||
matchComment = re.search("//", line)
|
||||
# Search for semicolon and if one is found before comment, add a third slash character
|
||||
# ("/") and a smaller than ("<") character to the comment to make Doxygen detect it.
|
||||
matchSemicolon = re.search(";", line)
|
||||
if matchSemicolon is not None and (matchComment is not None and matchSemicolon.start() < matchComment.start()):
|
||||
comment = "///<" + line[matchComment.end():]
|
||||
# Replace '.' in nested message references with '::'
|
||||
# don't work for multi-nested references and generates problems with URLs and acronyms
|
||||
#comment = re.sub(r'\s(\w+)\.(\w+)\s', r' \1::\2 ', comment)
|
||||
line = line[:matchComment.start()]
|
||||
elif matchComment is not None:
|
||||
if isMultilineComment:
|
||||
comment = " * " + line[matchComment.end():]
|
||||
else:
|
||||
comment = "/** " + line[matchComment.end():]
|
||||
isMultilineComment = True
|
||||
# replace '.' in nested message references with '::'
|
||||
# don't work for multi-nested references and generates problems with URLs and acronyms
|
||||
#comment = re.sub(r'\s(\w+)\.(\w+)\s', r' \1::\2 ', comment)
|
||||
line = line[:matchComment.start()]
|
||||
else:
|
||||
comment = ""
|
||||
|
||||
# End multiline comment, if there is no comment or if there are some chars before the comment.
|
||||
if (matchComment is None or len(line.strip())>0) and isMultilineComment:
|
||||
theOutput += " */\n"
|
||||
isMultilineComment = False
|
||||
|
||||
# line = line.replace(".", "::") but not in quoted strings (Necessary for import statement)
|
||||
line = re.sub(r'\.(?=(?:[^"]*"[^"]*")*[^"]*$)',r'::',line)
|
||||
|
||||
# Search for " option ...;", remove it
|
||||
line = re.sub(r'\boption\b[^;]+;', r'', line)
|
||||
|
||||
# Search for " package ", make a namespace
|
||||
matchPackage = re.search(r"\bpackage\b", line)
|
||||
if matchPackage is not None:
|
||||
isPackage = True
|
||||
# Convert to C++-style separator and block instead of statement
|
||||
line = "namespace" + line[:matchPackage.start()] + line[matchPackage.end():].replace(";", " {")
|
||||
|
||||
# Search for " repeated " fields and make them ...
|
||||
#matchRepeated = re.search(r"\brepeated\b", line)
|
||||
#if matchRepeated is not None:
|
||||
# # Convert
|
||||
# line = re.sub(r'\brepeated\s+(\S+)', r' repeated \1', line)
|
||||
|
||||
# Search for "enum", start changing all semicolons (";") to commas (",").
|
||||
matchEnum = re.search(r"\benum\b", line)
|
||||
if matchEnum is not None:
|
||||
isEnum = True
|
||||
|
||||
# Search semicolon if we have detected an enum, and replace semicolon with comma.
|
||||
if isEnum is True and matchSemicolon is not None:
|
||||
line = line.replace(";", ",")
|
||||
|
||||
# Search for a closing brace.
|
||||
matchClosingBrace = re.search("}", line)
|
||||
if isEnum is True and matchClosingBrace is not None:
|
||||
line = line[:matchClosingBrace.start()] + "};" + line[matchClosingBrace.end():]
|
||||
isEnum = False
|
||||
elif isEnum is False and matchClosingBrace is not None:
|
||||
# Message (to be struct) ends => add semicolon so that it'll
|
||||
# be a proper C(++) struct and Doxygen will handle it correctly.
|
||||
line = line[:matchClosingBrace.start()] + "};" + line[matchClosingBrace.end():]
|
||||
|
||||
# Replacements change start of comment...
|
||||
matchMsg = re.search(r"\bmessage\b", line)
|
||||
if matchMsg is not None:
|
||||
line = line[:matchMsg.start()] + "struct" + line[matchMsg.end():]
|
||||
|
||||
# Replacements change start of comment...
|
||||
matchExt = re.search(r"\bextend\b", line)
|
||||
if matchExt is not None:
|
||||
a_extend = line[matchExt.end():]
|
||||
matchName = re.search(r"\b\w[\S:]*\b", a_extend)
|
||||
if matchName is not None:
|
||||
name = a_extend[matchName.start():matchName.end()]
|
||||
name = re.sub(r'\w+::',r'',name)
|
||||
a_extend = a_extend[:matchName.start()] + name + ": public " + a_extend[matchName.start():]
|
||||
else:
|
||||
a_extend = "_Dummy: public " + a_extend;
|
||||
line = line[:matchExt.start()] + "struct " + a_extend
|
||||
|
||||
theOutput += (line.strip() + ' ' + comment.strip()).strip() + '\n'
|
||||
|
||||
if isPackage:
|
||||
# Close the package namespace
|
||||
theOutput += "}"
|
||||
isPackage = False
|
||||
|
||||
# Now that we've got all lines in the string let's split the lines and print out
|
||||
# one by one.
|
||||
# This is a workaround to get rid of extra empty line at the end which print() method adds.
|
||||
lines = theOutput.splitlines()
|
||||
for line in lines:
|
||||
if len(line) > 0:
|
||||
print(line) # Add linebreak to generate documentation correct.
|
||||
self.log(line + '\n')
|
||||
|
||||
## Writes @p string to log file.
|
||||
##
|
||||
## #logLevel must be #logAll or otherwise the logging is skipped.
|
||||
##
|
||||
## @param string String to be written to log file.
|
||||
#
|
||||
def log(self, string):
|
||||
if self.logLevel >= self.logAll:
|
||||
with open(self.logFile, 'a') as theFile:
|
||||
theFile.write(string)
|
||||
|
||||
## Writes @p string to error log file.
|
||||
##
|
||||
## #logLevel must be #logError or #logAll or otherwise the logging is skipped.
|
||||
##
|
||||
## @param string String to be written to error log file.
|
||||
#
|
||||
def logError(self, string):
|
||||
if self.logLevel >= self.logError:
|
||||
with open(self.errorLogFile, 'a') as theFile:
|
||||
theFile.write(string)
|
||||
|
||||
converter = proto2cpp()
|
||||
# Doxygen will give us the file names
|
||||
for filename in sys.argv[1:]:
|
||||
converter.handleFile(filename)
|
||||
|
||||
# end of file
|
||||
724
docs/doxygen/groups/doc_groups.dox
Normal file
|
|
@ -0,0 +1,724 @@
|
|||
/** @file doc_groups.dox
|
||||
* @brief Doxygen group definitions for Cockatrice.
|
||||
*
|
||||
* This file defines the documentation groups and hierarchy used
|
||||
* throughout the Cockatrice codebase. Reference these groups with @ingroup.
|
||||
*/
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Core */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @defgroup Core Core
|
||||
* @brief Core utilities, shared types, and infrastructure.
|
||||
*
|
||||
* Provides the foundational building blocks used throughout Cockatrice.
|
||||
* This includes logging facilities, settings management, serialization helpers,
|
||||
* common enumerations, and general-purpose utility classes and functions.
|
||||
*/
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* UI */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @defgroup UI User Interface
|
||||
* @brief Graphical interface components built with Qt.
|
||||
*
|
||||
* Includes high-level widgets, dialogs, models, and editors that form
|
||||
* the interactive frontend of Cockatrice.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup Widgets Widgets
|
||||
* @ingroup UI
|
||||
* @brief General-purpose Qt widgets.
|
||||
*
|
||||
* A collection of reusable widgets used throughout the application,
|
||||
* such as CardInfoPictureFoilWidget, DeckAnalyticsWidget, and others.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup Dialogs Dialogs
|
||||
* @ingroup Widgets
|
||||
* @brief Application dialogs.
|
||||
*
|
||||
* Includes dialogs for card prices, DeckList import/export,
|
||||
* settings, and other modal user interactions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup NetworkDialogs Networking Dialogs
|
||||
* @ingroup Dialogs
|
||||
* @ingroup NetworkingWidgets
|
||||
* @brief Dialogs related to a RemoteClient interacting with a Server.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup ClientUpdateDialogs Client Update Dialogs
|
||||
* @ingroup NetworkDialogs
|
||||
* @brief Dialogs relating to the client updating.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup CardDatabaseUpdateDialogs Card Database Update Dialogs
|
||||
* @ingroup NetworkDialogs
|
||||
* @ingroup CardDatabaseWidgets
|
||||
* @brief Dialogs relating to the CardDatabase updating.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup ConnectionDialogs Connection Dialogs
|
||||
* @ingroup NetworkDialogs
|
||||
* @brief Dialogs relating to the RemoteClient%s connection to a Server.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup AccountDialogs Account Dialogs
|
||||
* @ingroup NetworkDialogs
|
||||
* @brief Dialogs relating to account information on a Server.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup RoomDialogs
|
||||
* @ingroup NetworkDialogs
|
||||
* @brief Dialogs relating to a RemoteClient interacting with a Room on a Server.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup ServerLogDialogs Server Log Dialogs
|
||||
* @ingroup NetworkDialogs
|
||||
* @brief Dialogs relating to a RemoteClient interacting a Server%'s logs.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup Tabs Tabs
|
||||
* @ingroup Widgets
|
||||
* @brief Tabbed UI components.
|
||||
*
|
||||
* Provides tab-based interfaces for organizing and navigating
|
||||
* between multiple views in the application.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup DeckEditorTabs Deck Editor Tabs
|
||||
* @ingroup Tabs
|
||||
* @ingroup DeckEditors
|
||||
* @brief Tabs for the DeckList Editors
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup NetworkingTabs Networking Tabs
|
||||
* @ingroup Tabs
|
||||
* @ingroup NetworkingWidgets
|
||||
* @brief Tabs related to a RemoteClient interacting with a Server.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup ServerTabs Server Tabs
|
||||
* @ingroup NetworkingTabs
|
||||
* @brief Tabs related to a Server.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup AccountTabs Account Tabs
|
||||
* @ingroup NetworkingTabs
|
||||
* @brief Tabs related to user account information.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup RoomTabs Room Tabs
|
||||
* @ingroup NetworkingTabs
|
||||
* @ingroup RoomWidgets
|
||||
* @brief Tabs related to a Room on a Server.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup MessagingTabs Message Tabs
|
||||
* @ingroup NetworkingTabs
|
||||
* @brief Tabs related to users sending messages to each other.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup PictureLoader Card Picture Loader
|
||||
* @ingroup UI
|
||||
* @ingroup Cards
|
||||
* @brief The PictureLoader for CardInfoPictureWidget%s and CardItem%s
|
||||
*/
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Cards */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @defgroup Cards Cards
|
||||
* @brief Representation of individual cards and their state.
|
||||
*
|
||||
* Defines CardInfo and related objects which hold attributes, metadata, and runtime state within
|
||||
* both the CardDatabase and the Game engine.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup CardSets Card Sets
|
||||
* @ingroup Cards
|
||||
* @brief CardSet%s.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup CardPrintings Card Printings
|
||||
* @ingroup Cards
|
||||
* @brief Information about specific printings of CardInfo%s via PrintingInfo and ExactCard.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup CardWidgets Card Widgets
|
||||
* @ingroup Widgets
|
||||
* @ingroup Cards
|
||||
* @brief Widgets for CardInfo display and interaction.
|
||||
*
|
||||
* Provides UI components that render individual CardInfo%s, handle
|
||||
* interactions, and display card details within the application.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup CardExtraInfoWidgets Card Extra Info Widgets
|
||||
* @ingroup CardWidgets
|
||||
* @brief Widgets for displaying additional information about CardInfo%s.
|
||||
*
|
||||
* Displays additional data associated with CardInfo%s or data in a different form.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup CardDatabase Card Database
|
||||
* @ingroup Cards
|
||||
* @brief Core CardDatabase and loaders.
|
||||
*
|
||||
* Provides the underlying data models for cards, including loading,
|
||||
* parsing, and managing the complete CardDatabase used in the application.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup CardDatabaseModels Models
|
||||
* @ingroup CardDatabase
|
||||
* @brief Qt models for CardDatabase access.
|
||||
*
|
||||
* Supplies Qt model abstractions for presenting CardInfo data in views,
|
||||
* supporting filtering, sorting, and data binding to widgets.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup CardDatabaseModelFilters Filters
|
||||
* @ingroup CardDatabaseModels
|
||||
* @brief Filters for CardDatabase models.
|
||||
*
|
||||
* Provides reusable filtering components for Qt models, enabling
|
||||
* refined queries and customized CardDatabase views.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup CardDatabaseParsers Parsers
|
||||
* @ingroup CardDatabase
|
||||
* @brief Parsers for CardInfo data.
|
||||
*
|
||||
* Implements parsers for CardDatabase structures, handling the
|
||||
* transformation of raw data into structured objects and models.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup CardDatabaseWidgets Card Database Widgets
|
||||
* @ingroup Widgets
|
||||
* @ingroup CardDatabase
|
||||
* @brief Widgets for browsing and selecting CardInfo%s in the CardDatabase.
|
||||
*
|
||||
* Provides UI components for interacting with the CardDatabase,
|
||||
* including search, filtering, and browsing in tabular or list views.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup VisualCardDatabaseWidgets Visual Card Database Widgets
|
||||
* @ingroup CardDatabaseWidgets
|
||||
* @brief VisualDatabaseDisplayWidget and related helper classes.
|
||||
*
|
||||
* Enhances card browsing with graphical interfaces, offering
|
||||
* image-based and grid-based views for selecting cards.
|
||||
*/
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Decks */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @defgroup Decks Deck Management
|
||||
* @brief DeckList handling, persistence, and tooling.
|
||||
*
|
||||
* Covers the lifecycle of DeckList%s: loading, saving, editing, import/export,
|
||||
* and long-term persistence, including both textual and visual editors.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup DeckModels Deck Models
|
||||
* @ingroup Decks
|
||||
* @brief Qt Models relating to DeckList.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup DeckEditors Deck Editors
|
||||
* @ingroup Decks
|
||||
* @brief Editors for creating and modifying a DeckList.
|
||||
*
|
||||
* Provides visual and textual editors for building, modifying,
|
||||
* and analyzing DeckList%s. Includes support for card search,
|
||||
* sorting, and filtering of DeckList contents.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup DeckEditorWidgets Deck Editor Widgets
|
||||
* @ingroup Widgets
|
||||
* @ingroup DeckEditors
|
||||
* @brief Supporting widgets for DeckList editors.
|
||||
*
|
||||
* Contains specialized UI components that extend DeckList editors with
|
||||
* interactive functionality, data views, and user interaction tools.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup DeckEditorTabs Deck Editor Tabs
|
||||
* @ingroup DeckEditorWidgets
|
||||
* @brief Tabs which implement AbstractTabDeckEditor to provide DeckList editing functionality.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup DeckEditorAnalyticsWidgets Deck Editor Analytics Widgets
|
||||
* @ingroup DeckEditorWidgets
|
||||
* @brief Widgets for analyzing a DeckList.
|
||||
*
|
||||
* Provides visualization and statistical tools for analyzing DeckList%s.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup DeckEditorCardGroupWidgets Card Group Display Widgets
|
||||
* @ingroup DeckEditorWidgets
|
||||
* @brief Widgets for displaying groups of cards.
|
||||
*
|
||||
* Provides interactive views that organize and display cards in groups,
|
||||
* enabling filtering, sorting, and direct manipulation of DeckList sections.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup PrintingWidgets Printing Widgets
|
||||
* @ingroup DeckEditorWidgets
|
||||
* @ingroup Widgets
|
||||
* @brief Widgets for handling PrintingInfo of cards in a DeckList.
|
||||
*
|
||||
* Manages the display and editing of card printing information within a DeckList,
|
||||
* including editions, variations, and preferences.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup DeckStorage Deck Storage
|
||||
* @ingroup Decks
|
||||
* @brief Systems for storing a DeckList on a local file system or remote service.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup LocalDeckStorage Local Deck Storage
|
||||
* @ingroup DeckStorage
|
||||
* @brief Systems for storing a DeckList on a local file system.
|
||||
*
|
||||
* Handles the persistence of a DeckList, providing file-based storage,
|
||||
* metadata tracking, and tagging functionality.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup LocalDeckStorageWidgets Local Deck Storage Widgets
|
||||
* @ingroup Widgets
|
||||
* @ingroup LocalDeckStorage
|
||||
* @brief Widgets for browsing and managing stored DeckList%s.
|
||||
*
|
||||
* Provides list and tree-based views for exploring stored DeckList%s,
|
||||
* including tagging, searching, and preview functionality.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup LocalDeckStorageDialogs Local Deck Storage Dialogs
|
||||
* @ingroup LocalDeckStorageWidgets
|
||||
* @ingroup ImportExport
|
||||
* @ingroup Lobby
|
||||
* @brief Dialogs related to interacting with DeckList%s on a filesystem or in a remote location.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup VisualDeckStorageWidgets Visual Deck Storage Widgets
|
||||
* @ingroup LocalDeckStorageWidgets
|
||||
* @brief Visual widgets for DeckList storage.
|
||||
*
|
||||
* Offers graphical interfaces for browsing and interacting with a stored
|
||||
* DeckList and tags, focusing on a more visual browsing experience.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup VisualDeckPreviewWidgets Visual Deck Preview Widgets
|
||||
* @ingroup VisualDeckStorageWidgets
|
||||
* @brief Widgets for visually previewing a DeckList.
|
||||
*
|
||||
* Provides visual previews of a DeckList, allowing users
|
||||
* to inspect the DeckList name, color identity, and banner card at a glance.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup RemoteDeckStorage Remote Deck Storage
|
||||
* @ingroup DeckStorage
|
||||
* @brief Systems for storing a DeckList on a remote service.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup RemoteDeckStorageWidgets Remote Deck Storage Widgets
|
||||
* @ingroup Widgets
|
||||
* @ingroup RemoteDeckStorage
|
||||
* @ingroup ImportExport
|
||||
* @ingroup Lobby
|
||||
* @brief Widgets related to interacting with DeckList%s on a filesystem or in a remote location.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup RemoteDeckStorageDialogs Remote Deck Storage Dialogs
|
||||
* @ingroup RemoteDeckStorageWidgets
|
||||
* @brief Dialogs related to interacting with DeckList%s on a filesystem or in a remote location.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup ImportExport Import/Export
|
||||
* @ingroup DeckStorage
|
||||
* @brief DeckList import, export, and conversion.
|
||||
*
|
||||
* Supports importing and exporting a DeckList across formats, including
|
||||
* text, Cockatrice-native formats, and third-party platforms.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup Parsing Parsing
|
||||
* @ingroup DeckStorage
|
||||
* @brief DeckList parsing and external API integration.
|
||||
*
|
||||
* Contains parsers for DeckList URLs and APIs (e.g., TappedOut, Archidekt,
|
||||
* Moxfield, Deckstats), allowing seamless import and synchronization.
|
||||
*/
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Game */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @defgroup Game Game
|
||||
* @brief Core Game framework.
|
||||
*
|
||||
* Responsible for the main Game loop, GameState management,
|
||||
* CardZone handling, and resolution of actions during a Game.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup GameUi User Interface
|
||||
* @ingroup Game
|
||||
* @brief Graphical components for interaction during a Game.
|
||||
*
|
||||
* Provides Qt-based widgets, dialogs, and menus used during a Game,
|
||||
* supporting both visual presentation and interaction logic.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup GameLobby Lobby
|
||||
* @ingroup GameUi
|
||||
* @brief User interface for the multiplayer lobby.
|
||||
*
|
||||
* Contains widgets, dialogs, and models used to interact with the
|
||||
* lobby within the Game UI layer.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup GameWidgets Game Widgets
|
||||
* @ingroup Widgets
|
||||
* @ingroup GameUi
|
||||
* @brief Widgets specific to the interface in a Game.
|
||||
*
|
||||
* Provides reusable visual components that represent elements
|
||||
* of the ongoing Game, such as a PlayerGraphicsItem or CardZone views.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup GameDialogs Dialogs
|
||||
* @ingroup GameUi
|
||||
* @brief Dialogs for Game%s.
|
||||
*
|
||||
* Modal and non-modal dialogs for Player interaction during a Game,
|
||||
* including prompts, confirmations, and detailed views.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup GameMenus Menus
|
||||
* @ingroup GameUi
|
||||
* @brief Menus for Game%s.
|
||||
*
|
||||
* Provides menu bars, context menus, and hierarchical options
|
||||
* available to the Player during a Game session.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup GameMenusPlayers Player
|
||||
* @ingroup GameMenus
|
||||
* @brief Menus specific to Player actions.
|
||||
*
|
||||
* Defines context menus and options relating to Player state and PlayerActions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup GameMenusZones Zones
|
||||
* @ingroup GameMenus
|
||||
* @brief Menus for interacting with zones.
|
||||
*
|
||||
* Provides contextual options for a CardZone such as the hand,
|
||||
* library, graveyard, and battlefield.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup GameMenusCards Cards
|
||||
* @ingroup GameMenus
|
||||
* @brief Menus for CardItem interactions.
|
||||
*
|
||||
* Defines right-click menus and contextual options available
|
||||
* when interacting with an individual CardItem.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup GameGraphics Graphics
|
||||
* @ingroup GameUi
|
||||
* @brief Visual graphics for Game elements.
|
||||
*
|
||||
* Provides rendering and layout for core Game objects such as a
|
||||
* Player, CardZone, or CardItem in the interface.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup GameGraphicsPlayers Players
|
||||
* @ingroup GameGraphics
|
||||
* @brief Player-specific graphical elements.
|
||||
*
|
||||
* Defines visual representations of a Player, their states,
|
||||
* and associated on-screen information.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup GameGraphicsZones Zones
|
||||
* @ingroup GameGraphics
|
||||
* @brief Graphical representations of zones.
|
||||
*
|
||||
* Provides layout, visuals, and animations for a CardZone like the hand,
|
||||
* library, battlefield, and graveyard.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup GameGraphicsCards Cards
|
||||
* @ingroup GameGraphics
|
||||
* @brief Graphical representation of cards.
|
||||
*
|
||||
* Manages the visual look and behavior of a CardItem when displayed
|
||||
* in the Game interface.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup GameLogic Logic
|
||||
* @ingroup Game
|
||||
* @brief Game logic and rules framework.
|
||||
*
|
||||
* Implements resolution of actions and logical
|
||||
* state management for an ongoing Game.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup GameLogicPlayers Players
|
||||
* @ingroup GameLogic
|
||||
* @brief Logic related to Player state.
|
||||
*
|
||||
* Handles Player attributes, PlayerActions, resources, and their impact
|
||||
* on GameState.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup GameLogicZones Zones
|
||||
* @ingroup GameLogic
|
||||
* @brief Logical handling of CardZones during a Game.
|
||||
*
|
||||
* Defines the rules and behaviors of zones such as the hand,
|
||||
* battlefield, library, and graveyard.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup GameLogicActions Actions
|
||||
* @ingroup GameLogic
|
||||
* @brief Actions, events, and their resolution.
|
||||
*
|
||||
* Encapsulates how actions are created, queued, and resolved,
|
||||
* including event handling and triggered effects.
|
||||
*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Networking */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @defgroup Network Networking
|
||||
* @brief Networking components for client/server communication.
|
||||
*
|
||||
* Contains all systems related to communication between client and server.
|
||||
* This includes lobby and room management, server interaction logic, and
|
||||
* message handling infrastructure based on protobuf.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup ApiInterfaces API Interfaces
|
||||
* @ingroup Client
|
||||
* @ingroup Parsing
|
||||
* @ingroup ImportExport
|
||||
* @brief Interfaces for interacting with various APIs
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup ApiResponses API Responses
|
||||
* @ingroup ApiInterfaces
|
||||
* @brief Structures representing API responses.
|
||||
*
|
||||
* Encapsulates the data models for networking API responses,
|
||||
* providing consistent interfaces for parsing and handling results.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup ApiResponseDisplayWidgets API Response Display Widgets
|
||||
* @ingroup Widgets
|
||||
* @ingroup ApiResponses
|
||||
* @brief Widgets for displaying API responses.
|
||||
*
|
||||
* Provides user interface components that render and present API response
|
||||
* data to the user in a structured and interactive way.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup Messages Protocol Messages
|
||||
* @ingroup Network
|
||||
* @brief Protocol message definitions and handlers.
|
||||
*
|
||||
* Contains the generated protobuf messages and supporting code used
|
||||
* to encode, decode, and process communication between client and server.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup Client Client
|
||||
* @ingroup Network
|
||||
* @brief The Cockatrice client application.
|
||||
*
|
||||
* Handles all client-side communication with the server.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup NetworkingWidgets Networking Widgets
|
||||
* @ingroup Widgets
|
||||
* @ingroup Client
|
||||
* @brief Widgets related to a RemoteClient interacting with a Server.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup ClientUpdates Client Updates
|
||||
* @ingroup Client
|
||||
* @brief Client updates.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup Server Server
|
||||
* @ingroup Network
|
||||
* @brief The Cockatrice server application.
|
||||
*
|
||||
* Provides the authoritative game backend, handling authentication,
|
||||
* lobbies, game rooms, and relaying state between connected clients.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup RoomWidgets Room Widgets
|
||||
* @ingroup Widgets
|
||||
* @ingroup Client
|
||||
* @brief Server room management.
|
||||
*
|
||||
* Manages creation and interaction of Game lobbies and room state synchronization with the server.
|
||||
*/
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Replay */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @defgroup Replay Replays
|
||||
* @brief Replay recording and playback.
|
||||
*
|
||||
* Provides mechanisms for capturing Game sessions, storing them in
|
||||
* replay files, and replaying them for review or analysis.
|
||||
*/
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Settings */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @defgroup Settings Settings
|
||||
* @brief Application settings and configuration.
|
||||
*
|
||||
* Centralizes the configuration system, providing persistence and
|
||||
* runtime access to user preferences and application options.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup SettingsWidgets Settings Widgets
|
||||
* @ingroup Widgets
|
||||
* @ingroup Settings
|
||||
* @brief TODO: Document this
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup CoreSettings Core Settings
|
||||
* @ingroup Settings
|
||||
* @brief TODO: Document this
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup NetworkSettings Network Settings
|
||||
* @ingroup Settings
|
||||
* @brief TODO: Document this
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup GameSettings Game Settings
|
||||
* @ingroup Settings
|
||||
* @brief TODO: Document this
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup CardSettings Card Settings
|
||||
* @ingroup Settings
|
||||
* @brief TODO: Document this
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup DeckSettings Deck Settings
|
||||
* @ingroup Settings
|
||||
* @brief TODO: Document this
|
||||
*/
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Tests */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @defgroup Tests Tests
|
||||
* @brief Automated testing framework.
|
||||
*
|
||||
* Provides unit tests, integration tests, and regression checks
|
||||
* to ensure stability and correctness of the Cockatrice codebase.
|
||||
*/
|
||||
27
docs/doxygen/html/footer.html
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<!-- HTML footer for doxygen 1.14.0-->
|
||||
<!-- start footer part -->
|
||||
<script src="$relpath^graph_toggle.js"></script>
|
||||
<!--BEGIN GENERATE_TREEVIEW-->
|
||||
<div class="navpath" id="nav-path"><!-- id is needed for treeview function! -->
|
||||
<ul>
|
||||
$navpath
|
||||
<li class="footer">$generatedby <a href="https://www.doxygen.org/index.html"><img alt="doxygen"
|
||||
class="footer"
|
||||
height="31" src="$relpath^doxygen.svg"
|
||||
width="104"/></a>
|
||||
$doxygenversion
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!--END GENERATE_TREEVIEW-->
|
||||
<!--BEGIN !GENERATE_TREEVIEW-->
|
||||
<hr class="footer"/>
|
||||
<address class="footer"><small>
|
||||
$generatedby <a href="https://www.doxygen.org/index.html"><img alt="doxygen" class="footer"
|
||||
height="31" src="$relpath^doxygen.svg" width="104"/></a>
|
||||
$doxygenversion
|
||||
</small></address>
|
||||
</div><!-- doc-content -->
|
||||
<!--END !GENERATE_TREEVIEW-->
|
||||
</body>
|
||||
</html>
|
||||
81
docs/doxygen/html/header.html
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
<!-- HTML header for doxygen 1.14.0-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html lang="$langISO" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta content="text/xhtml;charset=UTF-8" http-equiv="Content-Type"/>
|
||||
<meta content="IE=11" http-equiv="X-UA-Compatible"/>
|
||||
<meta content="Doxygen $doxygenversion" name="generator"/>
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport"/>
|
||||
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
|
||||
<!--BEGIN PROJECT_ICON-->
|
||||
<link href="$relpath^$projecticon" rel="icon" type="image/x-icon"/>
|
||||
<!--END PROJECT_ICON-->
|
||||
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
|
||||
<!--BEGIN FULL_SIDEBAR-->
|
||||
<script type="text/javascript">var page_layout = 1;</script>
|
||||
<!--END FULL_SIDEBAR-->
|
||||
<script src="$relpath^jquery.js" type="text/javascript"></script>
|
||||
<script src="$relpath^dynsections.js" type="text/javascript"></script>
|
||||
<!--BEGIN COPY_CLIPBOARD-->
|
||||
<script src="$relpath^clipboard.js" type="text/javascript"></script>
|
||||
<!--END COPY_CLIPBOARD-->
|
||||
$treeview
|
||||
$search
|
||||
$mathjax
|
||||
$darkmode
|
||||
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css"/>
|
||||
$extrastylesheet
|
||||
</head>
|
||||
<body>
|
||||
<!--BEGIN FULL_SIDEBAR-->
|
||||
<div class="ui-resizable side-nav-resizable" id="side-nav"><!-- do not remove this div, it is closed by doxygen! -->
|
||||
<!--END FULL_SIDEBAR-->
|
||||
|
||||
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
|
||||
|
||||
<!--BEGIN TITLEAREA-->
|
||||
<div id="titlearea">
|
||||
<table cellpadding="0" cellspacing="0">
|
||||
<tbody>
|
||||
<tr id="projectrow">
|
||||
<!--BEGIN PROJECT_LOGO-->
|
||||
<td id="projectlogo"><img $logosize alt="Logo" src="$relpath^$projectlogo"/></td>
|
||||
<!--END PROJECT_LOGO-->
|
||||
<!--BEGIN PROJECT_NAME-->
|
||||
<td id="projectalign">
|
||||
<div id="projectname">$projectname<!--BEGIN PROJECT_NUMBER--><span id="projectnumber"> $projectnumber</span>
|
||||
<!--END PROJECT_NUMBER-->
|
||||
</div>
|
||||
<!--BEGIN PROJECT_BRIEF-->
|
||||
<div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
|
||||
</td>
|
||||
<!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME-->
|
||||
<!--BEGIN PROJECT_BRIEF-->
|
||||
<td>
|
||||
<div id="projectbrief">$projectbrief</div>
|
||||
</td>
|
||||
<!--END PROJECT_BRIEF-->
|
||||
<!--END !PROJECT_NAME-->
|
||||
<!--BEGIN DISABLE_INDEX-->
|
||||
<!--BEGIN SEARCHENGINE-->
|
||||
<!--BEGIN !FULL_SIDEBAR-->
|
||||
<td>$searchbox</td>
|
||||
<!--END !FULL_SIDEBAR-->
|
||||
<!--END SEARCHENGINE-->
|
||||
<!--END DISABLE_INDEX-->
|
||||
</tr>
|
||||
<!--BEGIN SEARCHENGINE-->
|
||||
<!--BEGIN FULL_SIDEBAR-->
|
||||
<tr>
|
||||
<td colspan="2">$searchbox</td>
|
||||
</tr>
|
||||
<!--END FULL_SIDEBAR-->
|
||||
<!--END SEARCHENGINE-->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--END TITLEAREA-->
|
||||
<!-- end header part -->
|
||||
232
docs/doxygen/images/cockatrice_logo.svg
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
docs/doxygen/images/screenshots/classic_database_display.png
Normal file
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
BIN
docs/doxygen/images/screenshots/classic_deck_editor.png
Normal file
|
After Width: | Height: | Size: 398 KiB |
BIN
docs/doxygen/images/screenshots/deck_dock_deck_list.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
docs/doxygen/images/screenshots/deck_dock_deck_list_buttons.png
Normal file
|
After Width: | Height: | Size: 885 B |
BIN
docs/doxygen/images/screenshots/deck_dock_deck_list_group_by.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
docs/doxygen/images/screenshots/deckeditordeckdockwidget.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
docs/doxygen/images/screenshots/printing_selector.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
docs/doxygen/images/screenshots/printing_selector_disable.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
docs/doxygen/images/screenshots/printing_selector_enable.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
docs/doxygen/images/screenshots/printing_selector_navigation.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
docs/doxygen/images/screenshots/printing_selector_options.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 94 KiB |
BIN
docs/doxygen/images/screenshots/release_channel_beta.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
docs/doxygen/images/screenshots/vde_deck_analytics.png
Normal file
|
After Width: | Height: | Size: 194 KiB |
|
After Width: | Height: | Size: 245 KiB |
BIN
docs/doxygen/images/screenshots/vde_flat_layout_type_grouped.png
Normal file
|
After Width: | Height: | Size: 303 KiB |
|
After Width: | Height: | Size: 278 KiB |
BIN
docs/doxygen/images/screenshots/vde_sample_hand.png
Normal file
|
After Width: | Height: | Size: 227 KiB |
147
docs/doxygen/js/graph_toggle.js
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document.querySelectorAll(".memdoc").forEach(memdoc => {
|
||||
let callContent = null, callHeader = null;
|
||||
let callerContent = null, callerHeader = null;
|
||||
|
||||
memdoc.querySelectorAll("div.dynheader").forEach(header => {
|
||||
const text = (header.textContent || "").trim().toLowerCase();
|
||||
let content = header.nextElementSibling;
|
||||
let tries = 0;
|
||||
while (content && !content.classList.contains("dyncontent") && tries < 8) {
|
||||
content = content.nextElementSibling;
|
||||
tries++;
|
||||
}
|
||||
if (!content) return;
|
||||
|
||||
if (text.includes("caller")) {
|
||||
callerContent = content;
|
||||
callerHeader = header;
|
||||
} else if (text.includes("call graph") || text.includes("callgraph") || text.includes("call graph for")) {
|
||||
callContent = content;
|
||||
callHeader = header;
|
||||
} else if (text.includes("call")) {
|
||||
if (!callContent) {
|
||||
callContent = content;
|
||||
callHeader = header;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!callContent && !callerContent) return;
|
||||
if (memdoc.querySelector(".graph-toggle")) return;
|
||||
|
||||
const toggle = document.createElement("div");
|
||||
toggle.className = "graph-toggle";
|
||||
|
||||
const callerBtn = document.createElement("button");
|
||||
callerBtn.type = "button";
|
||||
callerBtn.textContent = "Caller Graph";
|
||||
const callBtn = document.createElement("button");
|
||||
callBtn.type = "button";
|
||||
callBtn.textContent = "Call Graph";
|
||||
|
||||
toggle.appendChild(callerBtn);
|
||||
toggle.appendChild(callBtn);
|
||||
|
||||
const firstHeader = memdoc.querySelector("div.dynheader");
|
||||
memdoc.insertBefore(toggle, firstHeader || memdoc.firstChild);
|
||||
|
||||
// hide everything initially
|
||||
if (callerContent) {
|
||||
callerContent.style.display = "none";
|
||||
callerHeader.style.display = "none";
|
||||
}
|
||||
if (callContent) {
|
||||
callContent.style.display = "none";
|
||||
callHeader.style.display = "none";
|
||||
}
|
||||
|
||||
// disable missing buttons
|
||||
if (!callerContent) {
|
||||
callerBtn.disabled = true;
|
||||
callerBtn.classList.add("disabled");
|
||||
}
|
||||
if (!callContent) {
|
||||
callBtn.disabled = true;
|
||||
callBtn.classList.add("disabled");
|
||||
}
|
||||
|
||||
// track current state
|
||||
let current = null; // "caller", "call", "both", null=hidden
|
||||
|
||||
function setActive(type) {
|
||||
if (type === "caller") {
|
||||
if (current === "caller") { // hide it
|
||||
if (callerContent) {
|
||||
callerContent.style.display = "none";
|
||||
callerHeader.style.display = "none";
|
||||
}
|
||||
current = null;
|
||||
} else if (current === "call") { // show both
|
||||
if (callerContent) {
|
||||
callerContent.style.display = "block";
|
||||
callerHeader.style.display = "block";
|
||||
}
|
||||
current = "both";
|
||||
} else if (current === "both") { // hide caller only → call only
|
||||
if (callerContent) {
|
||||
callerContent.style.display = "none";
|
||||
callerHeader.style.display = "none";
|
||||
}
|
||||
current = "call";
|
||||
} else { // nothing visible → show caller
|
||||
if (callerContent) {
|
||||
callerContent.style.display = "block";
|
||||
callerHeader.style.display = "block";
|
||||
}
|
||||
if (callContent) {
|
||||
callContent.style.display = "none";
|
||||
callHeader.style.display = "none";
|
||||
}
|
||||
current = "caller";
|
||||
}
|
||||
} else if (type === "call") {
|
||||
if (current === "call") { // hide it
|
||||
if (callContent) {
|
||||
callContent.style.display = "none";
|
||||
callHeader.style.display = "none";
|
||||
}
|
||||
current = null;
|
||||
} else if (current === "caller") { // show both
|
||||
if (callContent) {
|
||||
callContent.style.display = "block";
|
||||
callHeader.style.display = "block";
|
||||
}
|
||||
current = "both";
|
||||
} else if (current === "both") { // hide call only → caller only
|
||||
if (callContent) {
|
||||
callContent.style.display = "none";
|
||||
callHeader.style.display = "none";
|
||||
}
|
||||
current = "caller";
|
||||
} else { // nothing visible → show call only
|
||||
if (callContent) {
|
||||
callContent.style.display = "block";
|
||||
callHeader.style.display = "block";
|
||||
}
|
||||
if (callerContent) {
|
||||
callerContent.style.display = "none";
|
||||
callerHeader.style.display = "none";
|
||||
}
|
||||
current = "call";
|
||||
}
|
||||
}
|
||||
|
||||
// update button styles
|
||||
callerBtn.classList.toggle("active", current === "caller" || current === "both");
|
||||
callBtn.classList.toggle("active", current === "call" || current === "both");
|
||||
}
|
||||
|
||||
callerBtn.addEventListener("click", () => {
|
||||
if (!callerBtn.disabled) setActive("caller");
|
||||
});
|
||||
callBtn.addEventListener("click", () => {
|
||||
if (!callBtn.disabled) setActive("call");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
@page card_database_schema_and_parsing Card Database Schema and Parsing
|
||||
|
||||
# Card Database Schemas
|
||||
|
||||
Cockatrice uses `XML files` to store information of available cards to be used in the app (`cards.xml`).
|
||||
The token file follows the schema of the card database (`tokens.xml`), too.
|
||||
|
||||
Saved decks (`<deckname>.xml`) use a different schema.
|
||||
|
||||
- [XSD Schema for `Card Databases`](https://github.com/Cockatrice/Cockatrice/blob/master/docs/xsd/cards.xsd) <kbd>v4</kbd>
|
||||
- [XSD Schema for `Decks`](https://github.com/Cockatrice/Cockatrice/blob/master/docs/xsd/deck.xsd) <kbd>v1</kbd>
|
||||
|
||||
|
||||
# Card Database Parsing
|
||||
|
||||
TODO
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
@page displaying_cards Displaying Cards
|
||||
|
||||
Cockatrice offers a number of widgets for displaying cards. To pick the right one for your purpose, it is first
|
||||
important
|
||||
to determine the context in which you will be displaying the card.
|
||||
|
||||
# In-client
|
||||
|
||||
In the client (like in your custom widgets), you can use the existing card display widgets.
|
||||
|
||||
## Simple Display
|
||||
|
||||
The most general purpose of these is @ref CardInfoPictureWidget, which simply displays a picture of a card.
|
||||
|
||||
The textual equivalent to this is @ref CardInfoTextWidget, which displays all the properties of a card in text form.
|
||||
|
||||
## Detailed Display
|
||||
|
||||
The convenience class @ref CardInfoDisplayWidget displays both of these widgets in a tabbed container, which allows
|
||||
choosing between either visual, textual, or both representations at the same time. It is the class that is most
|
||||
familiar to our users when a single card is highlighted or inspected, since it is used in the deck editor and in-game
|
||||
for these purposes.
|
||||
|
||||
If you would like the user to be able to inspect a single card in your custom widget, you should
|
||||
expose a @ref CardInfoDisplayWidget to them and incorporate it as a dock widget inside your custom widget.
|
||||
|
||||
@ref CardInfoDisplayWidget is great for ensuring that the user has all the relevant details of a card available.
|
||||
It is crucial anywhere the user might encounter a card for the first time or needs to make an informed decision.
|
||||
|
||||
## Visual Display
|
||||
|
||||
However, there is a reason why it is possible to have a visual-only representation of the card in the
|
||||
@ref CardInfoDisplayWidget.
|
||||
|
||||
Cards are, by design, meant to be represented as images and cards which we can reasonably
|
||||
expect the user to know (for example, cards contained in a user made deck or explicitly chosen by the user) can be
|
||||
represented with just their image as a sort of "visual shorthand".
|
||||
|
||||
The simplest way to do this is of course, the above-mentioned @ref CardInfoPictureWidget which simply displays the
|
||||
picture without modifications.
|
||||
|
||||
However, there exist a couple of other convenience classes which subclass @ref CardInfoPictureWidget to accomplish some
|
||||
things which you might find useful as well.
|
||||
|
||||
- @ref CardInfoPictureWithTextOverlayWidget displays text overlaid on top of the card picture and scales this text to
|
||||
fit automatically.
|
||||
- @ref DeckPreviewCardPictureWidget is a @ref CardInfoPictureWithTextOverlayWidget which also features a
|
||||
raise-on-mouse-enter animation, controlled by the global setting (TODO: specify which).
|
||||
- @ref CardInfoPictureArtCropWidget attempts to display an 'art crop' of the card by cropping the upper region of the
|
||||
card and only displaying that.
|
||||
|
||||
## Groups of Cards
|
||||
|
||||
Sometimes it is useful to display images of cards in groups, for example to represent the deck in the @ref
|
||||
VisualDeckEditorWidget or during confirmation in the pre-game lobby.
|
||||
|
||||
To this end, Cockatrice offers three convenience classes to display cards for an index in a @ref DeckListModel in a
|
||||
group and one to display all cards in a particular zone of a @ref DeckListModel.
|
||||
|
||||
The generic way to do this is @ref CardGroupDisplayWidget, which displays cards in @ref QVBoxLayout. This class is very
|
||||
generic and it is probably a better choice to either subclass it or use one of the two existing implementations.
|
||||
|
||||
@ref FlatCardGroupDisplayWidget displays cards using a horizontal @ref FlowWidget. This means it will fill available
|
||||
screen space, left to right with no margins, with card pictures, skipping to a new line if it overflows available screen
|
||||
space horizontally.
|
||||
|
||||
@ref OverlappedCardGroupDisplayWidget displays cards using an @ref OverlapWidget, which displays widgets on top of each
|
||||
other in vertical columns until it exceeds available screen space in which case it will start a new column. The amount
|
||||
of overlap as well as the overlap direction is configurable.
|
||||
|
||||
@ref DeckCardZoneDisplayWidget can be used to visualize all cards in a zone with either a @ref
|
||||
FlatCardGroupDisplayWidget or a @ref OverlappedCardGroupDisplayWidget and headed by a @ref BannerWidget. It also allows
|
||||
grouping and sorting.
|
||||
|
||||
# In-game
|
||||
|
||||
The in-game view is a @ref QGraphicsScene, which means any widget we want to display inside of it should be a @ref
|
||||
QGraphicsObject.
|
||||
|
||||
- @ref CardItem
|
||||
|
||||
|
||||
|
||||
13
docs/doxygen/pages/developer_documentation/index.md
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
@page developer_reference Developer Reference
|
||||
|
||||
- @subpage logging
|
||||
|
||||
- @subpage primer_cards
|
||||
|
||||
- @subpage card_database_schema_and_parsing
|
||||
- @subpage querying_the_card_database
|
||||
|
||||
- @subpage loading_card_pictures
|
||||
- @subpage displaying_cards
|
||||
|
||||
- @subpage developer_reference_network_overview
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
@page loading_card_pictures Loading Card Pictures
|
||||
|
||||
Pictures associated with CardInfo%s are retrieved either from on-disk or the network through the CardPictureLoader.
|
||||
|
||||
In most cases, you don't need to concern yourself with the internals of CardPictureLoader.
|
||||
|
||||
Simply using one of the ways described in @ref displaying_cards is enough to automatically queue a request to the
|
||||
CardPictureLoader when the chosen widget is shown, emitting signals to refresh the widget when the request is finished.
|
||||
|
||||
# How requests are triggered
|
||||
|
||||
CardPictureLoader::getPixmap() is called exactly two times in the code base, in CardInfoPictureWidget::loadPixmap(), the
|
||||
base class
|
||||
for all widget based card picture display, and AbstractCardItem::paintPicture(), the base class for all QGraphicsItem
|
||||
based card picture
|
||||
display. See @ref displaying_cards for more information on the difference between these two display methods.
|
||||
|
||||
Because both of these calls are made in the paintEvent() methods of their respective classes, this means that requests
|
||||
are issued as soon as but not before the widget is shown on screen.
|
||||
|
||||
It is also possible to "warm up" the cache by issuing card picture load requests to the CardPictureLoader without using
|
||||
a display widget and waiting for it to be shown by calling CardPictureLoader::cacheCardPixmaps() with a list of
|
||||
ExactCard%s.
|
||||
|
||||
# The QPixmapCache and QNetworkDiskCache
|
||||
|
||||
Cockatrice uses the QPixmapCache from the Qt GUI module to store card pictures in-memory and the QNetworkDiskCache from
|
||||
the Qt Network module to cache network requests for card pictures on-disk.
|
||||
|
||||
What this means is that the CardPictureLoader will first attempt to look up a card in the QPixmapCache according to the
|
||||
ExactCard::getPixmapCacheKey() method of an ExactCard object. If it does not find it in the in-memory cache, it will
|
||||
issue a load request, which will first look for local images on-disk and then consult the QNetworkDiskCache and if
|
||||
found, use the stored binary data from the network cache to populate the in-memory pixmap cache under the card's cache
|
||||
key. If it is not found, it will then proceed with issuing a network request.
|
||||
|
||||
The size of both of these caches can be configured by the user on the "Storage" settings page.
|
||||
|
||||
# PixmapCacheKeys and ProviderIDs
|
||||
|
||||
Every card picture that is loaded ends up in the QPixmapCache under a key that identifies the exact printing it belongs
|
||||
to. The key is produced by ExactCard::getPixmapCacheKey() and has the following shape:
|
||||
|
||||
```text
|
||||
card_<card name>_<provider ID>
|
||||
```
|
||||
|
||||
For example, the _Example Card_ printing with provider ID `0b23cdc8-d413-4fb1-8470-474221b10fe2` is stored
|
||||
under `card_Example Card_0b23cdc8-d413-4fb1-8470-474221b10fe2`. If the printing has no provider ID, the key
|
||||
drops the suffix and falls back to `card_<card name>`.
|
||||
|
||||
The **provider ID** is the Scryfall UUID of the printing. Oracle maps the `scryfallId` of every printing to the `uuid`
|
||||
property when building the card database, and deck files persist it as the `uuid` attribute of each card entry. Because
|
||||
the provider ID is part of the pixmap cache key, two different printings of the same card never share a cache entry.
|
||||
This is exactly what allows the printing selector and exact-card lookups to display the picture of the precise printing
|
||||
a card was added as.
|
||||
|
||||
The base key holds the full-size image. When a widget asks for a scaled version, the scaled pixmap is stored under an
|
||||
additional key of the form `<key>_<width>x<height>`, with the size adjusted for the device pixel ratio of the screen,
|
||||
so each widget size is only ever scaled once.
|
||||
|
||||
The cache key is also used for bookkeeping outside of the pixmap cache itself:
|
||||
|
||||
- CardPictureLoaderWorker keeps a set of keys that are currently being loaded so the same card is never queued twice.
|
||||
- CardPictureLoader tracks, per key, the last time loading failed. A failed load stores a NULL pixmap under the key; as
|
||||
long as that marker is present, subsequent requests for the card show the "failed" card back and are only re-queued
|
||||
after the retry interval of 300 seconds has passed.
|
||||
- When the CardInfo of a loaded card is destroyed, its cache entries and failure markers are removed.
|
||||
|
||||
# The Redirect Cache
|
||||
|
||||
Many picture URLs - in particular the Gatherer and Scryfall URLs from the default set of templates - redirect to a CDN
|
||||
or to a different host. To avoid following the same redirect for every single card, CardPictureLoaderWorker remembers
|
||||
redirects and applies them without an extra network round trip.
|
||||
|
||||
The redirect cache is a hash map from original URL to redirect URL plus timestamp. It is persisted to a `cache.ini`
|
||||
file (Qt's INI format, under the `redirects` array) inside the redirect cache directory
|
||||
(`SettingsCache::instance().paths().getRedirectCachePath()`, i.e. `<cache directory>/redirects/`). The cache is loaded when the worker
|
||||
starts, pruned of entries older than the configured TTL, and written back to disk when the application quits.
|
||||
|
||||
Entries are added whenever a network reply reports a redirection (see below) and are consulted before any request is
|
||||
made: both CardPictureLoaderWorker::queueRequest() and CardPictureLoaderWorker::makeRequest() check for a cached
|
||||
redirect first and jump straight to the final URL.
|
||||
|
||||
The TTL is the "Redirect Cache TTL" setting on the "Storage" settings page and defaults to 30 days. Lowering it makes
|
||||
Cockatrice re-resolve redirects sooner, which can help when a download URL changed its redirect target.
|
||||
|
||||
Because Cockatrice tracks redirects itself, the QNetworkAccessManager is configured with Qt's `ManualRedirectPolicy`.
|
||||
Redirects found in a reply are handled manually:
|
||||
|
||||
- A recursive redirect (a URL redirecting to itself) is treated as a failed load.
|
||||
- Otherwise the redirect is recorded in the redirect cache and the request is re-issued against the target URL.
|
||||
- A successful reply with one of the redirect status codes 301, 302, 303, 305, 307 or 308 is handled the same way.
|
||||
|
||||
Clearing the network cache (CardPictureLoader::clearNetworkCache()) also clears the redirect cache.
|
||||
|
||||
# Local Image Loading
|
||||
|
||||
Before any network request is issued, CardPictureLoaderWorker hands the ExactCard to CardPictureLoaderLocal, which
|
||||
tries to find a matching picture on disk. If a local picture is found, it is used and no network request is made.
|
||||
|
||||
CardPictureLoaderLocal searches three locations:
|
||||
|
||||
- The **CUSTOM folder** (`<pictures directory>/CUSTOM/`). Every file in it is indexed recursively by its base name
|
||||
(both `baseName` and `completeBaseName`, so a file named `ExampleCard.jpg` is indexed as `ExampleCard`). The index is rebuilt
|
||||
every 10 seconds, so new files are picked up without restarting the
|
||||
client (changing the configured pictures directory only reassigns the search paths; the next timer tick rebuilds the index).
|
||||
- The **set-named subfolders** of the pictures directory: `<pictures directory>/<set code>/` and
|
||||
`<pictures directory>/downloadedPics/<set code>/`.
|
||||
- The **root of the `downloadedPics` folder** (`<pictures directory>/downloadedPics/`). The export naming schemes without a
|
||||
set-folder part write their files straight into `downloadedPics/`, so this is where flat-scheme downloads and the
|
||||
local overrides described below are matched.
|
||||
|
||||
For each candidate folder, the loader generates file-name variants from the card's corrected name, set code, collector
|
||||
number and provider ID using the import naming schemes (Card Name + Provider ID, Card Name + Set + Collector,
|
||||
Set + Collector + Card Name, Card Name + Set, Card Name), each tried with both `_` and `-` as separator. A file is
|
||||
accepted when its name without the extension *equals* the variant exactly - the extension itself is free - and the
|
||||
first variant that yields a readable image wins. For example, the file `Example Card_EXM_43.png` in the `EXM` set
|
||||
folder matches the card with corrected name `Example Card`, set code `EXM` and collector number `43`.
|
||||
|
||||
\attention The file-name variants use the *corrected* card name, so split cards are stored under their joined name: the
|
||||
"Example // Card" card is matched by a file named `ExampleCard.*`.
|
||||
|
||||
The naming schemes are also documented in the user-facing page @ref custom_card_pictures, which additionally covers
|
||||
the CUSTOM folder workflow, `picurl` and download URL templates.
|
||||
|
||||
When the filesystem cache method is selected on the "Storage" settings page, downloaded images are additionally written
|
||||
into `<pictures directory>/downloadedPics/` using the configured export naming scheme (as `.png` files). The two export
|
||||
schemes with a set-folder part (`Set Folder / Name + Provider ID` and `Set Folder / Name + Set Name + Collector`) write
|
||||
into `downloadedPics/<set code>/`; the three flat schemes write directly into `downloadedPics/`. Automatic cache writes
|
||||
never overwrite an existing file, so a provider outage can permanently leave an outdated image in that folder until it
|
||||
is deleted manually - the user-facing troubleshooting guide @ref fixing_card_pictures covers how to do this. Explicit
|
||||
image overrides (see below) are the exception and always overwrite.
|
||||
|
||||
# Local Image Overrides
|
||||
|
||||
Beyond the generic on-disk lookup above, individual printings can be given explicit artwork that wins over every other
|
||||
source without touching the CUSTOM folder or any download URL. This is the "Image Overrides" submenu of the context menu
|
||||
that opens when you right-click a card in the deck editor's printing selector.
|
||||
|
||||
- **Load Custom Image...** asks for a picture file and installs it for the card through
|
||||
CardPictureLoader::saveCardImageToLocalStorage() with `allowOverwrite == true`.
|
||||
- **One entry per alternate printing** (labeled `<set> <collector number>`): selecting one hands the card to
|
||||
CardPictureLoader::installPrintingOverride(), which resolves that printing's artwork - enqueueing a load and waiting
|
||||
for the `CardInfo::pixmapUpdated` signal if it is not cached yet - and persists it for the card.
|
||||
- **Clear Custom Image** calls CardPictureLoader::deleteAllLocalOverrides() to remove every stored override image of the
|
||||
card, after which normal resolution resumes. The entry is only enabled while CardPictureLoader::hasLocalOverrides()
|
||||
reports at least one stored file.
|
||||
|
||||
Overrides are stored as `.png` files in `downloadedPics/` under the export naming scheme configured on the "Storage"
|
||||
settings page - which is exactly why the local matcher also looks into the `downloadedPics/` root (see above). They are
|
||||
written with `allowOverwrite == true`, so an override always replaces whatever the filesystem cache previously saved for
|
||||
that spelling; only *automatic* cache writes are prevented from clobbering it. Overriding a card with its own current
|
||||
printing is a no-op (the UI omits it from the menu), and an override whose artwork fails to resolve surfaces the
|
||||
"failed" card back instead of a silent no-op while any override already on disk is left in place and re-displayed.
|
||||
|
||||
# URL Generation and Resolution
|
||||
|
||||
When no local image is available and downloading is enabled, the network loader starts working through a list of
|
||||
candidate URLs. This list is managed by CardPictureToLoad and is built in two steps.
|
||||
|
||||
First, CardPictureToLoad::extractSetsSorted() collects all sets the card has printings in and sorts them by set
|
||||
priority. Unless the user disabled per-printing art ("Override all card art with personal set preference (Pre-ProviderID
|
||||
change behavior)"), the set that
|
||||
matches the requested printing's provider ID is moved to the front, so the exact printing is always attempted first.
|
||||
|
||||
For each set, CardPictureToLoad::populateSetUrls() builds an ordered URL list:
|
||||
|
||||
1. A custom URL defined for that printing via the `picurl` property in the card database, if present.
|
||||
2. The configured download URL templates, in priority order (Deck Editor → "URL Download Priority").
|
||||
|
||||
URL templates are transformed into concrete URLs by CardPictureToLoad::transformUrl(), which substitutes reference
|
||||
points. `!name!`, `!setcode!` and friends substitute card and printing data, while the `!set:<property>!` and
|
||||
`!prop:<property>!` reference points resolve a property of the printing or of the card respectively. The canonical list
|
||||
of all reference points with examples, including the `_fill_with_` and `_substr_` modifiers, lives in
|
||||
@ref custom_card_pictures.
|
||||
|
||||
The `!set:...!` and `!prop:...!` reference points also support two modifiers:
|
||||
|
||||
- `_fill_with_<text>` pads the value with the given text, right-aligned, e.g. `!set:num_fill_with_000!` turns collector
|
||||
number `1` into `001`. If the value is longer than the fill text, the template is invalidated.
|
||||
- `_substr_<start>_<length>` extracts a substring, e.g. `!set:num_substr_2_2!` takes two characters starting at the
|
||||
third. If the substring would extend past the end of the value, the template is invalidated.
|
||||
|
||||
Substituted values are percent-encoded. If a template asks for a property the card or printing does not have (or one of
|
||||
the modifiers invalidates it), the template yields no URL and is skipped; the next template is tried instead.
|
||||
|
||||
\attention Custom URLs should start with `http://` or `https://`. The scheme is not validated before the URL is handed
|
||||
to QNetworkAccessManager, so a template without an absolute scheme may silently fail to download; prefer HTTPS where the
|
||||
provider allows it.
|
||||
|
||||
The resolution order is: for the current set, try each URL in the list; when all URLs for a set are exhausted, move to
|
||||
the next set; when every set is exhausted, the load fails. A failed load is reported through the NULL-pixmap mechanism
|
||||
described in the PixmapCacheKeys and ProviderIDs section above.
|
||||
|
||||
Several mechanisms influence the resolution process:
|
||||
|
||||
- **Rate limiting.** The worker allows roughly 10 requests per second globally. A server that answers with HTTP 429
|
||||
gets its per-host allowance halved; the first 429 for a host is waited out (honoring the `Retry-After` header if
|
||||
present) and the same URL retried, while a second 429 makes the loader fall through to the other configured sources.
|
||||
When all sources are exhausted the request is deferred with some random jitter and retried once the back-off expires.
|
||||
- **Redirects.** Replies with a redirect status (301, 302, 303, 305, 307, 308) are followed and recorded in the
|
||||
redirect cache as described in the Redirect Cache section above.
|
||||
- **Blacklisted images.** Gatherer returns the card back image for cards it does not know. A few known MD5 hashes of
|
||||
that image are blacklisted, so such a "successful" download is treated as not found instead of being shown.
|
||||
- **WebP.** Images detected as WebP (RIFF/WEBP header) are decoded through QMovie instead of QImageReader.
|
||||
- **Downloads disabled.** When "Download card pictures on the fly" is disabled and the network cache method is active,
|
||||
requests use Qt's `AlwaysCache` policy so that only previously cached images are served.
|
||||
|
||||
A user-facing reference for writing download URL templates, including more worked examples, is available at
|
||||
@ref custom_card_pictures.
|
||||
184
docs/doxygen/pages/developer_documentation/logging.md
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
@page logging Logging
|
||||
|
||||
Cockatrice uses QtLogging from the QtCore module for its logging. See
|
||||
the [official documentation](https://doc.qt.io/qt-6/qtlogging.html) for further details.
|
||||
|
||||
# Log Message Pattern
|
||||
|
||||
Any message logged through the QtLogging system automatically conforms to this message pattern:
|
||||
|
||||
Generic:
|
||||
|
||||
```
|
||||
[<timestamp> <log_level>] [<class:function>] - <message> [<filename>:<line_no>]
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
[2025-12-05 14:48:25.908 I] [MainWindow::startupConfigCheck] - Startup: found config with current version [window_main.cpp:951]
|
||||
```
|
||||
|
||||
For more information, see [Logging Setup](#logging-setup).
|
||||
|
||||
# Log Level and Categories
|
||||
|
||||
\note The default log level for the application is info.
|
||||
|
||||
This means that you should only use qInfo() in production-level code if you are truly sure that this message is
|
||||
beneficial to end-users and other developers. As a general rule, if your functionality logs to info more than twice in
|
||||
response to a user interaction, you are advised to consider moving some of these logs down to the debug level.
|
||||
|
||||
\warning You are strongly advised to avoid the use of the generic logging macros (e.g. qDebug(), qInfo(), qWarn()).
|
||||
|
||||
\note You should instead use the corresponding category logging macros (qCDebug(), qCInfo(), qCWarn()) and define
|
||||
logging
|
||||
categories for your log statements.
|
||||
|
||||
Example:
|
||||
|
||||
```c++
|
||||
in .h
|
||||
|
||||
inline Q_LOGGING_CATEGORY(ExampleCategory, "cockatrice_example_category");
|
||||
inline Q_LOGGING_CATEGORY(ExampleSubCategory, "cockatrice_example_category.sub_category");
|
||||
|
||||
in .cpp
|
||||
|
||||
qCInfo(ExampleCategory) << "Info level logs are usually sent through the main category"
|
||||
qCDebug(ExampleSubCategory) << "Debug level logs are permitted their own category to allow selective silencing"
|
||||
```
|
||||
|
||||
For more information on how to enable or disable logging categories,
|
||||
see [Logging Configuration](#logging-configuration).
|
||||
|
||||
# Logging Configuration
|
||||
|
||||
For configuring our logging, we use the qtlogging.ini, located under cockatrice/resources/config/qtlogging.ini, which is
|
||||
baked into the application in release version and set as the QT_LOGGING_CONF environment variable in main.cpp.
|
||||
|
||||
```c++
|
||||
#ifdef Q_OS_APPLE
|
||||
// <build>/cockatrice/cockatrice.app/Contents/MacOS/cockatrice
|
||||
const QByteArray configPath = "../../../qtlogging.ini";
|
||||
#elif defined(Q_OS_UNIX)
|
||||
// <build>/cockatrice/cockatrice
|
||||
const QByteArray configPath = "./qtlogging.ini";
|
||||
#elif defined(Q_OS_WIN)
|
||||
// <build>/cockatrice/Debug/cockatrice.exe
|
||||
const QByteArray configPath = "../qtlogging.ini";
|
||||
#else
|
||||
const QByteArray configPath = "";
|
||||
#endif
|
||||
|
||||
if (!qEnvironmentVariableIsSet(("QT_LOGGING_CONF"))) {
|
||||
// Set the QT_LOGGING_CONF environment variable
|
||||
qputenv("QT_LOGGING_CONF", configPath);
|
||||
}
|
||||
```
|
||||
|
||||
For more information on how to use this file and on how Qt evaluates which logging rules/file to use, please
|
||||
see the [official Qt documentation](https://doc.qt.io/qt-6/qloggingcategory.html#configuring-categories).
|
||||
|
||||
Some examples:
|
||||
|
||||
```
|
||||
# Turn off all logging except everything from card_picture_loader and all sub categories
|
||||
|
||||
[Rules]
|
||||
# The default log level is info
|
||||
*.debug = false
|
||||
*.info = false
|
||||
*.warning = false
|
||||
*.critical = false
|
||||
*.fatal = false
|
||||
|
||||
card_picture_loader.* = true
|
||||
```
|
||||
|
||||
```
|
||||
# Turn off all logging except info level logs from card_picture_loader and all sub categories
|
||||
|
||||
[Rules]
|
||||
# The default log level is info
|
||||
*.debug = false
|
||||
*.info = false
|
||||
*.warning = false
|
||||
*.critical = false
|
||||
*.fatal = false
|
||||
|
||||
card_picture_loader.*.info = true
|
||||
```
|
||||
|
||||
```
|
||||
[Rules]
|
||||
# Turn on debug level logs for card_picture_loader but keep logging for sub categories suppressed
|
||||
*.debug = false
|
||||
|
||||
card_picture_loader.debug = true
|
||||
```
|
||||
|
||||
```
|
||||
[Rules]
|
||||
# Turn on all logs for worker subcategory of card_picture_loader
|
||||
*.debug = false
|
||||
|
||||
card_picture_loader.worker = true
|
||||
```
|
||||
|
||||
```
|
||||
[Rules]
|
||||
# Turn off some noisy and irrelevant startup logging for local development
|
||||
*.debug = false
|
||||
|
||||
qt_translator = false
|
||||
window_main.* = false
|
||||
release_channel = false
|
||||
spoiler_background_updater = false
|
||||
theme_manager = false
|
||||
sound_engine = false
|
||||
tapped_out_interface = false
|
||||
card_database = false
|
||||
card_database.loading = false
|
||||
card_database.loading.success_or_failure = true
|
||||
cockatrice_xml.* = false
|
||||
```
|
||||
|
||||
# Logging Setup
|
||||
|
||||
This is achieved through our logging setup in @ref main.cpp, where we set the message pattern and install a custom
|
||||
logger which replaces the full file path at the end with just the file name (Qt shows the full and quite lengthy path by
|
||||
default).
|
||||
|
||||
```c++
|
||||
qSetMessagePattern(
|
||||
"\033[0m[%{time yyyy-MM-dd h:mm:ss.zzz} "
|
||||
"%{if-debug}\033[36mD%{endif}%{if-info}\033[32mI%{endif}%{if-warning}\033[33mW%{endif}%{if-critical}\033[31mC%{"
|
||||
"endif}%{if-fatal}\033[1;31mF%{endif}\033[0m] [%{function}] - %{message} [%{file}:%{line}]");
|
||||
QApplication app(argc, argv);
|
||||
|
||||
QObject::connect(&app, &QApplication::lastWindowClosed, &app, &QApplication::quit);
|
||||
|
||||
qInstallMessageHandler(CockatriceLogger);
|
||||
```
|
||||
|
||||
```c++
|
||||
static void CockatriceLogger(QtMsgType type, const QMessageLogContext &ctx, const QString &message)
|
||||
{
|
||||
QString logMessage = qFormatLogMessage(type, ctx, message);
|
||||
|
||||
// Regular expression to match the full path in the square brackets and extract only the filename and line number
|
||||
QRegularExpression regex(R"(\[(?:.:)?[\/\\].*[\/\\]([^\/\\]+\:\d+)\])");
|
||||
QRegularExpressionMatch match = regex.match(logMessage);
|
||||
|
||||
if (match.hasMatch()) {
|
||||
// Extract the filename and line number (e.g., "main.cpp:211")
|
||||
QString filenameLine = match.captured(1);
|
||||
|
||||
// Replace the full path in square brackets with just the filename and line number
|
||||
logMessage.replace(match.captured(0), QString("[%1]").arg(filenameLine));
|
||||
}
|
||||
|
||||
Logger::getInstance().log(type, ctx, logMessage);
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
@page game_event_handler GameEventHandler
|
||||
|
||||
## Overview
|
||||
|
||||
`GameEventHandler` is the central coordinator for **game-wide commands and events**.
|
||||
It acts as the bridge between the networking layer (protobuf messages received from
|
||||
the server) and the local game model and UI.
|
||||
|
||||
Unlike `PlayerEventHandler`, which is responsible for player-scoped state and zones,
|
||||
`GameEventHandler` handles:
|
||||
|
||||
- Global game flow (turns, phases, host changes)
|
||||
- Player and spectator lifecycle events
|
||||
- Chat and logging events
|
||||
- Dispatching incoming events to the appropriate subsystem
|
||||
- Sending locally initiated commands to the server
|
||||
|
||||
In short, it is the **top-level event dispatcher** for an active game.
|
||||
|
||||
---
|
||||
|
||||
## Responsibilities
|
||||
|
||||
`GameEventHandler` has four primary responsibilities:
|
||||
|
||||
### 1. Sending game-wide commands
|
||||
|
||||
UI actions that affect the game as a whole (e.g. advancing the turn, changing phases,
|
||||
sending chat messages) are translated into protobuf commands and sent to the server
|
||||
via this class.
|
||||
|
||||
Examples include:
|
||||
|
||||
- Advancing or reversing the turn order
|
||||
- Conceding or unconceding
|
||||
- Changing the active phase
|
||||
- Leaving the game
|
||||
- Sending chat messages
|
||||
|
||||
These commands are wrapped in `PendingCommand` instances so their lifecycle and
|
||||
responses can be tracked.
|
||||
|
||||
---
|
||||
|
||||
### 2. Processing incoming game events
|
||||
|
||||
Incoming server messages arrive as a `GameEventContainer`.
|
||||
`GameEventHandler` is responsible for:
|
||||
|
||||
- Emitting lifecycle signals before and after processing
|
||||
- Dispatching each event to the correct handler
|
||||
- Forwarding player-scoped events to `PlayerEventHandler` instances
|
||||
- Handling spectator-only and game-global events directly
|
||||
|
||||
This design keeps networking concerns isolated from game logic and UI code.
|
||||
|
||||
---
|
||||
|
||||
### 3. Managing global game state transitions
|
||||
|
||||
Certain events affect the entire game state and require coordinated updates across
|
||||
multiple subsystems. Examples include:
|
||||
|
||||
- Full game state synchronization
|
||||
- Active player or phase changes
|
||||
- Game host changes
|
||||
- Game closure
|
||||
- Turn reversal
|
||||
|
||||
`GameEventHandler` ensures these events are processed in a consistent order and
|
||||
that all interested systems are notified via Qt signals.
|
||||
|
||||
---
|
||||
|
||||
### 4. Emitting UI and logging signals
|
||||
|
||||
Rather than directly manipulating UI widgets, `GameEventHandler` emits
|
||||
high-level signals that are consumed by:
|
||||
|
||||
- Game widgets
|
||||
- Player and spectator lists
|
||||
- Message and event logs
|
||||
- Status indicators (ready state, deck selection, connection state)
|
||||
|
||||
This keeps the handler independent of concrete UI implementations.
|
||||
|
||||
---
|
||||
|
||||
## Relationship to PlayerEventHandler
|
||||
|
||||
`GameEventHandler` and `PlayerEventHandler` work together but have distinct roles:
|
||||
|
||||
| GameEventHandler | PlayerEventHandler |
|
||||
| ----------------- | ---------------------- |
|
||||
| Global game state | Per-player state |
|
||||
| Turn / phase flow | Zones and cards |
|
||||
| Player join/leave | Player actions |
|
||||
| Spectator events | Player-specific events |
|
||||
| Chat dispatch | Card and zone updates |
|
||||
|
||||
When a server event is associated with a specific player, `GameEventHandler`
|
||||
routes it to the corresponding `PlayerEventHandler`. Events without a player
|
||||
context are handled directly.
|
||||
|
||||
---
|
||||
|
||||
## Event Processing Flow
|
||||
|
||||
A typical incoming event flow looks like this:
|
||||
|
||||
1. `AbstractClient` receives a `GameEventContainer`
|
||||
2. `GameEventHandler::processGameEventContainer()` is called
|
||||
3. `containerProcessingStarted()` is emitted
|
||||
4. Each event is:
|
||||
- Handled directly **or**
|
||||
- Forwarded to a `PlayerEventHandler`
|
||||
5. Logging and UI signals are emitted as needed
|
||||
6. `containerProcessingDone()` is emitted
|
||||
|
||||
This structured flow makes it easy to:
|
||||
|
||||
- Suppress UI updates during replays
|
||||
- Handle reconnections cleanly
|
||||
- Detect flood protection or error states
|
||||
|
||||
---
|
||||
|
||||
## Command Lifecycle
|
||||
|
||||
Outgoing commands follow a similar structured path:
|
||||
|
||||
1. UI action triggers a `handle*()` method
|
||||
2. A protobuf command is constructed
|
||||
3. The command is wrapped in a `PendingCommand`
|
||||
4. `sendGameCommand()` sends it to the server
|
||||
5. `commandFinished()` receives the response
|
||||
6. Errors or flood conditions are handled centrally
|
||||
|
||||
This approach avoids duplicated error handling across UI code.
|
||||
|
||||
---
|
||||
|
||||
## Design Goals
|
||||
|
||||
`GameEventHandler` is designed to be:
|
||||
|
||||
- **Centralized** – one entry point for all game-wide events
|
||||
- **UI-agnostic** – communicates via signals, not widgets
|
||||
- **Predictable** – well-defined event ordering and lifecycle
|
||||
- **Composable** – works in concert with `PlayerEventHandler`
|
||||
- **Testable** – logic is separated from rendering
|
||||
|
||||
---
|
||||
|
||||
## Related Classes
|
||||
|
||||
- @ref GameEventHandler
|
||||
- @ref PlayerEventHandler
|
||||
- `GameEventContainer`
|
||||
- `PendingCommand`
|
||||
- `AbstractClient`
|
||||
- `AbstractGame`
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- @ref GameLogic
|
||||
- @ref PlayerEventHandler
|
||||
- @ref GameState
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
@page developer_reference_network_client Client Networking (Overview)
|
||||
|
||||
The clients response to various network protocol events and associated handling are described here.
|
||||
|
||||
For information about game scoped events, see:
|
||||
|
||||
- @subpage game_event_handler
|
||||
|
||||
Certain game scoped events may be forwarded to player based handlers. For more information, see:
|
||||
|
||||
- @subpage player_event_handler
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
@page player_event_handler PlayerEventHandler
|
||||
|
||||
## Overview
|
||||
|
||||
`PlayerEventHandler` is responsible for applying **player-scoped game events**
|
||||
to a single `Player` instance. These events modify the player’s board state,
|
||||
zones, cards, counters, arrows, and associated UI and logging output.
|
||||
|
||||
Each `PlayerEventHandler` instance is bound **1:1 to a Player** and is invoked
|
||||
exclusively by @ref GameEventHandler after basic routing and validation of
|
||||
incoming server events.
|
||||
|
||||
This class represents the lowest-level authoritative application of game state
|
||||
changes on the client.
|
||||
|
||||
---
|
||||
|
||||
## Scope and Authority
|
||||
|
||||
`PlayerEventHandler` operates under the following guarantees:
|
||||
|
||||
- All incoming events are **authoritative** and already validated by the server
|
||||
- Events refer to valid players, zones, and card identifiers
|
||||
- Ordering is guaranteed by the server and preserved by `GameEventHandler`
|
||||
|
||||
As a result, the handler does **not** perform rule validation or permission
|
||||
checks. Its responsibility is to *apply* state, not *decide* legality.
|
||||
|
||||
---
|
||||
|
||||
## Responsibilities
|
||||
|
||||
### 1. Applying player-specific state changes
|
||||
|
||||
The primary responsibility of `PlayerEventHandler` is to mutate player-owned
|
||||
state, including:
|
||||
|
||||
- Cards (`CardItem`)
|
||||
- Zones (`CardZoneLogic`)
|
||||
- Counters (card-level and player-level)
|
||||
- Attachments and arrows
|
||||
- Zone configuration flags (reveal / peek behavior)
|
||||
|
||||
Many handlers update both **logical state** and **visual/UI state** in tandem.
|
||||
|
||||
---
|
||||
|
||||
### 2. Coordinating complex card movement
|
||||
|
||||
Some events—most notably card movement—require coordinated updates across
|
||||
multiple systems. For example, `eventMoveCard()`:
|
||||
|
||||
- Removes the card from the source zone
|
||||
- Updates ownership, visibility, and identity
|
||||
- Handles attachments and arrow cleanup
|
||||
- Emits undo-draw or move logs
|
||||
- Inserts the card into the destination zone
|
||||
- Updates menus, hover state, and graphics
|
||||
|
||||
This makes `PlayerEventHandler` intentionally stateful and tightly coupled to
|
||||
the board implementation.
|
||||
|
||||
---
|
||||
|
||||
### 3. Emitting logging signals
|
||||
|
||||
Rather than writing directly to logs or widgets, `PlayerEventHandler` emits
|
||||
structured Qt signals describing *what happened*, including:
|
||||
|
||||
- Chat messages
|
||||
- Card movement
|
||||
- Shuffles and randomization
|
||||
- Reveals and peeks
|
||||
- Counter and attribute changes
|
||||
|
||||
These signals are consumed by logging systems and UI components, keeping
|
||||
presentation concerns out of the handler.
|
||||
|
||||
Logging signals may be emitted **before or after mutation**, depending on
|
||||
whether later changes would invalidate log data (e.g. card identity).
|
||||
|
||||
---
|
||||
|
||||
### 4. Handling cross-player interactions
|
||||
|
||||
Although bound to a single player, some events necessarily affect other
|
||||
players’ state, including:
|
||||
|
||||
- Moving cards between players
|
||||
- Attaching cards to another player’s permanents
|
||||
- Creating arrows targeting other players or cards
|
||||
|
||||
In these cases, `PlayerEventHandler` performs the minimal required mutation
|
||||
while respecting ownership boundaries enforced elsewhere.
|
||||
|
||||
---
|
||||
|
||||
## Event Dispatch Model
|
||||
|
||||
`PlayerEventHandler` exposes a single public entry point:
|
||||
|
||||
- `processGameEvent()`
|
||||
|
||||
This method:
|
||||
|
||||
1. Receives a generic `GameEvent`
|
||||
2. Switches on `GameEventType`
|
||||
3. Extracts the appropriate protobuf extension
|
||||
4. Forwards the event to a typed handler
|
||||
|
||||
This keeps the event routing centralized and makes it easy to audit coverage
|
||||
when new game events are introduced.
|
||||
|
||||
Unhandled events are logged as warnings.
|
||||
|
||||
---
|
||||
|
||||
## Event Categories
|
||||
|
||||
For clarity, event handlers are grouped conceptually into:
|
||||
|
||||
- **Chat and randomization**
|
||||
- Chat messages
|
||||
- Shuffles
|
||||
- Dice rolls
|
||||
|
||||
- **Arrows and targeting**
|
||||
- Create / delete arrows
|
||||
|
||||
- **Card and token creation**
|
||||
- Token generation
|
||||
- Counter creation
|
||||
|
||||
- **Card attributes and counters**
|
||||
- Tapped state
|
||||
- Power/toughness
|
||||
- Annotations
|
||||
- Card- and player-level counters
|
||||
|
||||
- **Zone-level operations**
|
||||
- Card movement
|
||||
- Zone dumps
|
||||
- Card destruction
|
||||
- Attachments
|
||||
|
||||
- **Draw and reveal**
|
||||
- Drawing cards
|
||||
- Reveals, peeks, and reveal windows
|
||||
|
||||
- **Zone configuration**
|
||||
- Always-reveal and always-look-at-top-card flags
|
||||
|
||||
This grouping mirrors the structure of the header file and reflects the
|
||||
conceptual responsibilities of the class.
|
||||
|
||||
---
|
||||
|
||||
## Relationship to GameEventHandler
|
||||
|
||||
`PlayerEventHandler` is never instantiated or invoked directly by UI code.
|
||||
Instead:
|
||||
|
||||
1. `GameEventHandler` receives a `GameEventContainer`
|
||||
2. Player-scoped events are routed to the appropriate `PlayerEventHandler`
|
||||
3. Global or spectator events are handled elsewhere
|
||||
|
||||
This separation keeps game-wide logic decoupled from player board state and
|
||||
makes reconnection and replay handling simpler.
|
||||
|
||||
---
|
||||
|
||||
## Design Intent
|
||||
|
||||
`PlayerEventHandler` is designed to be:
|
||||
|
||||
- **Authoritative** – applies server state exactly as received
|
||||
- **Stateful** – maintains consistency across cards, zones, and UI
|
||||
- **Explicit** – one handler per event type
|
||||
- **UI-aware** – updates views and menus as part of state mutation
|
||||
- **Auditable** – easy to trace what code handles which event
|
||||
|
||||
---
|
||||
|
||||
## Related Classes
|
||||
|
||||
- @ref PlayerEventHandler
|
||||
- @ref GameEventHandler
|
||||
- `Player`
|
||||
- `CardItem`
|
||||
- `CardZoneLogic`
|
||||
- `GameEvent`
|
||||
- `GameEventContext`
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- @ref GameLogicPlayers
|
||||
- @ref GameLogic
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
@page developer_reference_network_overview Network (Overview)
|
||||
|
||||
This page provides information about the networking performed by the client and server.
|
||||
|
||||
For information about the protocol, see @subpage developer_reference_protocol
|
||||
|
||||
For information about the clients response to and handling of protocol messages, see @subpage developer_reference_network_client
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
@page developer_reference_protocol Protocol (Overview)
|
||||
|
||||
For an overview of the protocol used by the Cockatrice client and server, see:
|
||||
|
||||
- @subpage developer_reference_protocol_overview
|
||||
- @subpage protocol_game_command
|
||||
|
||||
For client → server communication, see:
|
||||
|
||||
- @subpage protocol_command_container
|
||||
|
||||
For server → client communication, see:
|
||||
|
||||
- @subpage protocol_server_message
|
||||
- @subpage protocol_response
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
@page protocol_command_container CommandContainer (Protocol Concept)
|
||||
|
||||
@section cc_overview Overview
|
||||
|
||||
CommandContainer is the client-to-server message envelope used for all
|
||||
client requests in the Cockatrice protocol.
|
||||
|
||||
Every client-initiated action is transmitted as a CommandContainer.
|
||||
The server never sends CommandContainer messages.
|
||||
|
||||
This is a protocol-level abstraction and should not be confused with the
|
||||
generated protobuf accessors or wire-level encoding.
|
||||
|
||||
@section cc_lifecycle Lifetime and Ownership
|
||||
|
||||
- CommandContainers are created by clients and consumed by the server.
|
||||
- Each container represents one logical request.
|
||||
- Containers are processed atomically and in order of arrival.
|
||||
|
||||
A container may generate:
|
||||
- Exactly one RESPONSE message, and
|
||||
- Zero or more EVENT messages.
|
||||
|
||||
@section cc_cmd_id Command Identification
|
||||
|
||||
The @c cmd_id field is a client-assigned identifier used to correlate
|
||||
responses with requests.
|
||||
|
||||
Rules:
|
||||
- @c cmd_id is optional but strongly recommended
|
||||
- The server echoes @c cmd_id only in RESPONSE messages
|
||||
- EVENT messages are never associated with a @c cmd_id
|
||||
|
||||
The server does not enforce uniqueness of @c cmd_id values.
|
||||
|
||||
@section cc_context Command Context
|
||||
|
||||
Certain command domains require additional context:
|
||||
|
||||
- Room commands require @c room_id
|
||||
- Game commands require @c game_id
|
||||
|
||||
Context fields are ignored for command domains that do not require them.
|
||||
|
||||
If a required context field is missing or invalid, the server responds with
|
||||
@c RespContextError.
|
||||
|
||||
@section cc_invariants Invariants
|
||||
|
||||
The following rules are enforced by the server:
|
||||
|
||||
- Exactly one command domain must be populated
|
||||
- Commands may be batched only within the same domain
|
||||
- Context fields must match the active command domain
|
||||
|
||||
Violations of these rules result in @c RespInvalidCommand.
|
||||
|
||||
@section cc_domains Command Domains
|
||||
|
||||
CommandContainer supports the following mutually exclusive command domains:
|
||||
|
||||
- Session commands
|
||||
- Authentication
|
||||
- User discovery
|
||||
- Private messaging
|
||||
|
||||
- Room commands
|
||||
- Chat
|
||||
- Game creation
|
||||
- Room membership management
|
||||
|
||||
- Game commands
|
||||
- In-game actions
|
||||
- State mutations
|
||||
|
||||
- Moderator commands
|
||||
- User moderation
|
||||
- Room moderation
|
||||
|
||||
- Admin commands
|
||||
- Server administration
|
||||
|
||||
@section cc_batching Command Batching
|
||||
|
||||
A CommandContainer may contain multiple commands of the same domain.
|
||||
|
||||
Batching guarantees:
|
||||
- Commands are processed in the order they appear in the container
|
||||
- All commands in the batch share the same context
|
||||
|
||||
Partial failure behavior is implementation-defined and may vary by domain.
|
||||
|
||||
@section cc_dispatch Dispatch
|
||||
|
||||
CommandContainers are dispatched by
|
||||
Server_ProtocolHandler::processCommandContainer().
|
||||
|
||||
Dispatch is performed by inspecting which command domain is populated.
|
||||
Only the first populated domain is considered.
|
||||
|
||||
@section cc_errors Error Handling
|
||||
|
||||
Common error responses include:
|
||||
- @c RespLoginNeeded – client is not authenticated
|
||||
- @c RespInvalidCommand – malformed container or invalid domain usage
|
||||
- @c RespContextError – missing or invalid room/game context
|
||||
|
||||
@section cc_related Related Concepts
|
||||
|
||||
- @ref protocol_server_message "ServerMessage"
|
||||
- @ref protocol_client_states "Client State Machine"
|
||||
|
||||
@see Server_ProtocolHandler::processCommandContainer
|
||||
|
||||
|
|
@ -0,0 +1,529 @@
|
|||
@page protocol_game_command GameCommand (Protocol Concept)
|
||||
|
||||
# Game Commands Reference
|
||||
|
||||
This document describes game-level commands (`GameCommandType`) and how they are
|
||||
handled across the server and client.
|
||||
|
||||
Flow overview:
|
||||
|
||||
Client
|
||||
→ GameCommand
|
||||
→ Server command handler (`Server_Player`, `Server_AbstractParticipant`, or `Server_Game`)
|
||||
→ GameEvent(s)
|
||||
→ PlayerEventHandler::event*
|
||||
|
||||
## Command Mapping
|
||||
|
||||
### `GAME_SAY` (1002)
|
||||
|
||||
**Purpose:** Send a chat message during a game.
|
||||
|
||||
**Server:**
|
||||
- `Server_AbstractParticipant::cmdGameSay`
|
||||
- Validates spectator chat permissions
|
||||
- Applies chat flood protection
|
||||
- Emits `Event_GameSay`
|
||||
- Logs the message to the server database
|
||||
|
||||
**Client:**
|
||||
- `PlayerEventHandler::eventGameSay`
|
||||
- Adds the chat message to the game log
|
||||
|
||||
---
|
||||
|
||||
### `SHUFFLE` (1003)
|
||||
|
||||
**Purpose:** Shuffle a card zone (usually library).
|
||||
|
||||
**Server:**
|
||||
- `Server_Player::cmdShuffle`
|
||||
- Only allows shuffling the deck zone
|
||||
- Rejects if the game has not started or the player has conceded
|
||||
- Supports partial-range shuffles (`start` / `end`)
|
||||
- Emits `Event_Shuffle`
|
||||
- Re-reveals the top card if the deck is being tracked
|
||||
|
||||
**Client:**
|
||||
- `PlayerEventHandler::eventShuffle`
|
||||
- Closes affected library views
|
||||
- Clears any revealed top card if necessary
|
||||
- Updates deck graphics
|
||||
- Logs the shuffle
|
||||
|
||||
---
|
||||
|
||||
### `ROLL_DIE` (1005)
|
||||
|
||||
**Purpose:** Roll one or more dice.
|
||||
|
||||
**Server:**
|
||||
- Computes random die results
|
||||
- Emits `Event_RollDie`
|
||||
|
||||
**Client:**
|
||||
- `PlayerEventHandler::eventRollDie`
|
||||
- Displays the sorted roll results in the game log
|
||||
|
||||
---
|
||||
|
||||
### `DRAW_CARDS` (1006)
|
||||
|
||||
**Purpose:** Draw cards from deck.
|
||||
|
||||
**Server:**
|
||||
- `Server_Player::cmdDrawCards`
|
||||
- Rejects if the game has not started or the player has conceded
|
||||
- Moves cards from deck to hand
|
||||
- Sends full card information privately to the drawing player
|
||||
- Sends only the draw count to all other players
|
||||
- Tracks drawn cards for undo
|
||||
- Updates revealed-top-card state when necessary
|
||||
- Emits `Event_DrawCards`
|
||||
|
||||
**Client:**
|
||||
- `PlayerEventHandler::eventDrawCards`
|
||||
- Moves cards from library to hand
|
||||
- Reveals card identities only when included in the event
|
||||
- Updates both zones
|
||||
- Logs the draw
|
||||
|
||||
---
|
||||
|
||||
### `UNDO_DRAW` (1007)
|
||||
|
||||
**Purpose:** Undo a previous draw.
|
||||
|
||||
**Server:**
|
||||
- `Server_Player::cmdUndoDraw`
|
||||
- Rejects if the game has not started or the player has conceded
|
||||
- Uses the tracked draw history (`lastDrawList`)
|
||||
- Moves the most recently drawable card from hand back to the top of the deck
|
||||
- Emits `Event_GameLogNotice` if undo is no longer possible
|
||||
|
||||
**Client:**
|
||||
- Processed as a normal `MOVE_CARD`
|
||||
- Detects `Context_UndoDraw`
|
||||
- Logs the undo draw instead of a normal move
|
||||
|
||||
---
|
||||
|
||||
### `FLIP_CARD` (1008)
|
||||
|
||||
**Purpose:** Flip a card face up or face down.
|
||||
|
||||
**Server:**
|
||||
- Updates the card's face-down state
|
||||
- Reveals identity when turning face up
|
||||
- Emits `Event_FlipCard`
|
||||
|
||||
**Client:**
|
||||
- `PlayerEventHandler::eventFlipCard`
|
||||
- Updates the card identity when revealed
|
||||
- Changes face-up / face-down state
|
||||
- Updates card menus
|
||||
- Logs the flip
|
||||
|
||||
---
|
||||
|
||||
### `ATTACH_CARD` (1009)
|
||||
|
||||
**Purpose:** Attach one card to another.
|
||||
|
||||
**Server:**
|
||||
- Updates attachment relationships
|
||||
- Emits `Event_AttachCard`
|
||||
|
||||
**Client:**
|
||||
- `PlayerEventHandler::eventAttachCard`
|
||||
- Updates parent/child attachment links
|
||||
- Reorganizes affected zones
|
||||
- Logs attach or detach operations
|
||||
- Refreshes card actions
|
||||
|
||||
---
|
||||
|
||||
### `CREATE_TOKEN` (1010)
|
||||
|
||||
**Purpose:** Create a token card.
|
||||
|
||||
**Server:**
|
||||
- Creates a new token card
|
||||
- Emits `Event_CreateToken`
|
||||
|
||||
**Client:**
|
||||
- `PlayerEventHandler::eventCreateToken`
|
||||
- Creates the token object
|
||||
- Applies token properties (PT, color, annotation, face-down state)
|
||||
- Adds it to the requested zone
|
||||
- Logs token creation
|
||||
|
||||
---
|
||||
|
||||
### `CREATE_ARROW` (1011)
|
||||
|
||||
**Purpose:** Create a visual arrow.
|
||||
|
||||
**Server:**
|
||||
- Creates a visual arrow
|
||||
- Emits `Event_CreateArrow`
|
||||
|
||||
**Client:**
|
||||
- `PlayerEventHandler::eventCreateArrow`
|
||||
- Creates the arrow graphics
|
||||
- Resolves endpoint card names
|
||||
- Logs arrow creation
|
||||
|
||||
---
|
||||
|
||||
### `DELETE_ARROW` (1012)
|
||||
|
||||
**Purpose:** Remove a visual arrow.
|
||||
|
||||
**Server:**
|
||||
- Removes an existing arrow
|
||||
- Emits `Event_DeleteArrow`
|
||||
|
||||
**Client:**
|
||||
- `PlayerEventHandler::eventDeleteArrow`
|
||||
- Removes the arrow graphics
|
||||
|
||||
---
|
||||
|
||||
### `SET_CARD_ATTR` (1013)
|
||||
|
||||
**Purpose:** Set a card attribute.
|
||||
|
||||
**Server:**
|
||||
- Updates one or more card attributes
|
||||
- Emits `Event_SetCardAttr`
|
||||
|
||||
**Client:**
|
||||
- `PlayerEventHandler::eventSetCardAttr`
|
||||
- Updates tapped state, color, annotation, power/toughness, face-down state, attacking state, and related visuals
|
||||
- Logs attribute changes where appropriate
|
||||
|
||||
---
|
||||
|
||||
### `SET_CARD_COUNTER` (1014)
|
||||
|
||||
**Purpose:** Set a card counter.
|
||||
|
||||
**Server:**
|
||||
- Updates the specified card counter
|
||||
- Emits `Event_SetCardCounter`
|
||||
|
||||
**Client:**
|
||||
- `PlayerEventHandler::eventSetCardCounter`
|
||||
- Updates the displayed counter value
|
||||
- Refreshes card actions
|
||||
- Logs the counter change
|
||||
|
||||
---
|
||||
|
||||
### `INC_CARD_COUNTER` (1015)
|
||||
|
||||
**Purpose:** Increment a card counter.
|
||||
|
||||
**Server:**
|
||||
- Normalizes to a set-counter operation
|
||||
- Emits `Event_SetCardCounter`
|
||||
|
||||
**Client:**
|
||||
- Processed identically to `SET_CARD_COUNTER`
|
||||
|
||||
---
|
||||
|
||||
### `READY_START` (1016)
|
||||
|
||||
**Purpose:** Mark player as ready.
|
||||
|
||||
**Server:**
|
||||
- Marks the player as ready
|
||||
- Starts the game once all required players are ready
|
||||
|
||||
**Client:**
|
||||
- Reflected through updated game state events
|
||||
|
||||
---
|
||||
|
||||
### `CONCEDE` (1017)
|
||||
|
||||
**Purpose:** Concede the game.
|
||||
|
||||
**Server:**
|
||||
- Marks the player as conceded
|
||||
- Returns borrowed cards where appropriate
|
||||
- Ends the game if only one player remains
|
||||
|
||||
**Client:**
|
||||
- Reflected through updated game state events
|
||||
|
||||
---
|
||||
|
||||
### `INC_COUNTER` (1018)
|
||||
|
||||
**Purpose:** Increment a player counter.
|
||||
|
||||
**Server:**
|
||||
- `Server_Player::cmdIncCounter`
|
||||
- Rejects if the game has not started or the player has conceded
|
||||
- Updates the counter value
|
||||
- Emits `Event_SetCounter` only if the value changed
|
||||
|
||||
**Client:**
|
||||
- `PlayerEventHandler::eventSetCounter`
|
||||
- Updates the displayed player counter
|
||||
- Logs the change
|
||||
|
||||
---
|
||||
|
||||
### `CREATE_COUNTER` (1019)
|
||||
|
||||
**Purpose:** Create a player counter.
|
||||
|
||||
**Server:**
|
||||
- `Server_Player::cmdCreateCounter`
|
||||
- Rejects if the game has not started or the player has conceded
|
||||
- Allocates a new counter ID
|
||||
- Creates the counter
|
||||
- Emits `Event_CreateCounter`
|
||||
|
||||
**Client:**
|
||||
- `PlayerEventHandler::eventCreateCounter`
|
||||
- Creates the local player counter
|
||||
|
||||
---
|
||||
|
||||
### `SET_COUNTER` (1020)
|
||||
|
||||
**Purpose:** Set a player counter value.
|
||||
|
||||
**Server:**
|
||||
- `Server_Player::cmdSetCounter`
|
||||
- Rejects if the game has not started or the player has conceded
|
||||
- Updates the counter value
|
||||
- Emits `Event_SetCounter` only if the value changed
|
||||
|
||||
**Client:**
|
||||
- `PlayerEventHandler::eventSetCounter`
|
||||
- Updates the counter value
|
||||
- Logs the change
|
||||
|
||||
---
|
||||
|
||||
### `DEL_COUNTER` (1021)
|
||||
|
||||
**Purpose:** Delete a player counter.
|
||||
|
||||
**Server:**
|
||||
- `Server_Player::cmdDelCounter`
|
||||
- Rejects if the game has not started or the player has conceded
|
||||
- Deletes the counter
|
||||
- Emits `Event_DelCounter`
|
||||
|
||||
**Client:**
|
||||
- `PlayerEventHandler::eventDelCounter`
|
||||
- Removes the counter
|
||||
|
||||
---
|
||||
|
||||
### `NEXT_TURN` (1022)
|
||||
|
||||
**Purpose:** Advance to the next turn.
|
||||
|
||||
**Server:**
|
||||
- `Server_Player::cmdNextTurn`
|
||||
- Rejects if the game has not started
|
||||
- Conceded players cannot advance turns unless acting as a judge
|
||||
- Calls `Server_Game::nextTurn()`
|
||||
|
||||
**Client:**
|
||||
- Reflected through subsequent active-player and active-phase events
|
||||
|
||||
---
|
||||
|
||||
### `SET_ACTIVE_PHASE` (1023)
|
||||
|
||||
**Purpose:** Set active phase.
|
||||
|
||||
**Server:**
|
||||
- `Server_Player::cmdSetActivePhase`
|
||||
- Rejects if the game has not started
|
||||
- Judges may change the phase at any time
|
||||
- Normal players may only change the phase during their own active turn
|
||||
- Calls `Server_Game::setActivePhase()`
|
||||
|
||||
**Client:**
|
||||
- Reflected through updated phase events
|
||||
|
||||
---
|
||||
|
||||
### `DUMP_ZONE` (1024)
|
||||
|
||||
**Purpose:** Dump zone contents.
|
||||
|
||||
**Server:**
|
||||
- Generates a zone summary
|
||||
- Emits `Event_DumpZone`
|
||||
|
||||
**Client:**
|
||||
- `PlayerEventHandler::eventDumpZone`
|
||||
- Logs the zone contents summary
|
||||
|
||||
---
|
||||
|
||||
### `REVEAL_CARDS` (1026)
|
||||
|
||||
**Purpose:** Reveal specific cards.
|
||||
|
||||
**Server:**
|
||||
- Reveals cards to one or more players
|
||||
- Emits `Event_RevealCards`
|
||||
|
||||
**Client:**
|
||||
- `PlayerEventHandler::eventRevealCards`
|
||||
- Reveals cards in-place or in a reveal window
|
||||
- Supports temporary peeks
|
||||
- Updates revealed top cards
|
||||
- Logs the reveal
|
||||
|
||||
---
|
||||
|
||||
### `MOVE_CARD` (1027)
|
||||
|
||||
**Purpose:** Move a card between zones.
|
||||
|
||||
**Server:**
|
||||
- Moves cards between zones
|
||||
- Updates ownership and attachments as needed
|
||||
- Emits `Event_MoveCard`
|
||||
|
||||
**Client:**
|
||||
- `PlayerEventHandler::eventMoveCard`
|
||||
- Moves the card between zones
|
||||
- Updates ownership, attachments, IDs and revealed information
|
||||
- Handles undo-draw context
|
||||
- Refreshes menus and zone layouts
|
||||
- Logs the move
|
||||
|
||||
---
|
||||
|
||||
### `SET_SIDEBOARD_PLAN` (1028)
|
||||
|
||||
**Purpose:** Set sideboard configuration.
|
||||
|
||||
**Server:**
|
||||
- `Server_Player::cmdSetSideboardPlan`
|
||||
- Allowed only before readying for the game
|
||||
- Requires a loaded deck
|
||||
- Requires sideboarding to be unlocked
|
||||
- Stores the current sideboard plan
|
||||
|
||||
**Client:**
|
||||
- No game event is generated
|
||||
|
||||
---
|
||||
|
||||
### `DECK_SELECT` (1029)
|
||||
|
||||
**Purpose:** Select a deck.
|
||||
|
||||
**Server:**
|
||||
- `Server_Player::cmdDeckSelect`
|
||||
- Allowed only before the game starts
|
||||
- Loads a deck from either the database or serialized deck data
|
||||
- Locks sideboarding
|
||||
- Updates player properties
|
||||
- Emits `Event_PlayerPropertiesChanged`
|
||||
- Attaches `Context_DeckSelect`
|
||||
- Returns the selected deck in the command response
|
||||
|
||||
**Client:**
|
||||
- Reflected through updated player properties and command response
|
||||
|
||||
---
|
||||
|
||||
### `SET_SIDEBOARD_LOCK` (1030)
|
||||
|
||||
**Purpose:** Lock or unlock sideboarding.
|
||||
|
||||
**Server:**
|
||||
- `Server_Player::cmdSetSideboardLock`
|
||||
- Allowed only before readying
|
||||
- Toggles sideboard locking
|
||||
- Clears pending sideboard plans when locking
|
||||
- Emits `Event_PlayerPropertiesChanged`
|
||||
- Attaches `Context_SetSideboardLock`
|
||||
|
||||
**Client:**
|
||||
- Reflected through updated player properties
|
||||
|
||||
---
|
||||
|
||||
### `CHANGE_ZONE_PROPERTIES` (1031)
|
||||
|
||||
**Purpose:** Change zone properties.
|
||||
|
||||
**Server:**
|
||||
- `Server_Player::cmdChangeZoneProperties`
|
||||
- Delegates to the abstract implementation
|
||||
- Updates top-card revelation if required
|
||||
- Emits `Event_ChangeZoneProperties`
|
||||
|
||||
**Client:**
|
||||
- `PlayerEventHandler::eventChangeZoneProperties`
|
||||
- Updates always-reveal-top-card and always-look-at-top-card settings
|
||||
- Logs property changes
|
||||
|
||||
---
|
||||
|
||||
### `UNCONCEDE` (1032)
|
||||
|
||||
**Purpose:** Undo a concede.
|
||||
|
||||
**Server:**
|
||||
- Restores the player's active status
|
||||
|
||||
**Client:**
|
||||
- Reflected through updated game state events
|
||||
|
||||
---
|
||||
|
||||
### `JUDGE` (1033)
|
||||
|
||||
**Purpose:** Execute commands on behalf of another player.
|
||||
|
||||
**Server:**
|
||||
- `Server_AbstractParticipant::cmdJudge`
|
||||
- Requires judge privileges
|
||||
- Executes embedded commands as the selected player
|
||||
- Marks resulting events as judge-forced
|
||||
|
||||
**Client:**
|
||||
- Transparent; resulting events are processed normally
|
||||
|
||||
---
|
||||
|
||||
### `REVERSE_TURN` (1034)
|
||||
|
||||
**Purpose:** Reverse turn order.
|
||||
|
||||
**Server:**
|
||||
- `Server_Player::cmdReverseTurn`
|
||||
- Conceded non-judge players cannot use the command
|
||||
- Delegates to `Server_AbstractParticipant::cmdReverseTurn`
|
||||
- Toggles turn order
|
||||
- Emits `Event_ReverseTurn`
|
||||
|
||||
**Client:**
|
||||
- Reflected by subsequent active-player progression
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- Game commands are handled by `Server_Player`, `Server_AbstractParticipant`, or `Server_Game`, depending on the command.
|
||||
- Only commands that emit `GameEvent`s produce corresponding `PlayerEventHandler` callbacks.
|
||||
- Some commands modify server state without generating a dedicated client event.
|
||||
- Judge commands are transport wrappers that execute other game commands on behalf of another player.
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
@page developer_reference_protocol_overview Protocol (Overview)
|
||||
|
||||
# Cockatrice Server Protocol – Overview
|
||||
|
||||
## Purpose
|
||||
|
||||
This document describes the Cockatrice client/server protocol as implemented
|
||||
by the Cockatrice server. It is intended for developers implementing clients
|
||||
or modifying the server.
|
||||
|
||||
The protocol is **stateful**, **event-driven**, and **asynchronous**.
|
||||
|
||||
---
|
||||
|
||||
## High-Level Properties
|
||||
|
||||
- Encoding: Google Protocol Buffers
|
||||
- Transport: TCP or WebSocket (transport-agnostic at protocol level)
|
||||
- Directionality:
|
||||
- Clients send `CommandContainer`
|
||||
- Server sends `ServerMessage`
|
||||
- Responses and events are interleaved
|
||||
- Clients must handle unsolicited events at any time
|
||||
|
||||
---
|
||||
|
||||
## Message Envelopes
|
||||
|
||||
### Client → Server: CommandContainer
|
||||
|
||||
A `CommandContainer` represents one logical client request.
|
||||
|
||||
Protocol invariants:
|
||||
- Exactly one command domain may be used per container
|
||||
- Multiple commands of the same domain may be batched
|
||||
- Context is provided via `room_id` or `game_id` when required
|
||||
|
||||
Command domains:
|
||||
- Session commands
|
||||
- Room commands
|
||||
- Game commands
|
||||
- Moderator commands
|
||||
- Admin commands
|
||||
|
||||
---
|
||||
|
||||
### Server → Client: ServerMessage
|
||||
|
||||
A `ServerMessage` represents either:
|
||||
- A response to a command (`RESPONSE`)
|
||||
- An unsolicited event (`*_EVENT`)
|
||||
|
||||
Protocol invariants:
|
||||
- Events may be delivered before or after responses
|
||||
- Responses reference the original command via `cmd_id`
|
||||
- Events are never correlated to a command
|
||||
|
||||
---
|
||||
|
||||
## Asynchronous Model
|
||||
|
||||
Clients MUST NOT assume request/response ordering.
|
||||
|
||||
Example valid sequence:
|
||||
|
||||
1. Client sends JOIN_ROOM
|
||||
2. Server sends room chat history events
|
||||
3. Server sends room join notifications
|
||||
4. Server sends JOIN_ROOM response
|
||||
|
||||
---
|
||||
|
||||
## Versioning
|
||||
|
||||
The server reports a protocol version during connection initialization.
|
||||
Clients MUST verify compatibility before sending commands.
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
@page protocol_response Response (Protocol Concept)
|
||||
|
||||
## Overview
|
||||
|
||||
`Response` is the server-to-client message sent immediately after a command, using the same `cmd_id` to link to the originating command.
|
||||
It provides a `Response::ResponseCode` and optionally a `Response::ResponseType` to guide client handling.
|
||||
|
||||
## Fields
|
||||
|
||||
- **cmd_id** (`uint64`, required) — Command ID this response corresponds to
|
||||
- **response_code** (`Response::ResponseCode`, optional) — Outcome of the command
|
||||
|
||||
## Response Codes
|
||||
|
||||
All possible outcome codes are defined in Response::ResponseCode
|
||||
|
||||
These describe the result of the command, e.g., success, failure, or special conditions like bans or registration requirements.
|
||||
|
||||
## Response Types
|
||||
|
||||
Responses are routed according to Response::ResponseType
|
||||
|
||||
Each `ResponseType` corresponds to a high-level category, like `JOIN_ROOM` or `DECK_UPLOAD`.
|
||||
|
||||
## Extensions
|
||||
|
||||
- Protobuf extensions from 100 to max are reserved for future use
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
@page protocol_server_message ServerMessage (Protocol Concept)
|
||||
|
||||
## Overview
|
||||
|
||||
`ServerMessage` is the server-to-client message envelope used for all
|
||||
communication originating from the Cockatrice server.
|
||||
|
||||
The server never sends `CommandContainer` messages, and clients never send
|
||||
`ServerMessage` messages.
|
||||
|
||||
A `ServerMessage` represents either:
|
||||
|
||||
- A direct response to a client command, or
|
||||
- An unsolicited event emitted by the server
|
||||
|
||||
This document describes `ServerMessage` as a protocol-level concept.
|
||||
It should not be confused with generated protobuf accessors or wire-level
|
||||
encoding details.
|
||||
|
||||
---
|
||||
|
||||
## Lifetime and Delivery
|
||||
|
||||
`ServerMessage` instances are emitted by the server and delivered
|
||||
asynchronously to clients.
|
||||
|
||||
Delivery guarantees:
|
||||
|
||||
- Messages are delivered in the order sent by the server
|
||||
- Messages may be delivered at any time
|
||||
- Events may be delivered before or after responses
|
||||
|
||||
Clients **must** be prepared to handle `ServerMessage` objects at all times,
|
||||
regardless of pending requests.
|
||||
|
||||
---
|
||||
|
||||
## Message Type
|
||||
|
||||
Each `ServerMessage` contains **exactly one payload**, identified by the
|
||||
`message_type` field.
|
||||
|
||||
The following message types are supported:
|
||||
|
||||
- `RESPONSE`
|
||||
- `SESSION_EVENT`
|
||||
- `ROOM_EVENT`
|
||||
- `GAME_EVENT_CONTAINER`
|
||||
|
||||
A message containing multiple payloads or an unknown type is considered
|
||||
malformed.
|
||||
|
||||
---
|
||||
|
||||
## RESPONSE
|
||||
|
||||
`RESPONSE` messages are direct replies to client-issued `CommandContainer`
|
||||
messages.
|
||||
|
||||
Characteristics:
|
||||
|
||||
- Contain a `Response` payload
|
||||
- Echo the client-assigned `cmd_id`
|
||||
- Indicate success or failure of the request
|
||||
|
||||
A single `CommandContainer` results in **at most one** `RESPONSE`.
|
||||
|
||||
`RESPONSE` messages may be preceded or followed by event messages.
|
||||
|
||||
---
|
||||
|
||||
## Events
|
||||
|
||||
Event messages are unsolicited notifications emitted by the server.
|
||||
|
||||
Event message types include:
|
||||
|
||||
- `SESSION_EVENT`
|
||||
- `ROOM_EVENT`
|
||||
- `GAME_EVENT_CONTAINER`
|
||||
|
||||
Event messages:
|
||||
|
||||
- Are not correlated to a specific client command
|
||||
- Do not include a `cmd_id`
|
||||
- May be delivered at any time
|
||||
|
||||
Clients **must** process events independently of responses.
|
||||
|
||||
---
|
||||
|
||||
## Event Scopes
|
||||
|
||||
### Session Events
|
||||
|
||||
Session events are global to the client session and may include:
|
||||
|
||||
- Server notifications
|
||||
- Private messages
|
||||
- User status updates
|
||||
|
||||
---
|
||||
|
||||
### Room Events
|
||||
|
||||
Room events are scoped to a specific room and are delivered only to clients
|
||||
currently present in that room.
|
||||
|
||||
---
|
||||
|
||||
### Game Events
|
||||
|
||||
Game events are scoped to a specific game and are delivered only to
|
||||
participating clients.
|
||||
|
||||
Game events are grouped within a `GameEventContainer` to allow batching.
|
||||
|
||||
---
|
||||
|
||||
## Event Batching
|
||||
|
||||
Some `ServerMessage` types may contain multiple logical events.
|
||||
|
||||
- `GameEventContainer` may batch multiple game events
|
||||
- Other event types represent a single logical event
|
||||
|
||||
Clients must process all contained events **in order**.
|
||||
|
||||
---
|
||||
|
||||
## Error Semantics
|
||||
|
||||
Errors are communicated **exclusively** via `RESPONSE` messages.
|
||||
|
||||
Event messages are never used to signal command failure.
|
||||
|
||||
Common error responses include:
|
||||
|
||||
- `RespInvalidCommand`
|
||||
- `RespLoginNeeded`
|
||||
- `RespContextError`
|
||||
- `RespChatFlood`
|
||||
|
||||
---
|
||||
|
||||
## Related Concepts
|
||||
|
||||
- CommandContainer
|
||||
- Client State Machine
|
||||
- Response
|
||||
|
||||
---
|
||||
|
||||
## Reference Proto Definition
|
||||
|
||||
```proto
|
||||
message ServerMessage {
|
||||
enum MessageType {
|
||||
RESPONSE = 0;
|
||||
SESSION_EVENT = 1;
|
||||
GAME_EVENT_CONTAINER = 2;
|
||||
ROOM_EVENT = 3;
|
||||
}
|
||||
|
||||
optional MessageType message_type = 1;
|
||||
optional Response response = 2;
|
||||
optional SessionEvent session_event = 3;
|
||||
optional GameEventContainer game_event_container = 4;
|
||||
optional RoomEvent room_event = 5;
|
||||
}
|
||||
```
|
||||
71
docs/doxygen/pages/developer_documentation/primer_cards.md
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
@page primer_cards A Primer on Cards
|
||||
|
||||
# The Cockatrice Card Library
|
||||
|
||||
All non-gui code related to cards used by Cockatrice is contained within libcockatrice_card.
|
||||
|
||||
# A Basic Card Object: CardInfo
|
||||
|
||||
A CardInfo object is the translational unit used by the CardDatabase to represent an on-disk XML entry as an in-memory
|
||||
Qt object.
|
||||
|
||||
For a complete overview of the fields that define a CardInfo object, see the class documentation and more specifically
|
||||
@ref PrivateCardProperties.
|
||||
|
||||
These fields correspond to either entries or a combination of entries in the XML card entry to which the
|
||||
CardDatabaseParser applies special logic to map, populate or determine their respective values in the CardInfo object.
|
||||
|
||||
Any property to which special parsing is not applied is stored in CardInfo::properties and may be retrieved or set with
|
||||
CardInfo::getProperty() or CardInfo::setProperty() as well as CardInfo::getProperties() to get a collection of the
|
||||
property keys a CardInfo contains.
|
||||
|
||||
For more information on how special fields contained within @ref PrivateCardProperties are parsed, see
|
||||
CockatriceXml4Parser::loadCardsFromXml().
|
||||
|
||||
Apart from access to the basic and extended properties of a card, CardInfo also provides two important mechanisms to us.
|
||||
The first is the CardInfo::pixmapUpdated() signal, which allows the CardPictureLoader to signal when it is done
|
||||
manipulating (in most cases: loading) the QPixmap of an ExactCard::getPixmapCacheKey(), where the ExactCard::card
|
||||
corresponds to this CardInfo object.
|
||||
|
||||
Put simply: Whenever the CardPictureLoader loads a new PrintingInfo which is not already in the cache, it emits this
|
||||
signal so that any widget using a CardInfo object can update the display of it to possibly use this new QPixmap.
|
||||
|
||||
\attention It should be noted that it is not possible to use CardPictureLoader::getPixmap() with anything but an
|
||||
ExactCard, which is a CardInfo object associated with a PrintingInfo, i.e. a definitive printing of a specific card
|
||||
|
||||
The other signal, CardInfo::cardInfoChanged() conceptually works much the same way, except it is concerned with the
|
||||
properties of the CardInfo object.
|
||||
|
||||
# Getting specific: PrintingInfo and ExactCard
|
||||
|
||||
## Printing Info
|
||||
|
||||
A CardInfo object describes the basic properties of a card in much the same way that we say "Two cards with the same
|
||||
name are and should be equal if their properties are equal". However, there are certain (mostly visual) properties which
|
||||
might differ between printings of a card but still conceptually classify two instances of a card as "the same card".
|
||||
The most obvious example to this are cards which fundamentally share all properties except the artwork depicted on the
|
||||
card.
|
||||
|
||||
We refer to such differences as Printings and track them in the PrintingInfo class. A PrintingInfo is always related to
|
||||
the CardSet that introduced the variation. Multiple variations can exist in the same CardSet and the respective
|
||||
PrintingInfo::getProperty() can be used to determine the differences, such as the collector numbers on the printings, if
|
||||
there are any.
|
||||
|
||||
## Exact Card
|
||||
|
||||
A CardInfo object is used to hold the functional properties of a card and a PrintingInfo object can be used to hold the
|
||||
visual properties of a card. To represent a 'physical' card, as in, a concrete immutable instance of a card, we can use
|
||||
ExactCard, which combines a CardInfo and a PrintingInfo object into one class. The class is used as a container around
|
||||
CardInfo objects whenever the user (and thus the program) expects to be presented with a defined card printing.
|
||||
|
||||
# Using Cards
|
||||
|
||||
For more information on the XML database schema which the CardDatabaseParser parses to generate CardInfo objects, see
|
||||
@ref card_database_schema_and_parsing.
|
||||
|
||||
For more information on querying the CardDatabase for CardInfo objects, see @ref querying_the_card_database.
|
||||
|
||||
For more information on displaying CardInfo and ExactCard objects using Qt Widgets, see @ref displaying_cards.
|
||||
|
||||
For more information on how card pictures are loaded from disk or the network, see @ref loading_card_pictures as well as
|
||||
the CardPictureLoader documentation.
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
@page querying_the_card_database Querying the Card Database
|
||||
|
||||
# The CardDatabaseQuerier Class
|
||||
|
||||
The CardDatabaseQuerier is the only class used for querying the database. The CardDatabase is an in-memory map and thus
|
||||
provides no structured query language. CardDatabaseQuerier offers methods to retrieve cards by name, by providerID or in
|
||||
bulk, as CardSet%s.
|
||||
|
||||
## Obtaining a handle to the CardDatabaseQuerier for usage
|
||||
|
||||
To obtain the CardDatabaseQuerier related to the global CardDatabase singleton, use CardDatabaseManager::query().
|
||||
|
||||
## Querying for known cards
|
||||
|
||||
There are, essentially, two ways to ensure card equality, with the second being optional but necessitating the first.
|
||||
These two ways are CardInfo name equality and PrintingInfo provider ID equality.
|
||||
|
||||
Because of this, most queries require, at the very least, a card name to match against and optionally a providerID to
|
||||
narrow the results.
|
||||
|
||||
### Generic Card Infos
|
||||
|
||||
To check if a card with the exact provided name exists as a CardInfo in the CardDatabase use,
|
||||
CardDatabaseQuerier::getCardInfo() or CardDatabaseQuerier::getCardInfos() for multiple cards.
|
||||
|
||||
### Guessing Cards
|
||||
|
||||
If the exact name might not be present in the CardDatabase, you can use CardDatabaseQuerier::getCardBySimpleName(),
|
||||
which automatically simplifies the card name and matches it against simplified card names in the CardDatabase.
|
||||
|
||||
Alternatively, you can use CardDatabaseQuerier::lookupCardByName(), which first attempts an exact match search and then
|
||||
uses CardDatabaseQuerier::getCardBySimpleName() as a fallback.
|
||||
|
||||
### ExactCard%s
|
||||
|
||||
To obtain an ExactCard from the CardDatabaseQuerier, you must use a CardRef as a parameter to
|
||||
CardDatabaseQuerier::getCard(), CardDatabaseQuerier::getCards(), or CardDatabaseQuerier::guessCard().
|
||||
|
||||
CardRef is a simple struct consisting of a card name and a card provider ID as QString%s.
|
||||
12
docs/doxygen/pages/index.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
@mainpage Documentation
|
||||
|
||||
<div align="center">
|
||||
<img src="cockatrice_logo.svg" style="width: 300px; max-width: 100%; height: auto;">
|
||||
</div>
|
||||
|
||||
Welcome to the Cockatrice code documentation.
|
||||
|
||||
- @subpage user_reference
|
||||
- @subpage developer_reference
|
||||
|
||||
Please also check the <a href="https://cockatrice.github.io/" target="_blank" rel="noopener noreferrer">Cockatrice Webpage</a> or our <a href="https://github.com/Cockatrice/Cockatrice" target="_blank" rel="noopener noreferrer">Code Repository</a> for general information.
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
@page custom_card_pictures Custom Card Pictures
|
||||
|
||||
There are four ways to make Cockatrice use custom artwork for your cards:
|
||||
|
||||
- Placing image files in the **CUSTOM pictures folder**.
|
||||
- Providing a **custom card database** that points each printing at a picture URL via the `picurl` property.
|
||||
- Writing your **own download URL templates**.
|
||||
- Setting an **image override** for a single card from inside the deck editor.
|
||||
|
||||
Each of these is described below. If pictures are missing or wrong, see @ref fixing_card_pictures instead.
|
||||
|
||||
# Custom Pictures Folder (CUSTOM)
|
||||
|
||||
Any image file placed in the CUSTOM folder is used as the card picture, and no download is attempted for cards that
|
||||
match a file there.
|
||||
|
||||
- The folder is `<pictures directory>/CUSTOM/`. The pictures directory is configured on the 'General' settings tab,
|
||||
under 'Directories' → 'Pictures directory'.
|
||||
- Any image format Qt can decode is accepted (PNG, JPG/JPEG, WebP, GIF, BMP, ...); the file extension is not filtered,
|
||||
so even an extension-less file is picked up if the decoder recognizes its content.
|
||||
- Files are indexed by their name, so you can organize them into subfolders freely.
|
||||
- New or changed files are picked up automatically within a few seconds — no client restart is required.
|
||||
|
||||
The file name must match the card using one of the naming schemes below. Both `_` and `-` are accepted as separators,
|
||||
and the file extension is ignored when matching:
|
||||
|
||||
| Scheme | Example file name |
|
||||
| --------------------------- | ------------------------------------------------------- |
|
||||
| Card Name | `Example Card.png` |
|
||||
| Card Name + Set | `Example Card_DDL.png` |
|
||||
| Card Name + Set + Collector | `Example Card_DDL_43.png` |
|
||||
| Set + Collector + Card Name | `DDL_43_Example Card.png` |
|
||||
| Card Name + Provider ID | `Example Card_0b23cdc8-d413-4fb1-8470-474221b10fe2.png` |
|
||||
|
||||
The name used for matching is the *corrected* card name. Correction removes the split-card separator ` // ` and the
|
||||
characters reserved in Windows file names (`* < > : " \ ?` and control characters), and turns `/` into a space, so the
|
||||
"Example // Card" card is matched by a file named `ExampleCard.png`, not `Example // Card.png`. Most other punctuation
|
||||
(commas, apostrophes, `!`, ...) is left untouched.
|
||||
|
||||
\attention A file in the CUSTOM folder always wins over downloaded pictures, even if it is the wrong image. Delete the
|
||||
file if you want to see the downloaded artwork again.
|
||||
|
||||
The naming conventions are the same as those recognized in the set-named subfolders and in `downloadedPics`, and are
|
||||
documented for developers in @ref loading_card_pictures.
|
||||
|
||||
# Custom Card Database (picurl)
|
||||
|
||||
If you maintain your own card database (see the
|
||||
[Custom Cards & Sets](https://github.com/Cockatrice/Cockatrice/wiki/Custom-Cards-&-Sets) wiki), each printing's `<set>`
|
||||
tag can carry a `picurl` attribute containing a full URL for that printing's picture:
|
||||
|
||||
```xml
|
||||
<set picurl="https://example.com/cards/example-card.jpg" ...>
|
||||
```
|
||||
|
||||
Cockatrice tries this URL **before** the configured download URL templates, so it is the most direct way to provide
|
||||
custom artwork for a specific printing.
|
||||
|
||||
- The URL should start with `http://` or `https://`; the scheme is not validated, so make sure it is absolute or the
|
||||
download may silently fail.
|
||||
- When you change a `picurl` for a card whose picture was already downloaded and cached, delete the stored images
|
||||
(Storage tab → 'Delete Saved Images' / 'Delete Cached Images') so Cockatrice fetches the new URL.
|
||||
|
||||
# Custom Download URL Templates
|
||||
|
||||
The built-in download URLs are templates: Cockatrice replaces reference points in the URL with information about the
|
||||
card and its printing. You can write your own templates in 'Cockatrice → Settings' (Ctrl + Shift + P by default), on
|
||||
the 'Deck Editor' tab, in the 'URL Download Priority' section.
|
||||
|
||||
The following reference points are available:
|
||||
|
||||
| Reference point | Description | Example |
|
||||
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
|
||||
| `!name!` | Card name | `Example Card` |
|
||||
| `!name_lower!` | Card name, lower case | `example card` |
|
||||
| `!corrected_name!` | Corrected card name | `ExampleCard` (instead of "Example // Card") |
|
||||
| `!corrected_name_lower!` | Corrected card name, lower case | `examplecard` |
|
||||
| `!sflang!` | Scryfall language code for the current client language; defaults to English when the language has no localized images | `en`, `zhs` |
|
||||
| `!setcode!` / `!setcode_lower!` | Set code | `EXM` / `exm` |
|
||||
| `!setname!` / `!setname_lower!` | Full set name | `Exemplary Set` / `exemplary set` |
|
||||
| `!set:<property>!` | A property of this printing, e.g. `muid` (Gatherer multiverse ID), `uuid` (Scryfall UUID), `num` (collector number), `rarity` | `373549` |
|
||||
| `!prop:<property>!` | A property of the card, e.g. `side` (front/back), `colors`, `cmc`, `coloridentity`, `type`, `pt`, and the format legality statuses | `front` |
|
||||
|
||||
The `!set:...!` and `!prop:...!` reference points support two modifiers:
|
||||
|
||||
- `_fill_with_<text>` pads the value with the given text, right-aligned, e.g. `!set:num_fill_with_000!` turns collector
|
||||
number `1` into `001`. If the value is longer than the fill text, the template is skipped.
|
||||
- `_substr_<start>_<length>` extracts a substring, e.g. `!set:num_substr_2_2!` takes two characters starting at the
|
||||
third. If the substring would extend past the end of the value, the template is skipped.
|
||||
|
||||
Substituted values are URL-encoded. A template that asks for a property the card or printing does not have is skipped,
|
||||
and the next template in the list is tried instead.
|
||||
|
||||
\attention Custom URLs should start with `http://` or `https://`. As with `picurl`, the scheme is not validated before
|
||||
the URL is handed to QNetworkAccessManager, so use an absolute URL or the download may silently fail.
|
||||
|
||||
Some working examples:
|
||||
|
||||
```text
|
||||
https://cards.scryfall.io/large/!prop:side!/!set:uuid_substr_0_1!/!set:uuid_substr_1_1!/!set:uuid!.jpg
|
||||
https://api.scryfall.com/cards/!set:uuid!?format=image&face=!prop:side!
|
||||
https://api.scryfall.com/cards/multiverse/!set:muid!?format=image
|
||||
https://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=!set:muid!&type=card
|
||||
https://gatherer.wizards.com/Handlers/Image.ashx?name=!name!&type=card
|
||||
```
|
||||
|
||||
See the [Custom Picture Download URLs](https://github.com/Cockatrice/Cockatrice/wiki/Custom-Picture-Download-URLs)
|
||||
wiki for more examples and ideas.
|
||||
|
||||
\attention Keep in mind that templates using `!name!` or `!set:muid!` resolve by name or multiverse ID, not by the
|
||||
exact printing. Only the Scryfall `!set:uuid!` templates always return the exact printing requested. See
|
||||
@ref fixing_card_pictures for more on this.
|
||||
|
||||
# Image Overrides
|
||||
|
||||
The quickest way to give one card custom art is an image override: right-click the card in the deck editor's printing
|
||||
selector and open the **Image Overrides** submenu of the context menu.
|
||||
|
||||
- **Load Custom Image...** — choose a picture file (the dialog suggests PNG, JPG/JPEG and WebP); it becomes that card's
|
||||
artwork immediately.
|
||||
- **One entry per alternate printing** of the card, labeled `<set> <collector number>` (hovering an entry previews that
|
||||
printing's artwork). Selecting one makes the card use that exact printing's picture, so e.g. a basic land can be shown
|
||||
with any of its artworks.
|
||||
- **Clear Custom Image** — removes the stored override and returns the card to normal resolution. It is only available
|
||||
while the card has a stored override.
|
||||
|
||||
Overrides are stored as `.png` files in `<pictures directory>/downloadedPics/`, under the "Naming scheme" configured on
|
||||
the Storage settings page, and are matched the same way as downloaded images. Because local files are checked before any
|
||||
URL is requested, an override always wins over downloaded artwork and `picurl` for that card. The override exists only on
|
||||
the machine it was created on - it is not part of the deck file - so a card with a stored override shows normally on
|
||||
another computer.
|
||||
|
||||
\attention If you also keep a matching file in the CUSTOM folder, that file is matched before the override. When you
|
||||
change an override, use **Clear Custom Image** so the stored `.png` is replaced; manually deleting the file in
|
||||
`downloadedPics/` has the same effect.
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
@page creating_decks Creating Decks
|
||||
|
||||
Creating a new deck is done using either the \ref editing_decks_classic or \ref editing_decks_visual.
|
||||
|
||||
They can be accessed either by clicking the "Create Deck" button in the TabHome screen, which will open the default deck
|
||||
editor configured in the "User Interface" -> "Deck editor/storage settings" -> "Default deck editor type" setting, or
|
||||
by selecting "Deck Editor" or "Visual Deck Editor" under the "Tabs" application menu located at the top.
|
||||
|
||||
# Further References
|
||||
|
||||
- See @ref editing_decks for information on how to modify the attributes and contents of a deck in the Deck Editor
|
||||
widgets.
|
||||
- See @ref exporting_decks for information on how to store and persist your deck either in-client or to external
|
||||
services.
|
||||
- See @ref importing_decks for information on how to import existing decks either in-client or from external services
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
@page editing_decks Editing Decks
|
||||
|
||||
- @subpage editing_decks_classic
|
||||
- @subpage editing_decks_visual
|
||||
|
||||
- @subpage visual_database_display
|
||||
- @subpage editing_decks_printings
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
@page editing_decks_classic Classic Deck Editor
|
||||
|
||||
\image html classic_deck_editor.png width=900px
|
||||
|
||||
# Editing Basic Deck Information
|
||||
|
||||
Editing basic deck information is done through the deck dock widget (DeckEditorDeckDockWidget).
|
||||
|
||||
\image html deckeditordeckdockwidget.png
|
||||
|
||||
This widget allows editing:
|
||||
|
||||
- The name
|
||||
- The comments
|
||||
- The banner card, which is used to represent the deck in the visual deck storage
|
||||
- The tags, which are used for filtering in the visual deck storage
|
||||
|
||||
# Adding Cards
|
||||
|
||||
Adding cards is done using the list of cards in the database presented in the list view on the left.
|
||||
|
||||
\image html classic_database_display.png
|
||||
|
||||
Cards can be added by either double-clicking an entry, pressing return with an entry selected to add it to the mainboard
|
||||
or pressing ctrl/cmd + return to add it to the sideboard.
|
||||
|
||||
There are also buttons for these two functions on the right of the search bar above the list view.
|
||||
|
||||
\image html classic_database_display_add_buttons.png
|
||||
|
||||
# Modifying the Deck List
|
||||
|
||||
To modify or remove cards in the deck list, the tree list view in the deck dock widget can be used.
|
||||
|
||||
\image html deck_dock_deck_list.png
|
||||
|
||||
Just above the list, at the top right, there are four buttons to manipulate the currently selected card(s):
|
||||
|
||||
- Increment Card
|
||||
- Decrement Card
|
||||
- Remove Card
|
||||
- Switch Card between Mainboard and Sideboard
|
||||
|
||||
\image html deck_dock_deck_list_buttons.png
|
||||
|
||||
Additionally, there is a combo box above the list, which may be used to change how cards are grouped in the list
|
||||
display. This is only for visual display and does not affect how the list is saved.
|
||||
|
||||
\image html deck_dock_deck_list_group_by.png
|
||||
|
||||
# Modifying printings
|
||||
|
||||
For more information on modifying the printings in a deck see @ref editing_decks_printings
|
||||
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
@page editing_decks_printings Printing Selector
|
||||
|
||||
\image html printing_selector.png
|
||||
|
||||
# Purpose
|
||||
|
||||
The PrintingSelector allows editing the PrintingInfo associated with CardInfo%s contained in DeckList%s.
|
||||
|
||||
As described in PrintingInfo, a CardInfo can have different variations contained within the same or different CardSet%s.
|
||||
|
||||
PrintingSelector allows the user to choose a variation, as well as pinning a variation to be used as the preferred
|
||||
printing in all cases where the 'default' or 'most preferred' image should be shown.
|
||||
|
||||
# Pre-requisites (User Interface)
|
||||
|
||||
To use the printing selector, ensure that you are using the Post-ProviderID change behavior (Checkbox as shown in the
|
||||
image).
|
||||
|
||||
\image html printing_selector_pre_providerid.png
|
||||
|
||||
\attention This is the default behavior, unless you have explicitly changed it, in which case you will have seen a
|
||||
warning as follows. Once again, checking this box will *disable* the new behavior and revert you to the legacy system
|
||||
without the printing selector.
|
||||
|
||||
Disabling the printing selector:
|
||||
|
||||
\image html printing_selector_enable.png
|
||||
|
||||
Enabling the printing selector:
|
||||
|
||||
\image html printing_selector_disable.png
|
||||
|
||||
# Pre-requisites (Card Database)
|
||||
|
||||
The PrintingSelector requires each underlying CardInfo object to have a <set> entry with a *unique* providerID attribute
|
||||
for each variation that should be displayed.
|
||||
|
||||
Here is an example, that first defines three sets, and then the 'Ace of Hearts' card with three variations.
|
||||
|
||||
```xml
|
||||
<set rarity="common" uuid="00000000-0000-0000-0000-000000000033" num="51" muid="AH">PKR</set>
|
||||
<set rarity="common" uuid="00000000-0000-0000-0001-000000000033" num="51" muid="AHSTD">STD</set>
|
||||
<set rarity="common" uuid="00000000-0000-0000-0002-000000000033" num="51" muid="AHNBR">NBR</set>
|
||||
```
|
||||
|
||||
```xml
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<cockatrice_carddatabase version="4">
|
||||
<sets>
|
||||
<set>
|
||||
<name>PKR</name>
|
||||
<longname>Cockatrice Poker</longname>
|
||||
<settype>Custom</settype>
|
||||
<releasedate>2025-11-15</releasedate>
|
||||
</set>
|
||||
<set>
|
||||
<name>STD</name>
|
||||
<longname>Standard Poker</longname>
|
||||
<settype>core</settype>
|
||||
<releasedate>2025-11-14</releasedate>
|
||||
</set>
|
||||
<set>
|
||||
<name>NBR</name>
|
||||
<longname>Numbers</longname>
|
||||
<settype>core</settype>
|
||||
<releasedate>2025-11-14</releasedate>
|
||||
</set>
|
||||
</sets>
|
||||
<cards>
|
||||
<card>
|
||||
<name>Ace of Hearts</name>
|
||||
<text>Ace of Hearts</text>
|
||||
<prop>
|
||||
<layout>normal</layout>
|
||||
<side>front</side>
|
||||
<type>Ace</type>
|
||||
<maintype>Ace</maintype>
|
||||
<manacost>14</manacost>
|
||||
<cmc>14</cmc>
|
||||
<colors>H</colors>
|
||||
<coloridentity>H</coloridentity>
|
||||
<format-standard>legal</format-standard>
|
||||
</prop>
|
||||
<reverse-related>Joker</reverse-related>
|
||||
<token>0</token>
|
||||
<tablerow>1</tablerow>
|
||||
<cipt>0</cipt>
|
||||
<upsidedown>0</upsidedown>
|
||||
<set rarity="common" uuid="00000000-0000-0000-0000-000000000033" num="51" muid="AH">PKR</set>
|
||||
<set rarity="common" uuid="00000000-0000-0000-0001-000000000033" num="51" muid="AHSTD">STD</set>
|
||||
<set rarity="common" uuid="00000000-0000-0000-0002-000000000033" num="51" muid="AHNBR">NBR</set>
|
||||
</card>
|
||||
</cards>
|
||||
</cockatrice_carddatabase>
|
||||
```
|
||||
|
||||
# Using the Printing Selector
|
||||
|
||||
To use the PrintingSelector, first select a card in the DeckEditorDeckDockWidget:
|
||||
|
||||
\image html deck_dock_deck_list.png
|
||||
|
||||
You may add a variation to the mainboard by clicking the + and - buttons in the top row. The bottom row will add the
|
||||
variation to your sideboard.
|
||||
|
||||
\image html printing_selector.png
|
||||
|
||||
You can also right-click a variation to pin it, which will move it to the top of the variation list for easy access and
|
||||
additionally, use it as the default printing whenever the printing isn't explicitly specified (such as adding a card
|
||||
from the database in the deck editor).
|
||||
|
||||
Only one printing may be pinned at a time.
|
||||
|
||||
Additionally, the PrintingSelector offers a navigation bar at the bottom to navigate through cards in your deck, as well
|
||||
as an option to modify the variations of cards in bulk via the 'Bulk Selection' option.
|
||||
|
||||
\image html printing_selector_navigation.png
|
||||
|
||||
The search bar at the top may be used to filter the displayed variations and the cogwheel may be clicked to open up
|
||||
additional settings.
|
||||
|
||||
\image html printing_selector_options.png
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
@page editing_decks_visual Visual Deck Editor
|
||||
|
||||
\image html vde_overlap_layout_type_grouped.png width=1200px
|
||||
|
||||
# Editing Basic Deck Information
|
||||
|
||||
Editing basic deck information is done through the deck dock widget (DeckEditorDeckDockWidget).
|
||||
|
||||
\image html deckeditordeckdockwidget.png
|
||||
|
||||
This widget allows editing:
|
||||
|
||||
- The name
|
||||
- The comments
|
||||
- The banner card, which is used to represent the deck in the visual deck storage
|
||||
- The tags, which are used for filtering in the visual deck storage
|
||||
|
||||
# Adding Cards
|
||||
|
||||
Adding cards is done by either using the "Quick search and add card" search bar at the top of the "Visual Deck View" tab
|
||||
or by clicking on a picture of a card in the "Visual Database Display" tab.
|
||||
|
||||
See @ref visual_database_display for more information on how to utilize the visual database display.
|
||||
|
||||
# Modifying the Deck List
|
||||
|
||||
To modify or remove cards in the deck list, the tree list view in the deck dock widget can be used.
|
||||
|
||||
\image html deck_dock_deck_list.png
|
||||
|
||||
Just above the list, at the top right, there are four buttons to manipulate the currently selected card(s):
|
||||
|
||||
- Increment Card
|
||||
- Decrement Card
|
||||
- Remove Card
|
||||
- Switch Card between Mainboard and Sideboard
|
||||
|
||||
\image html deck_dock_deck_list_buttons.png
|
||||
|
||||
Additionally, there is a combo box above the list, which may be used to change how cards are grouped in the list
|
||||
display. This is only for visual display and does not affect how the list is saved.
|
||||
|
||||
\image html deck_dock_deck_list_group_by.png
|
||||
|
||||
# Modifying the visual deck layout
|
||||
|
||||
The visual deck editor displays cards visually, as opposed to simply in list form in the Deck Dock Widget. Each entry in
|
||||
the deck list is represented by a picture. These entries are grouped together under their respective sub-groups.
|
||||
Sub-groups may be collapsed (i.e. the pictures contained within them are hidden) by clicking on the name of the group in
|
||||
the banner.
|
||||
|
||||
Cards may be displayed in a "Flat" layout, which displays each picture next to each other and ensures full
|
||||
visibility for each card.
|
||||
|
||||
\image html vde_flat_layout_type_grouped.png width=1200px
|
||||
|
||||
or in an "Overlap" layout, which overlaps cards on top of each other (leaving the top 20% of
|
||||
the card uncovered so names remain readable) and arranges them in stacks to save space and allow for an easy overview.
|
||||
|
||||
\image html vde_overlap_layout_type_grouped.png width=1200px
|
||||
|
||||
Additionally, it is possible to change how the cards in the deck list are grouped by selecting a different grouping
|
||||
method from the combo box, either in the top left of the "Visual Deck View" tab or above the list view in the deck dock
|
||||
widget.
|
||||
|
||||
Example - Cards grouped by suit/color:
|
||||
|
||||
\image html vde_flat_layout_color_grouped.png width=1200px
|
||||
|
||||
Furthermore, it is possible to change how the cards are sorted within the sub-group. This is done by clicking on the
|
||||
button with the cogwheel icon next to the combo box that adjusts grouping in the top left of the "Visual Deck View" tab.
|
||||
This presents a list of available sort criteria, which may be rearranged to change their priorities.
|
||||
|
||||
# Modifying printings
|
||||
|
||||
For more information on modifying the printings in a deck see @ref editing_decks_printings
|
||||
|
||||
# Deck Analytics
|
||||
|
||||
The visual deck editor offers a "Deck Analytics" tab, which displays information about:
|
||||
|
||||
- The mana curve
|
||||
- The mana devotion
|
||||
- The mana base
|
||||
|
||||
\image html vde_deck_analytics.png width=1200px
|
||||
|
||||
# Sample Hand
|
||||
|
||||
The visual deck editor offers a "Sample Hand" tab, which allows simulating drawing a configurable amount of cards from
|
||||
the deck, which reduces the need to launch a single player game for testing purposes.
|
||||
|
||||
\image html vde_sample_hand.png width=1200px
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
@page exporting_decks Exporting Decks
|
||||
|
||||
# Where to export?
|
||||
|
||||
There are two screens in the client which can be used to import decks, depending on the context.
|
||||
|
||||
- The deck editor tab
|
||||
- The deck storage tab (not to be confused with the visual deck storage tab)
|
||||
|
||||
# The Deck Editor Tab
|
||||
|
||||
The deck editor tabs (Classic and Visual) offer three ways of export a deck:
|
||||
|
||||
- To a file on your local storage
|
||||
- To your clipboard
|
||||
- To an online service
|
||||
|
||||
## Local File Storage
|
||||
|
||||
To save a deck to a file on your local storage, select the "Save Deck" action in the "Deck Editor" or "Visual Deck
|
||||
Editor" menu in the application menu bar at the top of the screen. Alternatively, you can use the shortcut Ctrl/Cmd + S
|
||||
to access this action.
|
||||
|
||||
Selecting this action will open a file picker dialog provided by your operating system. Simply enter a file name and
|
||||
select a format (.cod is recommended) and confirm.
|
||||
|
||||
Just below the "Save Deck" action described above is the "Save Deck as..." option, which allows saving an existing file
|
||||
under a different filename, which is useful for saving a different version or copy of a deck.
|
||||
|
||||
## From Clipboard
|
||||
|
||||
To save a deck to your clipboard, select the "Save deck to clipboard..." action in the "Deck Editor" or "Visual Deck
|
||||
Editor" menu in the application menu bar at the top of the screen. Alternatively, you can use the shortcut Ctrl/Cmd +
|
||||
Shift + C or Ctrl/Cmd + Shift + R to access this action.
|
||||
|
||||
Selecting this action will save the currently open deck list to your clipboard.
|
||||
|
||||
Saving the decklist without annotations will export the decklist, with each card being described in the following format
|
||||
|
||||
```
|
||||
CARD_AMOUNT CARD_NAME (SET_SHORT_NAME) CARD_COLLECTOR_NUMBER
|
||||
```
|
||||
|
||||
There is also the (no set info) option, which will simply export each card as
|
||||
|
||||
```
|
||||
CARD_AMOUNT CARD_NAME
|
||||
```
|
||||
|
||||
Mainboard and sideboard are delimited by a newline like so:
|
||||
|
||||
```
|
||||
1 MainboardCard
|
||||
1 OtherMainboardCard
|
||||
|
||||
1 SideboardCard
|
||||
```
|
||||
|
||||
Saving the decklist as annotated will insert comments (marked with // in front of them).
|
||||
It will first insert the name and any comments associated with the deck before separating each deck section into its own
|
||||
newline delimited and annotated group.
|
||||
|
||||
Example: TODO: Adjust this to be non mtg based.
|
||||
|
||||
```
|
||||
// Full Deck
|
||||
|
||||
// An example comment.
|
||||
|
||||
// 63 Maindeck
|
||||
// 4 Ace
|
||||
1 Ace of Clubs (PKR) 49
|
||||
1 Ace of Diamonds (PKR) 50
|
||||
1 Ace of Hearts (PKR) 51
|
||||
1 Ace of Spades (PKR) 52
|
||||
|
||||
// 13 Face
|
||||
1 Jack of Clubs (PKR) 53
|
||||
1 Jack of Diamonds (PKR) 54
|
||||
1 Jack of Hearts (PKR) 55
|
||||
1 Jack of Spades (PKR) 56
|
||||
1 King of Clubs (PKR) 57
|
||||
1 King of Diamonds (PKR) 58
|
||||
1 King of Spades (PKR) 60
|
||||
1 Queen of Clubs (PKR) 61
|
||||
1 Queen of Diamonds (PKR) 62
|
||||
1 Queen of Hearts (PKR) 63
|
||||
1 Queen of Spades (PKR) 64
|
||||
2 King of Hearts (PKR) 59
|
||||
|
||||
// 44 Number
|
||||
1 0 of Clubs (PKR) 1
|
||||
1 0 of Diamonds (PKR) 2
|
||||
1 0 of Hearts (PKR) 3
|
||||
1 1 of Clubs (PKR) 11
|
||||
1 1 of Diamonds (PKR) 12
|
||||
1 1 of Hearts (PKR) 13
|
||||
1 1 of Spades (PKR) 14
|
||||
1 10 of Clubs (PKR) 5
|
||||
1 10 of Diamonds (PKR) 6
|
||||
1 10 of Hearts (PKR) 7
|
||||
1 2 of Clubs (PKR) 17
|
||||
1 2 of Diamonds (PKR) 18
|
||||
1 2 of Hearts (PKR) 19
|
||||
1 2 of Spades (PKR) 20
|
||||
1 3 of Clubs (PKR) 21
|
||||
1 3 of Diamonds (PKR) 22
|
||||
1 3 of Hearts (PKR) 23
|
||||
1 3 of Spades (PKR) 24
|
||||
1 4 of Clubs (PKR) 25
|
||||
1 4 of Diamonds (PKR) 26
|
||||
1 4 of Hearts (PKR) 27
|
||||
1 4 of Spades (PKR) 28
|
||||
1 5 of Clubs (PKR) 29
|
||||
1 5 of Diamonds (PKR) 30
|
||||
1 5 of Hearts (PKR) 31
|
||||
1 5 of Spades (PKR) 32
|
||||
1 6 of Clubs (PKR) 33
|
||||
1 6 of Diamonds (PKR) 34
|
||||
1 6 of Hearts (PKR) 35
|
||||
1 6 of Spades (PKR) 36
|
||||
1 7 of Clubs (PKR) 37
|
||||
1 7 of Diamonds (PKR) 38
|
||||
1 7 of Hearts (PKR) 39
|
||||
1 7 of Spades (PKR) 40
|
||||
1 8 of Clubs (PKR) 41
|
||||
1 8 of Diamonds (PKR) 42
|
||||
1 8 of Hearts (PKR) 43
|
||||
1 8 of Spades (PKR) 44
|
||||
1 9 of Clubs (PKR) 45
|
||||
1 9 of Diamonds (PKR) 46
|
||||
1 9 of Hearts (PKR) 47
|
||||
1 9 of Spades (PKR) 48
|
||||
1 10 of Spades (PKR) 8
|
||||
1 0 of Spades (PKR) 4
|
||||
|
||||
|
||||
// 2 Joker
|
||||
1 1 Joker (PKR) 10
|
||||
1 2 Joker (PKR) 16
|
||||
```
|
||||
|
||||
## From an online service
|
||||
|
||||
To export a deck to an online service, select the "Send deck to online service..." action in the "Deck Editor" or "
|
||||
Visual Deck Editor" menu in the application menu bar at the top of the screen.
|
||||
|
||||
Selecting this action will open your browser with the selected service open and the deck list information from the
|
||||
client supplied to it.
|
||||
|
||||
Currently supported services are DeckList and TappedOut.
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
@page importing_decks Importing Decks
|
||||
|
||||
# Where to import?
|
||||
|
||||
There are three screens in the client which can be used to import decks, depending on the context.
|
||||
|
||||
- The deck editor tab
|
||||
- The pre-game lobby tab
|
||||
- The deck storage tab (not to be confused with the visual deck storage tab)
|
||||
|
||||
# The Deck Editor Tab
|
||||
|
||||
The deck editor tabs (Classic and Visual) offer three ways of importing a deck:
|
||||
|
||||
- From a file on your local storage
|
||||
- From your clipboard
|
||||
- From an online service
|
||||
|
||||
## Local File Storage
|
||||
|
||||
To load a deck from a file on your local storage, select the "Load Deck" action in the "Deck Editor" or "Visual Deck
|
||||
Editor" menu in the application menu bar at the top of the screen. Alternatively, you can use the shortcut Ctrl/Cmd + O
|
||||
to access this action.
|
||||
|
||||
Selecting this action will open a file picker dialog provided by your operating system. Simply select a supported file
|
||||
here and it will be loaded.
|
||||
|
||||
Just below the "Load Deck" action described above is the "Load recent deck" option, which keeps a record of the last 10
|
||||
loaded decks for quick access.
|
||||
|
||||
## From Clipboard
|
||||
|
||||
To load a deck from your clipboard, select the "Load deck from clipboard..." action in the "Deck Editor" or "Visual Deck
|
||||
Editor" menu in the application menu bar at the top of the screen. Alternatively, you can use the shortcut Ctrl/Cmd +
|
||||
Shift + V to access this action.
|
||||
|
||||
Selecting this action will open a new text editor dialog with the contents of your clipboard pasted inside it.
|
||||
|
||||
The import dialog expects each line to be a card with the following format:
|
||||
|
||||
`[quantity] Card Name [options]`
|
||||
|
||||
- `quantity` is the number of copies and can be written as `2`, `2x` or `2X`. If omitted, a single copy is imported.
|
||||
- `options` are appended after the card name and are understood on a best-effort basis:
|
||||
- a set code and collector number, either as `Card Name (SET) 123` or `Card Name (SET) 123-1`
|
||||
- a foil marker, `Card Name *F*`
|
||||
- Lines can also carry a `SB:` prefix or a set code prefix (`[SET]`) — Cockatrice tries to stay compatible with the most
|
||||
common deck formats.
|
||||
|
||||
Tab-separated exports from third-party apps (such as Delver Lens) are also accepted: any line whose first column is a
|
||||
plain quantity treats the following columns as the card name and printer/set metadata (which is discarded), for example
|
||||
`2\tCard Name\tSet Name`.
|
||||
|
||||
Each card should be on a separate line and there should be no empty lines between cards. The first empty line between
|
||||
two blocks of cards will be considered as the divider between mainboard and sideboard.
|
||||
|
||||
Selecting "Parse Set Name and Number (if available)" will automatically parse these options and attempt to resolve them
|
||||
to valid provider IDs found in the card database. If this option is unselected, Cockatrice will import all cards as
|
||||
versions without provider IDs, which means they will display to everyone according to their own user defined set
|
||||
preferences, rather than being the same defined printing for everyone.
|
||||
|
||||
## From an online service
|
||||
|
||||
To load a deck from an online service, select the "Load deck from online service..." action in the "Deck Editor" or "
|
||||
Visual Deck Editor" menu in the application menu bar at the top of the screen.
|
||||
|
||||
Selecting this action will open a dialog containing the contents of your clipboard pasted into it. If your clipboard
|
||||
currently contains a supported URL, the dialog will accept it and close on its own, otherwise you may adjust the URL and
|
||||
confirm.
|
||||
|
||||
The action will automatically import the deck from the online service without any other required user action.
|
||||
|
||||
Currently supported services are Archidekt, Deckstats, ManaBox, Moxfield, and TappedOut.
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
@page visual_database_display Visual Database Display
|
||||
|
||||
# Purpose
|
||||
|
||||
The VisualDatabaseDisplay presents all the cards from the enabled sets in the CardDatabase, providing a visual way to find and add cards to your DeckList.
|
||||
|
||||
# Using the Visual Database Display
|
||||
|
||||
The VisualDatabaseDisplay can be opened by itself using the Tabs section in the menu bar at the top of the screen, or by selecting the "Visual Database Display" tab in the visual deck editor. When opened as a part of the visual deck editor, the VisualDatabaseDisplay can be used to add and remove cards from your deck. See @ref editing_decks_visual for more information about modifying your deck.
|
||||
|
||||
By default the cards are displayed as a grid of pictures. However, a table view using card names can be toggled by pressing the Visual/Table button at the top right of the widget.
|
||||
|
||||
# Filtering cards
|
||||
|
||||
The top section of the VisualDatabaseDisplay can be used to filter cards based on various card properties, as well as specifying in which way they should be sorted. Filters can be saved and reused at a later time by using the Save/Load button at the right of this panel.
|
||||
|
||||
# Modifying the Deck list
|
||||
|
||||
When the VisualDatabaseDisplay is opened as part of the visual deck editor, clicking on cards adds or removes them to and from your deck.
|
||||
|
||||
In the picture view, left-clicking on a card will add that card to the main board of your deck, while right-clicking will remove that printing of the card from the main board, if it exists. When clicking cards, modifier keys can be used to add (or remove) the card to zones other than the main board. Holding a Ctrl key on Windows and Linux or a Command key on MacOS adds/removes cards to the sideboard, while the Shift keys add/remove cards to the maybeboard.
|
||||
|
||||
In the table view, double-clicking on a card adds that card to the main board of your deck, while right-click opens the card menu. Holding the Ctrl key on Windows and Linux or the Command key on MacOS when double-clicking adds the card to the sideboard instead.
|
||||
|
||||
Tokens are always added to the token zone, regardless of which view is open or which modifier key is pressed.
|
||||
30
docs/doxygen/pages/user_documentation/index.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
@page user_reference User Reference
|
||||
|
||||
## Deck Management
|
||||
|
||||
- @subpage creating_decks
|
||||
- @subpage importing_decks
|
||||
- @subpage editing_decks
|
||||
- @subpage exporting_decks
|
||||
|
||||
## Release Channels
|
||||
|
||||
- @subpage beta_release
|
||||
|
||||
## Settings
|
||||
|
||||
- @subpage settings_folder
|
||||
|
||||
## Card Pictures
|
||||
|
||||
- @subpage custom_card_pictures
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- @subpage fixing_card_pictures
|
||||
- @subpage enabling_debug_logs
|
||||
|
||||
## Syntax Help
|
||||
|
||||
- @subpage search_syntax_help
|
||||
- @subpage deck_search_syntax_help
|
||||
54
docs/doxygen/pages/user_documentation/settings/beta.md
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
@page beta_release Beta Release
|
||||
|
||||
The beta version of Cockatrice features all the newest features before they make their way into the latest stable
|
||||
release.
|
||||
It allows us to test these features ahead of time and you to give feedback on them.
|
||||
We release new betas very frequently and usually release a follow-up beta with a fix within a day or two if we break
|
||||
anything. In contrast, stable releases are released much less frequently but ensured to be stable.
|
||||
|
||||
# What to Expect from the Beta
|
||||
|
||||
Using the beta and giving feedback on it helps us improve Cockatrice a lot.
|
||||
We do not perform any automatic data collection so your voluntary feedback is the only way for us to get a sense of what
|
||||
you enjoy, expect, or would like to change.
|
||||
|
||||
We highly appreciate your active participation and feedback.
|
||||
|
||||
That being said, some words of warning should be issued:
|
||||
|
||||
The beta release may contain unfinished features, bugs, or performance issues.
|
||||
It is intended for testing and feedback and may be less stable than the default release.
|
||||
|
||||
We recommend using the beta only if you are comfortable encountering issues and helping us improve Cockatrice.
|
||||
|
||||
# Switching to the Beta
|
||||
|
||||
To switch to the Beta, navigate to your settings, using the Tab "Cockatrice → Settings" or Ctrl + Shift + P (default
|
||||
shortcut).
|
||||
|
||||
Within the settings, in the first tab 'General', the first section 'Personal Settings' contains the setting 'Release
|
||||
Channel'.
|
||||
|
||||
\image html release_channel_beta.png
|
||||
|
||||
Switch this to 'Beta'.
|
||||
|
||||
Afterwards, close your settings, and use the 'Help' tab to search for a client update. You should be prompted to install
|
||||
the beta version.
|
||||
|
||||
# Switching back to Stable
|
||||
|
||||
Follow the same steps as above, but set Release Channel to 'Default'.
|
||||
|
||||
# Giving Feedback
|
||||
|
||||
If you encounter a bug or unexpected behavior while using the beta, please report it on:
|
||||
|
||||
- GitHub: https://github.com/Cockatrice/Cockatrice/issues
|
||||
- Discord: https://discord.gg/4hNswvHeFd in #beta-testing
|
||||
|
||||
When reporting issues, please include:
|
||||
|
||||
- Your operating system
|
||||
- The beta version number (found in 'Help → About Cockatrice')
|
||||
- Steps to reproduce the problem, if possible
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
@page settings_folder Settings Folder and Backups
|
||||
|
||||
Cockatrice keeps all of its preferences in a settings folder on your computer, one `.ini` file per
|
||||
group of settings (appearance, sound, decks, and so on). Open it from the main window via
|
||||
Help → Open Settings Folder.
|
||||
|
||||
# The Per-File Migration
|
||||
|
||||
Older versions of Cockatrice stored every setting in a single `global.ini` file. The settings were
|
||||
split into per-group `.ini` files so that unrelated groups no longer share one file. The first time
|
||||
you start a version that uses the new layout, your settings are moved into the new files
|
||||
automatically and nothing is lost. The old `global.ini` is kept next to the new files as
|
||||
`global.ini.old`.
|
||||
|
||||
# Automatic Backups
|
||||
|
||||
Both migrations overwrite files in the settings folder in place, so before either one runs,
|
||||
Cockatrice copies the whole folder to a timestamped backup folder:
|
||||
|
||||
```
|
||||
<data folder>/settings-backup-20261002-193000/
|
||||
```
|
||||
|
||||
The backup folder sits next to the settings folder, not inside it. Every backup is kept, so if
|
||||
settings ever end up wrong you can compare against, or copy files back from, the earlier snapshot.
|
||||
Backups are only created when a migration actually has work to do, so a normal start creates
|
||||
nothing.
|
||||
|
||||
# Downgrading
|
||||
|
||||
Settings migrations only run forwards. If you ever go back to an older version of Cockatrice, that
|
||||
version does not read the new per-group files, so it will start with default settings.
|
||||
|
||||
Before downgrading, copy your settings folder somewhere safe, for example:
|
||||
|
||||
- Linux: `~/.local/share/Cockatrice/settings/`
|
||||
- macOS: `~/Library/Application Support/Cockatrice/settings/`
|
||||
- Windows: `%LOCALAPPDATA%\Cockatrice\settings\`
|
||||
|
||||
Portable installs keep the settings folder in the `data` folder next to the executable.
|
||||
|
||||
Copying that folder back after downgrading restores your settings. The automatic backup folder from
|
||||
the migration is there as a second copy of the same data if you need it.
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
@page enabling_debug_logs Enabling Debug Logs
|
||||
|
||||
Cockatrice ships with a "diagnostics mode" that prints detailed debug messages about what the client is doing. This is
|
||||
extremely useful when asking for help, because it shows exactly what happened: which URL the card picture loader tried,
|
||||
whether it found or missed a file on disk, whether a download succeeded or failed, which redirects were followed, and
|
||||
much more. Each of these messages belongs to a category, and you can enable or disable categories individually.
|
||||
|
||||
Don't worry, this sounds more technical than it is. You only need to do two things: create one small text file, and tell
|
||||
Cockatrice where it is. There are no installation steps and you can undo everything later (see [When you are
|
||||
done](#when-you-are-done)).
|
||||
|
||||
# Step 1: Create the file
|
||||
|
||||
Open a plain text editor (Notepad on Windows, TextEdit on macOS, or any text editor on Linux) and paste the following
|
||||
content:
|
||||
|
||||
```ini
|
||||
[Rules]
|
||||
# The default log level is info
|
||||
*.debug = false
|
||||
|
||||
# Turn on debug level logs for the card picture loader and all its sub categories
|
||||
card_picture_loader.* = true
|
||||
```
|
||||
|
||||
Save the file with the exact name `qtlogging.ini` in a place you can find again, for example your Documents folder.
|
||||
|
||||
\attention The file name matters: it must be `qtlogging.ini`, not `qtlogging.ini.txt`. If your text editor adds a
|
||||
`.txt` extension automatically, you need to stop it from doing so (see below). On macOS, TextEdit must be switched to
|
||||
plain text mode first via 'Format → Make Plain Text'.
|
||||
|
||||
The file contains one rule per line. The `*.debug = false` rule turns off debug messages everywhere by default, and the
|
||||
`card_picture_loader.* = true` line then re-enables them for the card picture loader. The `.*` at the end means "this
|
||||
category and all of its sub categories". To enable a different category instead, just replace that line with the
|
||||
category name of your choice, for example `card_database.loading = true` or `window_main.startup = true`.
|
||||
|
||||
# Step 2: Tell Cockatrice where the file is
|
||||
|
||||
Cockatrice does not know about the file yet. You have to point it there by setting an environment variable called
|
||||
`QT_LOGGING_CONF` to the full location of your file. How to do this depends on your operating system:
|
||||
|
||||
**Windows**
|
||||
|
||||
1. Press the Windows key, type "environment variables", and open "Edit the system environment variables".
|
||||
2. Click "Environment Variables...", then under "User variables" click "New...".
|
||||
3. Set "Variable name" to `QT_LOGGING_CONF` and "Variable value" to the full path of your file, for example
|
||||
`C:\Users\YourName\Documents\qtlogging.ini`.
|
||||
4. Confirm all dialogs, then close and reopen Cockatrice.
|
||||
|
||||
Alternatively, if Cockatrice is installed in a folder you can write to, you can simply place the `qtlogging.ini` file
|
||||
directly next to the Cockatrice executable (in the same folder as `cockatrice.exe`) and skip the environment variable
|
||||
altogether. Note that this copy may be replaced when you update the client.
|
||||
|
||||
**macOS**
|
||||
|
||||
Open the Terminal app (it is in 'Applications → Utilities') and run the following two commands, replacing the path
|
||||
with the full location of your file:
|
||||
|
||||
```shell
|
||||
launchctl setenv QT_LOGGING_CONF /path/to/qtlogging.ini
|
||||
open -a Cockatrice
|
||||
```
|
||||
|
||||
You can also use `export QT_LOGGING_CONF=/path/to/qtlogging.ini` to set the variable for the current terminal session.
|
||||
|
||||
The setting stays active until you log out or restart your Mac. If you have multiple users on the same Mac, be aware
|
||||
that this setting only applies to your user account.
|
||||
|
||||
**Linux**
|
||||
|
||||
For a quick test, open a terminal and start Cockatrice with the file on the command line, replacing the path with the
|
||||
full location of your file:
|
||||
|
||||
```shell
|
||||
QT_LOGGING_CONF=/path/to/qtlogging.ini cockatrice
|
||||
```
|
||||
|
||||
If this works and you want it to apply every time you start Cockatrice, add the following line to your `~/.profile`
|
||||
file and log in again:
|
||||
|
||||
```shell
|
||||
export QT_LOGGING_CONF="/path/to/qtlogging.ini"
|
||||
```
|
||||
|
||||
# Step 3: See the logs
|
||||
|
||||
Now that debug logging is enabled, open Cockatrice and trigger the behavior you are investigating, for example by
|
||||
opening a deck, reloading the card database, or starting a game.
|
||||
|
||||
The easiest way to see the logs is to use the built-in log viewer inside Cockatrice itself: open 'Help → View Debug
|
||||
Log'. A window appears that shows the log messages live and keeps the most recent entries. It even has a 'Copy to
|
||||
clipboard' button so you can paste the output into a bug report or a Discord message. This works the same on every
|
||||
operating system.
|
||||
|
||||
If you prefer to capture everything to a file instead, start Cockatrice with the `--debug-output` option:
|
||||
|
||||
```shell
|
||||
cockatrice --debug-output
|
||||
```
|
||||
|
||||
Cockatrice then writes the full log to a file called `qdebug.txt` in the folder it was started from.
|
||||
|
||||
# Which categories are available?
|
||||
|
||||
Every message Cockatrice logs belongs to a category. The following table lists the most useful ones for troubleshooting,
|
||||
grouped by area. Enable a category by adding a line like `category = true` to your `qtlogging.ini` file (or use a `.*`
|
||||
suffix, e.g. `card_picture_loader.*`, to include all sub categories).
|
||||
|
||||
| What you want to see | Categories |
|
||||
| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||
| Card picture loading (URLs, local file hits/misses, downloads, redirects) | `card_picture_loader.*` |
|
||||
| Card database loading and parsing | `card_database`, `card_database.loading`, `card_database.loading.success_or_failure`, `cockatrice_xml.*` |
|
||||
| Card database update (running Oracle, progress, exit status, errors) | `card_database.update` |
|
||||
| Card, set, and deck information | `card_info`, `card_list`, `deck_loader` |
|
||||
| Startup sequence and update checks | `window_main.startup.*`, `release_channel`, `spoiler_background_updater` |
|
||||
| User interface and themes | `theme_manager`, `sound_engine`, `flow_layout`, `flow_widget.*`, `pixel_map_generator`, `card_info_picture_widget` |
|
||||
| Networking and servers | `local_client`, `remote_client`, `tapped_out_interface`, `servers_settings` |
|
||||
| In-game logic | `player`, `game_scene.*`, `card_zone.*`, `view_zone`, `game_event_handler` |
|
||||
| Dialogs and tabs | `dlg_settings`, `dlg_update`, `dlg_tip_of_the_day`, `tab_game`, `tab_message`, `tab_supervisor` |
|
||||
| Settings and shortcuts | `settings_cache`, `shortcuts_settings` |
|
||||
| Deck and card filtering | `filter_string`, `deck_filter_string`, `syntax_help` |
|
||||
|
||||
For example, to investigate why a card database update seems to fail, enable the card database categories:
|
||||
|
||||
```ini
|
||||
[Rules]
|
||||
*.debug = false
|
||||
|
||||
card_database = true
|
||||
card_database.loading = true
|
||||
card_database.loading.success_or_failure = true
|
||||
card_database.update = true
|
||||
cockatrice_xml.* = true
|
||||
```
|
||||
|
||||
# When you are done
|
||||
|
||||
To turn the diagnostics back off, just reverse what you did: remove the `QT_LOGGING_CONF` environment variable (or
|
||||
unset it again via `launchctl unsetenv QT_LOGGING_CONF` on macOS) and/or delete the `qtlogging.ini` file, then restart
|
||||
Cockatrice. Leaving it on is harmless, but the extra logging can make the client slightly slower.
|
||||
|
||||
For the full details on how Cockatrice logging works, including the complete list of categories, see @ref logging.
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
@page fixing_card_pictures Fixing Card Pictures
|
||||
|
||||
This guide collects the common causes for card pictures not showing up, showing the wrong printing, or showing the
|
||||
card back instead of the artwork, and how to fix them.
|
||||
|
||||
Work through the steps in order. In most cases the problem is caused by an outdated client, misconfigured download
|
||||
URLs, stale local images, or a corrupted cache.
|
||||
|
||||
# Update Your Client
|
||||
|
||||
Picture handling bugs are fixed on a regular basis, so the first thing to try is updating your client.
|
||||
|
||||
Use the update check in 'Help → Check for Updates' (or look for the update prompt shown on startup).
|
||||
|
||||
If a fix has not yet made it into the latest stable release, it may already be available in the beta release. The beta
|
||||
ships very frequently and usually receives a follow-up fix within a day or two if something breaks.
|
||||
|
||||
See @subpage beta_release for instructions on how to switch to the beta channel.
|
||||
|
||||
# Check Your Download URLs
|
||||
|
||||
Card pictures are downloaded from a list of URL templates. Each template is tried in order until one produces a valid
|
||||
image, so the order matters: URLs at the top of the list are tried first.
|
||||
|
||||
The list can be found in 'Cockatrice → Settings' (or Ctrl + Shift + P by default), on the 'Deck Editor' tab, in the
|
||||
'URL Download Priority' section. Make sure 'Download card pictures on the fly' is enabled and that the list contains
|
||||
valid URLs. If you suspect the list has been modified or corrupted, press 'Reset Download URLs' to restore the
|
||||
defaults.
|
||||
|
||||
For information on how to add your own custom URL templates, see the 'How to add a custom URL' link in the same
|
||||
settings section, or @ref custom_card_pictures for a full reference of the URL reference points, the CUSTOM
|
||||
pictures folder, and custom card databases.
|
||||
|
||||
# Check Your Local Picture Folder
|
||||
|
||||
Before any network request is made, Cockatrice looks for local image files. If a matching file is found on disk it is
|
||||
shown instead of anything downloaded, even if it is the wrong image.
|
||||
|
||||
The pictures directory is configured on the 'General' settings tab, under 'Directories' → 'Pictures directory'.
|
||||
Cockatrice checks the following locations, in order:
|
||||
|
||||
- The custom pictures folder (recursively indexed by file name).
|
||||
- `<pictures directory>/<set code>/<card file name>`
|
||||
- `<pictures directory>/downloadedPics/<set code>/<card file name>`
|
||||
- `<pictures directory>/downloadedPics/<card file name>` (for export naming schemes without a set folder)
|
||||
|
||||
A file only matches when its name without the extension equals one of the recognized scheme patterns exactly.
|
||||
|
||||
The following import naming schemes are recognized (using both `_` and `-` as separators). The canonical table with
|
||||
concrete example file names is on @ref custom_card_pictures:
|
||||
|
||||
| Scheme | Pattern |
|
||||
| --------------------------- | -------------------------- |
|
||||
| Card Name + Provider ID | `{name}_{providerId}` |
|
||||
| Card Name + Set + Collector | `{name}_{set}_{collector}` |
|
||||
| Set + Collector + Card Name | `{set}_{collector}_{name}` |
|
||||
| Card Name + Set | `{name}_{set}` |
|
||||
| Card Name | `{name}` |
|
||||
|
||||
If a picture you downloaded or placed manually is wrong, stale, or corrupted, delete the offending file. Pay special
|
||||
attention to the `downloadedPics` subfolder: this is where the filesystem caching method writes downloaded images, and
|
||||
after a provider outage it can permanently contain the wrong printing until you delete it manually.
|
||||
|
||||
If a card persistently shows artwork you assigned yourself, you may have an **image override** set for it. Right-click
|
||||
the card in the deck editor's printing selector and use 'Image Overrides' → 'Clear Custom Image' to remove it (or
|
||||
delete the stored `.png` in `downloadedPics/`). See @ref custom_card_pictures for details.
|
||||
|
||||
See @ref loading_card_pictures for details on how local images are loaded.
|
||||
|
||||
# Clear Caches
|
||||
|
||||
Cockatrice caches card pictures in three places. All of them can be managed on the 'Storage' settings tab:
|
||||
|
||||
- **Network cache** — downloaded images stored on disk. Press 'Delete Cached Images' to clear it.
|
||||
- **Filesystem / image backup** — downloaded images written directly to `downloadedPics`. Press 'Delete Saved Images'
|
||||
to clear it.
|
||||
- **In-memory (pixmap) cache** — images currently held in RAM. Press 'Clear In-Memory Images' to clear it.
|
||||
|
||||
If a provider outage caused the wrong pictures to be downloaded and cached, clearing the network cache (and the
|
||||
'Delete Saved Images' button if you use the filesystem caching method) will force Cockatrice to download the correct
|
||||
images again. The redirect cache TTL (also on the Storage tab) controls how long previously seen redirects for
|
||||
download URLs are remembered; lowering it can help if a URL used to redirect somewhere else.
|
||||
|
||||
# Restart the Client
|
||||
|
||||
After updating the client, changing the download URLs, moving or deleting local image files, or clearing caches, it is
|
||||
recommended to restart Cockatrice so that all changes are fully picked up.
|
||||
|
||||
# Check Logs
|
||||
|
||||
Before changing any settings, check the existing logs first. Rate limit errors and most download errors are already
|
||||
logged at warn level, so you may find the cause without enabling debug mode.
|
||||
|
||||
Open 'Help → View Debug Log' and look for error or warning messages related to card picture loading. If you need more
|
||||
detail than the default log level provides, see below.
|
||||
|
||||
# Enable Picture Loader Debug Logs
|
||||
|
||||
If the steps above did not solve the problem, you can turn on a "diagnostics mode" that prints what the picture loader
|
||||
is actually doing: which URL it is trying, whether it found or missed a file on disk, whether the download succeeded or
|
||||
failed, and which redirects it followed. This information is extremely useful when asking for help.
|
||||
|
||||
See @subpage enabling_debug_logs for a step-by-step guide on how to enable the logs, including instructions
|
||||
for Windows, macOS, and Linux.
|
||||
|
||||
# Provider Accuracy
|
||||
|
||||
Cards in Cockatrice are identified by a provider ID, which is the Scryfall UUID of a specific printing. Decks store
|
||||
this ID, which is why the exact printing a card was added as can be looked up again.
|
||||
|
||||
The Scryfall URL templates built into Cockatrice use this provider ID directly (`!set:uuid!`), so they always download
|
||||
the exact printing that was requested.
|
||||
|
||||
The Gatherer URL templates, on the other hand, do **not** use the provider ID. They resolve pictures by multiverse ID
|
||||
(`!set:muid!`) or by card name (`!name!`) only. As a result they may return a different printing than the one the
|
||||
provider ID refers to, or no picture at all for cards Gatherer does not know. If you need pictures to match the exact
|
||||
printing of a card, make sure the Scryfall URLs are at the top of your download URL priority list and consider removing
|
||||
or demoting the Gatherer URLs.
|
||||
1
docs/doxygen/theme
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 46483f1e5a70ffb9ecd3b82d0a1cd1b24edf13da
|
||||