Commit graph

110 commits

Author SHA1 Message Date
BruebachL
c4ac80e282
[Server] Create the worker database interfaces through the factory (#7860) 2026-11-10 12:16:34 +01:00
BruebachL
0caacb27c1
[Server] Move the database lifecycle calls onto the interface (#7859) 2026-11-10 12:16:34 +01:00
BruebachL
526d528e96
[Server] Serve the e-mail queue through the database interface (#7858) 2026-11-10 12:16:33 +01:00
BruebachL
9087d0d3fe
[Server] Serve the bootstrap rooms through the database interface (#7857) 2026-11-10 12:16:33 +01:00
BruebachL
09d0bed997
[Server] Record uptime through the database interface (#7856) 2026-11-10 12:16:33 +01:00
BruebachL
f1ee6bdc2d
[Server] Serve the server list through the database interface (#7855) 2026-11-10 12:16:32 +01:00
BruebachL
f8f8fffa6b
[Server] Serve the login message through the database interface (#7854) 2026-11-10 12:16:32 +01:00
BruebachL
99bdcebf2e
[Server] Serve deck storage listings through the database interface (#7851)
deckListHelper still prepared the folder and deck-file statements inline through
Servatrice_DatabaseInterface, and the four deck commands reached checkSql,
getUserIdInDB and getDeckFromDatabase through a concrete pointer. Add
DeckListFolderRecord, DeckListFileRecord, getDeckListFolders and
getDeckListFiles to Server_DatabaseInterface with fail-closed defaults, serve
all four commands from the injected interface, and drop the sqlInterface member
so the handler no longer holds a database implementation at all. Folder
ordering, recursion order, public-only pruning and every response code are
unchanged.
2026-11-10 12:16:30 +01:00
BruebachL
1822d16a72
[Server] Serve card-art rules and params through the database interface (#7850)
The four card-art commands still prepared their statements inline through
Servatrice_DatabaseInterface: the two card_art_params updates in
cmdSetCardArtParams, the rule upsert and delete, and the rule listing that
mapped rows to protocol entries. Add CardArtRuleRecord, upsertCardArtRule,
removeCardArtRule, getCardArtRules, setCardArtParams and clearCardArtParams to
Server_DatabaseInterface with fail-closed defaults, implement them with the
previous statements, and keep the validation, clamping, in-memory userInfo sync
and broadcast in the handler.
2026-11-10 12:16:30 +01:00
BruebachL
2608228bdd
[Server] Serve account moderation through the database interface (#7849)
The moderator account commands still reached Servatrice_DatabaseInterface through
its concrete class: cmdResetUserPassword ran the privilege lookup inline and
called the concrete-only changeUserPasswordWithLegacyBackup, while the avatar,
banner and blacklisted-banner commands called concrete-only helpers. Promote
those four methods to Server_DatabaseInterface, move RemoveUserBannerResult into
the interface, and add getUserAdminFlags for the reset privilege lookup. The
escalation guard, audit records, had_banner response field and every response
code stay in the handler.
2026-11-10 12:16:29 +01:00
BruebachL
f7c6e81347
[Server] Serve account self-service updates through the database interface (#7848)
cmdAccountEdit assembled a dynamic UPDATE for the real name, email and country
columns in the socket handler, and cmdAccountImage wrote the avatar blob there
too. Add AccountUpdateRecord, updateUserAccount and setUserAvatar to
Server_DatabaseInterface with fail-closed defaults, implement them with the
previous query text and bind values, and keep the password gate, the email
permission check, the empty-edit early return and the in-memory userInfo updates
in the handler.
2026-11-10 12:16:29 +01:00
BruebachL
8c8dd0c072
[Server] Serve admin actions through the database interface (#7847)
cmdBanFromServer, the admin-flag helpers, cmdForceActivateUser and the
admin-notes commands still prepared their statements inline through
Servatrice_DatabaseInterface, and addWarning was only reachable through the
concrete Servatrice class. Add setAdminFlag, getUserToken, getAdminNotes,
setAdminNotes, addBan and getUserNamesByClientId to Server_DatabaseInterface
with fail-closed defaults, promote addWarning, and model the outcomes as
UserFieldResult and AdminFlagUpdateResult. Notifications, the audit record,
the ban disconnect fan-out and every response code stay in the handler.
2026-11-10 12:16:28 +01:00
BruebachL
948035af06
[Server] Serve report mutations through the database interface (#7846)
cmdReportAssign, cmdReportResolve, cmdReportReopen and cmdReportAddComment still
ran their lookups and updates inline through Servatrice_DatabaseInterface. Add
assignReport, resolveReport, reopenReport, getUserNameById,
getReportNotificationTarget, countReportCommentsInLastHour,
getReportCommentTarget and addReportComment to Server_DatabaseInterface with
fail-closed defaults, and model the outcomes as ReportAssignResult and
ReportUpdateResult so "no such report", "wrong state" and "query failed" stay
distinguishable. Authorization checks, audit records, notification recipients
and every response code stay in the handler.
2026-11-10 12:16:28 +01:00
BruebachL
a54e13756c
[Server] Serve report aggregates through the database interface (#7845)
cmdReportStats and cmdReportUserInfo still ran their queries inline through
Servatrice_DatabaseInterface and built the response in the socket handler. Add
ReportStatsRecord, ReportUserInfoRecord and their count records to
Server_DatabaseInterface with fail-closed defaults, implement getReportStats and
getReportUserInfo with the previous statements and query order, and keep
presence flags for the fields the old code set only on a successful query so
the proto2 wire output is unchanged. The stats cache decision and every response
code stay in the handler.
2026-11-10 12:16:28 +01:00
BruebachL
2ba80cb61d
[Server] Serve report list views through the database interface (#7844)
cmdReportList, cmdReportMyList and cmdReportDetails still prepared their SELECTs
inline through Servatrice_DatabaseInterface and mapped the result columns to
protocol records in the socket handler. Add getReportList,
getReportsSubmittedBy, getReportForDetails and getReportComments to
Server_DatabaseInterface with fail-closed defaults and a ReportLookupResult
outcome, implement them with the previous statements, joins and null handling,
and keep the detail authorization check and the tolerated comment-query failure
in the handler in the same order. Response codes are unchanged.
2026-11-10 12:16:27 +01:00
BruebachL
fb836c6e0c
[Server] Serve report submission through the database interface (#7843)
cmdReport and sendPendingReportNotifications still issued their SQL inline
through Servatrice_DatabaseInterface: the daily report cap, the reported-user
lookup, the report insert, both pending-notification selects and the notified
flips. Add the two notification records and seven report methods to
Server_DatabaseInterface with fail-closed defaults, implement them with the
previous statements and binds, and let both functions ask the injected
interface. Check order, response codes and notification payloads are unchanged.
2026-11-10 12:16:27 +01:00
BruebachL
9b00972b4d
[Server] Serve password reset from the database interface (#7842)
cmdForgotPasswordRequest, continuePasswordRequest, cmdForgotPasswordReset,
cmdForgotPasswordChallenge, cmdRequestPasswordSalt and cmdSubmitPasswordVerifier
still called Servatrice_DatabaseInterface, and two of them validated the account
token and email through the generic validateTableColumnStringData helper. Add
doesForgotPasswordExist, addForgotPassword and getUsableLegacyBackup to
Server_DatabaseInterface with fail-closed defaults, replace the table/column
probes with userTokenMatches and userEmailMatches using the same statement and
case-insensitive comparison, and let the commands ask the injected interface.
Audit records, check order and every response code are unchanged.
2026-11-10 12:16:26 +01:00
BruebachL
d05198101d
[Server] Serve account registration from the database interface (#7841)
cmdRegisterAccount and cmdActivateAccount still called
Servatrice_DatabaseInterface for username validation, user existence, the
per-email account limit, ban checks, registration and activation, and
cmdRegisterAccount issued the activation-email insert inline. Add
addActivationEmail to Server_DatabaseInterface with a false default, implement
it with the same statement and bind value, and let both commands ask the
injected interface. Validation order and every response code are unchanged.
2026-11-10 12:16:26 +01:00
BruebachL
7719ff021c
[Server] Expose the latest uptime snapshot on the database interface (#7840)
cmdGetServerStats reached through Servatrice_DatabaseInterface for checkSql and
getLatestUptimeSnapshot, a concrete-only method. Move the UptimeSnapshot record
to Server_DatabaseInterface with an invalid default for non-SQL stores, mark the
Servatrice implementation as an override, and let the command ask the injected
interface. The statement, its bind value and the invalid-snapshot response are
unchanged.
2026-11-10 12:16:25 +01:00
BruebachL
03adbd9787
[Server] Move replay code exchange into the database interface (#7839)
createHashForReplay, cmdReplayGetCode, cmdReplaySubmitCode,
cmdReplayDownloadByGameId and cmdGrantReplayAccess still issued their own SQL
through Servatrice_DatabaseInterface. Add checkReplayAccess, getReplayName,
getReplayHashSource, addReplayAccess, getLatestReplayForGame and
grantReplayAccessByName to Server_DatabaseInterface, implement them with the
same statements, bind order and response mapping, and let the commands ask the
injected interface. Hashing and code parsing stay in the handler; the shared
insert is reused by both grant paths.
2026-11-10 12:16:25 +01:00
BruebachL
b2e69d3443
[Server] Move replay access reads and writes into the database interface (#7838)
cmdReplayList, cmdReplayDownload, cmdReplayModifyMatch and cmdReplayDeleteMatch issued their own SQL through Servatrice_DatabaseInterface. Add ReplayMatchRecord/ReplayEntry and the ReplayAccessResult/ReplayWriteResult enums to Server_DatabaseInterface, implement the list, data, do-not-hide and delete operations with the same statements and response mapping, and let the commands ask the injected interface. checkSql stays in the two write commands where it already was.
2026-11-10 12:16:24 +01:00
BruebachL
ba037cbf04
[Server] Move deck share storage into the database interface (#7837)
The five deck-share commands reached through Servatrice_DatabaseInterface for share lookups, and cmdDeckShareCreate still issued the per-user rate-limit count and the folder deck listing inline. Move DeckShareItemRecord and DeckShareSummaryRecord to Server_DatabaseInterface, declare the share operations plus getDeckSharesCreatedToday and getDeckFilesInFolder there with failing defaults, and switch the commands to the injected interface. The MySQL statements and every response code stay as they were, including the -1-on-failure sentinel for the count.
2026-11-10 12:16:24 +01:00
BruebachL
417e7f7db4
[Server] Expose the audit log on the database interface (#7835)
addAuditRecord existed only on Servatrice_DatabaseInterface, so 37 moderator and admin commands reached through the concrete class just to append a log row. Declare it on Server_DatabaseInterface with an empty default, mark the Servatrice implementation as an override, and switch the handler's call sites to the injected interface. No SQL moves; the argument hashes are unchanged.
2026-11-10 12:16:24 +01:00
BruebachL
7734a94f83
[Server] Move deck file writes into the database interface (#7834)
Deleting a deck, inserting an uploaded deck, and updating one in place were SQL statements in the socket handler, including the dynamic is_public clause and the follow-up visibility read. Add DeckFileData and DeckFileWriteResult to Server_DatabaseInterface with create/update/delete operations, implement them in Servatrice_DatabaseInterface with the same statements and bind order, and let cmdDeckUpload and cmdDeckDel ask the injected interface.
2026-11-10 12:16:23 +01:00
BruebachL
23781af1f0
[Server] Move deck folder writes into the database interface (#7833)
Setting a deck or folder public, creating a folder, and deleting a folder tree were SQL statements in the socket handler. Declare them on Server_DatabaseInterface with a DeckStorageResult so the caller can still tell a missing row from a failed write, implement them in Servatrice_DatabaseInterface with the same statements, and let the three folder commands ask the injected interface. Readiness probing keeps its previous ignore-the-result shape.
2026-11-10 12:16:23 +01:00
BruebachL
8c0e796b0d
[Server] Move deck-path and visibility lookups into the database interface (#7832)
Resolving a deck folder path, reading a deck's owner, and walking its folder chain for public visibility were SQL queries living in the socket handler, keyed to the logged-in user through userInfo. Declare the three lookups on Server_DatabaseInterface, implement them in Servatrice_DatabaseInterface with the user id as an explicit parameter, and let the deck commands ask the injected interface.
2026-11-10 12:16:22 +01:00
BruebachL
28f92ac5f5
[Server] Expose chat-log reads on the database interface (#7830)
getMessageLogHistory, getPrivateChatHistory, and getUserIdInDB were reachable only through Servatrice_DatabaseInterface, so the two log-history commands in the socket handler needed the concrete class. Declare the two readers on Server_DatabaseInterface with empty/false defaults, mark the Servatrice implementations as overrides, and resolve the private-chat peer through the already-promoted getUserId.
2026-11-10 12:16:21 +01:00
BruebachL
2ca3117765
[Server] Expose moderation history reads on the database interface (#7829)
The ban/warn history, session, alt-account, and moderator-login readers existed only on Servatrice_DatabaseInterface, so the socket handler had to hold the concrete class to call them and to ask checkSql() whether the store was reachable. Declare them on Server_DatabaseInterface with empty-list defaults and a checkSql() that reports ready for stores without a connection, mark the Servatrice implementations as overrides, and let those five commands ask the injected interface.
2026-11-10 12:16:21 +01:00
BruebachL
52d8f84ec8
[Server] Move buddy and ignore list writes into the database interface (#7827)
cmdAddToList/cmdRemoveFromList issued raw SQL against the buddy and ignore tables and looked accounts up through getUserIdInDB, a method that only exists on the MySQL implementation. Add getUserId plus add/remove operations to Server_DatabaseInterface, implement them in Servatrice_DatabaseInterface over the shared single-row helpers, and let the handler ask the injected interface instead of the concrete class.
2026-11-10 12:16:20 +01:00
SwitchOCE
7709184ecc
[Server] Let users fetch their own private chat history (#7506)
* [Server] Let users fetch their own private chat history

Players lose their private conversations on reconnect or on another
device. Servatrice can already log private messages, but only
moderators can read the logs.

Add Command_GetPrivateChatHistory / Response_PrivateChatHistory so a
registered user can page through their own logged conversation with one
other user. Operators opt in with logging/allow_user_chat_history
(default off, and log_user_msg_chat must be on). The caller is matched
by account id so reused guest or deleted-account names can't read older
messages; only time, sender name and text are returned. Pages hold
1..200 messages and never split a second, since log_time has 1-second
resolution.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* [Server] Harden private chat history identity matching

Match both participants by account id and name so a federated user's remote id cannot collide with a local account, and stop storing foreign ids in the local chat log. Extract the paging assembly into a pure helper and cover the whole-second boundary rules with gtests.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Lukas Brübach <44814898+BruebachL@users.noreply.github.com>
2026-11-09 13:21:43 +01:00
BruebachL
53f69c9216
[Alts] Server-side search controls, IP counts, adjustable window (#7517)
* [Alts] Server-side search controls, IP counts, adjustable window

Plumbs the match-type chips through to the server: include_email/include_clientid/include_ip skip disabled identifier types in both matching and frontier expansion, so excluded types (e.g. throwaway domains) never enter the graph at any depth. Session lookback becomes adjustable (session_months, clamped 1-24, default 6). Each alt row carries ip_count, the distinct recent session IP count shown in a new IPs column. All proto additions are optional with old defaults, so old clients/servers interoperate.

* Fix remaining review threads on the alts search controls

- Disable keyboard tracking on the depth and window spin boxes so typing a
  two-digit value sends one search, not one per digit.
- Reload the user under investigation on a filter change instead of reading
  the search box, which could switch to a name never submitted.
- Keep a chip's last match count while it is unchecked; the server omits
  unchecked match types, so the label used to drop to "(0)".

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-08 08:59:51 +01:00
BruebachL
933db4edd2
[Server] Recursive alt search with match evidence (#7510)
* [Server] Recursive alt search with match evidence

getUserAlts() now walks a breadth-first graph to the requested depth (clamped 1-3): each level expands frontier accounts over registration email, current client id, recent session IPs (6-month window), and historic session client ids, so identifier changes no longer hide older alts. Every row carries match_reason (email / clientid:<id> / ip:<addr>), depth, and shared_count fan-out so moderators can distinguish real alts from VPN/shared-network overlap. Command_GetUserAlts depth is plumbed through; unset depth defaults to 1, preserving previous single-hop behavior for old clients.

* [Server] Fix alt search review findings

- Seed the BFS with the stored user name so the searched account is not
  re-listed as its own alt at depth 1.
- Normalise emails to lower case for the membership and fan-out checks;
  SQL compares them case-insensitively.
- Widen the per-level row limit by the visited count so level 2+ cannot
  return only already-known accounts.
- Prepare dynamic IN-list queries locally instead of through the shared
  prepared-statement cache, whose keys are query texts.
- Count historic client-id fan-out from _sessions, not from the current
  _users ids.
- Order capped identifiers by relevance (current email/client id first,
  then most recent session IPs) instead of alphabetically.

* Apply suggestion from @ZeldaZach

Co-authored-by: Zach H <zahalpern+github@gmail.com>

* Apply suggestion from @ZeldaZach

Co-authored-by: Zach H <zahalpern+github@gmail.com>

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: Zach H <zahalpern+github@gmail.com>
2026-11-08 08:14:03 +01:00
BruebachL
94e6281d87
[Logs] Browse room history without filters with paginated infinite scroll (#7531)
* [Logs] Browse room history without filters with paginated infinite scroll

* [Logs] Continue pagination when switching log tabs

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-02 07:36:49 +01:00
BruebachL
ef4574d2b9
[Server] Store a scrypt verifier when an admin resets a password (#7679)
The temporary password was written as a legacy salt+hash row, which makes the
next login look like an authentication downgrade. A client holding a saved
verifier refuses that downgrade, so the reset locked the account out of every
modern client.

Store the verifier and, where security/legacy_backup_key is configured, keep a
sealed legacy hash of the same temporary password as the downgrade backup, so
pre-challenge clients keep the credential path the reset used to provide.

Without the key no row shape serves both: the verifier and the legacy hash
cannot share one column, so those clients have no path at all. Warn at reset
time that the new password has no pre-challenge path, and say so in
servatrice.ini.example instead of promising that a reset gets them back in.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-02 07:00:48 +01:00
BruebachL
93309e01a2
[Server/Client/Protocol] Let moderators clear banner cards (#7546)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-10-25 13:19:20 +01:00
BruebachL
a139084ad6
[Security] Add challenge-response auth with scrypt verifiers and stop storing plaintext passwords (#7193)
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 cover (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 26 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Desktop / Prune caches (push) Waiting to run
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions
* [Security] Add challenge-response auth with scrypt verifiers and stop storing plaintext passwords

Challenge-response authentication: the client derives a scrypt verifier
(RFC 7914, EVP_PBE_scrypt, N=32768, r=8, p=1) and authenticates with
HMAC-SHA256(key, nonce), so neither the password nor its hash is
transmitted. The stored format becomes
"$scrypt$<n>$<r>$<p>$<salt>$<verifier>" and Response_PasswordSalt
now carries the cost parameters. Strict servers only accept scrypt
verifiers; legacy accounts are migrated after a successful login.

Fix #344 for challenge-response servers: a saved profile stores the
derived verifier under the password key instead of the plaintext password.
The connect dialog loads it without revealing it, autoconnect passes it
through, the change-password dialog no longer prefills the old password
field with it, and the client only persists the verifier when
"Save password" is checked.

Took 3 minutes

Took 1 minute

Took 10 seconds

Took 7 minutes

* [Security] Harden credential format acceptance and challenge nonce validation

* [Security] Refuse KDF downgrade; trim profile name and warn on silent credential drop

* [Security] Add SupportsAll ServerOptions enumerator to fix enum validation assert

The server advertised both SupportsPasswordHash (1) and
SupportsChallengeResponseAuth (2) via a bitwise-OR that produced the
value 3, which no enumerator declared. Protobuf's proto2 enum
validation rejects the value in _internal_set_server_options, crashing
servatrice on the debug assertion. Add a SupportsAll = 3 enumerator with
an identical wire value so the bitmask reads unchanged while the value
is now a documented, valid member of the enum.

* [Security] Normalize username when issuing challenge nonce

cmdRequestPasswordSalt bound the challenge nonce to the raw, un-simplified
username while cmdLogin verifies it against the simplified name. A user
who typed leading or trailing whitespace in the username box would be
silently rejected during challenge-response login even with the correct
password. Simplify at the nonce-issuing site so both commands agree on
the normalized name.

Also drive-by formatting: collapse the now single-expression
serverOptions initializer onto one line.

* [Security] Keep an encrypted legacy password backup for old-client logins

Migrating an account to scrypt stranded pre-challenge clients, which can
only present the legacy salt+hash: after migration the server served them
a scrypt salt they cannot use, so the only recovery was a password reset.

When security/legacy_backup_key is configured (64 hex chars,
openssl rand -hex 32), submitPasswordVerifier now seals the legacy
credential with AES-256-GCM under that key before overwriting the row,
storing it in password_legacy_backup with the time it was last used. Old
clients keep working: cmdRequestPasswordSalt serves the backup's 16-char
salt in password_salt so they can derive the legacy hash they were
always able to compute, while new clients derive the verifier from the
new scrypt_salt field, which always carries the verifier salt. The
non-challenge login path accepts the plaintext or legacy hash against
the decrypted backup and refreshes its window; a migrated row with no
usable backup fails closed.

Backups age out so a database dump taken after the window closes never
carries a crackable legacy hash: getUsableLegacyBackup purges on first
use past security/legacy_backup_ttl_days (default 90, clamped 1..3650),
and a six-hourly sweep drops backups idle accounts never touch.
changeUserPassword clears the backup since a new credential invalidates
it. Without a configured key nothing is sealed or purged, so a
temporarily missing key cannot destroy existing backups.

The AAD is the simplified user name on both sides, sealing the blob to
its row so it cannot be moved between accounts. Sealing failures
degrade to the previous lockout behaviour rather than blocking
migration. The columns extend the unmerged 0036->0037 migration, so the
schema version stays 37.

* [Security] Address review: proto compat, plaintext verify, AAD case, strict scope

Fix the two blockers and follow-ups from the 2026-10-16 review:

- Advertise challenge-response in a new supports_challenge_response
  field instead of the ServerOptions enum and drop SupportsAll:
  proto2 enums are closed, so already-released clients parsed
  server_options = 3 as NoOptions and fell back to plaintext
  passwords. server_options stays exactly SupportsPasswordHash
  on the wire; old clients ignore the new field.
- Verify a plaintext password directly against a migrated scrypt
  row with verifyPassword() so email changes (cmdAccountEdit)
  and old clients sending password keep working without a
  legacy backup key. Pre-hashed values are still checked
  against the backup, and its TTL window is refreshed only
  when the backup is what actually matched.
- Use the stored row name as the AES-GCM AAD on both seal
  and open so a login whose case differs from the stored
  name (utf8mb4_unicode_ci) still opens its backup.
- Require challenge-response in strict mode only for
  registered SQL accounts (userExists gate), leaving
  guest/unregistered logins and none/password auth
  methods unaffected.
- Document in servatrice.ini.example that a captured
  legacy hash can take over a not-yet-migrated account
  and that strict only constrains registered SQL
  accounts. Schema version is 38.

* [Security] Raise legacy backup TTL default to 180 days

Gives operators and slow-to-upgrade users a longer downgrade
window for pre-challenge clients before unused encrypted
legacy password backups age out.

* [Security] Fix servatrice.cpp formatting per clang-format-18

* [Security] Add missing forward declaration for Command_SubmitPasswordVerifier

The cmdSubmitPasswordVerifier declaration references the proto-generated
type, which otherwise does not name a type in this header and breaks
every C++ build.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: BruebachL <bruebachl@cockatrice.example>
2026-10-16 22:52:26 +02:00
BruebachL
ce51e7147d
[Servatrice] Log room channel messages in audit log (#7479)
* [Servatrice] Log room channel messages and bump schema to 38

* Fixup: show room_channel audit rows and keep migration nullable

The log history query returns room_channel rows, but the log tab
dropped them. Render them in the room table with an extra Channel
column. Also drop the NOT NULL on target_type from the 37->38
migration so migrated schemas match the nullable servatrice.sql
definition (and strict-mode ALTERs cannot fail on legacy NULL rows).
Update the channel tests to the flag-based privilege model: the admin
fixture now carries IsModerator alongside IsAdmin, as the database
layer assigns.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-10-16 22:46:39 +02:00
BruebachL
05dcde69db
[Server] Gate room channels on privilege levels (#7477)
* [Server] Gate room channels on privilege levels

* [Server] Filter channel catalog per user, split read/write gating, add banners

- Server_Room::getInfo() now takes the requesting user and only lists
  channels that user may read. Previously every room member received the
  full catalog (e.g. the moderators channel) while message delivery was
  already gated, so restricted channels were visible but silent.
- Split channel access into mayReadChannel/mayWriteChannel: history
  replay, catalog listing, and event fan-out use the read level
  (access_level); Command_RoomChannelSay uses the write level
  (write_access_level, falling back to the read level when unset).
- Plumb write_access_level and banner_message through both room sources
  (SQL rooms_channels table and config-file channels via writeaccess
  and banner keys).
- Schema 38 -> 39: add write_access_level (NULL means inherit the read
  level) and banner_message columns; also widen access_level to the
  full six-value privilege enum to match the 37 -> 38 migration.

* Fixup: evaluate channel access per user flag instead of ladder

User flags are independent bits: IsDeveloper does not imply
IsModerator/IsAdmin, so developer-only users must not reach
moderator/admin channels. Replace the userPrivilegeLevel ladder with
hasChannelPrivilege: moderator channels need IsModerator (admins hold
it alongside IsAdmin), admin channels need IsAdmin, developer channels
need IsDeveloper or IsAdmin, judge channels admit IsJudge/IsModerator.
Covers catalog filtering, history replay, write gating and fan-out,
and corrects the ini example and proto docs.

* [Server] Make channel privilege helpers static per review

* [Server] Fail closed on unknown channel access levels per review

- roomChannelAccessFromKey returns nullopt for unknown keywords
  instead of silently falling back to Public
- skip channels with unknown read/write levels with qWarning, empty
  read defaults to Public, empty write means no override
- mayWriteChannel now also requires read access so write-below-read
  configs cannot post into invisible channels

* [Server] Remove stray conflict marker in channel replay comment

* [Server] Fix servatrice.cpp formatting per clang-format-18

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: BruebachL <bruebachl@cockatrice.example>
2026-10-16 22:46:38 +02:00
BruebachL
17d2e8d86e
[Server] Add room chat channels with per-channel fan-out and history (#7476)
* [Server] Add room chat channels with per-channel fan-out and history

* Fixup: redact all channel histories on message removal

removeSaidMessages only redacted the main history, so banned/warned
content stayed visible to late joiners on every other channel.
Drop the unused channelId parameter and redact up to amount messages
per channel across the whole channelChatHistory map.

* [Server] Validate room channel ids and fix catalog schema per review

- servatrice.sql: expand rooms_channels access_level enum to full set
  to match 0037->0038 migration
- servatrice.cpp: skip empty, reserved (__main__) and duplicate channel
  ids when loading from ini and DB with qWarning
- server_protocolhandler.cpp: clarify catalog replay comment (filtering
  happens in moderator stack)
- server_room: drop duplicate channelIndex, linear search preserves
  catalog order

* [Server] Fix server_room.cpp formatting per clang-format-18

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: BruebachL <bruebachl@cockatrice.example>
2026-10-16 22:46:38 +02:00
BruebachL
b11b096828
[Server] Apply card art rules to playmats (#7532)
* [Server] Apply card art rules to playmats

Card art DENY rules were only enforced for user card art
(SetCardArtParams) while playmats using the same art bypassed them.

Move the rule lookup to Servatrice_DatabaseInterface::isCardArtAllowed
with a default-allow base on Server_DatabaseInterface, delegate the
session check to it, reject DENY-listed Command_SetPlaymat with
RespFunctionNotAllowed, and strip DENY-listed deck playmats on
DeckSelect so banned art is never broadcast.

* [Server] Address review nits on playmat card art rules

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-10-16 19:50:43 +02:00
BruebachL
d603a2bb6a
[Tooling] Apply include-what-you-use across the tree (#7396)
* [DeckList] Apply include-what-you-use to libcockatrice_deck_list

* [IWYU] Apply include-what-you-use across the tree

Run one whole-tree IWYU pass over the libcockatrice_* libraries, the
Cockatrice client, Oracle, Servatrice and the test suite, so each
translation unit includes what it uses directly and forward-declares
what it does not need.

IWYU only analyses real translation units, never the moc code CMake
generates from Q_OBJECT headers, so it strips includes that moc output
silently relies on. Where a type is passed by value (or nested in a
QList/QMap) in a signal or slot signature, the generated moc code needs
the complete type; those includes are kept with an explanatory
`// IWYU pragma: keep`. The same holds for Q_DECLARE_METATYPE targets
such as QSqlDatabase.

A handful of includes are added by hand where callers brace-initialize a
const-reference parameter, which also requires the complete type.

Applied as a single commit on purpose: stripping includes from the
libcockatrice_* headers is precisely what breaks the client, test and
server translation units, and the fixes for those live in the same
translation units. Splitting by component would therefore leave
intermediate commits that do not compile.

All 426 modified Q_OBJECT/Q_GADGET headers were verified to be
moc-self-contained by compiling each generated moc_*.cpp standalone;
several built only because of moc include ordering in
mocs_compilation.cpp before the keep-includes were added.

* [Tooling] Emit public Qt headers and fix IWYU fallout CI found

Three problems the local build could not see, all because a green local
build only proves the configuration it was run under.

Private Qt headers. IWYU emitted private, lowercase Qt headers, and
qtypes.h does not exist at all before Qt 6.9, so the Debian 12 and
Ubuntu 24.04 jobs on Qt 6.4.2 failed to compile. The generator only
emitted a mapping when the capital header name mirrored the private one
(QAbstractAnimation for qabstractanimation.h), so qtypes.h, which is
wrapped by QtTypes instead, got no mapping and kept the private
spelling. Map it explicitly to <QtTypes>, which is public since Qt 5 and
provides the same typedefs either way, and rewrite the 100 affected
includes.

Do not generalize this to "prefer any Qt* wrapper". The table is
generated from whichever Qt is installed, and the wrappers are not
equally old: QtLogging only appeared in Qt 6.8 and QtMinMax in 6.5, so
that heuristic trades one breakage for another. Every other lowercase
header still in the tree has shipped publicly since Qt 5, so those are
left alone.

Platform-guarded includes. IWYU only sees the preprocessor configuration
it is handed, so it dropped an include that only the macOS branch of
main.cpp needs, and the macOS jobs failed on an undeclared
UrlSchemeEventFilter. Restore the include. Force-compiling every file
that carries a platform guard under Q_OS_MAC, Q_OS_MACOS and __APPLE__
reports nothing else, so the macOS branches are covered rather than
assumed.

shellcheck. Drop a leftover variable and stop assigning the
include-what-you-use lookup, which was only ever an existence check.

* [Tooling] Map Qt 6.5+ split headers to QtGlobal for Qt 6.4 compatibility

IWYU emitted QtTypes, QtPreprocessorSupport, QtVersionChecks, QtAssert,
QtMinMax, QtClassHelperMacros, QtEnvironmentVariables and QtTranslation.
Qt 6.5 split those declarations out of qglobal.h into separate headers, so
neither the Qt* wrapper nor the q*.h private name exists on the oldest Qt we
support (6.4.2), and Debian 12 / Ubuntu 24.04 fail with "No such file or
directory". qtypes.h is worse still: it only appeared in Qt 6.9.

QtGlobal is the one spelling that works across the whole supported range. It
has been public since Qt 5, defines these declarations itself up to Qt 6.4, and
includes the split headers from Qt 6.5 on, so redirect all eight private
headers to it and collapse the affected includes in the tree.

Curated overrides now take precedence over the name-mirroring rule, which
cannot know about Qt version floors because the mapping is generated from
whatever Qt is installed. That rule was mapping qtversionchecks.h to
<QtVersionChecks> even with an override present, because the override was only
consulted as a fallback. A curated key that matches nothing is now a hard
error rather than a silent fallback, and the redirected headers are no longer
advertised as the home of a symbol.

Verified every Qt include in the tree resolves against the real
qt6-base-dev 6.4.2 package: 15 distinct includes, 0 missing.

* [Tooling] Map QOverload to QtGlobal and drop the QMetaMethodArgument include

Two more headers IWYU emitted do not exist on Qt 6.4.2:

QOverload was split out of qglobal.h in Qt 6.5, so on 6.4.2 it is declared in
qglobal.h directly and the standalone header is a hard error. QtGlobal covers
both, so map qoverload.h to it like the other 6.5+ split headers.

QMetaMethodArgument is never referenced anywhere in the tree. It is a bare
forwarder for qobjectdefs.h that our generator advertised as a class symbol,
which is how IWYU came to add the include at all. The symbol mappings are
generated from header names, so they cannot tell a forwarder that happens to be
capitalised from a real class; removing the unused include is the fix, and the
generic case stays a known limitation of the generator.

Extending the audit to every Q* header (an earlier pass only matched headers
starting with "Qt" or lowercase q*.h, so it missed all of them) against the real
qt6-base-dev, qt6-multimedia-dev, qt6-declarative-dev, qt6-svg-dev and
qt6-websockets-dev 6.4.2 packages: 341 distinct Qt includes, 0 missing.

* [Game] Give moc the complete CardItem type in player_actions.h

player_actions.h forward declares CardItem but passes QList<CardItem *> by
value in actPlay/actPlayFacedown/actHide. moc instantiates the metatype for
those parameters, and on Qt 6.4 that expands to sizeof(CardItem), so Ubuntu
24.04 fails with "invalid application of 'sizeof' to incomplete type
'CardItem'". Newer Qt tolerates the incomplete type, which is why only the
6.4.2 jobs failed.

Include the defining header with an IWYU pragma keep, matching how the other
MOC-required includes in this area are handled.

* [Game] Give moc the complete ServerInfo_Card type

player_logic.h, game_scene.h, view_zone.h and view_zone_widget.h forward
declare ServerInfo_Card but pass QList<const ServerInfo_Card *> through signal
and slot signatures. moc instantiates the metatype for those parameters, which
Qt 6.4 expands to sizeof(ServerInfo_Card), so the connect in game_scene.cpp
fails on Debian 12 and Ubuntu 24.04 with "invalid application of 'sizeof' to
incomplete type 'const ServerInfo_Card'". Newer Qt tolerates it, so only the
6.4.2 jobs failed.

Include the generated protobuf header with an IWYU pragma keep, following the
pattern already used for ServerInfo_Player and ServerInfo_User in
game_event_handler.h.

* [Client] Complete the types Qt 6.4 needs for connect and moc

Qt 6.4 expands QMetaType::fromType on the normalised argument list of a
PMF connect and on moc-generated metatypes, which requires sizeof on every
signal and slot parameter. Newer Qt tolerates forward declarations there,
so only the Debian 12 and Ubuntu 24.04 jobs failed, with
"invalid application of 'sizeof' to incomplete type" for QMouseEvent,
QPrinter and GameReplay.

Replace the forward declarations with the real includes:

- QMouseEvent in the nine widget headers that pass it through signals
- QPrinter in abstract_tab_deck_editor.cpp, for the actPrintDeck lambda
- game_replay.pb.h in tab_replays.h. GameReplay is a protobuf message, so
  the project's game/replay.h only forward declares it and cannot supply
  the complete type.

Found by recompiling all 1918 project TUs against the Debian Qt 6.4.2
headers, which reproduces those two CI jobs locally in one pass.

* [Client] Complete Release and QMenu for Qt 6.4 moc and connect

Continuing the Qt 6.4 complete-type sweep:

- window_main.cpp connects ClientUpdateChecker::finishedCheck to
  MainWindow::checkClientUpdatesFinished, whose Release * parameter made
  moc 6.4 instantiate the metatype. GameReplay turned out to be a protobuf
  message, so Release likewise needed release_channel.h in the three
  headers declaring those signals: window_main.h, dlg_update.h and
  client_update_checker.h.
- moc 6.4 also needs sizeof(QMenu) for the QList<QMenu *> carried by
  TabSupervisor::setMenu and MainWindow::updateTabMenu.

Verified by regenerating moc output with the Debian moc 6.4.2 for all 434
Q_OBJECT/Q_GADGET classes and compiling it, plus recompiling all 1612
source TUs, against the Qt 6.4.2 headers: no remaining failures.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-10-08 11:13:50 +02:00
BruebachL
073ec29c4d
[Server] Add deck share links and public deck visibility (#7241)
* [Server] Add deck share links and public deck visibility

* Address server review comments for deck share links

* Document transaction teardown in deck share rollback paths

* Address second round of deck share review comments

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-09-20 20:22:16 +02:00
BruebachL
e11c915a0c
[Servatrice] Detect MySQL strict mode on startup and exit early (#7251)
* [Servatrice] Detect MySQL strict mode on startup and exit early

* Update servatrice/src/servatrice_database_interface.cpp

Co-authored-by: tooomm <tooomm@users.noreply.github.com>

* [Servatrice] Treat failed strict-mode check as boot error

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: tooomm <tooomm@users.noreply.github.com>
2026-09-18 15:02:45 +02:00
BruebachL
d5d99e4dfb
[Server/Client/Protocol] Add developer staff role (#7211)
* [Server/Client/Protocol] Add developer staff role

Introduce a Developer staff level (proto flag 32, DB admin bit 8) that
sits between admin and moderator: no kick/ban/warn/report/admin powers,
but gets server log access via a new developer command container family
(GET_SERVER_STATS, VIEWLOG_HISTORY) and an idle-timeout exemption.

- Protocol: IsDeveloper flag, developer_commands.proto envelope,
  Command_GetServerStats/Command_GetLogHistory, Response_GetServerStats,
  Command_AdjustMod.should_be_developer
- Servatrice: fail-closed developer dispatcher, uptime snapshot handler,
  shared log history handler reuse, bit-8 DB mapping
- Client: burgundy pawn/badge/labels/sort order, prepareDeveloperCommand,
  minimal Developer stats tab, log tab access, promote/demote actions

Took 24 minutes

Took 18 seconds

* [Server/Client/Protocol] Address developer role review feedback

Address ZeizaZach's review of the developer staff role:

- Nudge the developer log query to exclude private chat and sender IPs
  (the ModeratorCommand path still sees everything).
- Deduplicate Command_GetLogHistory into Command_ViewLogHistory, which now
  extends both ModeratorCommand (ext) and DeveloperCommand (dev_ext); the
  client picks the DeveloperCommand-scoped extension by extendee, and the
  server reads it via the extension number.
- Pull the uptime snapshot SQL into Servatrice_DatabaseInterface as
  getLatestUptimeSnapshot() and widen the reported counters to 64-bit.
- Document the admin bitfield (1 admin, 2 moderator, 4 judge, 8 developer)
  and add a server-side test for the developer command path.

* Add missing trailing newline to user_context_menu.cpp

* Remove stale includes of deleted command_get_log_history proto

The Command_GetLogHistory message was folded into Command_ViewLogHistory,
which deleted command_get_log_history.proto, but serversocketinterface
still #included its generated header. Fresh CI builds fail on the missing
file; local builds masked it by reusing a previously generated header.

* [Server] Exclude chat rows when private-chat filter is bypassable

A developer who omits log_location entirely — or sends only "chat" —
leaves chatType, gameType, roomType all false, so getMessageLogHistory
skips the target_type clause and returns every row, private messages
included. When !allowPrivateChat the server now forces game+room when
no surviving location was requested, guaranteeing the query always
carries a target_type restriction.

[Client] Demote mod+dev to moderator path in log-tab dispatch

The developer command family is strictly weaker than the moderator one
(no private chat, no sender_ip, ip filter ignored), so granting the
developer bit to an existing moderator must not silently strip their
capabilities. useDeveloperCommands is now true only when the user holds
the developer bit and not the moderator bit.

[Client] Hide the IP-address filter for developer log tab users

The developer path ignores the ip_address query field server-side.
Showing the field lets a developer type an IP and get results that are
silently unfiltered by it rather than an empty result set — reads as a
broken filter. Hide labelFindIPAddress/findIPAddress alongside the
privateChat checkbox.

* Developer pawn is silver.

* [Client] Fix indentation of merged Card Art Rules / Developer tabs

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-09-11 17:18:56 +02:00
BruebachL
ed4eb1cb31
[Server/Client/Protocol] Reporting users + moderation queue functionality (#7091)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
* [Server/Client/Protocol] Reporting users + moderation queue functionality

Took 6 minutes

Took 3 minutes

Took 8 seconds

Took 11 minutes

Took 12 minutes

Took 7 minutes

Took 15 seconds

Took 2 minutes

Took 1 minute


Took 30 seconds

Took 16 seconds

* CI Fix

Took 6 minutes

* CI Fix

Took 6 minutes

* [Protocol] Add moderation investigation commands

Adds the protocol layer for the moderation investigation suite:
- Command_GetUserSessions/GetUserAlts/GetModeratorLastLogins/ResetUserPassword/RemoveUserAvatar (1013-1017)
- Response extensions 1215-1219 with ServerInfo messages for sessions, alts, and staff logins
- last_login on Response_ReportUserInfo and warning_il on Response_WarnList

Took 2 minutes

* [Utility] Add warning categories parser with infraction levels

Parses the server's 'officialwarnings' setting (comma-separated, optional
'|IL' suffix) into WarningCategory structs so the client can display the
infraction level of each warning category. Includes GTest coverage.

* [Server] Add moderation investigation tools

Implements the server side of the moderation suite:
- getUserSessions/getUserAlts/getModeratorLastLogins/removeUserAvatar DB methods
- Handlers for all five new commands with audit records (PASSWORD_RESET,
  REMOVE_USER_AVATAR); password resets return a generated temporary password
- cmdGetWarnList now reports per-category infraction levels from the
  officialwarnings setting; cmdReportUserInfo reports last_login
- Update servatrice.ini.example with the warning taxonomy
- Password/avatar mutations report RespNameNotFound when the user does not exist

* [Client] Add moderation tab with investigate, password reset, and avatar removal

- New Moderation tab: search a user to show account info, alternate
  accounts, login sessions, and staff last logins; actions to reset the
  user's password (shows the generated temporary password) and remove the
  user's avatar
- 'Investigate user' entry in the user context menu opens the tab pre-loaded
  for that user
- Warning dialog shows the infraction level of each warning category
- Tab wired into TabSupervisor with a moderator-gated menu action, shortcut,
  and tabs.ini persistence (default closed)

* [Server/Client/Protocol] Address PR #7091 review: security, bug, and perf fixes

Security:
- Promote RESET_USER_PASSWORD to admin-only dispatch (was moderator-accessible)
- Reject password reset on users with equal/higher privilege than caller
- Notify affected user via Event_NotifyUser::CUSTOM when password is reset
- Add server-side category whitelist for reports
- Drop reporter name fallback in comment/details authorization (ID-only)
- Force password change: new DB column + login enforcement + client disconnect

Bugs:
- XSS via QTextEdit::append() → insertPlainText() in report tab and utils
- allNotified initialized to true even with empty recipients list
- Warning combo box: use currentData() instead of baked-in display text
- Report resolution now records who resolved (resolved_by column + audit)

Performance:
- IP-correlation subquery: add 6-month window + LIMIT 200
- getUserSessions: clamp limit to 500

Non-blocking:
- Palette-aware colors in report_utils.cpp (dark/light mode)
- Report list pagination: offset/limit fields + total_count in response
- SessionCommand enum gap comment for reserved values 1201-1203

Schema: 36→37 (force_password_change), 37→38 (resolved_by)

Took 12 minutes


Took 16 seconds

* Fix macOs pedantry

Took 5 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-08-21 15:00:13 +02:00
BruebachL
2914874720
[Room][UserList] Introduce style delegate (#6981)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
* [Room] Additionally show a tab for friends and ignored users instead of just all online users.

Took 21 minutes


Took 12 minutes

* [Room][UserList] Introduce style delegate for user list
- Allow users to set a card name and parameters as their background banner
- Allow mods to white/blacklist cards
- Allow toggling back to the old display style

Took 7 minutes

Took 28 seconds

Took 2 minutes

Took 2 minutes

* Right checkstate.

Took 14 minutes


Took 2 minutes

* Utility for test.

Took 9 minutes

Took 8 seconds

Took 2 seconds

* Lint.

Took 10 minutes

* Algorithm for sql schema migration

Took 13 minutes

* Use {prefix}, bound card name, return errors.

Took 27 seconds

* Convert queue to while loop.

Took 19 seconds

* Hover popup.

Took 36 minutes

Took 1 minute

* More granular signals, popup for user info.

Took 25 minutes

Took 8 seconds

Took 16 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-06-26 20:52:24 -04:00
BruebachL
c5fde071e7
[Cleanup] Unused #includes (#6367)
* [Cleanup] Unused #includes

Took 44 minutes

* [Cleanup] More unused #includes

Took 55 minutes

* [Cleanup] Include QSet

Took 4 minutes

* [Cleanup] Include QDebug in deck_list.cpp

Took 3 minutes

* [Cleanup] Include protocol stuff in servatrice_database_interface.h

Took 3 minutes

* [Cleanup] Include QDialogButtonBox

Took 8 minutes

* [Cleanup] Include QUrl

Took 8 minutes

* [Cleanup] Include QTextOption in header.

Took 3 minutes

* [Cleanup] Include QMap in user_list_manager.h

Took 8 minutes

* [Cleanup] Adjust qjson

Took 8 minutes

* [Cleanup] include button box.

Took 3 minutes

* [Cleanup] Redo fwd declarations.

* [Cleanup] Redo last removed fwd declarations.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2025-11-29 18:53:11 +01:00
BruebachL
1ef07309d6
Turn Card, Deck_List, Protocol, RNG, Network (Client, Server), Settings and Utility into libraries and remove cockatrice_common. (#6212)
---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2025-10-09 07:36:12 +02:00
ebbit1q
f0c3860032
squash #6156 (#6161)
* move common server files

* update includes with move

* create participant, move code

* fix linker errors

* fix regressions

* mark function as override to make clang happy

* split out spectator to new file

* forgot to add to cmakelists

* autocompleter picking wrong casing for var name

* clean up forwards declarations in player

* fix includes in game
2025-09-20 14:37:12 +02:00
RickyRister
95cea0f191
Add custom server-side pawn colors (#5543) 2025-02-02 03:25:25 +00:00