rename doc folder
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` folder according to our [configuration](https://github.com/Cockatrice/Cockatrice/blob/master/Doxyfile):
|
||||
```bash
|
||||
doxygen Doxyfile
|
||||
```
|
||||
Open the `index.html` file in the `/docs/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/doc/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/doc/carddatabase_v4/cards.xsd) <kbd>v4</kbd>
|
||||
- [XSD Schema for `Decks`](https://github.com/Cockatrice/Cockatrice/blob/master/doc/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,6 @@
|
|||
@page editing_decks Editing Decks
|
||||
|
||||
- @subpage editing_decks_classic
|
||||
- @subpage editing_decks_visual
|
||||
|
||||
- @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,62 @@
|
|||
@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:
|
||||
|
||||
TODO
|
||||
|
||||
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, Moxfield, and TappedOut.
|
||||
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.
|
||||