Commit graph

6592 commits

Author SHA1 Message Date
tooomm
c8fd943025 use new macOS images 2026-11-04 15:33:30 +01:00
tooomm
3f736e795a update/add eol dates 2026-11-04 15:33:27 +01:00
Bryce!
67330ead5a
Embed bundled resources without compiling large C++ arrays [skip ci] (#7730) 2026-11-04 15:27:44 +01:00
BruebachL
7ca697348b
[CI] Give ccache room to hold a configuration (#7667)
* [CI] Re-assert the ccache timestamp sloppiness after configure

CMakeLists.txt sets sloppiness=pch_defines,time_macros at configure
time, and ccache --set-config replaces the whole list rather than
extending it. Setting the four entries before cmake would therefore
leave the build with only two, so the entries CI needs have to be
written after the configure step and before the build.

The mtime and ctime entries cover files created during the build
itself: moc/uic/rcc output and protobuf headers are generated after
ccache starts, so without them every TU that includes one is read as
a race and never cached. Checked-out sources are older than the
compile that follows them and do not trip the check.

* [Build] Keep risky ccache sloppiness out of the global config

USE_CCACHE defaults to ON, so CMakeLists.txt runs on a developer
machine, and ccache --set-config writes the user's global ccache.conf.
The include_file_mtime/include_file_ctime entries turn off the
modified-during-compile check for every project on the machine, not
only this one, so they belong in CI (compile.sh) and not here. The two
entries PCH caching needs stay.

* [CI] Size each compiler cache against the cache quota

The 600M bound sat below what a leg writes: ccache spent each build
deleting the entries the next run asked for, with 2624-3720 cleanup
passes and about 8% of calls served in the runs inspected on
2026-11-04.

Raising it needs room first. The repository's 10 GB quota currently
holds 1.1 GB of CodeQL trap, 1.1 GB of vcpkg, 0.7 GB of Qt and 2.1 GB
of docker buildkit layers. Two of those are now cheap to reclaim:

- docker-release wrote its layer cache with mode=max, but the build
  stage sits behind COPY . ., so its compile layer is invalidated by
  every source change and was never read back. mode=min keeps only the
  final image stage.
- the five nightly-only legs kept their own compiler caches, 600M
  each, that no pull request ever reads. They now keep none, and the
  prune job retires their leftover keys immediately instead of waiting
  for GitHub's whole-entry LRU eviction to pick them.

That funds per-leg bounds: 700M for one configuration, 850M for the
Debian 13 release that also runs the suite, 900M for Fedora 44's
Debug and Release, 800M for the instrumented leg, 400M for the
server-only leg, and 700M for each macOS leg. 6.45G of ccache plus the
3.2G of fixed entries leaves about 350M under the quota.

The prune step also covers the buildkit entries by age now, so the
mode=max generation of layer blobs is reclaimed instead of living
forever: a layer a build needs is downloaded and its date refreshed,
and dropping one that an index still lists only costs docker-release a
rebuilt layer, which gates nothing.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 10:06:56 +01:00
BruebachL
ed119cc940
[Fonts] Polish font editor accessibility and note text (#7542)
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / vcpkg baseline (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 sanitize (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 cover (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 26 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / Prune caches (push) Waiting to run
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:57:55 +01:00
BruebachL
a791389c6a
[Fonts] Restore DeckList Serif default and live-update all role readers (#7541)
* [Fonts] Restore DeckList Serif default and live-update all role readers

* [Fonts] Keep the DeckList fallback on the application font

DeckList is read by the deck editor headers, the filter tree and the deck
view too, not only by the two surfaces that hardcoded Serif. Resolving the
unset role to Serif in appFont() made every user without a fonts.toml see
Serif outside the deck list.

- appFont(DeckList) falls back to the application font again.
- ThemeManager::deckListFont() resolves the role and keeps the Serif
  fallback for the two historical sites: the deck view zone headers and
  the printed deck list.
- DeckView no longer repaints its viewport on fontsChanged; touching the
  scene items already schedules the repaint in every attached view.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:57:55 +01:00
BruebachL
13e7a3baaf
[Fonts] Fix font preview leaks and reuse default-font loader (#7540)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:57:55 +01:00
BruebachL
3bcdfc3793
[Fonts] Migrate DeckList and InterfaceDefault surfaces to theme roles (#7539)
* [Fonts] Migrate DeckList and InterfaceDefault surfaces to theme roles

* [Fonts] Drop unused lambda captures in font-refresh handlers

* [Fonts] Fix pixel sizes and game overlay fonts from review

- Set the sizes the stylesheets carried with setPixelSize() rather than
  setPointSize(): home buttons (34), banner (24), home group box (20),
  the placeholder labels (14), the analytics drag handle (10), the EDHREC
  navigation buttons (24), the user info popup (11/14/11/10/11) and the
  game overlays (14/16/12). Points at 96 DPI were ~33% larger.
- The drag/total counters and tally columns keep the monospace family the
  stylesheets set, so digit widths stop changing as counts tick and
  white-space: pre stays aligned.
- applyDeckListFont() drops the cached tally rows before re-measuring, so
  previewing a larger DeckList font no longer leaves the tally clipped.
- Tally labels and the constructor/fontsChanged pairs in the shared-deck
  preview, quick setup, analytics panel, home box and public decks build
  their font once in a local lambda instead of twice.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:57:54 +01:00
BruebachL
396196b762
[Fonts] Migrate CardText surfaces to theme font role (#7538)
* [Fonts] Migrate CardText surfaces to theme font role

* [Fonts] Keep CardText call sites on the app font and fix px sizes

- CardText's unset fallback is qApp->font() again: only the three counter
  painters (paintNumberEllipse, GeneralCounter, PlayerCounter) were Serif
  before the role existed, and they now ask for it through
  ThemeManager::cardTextFont(). Card names, player names, counters, the
  overlay, the Archidekt and EDHREC labels and the completer delegate keep the
  app font they used before.
- Use setPixelSize for the EDHREC card name (20px) and commander name (28px)
  and details (16px) sizes: the stylesheets said px, and setPointSize made
  them about a third larger.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:57:54 +01:00
BruebachL
7a40ed9079
[Fonts] Migrate ChatLog surfaces to theme font role (#7537)
* [Fonts] Migrate ChatLog surfaces to theme font role

* [Fonts] Keep ChatView on the app font and share the report log font

- ChatLog's unset fallback is qApp->font() again: ChatView never set a font
  before the role existed, so an unset role must not switch chat and the game
  log to monospace at the system GeneralFont size.
- Move the monospace fallback the report dialogs used before into
  report_utils::reportLogFont(), which returns the theme's ChatLog role when it
  is set and the old monospace otherwise. tab_report, dlg_my_reports and
  dlg_report_user now share it, so they cannot drift and they honour a themed
  size instead of overriding it.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:57:53 +01:00
BruebachL
634f8115a5
[Fonts] Add per-purpose font section to palette editor (#7536)
* [Fonts] Add per-purpose font section to palette editor

* [Fonts] Make the editor's fallback and Reset preview-independent

- Add ThemeManager::defaultAppFont(), which resolves an unset role against the
  startup font instead of currentFonts, and use it in FontGridWidget::loadFonts
  so the grid never shows the live preview as the theme's default.
- Reload the live theme in PaletteEditorDialog::onReset before re-reading the
  grid, so Reset restores the saved fonts before loadFonts resolves fallbacks.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:57:53 +01:00
BruebachL
54797452da
[Fonts] Wire FontConfig into ThemeManager with appFont and preview (#7535)
* [Fonts] Wire FontConfig into ThemeManager with appFont and preview

* [Fonts] Restore the startup font when a config has no InterfaceDefault

- Capture the untouched application font next to the default palette and use
  it in previewFonts' else branch, so a config without InterfaceDefault
  restores the startup font instead of leaving the last preview applied.
- Drop the unpolish/polish loop from previewFonts: QApplication::setFont
  already notifies every widget, and the loop ran on every editor auto-apply.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:57:52 +01:00
BruebachL
d8700b2947
[Fonts] Add AppFont roles and FontConfig fonts.toml round-trip (#7534)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:57:52 +01:00
BruebachL
140fbb4484
[CI] Cover the legacy distros and the slow macOS runner in a nightly (#7668)
* [CI] Cover the legacy distros and the slow macOS runner in a nightly

Every pull request built ten Linux legs and five macOS/Windows legs, including
the previous release of each distro and macOS 13 on the Intel runner. Those
legs exist to catch a regression for users on an older distribution, which a
change being reviewed almost never does, and macOS 13 Intel is the slowest leg
of the matrix - 28 minutes against 19 for the rest.

They now run in a nightly build of the default branch and on tag builds, which
is when a change actually ships. Pull requests keep the current release of each
distro, the sanitizer leg and the coverage leg.

* [CI] Run the nightly legs from their own jobs

jobs.<id>.if cannot read matrix, so the previous filter never validated and
no leg of this workflow ran. matrix.exclude cannot express it either: GitHub
processes exclude before include, and these matrices are include-only.

Split the regular and nightly legs into two jobs per platform, sharing the
step list through a YAML anchor and gating the jobs on the event instead of
on matrix. The nightly run now covers only the legs a push leaves out, and
the event name is part of the concurrency group so a nightly and a master
push do not cancel each other.

* [CI] Run the coverage leg in the nightly

gcovr.cfg sets no fail_under, so the leg cannot fail a build, and nothing
consumes coverage-report: it was an artifact produced on every pull request and
read by no one. In the nightly it becomes a trend over the default branch
instead, next to the legs a change being reviewed does not wait for.

The leg itself is unchanged - same distro, version and variant, so the image
name and ccache key still match the warm cache it built. It is marked
allow-failure so a reporting leg cannot redden the nightly run that
vcpkg-cache-import requires to conclude in success.

The release-test comment loses its per-leg parenthetical: it claimed the
sanitizer leg packages, which #7666 stopped being true, and the coverage leg is
no longer in this job.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:42:35 +01:00
BruebachL
3f7aa4ca82
[CI] Split the sanitizer leg from the Ubuntu packaging leg (#7666)
The sanitize leg re-ran the Release packaging build after the instrumented
one, so 18 minutes of Release compile queued behind the sanitizer run in a
single job. Give sanitize its own leg with its own image and ccache, and
keep the plain Ubuntu leg publishing the DEB.

- The coverage leg's packaging comment now points at the leg that actually
  publishes the DEB.
- The -DENABLE_SANITIZERS=OFF comment no longer describes a debug-sanitize
  then release-package sequence that no job runs.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:24:24 +01:00
BruebachL
75beb7f302
[CI] Run the Linux test suite against the release build (#7665)
* [CI] Run the Linux test suite against the release build

A packaging leg compiled the tree twice: once in Debug to run the suite, once
in Release to package it. The suite is 655 tests that finish in four seconds,
so the Debug build bought nothing the release build cannot provide - macOS and
Windows have always tested their release build.

The sanitizer and coverage legs keep their Debug build, because that is the
configuration their instrumentation is for. Every other leg runs ctest against
the release configuration, ahead of the package target, so a failing suite
still stops the leg before anything is built into an installer. A leg that only
tests, Arch, builds Release without the crash reporting SDK the packaging legs
ship with.

Debian 13 and Fedora 44 each drop a 15 minute compile from a job that gates a
pull request.

* [CI] Keep Fedora 44 and Arch on the Debug build

Only the Debug configuration passes -Wall -Wextra -Werror, so moving
those two legs to the release build would have stopped the newest distro
toolchains from failing on a new warning. They keep the Debug compile and
test master had; the other Linux legs still test the release build.

Addresses review thread PRRT_kwDOACxbl86x2tOP (PR #7665).

* [CI] Decide the release test run once per leg

The step's if: and its shell both recomputed "does this leg test the
release build", and the two could drift into a leg that builds a release
it never tests. RUN_TESTS now holds that decision and both read it.

It lives at job level because a step's own env is not visible to that
step's if:.

Addresses review thread PRRT_kwDOACxbl86x2tOV (PR #7665).

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:22:43 +01:00
BruebachL
99422c2bf0
[Client] Stop sending the saved login when its checkbox is unticked (#7713)
Unticking 'Save password' only disabled auto-connect: the dialog still handed
the stored verifier to the connection, so a password typed at the same time was
ignored, and a server that still has the legacy hash refused the login before it
could upgrade the account.

Clear the verifier in effect on untick, restore it on re-tick unless a password
has been typed, and drop the stale 'Password saved' hint. The downgrade abort
can now point at unticking the box and logging in with the password, without the
extra reconnect round-trip.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 07:06:35 +01:00
BruebachL
96f8c3a423
[Tests] Assert settings navigation through the event player (#7698)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 06:23:45 +01:00
BruebachL
3fa8d78786
[Tests] Play card interactions through a Cockatrice event player (#7697)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 06:23:45 +01:00
BruebachL
34201122ec
[Tests] Name the tab, home and settings widgets the GUI tests address (#7696)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 06:23:44 +01:00
BruebachL
d2abd645e8
[Tests] Name the menus and dialogs the shell GUI tests drive (#7695)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 06:23:44 +01:00
BruebachL
ee57d740c2
[Tests] Replay XML test files through a headless runner (#7694)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 06:23:43 +01:00
BruebachL
2e0a718cd7
[Tests] Vendor QtTesting behind an opt-in WITH_GUI_TEST flag (#7693)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 06:23:43 +01:00
Bryce!
28597ef716
Fix Deckstats and TappedOut request lifetime and result handling (#7705)
* Fix deck analysis request lifetime, result URLs and form encoding [skip ci]

* Preserve unknown cards in deck analysis exports

* Share deck analysis URL validation and drop the unused request latch

Both analysis interfaces carried a copy of normalizeAnalysisUrl; move it to one
header next to them. They are constructed per analysis and analyzeDeck is called
once, so the analysisStarted latch was unreachable; remove it from both.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 06:22:29 +01:00
Bryce!
75d7764643
[Client] Keep Archidekt navigation tied to its active request (#7723)
* [Client] Keep Archidekt navigation tied to its active request [skip ci]

* [Client] Keep Archidekt listing pagination after a failed navigation

Only update list mode and the next-page link when a listing or deck
response is accepted, so a failed deck navigation leaves the existing
listing scrollable. Retry still re-issues the failed request.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 05:59:17 +01:00
BruebachL
81a0b97240
[Replay] Seek to the exact time of an event (#7610)
Timeline seeks only expose whole timer intervals, so jumping to an event could leave that event unprocessed until the next tick. Add an event-time seek that also applies events at the target timestamp.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 05:56:32 +01:00
Bryce!
624163916f
[Client] Keep Commander Spellbook requests bound to the active deck (#7722)
* [Client] Keep Commander Spellbook requests bound to the active deck [skip ci]

* [Client] Restore the bracket info tooltip when idle

retranslateUi() used to install the static "Why this bracket?" tooltip, but it now calls displayEstimate(), which returned early while no estimate existed. That left the info button without its idle tooltip.

Set the fallback where displayEstimate() owns the button's tooltip. An existing estimate still wins, and a language change refreshes the text.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-04 05:49:31 +01:00
Bryce!
94f7de42f2
Fix user card games for offline users and stale requests (#7738) 2026-11-04 05:29:51 +01:00
Bryce!
7768416091
Place current deck-editor docks before restoring saved layouts [skip ci] (#7734) 2026-11-04 04:37:25 +01:00
BruebachL
c72192fd81
[Format] Small fixup (#7768)
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / vcpkg baseline (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 cover (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 26 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Desktop / Prune caches (push) Waiting to run
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions
* [Format] Small fixup.

* [CardDB] Stop leaking the loader's mutexes.

Both mutexes were heap-allocated through default member initializers and
never freed, so every CardDatabase destruction leaked 16 bytes. LeakSanitizer
flags this while discovering tests that create the database in main.

Hold them by value instead, as the loader is a QObject and therefore neither
copyable nor movable.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-03 15:59:32 +01:00
Bryce!
9588e1960d
Preserve saved deck formats through database reload and view refresh (#7745) 2026-11-03 11:10:50 +01:00
Bryce!
fd2a99e897
Respect nonempty Qt hints and explain discovery failures [skip ci] (#7733) 2026-11-03 10:41:47 +01:00
Bryce!
dc4aa19d23
Import Moxfield signature spells into the deck [skip ci] (#7751)
Co-authored-by: Codex <codex@openai.com>
2026-11-03 10:40:28 +01:00
Bryce!
ada8f399f1
Log server identification and connection lifecycle (#7731)
* Log remote server identification and connection lifecycle [skip ci]

* Update libcockatrice_network/libcockatrice/network/client/remote/remote_client.cpp

Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>

---------

Co-authored-by: BruebachL <44814898+BruebachL@users.noreply.github.com>
Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>
2026-11-03 10:38:54 +01:00
Jurriaan
bc09ec6afe
[Client] Make DeckStateManager::decrementCard remove tokens from the token zone if the token is not in the given zone (#7588)
* Fixed decrementCard for tokens not using the token zone

* Removed dead function actDecrementCard

Updated documentation

* Fixed no-op, added documentation

* Removed stray blank line

* Allow finding card nodes even if no database entry exists for the given card

* Added DeckStateManager tests

* Added tokens on mainboard test

* Added if checks and sorted sources

Added if checks

* Library include fix

* Renamed zone to targetZone

* Revert "Allow finding card nodes even if no database entry exists for the given card"

This reverts commit 88578cd0d9.

* Combined loader_local_matching_test and deck_state_manager_test settings sources

* Added settings sandbox, refactor

* Format

* Renamed return variable

* Flattened if statement

* Style fix
2026-11-03 10:37:17 +01:00
Jurriaan
bfb53c5def
[Client] Visual Deck Editor: Added more mouse click modifiers to add/decrement cards to specific zones (#7589)
* Make visual database mouse click modifiers properly add/decrement different card zones

* Use event->modifiers(), refactor

* Removed QApplication include

* Added visual database display documentation page

* Minor fixes

* Added documentation for the mouse interaction of the table view
2026-11-03 10:35:58 +01:00
PILIX123
381dd21e8c
[Client] Add option to load decks from ManaBox (#7642)
* [Client] Add option to load decks from ManaBox

* [Client] Fix PR comments

* [Client] Fix UI and transaltion requests

* [Client] Fix for UI and parser not doing what expected when loading maybe board

* Add ManaBox support to docs

As pointed out in review comments

* [Client] Fix final PR comments

---------

Co-authored-by: tooomm <tooomm@users.noreply.github.com>
2026-11-03 10:33:21 +01:00
transifex-integration[bot]
c5b776ca46
Translate oracle/oracle_en@source.ts in de (#7767)
100% translated source file: 'oracle/oracle_en@source.ts'
on 'de'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2026-11-03 10:31:12 +01:00
BruebachL
dd7ee3541c
[PictureLoader] Keep the download log from going quadratic (#7766)
* [PictureLoader] Keep the download log from going quadratic

A fast scrollbar sweep over the visual card database requests a picture for
every cell it paints, so one gesture can ask for thousands of downloads. The
status bar built a seven-widget log entry for each of them and answered every
completion with findChildren() over the whole, unbounded log, rebuilding
url.toString() per child: O(n) per completion, O(n^2) per burst.

Profiling a sweep on master put 47% of all cycles on the GUI thread, with
qt_qFindChildren_helper at 12.4% of the process (a quarter of that thread) and
QWidgetPrivate::reparentFocusChildren at 4.1%. LagMonitor reported stalls of
3-18 s, growing with the backlog.

Keep at most MAX_LOG_ENTRIES entries and index them by URL, so a completion
and the 1 Hz cleanup only visit the matching entries. The progress bar follows
the entries that remain: dropping a finished entry moves both counters back,
dropping a pending one only its maximum. The log is a diagnostic view, not a
queue - the downloads themselves are unaffected.

* fixup! [PictureLoader] Keep the download log from going quadratic

Make the URL index a QMultiHash and say why: a deferred retry re-queues the same URL while its first log entry is still pending, so one URL can back several live entries.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-03 10:17:53 +01:00
BruebachL
44d5138181
[VisualDatabase] Keep the card grid scrolling on macOS (#7764)
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / vcpkg baseline (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 cover (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 26 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Desktop / Prune caches (push) Waiting to run
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions
* [VisualDatabase] Keep the card grid scrolling on macOS

The view ignored the wheel whenever the vertical scroll bar was not
visible. On macOS, scroll bars are transient: once the fade-out finishes,
QScrollbarStyleAnimation hides the scroll bar widget, so isVisible() then
reports false even though the grid has more rows to scroll. Scrolling was
dead until something else brought the bar back.

Gate on the scroll bar's range instead: it says whether there is anything
to scroll, while visibility is only about presentation. An event with no
range to scroll is still ignored, so an enclosing scroll area can take it.

* Update card_database_visual_view.cpp

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-02 18:18:19 +01:00
sentry[bot]
9f30892dff
[Client] Fix crash when local game commands are sent after shutdown (#7714)
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / vcpkg baseline (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 cover (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 26 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Desktop / Prune caches (push) Waiting to run
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions
* [Client] Fix crash when local game commands are sent after shutdown

* [Network] Fix formatting in local client debug log

* [CI] Keep the vcpkg submodule pointer on the manifest baseline

vcpkg.json asks for builtin-baseline 9eec002 (vcpkg 2026.10.16), but the
submodule gitlink was still at 9e593bb. The runners check submodules out
shallowly, so vcpkg-tool cannot read versions/baseline.json out of the baseline
commit and every vcpkg leg (Windows and all macOS) fails in CMake configure
with:

  failed to `git show` versions/baseline.json

git phrases that as "path ... exists on disk, but not in <sha>" even when the
commit is simply absent from the shallow clone, so it reads like a tooling or
network failure rather than a mismatched pointer. Point the submodule back at
9eec002, and add a cheap job that fails when the gitlink and builtin-baseline
disagree, before the five build legs pay for it.

---------

Co-authored-by: sentry[bot] <39604003+sentry[bot]@users.noreply.github.com>
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-02 13:28:23 +01:00
sentry[bot]
8b0c980e85
[Client] Guard dragged card ghost from painting or dropping deleted cards (#7688)
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 cover (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 26 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Desktop / Prune caches (push) Waiting to run
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions
* [Client] Guard dragged card ghost from painting or dropping deleted cards

* [Game] Revert handling for cards destroyed mid-drag

Co-authored-by: sentry[bot] <39604003+sentry[bot]@users.noreply.github.com>

* [DeckView] Access pointer through QPointer in DeckViewCardDragItem

---------

Co-authored-by: sentry[bot] <39604003+sentry[bot]@users.noreply.github.com>
Co-authored-by: zeldazach <7460172+zeldazach@users.noreply.github.com>
2026-11-02 08:39:07 +01:00
RickyRister
62e590d300
[ServerGame] Refactor out spectator/judge bools into RoleInfo (#7761) 2026-11-02 00:37:05 -07:00
BruebachL
a1a8a59f4a
[VisualDatabase] Draw the card grid with a model/view (#7701)
* [VisualDatabase] Draw the card grid with a model/view instead of a widget per card

The visual card database built one CardInfoPictureWithTextOverlayWidget per
card, 100 per page, and tore them down on every search, resize and scroll
page. It also looked each card up again by name from the model row, kept a
shadow QList<ExactCard>, and carried paging state plus a 5 s timer that only
existed to defer the first page.

Replace that with a real view on a flattened model:

- CardDatabaseDisplayModel gains CardInfoRole and cardInfoAt(), so a view can
  get the CardInfoPtr behind a proxy row without a name round trip.
- New CardDatabaseVisualDisplayModel (libcockatrice_models) flattens the
  filtered and sorted proxy into one entry per displayed card picture: the
  preferred printing normally, every printing of the selected sets while set
  filters are active. Proxy changes rebuild it on a 30 ms coalescing timer.
- New CardDatabaseVisualView (QListView IconMode) paints only visible rows
  through the new CardPictureDelegate, keeps the old interaction contract
  (left/right/middle press -> cardClicked, hover -> cardHovered, Ctrl+wheel
  resize, no selection, no focus stealing, scroll to top on a new result) and
  repaints when CardPictureLoader reports an async image.
- CardSizeWidget::enableCtrlScrollResize() also watches scroll-area viewports,
  which is what makes Ctrl+wheel work over a QListView.
- VisualDatabaseDisplayWidget drops the paging, the shadow list, the dead
  members and the 5 s timer.

Behaviour-preserving: no gameplay change, no new user-facing strings.

* [VisualDatabase] Fix the card grid wheel step

QListView hands the delegate's item height to the scroll bar as its single step under ScrollPerPixel, so QAbstractSlider moved wheelScrollLines() whole card rows per notch. Handle the wheel in the view instead: one line is an eighth of the grid cell, so a notch moves about a third of a card row at any card size and still follows the OS line count.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-02 08:35:58 +01:00
BruebachL
94e6281d87
[Logs] Browse room history without filters with paginated infinite scroll (#7531)
* [Logs] Browse room history without filters with paginated infinite scroll

* [Logs] Continue pagination when switching log tabs

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-02 07:36:49 +01:00
BruebachL
3cf8bdb00e
[Tests] Add a headless game logic regression suite (#7702)
* [Tests] Add headless game logic regression suite

Cover CardState, CardZoneLogic and the Command_MoveCard protobufs PlayerActions emits, against libcockatrice_game with a recording AbstractClient and no graphics. The old game_smoke_test cases are migrated into the new suite.

* [Tests] Cover game event application through GameEventHandler

Feeds GameEventContainers through the real dispatcher and asserts the resulting zone and card state: move (including face down), the set-card-attr attributes plus the all-cards and does-not-untap branches, card counters, attach/detach, zone reveal properties, and drawing named cards from the deck.

* [Tests] Replay a synthetic game state through the dispatcher

Builds a GameReplay in code, feeds its containers through the same GameEventHandler path the client uses, and asserts the materialized players, zones, cards and attachments plus event ordering for move, attributes and counters. A small headless zone view stands in for the graphics side where processPlayerInfo asks for card items.

* [Tests] Cover card flip, destroy, token and turn events

Adds the remaining state-mutating event handlers: flip (face down keeps the ref, face up adopts the printing), destroy (removes the card and un-attaches its attached cards), create token (database P/T unless provided or face down) and player properties changed under the concede/unconcede contexts, plus active player and phase.

* [Tests] Cover player lifecycle and counter events

Adds the player-level events that mutate session state: leave removes the player from the manager, kicked emits gameClosed, game closed flips the closed flag and clears the started flag, host changed moves the host role to another player, and create/set/delete update the player counters with unknown ids ignored.

* [Tests] Drive the zone view request through addCard

The test emitted createCardRequested itself and then asserted it was emitted. Registering a ZoneViewZoneLogic on the zone and adding a card exercises the real prepareAddCard -> signal path, so the assertion now fails if addCard stops asking the graphics side to build the card.

* [Tests] Skip the card database when listing tests

gtest_discover_tests runs the binary with --gtest_list_tests during the build. That process built the card database and replaced its default path provider, leaving allocations that the sanitizer leg's default LeakSanitizer check reports at exit, so discovery failed on Ubuntu 26.04. No test runs in list mode, so return before the database setup.

* [Tests] Fix fixture comment nit

Wraps the RecordingClient::sent member name in backticks in the comment.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-02 07:35:41 +01:00
BruebachL
5134e72eb4
[Settings] Store server profiles with QSettings arrays (#7629)
The server list used hand-numbered saveName%1/server%1 keys and an O(n)
shift loop to delete entries. Store the profiles with beginReadArray and
beginWriteArray instead, and migrate the legacy keys once in the
constructor so existing profiles and saved passwords survive.

SettingsManager::batchWrite no longer disables atomic sync, so a batch is
still written through QSettings' atomic save. Add tests for add, update,
remove, password updates and the legacy migration.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-02 07:30:29 +01:00
BruebachL
b37c8b828f
[Utility] Budget scrypt's XY array in the derivation memory limit (#7712)
deriveKey passes maxmem = 128nr + 128rp + 4096 to EVP_PBE_scrypt, but scrypt
also allocates XY at 256r bytes. costParamsAreSane accepts r up to 32, so any
server asking for r >= 17 made derivation fail and the client report a login
abort it could never recover from. Cover the XY array and add a regression test
at the accepted-range boundary.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-02 07:29:15 +01:00
BruebachL
719863e43d
[CI] Lint only .sh files in the shell formatting pass (#7715)
The changed-file matcher reused the `*.sh` glob as a regular expression,
where the leading `*` applies to the `(^|/)` group instead of the file
name. Every path ending in "sh" matched, so NSIS include files (.nsh) were
handed to shellcheck and failed the format leg.

Match the suffix explicitly and keep the glob for the find(1) branch.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-02 07:29:05 +01:00
BruebachL
d021f20d14
[Version] Bump to 3.2.0 (#7762)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-02 07:28:14 +01:00