* [Protocol] Add shuffle players option and player order command/event
Adds a 'Shuffle players at start' option to game creation plus a local
player id on the game state so clients can reconcile seat changes, and a
SET_PLAYER_ORDER command (1206) with a PLAYER_ORDER_CHANGED event (2023)
so the game host can set or randomize the seating order.
Took 6 minutes
* [Server] Shuffle and reorder player seats before a game starts
Auto-shuffles the seats at game start when enabled (now for games with
two or more players instead of three or more) and handles the new
SET_PLAYER_ORDER command so the game host or a moderator can reorder or
randomize the seats before the game starts. Re-seating re-keys the
player ids while keeping the per-user game/seat routing and persistent
player records in sync, then broadcasts the new order to everyone.
* [Client] Add shuffle players option and turn order dialog
Adds the 'Shuffle players at start' checkbox to game creation and a
host-only 'Set turn order...' action that opens a dialog to reorder or
randomize the seats before the game starts. Seating changes are shown in
the game's message log.
* [Client] Move the turn order dialog strings into retranslateUi
Follows the DlgInviteToGame convention so the dialog can be retranslated
instead of freezing its strings at construction time.
* [Server] Keep player ids stable across seat shuffles
* [Client] Lay out table and turn order dialog in seat order
* [Server] Send game-joined player list in seat order
* [Client] Reword the shuffle option to match its behaviour
* [Client] Address the seat-order review comments
- Give the game setup checkboxes a row of their own so they no longer
collide in the create-game dialog
- Rename DlgTurnOrder::randomize() to isRandomizeRequested()
- Document what the ids in GameState::seatOrder mean
- Keep "Set turn order..." in sync with the game: hidden for non-hosts
and once the game has started, re-shown when the game ends or hosting
moves to the local player (new GameState::hostChanged signal)
* [Client] Do not preview a randomized turn order
Randomizing is a request to the server, not a local shuffle. Shuffling the
list locally meant the dialog showed an order the server then discarded, and
let the host re-open it until a favourable order appeared. The dialog now
switches to a mode that says the server will randomize and toggles back to the
manual list on request.
* [Client] Let moderators set the turn order
The server accepts SET_PLAYER_ORDER from the host or a moderator, but the
client hid the action from everyone except the host, making the moderator
branch unreachable. Visibility and the handler now share canSetTurnOrder().
* [Server] Rely on the player order event instead of a full state resend
cmdSetPlayerOrder re-sent the whole game state after reseating, so every
client reloaded its deck view mid-lobby for no reason. The new seating reaches
clients through the Event_PlayerOrderChanged that reseatPlayers broadcasts.
* [Client] Show random turn order in the game selector
The restrictions column listed "open decklists" but not the new seating
option, so a joining player had no way to see that seats will be randomized
each game. Game info already shows the disabled checkbox.
---------
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
* [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>
* [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>
* [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>
* [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>
* [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>
* [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>
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>
* 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>
* [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>
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>
* [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>
* 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
* 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
* [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>
* [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>
QSettings with fallbacks enabled reads the global preferences domain on
macOS, so allKeys() returns system keys (AppleLanguages etc.) even on a
fresh install and the tutorial never shows for new Mac users.
The master merge replaced several transitive includes with forward
declarations; add back the complete types the tutorial code needs:
CounterState and PlayerEventHandler in tab_game.cpp, DeckList in
visual_deck_editor_widget.cpp, TabDeckEditorVisualTabWidget and
VisualDeckEditorWidget in visual_database_display_widget.cpp.
* [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>