mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-10-26 17:41:15 -05:00
ecd955bc609c161cf665e13f6ed7fb2ebba5d102
6332 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ecd955bc60 |
[Installer] Wait on the launching process so auto-updates cannot write over a mixed Qt runtime (#7372)
* [Installer] Abort instead of installing over leftover runtime files
The silent upgrade could leave a mix of old and new Qt DLLs next to the new
executables, which is what makes the next start fail with "The procedure entry
point X could not be located in the dynamic link library ...Qt6QuickWidgets.dll"
or "The code execution cannot proceed because Qt6Multimedia.dll was not found".
The check that was supposed to catch this probed only Qt6Core.dll and
Qt6Network.dll, so it was blind to the two files the reports name. It also only
warned and then installed anyway, so a caught problem was still compounded.
- Scan every file the purge targets with FindFirst, which takes the same
wildcards the Delete/RMDir calls use, instead of two hardcoded names
- Widen IsExistingCockatriceInstall from Qt6Core.dll to Qt6*.dll so a directory
left holding only some Qt modules is still recognised as an install
- Gate the scan on that, so a first-time install into a directory that happens to
hold unrelated Qt5 DLLs is not turned into an abort
- Abort with a non-zero exit code instead of warning, so the new payload is
never written next to files of the previous version
- Skip the message box on an unattended /S install, which would block on it
forever, matching the two existing abort sites
* [Installer] Cancel a silent upgrade when the running-instance guard fails
The running instance guard is what stands between a locked install directory
and the mixed Qt DLL set that breaks the next start, but it failed open in two
places and let the install carry on anyway:
- powershell.exe could not be started: the check warned and returned
- the process lookup answered "could not determine": WaitForAppToClose returned
on $R0 != 1, so "unknown" was treated exactly like "not running"
On a silent upgrade there is nobody at the keyboard to close the applications in
response to that warning, which is precisely the state in which the launcher was
still holding its Qt DLLs mapped.
- Treat "could not determine" as its own state rather than folding it into "not
running", in all three places WaitForAppToClose inspects the result
- Cancel a silent upgrade in both cases; keep an interactive install on the
existing warn-and-continue path, since refusing there would leave an installer
that can never complete and the stale-runtime check still applies
- Route every abort through one AbortInstall so the dialog-vs-silent decision
lives in a single place, and stop the two pre-existing warning dialogs that a
/S install without /R would have blocked on forever
* [Installer] Wait on the process that launched the update installer
The in-app updater starts the installer and only then quits itself, so the
installer has to decide whether the launching process is still alive while it is
running. It did that by polling powershell.exe for an image name under the
install directory, which is a proxy for the real question - is the process that
started me still holding its Qt DLLs mapped - and a proxy that can answer "not
running" or "could not determine" while the process is very much alive. Either
answer let the install carry on into the mixed Qt DLL set.
Take /PID= from the command line and wait on that process handle directly:
- OpenProcess with PROCESS_SYNCHRONIZE only, which is all that is needed to wait
and the least likely to be refused for an unrelated reason
- WaitForSingleObject for the same 60 s the name based check allows, so a client
slow to flush its card database is not squeezed
- Treat an unopenable handle as "already exited", which is what it means for a
dead process
- Cancel the upgrade if the launcher has not exited, rather than replacing files
it is still using
Run it before EnsureAppsNotRunning, not after: it needs no powershell.exe, so it
is the one check that still works on a machine where the process lookup cannot
run, and it is what lets that case keep upgrading instead of being cancelled by
what was only ever a proxy for it. Keep an older client, which passes no /PID=,
on the existing path.
An older installer ignores /PID=, and /D= stays the last parameter either way.
* [Client] Pass our process id to the update installer
The updater starts the installer and only then quits itself, so the installer
begins replacing files while this process still has every Qt runtime DLL mapped,
and a mapped DLL cannot be deleted. It has to work out that it must wait, and
until now all it had was its own image name to go on.
Pass /PID= so it can wait on this process directly instead of guessing, and
cancel the update rather than replace files this process is still using. /D=
stays the last parameter, which NSIS requires.
* [CI] Fail the build when the installer is missing a Qt runtime DLL
The Windows payload is assembled by globbing every *.dll out of the build output
directories, so nothing but luck decides whether a module the client links
statically - Qt6Multimedia.dll, Qt6QuickWidgets.dll - reaches the installer at
all. A module that is left out is invisible until a user installs an update and
the client then refuses to start, with "The code execution cannot proceed because
Qt6Multimedia.dll was not found" or a missing procedure entry point in a
Qt6QuickWidgets.dll carried over from the previous version.
Derive the required list from SERVATRICE_QT_MODULES, COCKATRICE_QT_MODULES and
ORACLE_QT_MODULES, the same module lists the targets are actually built against,
so it cannot drift from what the binaries need, and write it next to the CMake
cache as required-qt-runtime.txt. Add the platform plugin, without which the
client cannot start at all.
Assert it in the existing 7-Zip pass that already rejects build-tree artifacts,
comparing exact relative paths so a same-named file in the wrong directory does
not satisfy a requirement, and normalizing the archive's own separators so it
does not matter which one NSIS chose. Missing manifest, missing entry or missing
7-Zip-independent failures all stop the build rather than warn, which is the
whole point: the previous check already failed hard for artifacts, and a warning
here is what let a broken package ship.
* [Client] Point the Windows qt.conf translations path at the real one
The Windows qt.conf carried Translations = Resources/translations, copied from
the macOS bundle block, but the Windows install has no Resources directory: the
.qm files are installed to COCKATRICE_WIN32_QM_INSTALL_DIR, which is
translations, and main.cpp looks for them at applicationDirPath() +
"/translations". Leave the macOS value alone, which is correct for it.
* [Installer] Close the /? help string so the installer compiles again
The last line of the continued MessageBox string ended in $\n\" instead of $\n".
NSIS reads \" as an escaped quote, so the string never terminated and makensis
rejected the script, which took the CPack packaging step down with it. The
Windows build failed on "Problem running NSIS command" before ever reaching the
new required-runtime check.
* [Installer] Give FindFirst the two arguments it takes
FindFirst takes the pattern and the output variable and leaves the search
handle in $0. There is no handle argument, and FindNext taking only a variable
is the giveaway: the handle has to live somewhere implicit, and it is $0.
The call passed three arguments, so makensis rejected the script and the CPack
packaging step failed with "Problem running NSIS command" before ever reaching
the required-runtime check.
This was not only a compile error. The handle variable was never populated, so
the stale-runtime check that commit
|
||
|
|
61e01990c4 |
[CI] Use Doxygen v1.18.0 (#7374)
Some checks failed
CodeQL / Analyze (cpp) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
Build Docker / Servatrice (arm) (push) Has been cancelled
Build Docker / Servatrice (x86) (push) Has been cancelled
Build Docker / Publish multi-platform Servatrice image (push) Has been cancelled
* Bump doxygen version to v1.18.0 * Update Doxyfile config to v1.18.0 * bump batch size * bump more |
||
|
|
6816c580cf |
Bump doc/doxygen/theme from d52eafe to 46483f1 (#7371)
|
||
|
|
1365f3cb49 |
[Avatar] Cap user avatar upload resolution at 1024x1024 (#7353)
Some checks failed
CodeQL / Analyze (cpp) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
Build Desktop / Configure (push) Has been cancelled
Build Docker / Servatrice (arm) (push) Has been cancelled
Build Docker / Servatrice (x86) (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 44 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Servatrice_Debian 12 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 26 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
Build Docker / Publish multi-platform Servatrice image (push) Has been cancelled
Update Translation Source / Push strings (push) Has been cancelled
* [Avatar] Cap user avatar upload resolution at 1024x1024 * Remove stylesheet. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>2026-09-27-Development-3.1.0-beta.15 |
||
|
|
2632f8a040 |
[Client/Server] Keep non-deck files out of the deck storage (#7352)
The local deck and replay browsers list every file in the folder, so a stray file (e.g. a PNG screenshot of a deck) can be parsed as a garbage plaintext deck, opened in the editor, and uploaded. The server also accepts oversized deck payloads with only silent truncation. Hide files that are not in a supported deck/replay format in the two QFileSystemModel views (directories stay visible), skip them when opening, reject them when uploading, and make the server reject deck uploads larger than MAX_FILE_LENGTH instead of truncating them. Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
fa8220df3c |
[Client] Exit deterministically when launching the update installer (#7309)
* [Client] Exit deterministically when launching the update installer * [Client] Only quit when the main window close is accepted Wait for MainWindow::close() to be accepted before quitting after the update installer is launched. When the close is vetoed (a running card DB update, open games, or an unsaved deck), keep running and tell the user the installer is already waiting, instead of exiting over their answer. Also fix the comment so it does not claim settings are saved on the vetoed path. * [Client] Only quit on update when the shutdown actually ran MainWindow::closeEvent has a static re-entrancy guard that returns early on a second close event, leaving it in its default accepted state. DlgUpdate reached from a nested event loop while a shutdown prompt was up could then get close() == true with no shutdown work done (no settings flush, no tab shutdown) and tear down the process behind an unanswered prompt. Add MainWindow::closeForUpdate(), which reports false when a close is already in progress or was vetoed, and gate the update-exit on it. * [Client] Use QCoreApplication::exit() to leave on update QCoreApplication::quit() only asks the application to quit and can be interrupted if a top-level window refuses its close event. On the update path the process must actually leave so its Qt DLLs are unlocked before the installer starts replacing files - apply the exit(0) variant both on the accepted-close path and the fallback where no MainWindow was ever closed. * [Client] Warn that the update installer force-closes within a minute Telling the user Cockatrice just 'stays open for now' is misleading: the installer launched by #7308 waits only about 60 s for a graceful close before it force-terminates the process, which would lose unsaved work. State the deadline and tell the user to save and close before then. * [Client] Warn that a vetoed update is cancelled, not forced The warning on the veto path promised the opposite of what the installer does: WaitForAppToClose in #7308 never force-closes cockatrice.exe ($AllowForceClose is 0), it aborts the update after 60 s so no unsaved data is lost. A user who is told Cockatrice will be terminated in a minute hurries to close it, only to find the update already given up. State the real outcome instead: the installer waits about a minute for a close, and the update is cancelled if that does not happen. * [Client] Warn about a waiting installer without blocking it CloseMatchingApps in #7308 sends the WM_CLOSE once and the wait loop only polls IsAppRunning afterwards, but QMessageBox::warning is application modal and QGuiApplicationPrivate::processCloseEvent drops spontaneous close events for windows blocked by a modal widget. The veto path therefore swallowed the installer's one and only request, so even a user who resolved the blocker and closed the app still ended in the 60 s timeout. Move the warning into warnInstallerIsWaiting() and show it modeless, so the main window stays able to answer the installer while the message is up. * [Client] Close the parent on the no-MainWindow update fallback The fallback branch exits the process without closing anything, so it skips the settings flush and tab shutdown this change exists to get in front of the installer - the opposite of the fix if a call site ever stops passing a MainWindow. Close the parent widget first, like the code before this change did, and only then leave unconditionally: the parent is the one that knows how to shut down gracefully, and the deterministic exit is what the installer needs. * Add override. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>2026-09-27-Development-3.1.0-beta.14 |
||
|
|
28d684f8aa |
[Windows] Close running instances and purge stale runtime DLLs during update (#7308)
* [Windows] Close running instances and purge stale runtime DLLs during update
* [Windows] Scope running-instance handling to install dir and fix NSIS build
- Drop !include nsExec.nsh: nsExec is a plugin DLL, not a header, so
makensis aborts before NSIS can build the installer.
- Match processes by image name and executable path under $INSTDIR via
PowerShell, then close (WM_CLOSE) and force-stop only those PIDs, so an
unrelated oracle.exe (Oracle DB) is never killed on a silent /R update.
- Gate the stale-runtime-DLL purge on $INSTDIR\cockatrice.exe existing, so
a first-time install can't recursively delete an unrelated Plugins
directory.
* [Windows] Make process detection rely on an explicit PowerShell exit code
* [Windows] Fix exit-code detection in the uninstaller process check
* [Windows] Pass the install dir to PowerShell without quoting pitfalls
* [Windows] Fail closed when a process path is unreadable
* [Windows] Only pre-check running instances for /R updates
* [Windows] Wait on a wall-clock 60 s deadline instead of a loop counter
* [Windows] Verify the force close and abort if the process survived
* [Windows] Defer silent updates instead of force-killing the client
* [Windows] Purge OpenSSL 3 DLLs on portable upgrades and broaden the stale-DLL guard
* [Windows] Replace the GetTickCount macro with a direct System::Call
* [Windows] Only close running instances whose path is readable
CloseMatchingApps reused the fail-closed detection filter, which counts a
process whose Path cannot be read (Access denied) as a match. The installer
runs elevated, so CloseMainWindow() would then be delivered to an unrelated
oracle.exe / servatrice.exe belonging to some other program, silently closing
a third party application during a Cockatrice update.
The set of processes we act on now has to be the strict one: a readable path
under $INSTDIR. Unreadable paths are simply not ours.
* [Windows] Match running instances by a readable path under the install dir
The Path read in the Where-Object filter raises "Access is denied" for
processes the installer cannot open, and both branches of the try/catch
turned that into a match. Together with mapping every non-zero nsExec result
to "running" this made the guard a hard stop rather than a fail-closed one: a
machine with an Oracle database service (or a servatrice service) running as
SYSTEM always has a process named oracle / servatrice whose path cannot be
read, so every silent update aborted after 60 s and every interactive install
re-asked for Retry indefinitely.
A process whose path cannot be read is not one of ours - the updater is
launched by the application it updates and therefore runs in the same user
session, and an interactive install is elevated and can read the paths of the
user's own processes. Both the detection and the close path now require a
readable path under $INSTDIR, which is what the comment above the helpers
already claimed.
* [Windows] Distinguish "could not determine" from "definitely running"
Mapping every non-zero nsExec result to "running" folds nsExec's "error"
return (powershell.exe could not be started at all - removed, blocked by
policy, refused by antivirus) into "Cockatrice is running". That state can
never clear: closing applications does not help, so silent installs aborted
after the full 60 s wait and interactive installs could not get past the
Retry/Cancel prompt.
IsAppRunning now reports three states (0 not running, 1 running, 2 could not
determine) and a new IsProcessLookupAvailable preflight checks that
powershell.exe can run at all. A broken PowerShell is reported to the user
once - with a message box, since a silent install would otherwise show
nothing - and the install continues instead of being blocked forever. Only a
definite "running" is now waited for.
* [Windows] Force-stop the console applications after the grace period
Servatrice and Oracle are console applications, so CloseMainWindow()
returns false and does nothing for them. Every silent update therefore
waited the full 60 s and then aborted, and every interactive install looped
on the Retry prompt, for any user running Servatrice out of the install
directory.
They now get a second shutdown path: once the grace period is over they are
force-stopped by PID with the same strict install-dir filter, since they hold
no unsaved user data. Cockatrice keeps the graceful-only treatment - a
force-kill there could destroy an unsaved deck - and still aborts instead.
* [Windows] Tell the user when a silent install or uninstall is aborted
On the /R path the installer runs with SetSilent and SetAutoClose, so the
DetailPrint before Abort went nowhere: the user clicks Help -> Check for
Client Updates, the download finishes, Cockatrice closes its window, and
nothing else happens - no update and no error, because the client launches the
installer detached and never looks at its exit code.
Every silent abort path now shows a message box telling the user which
application is still open and that they should close it and run the
installer again. The same applies to the silent uninstall, where the only
output is a log file nobody reads.
* [Windows] Treat leftover runtime files as an install marker for the purge
The guard on the stale-DLL purge only looked for the three executables and
uninstall.exe, so it skipped precisely the case the purge exists for: a
previous failed update whose uninstaller removed what it could and silently
skipped the Qt DLLs that a running instance had locked. None of the four
markers is left in that directory, and the stale DLLs then survive into the
new install.
A leftover Qt6Core.dll (what we ship today) or the qt.conf written by a
portable install now count as an install marker as well.
* [Windows] Run the section instance check before any file is deleted
Call EnsureAppsNotRunning sat after the portable-upgrade block, which had
already deleted the old cockatrice.exe, Qt*.dll and plugins directory. An
interactive install ("Portable mode" pointing at an existing portable folder)
has $ReinstallMode = 0, so the .onInit guard is skipped and locked files were
silently skipped - exactly the mixed-Qt-DLL state this change is about.
The check now runs right after SetOutPath, before the first Delete/RMDir in
the section.
* [Windows] Guard the old install before the old uninstaller deletes files
The interactive upgrade path removes the previous installation long before the
install section: componentsPagePre runs the old UninstallString on the
components page, i.e. before the directory page and before any call of ours
can check anything. Every uninstaller shipped before this change has no
un.EnsureAppsNotRunning, so for exactly the versions affected by #1576 the old
files were deleted while cockatrice.exe was still running and the new
installer had no say in it.
componentsPagePre now runs the same check before both ExecWait calls. At that
point $INSTDIR is still only the default, not the directory the old install
lives in, so the new EnsureOldInstallNotRunning guards the location recorded
in the registry instead. The helpers now work on a $GuardDir the caller
picks; the normal entry points keep seeding it with $INSTDIR.
* [Windows] Do not abort the section check past the point of no return
"nothing has been replaced yet, so aborting just defers the update" only holds
for the guard in .onInit. On the /R path AutoUninstallIfNeeded has already run
the old uninstaller, which RMDir /r's the whole install directory, and the
purge below it deletes the runtime files - so an Abort from the section would
leave the user with the old version uninstalled and the new one not installed
at all, which is strictly worse than the bug this change fixes.
The ordering now guarantees the check already passed: .onInit guards the /R
path before the uninstall, so the section only repeats it where that is not
already true, i.e. for interactive installs, where nothing of this installer
has been deleted yet and an Abort is the correct outcome.
As a safety net for whatever the purge could not remove (a still locked file,
or a check that could not run at all), HasStaleRuntimeFiles reports surviving
runtime files with a message box - also on a silent update, where there is no
other way to let the user know the install came out incomplete.
* [Windows] Only block the in-app updater with a message box
The message box on the silent abort would also fire for a plain /S install,
which is exactly the unattended case: a package manager (or a CI job) running
the installer with a leftover Cockatrice process would now hang forever on a
dialog nobody can see instead of aborting.
The box is therefore limited to /R, the path the in-app updater takes, where
the user just clicked Help -> Check for Client Updates, the client launches
the installer detached and never inspects its exit code, and the window is
about to disappear - without a message, the update would silently not happen.
Every silent abort also sets an error level now, so an unattended caller can
see that the run failed instead of having to read the log.
The silent uninstaller reports through log and exit code for the same reason:
it is run unattended by the /R updater itself (QuietUninstallString) and by
package managers. The installer that invoked it tells the user about the
incomplete result itself.
* [Windows] Treat an unset guard directory as undeterminable
$GuardDir is set by the three entry points into the check, but it is now what
decides whether a process counts as one of ours, and an empty string would
match every process that merely shares an image name - the same false positive
the strict path filter exists to avoid. Report it as "could not determine"
instead of running the check, so a future caller that forgets to set it warns
the user rather than closing or killing a third party process.
---------
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
|
||
|
|
7c9dc303f8 |
[Game] Make 'Put top cards on stack until...' history case-sensitive (#7358)
Some checks failed
CodeQL / Analyze (cpp) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
Build Desktop / Configure (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 44 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Servatrice_Debian 12 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 26 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
Build Docker / Servatrice (arm) (push) Has been cancelled
Build Docker / Servatrice (x86) (push) Has been cancelled
Build Docker / Publish multi-platform Servatrice image (push) Has been cancelled
|
||
|
|
26a74c96c5 |
[Oracle] Stop linking the full client network stack into the oracle binary (#7354)
Some checks failed
CodeQL / Analyze (cpp) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
Build Desktop / Configure (push) Has been cancelled
Build Docker / Servatrice (arm) (push) Has been cancelled
Build Docker / Servatrice (x86) (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 44 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Servatrice_Debian 12 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 26 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
Build Docker / Publish multi-platform Servatrice image (push) Has been cancelled
Co-authored-by: user.email <Bruebach.Lukas@bdosecurity.de> |
||
|
|
e9cd9453ea |
[Startup] Run the onboarding wizard before the first-run set dialog (#7349)
On a clean install, checkUnknownSets() fired before the onboarding wizard, so the legacy 'all the sets have been enabled' welcome and the Manage Sets dialog appeared first - with no card data there are no sets to manage yet. Defer the set check until after the wizard closes, suppress its dialogs while onboarding runs, and enable newly downloaded sets silently instead. Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
bc26e19564 |
[DeckEditor] Highlight card name in VDE quick search after adding (#7350)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
329a6ba94c |
[Installer] Restore cockatrice:// and .cod registration in the Windows NSIS installer (#7355)
Some checks failed
CodeQL / Analyze (cpp) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
Build Desktop / Configure (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 44 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Servatrice_Debian 12 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 26 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
Build Docker / Servatrice (arm) (push) Has been cancelled
Build Docker / Servatrice (x86) (push) Has been cancelled
Build Docker / Publish multi-platform Servatrice image (push) Has been cancelled
Co-authored-by: user.email <Bruebach.Lukas@bdosecurity.de> |
||
|
|
0955388d50 |
[Client] Release room-join join markers when a command times out (#7348)
RemoteClient's ping sweep deleted timed-out pending commands without emitting finished, so TabServer's in-flight room-join markers were never released: the room stayed silently unjoinable for the rest of the session even though the connection stayed alive. Answer swept commands with RespNotConnected instead of dropping them, matching the disconnect cleanup, so every awaiter gets a deterministic terminal response. Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
a9b38ec0e1 |
[UserList] Sort developers below judges (#7347)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
712ca61a9e |
[Themes] Encode home backgrounds as JPEG to slim the repo (#7346)
The home background PNGs shipped since #7280 total ~67 MB and bloat every clone and pull for other devs. Encode them as JPEG at quality 95 (matching resolution, visually lossless at 40-48 dB PSNR since the art is noise-heavy renders PNG cannot compress) for ~79% size reduction. The scheme-variant and theme asset resolution already probe .jpg, so only the qrc entries needed updating. Refs #7327 Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
eae9591a17 |
[Server/Client] Unify report categories (#7345)
* [Server/Client] Unify report categories into one shared source of truth * Lont. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
a1d8ce6165 |
[Accessibility] Bind card size sliders to Ctrl + Scroll (#7338)
Some checks failed
CodeQL / Analyze (cpp) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
Build Desktop / Configure (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 44 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Servatrice_Debian 12 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 26 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
Build Docker / Servatrice (arm) (push) Has been cancelled
Build Docker / Servatrice (x86) (push) Has been cancelled
Build Docker / Publish multi-platform Servatrice image (push) Has been cancelled
Ctrl+scroll over a card display now resizes the cards everywhere a card size slider is shown, matching the standard way of resizing content. CardSizeWidget learns to forward Ctrl+wheel events onto its slider via an event filter that is installed on the display container and, when present, on the scroll area's content widget so the resize intercepts the wheel event before the view scrolls. The existing slider valueChanged wiring then rescales the displayed cards. Applied to the visual deck editor (per card group, covering flat and overlapped layouts), visual database display, printing selector, sample hand, visual and public deck storage, Archidekt previews and EDHRec card displays. Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
71ef36374d |
[DeckEditor] Show card counts in Visual Deck Editor banners (#7339)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
7a41dfe175 |
[PictureLoader] Fix custom-folder pictures in subdirectories not loading (#7340)
Some checks failed
CodeQL / Analyze (cpp) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
Build Desktop / Configure (push) Has been cancelled
Build Docker / Servatrice (arm) (push) Has been cancelled
Build Docker / Servatrice (x86) (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 44 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Servatrice_Debian 12 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 26 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
Build Docker / Publish multi-platform Servatrice image (push) Has been cancelled
CardPictureLoaderLocal compared the full file name (including extension) of the custom-folder candidate against the extension-stripped base name of each directory entry, so a CUSTOM subfolder image like pics/CUSTOM/poker/1 of Hearts.png never matched and the card fell through to the network. Compare the complete base names instead. Add matcher tests covering CUSTOM subfolder resolution. Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>2026-09-21-Development-3.1.0-beta.13 |
||
|
|
e97ef5a617 |
[Style] Do not offer broken windows11 (#7341)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
fe83966087 |
[Theme] Revert default light palette and only include AppColors (#7342)
* [Theme] Revert default light palette and only include AppColors * [Style] Also remove dark mode palette --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
e01912d3c8 |
[Tests] Add manual picture loader benchmark against the real card hosts (#7288)
* tests: add manual picture loader benchmark against the real card hosts (cherry picked from commit 588d71d3b9a8278ab98b847802908f322c1035b7) * tests: address review on picture loader benchmark - Sandbox via unique app/org names plus Linux-only XDG redirection, derive the warm-cache probe and data path from SettingsCache, and bail out when the temporary sandbox cannot be created. - Run each pass with a fresh worker and shut workers down before reading the 429 counters, so the redirect cache is persisted and no worker thread can outlive the stack-local counters or the installed message handler. - Make s_activeCounters atomic and always forward log output when the previous handler is the built-in (nullptr) one. - Validate --timeout-min, scale the cached-pass budget with --count, honour the first --url as the stress template, and add the missing trailing newlines. - Zero-initialise SettingsCache members so the benchmark mock cannot dereference an indeterminate pointer. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
9f66a098ac |
[PictureLoader] Fix worker thread shutdown and cross-thread cache clearing (#7292)
* [PictureLoader] Fix worker thread shutdown and cross-thread cache clearing clearNetworkCache() ran directly on the UI thread while the worker thread owned the disk cache and redirect cache, racing cache reads/writes. Make it a worker-thread slot invoked via a blocking queued call when the thread is running, so the 'Cached card pictures have been reset.' message is truthful. The worker thread was also never quit()/wait()ed: both destructors only deleteLater'd their objects, so Qt warned 'QThread: Destroyed while thread is still running' and leaked a running loop at exit. Wire the worker's finished() signal to its own deleteLater() (canonical worker-object pattern), add shutdownThread() to stop the loop, and let CardPictureLoader destroy the QThread only after wait() has returned. * [PictureLoader] Guard cache teardown and stop blocking the UI thread --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
14acf3bf64 |
[PictureLoader] Add user-configurable per-host request caps (#7287)
* [PictureLoader] Add user-configurable per-host request caps Picture downloads were throttled to a uniform 10 requests/second per host with no way to tune a specific server. A rate-limited API host (Scryfall caps at 10 req/s) can trip 429s during bursts, and CDN hosts with no rate limit were throttled needlessly. Introduce developer-owned per-host caps that users can only ever lower, never raise, exposed in the download settings page: - DownloadSettings::DEVELOPER_HOST_CAPS sets the ceiling per host (api.scryfall.com 9, cards.scryfall.io unlimited, others 10). - A new hostRequestLimits setting stores user overrides in downloads.ini; clampHostRequestLimit() bounds them to [1, devCap] so a user can reduce api.scryfall.com to 5 but never raise it above 9. - The picture worker seeds, halves on 429, and recovers its sustained per-host allowance against the effective ceiling instead of the global maximum, and skips per-host accounting entirely for unlocked hosts (cards.scryfall.io) while global pacing and 429 backoff still apply. - The deck editor settings page gains one spinbox per known host, each clamped to its developer cap. * [PictureLoader] Let unlocked hosts skip dispatch pacing; adjust limits per URL Two refinements to the per-host request caps: - Unlocked hosts (UNLIMITED_HOST_QUOTA, e.g. cards.scryfall.io) no longer wait on the 100ms dispatch pacing or consume the global per-second quota. dispatchQueuedRequest fires their queued requests back-to-back, bounded only by their 429 backoff window and Qt's per-host connection pool, so an unthrottled CDN is not artificially slowed. - The deck editor download settings page replaces the static grid of one spinbox per known host with an "Adjust Rate Limit" toolbar action on the URL list. It picks the host out of the selected URL and clamps the entry against the developer cap table (including for user-added URLs). Also fixes a review finding: resetRequestQuota could write the UNLIMITED_HOST_QUOTA sentinel (-1) into the sustained per-host quota when a host became unlocked mid-run, permanently poisoning its allowance. Stale entries for unlocked hosts are now dropped, and the per-second seed is clamped against the effective ceiling so a lowered limit applies immediately. * [PictureLoader] Cap unlocked host bursts and adapt them to 429s * [PictureLoader] Store per-host limits readably and show them per URL * [PictureLoader] Make dispatch and rate-limit bookkeeping key on the real host Addresses ZeldaZach's round-4 review nits: - Dispatch now resolves the cached-redirect chain before the in-flight gate, so a redirect learned after a URL was queued can no longer bypass the MAX_IN_FLIGHT_PER_HOST cap and drain the whole queue onto the redirect target, which may carry its own developer cap. processSingleRequest does the same so the allowance math keys on the host that is actually hit. - The per-host in-flight slot is released when the reply is destroyed (with the worker as the connection context) rather than on a 'finished' connection bound to the work object, so an aborted reply or a work object deleted while a reply is pending can never permanently shrink the fast path's concurrency. - storeSettings only prunes limits for hosts with neither a URL nor a developer cap, so throttles on redirect targets (api.scryfall.com -> cards.scryfall.io) survive URL removal. - Unlocked hosts are offered 0..UNLOCKED_HOST_LIMIT_MAX (50) in the rate limit dialog, matching clampHostRequestLimit() and the documented hand-editable range, so values written into downloads.ini are no longer silently rewritten on the next edit. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
3d5eb84d81 |
[PictureLoader] Seed per-host allowances on demand and skip hosts in 429 backoff (#7286)
* [PictureLoader] Seed per-host allowances on demand and skip hosts in 429 backoff The quota reset re-filled every host's remaining allowance to a full MAX_REQUESTS_PER_SEC as soon as the queue had a request for it, so a server that was just rate limited could be hammered again at full speed immediately after (or even during) recovery. Only seed a host's allowance the first time it is dispatched in the current second, seeded from its reduced sustained quota, and skip hosts still inside their 429 backoff window entirely, handing the entry back to its worker so it can wait the backoff out or fall through to another source instead of parking in the queue with no reply pending. Deferrals wait on the host that is actually blocking the request (cached-redirect targets and the reply host of a 429) rather than the current card URL's host. Rebased onto network-requests/request-pacing, which absorbed the earlier pacing and dispatch-guard commits, and reuses its updateTimerState idle-429 recovery plumbing. * [PictureLoader] Hand backed-off entries back across the whole dispatch tick Review fixes on the 429-backoff skip: - Hand a backed-off queue entry back to its worker via scheduleDeferredRetry(host) instead of startNextPicDownload(), which was looping on the pre-redirect host every 100ms tick whenever the queued URL was a cached-redirect target whose redirect host was the one in backoff. - Keep scanning the queue after a hand-back (--i; continue) so a backed-off host at the head no longer consumes the entire dispatch tick, stalling every healthy host further down. - Emit imageRequestSucceeded(url) before makeRequest()'s cached-redirect backoff return so the status bar reclaims the deferred URL's widget instead of inflating the progress bar forever. - Only spend a per-second allowance when makeRequest() actually issues a request: it returns nullptr when the redirect target is backed off and the work is handed back, so the slot would otherwise be wasted on a no-op. - Gate the dispatch-time backoff skip on requestTouchesNetwork() so entries served straight from the disk cache (e.g. with downloads disabled) are not bounced into a 30-60s deferred retry for a host that 429'd earlier. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
334a743953 |
[PictureLoader] Pace requests and run the throttle timers on the worker thread (#7285)
* [PictureLoader] Pace requests and run the throttle timers on the worker thread Previously the whole backed-up queue was drained in a burst as soon as a request was enqueued, sending up to 10 requests back-to-back and then immediately re-filling the quota one second later. That hard-bursts a rate-limited API like Scryfall's (10 requests/second) into a 30 second lockout. Introduce a pacing timer that dispatches a single queue entry every 100 ms, so the per-second allowance is used smoothly instead of in spikes, and keep the quota timer at 1 second. Also fix both timers' thread affinity: they are QTimer value members and so are not QObject children, meaning moveToThread() on the worker left them on the main thread while the slot code started them from the picture thread, which was a no-op that also warned. They are moved to the worker thread explicitly and started lazily from there. * [PictureLoader] Guard dispatch timer restarts and drop dead request quota * [PictureLoader] Reconcile quota-timer lifecycle with idle 429 recovery --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
e2a4556546 |
[Filter] Restore exact, case-insensitive set code search (#7336)
Some checks failed
CodeQL / Analyze (cpp) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
Build Desktop / Configure (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 44 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Servatrice_Debian 12 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 26 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
Build Docker / Servatrice (arm) (push) Has been cancelled
Build Docker / Servatrice (x86) (push) Has been cancelled
Build Docker / Publish multi-platform Servatrice image (push) Has been cancelled
* [Filter] Restore exact, case-insensitive set code search (#7332, #7333) * [Filter] Group set query suffix modes into a subrule Address review feedback: keep the 'e'/'set' prefix in one place and move the three suffix modes (exact, negated, release-date comparison) into a dedicated choice-like rule so sv.choice() still dispatches on them. Add coverage for the release-date comparison mode. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
7a2492ac67 |
[Chat] Render room chat history usernames as live user tags (#7269)
* Render room chat history usernames as live user tags (#1595) Room chat history carries no user-level data, so history usernames were rendered as fixed, zero-level tags: the moderation context menu was missing the buddy/ignore and promote/demote entries and the stored name casing was never corrected. Resolve each history author against the online user list and, when found, build the user tag with the real user level and name so the entry behaves exactly like a live chat tag. Offline users keep the plain fallback. - chat_view: look up history authors via getOnlineUser for the real level/name * Fix offline history usernames getting a leading underscore The offline fallback used "_" as the level placeholder, producing an href of user://__NAME. The hover handler splits at the first underscore, so interactions targeted a nonexistent "_NAME" user. Use level 0 so offline history entries render as zero-level tags like before. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
ef68a7bdcc |
[Card] Add a setting for the language used in card search (#7314)
* [Card] Add a setting for the language used in card search Localized card names and texts can now be searched too, controlled by a 'Language used in card search' toggle (English, selected card language, or both) on the general settings page. Untranslated cards always keep matching in English. Removed the redundant local copy of the URL templates list in the localized picture loader while here. * [Card] Bind search language per FilterString instance The peg parser rules are set up once per process, so the GenericQuery and OracleQuery rule actions could not capture per-instance state. Instead of storing the search language in a process-global that FilterString instance methods mutate, hand it to the rule actions through a thread-local parse context and copy it into the filter closures they produce. Card evaluation in FilterString::check no longer reads any process-global state, and each instance keeps the language it was built with; constructing one instance no longer changes what unrelated instances (deck filter, drop-to-hand, zone views) match against. The card database display model stores the raw query and rebuilds the FilterString when the search language changes, since the language is now bound at parse time. Add tests for the English/Selected/Both search modes, the English fallback for untranslated cards, and per-instance language independence. * [Card] Pass the card search language to deck and zone card searches Wire the two remaining FilterString consumers to the configured card search language so card-name matches respect it everywhere: - DeckFilterString now takes the search language and mode, exposes them to its [[card name]] rule action via a thread-local parse context (same pattern as FilterString), and the engine's card database uses them for content search. - ZoneViewZone reads the card language from CardsDisplaySettings when applying its search filter, and the reveal-zone widget re-applies the active search when the language setting changes. - The deck-storage search re-runs its filter against the current card language setting, including live re-application when the setting changes. Game-action targeting (DlgMoveTopCardsUntil) intentionally keeps evaluating against English card names. * [Card] Rename CardSearchLanguage to SearchLanguageMode * [Card] Restore displaced namespace doc in card_localization.h * [Filters] Pass CardSearchLanguage as a single struct * [CardSearchModel] Match English and localized names in Both mode Card names are stored in both English and localized forms, so search for matches in both during the 'Both' search mode instead of checking only the localized name. * [CreateTokenDialog] Fetch cardsDisplay settings inside the apply lambda Avoid capturing the raw settings pointer in the lambda: resolve the card language and card search language from the settings cache at call time so the values are always current when the search language is re-applied. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
12299abcc8 |
[Doxygen] More picture docs (#7220)
Some checks failed
CodeQL / Analyze (cpp) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
Build Docker / Servatrice (arm) (push) Has been cancelled
Build Docker / Servatrice (x86) (push) Has been cancelled
Build Docker / Publish multi-platform Servatrice image (push) Has been cancelled
* [Doxygen] More picture docs Took 12 minutes Took 8 minutes * [Doxygen] Move custom card pictures page into card_pictures subfolder The actual cyclic dependency fix is converting the mutual @subpage reference from custom_card_pictures to fixing_card_pictures into a plain @ref, so the page hierarchy no longer loops back on itself. * [Doxygen] Correct card-picture docs per review * fix table layout * [Doxygen] Deduplicate placeholder table and document image overrides - Make custom_card_pictures.md the canonical home of the URL reference-point table; loading_card_pictures.md cross-references it through @ref instead of maintaining a second copy (unaddressed review comment). - Switch the remaining @subpage custom_card_pictures to @ref in fixing_card_pictures.md so the page keeps its single parent under user_reference. - Document the Image Overrides feature added in #7311/#7312 on the user page, loading_card_pictures.md and fixing_card_pictures.md: local override storage, the downloadedPics root lookup, exact file-name matching, and the set-folder vs flat export naming schemes. * Update doc/doxygen/extra-pages/user_documentation/card_pictures/custom_card_pictures.md Co-authored-by: tooomm <tooomm@users.noreply.github.com> --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> Co-authored-by: tooomm <tooomm@users.noreply.github.com> |
||
|
|
6823d54c1e |
[DeckShare] Browse and open public decks with loading, error and accessibility states (#7245)
Some checks failed
CodeQL / Analyze (cpp) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
Build Docker / Servatrice (arm) (push) Has been cancelled
Build Docker / Servatrice (x86) (push) Has been cancelled
Build Docker / Publish multi-platform Servatrice image (push) Has been cancelled
Build Desktop / Configure (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 44 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Servatrice_Debian 12 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 26 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
* [DeckShare] Browse and open public decks with loading, error and accessibility states Add a public-decks tab that lists decks published by other users using the server's deck visibility feature, previewing each deck's banner card, color identity, tags and upload time without downloading the deck list until the user opens it. - Add a public-decks tab with a shared-settings widget and a remote model that fetches the target user's decks and refreshes both automatically and on user request, with a loading indicator and a server-error message instead of a blank tab when the fetch fails or the connection drops - Render each deck as a focusable preview tile whose banner, color identity, tags and upload time follow the existing Preview settings, with the deck name announced as the tile's accessible name and Space/Enter opening the deck, mirroring the shared-deck preview tile - Show a message box when opening a public deck fails or arrives corrupted - Publish and unpublish decks from the server storage toolbar and context menu, toggling the deck's own visibility bit (what the server persists) rather than the inherited effective state, and batch the visibility refresh until the last in-flight change is acknowledged - Add the Show Upload Time setting so the tile's upload stamp can be hidden like the other preview details - Update the retranslateUi wiring for the new public-decks tab and rename the share action tooltip from "Deck share" to "Share link" * [DeckShare] Adapt deck upload to the server-derived banner and tag protocol The server now derives the banner card and tags from the uploaded deck list itself, so Command_DeckUpload only carries the client-computed color identity. Drop the reserved banner/tag setters from the editor and storage uploads, send the color identity on remote saves, and read tags from the now-repeated ServerInfo_DeckStorage_TreeItem field. * [DeckStorage] Refresh the visibility column with a guarded timer instead of a latch counter A dropped visibility reply used to leave the pendingVisibilityChanges counter permanently positive, so the Public/Private column never refreshed again and nothing reset it on disconnect. A restartable single-shot timer with a boolean guard re-reads the tree whenever publishes quiet down and is stopped on disconnect, so a lost reply costs one stale refresh instead of killing the column for the session. * [DeckStorage] Summarize batch publish failures when the batch drains Each rejected node stacked its own modal dialog, so publishing a ten-deck selection against a rejecting server made the user dismiss ten dialogs one at a time. Failures are now collected while the batch is in flight and shown as a single summary when the visibility refresh timer fires; a reply that lands outside an active batch still reports right away. * [PublicDecks] Time out the loading state so a dropped reply cannot wedge the tab loading only cleared in decksReceived, but the ping sweep can drop a pending command without ever emitting finished, leaving the tab stuck on 'Loading public decks...' and the refresh button permanently inert. A single-shot timer started per refresh clears the latch and reports a timeout; the latch also clears when the client disconnects. * [PublicDecks] Escape remote-crafted text in tooltips and the tab title Deck names and usernames come from other users' records and Qt renders QLabel tooltips as AutoText, so a name like '<h1><table>...' parsed as markup. Escape and bound the deck-name tooltip and escape the username interpolated into the title label. * [DeckStorage] Distinguish an inherited public state in the visibility column The column reported the effective state while publishing toggles the node's own bit, so a private deck inside a public folder already read 'Public' and toggling appeared to do nothing (and toggling again silently unpublished it). The cell now shows 'Public (inherited)' for that case and the tooltip explains why. * [PublicDecks] Run retranslateUi at construction and name the refresh button retranslateUi was never called from the constructor, so the tooltips set there were absent until a language change. Call it before the first refresh, and give the icon-only refresh button an accessible name for screen readers. * [PublicDecks] Keep the empty and status variants correct across language changes retranslateUi unconditionally rewrote the empty label to the 'nothing published' variant, stomping the 'no decks match your filters' choice rebuildGrid had made, and a visible loading message stayed in the old language. Let retranslateUi pick the same variant rebuildGrid does and re-show the status so it retranslates. * [VDS] Share one color-identity match rule between the two deck grids The remote public decks model verbatim-copied updateColorMatches' switch, down to the ExactMatch normalization and the fact that Includes/Excludes do not normalize case. Extract colorIdentityMatches() next to the FilterMode enum and call it from both so the subtle rule cannot drift. * [DeckStorage] Drop the unused tree widget model accessor The accessor handed the model out past the wrapper methods that exist to keep it encapsulated, and nothing in the stack called it. * [DeckShare] End the public-decks files with a trailing newline keeps the final line's diff clean and stops clang-format CI from flagging the files. * [VDS] Reuse the shared quick settings widget for the public decks tab PublicDecksQuickSettingsWidget was VisualDeckStorageQuickSettingsWidget minus the folders, banner and tooltip controls, with identical wiring for the shared keys and a version of the near-identical file to keep in sync by hand. Fold the Show Upload Time checkbox into the shared widget, give it a setPublicDecksMode() that hides the controls that do not apply, and delete the duplicate. * [PublicDecks] Drop stale deck-list replies after the loading timeout A reply that lands after its own loading timeout (the reverse of the ping sweep dropping the command) could stop the newer request's timeout timer and repaint the grid with out-of-date data. Each refresh now captures a monotonically increasing request id, and only the newest request's reply updates the grid. * [PublicDecks] Re-show a displayed failure message on language changes The status label carries both the loading and the failure message, and retranslateUi hid it whenever the model was not loading, so a language change while a server-error or timeout message was on screen swapped it for the (empty) grid. The tab now keeps the last failure text and re-shows it when not loading, clearing it once a new refresh starts. * [DeckStorage] Keep the visibility refresh armed until replies land The single-shot drain was armed with the 500 ms delay at send time, so a round trip slower than that drained before the server applied the change, re-read the old state and never re-armed, leaving the column stale until a manual refresh. The timer is now armed with the full network timeout at send time (a lost reply still costs one stale refresh) and re-armed with the short delay every time a reply lands. * [DeckShare] Close public decks tabs when the client disconnects TabSupervisor::stop() built tabsToDelete from the room and game tabs only, so a public decks tab survived a disconnect, sitting with stale contents and a refresh button that kept hitting the dead client. Its values are now folded into the same cleanup. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
8ca749c07d |
[DeckShare] Open shared decks via links with a gated preview flow (#7244)
* [DeckShare] Open shared decks via links with a gated preview flow - Serialized url-chain dispatcher in IntentUrlParser; queue-drained urlChainFinished(bool) drives the startup auto-connect fallback - Open-shared-deck intent with sequential download state machine, 15s per-item timeout, partial-success offer, livable Cancel via ApplicationModal dlg_login_prompt interactive fallback - Preview dialog: download progress label, share vocab sweep, palette-highlight selection frame, Space/Enter keyboard toggle, NoFocus checkbox, double-click tile opens immediately - Confirm-before-server-migration with one-shot restore to the previous server on failed/cancelled chains (statusChanged settle deferral), hostname-only identity comparisons - Skip credential link when already connected; arrow-key navigation in FlowWidget; card glows use palette highlight - Address code-review M1-M4 and UI/UX QA blockers 1-2 * [DeckShare] End the open-shared-deck files with a trailing newline * [DeckShare] Forward a dependency's cancellation as the owner's own * [DeckShare] Let intent chains opt into the link sign-in dialog * [DeckShare] Track link-intent chains per-run so each can restore its own session * [Settings] Match a server on the exact host and port when adding it * [DeckShare] Confirm the share link's target server before opening a deck * [DeckShare] Reformat the link sign-in intent constructor * [DeckShare] Time the share-list round trip and backstop silently-destroyed intent chains * [Client] Drain a single-instance payload before its handlers read the socket again * [Client] Treat a busy single-instance primary as alive instead of stealing its socket * [DeckShare] Keep arrow-key navigation between flow items inside a scroll area * [Client] Skip the startup connection when a macOS URL launch owns the connection * [Client] Redact share secrets from activation URL logs * [Client] Make the link-connection gates port-aware and keyboard-safe Second-pass review notes for the shared-deck link flow (Cockatrice#7244): - FlowWidget arrow-key navigation is opt-in via addNavigableWidget, so combo/spin controls on the analytics flows keep their own arrow keys - isConnectedTo and the open-deck/join-game preconditions compare the configured server port alongside the host, so a same-host/different-port link cannot resolve its share token or game id on the wrong instance - the link sign-in dialog reuses an existing server entry's saved name instead of renaming it to the raw hostname - skipStartupAutoConnect is cleared once the launch chain connects, so a later mid-session declined link cannot fire the startup fallback - the plain-launch path of SingleInstanceManager no longer blocks on the primary's ACK - link- and server-supplied text is html-escaped in the confirm prompts and shared-deck preview so markup cannot spoof the shown messages --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
ba2900dcb9 |
[DeckShare] Create temporary share links for local and server decks (#7243)
* [DeckShare] Create temporary share links for local and server decks
* [DeckShare] Address review findings and harden the share flows
Gate every share entry point on login, de-duplicate the share-link and
color-identity logic behind DeckShareUtils and an injected querier, and
replace the silent tray/status-bar notices with always-visible dialogs.
- abstract_tab_deck_editor: explain that sharing requires a connection
instead of silently doing nothing when logged out
- tab_deck_storage: disable the share action on disconnect, reject
folder/deck mixes and the root folder with clear warnings, re-enable
Create on every entry/response so a dropped connection cannot leave
the button disabled
- tab_deck_storage_visual: same login gate for the context-menu entry,
visible success/error dialogs, and a symmetric in-flight guard
- getDeckColorIdentity now takes a CardDatabaseQuerier, dropping the
CardDatabaseManager singleton access and enabling unit tests
* [DeckShare] Fix share-link expiry build on the minimum-supported Qt
QTimeZone::UTC (the Initialization enum) only exists since Qt 6.7, so
Debian 12 and Ubuntu 24.04 (Qt 6.4) fail to compile the share-link expiry
handling in the share dialog and the two deck-storage tabs. Mirror the
existing games_model guard and fall back to Qt::UTC on older Qt.
* [DeckShare] Use the stable server client for the visual deck storage tab
* [DeckShare] Extract the share-creation response handling into DeckShareUtils
* [DeckShare] Drop includes left unused by the share-response extraction
* [DeckShare] Format share expiry with the locale-aware short format
* [DeckShare] Build share links with QUrl and QUrlQuery for percent-encoding
* [DeckShare] Replace the duplicate computeColorIdentity with the shared getDeckColorIdentity
* [DeckShare] Recover the share controls when the server never answers
* [DeckShare] Provide the full share hint in each plural form
* [DeckShare] Join the selected-count label with a non-translatable separator
* [DeckShare] Retranslate the share button tooltip with the storage widget
* [DeckShare] Forward retranslateUi to the visual deck storage widget
* [DeckShare] Let the share bar owners supply the hint text
* [DeckShare] End the share-related headers and sources with a trailing newline
* [DeckShare] Keep the settings include in the project include block
* [DeckShare] Include the network settings header used by the share timeout
* [DeckShare] Resolve the share theme icon through themePixmap
QPixmap("theme:icons/share") has no file extension, so ThemeManager::assetPath()
is bypassed and the pixmap is always null. Use themePixmap(QStringLiteral("icons/share"))
like every other toolbar action, so the .svg (and dark/light variants) resolves.
* [DeckShare] Keep the share selection consistent with the visible decks
Filtered-out previews are hidden but kept alive, so selectedFilePaths() counted
them in the share and the selection highlight. Only decks the user can see are
now shared, and a deck that stops matching the filters is deselectd as the deck
pass runs, keeping the %n count and the highlight in sync with the screen.
* [DeckShare] Abandon an in-flight tree share on cancel
Leaving share mode never stopped the timeout timer, and a late response still
ran shareFromTreeFinished, copying the link and announcing success for a share
the user backed out of. Stopping the timer and tracking the outstanding request
by sequence number means a stale reply (or a timed-out one) after cancel is
ignored, and cancelling + re-entering share mode can no longer confuse the two
requests.
* [DeckShare] Abandon an in-flight tile share on cancel
exitShareMode() left shareTimeoutTimer running and did not abandon the pending
Command_DeckShareCreate, so a timer pop or a late success still reported the
share after the user cancelled. Stop the timer and ignore stale responses via a
sequence number, mirroring the tree tab.
* [DeckShare] Wire the status-changed handler after shareBar exists
handleConnectionChanged() dereferences shareBar->isVisible(), but the connection
was set up before shareBar was constructed and shareBar had no in-class
initializer. On any status change delivered before construction the slot read an
indeterminate pointer. Seed the connection (and the initial share availability)
after shareBar exists and give shareBar a = nullptr initializer.
* [DeckShare] Explain why a blank deck cannot be shared
A blank deck exited the share flow silently. The menu only disables the entry
via setSaveStatus(), a different predicate, so the path is reachable (e.g. add a
card and remove it again). Mirror the not-logged-in branch with a short
information dialog.
* [DeckShare] Restore the banner-text doc comment
Re-add the doc block above refreshBannerCardText() that was removed as part of
the share-selection work; it documents the coupling to refreshBannerCardToolTip.
* [DeckShare] Resolve the stable server client in the deck editor gate
actShareDeck went through tabSupervisor->getClient(), which hands back a
LocalClient while an offline game is running. LocalClient never sets its status,
so a logged-in user could not share from the deck editor during a local game,
and got a misleading "You must be connected" message. Expose the supervisor's
stable remote client and use it for the gate and the dialog, matching the other
share tabs.
---------
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
|
||
|
|
a289d61765 |
[VDS] Decouple tag filter and fix reordered-chips crash (#7242)
* [VDS] Decouple tag filter and fix reordered-chips crash * [VDS] Address review: dead code, chip reparenting, filter signal and sort fast-path * [VDS] Address second round of review nits --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
073ec29c4d |
[Server] Add deck share links and public deck visibility (#7241)
* [Server] Add deck share links and public deck visibility * Address server review comments for deck share links * Document transaction teardown in deck share rollback paths * Address second round of deck share review comments --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
a5e94d8a4f |
[Build] Keep Windows installs free of build-tree artifacts (#7316)
* [Build] Keep Windows installs free of build-tree artifacts
Several Windows packaging gaps could leak Visual Studio CMake build
output into the installed application or the NSIS installer:
- The per-app DLL sweep used
${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${CMAKE_BUILD_TYPE}, which is
empty on multi-config generators, collapsing the recursive
DIRECTORY install into the whole build tree (containing *.dir,
*_autogen, .qt, .qsb, x64, ...). Point it at the real per-config
output with $<TARGET_FILE_DIR:...> and exclude build artifacts.
- install(FILES ${OPENSSL_INCLUDE_DIRS} ...) tried to install OpenSSL
include directories as files. CMake refuses this
("install FILES given directory"); it only slipped through CI
because the vcpkg OpenSSL config leaves the variable empty. Remove it;
fixup_bundle already ships the OpenSSL runtime DLLs.
- The NSIS uninstaller only deleted *.exe/*.dll and a few known files,
so build-tree leftovers survived an uninstall/reinstall cycle. Wipe
the whole directory tree instead.
- Add a Windows CI gate that lists the packaged installer with 7-Zip
and fails the build if any build-tree artifact path is found.
* Update .ci/compile.sh
Co-authored-by: tooomm <tooomm@users.noreply.github.com>
* [Build] Rework Windows installer artifact exclusions per review
---------
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: tooomm <tooomm@users.noreply.github.com>
|
||
|
|
b3c426cd43 |
Alphabetical ordering of Qt modules/packages (#7334)
* ordering
* Update docker-release.yml
* Revert "Update docker-release.yml"
This reverts commit
|
||
|
|
c97e1c4149 | Add back dir location (#7335) | ||
|
|
ec41c103d1 |
[CI] Utilize version resolution in install-qt-action + cache with full version key (#6993)
* Direct wildcard resolution in action + cache with version key * add back space * Delete .ci/resolve_latest_aqt_qt_version.sh * Disable Qt slimming and manual caching (use build-in fat caching) * cleanup * Re-add resolve_latest_aqt_qt_version.sh |
||
|
|
d9cd2d1750 |
[CI] Only save caches from master (#7186)
* Save caches only from master * Save cache only from master * Update desktop-build.yml |
||
|
|
1405952f1b |
Space quantity + unit (#7328)
Some checks failed
CodeQL / Analyze (cpp) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
Build Desktop / Configure (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 44 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Servatrice_Debian 12 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 26 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
Build Docker / Servatrice (arm) (push) Has been cancelled
Build Docker / Servatrice (x86) (push) Has been cancelled
Build Docker / Publish multi-platform Servatrice image (push) Has been cancelled
|
||
|
|
db2e159dca |
[Game] Allow judges to enter any game regardless of restrictions (#7315)
Some checks failed
CodeQL / Analyze (cpp) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
Build Desktop / Configure (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 44 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Servatrice_Debian 12 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 26 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
Build Docker / Servatrice (arm) (push) Has been cancelled
Build Docker / Servatrice (x86) (push) Has been cancelled
Build Docker / Publish multi-platform Servatrice image (push) Has been cancelled
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
cb19922e55 |
[DeckList] Extract deck metadata element readers (#7325)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
ec39ec611b |
[DeckList] Extract deck root seeking and body reading in XML load (#7324)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
662f1b79cc |
[DeckList] Extract board-zone pruning in node deletion (#7323)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
eb1e34c5a6 |
[DeckList] Extract recursive card traversal helpers (#7322)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
dce9efcaa3 |
[DeckList] Extract deck-hash encoding helpers (#7321)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
c45cb8ac32 |
[DeckList] Extract deck-node sort helpers (#7320)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
1a6d9d7749 |
[DeckList] Extract card parsing from zone XML reader (#7319)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |
||
|
|
faffb5a837 |
[DeckList] Extract sideboard-plan move parsing into a helper (#7318)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> |