From 9dfac77ba28ad9a91fb5df683fd671e7e9c6ee08 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 05:49:17 +0200 Subject: [PATCH 01/25] Update translation source strings (#6762) --- cockatrice/cockatrice_en@source.ts | 2447 ++++++++++++++++------------ oracle/oracle_en@source.ts | 4 +- 2 files changed, 1362 insertions(+), 1089 deletions(-) diff --git a/cockatrice/cockatrice_en@source.ts b/cockatrice/cockatrice_en@source.ts index 9a2f56f07..810b345df 100644 --- a/cockatrice/cockatrice_en@source.ts +++ b/cockatrice/cockatrice_en@source.ts @@ -25,12 +25,12 @@ AbstractDlgDeckTextEdit - + &Refresh - + Parse Set Name and Number (if available) @@ -38,60 +38,60 @@ AbstractTabDeckEditor - + Open in new tab - + Are you sure? - + The decklist has been modified. Do you want to save the changes? - - - - - - + + + + + + Error - + Could not open deck at %1 - + Could not save remote deck - - + + The deck could not be saved. Please check that the directory is writable and try again. - + Save deck - + The deck could not be saved. - + There are no cards in your deck to be exported @@ -133,190 +133,168 @@ Please check that the directory is writable and try again. AppearanceSettingsPage - + seconds - + Error - + Could not create themes directory at '%1'. - - Enabling this feature will disable the use of the Printing Selector. - -You will not be able to manage printing preferences on a per-deck basis, or see printings other people have selected for their decks. - -You will have to use the Set Manager, available through Card Database -> Manage Sets. - -Are you sure you would like to enable this feature? - - - - - Disabling this feature will enable the Printing Selector. - -You can now choose printings on a per-deck basis in the Deck Editor and configure which printing gets added to a deck by default by pinning it in the Printing Selector. - -You can also use the Set Manager to adjust custom sort order for printings in the Printing Selector (other sort orders like alphabetical or release date are available). - -Are you sure you would like to disable this feature? - - - - - Confirm Change - - - - + Theme settings - + Current theme: - + Open themes folder - + Home tab background source: - + Home tab background shuffle frequency: - + Disabled - + + Display card name of background in bottom right: + + + + Menu settings - + Show keyboard shortcuts in right-click menus - + Show game filter toolbar above list in room tab - + Card rendering - + Display card names on cards having a picture - + Auto-Rotate cards with sideways layout - + Override all card art with personal set preference (Pre-ProviderID change behavior) - + Bump sets that the deck contains cards from to the top in the printing selector - + Scale cards on mouse over - + Use rounded card corners - + Minimum overlap percentage of cards on the stack and in vertical hand - + Maximum initial height for card view window: - - + + rows - + Maximum expanded height for card view window: - + Card counters - + Counter %1 - + Hand layout - + Display hand horizontally (wastes space) - + Enable left justification - + Table grid layout - + Invert vertical coordinate - + Minimum player count for multi-column layout: - + Maximum font size for information displayed on cards: @@ -324,7 +302,12 @@ Are you sure you would like to disable this feature? ArchidektApiResponseDeckDisplayWidget - + + Back to results + + + + Open Deck in Deck Editor @@ -643,22 +626,22 @@ This is only saved for moderators and cannot be seen by the banned person. CardInfoPictureWidget - + View related cards - + Add card to deck - + Mainboard - + Sideboard @@ -694,124 +677,124 @@ This is only saved for moderators and cannot be seen by the banned person. CardMenu - + Re&veal to... - + &All players - + View related cards - + Token: - + All tokens - + &Select All - + S&elect Row - + S&elect Column - + &Play - + &Hide - + Play &Face Down - + &Tap / Untap Turn sideways or back again - - Toggle &normal untapping + + Skip &untapping - + T&urn Over Turn face up/face down - + &Peek at card face - + &Clone - + Attac&h to card... - + Unattac&h - + &Draw arrow... - + &Set annotation... - + Ca&rd counters - + &Add counter (%1) - + &Remove counter (%1) - + &Set counters (%1)... @@ -827,133 +810,133 @@ This is only saved for moderators and cannot be seen by the banned person. CardZoneLogic - + their hand nominative - + %1's hand nominative - + their library look at zone - + %1's library look at zone - + of their library top cards of zone, - + of %1's library top cards of zone - + their library reveal zone - + %1's library reveal zone - + their library shuffle - + %1's library shuffle - + their library nominative - - - %1's library - nominative - - + %1's library + nominative + + + + their graveyard nominative - + %1's graveyard nominative - + their exile nominative - + %1's exile nominative - - - their sideboard - look at zone - - - - - %1's sideboard - look at zone - - their sideboard - nominative + look at zone %1's sideboard + look at zone + + + + + their sideboard nominative - + + %1's sideboard + nominative + + + + their custom zone '%1' nominative - + %1's custom zone '%2' nominative @@ -1015,7 +998,7 @@ This is only saved for moderators and cannot be seen by the banned person. DeckEditorCardDatabaseDockWidget - + Card Database @@ -1023,7 +1006,7 @@ This is only saved for moderators and cannot be seen by the banned person. DeckEditorCardInfoDockWidget - + Card Info @@ -1046,32 +1029,32 @@ This is only saved for moderators and cannot be seen by the banned person. - + Select Printing - + Show on EDHRec (Commander) - + Show on EDHRec (Card) - + Show Related cards - + Add card to &maindeck - + Add card to &sideboard @@ -1079,32 +1062,32 @@ This is only saved for moderators and cannot be seen by the banned person. DeckEditorDeckDockWidget - + Loading Database... - + Banner Card - + Main Type - + Mana Cost - + Colors - + Select Printing @@ -1177,17 +1160,17 @@ This is only saved for moderators and cannot be seen by the banned person. DeckEditorFilterDockWidget - + Filters - + &Clear all filters - + Delete selected @@ -1310,7 +1293,7 @@ This is only saved for moderators and cannot be seen by the banned person. DeckEditorPrintingSelectorDockWidget - + Printing Selector @@ -1318,166 +1301,166 @@ This is only saved for moderators and cannot be seen by the banned person. DeckEditorSettingsPage - - + + Update Spoilers - - + + Success - + Download URLs have been reset. - + Downloaded card pictures have been reset. - + Error - + One or more downloaded card pictures could not be cleared. - + Add URL - - + + URL: - - + + Edit URL - + Network Cache Size: - + Redirect Cache TTL: - + How long cached redirects for urls are valid for. - + Picture Cache Size: - + Add New URL - + Remove URL - + Day(s) - + Updating... - + Choose path - + URL Download Priority - + Spoilers - + Download Spoilers Automatically - + Spoiler Location: - + Last Change - + Spoilers download automatically on launch - + Press the button to manually update without relaunching - + Do not close settings until manual update is complete - + Download card pictures on the fly - + How to add a custom URL - + Delete Downloaded Images - + Reset Download URLs - + On-disk cache for downloaded pictures - + In-memory cache for pictures not currently on screen @@ -1485,32 +1468,32 @@ This is only saved for moderators and cannot be seen by the banned person. DeckListHistoryManagerWidget - + Undo - + Redo - + Undo/Redo history - + Click on an entry to revert to that point in the history. - + [redo] - + [undo] @@ -1518,27 +1501,27 @@ This is only saved for moderators and cannot be seen by the banned person. DeckListModel - + Count - + Set - + Number - + Provider ID - + Card @@ -1546,12 +1529,12 @@ This is only saved for moderators and cannot be seen by the banned person. DeckLoader - + Common deck formats (%1) - + All files (*.*) @@ -1648,94 +1631,94 @@ This is only saved for moderators and cannot be seen by the banned person. DeckPreviewWidget - + Banner Card - + Open in deck editor - + Edit Tags - + Rename Deck - + Save Deck to Clipboard - + Annotated - + Annotated (No set info) - + Not Annotated - + Not Annotated (No set info) - + Rename File - + Delete File - + Set Banner Card - - + + New name: - - + + Error - + Rename failed - + Delete file - + Are you sure you want to delete the selected file? - + Delete failed @@ -1768,32 +1751,32 @@ This is only saved for moderators and cannot be seen by the banned person. - + Added (%1): %2 (%3) %4 - + Moved to %1 1 × "%2" (%3) - + Removed "%1" (all copies) - + %1 1 × "%2" (%3) - + Added - + Removed @@ -1860,29 +1843,29 @@ This is only saved for moderators and cannot be seen by the banned person. - - + + Error - + The selected file could not be loaded. - + Deck is greater than maximum file size. - + Are you sure you want to force start? This will kick all non-ready players from the game. - + Cockatrice @@ -2374,17 +2357,17 @@ To remove your current avatar, confirm without choosing a new image. DlgEditDeckInClipboard - + Edit deck in clipboard - + Error - + Invalid deck list. @@ -2884,17 +2867,17 @@ Make sure to enable the 'Token' set in the "Manage sets" dia DlgLoadDeckFromClipboard - + Load deck from clipboard - + Error - + Invalid deck list. @@ -2902,43 +2885,43 @@ Make sure to enable the 'Token' set in the "Manage sets" dia DlgLoadDeckFromWebsite - + Paste a link to a decklist site here to import it. (Archidekt, Deckstats, Moxfield, and TappedOut are supported.) - - - - - + + + + + Load Deck from Website - + No parser available for this deck provider. (Archidekt, Deckstats, Moxfield, and TappedOut are supported.) - + Network error: %1 - + Received empty deck data. - + Failed to parse deck data: %1 - + The provided URL is not recognized as a valid deck URL. Valid deck URLs look like this: @@ -2957,40 +2940,73 @@ https://tappedout.net/mtg-decks/your-deck-name/ + + DlgLocalGameOptions + + + Players: + + + + + General + + + + + Starting life total: + + + + + Game setup options + + + + + Remember settings + + + + + Local game options + + + DlgMoveTopCardsUntil - + Card name (or search expressions): - + Number of hits: - + Auto play hits - + Put top cards on stack until... - + No cards matching the search expression exists in the card database. Proceed anyways? - + Cockatrice - + Invalid filter @@ -3151,12 +3167,12 @@ Your email will be used to verify your account. DlgSettings - + Unknown Error loading card database - + Your card database is invalid. Cockatrice may not function correctly with an invalid database @@ -3167,7 +3183,7 @@ Would you like to change your database location setting? - + Your card database version is too old. This can cause problems loading card information or images @@ -3178,7 +3194,7 @@ Would you like to change your database location setting? - + Your card database did not finish loading Please file a ticket at https://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached @@ -3187,21 +3203,21 @@ Would you like to change your database location setting? - + File Error loading your card database. Would you like to change your database location setting? - + Your card database was loaded but contains no cards. Would you like to change your database location setting? - + Unknown card database load status Please file a ticket at https://github.com/Cockatrice/Cockatrice/issues @@ -3210,59 +3226,59 @@ Would you like to change your database location setting? - - - + + + Error - + The path to your deck directory is invalid. Would you like to go back and set the correct path? - + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? - + Settings - + General - + Appearance - + User Interface - + Card Sources - + Chat - + Sound - + Shortcuts @@ -3575,67 +3591,67 @@ You may have to manually download the new version. DrawProbabilityWidget - + Draw Probability - + Probability of drawing - + Card Name - + Type - + Subtype - + Mana Value - + At least - + Exactly - + card(s) having drawn at least - + cards - + Category - + Qty - + Odds (%) @@ -4089,143 +4105,143 @@ You may have to manually download the new version. GeneralSettingsPage - + Reset all paths - + All paths have been reset - - - - - - - + + + + + + + Choose path - + Personal settings - + Language: - + Paths (editing disabled in portable mode) - + Paths - + How to help with translations - + Decks directory: - + Filters directory: - + Replays directory: - + Pictures directory: - + Card database: - + Custom database directory: - + Token database: - + Update channel - + Check for client updates on startup - + Check for card database updates on startup - + Don't check - + Prompt for update - + Always update in the background - + Check for card database updates every - + days - + Notify if a feature supported by the server is missing in my client - + Automatically run Oracle when running a new version of Cockatrice - + Show tips on startup - + Last update check on %1 (%2 days ago) @@ -4233,47 +4249,47 @@ You may have to manually download the new version. GraveyardMenu - + &Graveyard - + &View graveyard - + &Move graveyard to... - + &Top of library - + &Bottom of library - + &All players - + &Hand - + &Exile - + Reveal random card to... @@ -4281,88 +4297,88 @@ You may have to manually download the new version. HandMenu - + &Hand - + &View hand - + Sort hand by... - + Name - + Type - + Mana Value - + Take &mulligan (Choose hand size) - + Take mulligan (Same hand size) - + Take mulligan (Hand size - 1) - + &Move hand to... - + &Top of library - + &Bottom of library - + &Graveyard - + &Exile - + &Reveal hand to... - - + + All players - + Reveal r&andom card to... @@ -4370,52 +4386,52 @@ You may have to manually download the new version. HomeWidget - + Create New Deck - + Browse Decks - + Browse Card Database - + Browse EDHRec - + Browse Archidekt - + View Replays - + Quit - + Connecting... - + Connect - + Play @@ -4423,193 +4439,213 @@ You may have to manually download the new version. LibraryMenu - + &Library - + &View library - + View &top cards of library... - + View bottom cards of library... - + Reveal &library to... - + Lend library to... - + Reveal &top cards to... - + &Top of library... - + &Bottom of library... - + &Always reveal top card - + &Always look at top card - + &Open deck in deck editor - + &Draw card - + D&raw cards... - + &Undo last draw - + Shuffle - + &Play top card - + Play top card &face down - + Put top card on &bottom - + Move top card to grave&yard - + Move top card to e&xile - + Move top cards to &graveyard... - + + Move top cards to graveyard face down... + + + + Move top cards to &exile... - + + Move top cards to exile face down... + + + + Put top cards on stack &until... - + Shuffle top cards... - + &Draw bottom card - + D&raw bottom cards... - + &Play bottom card - + Play bottom card &face down - + Move bottom card to grave&yard - + Move bottom card to e&xile - + Move bottom cards to &graveyard... - + + Move bottom cards to graveyard face down... + + + + Move bottom cards to &exile... - + + Move bottom cards to exile face down... + + + + Put bottom card on &top - + Shuffle bottom cards... - - + + &All players - + Reveal top cards of library - + Number of cards: (max. %1) @@ -4703,18 +4739,8 @@ Will now login. - - Number of players - - - - - Please enter the number of players. - - - - - + + Player %1 @@ -4817,8 +4843,8 @@ Will now login. - - + + Error @@ -5219,63 +5245,63 @@ Local version is %1, remote version is %2. - + New Version - + Congratulations on updating to Cockatrice %1! Oracle will now launch to update your card database. - + Cockatrice installed - + Congratulations on installing Cockatrice %1! Oracle will now launch to install the initial card database. - + Card database - + Cockatrice is unable to load the card database. Do you want to update your card database now? If unsure or first time user, choose "Yes" - - + + Yes - - + + No - + Open settings - + New sets found - + %n new set(s) found in the card database Set code(s): %1 Do you want to enable it/them? @@ -5285,81 +5311,81 @@ Do you want to enable it/them? - + View sets - + Welcome - + Hi! It seems like you're running this version of Cockatrice for the first time. All the sets in the card database have been enabled. Read more about changing the set order or disabling specific sets and consequent effects in the "Manage Sets" dialog. - - + + Information - + A card database update is already running. - + Unable to run the card database updater: - + Card database update running. - + Failed to start. The file might be missing, or permissions might be incorrect. - + The process crashed some time after starting successfully. - + Timed out. The process took too long to respond. The last waitFor...() function timed out. - + An error occurred when attempting to write to the process. For example, the process may not be running, or it may have closed its input channel. - + An error occurred when attempting to read from the process. For example, the process may not be running. - + Unknown error occurred. - + The card database updater exited with an error: %1 - + This server supports additional features that your client doesn't have. This is most likely not a problem, but this message might mean there is a new version of Cockatrice available or this server is running a custom or pre-release version. @@ -5367,54 +5393,54 @@ To update your client, go to Help -> Check for Updates. - - - - - + + + + + Load sets/cards - + Selected file cannot be found. - + You can only import XML databases at this time. - + The new sets/cards have been added successfully. Cockatrice will now reload the card database. - + Sets/cards failed to import. - - - + + + Reset Password - + Your password has been reset successfully, you can now log in using the new credentials. - + Failed to reset user account password, please contact the server operator to reset your password. - + Activation request received, please check your email for an activation token. @@ -5465,7 +5491,7 @@ Cockatrice will now reload the card database. ManaBaseWidget - + Mana Base @@ -5619,277 +5645,297 @@ Cockatrice will now reload the card database. MessageLogWidget - + from play - + from their graveyard - + from exile - + from their hand - + the top card of %1's library - + the top card of their library - + from the top of %1's library - + from the top of their library - + the bottom card of %1's library - + the bottom card of their library - + from the bottom of %1's library - + from the bottom of their library - + from %1's library - + from their library - + from sideboard - + from the stack - + from custom zone '%1' - + %1 is now keeping the top card %2 revealed. - + %1 is not revealing the top card %2 any longer. - + %1 can now look at top card %2 at any time. - + %1 no longer can look at top card %2 at any time. - + %1 attaches %2 to %3's %4. - + %1 has conceded the game. - + %1 has unconceded the game. - + %1 has restored connection to the game. - + %1 has lost connection to the game. - + %1 points from their %2 to themselves. - + %1 points from their %2 to %3. - + %1 points from %2's %3 to themselves. - + %1 points from %2's %3 to %4. - + %1 points from their %2 to their %3. - + %1 points from their %2 to %3's %4. - + %1 points from %2's %3 to their own %4. - + %1 points from %2's %3 to %4's %5. - + %1 creates a face down token. - + %1 creates token: %2%3. - + %1 has loaded a deck (%2). - + %1 has loaded a deck with %2 sideboard cards (%3). - + %1 destroys %2. - + a card - + %1 gives %2 control over %3. - + %1 puts %2 into play%3 face down. - + %1 puts %2 into play%3. - + + %1 puts %2%3 into their graveyard face down. + + + + %1 puts %2%3 into their graveyard. + + + %1 exiles %2%3 face down. + + %1 exiles %2%3. - + %1 moves %2%3 to their hand. - + %1 puts %2%3 into their library. - + %1 puts %2%3 onto the bottom of their library. - + %1 puts %2%3 on top of their library. - + %1 puts %2%3 into their library %4 cards from the top. - + %1 moves %2%3 to sideboard. - + + %1 plays %2%3 face down. + + + + %1 plays %2%3. - + + %1 moves %2%3 to custom zone '%4' face down. + + + + %1 moves %2%3 to custom zone '%4'. - + %1 tries to draw from an empty library - + %1 draws %2 card(s). @@ -5897,12 +5943,12 @@ Cockatrice will now reload the card database. - + %1 is looking at %2. - + %1 is looking at the %4 %3 card(s) %2. top card for singular, top %3 cards for plural @@ -5911,72 +5957,72 @@ Cockatrice will now reload the card database. - + bottom - + top - + %1 turns %2 face-down. - + %1 turns %2 face-up. - + The game has been closed. - + The game has started. - + You are flooding the game. Please wait a couple of seconds. - + %1 has joined the game. - + %1 is now watching the game. - + You have been kicked out of the game. - + %1 has left the game (%2). - + %1 is not watching the game any more (%2). - + %1 is not ready to start the game any more. - + %1 shuffles their deck and draws a new hand of %2 card(s). @@ -5984,28 +6030,28 @@ Cockatrice will now reload the card database. - + %1 shuffles their deck and draws a new hand. - + You are watching a replay of game #%1. - + %1 is ready to start the game. - + cards an unknown amount of cards - + %1 card(s) a card for singular, %1 cards for plural @@ -6014,107 +6060,107 @@ Cockatrice will now reload the card database. - + %1 lends %2 to %3. - + %1 reveals %2 to %3. - + %1 reveals %2. - + %1 randomly reveals %2%3 to %4. - + %1 randomly reveals %2%3. - + %1 peeks at face down card #%2. - + %1 peeks at face down card #%2: %3. - + %1 reveals %2%3 to %4. - + %1 reveals %2%3. - + %1 reversed turn order, now it's %2. - + reversed - + normal - + Heads - + Tails - + %1 flipped a coin. It landed as %2. - + %1 rolls a %2 with a %3-sided die. - + %1 flips %2 coins. There are %3 heads and %4 tails. - + %1 rolls a %2-sided dice %3 times: %4. - + %1's turn. - + %1 sets annotation of %2 to %3. - + %1 places %2 "%3" counter(s) on %4 (now %5). @@ -6122,7 +6168,7 @@ Cockatrice will now reload the card database. - + %1 removes %2 "%3" counter(s) from %4 (now %5). @@ -6130,97 +6176,97 @@ Cockatrice will now reload the card database. - + %1 sets counter %2 to %3 (%4%5). - + %1 sets %2 to not untap normally. - + %1 sets %2 to untap normally. - + %1 removes the PT of %2. - + %1 changes the PT of %2 from nothing to %4. - + %1 changes the PT of %2 from %3 to %4. - + %1 has locked their sideboard. - + %1 has unlocked their sideboard. - + %1 taps their permanents. - + %1 untaps their permanents. - + %1 taps %2. - + %1 untaps %2. - + %1 shuffles %2. - + %1 shuffles the bottom %3 cards of %2. - + %1 shuffles the top %3 cards of %2. - + %1 shuffles cards %3 - %4 of %2. - + %1 unattaches %2. - + %1 undoes their last draw. - + %1 undoes their last draw (%2). @@ -6228,110 +6274,110 @@ Cockatrice will now reload the card database. MessagesSettingsPage - + Word1 Word2 Word3 - + Add New Message - + Edit Message - + Remove Message - + Add message - - + + Message: - + Edit message - + Chat settings - + Custom alert words - + Enable chat mentions - + Enable mention completer - + In-game message macros - + How to use in-game message macros - + Ignore chat room messages sent by unregistered users - + Ignore private messages sent by unregistered users - - + + Invert text color - + Enable desktop notifications for private messages - + Enable desktop notification for mentions - + Enable room message history on join - - + + (Color is hexadecimal) - + Separate words with a space, alphanumeric characters only @@ -6344,32 +6390,37 @@ Cockatrice will now reload the card database. - + &Top of library in random order - + X cards from the top of library... - + &Bottom of library in random order - + + T&able + + + + &Hand - + &Graveyard - + &Exile @@ -6511,57 +6562,57 @@ Cockatrice will now reload the card database. PhasesToolbar - + Untap step - + Upkeep step - + Draw step - + First main phase - + Beginning of combat step - + Declare attackers step - + Declare blockers step - + Combat damage step - + End of combat step - + Second main phase - + End of turn step @@ -6578,134 +6629,138 @@ Cockatrice will now reload the card database. PlayerActions - + View top cards of library - - - - - - - - - - - + + + + + + + + + Number of cards: (max. %1) - + View bottom cards of library - + Shuffle top cards of library - + Shuffle bottom cards of library - + Draw hand - + 0 and lower are in comparison to current hand size - + Draw cards + + + + + + grave + + - Move top cards to grave + + + + exile - - Move top cards to exile + + Move top cards to %1 - - Move bottom cards to grave + + Move bottom cards to %1 - - Move bottom cards to exile - - - - + Draw bottom cards - - + + C&reate another %1 token - + Create tokens - - + + Number: - + Place card X cards from top of library - + Which position should this card be placed: - + (max. %1) - + Change power/toughness - + Change stats to: - + Set annotation - + Please enter the new annotation: - + Set counters @@ -6713,48 +6768,65 @@ Cockatrice will now reload the card database. PlayerMenu - + Player "%1" - + &Counters + + + PrintingDisabledInfoWidget - - S&ay + + The Printing Selector is disabled because you have currently enabled the setting to override all selected printings with personal set preferences. + +This setting means you'll only see the default printing for each card, instead of being able to select a printing, and will not see the printings other people have selected. + + + + + + + Enable printings again PrintingSelector - + Display Navigation Buttons + + + Printing Selector + + PrintingSelectorCardOverlayWidget - + Preference - + Pin Printing - + Unpin Printing - + Show Related cards @@ -6803,17 +6875,25 @@ Cockatrice will now reload the card database. - - + + Descending - + Ascending + + PrintingSelectorPlaceholderWidget + + + Select a card to view its available printings + + + PtMenu @@ -6952,6 +7032,33 @@ Cockatrice will now reload the card database. A .cod version of this deck already exists. Overwrite it? + + + Enabling this feature will disable the use of the Printing Selector. + +You will not be able to manage printing preferences on a per-deck basis, or see printings other people have selected for their decks. + +You will have to use the Set Manager, available through Card Database -> Manage Sets. + +Are you sure you would like to enable this feature? + + + + + Disabling this feature will enable the Printing Selector. + +You can now choose printings on a per-deck basis in the Deck Editor and configure which printing gets added to a deck by default by pinning it in the Printing Selector. + +You can also use the Set Manager to adjust custom sort order for printings in the Printing Selector (other sort orders like alphabetical or release date are available). + +Are you sure you would like to disable this feature? + + + + + Confirm Change + + QPlatformTheme @@ -7100,37 +7207,37 @@ Cockatrice will now reload the card database. RfgMenu - + &Exile - + &View exile - + &Move exile to... - + &Top of library - + &Bottom of library - + &Hand - + &Graveyard @@ -7173,6 +7280,14 @@ Cockatrice will now reload the card database. + + SayMenu + + + S&ay + + + SequenceEdit @@ -7237,53 +7352,53 @@ Cockatrice will now reload the card database. ShortcutSettingsPage - - + + Restore all default shortcuts - + Do you really want to restore all default shortcuts? - + Clear all default shortcuts - + Do you really want to clear all shortcuts? - + Section: - + Action: - + Shortcut: - + How to set custom shortcuts - + Clear all shortcuts - + Search by shortcut name @@ -7350,27 +7465,27 @@ Please check your shortcut settings! SoundSettingsPage - + Enable &sounds - + Current sounds theme: - + Test system sound engine - + Sound settings - + Master volume @@ -7586,59 +7701,123 @@ Please check your shortcut settings! TabArchidekt - - + + + Desc. - - Asc. + + + AND - - - Any Bracket + + + Require ALL selected colors - - Deck name contains... + + + Deck name... - - Owner name contains... + + + Owner... + + + + + + Packages + + + + + + Advanced Filters - Disabled + Bracket: - + + + Any + + + + + + Contains card... + + + + + + Commander... + + + + + + Tag... + + + + + + Deck Size + + + + + Cards: + + + + + + Asc. + + + + + Sort by: + + + + + Filter by: + + + + + Display Settings + + + + + Search - + + Formats - - Min. # of Cards: - - - - - Page: - - - - + Archidekt: @@ -7646,60 +7825,52 @@ Please check your shortcut settings! TabDeckEditor - + Card Info - + Deck - + Filters - + &View - + Card Database - + Printing - - - - - + Visible - - - - - + Floating - + Reset layout - + Deck: %1 @@ -7707,61 +7878,55 @@ Please check your shortcut settings! TabDeckEditorVisual - + Visual Deck: %1 - + &Visual Deck Editor - - + + Card Info - - + + Deck - - + + Filters - + &View - + Printing - - - - + Visible - - - - + Floating - + Reset layout @@ -7826,7 +7991,7 @@ Please check your shortcut settings! - + New folder @@ -7907,18 +8072,18 @@ Please enter a name: - + Delete remote decks - + Are you sure you want to delete the selected decks? - + Name of new folder: @@ -7936,12 +8101,12 @@ Please enter a name: - + Error - + Could not open deck at %1 @@ -7990,197 +8155,191 @@ Please enter a name: TabGame - - - + + + Replay - - + + Game - - + + Player List - - + + Card Info - - + + Messages - - + + Replay Timeline - + &Phases - + &Game - + Next &phase - + Next phase with &action - + Next &turn - + Reverse turn order - + &Remove all local arrows - + Rotate View Cl&ockwise - + Rotate View Co&unterclockwise - + Game &information - + Un&concede - - - + + + &Concede - + &Leave game - + C&lose replay - + &Focus Chat - + &Say: - + Selected cards - + &View - - - - + Visible - - - - + Floating - + Reset layout - + Concede - + Are you sure you want to concede this game? - + Unconcede - + You have already conceded. Do you want to return to this game? - + Leave game - + Are you sure you want to leave this game? - + A player has joined game #%1 - + %1 has joined the game - + You have been kicked out of the game. @@ -9280,142 +9439,152 @@ Please refrain from engaging in this activity or further actions may be taken ag UserInterfaceSettingsPage - + General interface settings - + &Double-click cards to play them (instead of single-click) - + &Clicking plays all selected cards (instead of just the clicked card) - + &Play all nonlands onto the stack (not the battlefield) by default - + Do not delete &arrows inside of subphases - + Close card view window when last card is removed - + Auto focus search bar when card view window is opened - + Annotate card text on tokens - - - Use tear-off menus, allowing right click menus to persist on screen - - - - - Notifications settings - - - - - Enable notifications in taskbar - - - - - Notify in the taskbar for game events while you are spectating - - - - - Notify in the taskbar when users in your buddy list connect - - - - - Animation settings - - - - - &Tap/untap animation - - - - - Deck editor/storage settings - - - - - Open deck in new tab by default - - - Use visual deck storage in game lobby + Show selection counter during drag selection - Use selection animation for Visual Deck Storage + Show total selection counter + + + + + Use tear-off menus, allowing right click menus to persist on screen - When adding a tag in the visual deck storage to a .txt deck: + Notifications settings + + + + + Enable notifications in taskbar - do nothing + Notify in the taskbar for game events while you are spectating + + + + + Notify in the taskbar when users in your buddy list connect - ask to convert to .cod + Animation settings + + + + + &Tap/untap animation - always convert to .cod + Deck editor/storage settings - Default deck editor type + Open deck in new tab by default - Classic Deck Editor + Use visual deck storage in game lobby - Visual Deck Editor - - - - - Replay settings + Use selection animation for Visual Deck Storage + When adding a tag in the visual deck storage to a .txt deck: + + + + + do nothing + + + + + ask to convert to .cod + + + + + always convert to .cod + + + + + Default deck editor type + + + + + Classic Deck Editor + + + + + Visual Deck Editor + + + + + Replay settings + + + + Buffer time for backwards skip via shortcut: @@ -9479,23 +9648,24 @@ Please refrain from engaging in this activity or further actions may be taken ag VisualDatabaseDisplayColorFilterWidget - - Mode: Exact Match + + Exact match + + + + + Includes - Mode: Includes + Include / Exclude + Mode: Includes - - Mode: Include/Exclude - - - - - Filter mode (AND/OR/NOT conjunctions of filters) + + How selected and unselected colors are combined in the filter @@ -9522,25 +9692,108 @@ Please refrain from engaging in this activity or further actions may be taken ag + + VisualDatabaseDisplayFilterToolbarWidget + + + Sort by + + + + + Filter by + + + + + Save and load filters + + + + + Filter by exact card name + + + + + Filter by card main-type + + + + + Filter by card sub-type + + + + + Filter by set + + + + + Filter by format legality + + + + + Save/Load + + + + + Name + + + + + Main Type + + + + + Sub Type + + + + + Sets + + + + + Formats + + + VisualDatabaseDisplayFormatLegalityFilterWidget - + + Show formats with at least: + + + + + cards + + + + Do not display formats with less than this amount of cards in the database - + Filter mode (AND/OR/NOT conjunctions of filters) - + Mode: Exact Match - + Mode: Includes @@ -9548,22 +9801,32 @@ Please refrain from engaging in this activity or further actions may be taken ag VisualDatabaseDisplayMainTypeFilterWidget - + + Show main types with at least: + + + + + cards + + + + Do not display card main-types with less than this amount of cards in the database - + Filter mode (AND/OR/NOT conjunctions of filters) - + Mode: Exact Match - + Mode: Includes @@ -9599,7 +9862,7 @@ Please refrain from engaging in this activity or further actions may be taken ag VisualDatabaseDisplayRecentSetFilterSettingsWidget - + Filter to most recent sets @@ -9607,19 +9870,19 @@ Please refrain from engaging in this activity or further actions may be taken ag VisualDatabaseDisplaySetFilterWidget - + Search sets... - - + + Mode: Exact Match - - + + Mode: Includes @@ -9627,27 +9890,37 @@ Please refrain from engaging in this activity or further actions may be taken ag VisualDatabaseDisplaySubTypeFilterWidget - + Search subtypes... - + + Show sub types with at least: + + + + + cards + + + + Do not display card sub-types with less than this amount of cards in the database - + Filter mode (AND/OR/NOT conjunctions of filters) - + Mode: Exact Match - + Mode: Includes @@ -9661,52 +9934,22 @@ Please refrain from engaging in this activity or further actions may be taken ag - + Visual - + Loading database ... - + Clear all filters - - Sort by: - - - - - Filter by: - - - - - Save and load filters - - - - - Filter by exact card name - - - - - Filter by card sub-type - - - - - Filter by set - - - - + Table @@ -9714,56 +9957,64 @@ Please refrain from engaging in this activity or further actions may be taken ag VisualDeckDisplayOptionsWidget - + Group by: - + Change how cards are divided into categories/groups. - + Sort by: - + Click and drag to change the sort order within the groups - + Configure how cards are sorted within their groups - - + + Toggle Layout: Overlap - + Change how cards are displayed within zones (i.e. overlapped or fully visible.) - + Toggle Layout: Flat + + VisualDeckEditorPlaceholderWidget + + + Add cards using the search bar or database tab to have them appear here + + + VisualDeckEditorSampleHandWidget - + Draw a new sample hand - + Sample hand size @@ -9771,17 +10022,17 @@ Please refrain from engaging in this activity or further actions may be taken ag VisualDeckEditorWidget - + Type a card name here for suggestions from the database... - + Quick search and add card - + Search for closest match in the database (with auto-suggestions) and add preferred printing to the deck on pressing enter @@ -9797,47 +10048,52 @@ Please refrain from engaging in this activity or further actions may be taken ag VisualDeckStorageQuickSettingsWidget - + Show Folders - + Show Tag Filter - + + Show Color Identity + + + + Show Tags On Deck Previews - + Show Banner Card Selection Option - + Draw unused Color Identities - + Unused Color Identities Opacity - + Deck tooltip: - + None - + Filepath @@ -9938,133 +10194,133 @@ Please refrain from engaging in this activity or further actions may be taken ag WndSets - + Move selected set to the top - + Move selected set up - + Move selected set down - + Move selected set to the bottom - + Search by set name, code, or type - + Default order - + Restore original art priority order - + Enable all sets - + Disable all sets - + Enable selected set(s) - + Disable selected set(s) - + Deck Editor - + Use CTRL+A to select all sets in the view. - + Only cards in enabled sets will appear in the card list of the deck editor. - + Image priority is decided in the following order: - + first the CUSTOM Folder (%1), then the Enabled Sets in this dialog (Top to Bottom) %1 is a link to the wiki - + Include cards rebalanced for Alchemy [requires restart] - + Card Art - + How to use custom card art - + Hints - + Note - + Sorting by column allows you to find a set while not changing set priority. - + To enable ordering again, click the column header until this message disappears. - + Use the current sorting as the set priority instead - + Sorts the set priority using the same column - + Manage sets @@ -10072,72 +10328,72 @@ Please refrain from engaging in this activity or further actions may be taken ag ZoneViewWidget - + Search by card name (or search expressions) - + Ungrouped - + Group by Type - + Group by Mana Value - + Group by Color - + Unsorted - + Sort by Name - + Sort by Type - + Sort by Mana Cost - + Sort by Colors - + Sort by P/T - + Sort by Set - + shuffle when closing - + pile view @@ -10172,7 +10428,7 @@ Please refrain from engaging in this activity or further actions may be taken ag - + Deck Editor @@ -10253,7 +10509,7 @@ Please refrain from engaging in this activity or further actions may be taken ag - + Replays @@ -10405,7 +10661,7 @@ Please refrain from engaging in this activity or further actions may be taken ag - + Reset Layout @@ -10826,7 +11082,8 @@ Please refrain from engaging in this activity or further actions may be taken ag - Toggle Untap + Toggle Skip Untapping + Toggle Untap @@ -10846,98 +11103,102 @@ Please refrain from engaging in this activity or further actions may be taken ag - Attach Card... + Play Card, Face Down - Unattach Card + Attach Card... - Clone Card + Unattach Card - Create Token... + Clone Card - Create All Related Tokens + Create Token... - Create Another Token + Create All Related Tokens - Set Annotation... + Create Another Token - Select All Cards in Zone + Set Annotation... - Select All Cards in Row + Select All Cards in Zone - Select All Cards in Column + Select All Cards in Row - Reveal Selected Cards to All Players + Select All Cards in Column - - Bottom of Library + Reveal Selected Cards to All Players - - - - Exile + + Bottom of Library + + + + Exile + + + + - + Graveyard - + Hand - - + + Top of Library - - + Battlefield, Face Down @@ -10973,234 +11234,246 @@ Please refrain from engaging in this activity or further actions may be taken ag - + Stack - + Graveyard (Multiple) - - + + + Graveyard (Multiple), Face Down + + + + + Exile (Multiple) - + + + Exile (Multiple), Face Down + + + + Stack Until Found - + Draw Bottom Card - + Draw Multiple Cards from Bottom... - + Draw Arrow... - + Remove Local Arrows - + Leave Game - + Concede - + Roll Dice... - + Shuffle Library - + Shuffle Top Cards of Library - + Shuffle Bottom Cards of Library - + Mulligan - + Mulligan (Same hand size) - + Mulligan (Hand size - 1) - + Draw a Card - + Draw Multiple Cards... - + Undo Draw - + Always Reveal Top Card - + Always Look At Top Card - + Sort Hand by Name - + Sort Hand by Type - + Sort Hand by Mana Value - + Reveal Hand to All Players - + Reveal Random Card to All Players - + Rotate View Clockwise - + Rotate View Counterclockwise - + Unfocus Text Box - + Focus Chat - + Clear Chat - + Refresh - + Skip Forward - + Skip Backward - + Skip Forward by a lot - + Skip Backward by a lot - + Play/Pause - + Toggle Fast Forward - + Home - + Visual Deck Storage - + Deck Storage - + Server - + Account - + Administration - + Logs diff --git a/oracle/oracle_en@source.ts b/oracle/oracle_en@source.ts index 53ad27b4d..943b44a97 100644 --- a/oracle/oracle_en@source.ts +++ b/oracle/oracle_en@source.ts @@ -278,7 +278,7 @@ OracleImporter - + Dummy set containing tokens @@ -286,7 +286,7 @@ OracleWizard - + Oracle Importer From ba786a0289a8dbfcaf833d1452c018ff1575956a Mon Sep 17 00:00:00 2001 From: tooomm Date: Wed, 1 Apr 2026 14:54:27 +0200 Subject: [PATCH 02/25] Update dependabot.yml (#6756) --- .github/dependabot.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index fc25af67f..88bed3663 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,6 +9,9 @@ updates: - package-ecosystem: "gitsubmodule" # Look for `.gitmodules` in the `root` directory directory: "/" + ignore: + # Ignore updates for vcpkg (Bump to latest tag not working (no SemVer used) & macOS Intel triplet broken with newer releases) + - dependency-name: "vcpkg" # Check for updates once a month schedule: interval: "monthly" From 690a00aa6c0779a8ffe67323bf21f6c793689920 Mon Sep 17 00:00:00 2001 From: SlightlyCircuitous <71394296+SlightlyCircuitous@users.noreply.github.com> Date: Fri, 3 Apr 2026 14:40:42 -0400 Subject: [PATCH 03/25] Add Ubuntu 26.04 "Resolute Racoon" build (#6766) * Create 26.04 Dockerfile * Update desktop-build.yml * Update release_template.md * Add ca-certificates package to build --- .ci/Ubuntu26.04/Dockerfile | 29 +++++++++++++++++++++++++++++ .ci/release_template.md | 1 + .github/workflows/desktop-build.yml | 5 +++++ 3 files changed, 35 insertions(+) create mode 100644 .ci/Ubuntu26.04/Dockerfile diff --git a/.ci/Ubuntu26.04/Dockerfile b/.ci/Ubuntu26.04/Dockerfile new file mode 100644 index 000000000..7b0cd389f --- /dev/null +++ b/.ci/Ubuntu26.04/Dockerfile @@ -0,0 +1,29 @@ +FROM ubuntu:26.04 + +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + ccache \ + clang-format \ + cmake \ + file \ + g++ \ + git \ + libgl-dev \ + liblzma-dev \ + libmariadb-dev-compat \ + libprotobuf-dev \ + libqt6multimedia6 \ + libqt6sql6-mysql \ + ninja-build \ + protobuf-compiler \ + qt6-image-formats-plugins \ + qt6-l10n-tools \ + qt6-multimedia-dev \ + qt6-svg-dev \ + qt6-tools-dev \ + qt6-tools-dev-tools \ + qt6-websockets-dev \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* diff --git a/.ci/release_template.md b/.ci/release_template.md index b0924b92a..1ce9f4bf7 100644 --- a/.ci/release_template.md +++ b/.ci/release_template.md @@ -17,6 +17,7 @@ Available pre-compiled binaries for installation: • macOS 13+ Ventura Intel Linux + • Ubuntu 26.04 LTS Resolute RacoonUbuntu 24.04 LTS Noble NumbatUbuntu 22.04 LTS Jammy JellyfishDebian 13 Trixie diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index 820044059..7bd84eb21 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -142,11 +142,16 @@ jobs: - distro: Ubuntu version: 22.04 package: DEB + test: skip # Running tests on all distros is superfluous - distro: Ubuntu version: 24.04 package: DEB + - distro: Ubuntu + version: 26.04 + package: DEB + name: ${{matrix.distro}} ${{matrix.version}} needs: configure runs-on: ubuntu-latest From a46ab5cd68dbbead94379778a0b6dca3dfaa89dc Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sat, 4 Apr 2026 02:40:38 -0700 Subject: [PATCH 04/25] [Game] Allow uppercase X in expressions (#6767) --- libcockatrice_utility/libcockatrice/utility/expression.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcockatrice_utility/libcockatrice/utility/expression.cpp b/libcockatrice_utility/libcockatrice/utility/expression.cpp index 3b2e815d1..42073670c 100644 --- a/libcockatrice_utility/libcockatrice/utility/expression.cpp +++ b/libcockatrice_utility/libcockatrice/utility/expression.cpp @@ -20,7 +20,7 @@ peg::parser math(R"( NUMBER <- < '-'? [0-9]+ > NAME <- < [a-z][a-z0-9]* > - VARIABLE <- < [x] > + VARIABLE <- < [xX] > FUNCTION <- NAME '(' EXPRESSION ( [,\n] EXPRESSION )* ')' %whitespace <- [ \t\r]* From 3ec9ae97723252b3d4d7d821968d775a739893af Mon Sep 17 00:00:00 2001 From: ebbit1q Date: Sun, 5 Apr 2026 21:52:46 +0200 Subject: [PATCH 05/25] fix compiling on arch (#6768) * fix compiling on arch * redo all the logging in affected files --- .../interface/widgets/tabs/tab_replays.cpp | 17 ++- cockatrice/src/interface/window_main.cpp | 9 +- .../network/client/remote/remote_client.cpp | 46 +++--- .../protocol/debug_pb_message.cpp | 8 +- servatrice/src/isl_interface.cpp | 51 ++++--- .../src/servatrice_database_interface.cpp | 105 +++++++------- servatrice/src/serversocketinterface.cpp | 131 +++++++++++------- 7 files changed, 219 insertions(+), 148 deletions(-) diff --git a/cockatrice/src/interface/widgets/tabs/tab_replays.cpp b/cockatrice/src/interface/widgets/tabs/tab_replays.cpp index 2db9e83c5..570677ada 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_replays.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_replays.cpp @@ -30,6 +30,8 @@ #include #include +inline Q_LOGGING_CATEGORY(TabReplaysLog, "replays_tab"); + TabReplays::TabReplays(TabSupervisor *_tabSupervisor, AbstractClient *_client, const ServerInfo_User *currentUserInfo) : Tab(_tabSupervisor), client(_client) { @@ -265,9 +267,11 @@ void TabReplays::actOpenLocalReplay() f.close(); GameReplay *replay = new GameReplay; - replay->ParseFromArray(_data.data(), _data.size()); - - emit openReplay(replay); + if (replay->ParseFromArray(_data.data(), _data.size())) { + emit openReplay(replay); + } else { + qCWarning(TabReplaysLog) << "could not parse replay!"; + } } } @@ -379,9 +383,12 @@ void TabReplays::openRemoteReplayFinished(const Response &r) const Response_ReplayDownload &resp = r.GetExtension(Response_ReplayDownload::ext); GameReplay *replay = new GameReplay; - replay->ParseFromString(resp.replay_data()); + if (replay->ParseFromString(resp.replay_data())) { - emit openReplay(replay); + emit openReplay(replay); + } else { + qCWarning(TabReplaysLog) << "could not parse remote replay!"; + } } void TabReplays::actDownload() diff --git a/cockatrice/src/interface/window_main.cpp b/cockatrice/src/interface/window_main.cpp index fbba6c3f5..86e6c1534 100644 --- a/cockatrice/src/interface/window_main.cpp +++ b/cockatrice/src/interface/window_main.cpp @@ -84,6 +84,7 @@ const QString MainWindow::appName = "Cockatrice"; const QStringList MainWindow::fileNameFilters = QStringList() << QObject::tr("Cockatrice card database (*.xml)") << QObject::tr("All files (*.*)"); +inline Q_LOGGING_CATEGORY(MainWindowLog, "main_window"); /** * Replaces the tab-specific menus that are shown in the menuBar. @@ -277,9 +278,11 @@ void MainWindow::actWatchReplay() file.close(); replay = new GameReplay; - replay->ParseFromArray(buf.data(), buf.size()); - - tabSupervisor->openReplay(replay); + if (replay->ParseFromArray(buf.data(), buf.size())) { + tabSupervisor->openReplay(replay); + } else { + qCWarning(MainWindowLog) << "failed to parse replay!"; + } } void MainWindow::localGameEnded() diff --git a/libcockatrice_network/libcockatrice/network/client/remote/remote_client.cpp b/libcockatrice_network/libcockatrice/network/client/remote/remote_client.cpp index 4b5d3f1b8..3e3ec889d 100644 --- a/libcockatrice_network/libcockatrice/network/client/remote/remote_client.cpp +++ b/libcockatrice_network/libcockatrice/network/client/remote/remote_client.cpp @@ -390,14 +390,18 @@ void RemoteClient::readData() return; ServerMessage newServerMessage; - newServerMessage.ParseFromArray(inputBuffer.data(), messageLength); - - qCDebug(RemoteClientLog).noquote() << "IN" << getSafeDebugString(newServerMessage); + bool ok = newServerMessage.ParseFromArray(inputBuffer.data(), messageLength); inputBuffer.remove(0, messageLength); messageInProgress = false; - processProtocolItem(newServerMessage); + if (ok) { + qCDebug(RemoteClientLog).noquote() << "IN" << getSafeDebugString(newServerMessage); + + processProtocolItem(newServerMessage); + } else { + qCDebug(RemoteClientLog) << "parsing error!"; + } if (getStatus() == StatusDisconnecting) // use thread-safe getter doDisconnectFromServer(); @@ -408,11 +412,13 @@ void RemoteClient::websocketMessageReceived(const QByteArray &message) { lastDataReceived = timeRunning; ServerMessage newServerMessage; - newServerMessage.ParseFromArray(message.data(), message.length()); + if (newServerMessage.ParseFromArray(message.data(), message.length())) { + qCDebug(RemoteClientLog).noquote() << "IN" << getSafeDebugString(newServerMessage); - qCDebug(RemoteClientLog).noquote() << "IN" << getSafeDebugString(newServerMessage); - - processProtocolItem(newServerMessage); + processProtocolItem(newServerMessage); + } else { + qCDebug(RemoteClientLog) << "parsing error!"; + } } void RemoteClient::sendCommandContainer(const CommandContainer &cont) @@ -426,19 +432,27 @@ void RemoteClient::sendCommandContainer(const CommandContainer &cont) qCDebug(RemoteClientLog).noquote() << "OUT" << getSafeDebugString(cont); QByteArray buf; + bool ok; if (usingWebSocket) { buf.resize(size); - cont.SerializeToArray(buf.data(), size); - websocket->sendBinaryMessage(buf); + ok = cont.SerializeToArray(buf.data(), size); + if (ok) { + websocket->sendBinaryMessage(buf); + } } else { buf.resize(size + 4); - cont.SerializeToArray(buf.data() + 4, size); - buf.data()[3] = (unsigned char)size; - buf.data()[2] = (unsigned char)(size >> 8); - buf.data()[1] = (unsigned char)(size >> 16); - buf.data()[0] = (unsigned char)(size >> 24); + ok = cont.SerializeToArray(buf.data() + 4, size); + if (ok) { + buf.data()[3] = (unsigned char)size; + buf.data()[2] = (unsigned char)(size >> 8); + buf.data()[1] = (unsigned char)(size >> 16); + buf.data()[0] = (unsigned char)(size >> 24); - socket->write(buf); + socket->write(buf); + } + } + if (!ok) { + qCDebug(RemoteClientLog) << "transmit error!"; } } diff --git a/libcockatrice_protocol/libcockatrice/protocol/debug_pb_message.cpp b/libcockatrice_protocol/libcockatrice/protocol/debug_pb_message.cpp index f0c3448b5..718487c18 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/debug_pb_message.cpp +++ b/libcockatrice_protocol/libcockatrice/protocol/debug_pb_message.cpp @@ -101,6 +101,10 @@ QString getSafeDebugString(const ::google::protobuf::Message &message) #endif // GOOGLE_PROTOBUF_VERSION > 3004000 std::string debug_string; - printer.PrintToString(message, &debug_string); - return QString::number(size) + " bytes " + QString::fromStdString(debug_string); + bool ok = printer.PrintToString(message, &debug_string); + if (ok) { + return QString::number(size) + " bytes " + QString::fromStdString(debug_string); + } else { + return "[could not convert message to string]"; + } } diff --git a/servatrice/src/isl_interface.cpp b/servatrice/src/isl_interface.cpp index bcf71a98a..692a0fdba 100644 --- a/servatrice/src/isl_interface.cpp +++ b/servatrice/src/isl_interface.cpp @@ -3,6 +3,7 @@ #include "main.h" #include "server_logger.h" +#include #include #include #include @@ -21,6 +22,8 @@ #include #include +inline Q_LOGGING_CATEGORY(IslInterfaceLog, "isl_interface"); + void IslInterface::sharedCtor(const QSslCertificate &cert, const QSslKey &privateKey) { socket = new QSslSocket(this); @@ -113,10 +116,11 @@ void IslInterface::initServer() socket->startServerEncryption(); if (!socket->waitForEncrypted(5000)) { QList sslErrors(socket->sslHandshakeErrors()); - if (sslErrors.isEmpty()) - qDebug() << "[ISL] SSL handshake timeout, terminating connection"; - else - qDebug() << "[ISL] SSL errors:" << sslErrors; + if (sslErrors.isEmpty()) { + qCDebug(IslInterfaceLog) << "SSL handshake timeout, terminating connection"; + } else { + qCWarning(IslInterfaceLog) << "SSL errors:" << sslErrors; + } deleteLater(); return; } @@ -157,7 +161,7 @@ void IslInterface::initServer() server->islLock.lockForWrite(); if (server->islConnectionExists(serverId)) { - qDebug() << "[ISL] Duplicate connection to #" << serverId << "terminating connection"; + qCDebug(IslInterfaceLog) << "Duplicate connection to #" << serverId << "terminating connection"; deleteLater(); } else { transmitMessage(message); @@ -180,27 +184,28 @@ void IslInterface::initClient() expectedErrors.append(QSslError(QSslError::SelfSignedCertificate, peerCert)); socket->ignoreSslErrors(expectedErrors); - qDebug() << "[ISL] Connecting to #" << serverId << ":" << peerAddress << ":" << peerPort; + qCDebug(IslInterfaceLog) << "Connecting to #" << serverId << ":" << peerAddress << ":" << peerPort; socket->connectToHostEncrypted(peerAddress, peerPort, peerHostName); if (!socket->waitForConnected(5000)) { - qDebug() << "[ISL] Socket error:" << socket->errorString(); + qCDebug(IslInterfaceLog) << "Socket error:" << socket->errorString(); deleteLater(); return; } if (!socket->waitForEncrypted(5000)) { QList sslErrors(socket->sslHandshakeErrors()); - if (sslErrors.isEmpty()) - qDebug() << "[ISL] SSL handshake timeout, terminating connection"; - else - qDebug() << "[ISL] SSL errors:" << sslErrors; + if (sslErrors.isEmpty()) { + qCDebug(IslInterfaceLog) << "SSL handshake timeout, terminating connection"; + } else { + qCWarning(IslInterfaceLog) << "SSL errors:" << sslErrors; + } deleteLater(); return; } server->islLock.lockForWrite(); if (server->islConnectionExists(serverId)) { - qDebug() << "[ISL] Duplicate connection to #" << serverId << "terminating connection"; + qCDebug(IslInterfaceLog) << "Duplicate connection to #" << serverId << "terminating connection"; deleteLater(); return; } @@ -242,17 +247,21 @@ void IslInterface::readClient() return; IslMessage newMessage; - newMessage.ParseFromArray(inputBuffer.data(), messageLength); + bool ok = newMessage.ParseFromArray(inputBuffer.data(), messageLength); inputBuffer.remove(0, messageLength); messageInProgress = false; - processMessage(newMessage); + if (ok) { + processMessage(newMessage); + } else { + qCWarning(IslInterfaceLog) << "parsing error!"; + } } while (!inputBuffer.isEmpty()); } void IslInterface::catchSocketError(QAbstractSocket::SocketError socketError) { - qDebug() << "[ISL] Socket error:" << socketError; + qCWarning(IslInterfaceLog) << "Socket error:" << socketError; server->islLock.lockForWrite(); server->removeIslInterface(serverId); @@ -270,7 +279,10 @@ void IslInterface::transmitMessage(const IslMessage &item) unsigned int size = static_cast(item.ByteSize()); #endif buf.resize(size + 4); - item.SerializeToArray(buf.data() + 4, size); + if (!item.SerializeToArray(buf.data() + 4, size)) { + qCWarning(IslInterfaceLog) << "transmit error!"; + return; + } buf.data()[3] = (unsigned char)size; buf.data()[2] = (unsigned char)(size >> 8); buf.data()[1] = (unsigned char)(size >> 16); @@ -368,7 +380,7 @@ void IslInterface::processSessionEvent(const SessionEvent &event, qint64 session QReadLocker clientsLocker(&server->clientsLock); Server_AbstractUserInterface *client = server->getUsersBySessionId().value(sessionId); if (!client) { - qDebug() << "IslInterface::processSessionEvent: session id" << sessionId << "not found"; + qCDebug(IslInterfaceLog) << "IslInterface::processSessionEvent: session id" << sessionId << "not found"; break; } const Event_GameJoined &gameJoined = event.GetExtension(Event_GameJoined::ext); @@ -382,7 +394,8 @@ void IslInterface::processSessionEvent(const SessionEvent &event, qint64 session QReadLocker clientsLocker(&server->clientsLock); Server_AbstractUserInterface *client = server->getUsersBySessionId().value(sessionId); if (!client) { - qDebug() << "IslInterface::processSessionEvent: session id" << sessionId << "not found"; + qCWarning(IslInterfaceLog) + << "IslInterface::processSessionEvent: session id" << sessionId << "not found"; break; } @@ -430,7 +443,7 @@ void IslInterface::processRoomCommand(const CommandContainer &cont, qint64 sessi void IslInterface::processMessage(const IslMessage &item) { - qDebug() << getSafeDebugString(item); + qCDebug(IslInterfaceLog) << getSafeDebugString(item); switch (item.message_type()) { case IslMessage::ROOM_COMMAND_CONTAINER: { diff --git a/servatrice/src/servatrice_database_interface.cpp b/servatrice/src/servatrice_database_interface.cpp index fa9b14f31..bce3542e8 100644 --- a/servatrice/src/servatrice_database_interface.cpp +++ b/servatrice/src/servatrice_database_interface.cpp @@ -7,12 +7,15 @@ #include #include #include +#include #include #include #include #include #include +inline Q_LOGGING_CATEGORY(DatabaseInterfaceLog, "database_interface"); + Servatrice_DatabaseInterface::Servatrice_DatabaseInterface(int _instanceId, Servatrice *_server) : instanceId(_instanceId), sqlDatabase(QSqlDatabase()), server(_server) { @@ -56,17 +59,16 @@ bool Servatrice_DatabaseInterface::openDatabase() sqlDatabase.close(); const QString poolStr = instanceId == -1 ? QString("main") : QString("pool %1").arg(instanceId); - qDebug().noquote() << QString("[%1] Opening database...").arg(poolStr); + qCDebug(DatabaseInterfaceLog).noquote() << poolStr << "Opening database..."; if (!sqlDatabase.open()) { - qCritical() << QString("[%1] Error opening database: %2").arg(poolStr).arg(sqlDatabase.lastError().text()); + qCCritical(DatabaseInterfaceLog) << poolStr << "Error opening database:" << sqlDatabase.lastError().text(); return false; } QSqlQuery *versionQuery = prepareQuery("select version from {prefix}_schema_version limit 1"); if (!execSqlQuery(versionQuery)) { - qCritical() << QString("[%1] Error opening database: unable to load database schema version (hint: ensure the " - "cockatrice_schema_version exists)") - .arg(poolStr); + qCCritical(DatabaseInterfaceLog) << poolStr << "Error opening database: unable to load database schema version" + << "(hint: ensure the cockatrice_schema_version exists)"; return false; } @@ -74,24 +76,21 @@ bool Servatrice_DatabaseInterface::openDatabase() const int dbversion = versionQuery->value(0).toInt(); const int expectedversion = DATABASE_SCHEMA_VERSION; if (dbversion < expectedversion) { - qCritical() << QString("[%1] Error opening database: the database schema version is too old, you need to " - "run the migrations to update it from version %2 to version %3") - .arg(poolStr) - .arg(dbversion) - .arg(expectedversion); + qCCritical(DatabaseInterfaceLog) << poolStr + << "Error opening database: the database schema version is too old, you " + "need to run the migrations to update it from version" + << dbversion << "to version" << expectedversion; return false; } else if (dbversion > expectedversion) { - qCritical() << QString("[%1] Error opening database: the database schema version %2 is too new, you need " - "to update servatrice (this servatrice actually uses version %3)") - .arg(poolStr) - .arg(dbversion) - .arg(expectedversion); + qCCritical(DatabaseInterfaceLog) << poolStr << "Error opening database: the database schema version" + << dbversion << "is too new, you need to update servatrice" + << "(this servatrice actually uses version" << expectedversion << ")"; return false; } } else { - qCritical() << QString("[%1] Error opening database: unable to load database schema version (hint: ensure the " - "cockatrice_schema_version contains a single record)") - .arg(poolStr); + qCCritical(DatabaseInterfaceLog) << poolStr + << "Error opening database: unable to load database schema version (hint: " + "ensure the cockatrice_schema_version contains a single record)"; return false; } @@ -114,9 +113,7 @@ bool Servatrice_DatabaseInterface::checkSql() if (query.lastError().isValid()) { const auto &poolStr = instanceId == -1 ? QString("main") : QString("pool %1").arg(instanceId); - qCritical() << QString("[%1] Error executing query: %2, resetting connection") - .arg(poolStr) - .arg(query.lastError().text()); + qCCritical(DatabaseInterfaceLog) << poolStr << "Error executing query:" << query.lastError().text(); sqlDatabase.close(); return openDatabase(); @@ -145,7 +142,7 @@ bool Servatrice_DatabaseInterface::execSqlQuery(QSqlQuery *query) if (query->exec()) return true; const QString poolStr = instanceId == -1 ? QString("main") : QString("pool %1").arg(instanceId); - qCritical() << QString("[%1] Error executing query: %2").arg(poolStr).arg(query->lastError().text()); + qCCritical(DatabaseInterfaceLog) << poolStr << "Error executing query:" << query->lastError().text(); sqlDatabase.close(); openDatabase(); return false; @@ -252,7 +249,8 @@ bool Servatrice_DatabaseInterface::registerUser(const QString &userName, query->bindValue(":token", token); if (!execSqlQuery(query)) { - qDebug() << "Failed to insert user: " << query->lastError() << " sql: " << query->lastQuery(); + qCWarning(DatabaseInterfaceLog) << "Failed to insert user: " << query->lastError() + << " sql: " << query->lastQuery(); return false; } @@ -270,8 +268,8 @@ bool Servatrice_DatabaseInterface::activateUser(const QString &userName, const Q activateQuery->bindValue(":username", userName); activateQuery->bindValue(":token", token); if (!execSqlQuery(activateQuery)) { - qDebug() << "Account activation failed: SQL error." << activateQuery->lastError() - << " sql: " << activateQuery->lastQuery(); + qCWarning(DatabaseInterfaceLog) << "Account activation failed: SQL error." << activateQuery->lastError() + << " sql: " << activateQuery->lastQuery(); return false; } @@ -284,7 +282,8 @@ bool Servatrice_DatabaseInterface::activateUser(const QString &userName, const Q query->bindValue(":userName", userName); if (!execSqlQuery(query)) { - qDebug() << "Failed to activate user: " << query->lastError() << " sql: " << query->lastQuery(); + qCWarning(DatabaseInterfaceLog) + << "Failed to activate user: " << query->lastError() << " sql: " << query->lastQuery(); return false; } @@ -326,7 +325,7 @@ AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_Prot prepareQuery("select password_sha512, active from {prefix}_users where name = :name"); passwordQuery->bindValue(":name", user); if (!execSqlQuery(passwordQuery)) { - qDebug("Login denied: SQL error"); + qCWarning(DatabaseInterfaceLog) << "Login denied: SQL error"; return NotLoggedIn; } @@ -334,7 +333,7 @@ AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_Prot const QString correctPasswordSha512 = passwordQuery->value(0).toString(); const bool userIsActive = passwordQuery->value(1).toBool(); if (!userIsActive) { - qDebug("Login denied: user not active"); + qCWarning(DatabaseInterfaceLog) << "Login denied: user not active"; return UserIsInactive; } QString hashedPassword; @@ -344,14 +343,14 @@ AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_Prot hashedPassword = password; } if (correctPasswordSha512 == hashedPassword) { - qDebug("Login accepted: password right"); + qCDebug(DatabaseInterfaceLog) << "Login accepted: password right"; return PasswordRight; } else { - qDebug("Login denied: password wrong"); + qCDebug(DatabaseInterfaceLog) << "Login denied: password wrong"; return NotLoggedIn; } } else { - qDebug("Login accepted: unknown user"); + qCDebug(DatabaseInterfaceLog) << "Login accepted: unknown user"; return UnknownUser; } } @@ -369,7 +368,7 @@ bool Servatrice_DatabaseInterface::checkUserIsBanned(const QString &ipAddress, return false; if (!checkSql()) { - qDebug("Failed to check if user is banned. Database invalid."); + qCWarning(DatabaseInterfaceLog) << "Failed to check if user is banned. Database invalid."; return false; } @@ -400,7 +399,7 @@ bool Servatrice_DatabaseInterface::checkUserIsIdBanned(const QString &clientId, idBanQuery->bindValue(":id", clientId); idBanQuery->bindValue(":id2", clientId); if (!execSqlQuery(idBanQuery)) { - qDebug() << "Id ban check failed: SQL error." << idBanQuery->lastError(); + qCWarning(DatabaseInterfaceLog) << "Id ban check failed: SQL error." << idBanQuery->lastError(); return false; } @@ -410,7 +409,7 @@ bool Servatrice_DatabaseInterface::checkUserIsIdBanned(const QString &clientId, if ((secondsLeft > 0) || permanentBan) { banReason = idBanQuery->value(2).toString(); banSecondsRemaining = permanentBan ? 0 : secondsLeft; - qDebug() << "User is banned by client id" << clientId; + qCDebug(DatabaseInterfaceLog) << "User is banned by client id" << clientId; return true; } } @@ -428,7 +427,7 @@ bool Servatrice_DatabaseInterface::checkUserIsNameBanned(const QString &userName nameBanQuery->bindValue(":name1", userName); nameBanQuery->bindValue(":name2", userName); if (!execSqlQuery(nameBanQuery)) { - qDebug() << "Name ban check failed: SQL error" << nameBanQuery->lastError(); + qCWarning(DatabaseInterfaceLog) << "Name ban check failed: SQL error" << nameBanQuery->lastError(); return false; } @@ -438,7 +437,7 @@ bool Servatrice_DatabaseInterface::checkUserIsNameBanned(const QString &userName if ((secondsLeft > 0) || permanentBan) { banReason = nameBanQuery->value(2).toString(); banSecondsRemaining = permanentBan ? 0 : secondsLeft; - qDebug() << "Username" << userName << "is banned by name"; + qCDebug(DatabaseInterfaceLog) << "Username" << userName << "is banned by name"; return true; } } @@ -464,7 +463,7 @@ bool Servatrice_DatabaseInterface::checkUserIsIpBanned(const QString &ipAddress, ipBanQuery->bindValue(":address", ipAddress); ipBanQuery->bindValue(":address2", ipAddress); if (!execSqlQuery(ipBanQuery)) { - qDebug() << "IP ban check failed: SQL error." << ipBanQuery->lastError(); + qCWarning(DatabaseInterfaceLog) << "IP ban check failed: SQL error." << ipBanQuery->lastError(); return false; } @@ -474,7 +473,7 @@ bool Servatrice_DatabaseInterface::checkUserIsIpBanned(const QString &ipAddress, if ((secondsLeft > 0) || permanentBan) { banReason = ipBanQuery->value(2).toString(); banSecondsRemaining = permanentBan ? 0 : secondsLeft; - qDebug() << "User is banned by address" << ipAddress; + qCDebug(DatabaseInterfaceLog) << "User is banned by address" << ipAddress; return true; } } @@ -865,12 +864,17 @@ void Servatrice_DatabaseInterface::storeGameInformation(const QString &roomName, const unsigned int size = static_cast(replayList[i]->ByteSize()); #endif blob.resize(size); - replayList[i]->SerializeToArray(blob.data(), size); + qulonglong replayId = replayList[i]->replay_id(); + if (replayList[i]->SerializeToArray(blob.data(), size)) { - replayIds.append(QVariant((qulonglong)replayList[i]->replay_id())); - replayGameIds.append(gameInfo.game_id()); - replayDurations.append(replayList[i]->duration_seconds()); - replayBlobs.append(blob); + replayIds.append(QVariant(replayId)); + replayGameIds.append(gameInfo.game_id()); + replayDurations.append(replayList[i]->duration_seconds()); + replayBlobs.append(blob); + } else { + qCWarning(DatabaseInterfaceLog) + << "failed to serialise replay, id:" << replayId << "game:" << gameInfo.game_id(); + } } { @@ -1017,7 +1021,7 @@ bool Servatrice_DatabaseInterface::changeUserPassword(const QString &user, passwordQuery->bindValue(":name", user); if (!execSqlQuery(passwordQuery)) { - qDebug("Change password denied: SQL error"); + qCWarning(DatabaseInterfaceLog) << "Change password denied: SQL error"; return false; } @@ -1084,7 +1088,7 @@ void Servatrice_DatabaseInterface::updateUsersLastLoginData(const QString &userN QSqlQuery *query = prepareQuery("select id from {prefix}_users where name = :user_name"); query->bindValue(":user_name", userName); if (!execSqlQuery(query)) { - qDebug("Failed to locate user id when updating users last login data: SQL Error"); + qCWarning(DatabaseInterfaceLog) << "Failed to locate user id when updating users last login data: SQL Error"; return; } @@ -1133,7 +1137,7 @@ QList Servatrice_DatabaseInterface::getUserBanHistory(const QStr query->bindValue(":user_name", userName); if (!execSqlQuery(query)) { - qDebug("Failed to collect ban history information: SQL Error"); + qCWarning(DatabaseInterfaceLog) << "Failed to collect ban history information: SQL Error"; return results; } @@ -1168,7 +1172,7 @@ bool Servatrice_DatabaseInterface::addWarning(const QString userName, query->bindValue(":warn_reason", warningReason); query->bindValue(":client_id", clientID); if (!execSqlQuery(query)) { - qDebug("Failed to collect create warning history information: SQL Error"); + qCWarning(DatabaseInterfaceLog) << "Failed to collect create warning history information: SQL Error"; return false; } @@ -1189,7 +1193,7 @@ QList Servatrice_DatabaseInterface::getUserWarnHistory(const query->bindValue(":user_id", userID); if (!execSqlQuery(query)) { - qDebug("Failed to collect warning history information: SQL Error"); + qCWarning(DatabaseInterfaceLog) << "Failed to collect warning history information: SQL Error"; return results; } @@ -1296,7 +1300,7 @@ QList Servatrice_DatabaseInterface::getMessageLogHistory } if (!execSqlQuery(query)) { - qDebug("Failed to collect log history information: SQL Error"); + qCWarning(DatabaseInterfaceLog) << "Failed to collect log history information: SQL Error"; return results; } @@ -1324,7 +1328,8 @@ int Servatrice_DatabaseInterface::checkNumberOfUserAccounts(const QString &email query->bindValue(":user_email", email); if (!execSqlQuery(query)) { - qDebug("Failed to identify the number of users accounts for users email address: SQL Error"); + qCWarning(DatabaseInterfaceLog) + << "Failed to identify the number of users accounts for users email address: SQL Error"; return 0; } diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index 619d36d3a..41e61ddec 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -83,6 +84,10 @@ #include #include +inline Q_LOGGING_CATEGORY(AbstractServerSocketInterfaceLog, "abstract_server_socket_interface"); +inline Q_LOGGING_CATEGORY(TcpServerSocketInterfaceLog, "tcp_server_socket_interface"); +inline Q_LOGGING_CATEGORY(WebsocketServerSocketInterfaceLog, "websocket_server_socket_interface"); + static const int protocolVersion = 14; AbstractServerSocketInterface::AbstractServerSocketInterface(Servatrice *_server, @@ -130,7 +135,7 @@ bool AbstractServerSocketInterface::initSession() void AbstractServerSocketInterface::catchSocketError(QAbstractSocket::SocketError socketError) { - qDebug() << "Socket error:" << socketError; + qCWarning(AbstractServerSocketInterfaceLog) << "Socket error:" << socketError; prepareDestroy(); } @@ -1100,7 +1105,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdBanFromServer(const Com clientIdQuery->bindValue(":client_id", nameFromStdString(cmd.clientid())); sqlInterface->execSqlQuery(clientIdQuery); if (!sqlInterface->execSqlQuery(clientIdQuery)) { - qDebug("ClientID username ban lookup failed: SQL Error"); + qCWarning(AbstractServerSocketInterfaceLog) << "ClientID username ban lookup failed: SQL Error"; } else { while (clientIdQuery->next()) { userName = clientIdQuery->value(0).toString(); @@ -1152,7 +1157,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdRegisterAccount(const C { QString userName = nameFromStdString(cmd.user_name()); QString clientId = nameFromStdString(cmd.clientid()); - qDebug() << "Got register command for user:" << userName; + qCDebug(AbstractServerSocketInterfaceLog) << "Got register command for user:" << userName; bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool(); if (!registrationEnabled) { @@ -1289,7 +1294,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdRegisterAccount(const C country, !requireEmailActivation); if (regSucceeded) { - qDebug() << "Accepted register command for user:" << userName; + qCDebug(AbstractServerSocketInterfaceLog) << "Accepted register command for user:" << userName; if (requireEmailActivation) { QSqlQuery *query = sqlInterface->prepareQuery("insert into {prefix}_activation_emails (name) values(:name)"); @@ -1337,7 +1342,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdActivateAccount(const C clientId = "UNKNOWN"; if (sqlInterface->activateUser(userName, token)) { - qDebug() << "Accepted activation for user" << userName; + qCDebug(AbstractServerSocketInterfaceLog) << "Accepted activation for user" << userName; if (servatrice->getEnableRegistrationAudit()) sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), @@ -1345,7 +1350,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdActivateAccount(const C return Response::RespActivationAccepted; } else { - qDebug() << "Failed activation for user" << userName; + qCDebug(AbstractServerSocketInterfaceLog) << "Failed activation for user" << userName; if (servatrice->getEnableRegistrationAudit()) sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), @@ -1493,7 +1498,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdForgotPasswordRequest(c const QString userName = nameFromStdString(cmd.user_name()); const QString clientId = nameFromStdString(cmd.clientid()); - qDebug() << "Received reset password request from user:" << userName; + qCDebug(AbstractServerSocketInterfaceLog) << "Received reset password request from user:" << userName; if (!servatrice->getEnableForgotPassword()) { if (servatrice->getEnableForgotPasswordAudit()) @@ -1575,7 +1580,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdForgotPasswordReset(con Q_UNUSED(rc); QString userName = nameFromStdString(cmd.user_name()); QString clientId = nameFromStdString(cmd.clientid()); - qDebug() << "Received reset password reset from user:" << userName; + qCDebug(AbstractServerSocketInterfaceLog) << "Received reset password reset from user:" << userName; if (!sqlInterface->doesForgotPasswordExist(userName)) { if (servatrice->getEnableForgotPasswordAudit()) @@ -1626,7 +1631,7 @@ AbstractServerSocketInterface::cmdForgotPasswordChallenge(const Command_ForgotPa const QString userName = nameFromStdString(cmd.user_name()); const QString clientId = nameFromStdString(cmd.clientid()); - qDebug() << "Received reset password challenge from user:" << userName; + qCDebug(AbstractServerSocketInterfaceLog) << "Received reset password challenge from user:" << userName; if (!servatrice->getEnableForgotPasswordChallenge()) { if (servatrice->getEnableForgotPasswordAudit()) { @@ -1971,13 +1976,16 @@ void TcpServerSocketInterface::flushOutputQueue() unsigned int size = static_cast(item.ByteSize()); #endif buf.resize(size + 4); - item.SerializeToArray(buf.data() + 4, size); - buf.data()[3] = (unsigned char)size; - buf.data()[2] = (unsigned char)(size >> 8); - buf.data()[1] = (unsigned char)(size >> 16); - buf.data()[0] = (unsigned char)(size >> 24); - // In case socket->write() calls catchSocketError(), the mutex must not be locked during this call. - writeToSocket(buf); + if (item.SerializeToArray(buf.data() + 4, size)) { + buf.data()[3] = (unsigned char)size; + buf.data()[2] = (unsigned char)(size >> 8); + buf.data()[1] = (unsigned char)(size >> 16); + buf.data()[0] = (unsigned char)(size >> 24); + // In case socket->write() calls catchSocketError(), the mutex must not be locked during this call. + writeToSocket(buf); + } else { + qCWarning(TcpServerSocketInterfaceLog) << "serialisation error!"; + } totalBytes += size + 4; locker.relock(); @@ -2010,41 +2018,48 @@ void TcpServerSocketInterface::readClient() return; CommandContainer newCommandContainer; + bool ok; try { - newCommandContainer.ParseFromArray(inputBuffer.data(), messageLength); + ok = newCommandContainer.ParseFromArray(inputBuffer.data(), messageLength); } catch (std::exception &e) { - qDebug() << "Caught std::exception in" << __FILE__ << __LINE__ << + qCWarning(TcpServerSocketInterfaceLog) << "Caught std::exception in" << __FILE__ << __LINE__ << #ifdef _MSC_VER // Visual Studio - __FUNCTION__; + __FUNCTION__ #else - __PRETTY_FUNCTION__; + __PRETTY_FUNCTION__ #endif - qDebug() << "Exception:" << e.what(); - qDebug() << "Message coming from:" << getAddress(); - qDebug() << "Message length:" << messageLength; - qDebug() << "Message content:" << inputBuffer.toHex(); + << Qt::endl + << "Exception:" << e.what() << Qt::endl + << "Message coming from:" << getAddress() << Qt::endl + << "Message length:" << messageLength << Qt::endl + << "Message content:" << inputBuffer.toHex(); } catch (...) { - qDebug() << "Unhandled exception in" << __FILE__ << __LINE__ << + qCWarning(TcpServerSocketInterfaceLog) << "Unhandled exception in" << __FILE__ << __LINE__ << #ifdef _MSC_VER // Visual Studio - __FUNCTION__; + __FUNCTION__ #else - __PRETTY_FUNCTION__; + __PRETTY_FUNCTION__ #endif - qDebug() << "Message coming from:" << getAddress(); + << Qt::endl + << "Message coming from:" << getAddress(); } - inputBuffer.remove(0, messageLength); messageInProgress = false; - // dirty hack to make v13 client display the correct error message - if (handshakeStarted) - processCommandContainer(newCommandContainer); - else if (!newCommandContainer.has_cmd_id()) { - handshakeStarted = true; - if (!initTcpSession()) - prepareDestroy(); + if (ok) { + // dirty hack to make v13 client display the correct error message + if (handshakeStarted) + processCommandContainer(newCommandContainer); + else if (!newCommandContainer.has_cmd_id()) { + handshakeStarted = true; + if (!initTcpSession()) + prepareDestroy(); + } + // end of hack + } else { + qCWarning(TcpServerSocketInterfaceLog) << "parsing error!"; } - // end of hack + } while (!inputBuffer.isEmpty()); } @@ -2172,9 +2187,12 @@ void WebsocketServerSocketInterface::flushOutputQueue() unsigned int size = static_cast(item.ByteSize()); #endif buf.resize(size); - item.SerializeToArray(buf.data(), size); - // In case socket->write() calls catchSocketError(), the mutex must not be locked during this call. - writeToSocket(buf); + if (item.SerializeToArray(buf.data(), size)) { + // In case socket->write() calls catchSocketError(), the mutex must not be locked during this call. + writeToSocket(buf); + } else { + qCWarning(TcpServerSocketInterfaceLog) << "serialisation error!"; + } totalBytes += size; locker.relock(); @@ -2190,30 +2208,37 @@ void WebsocketServerSocketInterface::binaryMessageReceived(const QByteArray &mes servatrice->incRxBytes(message.size()); CommandContainer newCommandContainer; + bool ok; try { - newCommandContainer.ParseFromArray(message.data(), message.size()); + ok = newCommandContainer.ParseFromArray(message.data(), message.size()); } catch (std::exception &e) { - qDebug() << "Caught std::exception in" << __FILE__ << __LINE__ << + qCWarning(WebsocketServerSocketInterfaceLog) << "Caught std::exception in" << __FILE__ << __LINE__ << #ifdef _MSC_VER // Visual Studio - __FUNCTION__; + __FUNCTION__ #else - __PRETTY_FUNCTION__; + __PRETTY_FUNCTION__ #endif - qDebug() << "Exception:" << e.what(); - qDebug() << "Message coming from:" << getAddress(); - qDebug() << "Message length:" << message.size(); - qDebug() << "Message content:" << message.toHex(); + << Qt::endl + << "Exception:" << e.what() << Qt::endl + << "Message coming from:" << getAddress() << Qt::endl + << "Message length:" << message.size() << Qt::endl + << "Message content:" << message.toHex(); } catch (...) { - qDebug() << "Unhandled exception in" << __FILE__ << __LINE__ << + qCWarning(WebsocketServerSocketInterfaceLog) << "Unhandled exception in" << __FILE__ << __LINE__ << #ifdef _MSC_VER // Visual Studio - __FUNCTION__; + __FUNCTION__ #else - __PRETTY_FUNCTION__; + __PRETTY_FUNCTION__ #endif - qDebug() << "Message coming from:" << getAddress(); + << Qt::endl + << "Message coming from:" << getAddress(); } - processCommandContainer(newCommandContainer); + if (ok) { + processCommandContainer(newCommandContainer); + } else { + qCWarning(WebsocketServerSocketInterfaceLog) << "parsing error!"; + } } bool AbstractServerSocketInterface::isPasswordLongEnough(const int passwordLength) From dbed6890dac7b308ef228362e63cb67413c14fcd Mon Sep 17 00:00:00 2001 From: Bruno Alexandre Rosa <1791393+brunoalr@users.noreply.github.com> Date: Tue, 7 Apr 2026 02:36:45 -0300 Subject: [PATCH 06/25] feat: support expressions when setting card counters (#6753) * feat: enable expressions in card counters * fix includes * fix multiple selection * cleanup useless conversions * const ref where possible * do not use const, be consistent with local patterns in the file --- cockatrice/src/game/player/player_actions.cpp | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/cockatrice/src/game/player/player_actions.cpp b/cockatrice/src/game/player/player_actions.cpp index ca0967636..a44c28d40 100644 --- a/cockatrice/src/game/player/player_actions.cpp +++ b/cockatrice/src/game/player/player_actions.cpp @@ -3,6 +3,7 @@ #include "../../interface/widgets/tabs/tab_game.h" #include "../../interface/widgets/utility/get_text_with_max.h" #include "../board/card_item.h" +#include "../client/settings/card_counter_settings.h" #include "../dialogs/dlg_move_top_cards_until.h" #include "../dialogs/dlg_roll_dice.h" #include "../zones/hand_zone.h" @@ -27,6 +28,7 @@ #include #include #include +#include #include #include @@ -1572,23 +1574,34 @@ void PlayerActions::actCardCounterTrigger() break; } case 11: { // set counter with dialog - bool ok; player->setDialogSemaphore(true); - int oldValue = 0; - if (player->getGameScene()->selectedItems().size() == 1) { - auto *card = static_cast(player->getGameScene()->selectedItems().first()); - oldValue = card->getCounters().value(counterId, 0); + // If a single card is selected, we show the old value in the dialog. Otherwise, we show "x" + QList sel = player->getGameScene()->selectedItems(); + QString oldValueForDlg = "x"; + if (sel.size() == 1) { + auto *card = dynamic_cast(sel.first()); + oldValueForDlg = QString::number(card->getCounters().value(counterId, 0)); } - int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Set counters"), tr("Number:"), oldValue, - 0, MAX_COUNTERS_ON_CARD, 1, &ok); + + auto &cardCounterSettings = SettingsCache::instance().cardCounters(); + QString counterName = cardCounterSettings.displayName(counterId); + + AbstractCounterDialog dialog(counterName, oldValueForDlg, player->getGame()->getTab()); + int ok = dialog.exec(); + player->setDialogSemaphore(false); if (player->clearCardsToDelete() || !ok) { return; } - for (const auto &item : player->getGameScene()->selectedItems()) { - auto *card = static_cast(item); + for (const auto &item : sel) { + auto *card = dynamic_cast(item); + + int oldValue = card->getCounters().value(counterId, 0); + Expression exp(oldValue); + int number = static_cast(exp.parse(dialog.textValue())); + auto *cmd = new Command_SetCardCounter; cmd->set_zone(card->getZone()->getName().toStdString()); cmd->set_card_id(card->getId()); From 335022c4aa3ebda39eb0f42fbfc3728cc2b831aa Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Tue, 7 Apr 2026 15:02:00 +0200 Subject: [PATCH 07/25] Include themes (#6781) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Properly reset default theme. Took 9 minutes * Descend in preference on windows. Took 9 minutes * Also reset style for custom themes. Took 3 minutes * Try things Took 9 minutes * Add modern windows style. Took 8 minutes * Update theme_manager.cpp --------- Co-authored-by: Lukas Brübach --- cockatrice/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 1ca3c77c2..12733afe6 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -564,6 +564,9 @@ if(WIN32) PATTERN "styles/qopensslbackend.dll" PATTERN "styles/qschannelbackend.dll" PATTERN "styles/qwindowsvistastyle.dll" + PATTERN "styles/qwindows11style.dll" + PATTERN "styles/qmodernwindowsstyle.dll" + PATTERN "styles/qmodernwindowsstyled.dll" PATTERN "tls/qcertonlybackend.dll" PATTERN "tls/qopensslbackend.dll" PATTERN "tls/qschannelbackend.dll" From 635fae101d2d5c97a52d774b90ffa6ffebd6f244 Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Tue, 7 Apr 2026 15:57:03 +0200 Subject: [PATCH 08/25] Use palette colors for resizable panel stylesheets. (#6782) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Took 7 minutes Took 5 seconds Co-authored-by: Lukas Brübach --- .../deck_analytics/resizable_panel.cpp | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/cockatrice/src/interface/widgets/deck_analytics/resizable_panel.cpp b/cockatrice/src/interface/widgets/deck_analytics/resizable_panel.cpp index a0c971a75..f7bb5ac35 100644 --- a/cockatrice/src/interface/widgets/deck_analytics/resizable_panel.cpp +++ b/cockatrice/src/interface/widgets/deck_analytics/resizable_panel.cpp @@ -20,7 +20,6 @@ ResizablePanel::ResizablePanel(const QString &_typeId, AbstractAnalyticsPanelWid frame = new QFrame(this); frame->setFrameShape(QFrame::Box); frame->setLineWidth(2); - frame->setStyleSheet("border: none;"); auto *frameLayout = new QVBoxLayout(frame); frameLayout->setContentsMargins(0, 0, 0, 0); @@ -30,15 +29,13 @@ ResizablePanel::ResizablePanel(const QString &_typeId, AbstractAnalyticsPanelWid frameLayout->addWidget(analyticsPanel); dropIndicator = new QFrame(frame); - dropIndicator->setStyleSheet("background-color: #3daee9;"); dropIndicator->setFixedHeight(3); dropIndicator->hide(); // hidden by default dropIndicator->raise(); // make sure it's above children selectionOverlay = new QFrame(frame); - selectionOverlay->setStyleSheet("background-color: rgba(61,174,233,50);"); // semi-transparent blue - selectionOverlay->hide(); // hidden by default - selectionOverlay->raise(); // make sure it is above children + selectionOverlay->hide(); // hidden by default + selectionOverlay->raise(); // make sure it is above children selectionOverlay->setAttribute(Qt::WA_TransparentForMouseEvents); // Bottom bar with drag button and resize handle @@ -51,24 +48,41 @@ ResizablePanel::ResizablePanel(const QString &_typeId, AbstractAnalyticsPanelWid dragButton = new QPushButton("☰", bottomBar); dragButton->setFixedSize(40, 8); dragButton->setCursor(Qt::OpenHandCursor); - dragButton->setStyleSheet("QPushButton { " - "background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #4a4a4a, stop:1 #3a3a3a); " - "border: none; color: #888; font-size: 10px; }" - "QPushButton:hover { background: #5a5a5a; }"); bottomLayout->addWidget(dragButton); // Resize handle fills the rest resizeHandle = new QWidget(bottomBar); resizeHandle->setFixedHeight(8); resizeHandle->setCursor(Qt::SizeVerCursor); - resizeHandle->setStyleSheet("background: qlineargradient(x1:0, y1:0, x2:0, y2:1, " - "stop:0 #3a3a3a, stop:1 #2a2a2a);"); bottomLayout->addWidget(resizeHandle, 1); frameLayout->addWidget(bottomBar); mainLayout->addWidget(frame); + const QPalette &pal = QApplication::palette(); + QColor mid = pal.color(QPalette::Mid); + QColor dark = pal.color(QPalette::Dark); + QColor midLight = pal.color(QPalette::Midlight); + QColor shadow = pal.color(QPalette::Shadow); + QColor placeholderText = pal.color(QPalette::PlaceholderText); + + frame->setStyleSheet("QFrame { border: none; }"); + + dropIndicator->setStyleSheet("QFrame { background-color: #3daee9; }"); + + selectionOverlay->setStyleSheet("QFrame { background-color: rgba(61,174,233,50); }"); + + dragButton->setStyleSheet(QString("QPushButton { " + "background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 %1, stop:1 %2); " + "border: none; color: %3; font-size: 10px; }" + "QPushButton:hover { background: %4; }") + .arg(mid.name(), dark.name(), placeholderText.name(), midLight.name())); + + resizeHandle->setStyleSheet(QString("QWidget { background: qlineargradient(x1:0, y1:0, x2:0, y2:1, " + "stop:0 %1, stop:1 %2); }") + .arg(dark.name(), shadow.name())); + // Set size policy setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); From d7b31f2f9d87ba8e9cfa563cd95229a922db1567 Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Tue, 7 Apr 2026 15:58:42 +0200 Subject: [PATCH 09/25] Set OracleWizard style to "Modern" instead of "Aero" (#6778) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use fusions own palette. Took 6 minutes * Start from default palette always. Took 4 minutes * Add modern style. Took 24 seconds * Scope this fix to Windows. Took 4 minutes --------- Co-authored-by: Lukas Brübach --- cockatrice/src/interface/theme_manager.cpp | 14 ++++++++------ oracle/src/oraclewizard.cpp | 4 ++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/cockatrice/src/interface/theme_manager.cpp b/cockatrice/src/interface/theme_manager.cpp index 1dc61fdb7..5809ce350 100644 --- a/cockatrice/src/interface/theme_manager.cpp +++ b/cockatrice/src/interface/theme_manager.cpp @@ -179,7 +179,7 @@ QBrush ThemeManager::loadExtraBrush(QString fileName, QBrush &fallbackBrush) static inline QPalette createDarkGreenFusionPalette() { - QPalette p; + QPalette p = QStyleFactory::create("Fusion")->standardPalette(); // ---------- Core backgrounds ---------- p.setColor(QPalette::Window, QColor(30, 30, 30)); // #ff1e1e1e @@ -248,7 +248,7 @@ static inline QPalette createDarkGreenFusionPalette() static inline QPalette createLightGreenFusionPalette() { - QPalette p; + QPalette p = QStyleFactory::create("Fusion")->standardPalette(); // ---------- Core backgrounds ---------- p.setColor(QPalette::Window, QColor(240, 240, 240)); // #fff0f0f0 @@ -332,13 +332,15 @@ void ThemeManager::themeChangedSlot() } if (themeName == FUSION_THEME_NAME) { - qApp->setStyle(QStyleFactory::create("Fusion")); + QStyle *fusionStyle = QStyleFactory::create("Fusion"); + qApp->setStyle(fusionStyle); #if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)) - QPalette palette; + // Start from Fusion's own palette so dark mode is handled correctly, + // then apply any tweaks on top of it. + QPalette palette = fusionStyle->standardPalette(); if (QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark) { palette.setColor(QPalette::AlternateBase, QColor(53, 53, 53)); } - qApp->setPalette(palette); #endif } else if (themeName == FUSION_THEME_NAME_LIGHT) { @@ -348,7 +350,7 @@ void ThemeManager::themeChangedSlot() qApp->setStyle(QStyleFactory::create("Fusion")); qApp->setPalette(createDarkGreenFusionPalette()); } else { - qApp->setStyle(defaultStyleName); // setting the style also sets the palette + qApp->setStyle(QStyleFactory::create(defaultStyleName)); // setting the style also sets the palette } if (dirPath.isEmpty()) { diff --git a/oracle/src/oraclewizard.cpp b/oracle/src/oraclewizard.cpp index 9d32a993b..2edb9e561 100644 --- a/oracle/src/oraclewizard.cpp +++ b/oracle/src/oraclewizard.cpp @@ -21,6 +21,10 @@ OracleWizard::OracleWizard(QWidget *parent) : QWizard(parent) // define a dummy context that will be used where needed QString dummy = QT_TRANSLATE_NOOP("i18n", "English"); +#ifdef Q_OS_WIN + setWizardStyle(QWizard::ModernStyle); +#endif + QString oracleSettingsFile = SettingsCache::instance().getSettingsPath() + "oracle.ini"; settings = new QSettings(oracleSettingsFile, QSettings::IniFormat, this); From ac06fb9d1c945a311c1824bdaafc1ee99525ed9a Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Thu, 9 Apr 2026 06:02:00 -0700 Subject: [PATCH 10/25] [Game] Fix crash by properly parenting QObjects (#6788) --- cockatrice/src/game/abstract_game.cpp | 3 ++- cockatrice/src/game/player/menu/player_menu.cpp | 2 +- cockatrice/src/game/player/menu/player_menu.h | 2 +- cockatrice/src/game/player/player_actions.cpp | 3 ++- cockatrice/src/game/player/player_event_handler.cpp | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cockatrice/src/game/abstract_game.cpp b/cockatrice/src/game/abstract_game.cpp index 505cd5fde..9216f9174 100644 --- a/cockatrice/src/game/abstract_game.cpp +++ b/cockatrice/src/game/abstract_game.cpp @@ -1,8 +1,9 @@ #include "abstract_game.h" +#include "../interface/widgets/tabs/tab_game.h" #include "player/player.h" -AbstractGame::AbstractGame(TabGame *_tab) : tab(_tab) +AbstractGame::AbstractGame(TabGame *_tab) : QObject(_tab), tab(_tab) { gameMetaInfo = new GameMetaInfo(this); gameEventHandler = new GameEventHandler(this); diff --git a/cockatrice/src/game/player/menu/player_menu.cpp b/cockatrice/src/game/player/menu/player_menu.cpp index 7786ec3fc..0dc381e28 100644 --- a/cockatrice/src/game/player/menu/player_menu.cpp +++ b/cockatrice/src/game/player/menu/player_menu.cpp @@ -10,7 +10,7 @@ #include -PlayerMenu::PlayerMenu(Player *_player) : player(_player) +PlayerMenu::PlayerMenu(Player *_player) : QObject(_player), player(_player) { playerMenu = new TearOffMenu(); diff --git a/cockatrice/src/game/player/menu/player_menu.h b/cockatrice/src/game/player/menu/player_menu.h index 5fce27158..104c5a930 100644 --- a/cockatrice/src/game/player/menu/player_menu.h +++ b/cockatrice/src/game/player/menu/player_menu.h @@ -37,7 +37,7 @@ private slots: void refreshShortcuts(); public: - PlayerMenu(Player *player); + explicit PlayerMenu(Player *player); /// Lifecycle methods: delegate to all managedComponents, plus counters separately via player->getCounters(). void retranslateUi(); diff --git a/cockatrice/src/game/player/player_actions.cpp b/cockatrice/src/game/player/player_actions.cpp index a44c28d40..20034df16 100644 --- a/cockatrice/src/game/player/player_actions.cpp +++ b/cockatrice/src/game/player/player_actions.cpp @@ -35,7 +35,8 @@ // milliseconds in between triggers of the move top cards until action static constexpr int MOVE_TOP_CARD_UNTIL_INTERVAL = 100; -PlayerActions::PlayerActions(Player *_player) : player(_player), lastTokenTableRow(0), movingCardsUntil(false) +PlayerActions::PlayerActions(Player *_player) + : QObject(_player), player(_player), lastTokenTableRow(0), movingCardsUntil(false) { moveTopCardTimer = new QTimer(this); moveTopCardTimer->setInterval(MOVE_TOP_CARD_UNTIL_INTERVAL); diff --git a/cockatrice/src/game/player/player_event_handler.cpp b/cockatrice/src/game/player/player_event_handler.cpp index f4c3840e0..5571010d1 100644 --- a/cockatrice/src/game/player/player_event_handler.cpp +++ b/cockatrice/src/game/player/player_event_handler.cpp @@ -32,7 +32,7 @@ #include #include -PlayerEventHandler::PlayerEventHandler(Player *_player) : player(_player) +PlayerEventHandler::PlayerEventHandler(Player *_player) : QObject(_player), player(_player) { } From 2d412bfe5266bb8c5ae00ff11b5baf1718bd0767 Mon Sep 17 00:00:00 2001 From: ebbit1q Date: Thu, 9 Apr 2026 21:16:38 +0200 Subject: [PATCH 11/25] fix cardloading on qt5 (#6790) --- .../src/interface/card_picture_loader/card_picture_loader.cpp | 1 + libcockatrice_card/libcockatrice/card/printing/exact_card.h | 1 + 2 files changed, 2 insertions(+) diff --git a/cockatrice/src/interface/card_picture_loader/card_picture_loader.cpp b/cockatrice/src/interface/card_picture_loader/card_picture_loader.cpp index 06a0476c9..dbd51b973 100644 --- a/cockatrice/src/interface/card_picture_loader/card_picture_loader.cpp +++ b/cockatrice/src/interface/card_picture_loader/card_picture_loader.cpp @@ -28,6 +28,7 @@ CardPictureLoader::CardPictureLoader() : QObject(nullptr) connect(&SettingsCache::instance(), &SettingsCache::picDownloadChanged, this, &CardPictureLoader::picDownloadChanged); + qRegisterMetaType(); connect(worker, &CardPictureLoaderWorker::imageLoaded, this, &CardPictureLoader::imageLoaded); statusBar = new CardPictureLoaderStatusBar(nullptr); diff --git a/libcockatrice_card/libcockatrice/card/printing/exact_card.h b/libcockatrice_card/libcockatrice/card/printing/exact_card.h index 01c61a3a1..74fc75da3 100644 --- a/libcockatrice_card/libcockatrice/card/printing/exact_card.h +++ b/libcockatrice_card/libcockatrice/card/printing/exact_card.h @@ -114,5 +114,6 @@ public: */ void emitPixmapUpdated() const; }; +Q_DECLARE_METATYPE(ExactCard) #endif // EXACT_CARD_H From 9aa5702e14bcf17f72faa5e24d3eb16ee4b83bec Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 21:27:14 +0200 Subject: [PATCH 12/25] Translate cockatrice_en@source.ts in en_US (#6783) 100% translated source file: 'cockatrice_en@source.ts' on 'en_US'. Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com> --- cockatrice/translations/cockatrice_en_US.ts | 2435 +++++++++++-------- 1 file changed, 1356 insertions(+), 1079 deletions(-) diff --git a/cockatrice/translations/cockatrice_en_US.ts b/cockatrice/translations/cockatrice_en_US.ts index c74e63114..254ead590 100644 --- a/cockatrice/translations/cockatrice_en_US.ts +++ b/cockatrice/translations/cockatrice_en_US.ts @@ -23,12 +23,12 @@ AbstractDlgDeckTextEdit - + &Refresh &Refresh - + Parse Set Name and Number (if available) Parse Set Name and Number (if available) @@ -36,62 +36,62 @@ AbstractTabDeckEditor - + Open in new tab Open in new tab - + Are you sure? Are you sure? - + The decklist has been modified. Do you want to save the changes? The decklist has been modified. Do you want to save the changes? - - - - - - + + + + + + Error Error - + Could not open deck at %1 Could not open deck at %1 - + Could not save remote deck Could not save remote deck - - + + The deck could not be saved. Please check that the directory is writable and try again. The deck could not be saved. Please check that the directory is writable and try again. - + Save deck Save deck - + The deck could not be saved. The deck could not be saved. - + There are no cards in your deck to be exported There are no cards in your deck to be exported @@ -133,202 +133,168 @@ Please check that the directory is writable and try again. AppearanceSettingsPage - + seconds seconds - + Error Error - + Could not create themes directory at '%1'. Could not create themes directory at '%1'. - - Enabling this feature will disable the use of the Printing Selector. - -You will not be able to manage printing preferences on a per-deck basis, or see printings other people have selected for their decks. - -You will have to use the Set Manager, available through Card Database -> Manage Sets. - -Are you sure you would like to enable this feature? - Enabling this feature will disable the use of the Printing Selector. - -You will not be able to manage printing preferences on a per-deck basis, or see printings other people have selected for their decks. - -You will have to use the Set Manager, available through Card Database -> Manage Sets. - -Are you sure you would like to enable this feature? - - - - Disabling this feature will enable the Printing Selector. - -You can now choose printings on a per-deck basis in the Deck Editor and configure which printing gets added to a deck by default by pinning it in the Printing Selector. - -You can also use the Set Manager to adjust custom sort order for printings in the Printing Selector (other sort orders like alphabetical or release date are available). - -Are you sure you would like to disable this feature? - Disabling this feature will enable the Printing Selector. - -You can now choose printings on a per-deck basis in the Deck Editor and configure which printing gets added to a deck by default by pinning it in the Printing Selector. - -You can also use the Set Manager to adjust custom sort order for printings in the Printing Selector (other sort orders like alphabetical or release date are available). - -Are you sure you would like to disable this feature? - - - - Confirm Change - Confirm Change - - - + Theme settings Theme settings - + Current theme: Current theme: - + Open themes folder Open themes folder - + Home tab background source: Home tab background source: - + Home tab background shuffle frequency: Home tab background shuffle frequency: - + Disabled Disabled - + + Display card name of background in bottom right: + Display card name of background in bottom right: + + + Menu settings Menu settings - + Show keyboard shortcuts in right-click menus Show keyboard shortcuts in right-click menus - + Show game filter toolbar above list in room tab Show game filter toolbar above list in room tab - + Card rendering Card rendering - + Display card names on cards having a picture Display card names on cards having a picture - + Auto-Rotate cards with sideways layout Auto-Rotate cards with sideways layout - + Override all card art with personal set preference (Pre-ProviderID change behavior) Override all card art with personal set preference (Pre-ProviderID change behavior) - + Bump sets that the deck contains cards from to the top in the printing selector Bump sets that the deck contains cards from to the top in the printing selector - + Scale cards on mouse over Scale cards on mouse over - + Use rounded card corners Use rounded card corners - + Minimum overlap percentage of cards on the stack and in vertical hand Minimum overlap percentage of cards on the stack and in vertical hand - + Maximum initial height for card view window: Maximum initial height for card view window: - - + + rows rows - + Maximum expanded height for card view window: Maximum expanded height for card view window: - + Card counters Card counters - + Counter %1 Counter %1 - + Hand layout Hand layout - + Display hand horizontally (wastes space) Display hand horizontally (wastes space) - + Enable left justification Enable left justification - + Table grid layout Table grid layout - + Invert vertical coordinate Invert vertical coordinate - + Minimum player count for multi-column layout: Minimum player count for multi-column layout: - + Maximum font size for information displayed on cards: Maximum font size for information displayed on cards: @@ -336,7 +302,12 @@ Are you sure you would like to disable this feature? ArchidektApiResponseDeckDisplayWidget - + + Back to results + Back to results + + + Open Deck in Deck Editor Open Deck in Deck Editor @@ -656,22 +627,22 @@ This is only saved for moderators and cannot be seen by the banned person. CardInfoPictureWidget - + View related cards View related cards - + Add card to deck Add card to deck - + Mainboard Mainboard - + Sideboard Sideboard @@ -707,124 +678,124 @@ This is only saved for moderators and cannot be seen by the banned person. CardMenu - + Re&veal to... Re&veal to... - + &All players &All players - + View related cards View related cards - + Token: Token: - + All tokens All tokens - + &Select All &Select All - + S&elect Row S&elect Row - + S&elect Column S&elect Column - + &Play &Play - + &Hide &Hide - + Play &Face Down Play &Face Down - + &Tap / Untap Turn sideways or back again &Tap / Untap - - Toggle &normal untapping - Toggle &normal untapping + + Skip &untapping + Skip &untapping - + T&urn Over Turn face up/face down T&urn Over - + &Peek at card face &Peek at card face - + &Clone &Clone - + Attac&h to card... Attac&h to card... - + Unattac&h Unattac&h - + &Draw arrow... &Draw arrow... - + &Set annotation... &Set annotation... - + Ca&rd counters Ca&rd counters - + &Add counter (%1) &Add counter (%1) - + &Remove counter (%1) &Remove counter (%1) - + &Set counters (%1)... &Set counters (%1)... @@ -840,133 +811,133 @@ This is only saved for moderators and cannot be seen by the banned person. CardZoneLogic - + their hand nominative their hand - + %1's hand nominative %1's hand - + their library look at zone their library - + %1's library look at zone %1's library - + of their library top cards of zone, of their library - + of %1's library top cards of zone of %1's library - + their library reveal zone their library - + %1's library reveal zone %1's library - + their library shuffle their library - + %1's library shuffle %1's library - - - their library - nominative - their library - - - - %1's library - nominative - %1's library - + their library + nominative + their library + + + + %1's library + nominative + %1's library + + + their graveyard nominative their graveyard - + %1's graveyard nominative %1's graveyard - + their exile nominative their exile - + %1's exile nominative %1's exile - - - their sideboard - look at zone - their sideboard - - - - %1's sideboard - look at zone - %1's sideboard - their sideboard - nominative + look at zone their sideboard %1's sideboard + look at zone + %1's sideboard + + + + their sideboard + nominative + their sideboard + + + + %1's sideboard nominative %1's sideboard - + their custom zone '%1' nominative their custom zone '%1' - + %1's custom zone '%2' nominative %1's custom zone '%2' @@ -1028,7 +999,7 @@ This is only saved for moderators and cannot be seen by the banned person. DeckEditorCardDatabaseDockWidget - + Card Database Card Database @@ -1036,7 +1007,7 @@ This is only saved for moderators and cannot be seen by the banned person. DeckEditorCardInfoDockWidget - + Card Info Card Info @@ -1059,32 +1030,32 @@ This is only saved for moderators and cannot be seen by the banned person.Add to Sideboard - + Select Printing Select Printing - + Show on EDHRec (Commander) Show on EDHRec (Commander) - + Show on EDHRec (Card) Show on EDHRec (Card) - + Show Related cards Show Related cards - + Add card to &maindeck Add card to &maindeck - + Add card to &sideboard Add card to &sideboard @@ -1092,32 +1063,32 @@ This is only saved for moderators and cannot be seen by the banned person. DeckEditorDeckDockWidget - + Loading Database... Loading Database... - + Banner Card Banner Card - + Main Type Main Type - + Mana Cost Mana Cost - + Colors Colors - + Select Printing Select Printing @@ -1190,17 +1161,17 @@ This is only saved for moderators and cannot be seen by the banned person. DeckEditorFilterDockWidget - + Filters Filters - + &Clear all filters &Clear all filters - + Delete selected Delete selected @@ -1323,7 +1294,7 @@ This is only saved for moderators and cannot be seen by the banned person. DeckEditorPrintingSelectorDockWidget - + Printing Selector Printing Selector @@ -1331,166 +1302,166 @@ This is only saved for moderators and cannot be seen by the banned person. DeckEditorSettingsPage - - + + Update Spoilers Update Spoilers - - + + Success Success - + Download URLs have been reset. Download URLs have been reset. - + Downloaded card pictures have been reset. Downloaded card pictures have been reset. - + Error Error - + One or more downloaded card pictures could not be cleared. One or more downloaded card pictures could not be cleared. - + Add URL Add URL - - + + URL: URL: - - + + Edit URL Edit URL - + Network Cache Size: Network Cache Size: - + Redirect Cache TTL: Redirect Cache TTL: - + How long cached redirects for urls are valid for. How long cached redirects for urls are valid for. - + Picture Cache Size: Picture Cache Size: - + Add New URL Add New URL - + Remove URL Remove URL - + Day(s) Day(s) - + Updating... Updating... - + Choose path Choose path - + URL Download Priority URL Download Priority - + Spoilers Spoilers - + Download Spoilers Automatically Download Spoilers Automatically - + Spoiler Location: Spoiler Location: - + Last Change Last Change - + Spoilers download automatically on launch Spoilers download automatically on launch - + Press the button to manually update without relaunching Press the button to manually update without relaunching - + Do not close settings until manual update is complete Do not close settings until manual update is complete - + Download card pictures on the fly Download card pictures on the fly - + How to add a custom URL How to add a custom URL - + Delete Downloaded Images Delete Downloaded Images - + Reset Download URLs Reset Download URLs - + On-disk cache for downloaded pictures On-disk cache for downloaded pictures - + In-memory cache for pictures not currently on screen In-memory cache for pictures not currently on screen @@ -1498,32 +1469,32 @@ This is only saved for moderators and cannot be seen by the banned person. DeckListHistoryManagerWidget - + Undo Undo - + Redo Redo - + Undo/Redo history Undo/Redo history - + Click on an entry to revert to that point in the history. Click on an entry to revert to that point in the history. - + [redo] [redo] - + [undo] [undo] @@ -1531,27 +1502,27 @@ This is only saved for moderators and cannot be seen by the banned person. DeckListModel - + Count Count - + Set Set - + Number Number - + Provider ID Provider ID - + Card Card @@ -1559,12 +1530,12 @@ This is only saved for moderators and cannot be seen by the banned person. DeckLoader - + Common deck formats (%1) Common deck formats (%1) - + All files (*.*) All files (*.*) @@ -1661,94 +1632,94 @@ This is only saved for moderators and cannot be seen by the banned person. DeckPreviewWidget - + Banner Card Banner Card - + Open in deck editor Open in deck editor - + Edit Tags Edit Tags - + Rename Deck Rename Deck - + Save Deck to Clipboard Save Deck to Clipboard - + Annotated Annotated - + Annotated (No set info) Annotated (No set info) - + Not Annotated Not Annotated - + Not Annotated (No set info) Not Annotated (No set info) - + Rename File Rename File - + Delete File Delete File - + Set Banner Card Set Banner Card - - + + New name: New name: - - + + Error Error - + Rename failed Rename failed - + Delete file Delete file - + Are you sure you want to delete the selected file? Are you sure you want to delete the selected file? - + Delete failed Delete failed @@ -1781,32 +1752,32 @@ This is only saved for moderators and cannot be seen by the banned person.Set format to %1 - + Added (%1): %2 (%3) %4 Added (%1): %2 (%3) %4 - + Moved to %1 1 × "%2" (%3) Moved to %1 1 × "%2" (%3) - + Removed "%1" (all copies) Removed "%1" (all copies) - + %1 1 × "%2" (%3) %1 1 × "%2" (%3) - + Added Added - + Removed Removed @@ -1873,30 +1844,30 @@ This is only saved for moderators and cannot be seen by the banned person.Sideboard locked - - + + Error Error - + The selected file could not be loaded. The selected file could not be loaded. - + Deck is greater than maximum file size. Deck is greater than maximum file size. - + Are you sure you want to force start? This will kick all non-ready players from the game. Are you sure you want to force start? This will kick all non-ready players from the game. - + Cockatrice Cockatrice @@ -2391,17 +2362,17 @@ To remove your current avatar, confirm without choosing a new image. DlgEditDeckInClipboard - + Edit deck in clipboard Edit deck in clipboard - + Error Error - + Invalid deck list. Invalid deck list. @@ -2902,17 +2873,17 @@ Make sure to enable the 'Token' set in the "Manage sets" dia DlgLoadDeckFromClipboard - + Load deck from clipboard Load deck from clipboard - + Error Error - + Invalid deck list. Invalid deck list. @@ -2920,45 +2891,45 @@ Make sure to enable the 'Token' set in the "Manage sets" dia DlgLoadDeckFromWebsite - + Paste a link to a decklist site here to import it. (Archidekt, Deckstats, Moxfield, and TappedOut are supported.) Paste a link to a decklist site here to import it. (Archidekt, Deckstats, Moxfield, and TappedOut are supported.) - - - - - + + + + + Load Deck from Website Load Deck from Website - + No parser available for this deck provider. (Archidekt, Deckstats, Moxfield, and TappedOut are supported.) No parser available for this deck provider. (Archidekt, Deckstats, Moxfield, and TappedOut are supported.) - + Network error: %1 Network error: %1 - + Received empty deck data. Received empty deck data. - + Failed to parse deck data: %1 Failed to parse deck data: %1 - + The provided URL is not recognized as a valid deck URL. Valid deck URLs look like this: @@ -2983,40 +2954,73 @@ https://tappedout.net/mtg-decks/your-deck-name/ Load deck + + DlgLocalGameOptions + + + Players: + Players: + + + + General + General + + + + Starting life total: + Starting life total: + + + + Game setup options + Game setup options + + + + Remember settings + Remember settings + + + + Local game options + Local game options + + DlgMoveTopCardsUntil - + Card name (or search expressions): Card name (or search expressions): - + Number of hits: Number of hits: - + Auto play hits Auto play hits - + Put top cards on stack until... Put top cards on stack until... - + No cards matching the search expression exists in the card database. Proceed anyways? No cards matching the search expression exists in the card database. Proceed anyways? - + Cockatrice Cockatrice - + Invalid filter Invalid filter @@ -3178,12 +3182,12 @@ Your email will be used to verify your account. DlgSettings - + Unknown Error loading card database Unknown Error loading card database - + Your card database is invalid. Cockatrice may not function correctly with an invalid database @@ -3200,7 +3204,7 @@ You may need to rerun oracle to update your card database. Would you like to change your database location setting? - + Your card database version is too old. This can cause problems loading card information or images @@ -3217,7 +3221,7 @@ Usually this can be fixed by rerunning oracle to to update your card database. Would you like to change your database location setting? - + Your card database did not finish loading Please file a ticket at https://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached @@ -3230,7 +3234,7 @@ Please file a ticket at https://github.com/Cockatrice/Cockatrice/issues with you Would you like to change your database location setting? - + File Error loading your card database. Would you like to change your database location setting? @@ -3239,7 +3243,7 @@ Would you like to change your database location setting? Would you like to change your database location setting? - + Your card database was loaded but contains no cards. Would you like to change your database location setting? @@ -3248,7 +3252,7 @@ Would you like to change your database location setting? Would you like to change your database location setting? - + Unknown card database load status Please file a ticket at https://github.com/Cockatrice/Cockatrice/issues @@ -3261,59 +3265,59 @@ Please file a ticket at https://github.com/Cockatrice/Cockatrice/issues Would you like to change your database location setting? - - - + + + Error Error - + The path to your deck directory is invalid. Would you like to go back and set the correct path? The path to your deck directory is invalid. Would you like to go back and set the correct path? - + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? The path to your card pictures directory is invalid. Would you like to go back and set the correct path? - + Settings Settings - + General General - + Appearance Appearance - + User Interface User Interface - + Card Sources Card Sources - + Chat Chat - + Sound Sound - + Shortcuts Shortcuts @@ -3630,67 +3634,67 @@ You may have to manually download the new version. DrawProbabilityWidget - + Draw Probability Draw Probability - + Probability of drawing Probability of drawing - + Card Name Card Name - + Type Type - + Subtype Subtype - + Mana Value Mana Value - + At least At least - + Exactly Exactly - + card(s) having drawn at least card(s) having drawn at least - + cards cards - + Category Category - + Qty Qty - + Odds (%) Odds (%) @@ -4138,143 +4142,143 @@ You may have to manually download the new version. GeneralSettingsPage - + Reset all paths Reset all paths - + All paths have been reset All paths have been reset - - - - - - - + + + + + + + Choose path Choose path - + Personal settings Personal settings - + Language: Language: - + Paths (editing disabled in portable mode) Paths (editing disabled in portable mode) - + Paths Paths - + How to help with translations How to help with translations - + Decks directory: Decks directory: - + Filters directory: Filters directory: - + Replays directory: Replays directory: - + Pictures directory: Pictures directory: - + Card database: Card database: - + Custom database directory: Custom database directory: - + Token database: Token database: - + Update channel Update channel - + Check for client updates on startup Check for client updates on startup - + Check for card database updates on startup Check for card database updates on startup - + Don't check Don't check - + Prompt for update Prompt for update - + Always update in the background Always update in the background - + Check for card database updates every Check for card database updates every - + days days - + Notify if a feature supported by the server is missing in my client Notify if a feature supported by the server is missing in my client - + Automatically run Oracle when running a new version of Cockatrice Automatically run Oracle when running a new version of Cockatrice - + Show tips on startup Show tips on startup - + Last update check on %1 (%2 days ago) Last update check on %1 (%2 days ago) @@ -4282,47 +4286,47 @@ You may have to manually download the new version. GraveyardMenu - + &Graveyard &Graveyard - + &View graveyard &View graveyard - + &Move graveyard to... &Move graveyard to... - + &Top of library &Top of library - + &Bottom of library &Bottom of library - + &All players &All players - + &Hand &Hand - + &Exile &Exile - + Reveal random card to... Reveal random card to... @@ -4330,88 +4334,88 @@ You may have to manually download the new version. HandMenu - + &Hand &Hand - + &View hand &View hand - + Sort hand by... Sort hand by... - + Name Name - + Type Type - + Mana Value Mana Value - + Take &mulligan (Choose hand size) Take &mulligan (Choose hand size) - + Take mulligan (Same hand size) Take mulligan (Same hand size) - + Take mulligan (Hand size - 1) Take mulligan (Hand size - 1) - + &Move hand to... &Move hand to... - + &Top of library &Top of library - + &Bottom of library &Bottom of library - + &Graveyard &Graveyard - + &Exile &Exile - + &Reveal hand to... &Reveal hand to... - - + + All players All players - + Reveal r&andom card to... Reveal r&andom card to... @@ -4419,52 +4423,52 @@ You may have to manually download the new version. HomeWidget - + Create New Deck Create New Deck - + Browse Decks Browse Decks - + Browse Card Database Browse Card Database - + Browse EDHRec Browse EDHRec - + Browse Archidekt Browse Archidekt - + View Replays View Replays - + Quit Quit - + Connecting... Connecting... - + Connect Connect - + Play Play @@ -4472,193 +4476,213 @@ You may have to manually download the new version. LibraryMenu - + &Library &Library - + &View library &View library - + View &top cards of library... View &top cards of library... - + View bottom cards of library... View bottom cards of library... - + Reveal &library to... Reveal &library to... - + Lend library to... Lend library to... - + Reveal &top cards to... Reveal &top cards to... - + &Top of library... &Top of library... - + &Bottom of library... &Bottom of library... - + &Always reveal top card &Always reveal top card - + &Always look at top card &Always look at top card - + &Open deck in deck editor &Open deck in deck editor - + &Draw card &Draw card - + D&raw cards... D&raw cards... - + &Undo last draw &Undo last draw - + Shuffle Shuffle - + &Play top card &Play top card - + Play top card &face down Play top card &face down - + Put top card on &bottom Put top card on &bottom - + Move top card to grave&yard Move top card to grave&yard - + Move top card to e&xile Move top card to e&xile - + Move top cards to &graveyard... Move top cards to &graveyard... - + + Move top cards to graveyard face down... + Move top cards to graveyard face down... + + + Move top cards to &exile... Move top cards to &exile... - + + Move top cards to exile face down... + Move top cards to exile face down... + + + Put top cards on stack &until... Put top cards on stack &until... - + Shuffle top cards... Shuffle top cards... - + &Draw bottom card &Draw bottom card - + D&raw bottom cards... D&raw bottom cards... - + &Play bottom card &Play bottom card - + Play bottom card &face down Play bottom card &face down - + Move bottom card to grave&yard Move bottom card to grave&yard - + Move bottom card to e&xile Move bottom card to e&xile - + Move bottom cards to &graveyard... Move bottom cards to &graveyard... - + + Move bottom cards to graveyard face down... + Move bottom cards to graveyard face down... + + + Move bottom cards to &exile... Move bottom cards to &exile... - + + Move bottom cards to exile face down... + Move bottom cards to exile face down... + + + Put bottom card on &top Put bottom card on &top - + Shuffle bottom cards... Shuffle bottom cards... - - + + &All players &All players - + Reveal top cards of library Reveal top cards of library - + Number of cards: (max. %1) Number of cards: (max. %1) @@ -4756,18 +4780,8 @@ Will now login. Will now login. - - Number of players - Number of players - - - - Please enter the number of players. - Please enter the number of players. - - - - + + Player %1 Player %1 @@ -4870,8 +4884,8 @@ Will now login. - - + + Error Error @@ -5279,36 +5293,36 @@ Local version is %1, remote version is %2. Show/Hide - + New Version New Version - + Congratulations on updating to Cockatrice %1! Oracle will now launch to update your card database. Congratulations on updating to Cockatrice %1! Oracle will now launch to update your card database. - + Cockatrice installed Cockatrice installed - + Congratulations on installing Cockatrice %1! Oracle will now launch to install the initial card database. Congratulations on installing Cockatrice %1! Oracle will now launch to install the initial card database. - + Card database Card database - + Cockatrice is unable to load the card database. Do you want to update your card database now? If unsure or first time user, choose "Yes" @@ -5317,29 +5331,29 @@ Do you want to update your card database now? If unsure or first time user, choose "Yes" - - + + Yes Yes - - + + No No - + Open settings Open settings - + New sets found New sets found - + %n new set(s) found in the card database Set code(s): %1 Do you want to enable it/them? @@ -5350,17 +5364,17 @@ Set codes: %1 Do you want to enable them? - + View sets View sets - + Welcome Welcome - + Hi! It seems like you're running this version of Cockatrice for the first time. All the sets in the card database have been enabled. Read more about changing the set order or disabling specific sets and consequent effects in the "Manage Sets" dialog. @@ -5369,65 +5383,65 @@ All the sets in the card database have been enabled. Read more about changing the set order or disabling specific sets and consequent effects in the "Manage Sets" dialog. - - + + Information Information - + A card database update is already running. A card database update is already running. - + Unable to run the card database updater: Unable to run the card database updater: - + Card database update running. Card database update running. - + Failed to start. The file might be missing, or permissions might be incorrect. Failed to start. The file might be missing, or permissions might be incorrect. - + The process crashed some time after starting successfully. The process crashed some time after starting successfully. - + Timed out. The process took too long to respond. The last waitFor...() function timed out. Timed out. The process took too long to respond. The last waitFor...() function timed out. - + An error occurred when attempting to write to the process. For example, the process may not be running, or it may have closed its input channel. An error occurred when attempting to write to the process. For example, the process may not be running, or it may have closed its input channel. - + An error occurred when attempting to read from the process. For example, the process may not be running. An error occurred when attempting to read from the process. For example, the process may not be running. - + Unknown error occurred. Unknown error occurred. - + The card database updater exited with an error: %1 The card database updater exited with an error: %1 - + This server supports additional features that your client doesn't have. This is most likely not a problem, but this message might mean there is a new version of Cockatrice available or this server is running a custom or pre-release version. @@ -5438,55 +5452,55 @@ This is most likely not a problem, but this message might mean there is a new ve To update your client, go to Help -> Check for Updates. - - - - - + + + + + Load sets/cards Load sets/cards - + Selected file cannot be found. Selected file cannot be found. - + You can only import XML databases at this time. You can only import XML databases at this time. - + The new sets/cards have been added successfully. Cockatrice will now reload the card database. The new sets/cards have been added successfully. Cockatrice will now reload the card database. - + Sets/cards failed to import. Sets/cards failed to import. - - - + + + Reset Password Reset Password - + Your password has been reset successfully, you can now log in using the new credentials. Your password has been reset successfully, you can now log in using the new credentials. - + Failed to reset user account password, please contact the server operator to reset your password. Failed to reset user account password, please contact the server operator to reset your password. - + Activation request received, please check your email for an activation token. Activation request received, please check your email for an activation token. @@ -5537,7 +5551,7 @@ Cockatrice will now reload the card database. ManaBaseWidget - + Mana Base Mana Base @@ -5691,590 +5705,610 @@ Cockatrice will now reload the card database. MessageLogWidget - + from play from play - + from their graveyard from their graveyard - + from exile from exile - + from their hand from their hand - + the top card of %1's library the top card of %1's library - + the top card of their library the top card of their library - + from the top of %1's library from the top of %1's library - + from the top of their library from the top of their library - + the bottom card of %1's library the bottom card of %1's library - + the bottom card of their library the bottom card of their library - + from the bottom of %1's library from the bottom of %1's library - + from the bottom of their library from the bottom of their library - + from %1's library from %1's library - + from their library from their library - + from sideboard from sideboard - + from the stack from the stack - + from custom zone '%1' from custom zone '%1' - + %1 is now keeping the top card %2 revealed. %1 is now keeping the top card %2 revealed. - + %1 is not revealing the top card %2 any longer. %1 is not revealing the top card %2 any longer. - + %1 can now look at top card %2 at any time. %1 can now look at top card %2 at any time. - + %1 no longer can look at top card %2 at any time. %1 no longer can look at top card %2 at any time. - + %1 attaches %2 to %3's %4. %1 attaches %2 to %3's %4. - + %1 has conceded the game. %1 has conceded the game. - + %1 has unconceded the game. %1 has unconceded the game. - + %1 has restored connection to the game. %1 has restored connection to the game. - + %1 has lost connection to the game. %1 has lost connection to the game. - + %1 points from their %2 to themselves. %1 points from their %2 to themselves. - + %1 points from their %2 to %3. %1 points from their %2 to %3. - + %1 points from %2's %3 to themselves. %1 points from %2's %3 to themselves. - + %1 points from %2's %3 to %4. %1 points from %2's %3 to %4. - + %1 points from their %2 to their %3. %1 points from their %2 to their %3. - + %1 points from their %2 to %3's %4. %1 points from their %2 to %3's %4. - + %1 points from %2's %3 to their own %4. %1 points from %2's %3 to their own %4. - + %1 points from %2's %3 to %4's %5. %1 points from %2's %3 to %4's %5. - + %1 creates a face down token. %1 creates a face down token. - + %1 creates token: %2%3. %1 creates token: %2%3. - + %1 has loaded a deck (%2). %1 has loaded a deck (%2). - + %1 has loaded a deck with %2 sideboard cards (%3). %1 has loaded a deck with %2 sideboard cards (%3). - + %1 destroys %2. %1 destroys %2. - + a card a card - + %1 gives %2 control over %3. %1 gives %2 control over %3. - + %1 puts %2 into play%3 face down. %1 puts %2 into play%3 face down. - + %1 puts %2 into play%3. %1 puts %2 into play%3. - + + %1 puts %2%3 into their graveyard face down. + %1 puts %2%3 into their graveyard face down. + + + %1 puts %2%3 into their graveyard. %1 puts %2%3 into their graveyard. + + + %1 exiles %2%3 face down. + %1 exiles %2%3 face down. + %1 exiles %2%3. %1 exiles %2%3. - + %1 moves %2%3 to their hand. %1 moves %2%3 to their hand. - + %1 puts %2%3 into their library. %1 puts %2%3 into their library. - + %1 puts %2%3 onto the bottom of their library. %1 puts %2%3 onto the bottom of their library. - + %1 puts %2%3 on top of their library. %1 puts %2%3 on top of their library. - + %1 puts %2%3 into their library %4 cards from the top. %1 puts %2%3 into their library %4 cards from the top. - + %1 moves %2%3 to sideboard. %1 moves %2%3 to sideboard. - + + %1 plays %2%3 face down. + %1 plays %2%3 face down. + + + %1 plays %2%3. %1 plays %2%3. - + + %1 moves %2%3 to custom zone '%4' face down. + %1 moves %2%3 to custom zone '%4' face down. + + + %1 moves %2%3 to custom zone '%4'. %1 moves %2%3 to custom zone '%4'. - + %1 tries to draw from an empty library %1 tries to draw from an empty library - + %1 draws %2 card(s). %1 draws %2 card.%1 draws %2 cards. - + %1 is looking at %2. %1 is looking at %2. - + %1 is looking at the %4 %3 card(s) %2. top card for singular, top %3 cards for plural %1 is looking at the %4 %3 card %2.%1 is looking at the %4 %3 cards %2. - + bottom bottom - + top top - + %1 turns %2 face-down. %1 turns %2 face-down. - + %1 turns %2 face-up. %1 turns %2 face-up. - + The game has been closed. The game has been closed. - + The game has started. The game has started. - + You are flooding the game. Please wait a couple of seconds. You are flooding the game. Please wait a couple of seconds. - + %1 has joined the game. %1 has joined the game. - + %1 is now watching the game. %1 is now watching the game. - + You have been kicked out of the game. You have been kicked out of the game. - + %1 has left the game (%2). %1 has left the game (%2). - + %1 is not watching the game any more (%2). %1 is not watching the game any more (%2). - + %1 is not ready to start the game any more. %1 is not ready to start the game any more. - + %1 shuffles their deck and draws a new hand of %2 card(s). %1 shuffles their deck and draws a card.%1 shuffles their deck and draws a new hand of %2 cards. - + %1 shuffles their deck and draws a new hand. %1 shuffles their deck and draws a new hand. - + You are watching a replay of game #%1. You are watching a replay of game #%1. - + %1 is ready to start the game. %1 is ready to start the game. - + cards an unknown amount of cards cards - + %1 card(s) a card for singular, %1 cards for plural one card%1 cards - + %1 lends %2 to %3. %1 lends %2 to %3. - + %1 reveals %2 to %3. %1 reveals %2 to %3. - + %1 reveals %2. %1 reveals %2. - + %1 randomly reveals %2%3 to %4. %1 randomly reveals %2%3 to %4. - + %1 randomly reveals %2%3. %1 randomly reveals %2%3. - + %1 peeks at face down card #%2. %1 peeks at face down card #%2. - + %1 peeks at face down card #%2: %3. %1 peeks at face down card #%2: %3. - + %1 reveals %2%3 to %4. %1 reveals %2%3 to %4. - + %1 reveals %2%3. %1 reveals %2%3. - + %1 reversed turn order, now it's %2. %1 reversed turn order, now it's %2. - + reversed reversed - + normal normal - + Heads Heads - + Tails Tails - + %1 flipped a coin. It landed as %2. %1 flipped a coin. It landed as %2. - + %1 rolls a %2 with a %3-sided die. %1 rolls a %2 with a %3-sided die. - + %1 flips %2 coins. There are %3 heads and %4 tails. %1 flips %2 coins. There are %3 heads and %4 tails. - + %1 rolls a %2-sided dice %3 times: %4. %1 rolls a %2-sided dice %3 times: %4. - + %1's turn. %1's turn. - + %1 sets annotation of %2 to %3. %1 sets annotation of %2 to %3. - + %1 places %2 "%3" counter(s) on %4 (now %5). %1 places %2 "%3" counter on %4 (now %5).%1 places %2 "%3" counters on %4 (now %5). - + %1 removes %2 "%3" counter(s) from %4 (now %5). %1 removes %2 "%3" counter from %4 (now %5).%1 removes %2 "%3" counters from %4 (now %5). - + %1 sets counter %2 to %3 (%4%5). %1 sets counter %2 to %3 (%4%5). - + %1 sets %2 to not untap normally. %1 sets %2 to not untap normally. - + %1 sets %2 to untap normally. %1 sets %2 to untap normally. - + %1 removes the PT of %2. %1 removes the PT of %2. - + %1 changes the PT of %2 from nothing to %4. %1 changes the PT of %2 from nothing to %4. - + %1 changes the PT of %2 from %3 to %4. %1 changes the PT of %2 from %3 to %4. - + %1 has locked their sideboard. %1 has locked their sideboard. - + %1 has unlocked their sideboard. %1 has unlocked their sideboard. - + %1 taps their permanents. %1 taps their permanents. - + %1 untaps their permanents. %1 untaps their permanents. - + %1 taps %2. %1 taps %2. - + %1 untaps %2. %1 untaps %2. - + %1 shuffles %2. %1 shuffles %2. - + %1 shuffles the bottom %3 cards of %2. %1 shuffles the bottom %3 cards of %2. - + %1 shuffles the top %3 cards of %2. %1 shuffles the top %3 cards of %2. - + %1 shuffles cards %3 - %4 of %2. %1 shuffles cards %3 - %4 of %2. - + %1 unattaches %2. %1 unattaches %2. - + %1 undoes their last draw. %1 undoes their last draw. - + %1 undoes their last draw (%2). %1 undoes their last draw (%2). @@ -6282,110 +6316,110 @@ Cockatrice will now reload the card database. MessagesSettingsPage - + Word1 Word2 Word3 Word1 Word2 Word3 - + Add New Message Add New Message - + Edit Message Edit Message - + Remove Message Remove Message - + Add message Add message - - + + Message: Message: - + Edit message Edit message - + Chat settings Chat settings - + Custom alert words Custom alert words - + Enable chat mentions Enable chat mentions - + Enable mention completer Enable mention completer - + In-game message macros In-game message macros - + How to use in-game message macros How to use in-game message macros - + Ignore chat room messages sent by unregistered users Ignore chat room messages sent by unregistered users - + Ignore private messages sent by unregistered users Ignore private messages sent by unregistered users - - + + Invert text color Invert text color - + Enable desktop notifications for private messages Enable desktop notifications for private messages - + Enable desktop notification for mentions Enable desktop notification for mentions - + Enable room message history on join Enable room message history on join - - + + (Color is hexadecimal) (Color is hexadecimal) - + Separate words with a space, alphanumeric characters only Separate words with a space, alphanumeric characters only @@ -6398,32 +6432,37 @@ Cockatrice will now reload the card database. Move to - + &Top of library in random order &Top of library in random order - + X cards from the top of library... X cards from the top of library... - + &Bottom of library in random order &Bottom of library in random order - + + T&able + T&able + + + &Hand &Hand - + &Graveyard &Graveyard - + &Exile &Exile @@ -6565,57 +6604,57 @@ Cockatrice will now reload the card database. PhasesToolbar - + Untap step Untap step - + Upkeep step Upkeep step - + Draw step Draw step - + First main phase First main phase - + Beginning of combat step Beginning of combat step - + Declare attackers step Declare attackers step - + Declare blockers step Declare blockers step - + Combat damage step Combat damage step - + End of combat step End of combat step - + Second main phase Second main phase - + End of turn step End of turn step @@ -6632,134 +6671,138 @@ Cockatrice will now reload the card database. PlayerActions - + View top cards of library View top cards of library - - - - - - - - - - - + + + + + + + + + Number of cards: (max. %1) Number of cards: (max. %1) - + View bottom cards of library View bottom cards of library - + Shuffle top cards of library Shuffle top cards of library - + Shuffle bottom cards of library Shuffle bottom cards of library - + Draw hand Draw hand - + 0 and lower are in comparison to current hand size 0 and lower are in comparison to current hand size - + Draw cards Draw cards + + + + + + grave + grave + - Move top cards to grave - Move top cards to grave + + + + exile + exile - - Move top cards to exile - Move top cards to exile + + Move top cards to %1 + Move top cards to %1 - - Move bottom cards to grave - Move bottom cards to grave + + Move bottom cards to %1 + Move bottom cards to %1 - - Move bottom cards to exile - Move bottom cards to exile - - - + Draw bottom cards Draw bottom cards - - + + C&reate another %1 token C&reate another %1 token - + Create tokens Create tokens - - + + Number: Number: - + Place card X cards from top of library Place card X cards from top of library - + Which position should this card be placed: Which position should this card be placed: - + (max. %1) (max. %1) - + Change power/toughness Change power/toughness - + Change stats to: Change stats to: - + Set annotation Set annotation - + Please enter the new annotation: Please enter the new annotation: - + Set counters Set counters @@ -6767,48 +6810,69 @@ Cockatrice will now reload the card database. PlayerMenu - + Player "%1" Player "%1" - + &Counters &Counters + + + PrintingDisabledInfoWidget - - S&ay - S&ay + + The Printing Selector is disabled because you have currently enabled the setting to override all selected printings with personal set preferences. + +This setting means you'll only see the default printing for each card, instead of being able to select a printing, and will not see the printings other people have selected. + + + The Printing Selector is disabled because you have currently enabled the setting to override all selected printings with personal set preferences. + +This setting means you'll only see the default printing for each card, instead of being able to select a printing, and will not see the printings other people have selected. + + + + + + Enable printings again + Enable printings again PrintingSelector - + Display Navigation Buttons Display Navigation Buttons + + + Printing Selector + Printing Selector + PrintingSelectorCardOverlayWidget - + Preference Preference - + Pin Printing Pin Printing - + Unpin Printing Unpin Printing - + Show Related cards Show Related cards @@ -6857,17 +6921,25 @@ Cockatrice will now reload the card database. Release Date - - + + Descending Descending - + Ascending Ascending + + PrintingSelectorPlaceholderWidget + + + Select a card to view its available printings + Select a card to view its available printings + + PtMenu @@ -7006,6 +7078,45 @@ Cockatrice will now reload the card database. A .cod version of this deck already exists. Overwrite it? A .cod version of this deck already exists. Overwrite it? + + + Enabling this feature will disable the use of the Printing Selector. + +You will not be able to manage printing preferences on a per-deck basis, or see printings other people have selected for their decks. + +You will have to use the Set Manager, available through Card Database -> Manage Sets. + +Are you sure you would like to enable this feature? + Enabling this feature will disable the use of the Printing Selector. + +You will not be able to manage printing preferences on a per-deck basis, or see printings other people have selected for their decks. + +You will have to use the Set Manager, available through Card Database -> Manage Sets. + +Are you sure you would like to enable this feature? + + + + Disabling this feature will enable the Printing Selector. + +You can now choose printings on a per-deck basis in the Deck Editor and configure which printing gets added to a deck by default by pinning it in the Printing Selector. + +You can also use the Set Manager to adjust custom sort order for printings in the Printing Selector (other sort orders like alphabetical or release date are available). + +Are you sure you would like to disable this feature? + Disabling this feature will enable the Printing Selector. + +You can now choose printings on a per-deck basis in the Deck Editor and configure which printing gets added to a deck by default by pinning it in the Printing Selector. + +You can also use the Set Manager to adjust custom sort order for printings in the Printing Selector (other sort orders like alphabetical or release date are available). + +Are you sure you would like to disable this feature? + + + + Confirm Change + Confirm Change + QPlatformTheme @@ -7154,37 +7265,37 @@ Cockatrice will now reload the card database. RfgMenu - + &Exile &Exile - + &View exile &View exile - + &Move exile to... &Move exile to... - + &Top of library &Top of library - + &Bottom of library &Bottom of library - + &Hand &Hand - + &Graveyard &Graveyard @@ -7227,6 +7338,14 @@ Cockatrice will now reload the card database. Games + + SayMenu + + + S&ay + S&ay + + SequenceEdit @@ -7291,53 +7410,53 @@ Cockatrice will now reload the card database. ShortcutSettingsPage - - + + Restore all default shortcuts Restore all default shortcuts - + Do you really want to restore all default shortcuts? Do you really want to restore all default shortcuts? - + Clear all default shortcuts Clear all default shortcuts - + Do you really want to clear all shortcuts? Do you really want to clear all shortcuts? - + Section: Section: - + Action: Action: - + Shortcut: Shortcut: - + How to set custom shortcuts How to set custom shortcuts - + Clear all shortcuts Clear all shortcuts - + Search by shortcut name Search by shortcut name @@ -7406,27 +7525,27 @@ Please check your shortcut settings! SoundSettingsPage - + Enable &sounds Enable &sounds - + Current sounds theme: Current sounds theme: - + Test system sound engine Test system sound engine - + Sound settings Sound settings - + Master volume Master volume @@ -7642,59 +7761,123 @@ Please check your shortcut settings! TabArchidekt - - + + + Desc. Desc. - + + + AND + AND + + + + + Require ALL selected colors + Require ALL selected colors + + + + + Deck name... + Deck name... + + + + + Owner... + Owner... + + + + + Packages + Packages + + + + + Advanced Filters + Advanced Filters + + + + Bracket: + Bracket: + + + + + Any + Any + + + + + Contains card... + Contains card... + + + + + Commander... + Commander... + + + + + Tag... + Tag... + + + + + Deck Size + Deck Size + + + + Cards: + Cards: + + + + Asc. Asc. - - - Any Bracket - Any Bracket + + Sort by: + Sort by: - - Deck name contains... - Deck name contains... + + Filter by: + Filter by: - - Owner name contains... - Owner name contains... + + Display Settings + Display Settings - - Disabled - Disabled - - - + + Search Search - + + Formats Formats - - Min. # of Cards: - Min. # of Cards: - - - - Page: - Page: - - - + Archidekt: Archidekt: @@ -7702,60 +7885,52 @@ Please check your shortcut settings! TabDeckEditor - + Card Info Card Info - + Deck Deck - + Filters Filters - + &View &View - + Card Database Card Database - + Printing Printing - - - - - + Visible Visible - - - - - + Floating Floating - + Reset layout Reset layout - + Deck: %1 Deck: %1 @@ -7763,61 +7938,55 @@ Please check your shortcut settings! TabDeckEditorVisual - + Visual Deck: %1 Visual Deck: %1 - + &Visual Deck Editor &Visual Deck Editor - - + + Card Info Card Info - - + + Deck Deck - - + + Filters Filters - + &View &View - + Printing Printing - - - - + Visible Visible - - - - + Floating Floating - + Reset layout Reset layout @@ -7882,7 +8051,7 @@ Please check your shortcut settings! - + New folder New folder @@ -7964,18 +8133,18 @@ Please enter a name: Are you sure you want to delete the selected files? - + Delete remote decks Delete remote decks - + Are you sure you want to delete the selected decks? Are you sure you want to delete the selected decks? - + Name of new folder: Name of new folder: @@ -7993,12 +8162,12 @@ Please enter a name: Visual Deck Storage - + Error Error - + Could not open deck at %1 Could not open deck at %1 @@ -8047,197 +8216,191 @@ Please enter a name: TabGame - - - + + + Replay Replay - - + + Game Game - - + + Player List Player List - - + + Card Info Card Info - - + + Messages Messages - - + + Replay Timeline Replay Timeline - + &Phases &Phases - + &Game &Game - + Next &phase Next &phase - + Next phase with &action Next phase with &action - + Next &turn Next &turn - + Reverse turn order Reverse turn order - + &Remove all local arrows &Remove all local arrows - + Rotate View Cl&ockwise Rotate View Cl&ockwise - + Rotate View Co&unterclockwise Rotate View Co&unterclockwise - + Game &information Game &information - + Un&concede Un&concede - - - + + + &Concede &Concede - + &Leave game &Leave game - + C&lose replay C&lose replay - + &Focus Chat &Focus Chat - + &Say: &Say: - + Selected cards Selected cards - + &View &View - - - - + Visible Visible - - - - + Floating Floating - + Reset layout Reset layout - + Concede Concede - + Are you sure you want to concede this game? Are you sure you want to concede this game? - + Unconcede Unconcede - + You have already conceded. Do you want to return to this game? You have already conceded. Do you want to return to this game? - + Leave game Leave game - + Are you sure you want to leave this game? Are you sure you want to leave this game? - + A player has joined game #%1 A player has joined game #%1 - + %1 has joined the game %1 has joined the game - + You have been kicked out of the game. You have been kicked out of the game. @@ -9340,142 +9503,152 @@ Please refrain from engaging in this activity or further actions may be taken ag UserInterfaceSettingsPage - + General interface settings General interface settings - + &Double-click cards to play them (instead of single-click) &Double-click cards to play them (instead of single-click) - + &Clicking plays all selected cards (instead of just the clicked card) &Clicking plays all selected cards (instead of just the clicked card) - + &Play all nonlands onto the stack (not the battlefield) by default &Play all nonlands onto the stack (not the battlefield) by default - + Do not delete &arrows inside of subphases Do not delete &arrows inside of subphases - + Close card view window when last card is removed Close card view window when last card is removed - + Auto focus search bar when card view window is opened Auto focus search bar when card view window is opened - + Annotate card text on tokens Annotate card text on tokens - + + Show selection counter during drag selection + Show selection counter during drag selection + + + + Show total selection counter + Show total selection counter + + + Use tear-off menus, allowing right click menus to persist on screen Use tear-off menus, allowing right click menus to persist on screen - + Notifications settings Notifications settings - + Enable notifications in taskbar Enable notifications in taskbar - + Notify in the taskbar for game events while you are spectating Notify in the taskbar for game events while you are spectating - + Notify in the taskbar when users in your buddy list connect Notify in the taskbar when users in your buddy list connect - + Animation settings Animation settings - + &Tap/untap animation &Tap/untap animation - + Deck editor/storage settings Deck editor/storage settings - + Open deck in new tab by default Open deck in new tab by default - + Use visual deck storage in game lobby Use visual deck storage in game lobby - + Use selection animation for Visual Deck Storage Use selection animation for Visual Deck Storage - + When adding a tag in the visual deck storage to a .txt deck: When adding a tag in the visual deck storage to a .txt deck: - + do nothing do nothing - + ask to convert to .cod ask to convert to .cod - + always convert to .cod always convert to .cod - + Default deck editor type Default deck editor type - + Classic Deck Editor Classic Deck Editor - + Visual Deck Editor Visual Deck Editor - + Replay settings Replay settings - + Buffer time for backwards skip via shortcut: Buffer time for backwards skip via shortcut: @@ -9539,24 +9712,25 @@ Please refrain from engaging in this activity or further actions may be taken ag VisualDatabaseDisplayColorFilterWidget - - Mode: Exact Match - Mode: Exact Match + + Exact match + Exact match + + + + Includes + Includes - Mode: Includes - Mode: Includes + Include / Exclude + Mode: Includes + Include / Exclude - - Mode: Include/Exclude - Mode: Include/Exclude - - - - Filter mode (AND/OR/NOT conjunctions of filters) - Filter mode (AND/OR/NOT conjunctions of filters) + + How selected and unselected colors are combined in the filter + How selected and unselected colors are combined in the filter @@ -9582,25 +9756,108 @@ Please refrain from engaging in this activity or further actions may be taken ag Enter filename... + + VisualDatabaseDisplayFilterToolbarWidget + + + Sort by + Sort by + + + + Filter by + Filter by + + + + Save and load filters + Save and load filters + + + + Filter by exact card name + Filter by exact card name + + + + Filter by card main-type + Filter by card main-type + + + + Filter by card sub-type + Filter by card sub-type + + + + Filter by set + Filter by set + + + + Filter by format legality + Filter by format legality + + + + Save/Load + Save/Load + + + + Name + Name + + + + Main Type + Main Type + + + + Sub Type + Sub Type + + + + Sets + Sets + + + + Formats + Formats + + VisualDatabaseDisplayFormatLegalityFilterWidget - + + Show formats with at least: + Show formats with at least: + + + + cards + cards + + + Do not display formats with less than this amount of cards in the database Do not display formats with less than this amount of cards in the database - + Filter mode (AND/OR/NOT conjunctions of filters) Filter mode (AND/OR/NOT conjunctions of filters) - + Mode: Exact Match Mode: Exact Match - + Mode: Includes Mode: Includes @@ -9608,22 +9865,32 @@ Please refrain from engaging in this activity or further actions may be taken ag VisualDatabaseDisplayMainTypeFilterWidget - + + Show main types with at least: + Show main types with at least: + + + + cards + cards + + + Do not display card main-types with less than this amount of cards in the database Do not display card main-types with less than this amount of cards in the database - + Filter mode (AND/OR/NOT conjunctions of filters) Filter mode (AND/OR/NOT conjunctions of filters) - + Mode: Exact Match Mode: Exact Match - + Mode: Includes Mode: Includes @@ -9659,7 +9926,7 @@ Please refrain from engaging in this activity or further actions may be taken ag VisualDatabaseDisplayRecentSetFilterSettingsWidget - + Filter to most recent sets Filter to most recent sets @@ -9667,19 +9934,19 @@ Please refrain from engaging in this activity or further actions may be taken ag VisualDatabaseDisplaySetFilterWidget - + Search sets... Search sets... - - + + Mode: Exact Match Mode: Exact Match - - + + Mode: Includes Mode: Includes @@ -9687,27 +9954,37 @@ Please refrain from engaging in this activity or further actions may be taken ag VisualDatabaseDisplaySubTypeFilterWidget - + Search subtypes... Search subtypes... - + + Show sub types with at least: + Show sub types with at least: + + + + cards + cards + + + Do not display card sub-types with less than this amount of cards in the database Do not display card sub-types with less than this amount of cards in the database - + Filter mode (AND/OR/NOT conjunctions of filters) Filter mode (AND/OR/NOT conjunctions of filters) - + Mode: Exact Match Mode: Exact Match - + Mode: Includes Mode: Includes @@ -9721,52 +9998,22 @@ Please refrain from engaging in this activity or further actions may be taken ag - + Visual Visual - + Loading database ... Loading database ... - + Clear all filters Clear all filters - - Sort by: - Sort by: - - - - Filter by: - Filter by: - - - - Save and load filters - Save and load filters - - - - Filter by exact card name - Filter by exact card name - - - - Filter by card sub-type - Filter by card sub-type - - - - Filter by set - Filter by set - - - + Table Table @@ -9774,56 +10021,64 @@ Please refrain from engaging in this activity or further actions may be taken ag VisualDeckDisplayOptionsWidget - + Group by: Group by: - + Change how cards are divided into categories/groups. Change how cards are divided into categories/groups. - + Sort by: Sort by: - + Click and drag to change the sort order within the groups Click and drag to change the sort order within the groups - + Configure how cards are sorted within their groups Configure how cards are sorted within their groups - - + + Toggle Layout: Overlap Toggle Layout: Overlap - + Change how cards are displayed within zones (i.e. overlapped or fully visible.) Change how cards are displayed within zones (i.e. overlapped or fully visible.) - + Toggle Layout: Flat Toggle Layout: Flat + + VisualDeckEditorPlaceholderWidget + + + Add cards using the search bar or database tab to have them appear here + Add cards using the search bar or database tab to have them appear here + + VisualDeckEditorSampleHandWidget - + Draw a new sample hand Draw a new sample hand - + Sample hand size Sample hand size @@ -9831,17 +10086,17 @@ Please refrain from engaging in this activity or further actions may be taken ag VisualDeckEditorWidget - + Type a card name here for suggestions from the database... Type a card name here for suggestions from the database... - + Quick search and add card Quick search and add card - + Search for closest match in the database (with auto-suggestions) and add preferred printing to the deck on pressing enter Search for closest match in the database (with auto-suggestions) and add preferred printing to the deck on pressing enter @@ -9857,47 +10112,52 @@ Please refrain from engaging in this activity or further actions may be taken ag VisualDeckStorageQuickSettingsWidget - + Show Folders Show Folders - + Show Tag Filter Show Tag Filter - + + Show Color Identity + Show Color Identity + + + Show Tags On Deck Previews Show Tags On Deck Previews - + Show Banner Card Selection Option Show Banner Card Selection Option - + Draw unused Color Identities Draw unused Color Identities - + Unused Color Identities Opacity Unused Color Identities Opacity - + Deck tooltip: Deck tooltip: - + None None - + Filepath Filepath @@ -9998,133 +10258,133 @@ Please refrain from engaging in this activity or further actions may be taken ag WndSets - + Move selected set to the top Move selected set to the top - + Move selected set up Move selected set up - + Move selected set down Move selected set down - + Move selected set to the bottom Move selected set to the bottom - + Search by set name, code, or type Search by set name, code, or type - + Default order Default order - + Restore original art priority order Restore original art priority order - + Enable all sets Enable all sets - + Disable all sets Disable all sets - + Enable selected set(s) Enable selected set(s) - + Disable selected set(s) Disable selected set(s) - + Deck Editor Deck Editor - + Use CTRL+A to select all sets in the view. Use CTRL+A to select all sets in the view. - + Only cards in enabled sets will appear in the card list of the deck editor. Only cards in enabled sets will appear in the card list of the deck editor. - + Image priority is decided in the following order: Image priority is decided in the following order: - + first the CUSTOM Folder (%1), then the Enabled Sets in this dialog (Top to Bottom) %1 is a link to the wiki first the CUSTOM Folder (%1), then the Enabled Sets in this dialog (Top to Bottom) - + Include cards rebalanced for Alchemy [requires restart] Include cards rebalanced for Alchemy [requires restart] - + Card Art Card Art - + How to use custom card art How to use custom card art - + Hints Hints - + Note Note - + Sorting by column allows you to find a set while not changing set priority. Sorting by column allows you to find a set while not changing set priority. - + To enable ordering again, click the column header until this message disappears. To enable ordering again, click the column header until this message disappears. - + Use the current sorting as the set priority instead Use the current sorting as the set priority instead - + Sorts the set priority using the same column Sorts the set priority using the same column - + Manage sets Manage sets @@ -10132,72 +10392,72 @@ Please refrain from engaging in this activity or further actions may be taken ag ZoneViewWidget - + Search by card name (or search expressions) Search by card name (or search expressions) - + Ungrouped Ungrouped - + Group by Type Group by Type - + Group by Mana Value Group by Mana Value - + Group by Color Group by Color - + Unsorted Unsorted - + Sort by Name Sort by Name - + Sort by Type Sort by Type - + Sort by Mana Cost Sort by Mana Cost - + Sort by Colors Sort by Colors - + Sort by P/T Sort by P/T - + Sort by Set Sort by Set - + shuffle when closing shuffle when closing - + pile view pile view @@ -10232,7 +10492,7 @@ Please refrain from engaging in this activity or further actions may be taken ag - + Deck Editor Deck Editor @@ -10313,7 +10573,7 @@ Please refrain from engaging in this activity or further actions may be taken ag - + Replays Replays @@ -10465,7 +10725,7 @@ Please refrain from engaging in this activity or further actions may be taken ag - + Reset Layout Reset Layout @@ -10886,8 +11146,9 @@ Please refrain from engaging in this activity or further actions may be taken ag - Toggle Untap - Toggle Untap + Toggle Skip Untapping + Toggle Untap + Toggle Skip Untapping @@ -10906,98 +11167,102 @@ Please refrain from engaging in this activity or further actions may be taken ag + Play Card, Face Down + Play Card, Face Down + + + Attach Card... Attach Card... - + Unattach Card Unattach Card - + Clone Card Clone Card - + Create Token... Create Token... - + Create All Related Tokens Create All Related Tokens - + Create Another Token Create Another Token - + Set Annotation... Set Annotation... - + Select All Cards in Zone Select All Cards in Zone - + Select All Cards in Row Select All Cards in Row - + Select All Cards in Column Select All Cards in Column - + Reveal Selected Cards to All Players Reveal Selected Cards to All Players - - + + Bottom of Library Bottom of Library - + - - + + Exile Exile - + - + Graveyard Graveyard - + Hand Hand - - + + Top of Library Top of Library - - + Battlefield, Face Down Battlefield, Face Down @@ -11033,234 +11298,246 @@ Please refrain from engaging in this activity or further actions may be taken ag - + Stack Stack - + Graveyard (Multiple) Graveyard (Multiple) - - + + + Graveyard (Multiple), Face Down + Graveyard (Multiple), Face Down + + + + Exile (Multiple) Exile (Multiple) - + + + Exile (Multiple), Face Down + Exile (Multiple), Face Down + + + Stack Until Found Stack Until Found - + Draw Bottom Card Draw Bottom Card - + Draw Multiple Cards from Bottom... Draw Multiple Cards from Bottom... - + Draw Arrow... Draw Arrow... - + Remove Local Arrows Remove Local Arrows - + Leave Game Leave Game - + Concede Concede - + Roll Dice... Roll Dice... - + Shuffle Library Shuffle Library - + Shuffle Top Cards of Library Shuffle Top Cards of Library - + Shuffle Bottom Cards of Library Shuffle Bottom Cards of Library - + Mulligan Mulligan - + Mulligan (Same hand size) Mulligan (Same hand size) - + Mulligan (Hand size - 1) Mulligan (Hand size - 1) - + Draw a Card Draw a Card - + Draw Multiple Cards... Draw Multiple Cards... - + Undo Draw Undo Draw - + Always Reveal Top Card Always Reveal Top Card - + Always Look At Top Card Always Look At Top Card - + Sort Hand by Name Sort Hand by Name - + Sort Hand by Type Sort Hand by Type - + Sort Hand by Mana Value Sort Hand by Mana Value - + Reveal Hand to All Players Reveal Hand to All Players - + Reveal Random Card to All Players Reveal Random Card to All Players - + Rotate View Clockwise Rotate View Clockwise - + Rotate View Counterclockwise Rotate View Counterclockwise - + Unfocus Text Box Unfocus Text Box - + Focus Chat Focus Chat - + Clear Chat Clear Chat - + Refresh Refresh - + Skip Forward Skip Forward - + Skip Backward Skip Backward - + Skip Forward by a lot Skip Forward by a lot - + Skip Backward by a lot Skip Backward by a lot - + Play/Pause Play/Pause - + Toggle Fast Forward Toggle Fast Forward - + Home Home - + Visual Deck Storage Visual Deck Storage - + Deck Storage Deck Storage - + Server Server - + Account Account - + Administration Administration - + Logs Logs From d2732ac742cd7acf19719bb60ce5007ae3d8d91b Mon Sep 17 00:00:00 2001 From: ebbit1q Date: Fri, 10 Apr 2026 13:42:05 +0200 Subject: [PATCH 13/25] fix prepare cards (#6792) --- oracle/src/oracleimporter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oracle/src/oracleimporter.cpp b/oracle/src/oracleimporter.cpp index d585842f6..b5d7b9856 100644 --- a/oracle/src/oracleimporter.cpp +++ b/oracle/src/oracleimporter.cpp @@ -360,13 +360,13 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, const QList } // split cards are considered a single card, enqueue for later merging - if (layout == "split" || layout == "aftermath" || layout == "adventure") { + if (layout == "split" || layout == "aftermath" || layout == "adventure" || layout == "prepare") { auto _faceName = getStringPropertyFromMap(card, "faceName"); SplitCardPart split(_faceName, text, properties, printingInfo); auto found_iter = splitCards.find(name + numProperty); if (found_iter == splitCards.end()) { splitCards.insert(name + numProperty, {{split}, name}); - } else if (layout == "adventure") { + } else if (layout == "adventure" || layout == "prepare") { found_iter->first.insert(0, split); } else { found_iter->first.append(split); From f9fb03b26b796bdfd1ac298110224598dc506cdc Mon Sep 17 00:00:00 2001 From: ebbit1q Date: Fri, 10 Apr 2026 18:17:43 +0200 Subject: [PATCH 14/25] update all runners to use qt6.11 (#6794) * update all runners to use qt6.11 * use aqt version directly from repo --- .github/workflows/desktop-build.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index 7bd84eb21..c93666640 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -270,7 +270,7 @@ jobs: override_target: 13 make_package: 1 package_suffix: "-macOS13_Intel" - qt_version: 6.10.* + qt_version: 6.11.* qt_arch: clang_64 qt_modules: qtimageformats qtmultimedia qtwebsockets cmake_generator: Ninja @@ -284,7 +284,7 @@ jobs: type: Release make_package: 1 package_suffix: "-macOS14" - qt_version: 6.10.* + qt_version: 6.11.* qt_arch: clang_64 qt_modules: qtimageformats qtmultimedia qtwebsockets cmake_generator: Ninja @@ -298,7 +298,7 @@ jobs: type: Release make_package: 1 package_suffix: "-macOS15" - qt_version: 6.10.* + qt_version: 6.11.* qt_arch: clang_64 qt_modules: qtimageformats qtmultimedia qtwebsockets cmake_generator: Ninja @@ -310,7 +310,7 @@ jobs: soc: Apple xcode: "16.4" type: Debug - qt_version: 6.10.* + qt_version: 6.11.* qt_arch: clang_64 qt_modules: qtimageformats qtmultimedia qtwebsockets cmake_generator: Ninja @@ -322,7 +322,7 @@ jobs: type: Release make_package: 1 package_suffix: "-Win10" - qt_version: 6.10.* + qt_version: 6.11.* qt_arch: win64_msvc2022_64 qt_modules: qtimageformats qtmultimedia qtwebsockets cmake_generator: "Visual Studio 17 2022" @@ -409,6 +409,8 @@ jobs: if: matrix.os == 'Windows' uses: jurplel/install-qt-action@v4 with: + # qt 6.11.0 only works with aqtinstall directly from git until aqtinstall 3.4 is released + aqtsource: git+https://github.com/miurahr/aqtinstall.git version: ${{ steps.resolve_qt_version.outputs.version }} arch: ${{matrix.qt_arch}} modules: ${{matrix.qt_modules}} From 36aba81b1b668fcfafcbd4b0966e35d0b43dca26 Mon Sep 17 00:00:00 2001 From: ebbit1q Date: Fri, 10 Apr 2026 18:18:08 +0200 Subject: [PATCH 15/25] adds eternal to prioritisation list (#6793) --- oracle/src/oracleimporter.h | 1 + 1 file changed, 1 insertion(+) diff --git a/oracle/src/oracleimporter.h b/oracle/src/oracleimporter.h index e83958d73..5bf352594 100644 --- a/oracle/src/oracleimporter.h +++ b/oracle/src/oracleimporter.h @@ -22,6 +22,7 @@ const QMap setTypePriorities{ {"archenemy", CardSet::PriorityReprint}, {"arsenal", CardSet::PriorityReprint}, {"box", CardSet::PriorityReprint}, + {"eternal", CardSet::PriorityReprint}, {"from_the_vault", CardSet::PriorityReprint}, {"masterpiece", CardSet::PriorityReprint}, {"masters", CardSet::PriorityReprint}, From 29c1d7f3e4eada9981da383e80728df9dff0e5f0 Mon Sep 17 00:00:00 2001 From: tooomm Date: Sat, 11 Apr 2026 18:19:11 +0200 Subject: [PATCH 16/25] add timeout to job matrixes (#6797) --- .github/workflows/desktop-build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index c93666640..653df4ba0 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -156,6 +156,7 @@ jobs: needs: configure runs-on: ubuntu-latest continue-on-error: ${{matrix.allow-failure == 'yes'}} + timeout-minutes: 70 env: NAME: ${{matrix.distro}}${{matrix.version}} CACHE: ${{github.workspace}}/.cache/${{matrix.distro}}${{matrix.version}} # directory for caching docker image and ccache @@ -331,6 +332,7 @@ jobs: name: ${{matrix.os}} ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }} needs: configure runs-on: ${{matrix.runner}} + timeout-minutes: 100 env: CCACHE_DIR: ${{github.workspace}}/.cache/ # Cache size over the entire repo is 10Gi: From f56b6723070fedbf3a31c63b195a341d61feff21 Mon Sep 17 00:00:00 2001 From: tooomm Date: Sat, 11 Apr 2026 18:20:35 +0200 Subject: [PATCH 17/25] CI: Allow failing of ccache deletion step (#6799) * Don't fail cache delete step * Use `continue-on-error` * remove leftover --- .github/workflows/desktop-build.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index 653df4ba0..8c3fd01de 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -208,9 +208,10 @@ jobs: --ccache "$CCACHE_SIZE" $NO_CLIENT .ci/name_build.sh - # Delete used cache to emulate a ccache update. See https://github.com/actions/cache/issues/342. + # Delete used cache to emulate a ccache update. See https://github.com/actions/cache/issues/342 - name: Delete remote compiler cache (ccache) if: github.ref == 'refs/heads/master' && steps.ccache_restore.outputs.cache-hit + continue-on-error: true env: GH_TOKEN: ${{ github.token }} run: gh cache delete --repo ${{ github.repository }} ${{ steps.ccache_restore.outputs.cache-primary-key }} @@ -451,9 +452,10 @@ jobs: TARGET_MACOS_VERSION: ${{ matrix.override_target }} run: .ci/compile.sh --server --test --vcpkg - # Delete used cache to emulate a ccache update. See https://github.com/actions/cache/issues/342. + # Delete used cache to emulate a ccache update. See https://github.com/actions/cache/issues/342 - name: Delete remote compiler cache (ccache) if: github.ref == 'refs/heads/master' && matrix.use_ccache == 1 && steps.ccache_restore.outputs.cache-hit + continue-on-error: true env: GH_TOKEN: ${{ github.token }} run: gh cache delete --repo ${{ github.repository }} ${{ steps.ccache_restore.outputs.cache-primary-key }} From e977f123ce47d393cc308b4575fe7444d8a6272c Mon Sep 17 00:00:00 2001 From: ebbit1q Date: Sat, 11 Apr 2026 19:18:12 +0200 Subject: [PATCH 18/25] add ccache eviction for files older than 7 days (#6795) * add ccache eviction for files older than 7 days also clean up some of the scripts to be more internally consistent * move name build into the docker container again * remove one extra empty line [skip ci] * allow canceling concurrent builds on master for both desktop and docker * add ccache eviction age to macos as well --- .ci/compile.sh | 29 +++++++++++++++++++++++- .ci/docker.sh | 29 +++++++++++++++++++----- .github/workflows/desktop-build.yml | 33 ++++++++++++++++++---------- .github/workflows/docker-release.yml | 5 +++++ 4 files changed, 78 insertions(+), 18 deletions(-) diff --git a/.ci/compile.sh b/.ci/compile.sh index 424527f96..7ebdd6e4e 100755 --- a/.ci/compile.sh +++ b/.ci/compile.sh @@ -10,9 +10,11 @@ # --test runs tests # --debug or --release sets the build type ie CMAKE_BUILD_TYPE # --ccache [] uses ccache and shows stats, optionally provide size +# --evict-ccache runs ccache eviction based on given age after build # --dir sets the name of the build dir, default is "build" +# --cmake-generator sets CMAKE_GENERATOR as used by cmake # --target-macos-version sets the min os version - only used for macOS builds -# uses env: BUILDTYPE MAKE_INSTALL MAKE_PACKAGE PACKAGE_TYPE PACKAGE_SUFFIX MAKE_SERVER MAKE_NO_CLIENT MAKE_TEST USE_CCACHE CCACHE_SIZE BUILD_DIR CMAKE_GENERATOR TARGET_MACOS_VERSION +# uses env: BUILDTYPE MAKE_INSTALL MAKE_PACKAGE PACKAGE_TYPE PACKAGE_SUFFIX MAKE_SERVER MAKE_NO_CLIENT MAKE_TEST USE_CCACHE CCACHE_SIZE CCACHE_EVICTION_AGE BUILD_DIR CMAKE_GENERATOR TARGET_MACOS_VERSION # (correspond to args: --debug/--release --install --package --suffix --server --test --ccache --dir ) # exitcode: 1 for failure, 3 for invalid arguments @@ -71,6 +73,15 @@ while [[ $# != 0 ]]; do shift fi ;; + '--evict-ccache') + shift + if [[ $# == 0 ]]; then + echo "::error file=$0::--evict-ccache expects an argument" + exit 3 + fi + CCACHE_EVICTION_AGE=$1 + shift + ;; '--vcpkg') USE_VCPKG=1 shift @@ -84,6 +95,15 @@ while [[ $# != 0 ]]; do BUILD_DIR="$1" shift ;; + '--cmake-generator') + shift + if [[ $# == 0 ]]; then + echo "::error file=$0::--cmake-generator expects an argument" + exit 3 + fi + export CMAKE_GENERATOR=$1 + shift + ;; '--target-macos-version') shift if [[ $# == 0 ]]; then @@ -251,9 +271,16 @@ cmake --build . "${buildflags[@]}" echo "::endgroup::" if [[ $USE_CCACHE ]]; then + if [[ $CCACHE_EVICTION_AGE ]]; then + echo "::group::evict ccache files older than $CCACHE_EVICTION_AGE" + ccache --evict-older-than "$CCACHE_EVICTION_AGE" + echo "::endgroup::" + fi echo "::group::Show ccache stats again" ccachestatsverbose echo "::endgroup::" +elif [[ $CCACHE_EVICTION_AGE ]]; then + echo "::error file=$0::ccache eviction is enabled while ccache is disabled!" fi if [[ $RUNNER_OS == macOS ]]; then diff --git a/.ci/docker.sh b/.ci/docker.sh index 911488ecf..46112daaa 100644 --- a/.ci/docker.sh +++ b/.ci/docker.sh @@ -3,17 +3,28 @@ # This script is to be used by the ci environment from the project root directory, do not use it from somewhere else. # Creates or loads docker images to use in compilation, creates RUN function to start compilation on the docker image. -# sets the name of the docker image, these correspond to directories in .ci +# +# usage: source