diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index ed196e501..a5f9f6747 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -43,9 +43,7 @@ set(cockatrice_SOURCES src/interface/widgets/dialogs/dlg_load_remote_deck.cpp src/interface/widgets/dialogs/dlg_local_game_options.cpp src/interface/widgets/dialogs/dlg_manage_sets.cpp - src/interface/widgets/dialogs/dlg_my_reports.cpp src/interface/widgets/dialogs/dlg_register.cpp - src/interface/widgets/dialogs/dlg_report_user.cpp src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp src/interface/widgets/dialogs/dlg_settings.cpp src/interface/widgets/dialogs/dlg_startup_card_check.cpp @@ -242,10 +240,6 @@ set(cockatrice_SOURCES src/interface/widgets/printing_selector/printing_selector_card_selection_widget.cpp src/interface/widgets/printing_selector/printing_selector_card_sorting_widget.cpp src/interface/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp - src/interface/widgets/playmat/playmat_collection_dialog.cpp - src/interface/widgets/playmat/playmat_collection_dialog.h - src/interface/widgets/playmat/playmat_preview_widget.cpp - src/interface/widgets/playmat/playmat_settings_dialog.cpp src/interface/widgets/quick_settings/settings_button_widget.cpp src/interface/widgets/quick_settings/settings_popup_widget.cpp src/interface/widgets/replay/replay_manager.cpp @@ -284,8 +278,6 @@ set(cockatrice_SOURCES src/interface/widgets/settings_page/user_interface_settings_page.cpp src/interface/widgets/utility/custom_line_edit.cpp src/interface/widgets/utility/get_text_with_max.cpp - src/interface/widgets/utility/report_utils.cpp - src/interface/widgets/utility/report_utils.h src/interface/widgets/utility/sequence_edit.cpp src/interface/widgets/utility/visibility_change_listener.cpp src/interface/widgets/utility/visibility_change_listener.h @@ -379,8 +371,6 @@ set(cockatrice_SOURCES src/interface/widgets/tabs/tab_home.cpp src/interface/widgets/tabs/tab_logs.cpp src/interface/widgets/tabs/tab_message.cpp - src/interface/widgets/tabs/tab_moderation.cpp - src/interface/widgets/tabs/tab_report.cpp src/interface/widgets/tabs/tab_replays.cpp src/interface/widgets/tabs/tab_room.cpp src/interface/widgets/tabs/tab_server.cpp diff --git a/cockatrice/src/client/network/connection_controller/remote_connection_controller.cpp b/cockatrice/src/client/network/connection_controller/remote_connection_controller.cpp index 53dde125f..4e425fb66 100644 --- a/cockatrice/src/client/network/connection_controller/remote_connection_controller.cpp +++ b/cockatrice/src/client/network/connection_controller/remote_connection_controller.cpp @@ -296,15 +296,6 @@ void ConnectionController::onLoginError(int r, return; } - case Response::RespPasswordChangeRequired: { - QMessageBox::information( - dialogParent, tr("Password Change Required"), - tr("An administrator has reset your password. Please contact your server administrator to obtain " - "your temporary password, then log in and change it via Account -> Change Password.")); - remoteClient->disconnectFromServer(); - return; - } - case Response::RespServerFull: { QMessageBox::critical(dialogParent, tr("Server Full"), tr("The server has reached its maximum user capacity, please check back later.")); diff --git a/cockatrice/src/client/settings/shortcuts_settings.h b/cockatrice/src/client/settings/shortcuts_settings.h index f4ebc204e..95155b8d1 100644 --- a/cockatrice/src/client/settings/shortcuts_settings.h +++ b/cockatrice/src/client/settings/shortcuts_settings.h @@ -786,10 +786,6 @@ private: ShortcutGroup::Tabs)}, {"Tabs/aTabLogs", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Logs"), parseSequenceString(""), ShortcutGroup::Tabs)}, - {"Tabs/aTabReport", - ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Report Queue"), parseSequenceString(""), ShortcutGroup::Tabs)}, - {"Tabs/aTabModeration", - ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Moderation"), parseSequenceString(""), ShortcutGroup::Tabs)}, }; }; diff --git a/cockatrice/src/game/game_event_handler.cpp b/cockatrice/src/game/game_event_handler.cpp index bc68d4d7c..95460011f 100644 --- a/cockatrice/src/game/game_event_handler.cpp +++ b/cockatrice/src/game/game_event_handler.cpp @@ -285,10 +285,6 @@ void GameEventHandler::eventGameStateChanged(const Event_GameStateChanged &event emit playerJoined(prop); } player->processPlayerInfo(playerInfo); - // Extract playmat from player properties for opponent display - if (prop.has_playmat_params()) { - player->setPlaymatFromProperties(prop); - } if (player->getPlayerInfo()->getLocal()) { emit localPlayerDeckSelected(player, playerId, playerInfo); } else { @@ -355,11 +351,6 @@ void GameEventHandler::eventPlayerPropertiesChanged(const Event_PlayerProperties const ServerInfo_PlayerProperties &prop = event.player_properties(); emit playerPropertiesChanged(prop, eventPlayerId); - // Update playmat from player properties - if (prop.has_playmat_params()) { - player->setPlaymatFromProperties(prop); - } - const auto contextType = static_cast(getPbExtension(context)); switch (contextType) { case GameEventContext::READY_START: { diff --git a/cockatrice/src/game/player/player_logic.cpp b/cockatrice/src/game/player/player_logic.cpp index 45ba09aac..485e2fc5c 100644 --- a/cockatrice/src/game/player/player_logic.cpp +++ b/cockatrice/src/game/player/player_logic.cpp @@ -250,22 +250,6 @@ void PlayerLogic::setDeck(const DeckList &_deck) emit deckChanged(); } -void PlayerLogic::setPlaymatFromProperties(const ServerInfo_PlayerProperties &props) -{ - if (props.has_playmat_params() && !props.playmat_params().card_name().empty()) { - const auto &pp = props.playmat_params(); - remotePlaymatCard = {QString::fromStdString(pp.card_name()), QString::fromStdString(pp.card_provider_id())}; - remotePlaymatParams = {qBound(0.0, pp.margin_pct_l(), 0.95), qBound(0.0, pp.margin_pct_r(), 0.95), - qBound(0.0, pp.vertical_offset(), 1.0), qBound(0.1, pp.zoom(), 4.0)}; - hasRemotePlaymat = true; - } else { - remotePlaymatCard = CardRef{}; - remotePlaymatParams = PlaymatParams{}; - hasRemotePlaymat = false; - } - emit playmatChanged(); -} - CounterState *PlayerLogic::addCounter(const ServerInfo_Counter &counter) { return addCounter(counter.id(), QString::fromStdString(counter.name()), diff --git a/cockatrice/src/game/player/player_logic.h b/cockatrice/src/game/player/player_logic.h index 6923b3afe..a89cb6eed 100644 --- a/cockatrice/src/game/player/player_logic.h +++ b/cockatrice/src/game/player/player_logic.h @@ -17,7 +17,6 @@ #include "../zones/table_zone_logic.h" #include "player_event_handler.h" #include "player_info.h" -#include "player_manager.h" #include #include @@ -73,8 +72,6 @@ signals: const QList &cardList, bool withWritePermission); void deckChanged(); - /** @brief Emitted when the remote playmat (card/params) is updated from player properties. */ - void playmatChanged(); void newCardAdded(AbstractCardItem *card); void requestCardMenuUpdate(const CardItem *card); void counterAdded(CounterState *state); @@ -229,20 +226,6 @@ public: void setZoneId(int _zoneId); - void setPlaymatFromProperties(const ServerInfo_PlayerProperties &props); - const CardRef &getRemotePlaymatCard() const - { - return remotePlaymatCard; - } - const PlaymatParams &getRemotePlaymatParams() const - { - return remotePlaymatParams; - } - bool getHasRemotePlaymat() const - { - return hasRemotePlaymat; - } - private: AbstractGame *game; PlayerInfo *playerInfo; @@ -260,11 +243,6 @@ private: bool dialogSemaphore; QList cardsToDelete; - - // Playmat from player properties (for opponent display) - CardRef remotePlaymatCard; - PlaymatParams remotePlaymatParams; - bool hasRemotePlaymat = false; }; class AnnotationDialog : public QInputDialog diff --git a/cockatrice/src/game_graphics/deckview/deck_view_container.cpp b/cockatrice/src/game_graphics/deckview/deck_view_container.cpp index 23ed4316d..69941040f 100644 --- a/cockatrice/src/game_graphics/deckview/deck_view_container.cpp +++ b/cockatrice/src/game_graphics/deckview/deck_view_container.cpp @@ -14,15 +14,12 @@ #include #include #include -#include #include #include -#include #include #include #include #include -#include #include #include @@ -103,9 +100,6 @@ DeckViewContainer::DeckViewContainer(int _playerId, TabGame *parent) connect(&SettingsCache::instance().visualDeckStorage(), &VisualDeckStorageSettings::visualDeckStorageInGameChanged, this, &DeckViewContainer::setVisualDeckStorageExists); - connect(&SettingsCache::instance().userInterface(), &InterfaceSettings::playmatSettingsChanged, this, - &DeckViewContainer::onPlaymatSettingsChanged); - switchToDeckSelectView(); } @@ -283,8 +277,6 @@ void DeckViewContainer::loadDeckFromFile(const QString &filePath) void DeckViewContainer::loadDeckFromDeckList(const DeckList &deck) { - currentDeck = deck; - QString deckString = deck.writeToString_Native(); if (deckString.length() > MAX_FILE_LENGTH) { @@ -297,52 +289,6 @@ void DeckViewContainer::loadDeckFromDeckList(const DeckList &deck) PendingCommand *pend = parentGame->getGame()->getGameEventHandler()->prepareGameCommand(cmd); connect(pend, &PendingCommand::finished, this, &DeckViewContainer::deckSelectFinished); parentGame->getGame()->getGameEventHandler()->sendGameCommand(pend, playerId); - - resolveAndSendPlaymat(); -} - -void DeckViewContainer::resolveAndSendPlaymat() -{ - if (currentDeck.getCardRefList().isEmpty() && currentDeck.getPlaymat().card.isEmpty()) { - return; - } - - const auto &settings = SettingsCache::instance().userInterface(); - const auto fallbackBehavior = static_cast(settings.getPlaymatFallbackBehavior()); - - QList fallbackList = settings.getPlaymatFallbackList(); - - // In random mode with 2+ entries, remove the last-resolved mat to avoid repeats. - if (fallbackBehavior == PlaymatFallbackModeRandom && fallbackList.size() > 1) { - fallbackList.removeAll(lastResolvedPlaymat); - } - - const PlaymatInfo resolved = - resolvePlaymatForDeck(currentDeck, fallbackList, static_cast(settings.getPlaymatMode()), - fallbackBehavior, playmatRotationIndex); - - lastResolvedPlaymat = resolved; - - Command_SetPlaymat playmatCmd; - auto *pp = playmatCmd.mutable_playmat_params(); - pp->set_card_name(resolved.card.name.toStdString()); - pp->set_card_provider_id(resolved.card.providerId.toStdString()); - pp->set_margin_pct_l(resolved.params.marginPctL); - pp->set_margin_pct_r(resolved.params.marginPctR); - pp->set_vertical_offset(resolved.params.verticalOffset); - pp->set_zoom(resolved.params.zoom); - PendingCommand *playmatPend = parentGame->getGame()->getGameEventHandler()->prepareGameCommand(playmatCmd); - parentGame->getGame()->getGameEventHandler()->sendGameCommand(playmatPend, playerId); -} - -void DeckViewContainer::onPlaymatSettingsChanged() -{ - resolveAndSendPlaymat(); -} - -void DeckViewContainer::advancePlaymatRotation() -{ - playmatRotationIndex++; } void DeckViewContainer::loadRemoteDeck() @@ -433,10 +379,6 @@ void DeckViewContainer::sideboardPlanChanged() */ void DeckViewContainer::sendReadyStartCommand(bool ready) { - if (ready) { - resolveAndSendPlaymat(); - } - Command_ReadyStart cmd; cmd.set_ready(ready); parentGame->getGame()->getGameEventHandler()->sendGameCommand(cmd, playerId); @@ -474,7 +416,6 @@ void DeckViewContainer::setSideboardLocked(bool locked) void DeckViewContainer::setDeck(const DeckList &deck) { - currentDeck = deck; deckView->setDeck(deck); switchToDeckLoadedView(); } \ No newline at end of file diff --git a/cockatrice/src/game_graphics/deckview/deck_view_container.h b/cockatrice/src/game_graphics/deckview/deck_view_container.h index b5317c39a..ec024bace 100644 --- a/cockatrice/src/game_graphics/deckview/deck_view_container.h +++ b/cockatrice/src/game_graphics/deckview/deck_view_container.h @@ -57,9 +57,6 @@ private: VisualDeckStorageWidget *visualDeckStorageWidget; TabGame *parentGame; int playerId; - int playmatRotationIndex = 0; ///< Per-match cursor for round-robin playmat mode. - DeckList currentDeck; ///< Cached deck for live settings re-resolution. - PlaymatInfo lastResolvedPlaymat; ///< Tracks last sent playmat to avoid repeats in random mode. void tryCreateVisualDeckStorageWidget(); void sendReadyStartCommand(bool ready); @@ -78,7 +75,6 @@ private slots: void sideboardLockButtonClicked(); void updateSideboardLockButtonText(); void refreshShortcuts(); - void onPlaymatSettingsChanged(); signals: void newCardAdded(AbstractCardItem *card); void notIdle(); @@ -91,8 +87,6 @@ public: void setSideboardLocked(bool locked); void setDeck(const DeckList &deck); void setVisualDeckStorageExists(bool exists); - void advancePlaymatRotation(); - void resolveAndSendPlaymat(); public slots: void loadDeckFromFile(const QString &filePath); diff --git a/cockatrice/src/game_graphics/game_scene.cpp b/cockatrice/src/game_graphics/game_scene.cpp index cd2b12828..4d3144ad4 100644 --- a/cockatrice/src/game_graphics/game_scene.cpp +++ b/cockatrice/src/game_graphics/game_scene.cpp @@ -252,27 +252,17 @@ void GameScene::adjustPlayerRotation(int rotationAdjustment) */ void GameScene::rearrange() { - if (rearranging) { - needsReArrange = true; - return; - } - rearranging = true; - do { - needsReArrange = false; + int firstPlayerIndex = 0; + auto playersPlaying = collectActivePlayers(firstPlayerIndex); + playersPlaying = rotatePlayers(playersPlaying, firstPlayerIndex); - int firstPlayerIndex = 0; - auto playersPlaying = collectActivePlayers(firstPlayerIndex); - playersPlaying = rotatePlayers(playersPlaying, firstPlayerIndex); + int columns = determineColumnCount(playersPlaying.size()); + QSizeF sceneSize = computeSceneSizeAndPlayerLayout(playersPlaying, columns); - int columns = determineColumnCount(playersPlaying.size()); - QSizeF sceneSize = computeSceneSizeAndPlayerLayout(playersPlaying, columns); + phasesToolbar->setHeight(sceneSize.height()); + setSceneRect(0, 0, sceneSize.width(), sceneSize.height()); - phasesToolbar->setHeight(sceneSize.height()); - setSceneRect(0, 0, sceneSize.width(), sceneSize.height()); - - processViewSizeChange(viewSize); - } while (needsReArrange); - rearranging = false; + processViewSizeChange(viewSize); } // ---------- View Size ---------- @@ -469,14 +459,8 @@ void GameScene::resizeColumnsAndPlayers(const QList &minWidthByColumn, qr qreal extraWidthPerColumn = (newWidth - minWidth) / playersByColumn.size(); qreal newx = phasesToolbar->getWidth(); - // Snapshot the columns: resizing a player's table can synchronously trigger - // GameScene::rearrange (table width -> sizeChanged -> updateBoundingRect -> - // sizeChanged -> rearrange), and rearrange rebuilds playersByColumn. Iterating - // the live container across that re-entrant call would use invalidated iterators. - const QList> columns = playersByColumn; - - for (int col = 0; col < columns.size(); ++col) { - for (PlayerGraphicsItem *player : columns[col]) { + for (int col = 0; col < playersByColumn.size(); ++col) { + for (PlayerGraphicsItem *player : playersByColumn[col]) { player->processSceneSizeChange(minWidthByColumn[col] + extraWidthPerColumn); player->setPos(newx, player->y()); } diff --git a/cockatrice/src/game_graphics/game_scene.h b/cockatrice/src/game_graphics/game_scene.h index c12696189..7f01bf1f5 100644 --- a/cockatrice/src/game_graphics/game_scene.h +++ b/cockatrice/src/game_graphics/game_scene.h @@ -55,8 +55,6 @@ private: QBasicTimer *animationTimer; ///< Timer for scene animations QHash animatedItems; ///< Items currently animating int playerRotation; ///< Rotation offset for player layout - bool rearranging = false; ///< Guard against re-entrant rearrange - bool needsReArrange = false; ///< Pending rearrange requested during a pass /** * @brief Updates which card is currently hovered based on scene coordinates. diff --git a/cockatrice/src/game_graphics/game_view.cpp b/cockatrice/src/game_graphics/game_view.cpp index ed6355157..b768c8317 100644 --- a/cockatrice/src/game_graphics/game_view.cpp +++ b/cockatrice/src/game_graphics/game_view.cpp @@ -114,7 +114,6 @@ void GameView::startRubberBand(const QPointF &_selectionOrigin) } selectionOrigin = _selectionOrigin; - previousBandRect = QRect(); rubberBand->setGeometry(QRect(mapFromScene(selectionOrigin), QSize(0, 0))); rubberBand->show(); } @@ -129,17 +128,7 @@ void GameView::resizeRubberBand(const QPointF &cursorPoint, int selectedCount) QPoint cursor = cursorPoint.toPoint(); QRect rect = QRect(mapFromScene(selectionOrigin), cursor).normalized(); - rubberBand->setGeometry(rect); - if (viewport()) { - // Repaint the union of the previous and current band rects: the vacated - // strip of a child widget is not reliably invalidated on all platforms - // (notably macOS), leaving stale pixels under the selection. - QRect dirty = previousBandRect.isNull() ? rect : previousBandRect.united(rect); - dirty.adjust(-1, -1, 1, 1); - viewport()->update(dirty); - previousBandRect = rect; - } if (!SettingsCache::instance().userInterface().getShowDragSelectionCount()) { dragCountLabel->hide(); @@ -182,13 +171,7 @@ void GameView::stopRubberBand() return; } - // Same rationale as resizeRubberBand: repaint the last known band area - // since hiding a child widget doesn't reliably invalidate its region. rubberBand->hide(); - if (viewport() && !previousBandRect.isNull()) { - viewport()->update(previousBandRect.adjusted(-1, -1, 1, 1)); - previousBandRect = QRect(); - } dragCountLabel->hide(); } diff --git a/cockatrice/src/game_graphics/game_view.h b/cockatrice/src/game_graphics/game_view.h index a23655513..3f6b60dbc 100644 --- a/cockatrice/src/game_graphics/game_view.h +++ b/cockatrice/src/game_graphics/game_view.h @@ -27,7 +27,6 @@ private: QWidget *tallyContainer; QGridLayout *tallyLayout; QPointF selectionOrigin; - QRect previousBandRect; ///< Last rubber-band rect for targeted repaint QList cachedTallyRows; ///< Cached entries to avoid redundant rebuilds QSize rebuildTallyLabels(const QList &entries); diff --git a/cockatrice/src/game_graphics/player/player_graphics_item.cpp b/cockatrice/src/game_graphics/player/player_graphics_item.cpp index 026e00588..2831f3393 100644 --- a/cockatrice/src/game_graphics/player/player_graphics_item.cpp +++ b/cockatrice/src/game_graphics/player/player_graphics_item.cpp @@ -1,9 +1,6 @@ #include "player_graphics_item.h" #include "../../game/player/player_actions.h" -#include "../../interface/card_picture_loader/card_picture_loader.h" -#include "../../interface/widgets/cards/art_crop_attribution.h" -#include "../../interface/widgets/playmat/playmat_utils.h" #include "../../interface/widgets/tabs/tab_game.h" #include "../board/abstract_card_item.h" #include "../board/counter_general.h" @@ -16,9 +13,6 @@ #include "player_dialogs.h" #include -#include -#include -#include #include PlayerGraphicsItem::PlayerGraphicsItem(PlayerLogic *_player) : player(_player) @@ -34,10 +28,6 @@ PlayerGraphicsItem::PlayerGraphicsItem(PlayerLogic *_player) : player(_player) connect(player, &PlayerLogic::counterAdded, this, &PlayerGraphicsItem::onCounterAdded); connect(player, &PlayerLogic::counterRemoved, this, &PlayerGraphicsItem::onCounterRemoved); - connect(player, &PlayerLogic::deckChanged, this, &PlayerGraphicsItem::updatePlaymat); - connect(player, &PlayerLogic::playmatChanged, this, &PlayerGraphicsItem::updatePlaymat); - connect(&SettingsCache::instance().userInterface(), &InterfaceSettings::playmatVisibilityChanged, this, - [this](int) { updatePlaymat(); }); playerMenu = new PlayerMenu(this); @@ -77,9 +67,6 @@ PlayerGraphicsItem::PlayerGraphicsItem(PlayerLogic *_player) : player(_player) connect(tableZoneGraphicsItem, &TableZone::sizeChanged, this, &PlayerGraphicsItem::updateBoundingRect); - connect(this, &PlayerGraphicsItem::playmatChanged, tableZoneGraphicsItem, &TableZone::onPlaymatChanged); - connect(this, &PlayerGraphicsItem::playmatChanged, stackZoneGraphicsItem, &StackZone::onPlaymatChanged); - updateBoundingRect(); rearrangeZones(); @@ -125,6 +112,7 @@ void PlayerGraphicsItem::initializeZones() rfgZoneGraphicsItem->setPos(base + QPointF(0, 2 * h + h2 + 10)); tableZoneGraphicsItem = new TableZone(player->getTableZone(), mirrored, this); + connect(tableZoneGraphicsItem, &TableZone::sizeChanged, this, &PlayerGraphicsItem::updateBoundingRect); connect(this, &PlayerGraphicsItem::mirroredChanged, tableZoneGraphicsItem, &TableZone::setMirrored); stackZoneGraphicsItem = @@ -167,61 +155,10 @@ qreal PlayerGraphicsItem::getMinimumWidth() const return result; } -void PlayerGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +void PlayerGraphicsItem::paint(QPainter * /*painter*/, + const QStyleOptionGraphicsItem * /*option*/, + QWidget * /*widget*/) { - if (!hasPlaymat || playmatPixmap.isNull()) { - return; - } - - // Calculate the combined bounding rect of stack + table zones - QPointF stackPos = stackZoneGraphicsItem->pos(); - QPointF tablePos = tableZoneGraphicsItem->pos(); - QSizeF stackSize = stackZoneGraphicsItem->boundingRect().size(); - QSizeF tableSize = tableZoneGraphicsItem->boundingRect().size(); - - // Combined area: from stack left edge to table right edge - double combinedLeft = qMin(stackPos.x(), tablePos.x()); - double combinedTop = qMin(stackPos.y(), tablePos.y()); - double combinedRight = qMax(stackPos.x() + stackSize.width(), tablePos.x() + tableSize.width()); - double combinedBottom = qMax(stackPos.y() + stackSize.height(), tablePos.y() + tableSize.height()); - - QRectF combinedArea(combinedLeft, combinedTop, combinedRight - combinedLeft, combinedBottom - combinedTop); - - const QRectF srcRect = computeArtSourceRect(playmatPixmap.size(), playmatParams); - const QRectF dstRect = coverFitRect(combinedArea, srcRect.size()); - - painter->save(); - painter->setClipRect(combinedArea); - painter->setRenderHint(QPainter::SmoothPixmapTransform, true); - - // Render from a down-scaled copy of the art so the full-resolution source - // pixmap is never re-sampled at a tiny device size (also much cheaper than - // scaling it on every frame). - const QPixmap scaledPixmap = scaledPlaymatFor(srcRect, painter->worldTransform().mapRect(dstRect).size()); - painter->drawPixmap(dstRect, scaledPixmap, QRectF(scaledPixmap.rect())); - - painter->restore(); - - if (!playmatAttribution.isEmpty()) { - paintArtAttribution(*painter, combinedArea, playmatAttribution, Qt::AlignRight | Qt::AlignBottom, 0.8); - } -} - -QPixmap PlayerGraphicsItem::scaledPlaymatFor(const QRectF &srcRect, const QSizeF &deviceDstSize) -{ - // Bucket the render size so the source pixmap is re-scaled at most once per - // zoom step instead of once per frame. - constexpr int bucketSize = 32; - const QSize target = QSize(qMax(1, qRound(deviceDstSize.width() / bucketSize) * bucketSize), - qMax(1, qRound(deviceDstSize.height() / bucketSize) * bucketSize)) - .boundedTo(srcRect.toAlignedRect().size()); - - if (scaledPlaymatKey != target) { - const QPixmap crop = playmatPixmap.copy(srcRect.toAlignedRect()); - scaledPlaymatPixmap = crop.scaled(target, Qt::KeepAspectRatio, Qt::SmoothTransformation); - scaledPlaymatKey = target; - } - return scaledPlaymatPixmap; } void PlayerGraphicsItem::processSceneSizeChange(int newPlayerWidth) @@ -366,100 +303,3 @@ void PlayerGraphicsItem::updateBoundingRect() emit sizeChanged(); } - -void PlayerGraphicsItem::updatePlaymat() -{ - int visibility = SettingsCache::instance().userInterface().getPlaymatVisibility(); - - // "Don't use playmats" — never show - if (visibility == PlaymatVisibilityNone) { - clearPlaymat(); - return; - } - - // "Show own playmat only" — hide playmats for remote players - if (visibility == PlaymatVisibilityOwnOnly && !player->getPlayerInfo()->getLocal()) { - clearPlaymat(); - return; - } - - CardRef playmatCard; - PlaymatParams params; - - if (player->getHasRemotePlaymat()) { - // Prefer the server-confirmed playmat (updated by Command_SetPlaymat). - playmatCard = player->getRemotePlaymatCard(); - params = player->getRemotePlaymatParams(); - } else if (player->getPlayerInfo()->getLocal()) { - // Local player without a server broadcast yet: apply the full - // settings-based resolution chain (mode, fallback list, behavior). - const auto &settings = SettingsCache::instance().userInterface(); - const PlaymatInfo resolved = resolvePlaymatForDeck( - player->getDeck(), settings.getPlaymatFallbackList(), static_cast(settings.getPlaymatMode()), - static_cast(settings.getPlaymatFallbackBehavior()), 0); - playmatCard = resolved.card; - params = resolved.params; - } else { - // Opponent without a server broadcast: use the deck-embedded playmat. - const DeckList &deck = player->getDeck(); - const PlaymatInfo &deckPlaymat = deck.getPlaymat(); - if (!deckPlaymat.card.isEmpty()) { - playmatCard = deckPlaymat.card; - params = deckPlaymat.params; - } - } - - if (playmatCard.isEmpty()) { - clearPlaymat(); - return; - } - - playmatParams = params; - scaledPlaymatKey = QSize(); // the art crop depends on the params, drop any cached scale - - ExactCard card = CardDatabaseManager::query()->getCard(playmatCard); - if (!card) { - clearPlaymat(); - return; - } - - playmatAttribution = buildArtAttribution(card); - - QPixmap fullRes; - CardPictureLoader::getPixmap(fullRes, card, QSize(745, 1040)); - - if (fullRes.isNull()) { - disconnect(playmatPixmapConnection); - CardInfo *cardInfo = card.getCardPtr().data(); - if (cardInfo) { - playmatPixmapConnection = - connect(cardInfo, &CardInfo::pixmapUpdated, this, &PlayerGraphicsItem::onPlaymatPixmapReady); - } - return; - } - - if (!hasPlaymat) { - hasPlaymat = true; - emit playmatChanged(true); - } - playmatPixmap = fullRes; - update(); -} - -void PlayerGraphicsItem::clearPlaymat() -{ - disconnect(playmatPixmapConnection); - playmatAttribution.clear(); - if (hasPlaymat) { - hasPlaymat = false; - playmatPixmap = QPixmap(); - scaledPlaymatKey = QSize(); - emit playmatChanged(false); - update(); - } -} - -void PlayerGraphicsItem::onPlaymatPixmapReady() -{ - updatePlaymat(); -} diff --git a/cockatrice/src/game_graphics/player/player_graphics_item.h b/cockatrice/src/game_graphics/player/player_graphics_item.h index e5ae59a61..d02234ded 100644 --- a/cockatrice/src/game_graphics/player/player_graphics_item.h +++ b/cockatrice/src/game_graphics/player/player_graphics_item.h @@ -11,7 +11,6 @@ #include "../game_scene.h" #include -#include class HandZone; class PileZone; @@ -127,7 +126,6 @@ signals: void playerCountChanged(); void mirroredChanged(bool isMirrored); void cardInfoRequested(const CardRef &cardRef); - void playmatChanged(bool hasPlaymat); private: PlayerLogic *player; @@ -148,23 +146,9 @@ private: bool mirrored; bool handVisible = false; - QPixmap playmatPixmap; - QPixmap scaledPlaymatPixmap; // down-scaled copy of playmatPixmap for the current render size - QSize scaledPlaymatKey; // size bucket scaledPlaymatPixmap was rendered for - PlaymatParams playmatParams; - QString playmatAttribution; - bool hasPlaymat = false; - QMetaObject::Connection playmatPixmapConnection; - private slots: void updateBoundingRect(); void rearrangeZones(); - void clearPlaymat(); - void updatePlaymat(); - void onPlaymatPixmapReady(); - -private: - QPixmap scaledPlaymatFor(const QRectF &srcRect, const QSizeF &deviceDstSize); }; #endif // COCKATRICE_PLAYER_GRAPHICS_ITEM_H diff --git a/cockatrice/src/game_graphics/zones/stack_zone.cpp b/cockatrice/src/game_graphics/zones/stack_zone.cpp index e9b14f13d..184f96d62 100644 --- a/cockatrice/src/game_graphics/zones/stack_zone.cpp +++ b/cockatrice/src/game_graphics/zones/stack_zone.cpp @@ -31,22 +31,8 @@ QRectF StackZone::boundingRect() const void StackZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) { - if (playmatActive) { - // Subtle overlay to distinguish stack zone from table zone (slightly darker) - painter->fillRect(boundingRect(), QColor(0, 0, 0, 80)); - } else { - QBrush brush = themeManager->getExtraBgBrush(ThemeManager::Stack, getLogic()->getPlayer()->getZoneId()); - painter->fillRect(boundingRect(), brush); - } -} - -void StackZone::onPlaymatChanged(bool active) -{ - playmatActive = active; - // See TableZone::onPlaymatChanged for the rationale. Translucent overlay - // over a dynamic playmat should not be held in the device cache. - setCacheMode(active ? QGraphicsItem::NoCache : QGraphicsItem::DeviceCoordinateCache); - update(); + QBrush brush = themeManager->getExtraBgBrush(ThemeManager::Stack, getLogic()->getPlayer()->getZoneId()); + painter->fillRect(boundingRect(), brush); } void StackZone::handleDropEvent(const QList &dragItems, diff --git a/cockatrice/src/game_graphics/zones/stack_zone.h b/cockatrice/src/game_graphics/zones/stack_zone.h index 96b3f96a6..147c3e2fc 100644 --- a/cockatrice/src/game_graphics/zones/stack_zone.h +++ b/cockatrice/src/game_graphics/zones/stack_zone.h @@ -15,13 +15,9 @@ class StackZone : public SelectZone Q_OBJECT private: qreal zoneHeight; - bool playmatActive = false; private slots: void updateBg(); -public slots: - void onPlaymatChanged(bool active); - public: StackZone(StackZoneLogic *_logic, int _zoneHeight, QGraphicsItem *parent); /** @brief Resizes the stack zone height, e.g. when sharing vertical space with the command zone. */ diff --git a/cockatrice/src/game_graphics/zones/table_zone.cpp b/cockatrice/src/game_graphics/zones/table_zone.cpp index 88e9abe6c..306e2927e 100644 --- a/cockatrice/src/game_graphics/zones/table_zone.cpp +++ b/cockatrice/src/game_graphics/zones/table_zone.cpp @@ -92,19 +92,14 @@ bool TableZone::isInverted() const void TableZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) { - if (playmatActive) { - // Subtle overlay to distinguish table zone from stack zone - painter->fillRect(boundingRect(), QColor(0, 0, 0, 60)); - } else { - QBrush brush = themeManager->getExtraBgBrush(ThemeManager::Table, getLogic()->getPlayer()->getZoneId()); - painter->fillRect(boundingRect(), brush); - } + QBrush brush = themeManager->getExtraBgBrush(ThemeManager::Table, getLogic()->getPlayer()->getZoneId()); + painter->fillRect(boundingRect(), brush); if (active) { paintZoneOutline(painter); } else { // inactive player gets a darker table zone with a semi transparent black mask - // this means if the user provides a custom background or playmat it will fade + // this means if the user provides a custom background it will fade painter->fillRect(boundingRect(), FADE_MASK); } @@ -118,17 +113,6 @@ void TableZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*opti paintLandDivider(painter); } -void TableZone::onPlaymatChanged(bool active) -{ - playmatActive = active; - // While a playmat is shown the zone paints a translucent overlay over the - // dynamic playmat behind it. Keep it out of the device cache so the cached - // pixels are never stale relative to the playmat (and to avoid compositing - // artifacts of cached translucent content on some platforms). - setCacheMode(active ? QGraphicsItem::NoCache : QGraphicsItem::DeviceCoordinateCache); - update(); -} - /** Render a soft outline around the edge of the TableZone. diff --git a/cockatrice/src/game_graphics/zones/table_zone.h b/cockatrice/src/game_graphics/zones/table_zone.h index 92915a2ed..1836c96ff 100644 --- a/cockatrice/src/game_graphics/zones/table_zone.h +++ b/cockatrice/src/game_graphics/zones/table_zone.h @@ -86,7 +86,6 @@ private: */ bool active = false; bool mirrored = false; - bool playmatActive = false; [[nodiscard]] bool isInverted() const; @@ -96,9 +95,6 @@ private slots: */ void updateBg(); -public slots: - void onPlaymatChanged(bool active); - public slots: /** Reorganizes CardItems in the TableZone @@ -188,17 +184,8 @@ public: } void setWidth(qreal _width) { - // The width is stored as an int; truncate to match the previous implicit conversion. - const int newWidth = static_cast(_width); - if (width == newWidth) { - return; - } prepareGeometryChange(); - width = newWidth; - // The parent player item's boundingRect (which clips the playmat painting) is - // derived from this zone's size. Without this signal the playmat is cut off at - // the stale boundingRect edge whenever the scene is resized wider. - emit sizeChanged(); + width = _width; } [[nodiscard]] qreal getWidth() const { diff --git a/cockatrice/src/interface/intents/contexts/context_join_game.h b/cockatrice/src/interface/intents/contexts/context_join_game.h index 2e5a88ea2..102e2a520 100644 --- a/cockatrice/src/interface/intents/contexts/context_join_game.h +++ b/cockatrice/src/interface/intents/contexts/context_join_game.h @@ -6,7 +6,6 @@ struct ContextJoinGame { ContextJoinRoom roomContext; int gameId; - bool asSpectator = false; }; #endif // COCKATRICE_CONTEXT_JOIN_GAME_H diff --git a/cockatrice/src/interface/intents/intent_join_server_game.cpp b/cockatrice/src/interface/intents/intent_join_server_game.cpp index 205c4dc70..fb9c4d5ce 100644 --- a/cockatrice/src/interface/intents/intent_join_server_game.cpp +++ b/cockatrice/src/interface/intents/intent_join_server_game.cpp @@ -55,7 +55,7 @@ bool IntentJoinServerGame::tryJoinGame(TabRoom *room) return false; } - if (room->getGameSelector()->joinGameById(context->gameId, context->asSpectator)) { + if (room->getGameSelector()->joinGameById(context->gameId)) { emitFinished(); return true; } diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp index 14defc8e9..fc53b296f 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp @@ -2,7 +2,6 @@ #include "../../../client/settings/cache_settings.h" #include "../../../client/settings/shortcuts_settings.h" -#include "../playmat/playmat_settings_dialog.h" #include "../settings_page/user_interface_settings_page.h" #include "../tabs/api/commander_spellbook/commander_bracket_widget.h" #include "deck_list_style_proxy.h" @@ -12,12 +11,10 @@ #include #include #include -#include #include #include #include #include -#include #include #include @@ -231,18 +228,10 @@ void DeckEditorDeckDockWidget::createDeckDock() upperLayout->addWidget(bannerCardLabel, 4, 0); upperLayout->addWidget(bannerCardComboBox, 4, 1); - playmatLabel = new QLabel(); - playmatLabel->setObjectName("playmatLabel"); - playmatLabel->setText(tr("Playmat")); - playmatSettingsButton = new QPushButton(tr("Edit Playmat...")); - connect(playmatSettingsButton, &QPushButton::clicked, this, &DeckEditorDeckDockWidget::openPlaymatSettings); - upperLayout->addWidget(playmatLabel, 5, 0); - upperLayout->addWidget(playmatSettingsButton, 5, 1); + upperLayout->addWidget(deckTagsDisplayWidget, 5, 1); - upperLayout->addWidget(deckTagsDisplayWidget, 6, 1); - - upperLayout->addWidget(activeGroupCriteriaLabel, 7, 0); - upperLayout->addWidget(activeGroupCriteriaComboBox, 7, 1); + upperLayout->addWidget(activeGroupCriteriaLabel, 6, 0); + upperLayout->addWidget(activeGroupCriteriaComboBox, 6, 1); hashLabel1 = new QLabel(); hashLabel1->setObjectName("hashLabel1"); @@ -451,35 +440,6 @@ void DeckEditorDeckDockWidget::writeBannerCard(int index) deckStateManager->setBannerCard(bannerCard); } -void DeckEditorDeckDockWidget::openPlaymatSettings() -{ - PlaymatInfo current = deckStateManager->getMetadata().playmat; - - PlaymatSettingsDialog dialog(current.card, current.params, this); - if (dialog.exec() == QDialog::Accepted) { - CardRef newCard = dialog.card(); - PlaymatParams newParams = dialog.params(); - - if (newCard.isEmpty()) { - deckStateManager->setPlaymat(PlaymatInfo{}); - } else { - deckStateManager->setPlaymat({newCard, newParams}); - } - - updatePlaymatLabel(); - } -} - -void DeckEditorDeckDockWidget::updatePlaymatLabel() -{ - CardRef playmat = deckStateManager->getMetadata().playmat.card; - if (playmat.isEmpty()) { - playmatSettingsButton->setText(tr("Edit Playmat...")); - } else { - playmatSettingsButton->setText(tr("Edit Playmat (%1)").arg(playmat.name)); - } -} - void DeckEditorDeckDockWidget::applyActiveGroupCriteria() { getModel()->setActiveGroupCriteria( @@ -537,7 +497,6 @@ void DeckEditorDeckDockWidget::syncDisplayWidgetsToModel() syncBannerCardComboBoxSelectionWithDeck(); updateBannerCardComboBox(); bannerCardComboBox->blockSignals(false); - updatePlaymatLabel(); updateHash(); formatComboBox->blockSignals(true); diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h index a55056bda..540199f0d 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h @@ -20,7 +20,6 @@ #include #include #include -#include class CommanderBracketWidget; class DeckListModel; @@ -34,8 +33,6 @@ public: DeckListStyleProxy *proxy; QTreeView *deckView; QComboBox *bannerCardComboBox; - QLabel *playmatLabel; - QPushButton *playmatSettingsButton; void createDeckDock(); ExactCard getCurrentCard(); void retranslateUi(); @@ -105,8 +102,6 @@ private slots: void writeName(); void writeComments(); void writeBannerCard(int); - void openPlaymatSettings(); - void updatePlaymatLabel(); void applyActiveGroupCriteria(); void setSelectedIndex(const QModelIndex &newCardIndex, bool preserveWidgetFocus); void updateHash(); diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp index eda741728..f8fb450ce 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp @@ -142,19 +142,6 @@ void DeckStateManager::setBannerCard(const CardRef &bannerCard) doMetadataModified(); } -void DeckStateManager::setPlaymat(const PlaymatInfo &playmat) -{ - PlaymatInfo previous = deckList->getPlaymat(); - if (previous == playmat) { - return; - } - - requestHistorySave(tr("Set playmat to %1").arg(playmat.card.name)); - deckList->setPlaymat(playmat); - - doMetadataModified(); -} - void DeckStateManager::setTags(const QStringList &tags) { QStringList previous = deckList->getTags(); diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.h b/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.h index b9c99903e..6fce6be57 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.h +++ b/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.h @@ -171,7 +171,6 @@ public: void setName(const QString &name); void setComments(const QString &comments); void setBannerCard(const CardRef &bannerCard); - void setPlaymat(const PlaymatInfo &playmat); void setTags(const QStringList &tags); void setFormat(const QString &format); ///@} diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_my_reports.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_my_reports.cpp deleted file mode 100644 index 1af83bfb5..000000000 --- a/cockatrice/src/interface/widgets/dialogs/dlg_my_reports.cpp +++ /dev/null @@ -1,297 +0,0 @@ -#include "dlg_my_reports.h" - -#include "../utility/report_utils.h" -#include "abstract_client.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace -{ -constexpr int COL_ID = 0; -constexpr int COL_TIME = 1; -constexpr int COL_REPORTED = 2; -constexpr int COL_CATEGORY = 3; -constexpr int COL_GAMEID = 4; -constexpr int COL_STATUS = 5; -constexpr int COL_ASSIGNED = 6; -constexpr int COL_COUNT = 7; -} // namespace - -DlgMyReports::DlgMyReports(AbstractClient *_client, QWidget *parent) - : QDialog(parent), client(_client), selectedReportId(-1) -{ - setWindowTitle(tr("My Reports")); - setMinimumSize(800, 500); - - table = new QTableWidget(0, COL_COUNT); - table->setHorizontalHeaderLabels( - {tr("#"), tr("Time"), tr("Reported User"), tr("Category"), tr("Game ID"), tr("Status"), tr("Assigned To")}); - table->setSelectionBehavior(QAbstractItemView::SelectRows); - table->setSelectionMode(QAbstractItemView::SingleSelection); - table->setEditTriggers(QAbstractItemView::NoEditTriggers); - table->setSortingEnabled(true); - table->verticalHeader()->setVisible(false); - table->setAlternatingRowColors(true); - table->horizontalHeader()->setSectionResizeMode(COL_TIME, QHeaderView::ResizeToContents); - table->horizontalHeader()->setSectionResizeMode(COL_REPORTED, QHeaderView::Stretch); - connect(table, &QTableWidget::itemSelectionChanged, this, &DlgMyReports::onSelectionChanged); - - auto *detailsGroup = new QGroupBox(tr("Report Details")); - descriptionEdit = new QTextEdit; - descriptionEdit->setReadOnly(true); - descriptionEdit->setFixedHeight(80); - - auto *chatGroup = new QGroupBox(tr("Chat Log Context")); - chatLogEdit = new QTextEdit; - chatLogEdit->setReadOnly(true); - QFont monoFont("monospace"); - monoFont.setStyleHint(QFont::Monospace); - const int systemPointSize = QFontDatabase::systemFont(QFontDatabase::GeneralFont).pointSize(); - if (systemPointSize > 0) { - monoFont.setPointSize(systemPointSize); - } - chatLogEdit->setFont(monoFont); - auto *chatLayout = new QVBoxLayout(chatGroup); - chatLayout->setContentsMargins(4, 4, 4, 4); - chatLayout->addWidget(chatLogEdit); - - auto *commentsLabel = new QLabel(tr("Comments:")); - commentsEdit = new QTextEdit; - commentsEdit->setReadOnly(true); - commentsEdit->setFixedHeight(120); - - auto *addCommentLabel = new QLabel(tr("Add a comment:")); - commentInput = new QLineEdit; - commentInput->setPlaceholderText(tr("Type your comment here...")); - commentButton = new QPushButton(tr("Send")); - commentButton->setEnabled(false); - connect(commentButton, &QPushButton::clicked, this, &DlgMyReports::addComment); - connect(commentInput, &QLineEdit::returnPressed, this, &DlgMyReports::addComment); - - auto *detailsLayout = new QVBoxLayout(detailsGroup); - detailsLayout->setContentsMargins(4, 4, 4, 4); - detailsLayout->addWidget(descriptionEdit); - detailsLayout->addWidget(chatGroup); - detailsLayout->addWidget(commentsLabel); - detailsLayout->addWidget(commentsEdit); - detailsLayout->addWidget(addCommentLabel); - auto *commentRow = new QHBoxLayout; - commentRow->addWidget(commentInput); - commentRow->addWidget(commentButton); - detailsLayout->addLayout(commentRow); - - closeButton = new QPushButton(tr("Close")); - connect(closeButton, &QPushButton::clicked, this, &QDialog::accept); - - refreshButton = new QPushButton(tr("Refresh")); - connect(refreshButton, &QPushButton::clicked, this, &DlgMyReports::refreshList); - - statusLabel = new QLabel; - - auto *bottomBar = new QHBoxLayout; - bottomBar->addWidget(statusLabel); - bottomBar->addStretch(); - bottomBar->addWidget(refreshButton); - bottomBar->addWidget(closeButton); - - auto *layout = new QVBoxLayout(this); - layout->addWidget(table, 1); - layout->addWidget(detailsGroup); - layout->addLayout(bottomBar); - - setActionsEnabled(false); - refreshList(); -} - -void DlgMyReports::refreshList() -{ - selectedReportIdBeforeRefresh = selectedReportId; - commentDraftBeforeRefresh = commentInput->text(); - statusLabel->setText(tr("Loading...")); - refreshButton->setEnabled(false); - table->setRowCount(0); - currentReports.clear(); - descriptionEdit->clear(); - chatLogEdit->clear(); - commentsEdit->clear(); - commentInput->clear(); - setActionsEnabled(false); - selectedReportId = -1; - - Command_ReportMyList cmd; - - PendingCommand *pend = client->prepareSessionCommand(cmd); - connect(pend, &PendingCommand::finished, this, &DlgMyReports::reportListResponse); - client->sendCommand(pend); -} - -void DlgMyReports::reportListResponse(const Response &response) -{ - refreshButton->setEnabled(true); - - if (response.response_code() != Response::RespOk) { - statusLabel->setText(tr("Failed to load reports.")); - return; - } - - const Response_ReportMyList &resp = response.GetExtension(Response_ReportMyList::ext); - currentReports.clear(); - for (int i = 0; i < resp.reports_size(); ++i) { - currentReports.append(resp.reports(i)); - } - - table->setSortingEnabled(false); - table->setRowCount(currentReports.size()); - - for (int row = 0; row < currentReports.size(); ++row) { - const ServerInfo_Report &r = currentReports[row]; - - report_utils::fillReportTableRow(table, row, r, COL_ID, COL_TIME, COL_REPORTED, COL_CATEGORY, COL_GAMEID, - COL_STATUS, COL_ASSIGNED); - } - - table->setSortingEnabled(true); - table->resizeColumnsToContents(); - table->horizontalHeader()->setSectionResizeMode(COL_REPORTED, QHeaderView::Stretch); - - if (selectedReportIdBeforeRefresh >= 0) { - for (int row = 0; row < table->rowCount(); ++row) { - if (table->item(row, COL_ID) && - table->item(row, COL_ID)->data(Qt::UserRole).toInt() == selectedReportIdBeforeRefresh) { - table->setCurrentCell(row, 0); - break; - } - } - } - - if (commentInput->text().isEmpty()) { - commentInput->setText(commentDraftBeforeRefresh); - } - - statusLabel->setText(tr("%1 report(s)").arg(currentReports.size())); -} - -void DlgMyReports::onSelectionChanged() -{ - const int row = table->currentRow(); - if (row < 0 || !table->item(row, COL_ID)) { - descriptionEdit->clear(); - chatLogEdit->clear(); - commentsEdit->clear(); - commentInput->clear(); - commentButton->setEnabled(false); - selectedReportId = -1; - return; - } - - const int reportId = table->item(row, COL_ID)->data(Qt::UserRole).toInt(); - selectedReportId = reportId; - - for (const ServerInfo_Report &r : currentReports) { - if (r.report_id() == reportId) { - descriptionEdit->setPlainText(QString::fromStdString(r.description())); - break; - } - } - - chatLogEdit->setPlainText(tr("Loading...")); - commentsEdit->setPlainText(tr("Loading...")); - - Command_ReportDetails cmd; - cmd.set_report_id(reportId); - - PendingCommand *pend = client->prepareSessionCommand(cmd); - connect(pend, &PendingCommand::finished, this, &DlgMyReports::reportDetailsResponse); - client->sendCommand(pend); - - QString status = table->item(row, COL_STATUS)->text(); - bool canComment = (status == "open" || status == "assigned"); - commentButton->setEnabled(canComment); - commentInput->setEnabled(canComment); - if (!canComment) { - commentInput->setPlaceholderText(tr("This report is closed.")); - } else { - commentInput->setPlaceholderText(tr("Type your comment here...")); - } -} - -void DlgMyReports::reportDetailsResponse(const Response &response) -{ - if (response.response_code() != Response::RespOk) { - if (selectedReportId == -1) { - return; - } - chatLogEdit->clear(); - commentsEdit->setPlainText(tr("Failed to load report details.")); - return; - } - - const Response_ReportDetails &resp = response.GetExtension(Response_ReportDetails::ext); - const ServerInfo_Report &r = resp.report(); - - if (selectedReportId != r.report_id()) { - return; - } - - loadReportDetails(r); -} - -void DlgMyReports::loadReportDetails(const ServerInfo_Report &report) -{ - report_utils::renderReportDetails(chatLogEdit, commentsEdit, report, tr("No comments yet."), tr("[Moderator]"), - tr("[You]")); -} - -void DlgMyReports::addComment() -{ - if (selectedReportId < 0) { - return; - } - - QString text = commentInput->text().trimmed(); - if (text.isEmpty()) { - return; - } - - commentButton->setEnabled(false); - - Command_ReportAddComment cmd; - cmd.set_report_id(selectedReportId); - cmd.set_comment(text.toStdString()); - - PendingCommand *pend = client->prepareSessionCommand(cmd); - connect(pend, &PendingCommand::finished, this, &DlgMyReports::addCommentResponse); - client->sendCommand(pend); -} - -void DlgMyReports::addCommentResponse(const Response &response) -{ - if (response.response_code() == Response::RespOk) { - commentInput->clear(); - refreshList(); - } else { - commentButton->setEnabled(true); - } -} - -void DlgMyReports::setActionsEnabled(bool enabled) -{ - commentButton->setEnabled(enabled); - commentInput->setEnabled(enabled); -} diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_my_reports.h b/cockatrice/src/interface/widgets/dialogs/dlg_my_reports.h deleted file mode 100644 index 09b1963dd..000000000 --- a/cockatrice/src/interface/widgets/dialogs/dlg_my_reports.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef COCKATRICE_DLG_MY_REPORTS_H -#define COCKATRICE_DLG_MY_REPORTS_H - -#include -#include -#include -#include - -class AbstractClient; -class QTableWidget; -class QTextEdit; -class QLineEdit; -class QPushButton; -class QLabel; - -class DlgMyReports : public QDialog -{ - Q_OBJECT -public: - explicit DlgMyReports(AbstractClient *_client, QWidget *parent = nullptr); - -private slots: - void refreshList(); - void reportListResponse(const Response &response); - void onSelectionChanged(); - void reportDetailsResponse(const Response &response); - void addComment(); - void addCommentResponse(const Response &response); - -private: - void loadReportDetails(const ServerInfo_Report &report); - void setActionsEnabled(bool enabled); - - AbstractClient *client; - - QTableWidget *table; - QTextEdit *descriptionEdit; - QTextEdit *chatLogEdit; - QTextEdit *commentsEdit; - QLineEdit *commentInput; - QPushButton *commentButton; - QPushButton *refreshButton; - QPushButton *closeButton; - QLabel *statusLabel; - - QList currentReports; - int selectedReportId; - int selectedReportIdBeforeRefresh = -1; - QString commentDraftBeforeRefresh; -}; - -#endif // COCKATRICE_DLG_MY_REPORTS_H diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_report_user.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_report_user.cpp deleted file mode 100644 index 9519846e2..000000000 --- a/cockatrice/src/interface/widgets/dialogs/dlg_report_user.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include "dlg_report_user.h" - -#include "abstract_client.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -DlgReportUser::DlgReportUser(AbstractClient *_client, - const QString &_reportedUser, - int _gameId, - const QString &_autoChatLog, - QWidget *parent) - : QDialog(parent), client(_client), reportedUser(_reportedUser), gameId(_gameId) -{ - setWindowTitle(tr("Report User")); - setMinimumWidth(500); - - auto *infoLabel = - new QLabel(tr("Reports are reviewed by moderators. False reports may result in account penalties.")); - infoLabel->setWordWrap(true); - infoLabel->setStyleSheet("color: palette(placeholderText); padding: 5px;"); - - auto *reportGroup = new QGroupBox(tr("Report Details")); - auto *reportGrid = new QGridLayout(reportGroup); - - reportGrid->addWidget(new QLabel(tr("Reported User:")), 0, 0); - reportedUserLabel = new QLabel(reportedUser); - reportedUserLabel->setStyleSheet("font-weight: bold;"); - reportGrid->addWidget(reportedUserLabel, 0, 1); - - reportGrid->addWidget(new QLabel(tr("Game ID:")), 1, 0); - if (gameId >= 0) { - gameIdLabel = new QLabel(QString::number(gameId)); - gameIdLabel->setStyleSheet("font-weight: bold;"); - reportGrid->addWidget(gameIdLabel, 1, 1); - } else { - gameIdEdit = new QLineEdit; - gameIdEdit->setPlaceholderText(tr("(Optional) Enter game ID if available")); - gameIdEdit->setToolTip(tr("If the report is related to a specific game, enter its ID.")); - reportGrid->addWidget(gameIdEdit, 1, 1); - gameIdLabel = nullptr; - } - - auto *categoryGroup = new QGroupBox(tr("Category")); - auto *categoryGrid = new QGridLayout(categoryGroup); - - categoryBox = new QComboBox; - categoryBox->addItem(tr("Cheating / Unsporting behavior"), "cheating"); - categoryBox->setItemData(categoryBox->count() - 1, - tr("Using external tools, card marked manipulation, or exploiting game bugs"), - Qt::ToolTipRole); - categoryBox->addItem(tr("Harassment / Abuse"), "harassment"); - categoryBox->setItemData(categoryBox->count() - 1, tr("Threatening, bullying, or persistent unwanted contact"), - Qt::ToolTipRole); - categoryBox->addItem(tr("Hate speech"), "hate_speech"); - categoryBox->setItemData(categoryBox->count() - 1, - tr("Discriminatory language targeting race, gender, religion, etc."), Qt::ToolTipRole); - categoryBox->addItem(tr("Spam"), "spam"); - categoryBox->setItemData(categoryBox->count() - 1, tr("Repeated unwanted messages or advertisements"), - Qt::ToolTipRole); - categoryBox->addItem(tr("Other"), "other"); - categoryBox->setItemData(categoryBox->count() - 1, tr("Any behavior not covered by the above categories"), - Qt::ToolTipRole); - - categoryGrid->addWidget(new QLabel(tr("Category:")), 0, 0); - categoryGrid->addWidget(categoryBox, 0, 1); - - auto *descGroup = new QGroupBox(tr("Description")); - auto *descLayout = new QVBoxLayout(descGroup); - - descriptionEdit = new QTextEdit; - descriptionEdit->setPlaceholderText( - tr("Please describe what happened. Include dates, game details, or any evidence if available.")); - descriptionEdit->setFixedHeight(120); - descLayout->addWidget(descriptionEdit); - - auto *chatGroup = new QGroupBox(tr("Chat Log Context")); - auto *chatLayout = new QVBoxLayout(chatGroup); - chatLogEdit = new QTextEdit; - chatLogEdit->setReadOnly(true); - QFont monoFont("monospace"); - monoFont.setStyleHint(QFont::Monospace); - const int systemPointSize = QFontDatabase::systemFont(QFontDatabase::GeneralFont).pointSize(); - if (systemPointSize > 0) { - monoFont.setPointSize(systemPointSize); - } - chatLogEdit->setFont(monoFont); - if (!_autoChatLog.isEmpty()) { - chatLogEdit->setPlainText(_autoChatLog); - } else { - chatLogEdit->setPlaceholderText(tr("No chat context available (not triggered from chat).")); - } - chatLogEdit->setFixedHeight(100); - chatLayout->addWidget(chatLogEdit); - - auto *chatNote = new QLabel( - tr("This chat log is captured from your local chat window and may not reflect the full conversation.")); - chatNote->setWordWrap(true); - chatNote->setStyleSheet("color: palette(placeholderText);"); - chatLayout->addWidget(chatNote); - - buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Submit Report")); - connect(buttonBox, &QDialogButtonBox::accepted, this, &DlgReportUser::actSubmit); - connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); - - auto *layout = new QVBoxLayout(this); - layout->addWidget(infoLabel); - layout->addWidget(reportGroup); - layout->addWidget(categoryGroup); - layout->addWidget(descGroup); - layout->addWidget(chatGroup); - layout->addWidget(buttonBox); -} - -void DlgReportUser::actSubmit() -{ - const QString description = descriptionEdit->toPlainText().trimmed(); - if (description.isEmpty()) { - QMessageBox::warning(this, tr("Missing description"), tr("Please describe what happened before submitting.")); - return; - } - - QMessageBox::StandardButton reply = - QMessageBox::question(this, tr("Confirm Report"), - tr("Submit report against %1 for %2?").arg(reportedUser, categoryBox->currentText()), - QMessageBox::Yes | QMessageBox::No); - - if (reply != QMessageBox::Yes) { - return; - } - - buttonBox->setEnabled(false); - buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Submitting...")); - - Command_Report cmd; - cmd.set_reported_user(reportedUser.toStdString()); - cmd.set_category(categoryBox->currentData().toString().toStdString()); - cmd.set_description(description.toStdString()); - - if (gameId >= 0) { - cmd.set_game_id(gameId); - } else if (gameIdEdit && !gameIdEdit->text().trimmed().isEmpty()) { - bool ok; - int manualGameId = gameIdEdit->text().trimmed().toInt(&ok); - if (ok && manualGameId > 0) { - cmd.set_game_id(manualGameId); - } - } - - const QString chatLog = chatLogEdit->toPlainText().trimmed(); - if (!chatLog.isEmpty()) { - cmd.set_chat_log(chatLog.toStdString()); - } - - PendingCommand *pend = client->prepareSessionCommand(cmd); - connect(pend, &PendingCommand::finished, this, &DlgReportUser::reportResponse); - client->sendCommand(pend); -} - -void DlgReportUser::reportResponse(const Response &response) -{ - buttonBox->setEnabled(true); - buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Submit Report")); - - if (response.response_code() == Response::RespOk) { - QMessageBox::information(this, tr("Report Submitted"), - tr("Your report has been submitted and will be reviewed by a moderator. Thank you.")); - accept(); - } else if (response.response_code() == Response::RespTooManyRequests) { - QMessageBox::warning(this, tr("Submission Failed"), - tr("You have reached the daily report limit. Please try again later.")); - } else if (response.response_code() == Response::RespNameNotFound) { - QMessageBox::warning( - this, tr("Submission Failed"), - tr("The reported user could not be found. Guests (unregistered users) cannot be reported.")); - } else { - QMessageBox::warning(this, tr("Submission Failed"), tr("Failed to submit report. Please try again.")); - } -} diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_report_user.h b/cockatrice/src/interface/widgets/dialogs/dlg_report_user.h deleted file mode 100644 index c59fc5084..000000000 --- a/cockatrice/src/interface/widgets/dialogs/dlg_report_user.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef COCKATRICE_DLG_REPORT_USER_H -#define COCKATRICE_DLG_REPORT_USER_H - -#include -#include - -class AbstractClient; -class QComboBox; -class QDialogButtonBox; -class QLineEdit; -class QTextEdit; -class QLabel; - -class DlgReportUser : public QDialog -{ - Q_OBJECT -public: - DlgReportUser(AbstractClient *_client, - const QString &_reportedUser, - int _gameId = -1, - const QString &_autoChatLog = QString(), - QWidget *parent = nullptr); - -private slots: - void actSubmit(); - void reportResponse(const Response &response); - -private: - AbstractClient *client; - QString reportedUser; - int gameId; - - QLabel *reportedUserLabel; - QLabel *gameIdLabel = nullptr; - QLineEdit *gameIdEdit = nullptr; - QComboBox *categoryBox; - QTextEdit *descriptionEdit; - QTextEdit *chatLogEdit; - QDialogButtonBox *buttonBox; -}; - -#endif // COCKATRICE_DLG_REPORT_USER_H diff --git a/cockatrice/src/interface/widgets/playmat/playmat_collection_dialog.cpp b/cockatrice/src/interface/widgets/playmat/playmat_collection_dialog.cpp deleted file mode 100644 index e970f8413..000000000 --- a/cockatrice/src/interface/widgets/playmat/playmat_collection_dialog.cpp +++ /dev/null @@ -1,188 +0,0 @@ -#include "playmat_collection_dialog.h" - -#include "../../../client/settings/cache_settings.h" -#include "playmat_settings_dialog.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -PlaymatCollectionDialog::PlaymatCollectionDialog(QWidget *parent) : QDialog(parent) -{ - setMinimumWidth(420); - setupUi(); - retranslateUi(); -} - -void PlaymatCollectionDialog::accept() -{ - auto &interfaceSettings = SettingsCache::instance().userInterface(); - interfaceSettings.setPlaymatFallbackList(playmats); - interfaceSettings.setPlaymatFallbackBehavior(modeCombo->currentData().toInt()); - QDialog::accept(); -} - -int PlaymatCollectionDialog::currentRow() const -{ - return playmatList->currentRow(); -} - -void PlaymatCollectionDialog::setupUi() -{ - auto &interfaceSettings = SettingsCache::instance().userInterface(); - playmats = interfaceSettings.getPlaymatFallbackList(); - - playmatList = new QListWidget; - for (const PlaymatInfo &entry : playmats) { - playmatList->addItem(entry.card.name); - } - connect(playmatList, &QListWidget::itemSelectionChanged, this, &PlaymatCollectionDialog::selectionChanged); - connect(playmatList, &QListWidget::itemDoubleClicked, this, [this](QListWidgetItem *) { editPlaymat(); }); - - addButton = new QPushButton; - editButton = new QPushButton; - removeButton = new QPushButton; - moveUpButton = new QPushButton; - moveDownButton = new QPushButton; - - connect(addButton, &QPushButton::clicked, this, &PlaymatCollectionDialog::addPlaymat); - connect(editButton, &QPushButton::clicked, this, &PlaymatCollectionDialog::editPlaymat); - connect(removeButton, &QPushButton::clicked, this, &PlaymatCollectionDialog::removePlaymat); - connect(moveUpButton, &QPushButton::clicked, this, &PlaymatCollectionDialog::movePlaymatUp); - connect(moveDownButton, &QPushButton::clicked, this, &PlaymatCollectionDialog::movePlaymatDown); - - auto *listButtons = new QVBoxLayout; - listButtons->addWidget(addButton); - listButtons->addWidget(editButton); - listButtons->addWidget(removeButton); - listButtons->addWidget(moveUpButton); - listButtons->addWidget(moveDownButton); - listButtons->addStretch(); - - auto *listRow = new QHBoxLayout; - listRow->addWidget(playmatList, 1); - listRow->addLayout(listButtons); - - modeCombo = new QComboBox; - modeCombo->addItem(QString(), PlaymatFallbackModeFixed); - modeCombo->addItem(QString(), PlaymatFallbackModeRoundRobin); - modeCombo->addItem(QString(), PlaymatFallbackModeRandom); - const int modeIndex = modeCombo->findData(interfaceSettings.getPlaymatFallbackBehavior()); - if (modeIndex >= 0) { - modeCombo->setCurrentIndex(modeIndex); - } - - auto *modeRow = new QHBoxLayout; - modeLabel = new QLabel; - modeLabel->setBuddy(modeCombo); - modeRow->addWidget(modeLabel); - modeRow->addWidget(modeCombo, 1); - - auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - connect(buttonBox, &QDialogButtonBox::accepted, this, &PlaymatCollectionDialog::accept); - connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); - - auto *root = new QVBoxLayout; - root->addLayout(listRow); - root->addLayout(modeRow); - root->addWidget(buttonBox); - setLayout(root); - - selectionChanged(); -} - -void PlaymatCollectionDialog::selectionChanged() -{ - const bool hasSelection = playmatList->currentRow() >= 0; - editButton->setEnabled(hasSelection); - removeButton->setEnabled(hasSelection); - moveUpButton->setEnabled(hasSelection && playmatList->currentRow() > 0); - moveDownButton->setEnabled(hasSelection && playmatList->currentRow() < playmatList->count() - 1); -} - -void PlaymatCollectionDialog::addPlaymat() -{ - PlaymatSettingsDialog dialog(CardRef{}, PlaymatParams{}, this); - if (dialog.exec() == QDialog::Accepted) { - const CardRef card = dialog.card(); - if (!card.isEmpty()) { - PlaymatInfo res = {card, dialog.params()}; - playmats.append(res); - playmatList->addItem(res.card.name); - playmatList->setCurrentRow(playmatList->count() - 1); - } - } -} - -void PlaymatCollectionDialog::editPlaymat() -{ - const int row = currentRow(); - if (row < 0) { - return; - } - - const PlaymatInfo ¤t = playmats.at(row); - PlaymatSettingsDialog dialog(current.card, current.params, this); - if (dialog.exec() == QDialog::Accepted) { - const CardRef card = dialog.card(); - if (card.isEmpty()) { - return; // Removal is handled by the Remove button - } - playmats[row] = {card, dialog.params()}; - playmatList->item(row)->setText(card.name); - } -} - -void PlaymatCollectionDialog::removePlaymat() -{ - const int row = currentRow(); - if (row < 0) { - return; - } - playmats.removeAt(row); - delete playmatList->takeItem(row); - selectionChanged(); -} - -void PlaymatCollectionDialog::movePlaymatUp() -{ - const int row = currentRow(); - if (row <= 0) { - return; - } - playmats.swapItemsAt(row, row - 1); - playmatList->insertItem(row - 1, playmatList->takeItem(row)); - playmatList->setCurrentRow(row - 1); - selectionChanged(); -} - -void PlaymatCollectionDialog::movePlaymatDown() -{ - const int row = currentRow(); - if (row < 0 || row >= playmats.size() - 1) { - return; - } - playmats.swapItemsAt(row, row + 1); - playmatList->insertItem(row + 1, playmatList->takeItem(row)); - playmatList->setCurrentRow(row + 1); - selectionChanged(); -} - -void PlaymatCollectionDialog::retranslateUi() -{ - setWindowTitle(tr("Default Playmats")); - addButton->setText(tr("Add...")); - editButton->setText(tr("Edit...")); - removeButton->setText(tr("Remove")); - moveUpButton->setText(tr("Move Up")); - moveDownButton->setText(tr("Move Down")); - modeLabel->setText(tr("List mode:")); - modeCombo->setItemText(0, tr("Fixed (always the first entry)")); - modeCombo->setItemText(1, tr("Round-robin (cycle through entries)")); - modeCombo->setItemText(2, tr("Random (pick one per game)")); -} diff --git a/cockatrice/src/interface/widgets/playmat/playmat_collection_dialog.h b/cockatrice/src/interface/widgets/playmat/playmat_collection_dialog.h deleted file mode 100644 index 167893f8e..000000000 --- a/cockatrice/src/interface/widgets/playmat/playmat_collection_dialog.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef COCKATRICE_PLAYMAT_COLLECTION_DIALOG_H -#define COCKATRICE_PLAYMAT_COLLECTION_DIALOG_H - -#include -#include - -class QComboBox; -class QLabel; -class QListWidget; -class QListWidgetItem; -class QPushButton; - -/** - * @brief Dialog for editing the user-level playmat collection. - * - * The collection is the fallback used when a deck has no playmat of its own. - * It supports multiple entries and a pick mode (always first / round-robin / - * random). The dialog edits a working copy and writes it to the settings only - * when accepted. - */ -class PlaymatCollectionDialog : public QDialog -{ - Q_OBJECT - -public: - explicit PlaymatCollectionDialog(QWidget *parent = nullptr); - - void accept() override; - -private slots: - void addPlaymat(); - void editPlaymat(); - void removePlaymat(); - void movePlaymatUp(); - void movePlaymatDown(); - void selectionChanged(); - -private: - void setupUi(); - void retranslateUi(); - int currentRow() const; - - QList playmats; ///< Working copy edited by the dialog. - QListWidget *playmatList; - QComboBox *modeCombo; - QLabel *modeLabel; - QPushButton *addButton; - QPushButton *editButton; - QPushButton *removeButton; - QPushButton *moveUpButton; - QPushButton *moveDownButton; -}; - -#endif // COCKATRICE_PLAYMAT_COLLECTION_DIALOG_H diff --git a/cockatrice/src/interface/widgets/playmat/playmat_preview_widget.cpp b/cockatrice/src/interface/widgets/playmat/playmat_preview_widget.cpp deleted file mode 100644 index dc3afc2cd..000000000 --- a/cockatrice/src/interface/widgets/playmat/playmat_preview_widget.cpp +++ /dev/null @@ -1,99 +0,0 @@ -#include "playmat_preview_widget.h" - -#include "../cards/art_crop_attribution.h" -#include "playmat_utils.h" - -#include -#include -#include - -PlaymatPreviewWidget::PlaymatPreviewWidget(QWidget *parent) : QWidget(parent) -{ - setMinimumSize(400, 120); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); -} - -void PlaymatPreviewWidget::setPixmap(const QPixmap &pixmap) -{ - sourcePixmap = pixmap; - update(); -} - -void PlaymatPreviewWidget::setParams(const PlaymatParams &p) -{ - params = p; - update(); -} - -void PlaymatPreviewWidget::setAttribution(const QString &attribution) -{ - attributionText = attribution; - update(); -} - -void PlaymatPreviewWidget::paintEvent(QPaintEvent *) -{ - QPainter painter(this); - painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing); - - const QRect rect = this->rect(); - const QColor accentColor(100, 116, 139); - - // Background - const QRectF cardRect = QRectF(rect).adjusted(3, 2, -3, -2); - QLinearGradient bg(cardRect.topLeft(), cardRect.topRight()); - bg.setColorAt(0, accentColor.darker(320)); - bg.setColorAt(1, QColor(18, 22, 30)); - painter.setPen(Qt::NoPen); - painter.setBrush(bg); - painter.drawRoundedRect(cardRect, 6, 6); - painter.setBrush(accentColor); - painter.drawRoundedRect(QRectF(cardRect.left(), cardRect.top(), 3, cardRect.height()), 2, 2); - - if (sourcePixmap.isNull()) { - painter.setPen(QColor(150, 150, 150)); - painter.drawText(rect, Qt::AlignCenter, tr("No card selected")); - return; - } - - // Draw the playmat art using the same logic as PlayerGraphicsItem - // The preview area represents the combined stack+table play area - // Stack is ~20% width on the left, table is ~80% on the right - const QRectF playArea = cardRect.adjusted(6, 4, -4, -4); - - const QRectF srcRect = computeArtSourceRect(sourcePixmap.size(), params); - const QRectF dstRect = coverFitRect(playArea, srcRect.size()); - - painter.setClipRect(playArea.toRect()); - painter.drawPixmap(dstRect, sourcePixmap, srcRect); - painter.setClipping(false); - - // Draw zone divider: stack is roughly the left portion - const double stackWidthRatio = 0.18; // Stack is about 18% of total play area - const double stackDividerX = playArea.left() + playArea.width() * stackWidthRatio; - - // Subtle semi-transparent overlays to distinguish zones - // Stack zone overlay (slightly darker) - QRectF stackOverlay(playArea.left(), playArea.top(), playArea.width() * stackWidthRatio, playArea.height()); - painter.fillRect(stackOverlay, QColor(0, 0, 0, 40)); - - // Table zone overlay (very subtle) - QRectF tableOverlay(stackDividerX, playArea.top(), playArea.width() * (1.0 - stackWidthRatio), playArea.height()); - painter.fillRect(tableOverlay, QColor(0, 0, 0, 20)); - - // Zone divider line - painter.setPen(QPen(QColor(255, 255, 255, 50), 1)); - painter.drawLine(QPointF(stackDividerX, playArea.top()), QPointF(stackDividerX, playArea.bottom())); - - // Land divider line (about 60% down the table area) - const double landDividerY = playArea.top() + playArea.height() * 0.65; - painter.setPen(QPen(QColor(255, 255, 255, 30), 1)); - painter.drawLine(QPointF(stackDividerX, landDividerY), QPointF(playArea.right(), landDividerY)); - - // Border around entire play area - painter.setPen(QPen(QColor(70, 80, 95, 120), 1)); - painter.setBrush(Qt::NoBrush); - painter.drawRoundedRect(playArea.adjusted(0, 0, -1, -1), 3, 3); - - paintArtAttribution(painter, playArea, attributionText, Qt::AlignRight | Qt::AlignBottom, 0.8); -} diff --git a/cockatrice/src/interface/widgets/playmat/playmat_preview_widget.h b/cockatrice/src/interface/widgets/playmat/playmat_preview_widget.h deleted file mode 100644 index 55771192f..000000000 --- a/cockatrice/src/interface/widgets/playmat/playmat_preview_widget.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef COCKATRICE_PLAYMAT_PREVIEW_WIDGET_H -#define COCKATRICE_PLAYMAT_PREVIEW_WIDGET_H - -#include -#include -#include - -/** - * @brief Preview widget that shows how a playmat card art will appear - * across the combined table + stack play area. - * - * Renders a miniature mockup with the card art applied using the - * given PlaymatParams, including faint zone divider lines. - */ -class PlaymatPreviewWidget : public QWidget -{ - Q_OBJECT - -public: - explicit PlaymatPreviewWidget(QWidget *parent = nullptr); - - void setPixmap(const QPixmap &pixmap); - void setParams(const PlaymatParams ¶ms); - void setAttribution(const QString &attribution); - -protected: - void paintEvent(QPaintEvent *event) override; - -private: - QPixmap sourcePixmap; - PlaymatParams params; - QString attributionText; -}; - -#endif // COCKATRICE_PLAYMAT_PREVIEW_WIDGET_H diff --git a/cockatrice/src/interface/widgets/playmat/playmat_settings_dialog.cpp b/cockatrice/src/interface/widgets/playmat/playmat_settings_dialog.cpp deleted file mode 100644 index 72c715e13..000000000 --- a/cockatrice/src/interface/widgets/playmat/playmat_settings_dialog.cpp +++ /dev/null @@ -1,277 +0,0 @@ -#include "playmat_settings_dialog.h" - -#include "../../card_picture_loader/card_picture_loader.h" -#include "../cards/art_crop_attribution.h" -#include "../utility/completer_utils.h" -#include "card_database_display_model.h" -#include "card_database_model.h" -#include "playmat_preview_widget.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -PlaymatSettingsDialog::PlaymatSettingsDialog(const CardRef &initialCard, - const PlaymatParams &initialParams, - QWidget *parent) - : QDialog(parent), currentCard(initialCard), currentParams(initialParams) -{ - setMinimumWidth(500); - setupUi(); - - // Seed UI from initial values - if (!initialCard.name.isEmpty()) { - searchBar->setText(initialCard.name); - onCardNameChanged(initialCard.name); - - // onCardNameChanged leaves the printing combo on the first printing in - // the database, which would silently change the deck's stored playmat - // card on accept. Restore the stored printing when it resolves locally. - const int storedPrintingIndex = providerComboBox->findData(initialCard.providerId); - if (storedPrintingIndex != -1) { - providerComboBox->setCurrentIndex(storedPrintingIndex); - } else { - // Stored printing not in the local database: keep it rather than - // silently substituting the first printing. - currentCard.providerId = initialCard.providerId; - reloadPreview(); - } - } - marginLSpin->setValue(initialParams.marginPctL); - marginRSpin->setValue(initialParams.marginPctR); - verticalOffsetSpin->setValue(initialParams.verticalOffset); - zoomSpin->setValue(initialParams.zoom); - - retranslateUi(); -} - -CardRef PlaymatSettingsDialog::card() const -{ - return currentCard; -} - -PlaymatParams PlaymatSettingsDialog::params() const -{ - return currentParams; -} - -QDoubleSpinBox *PlaymatSettingsDialog::makeSpinBox(double min, double max, double value, double step) -{ - auto *spin = new QDoubleSpinBox; - spin->setRange(min, max); - spin->setSingleStep(step); - spin->setDecimals(3); - spin->setValue(value); - return spin; -} - -void PlaymatSettingsDialog::initializeSearchBar() -{ - searchBar = new QLineEdit; - - cardDatabaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), false, this); - cardDatabaseDisplayModel = new CardDatabaseDisplayModel(this); - cardDatabaseDisplayModel->setSourceModel(cardDatabaseModel); - - const CardCompleterSetup cardSetup = createCardCompleter(cardDatabaseDisplayModel, this, 15); - searchModel = cardSetup.searchModel; - proxyModel = cardSetup.proxyModel; - completer = cardSetup.completer; - searchBar->setCompleter(completer); - - connectCardCompleterSearch(searchBar, cardSetup); - - connect(completer, static_cast(&QCompleter::activated), this, - [this](const QString &completion) { - if (searchBar->text() != completion) { - searchBar->setText(completion); - searchBar->setCursorPosition(searchBar->text().length()); - } - onCardNameChanged(completion); - }); - - connect(searchBar, &QLineEdit::returnPressed, this, [this]() { onCardNameChanged(searchBar->text()); }); -} - -void PlaymatSettingsDialog::setupUi() -{ - initializeSearchBar(); - - providerComboBox = new QComboBox; - connect(providerComboBox, &QComboBox::currentIndexChanged, this, [this]() { - currentCard.providerId = providerComboBox->currentData().toString(); - reloadPreview(); - onParamChanged(); - }); - - marginLSpin = makeSpinBox(0.0, 0.95, currentParams.marginPctL, 0.01); - marginRSpin = makeSpinBox(0.0, 0.95, currentParams.marginPctR, 0.01); - verticalOffsetSpin = makeSpinBox(0.0, 1.0, currentParams.verticalOffset, 0.01); - zoomSpin = makeSpinBox(0.1, 4.0, currentParams.zoom, 0.05); - - auto *form = new QFormLayout; - cardNameLabel = new QLabel; - printingLabel = new QLabel; - leftMarginLabel = new QLabel; - rightMarginLabel = new QLabel; - verticalOffsetLabel = new QLabel; - zoomLabel = new QLabel; - form->addRow(cardNameLabel, searchBar); - form->addRow(printingLabel, providerComboBox); - form->addRow(leftMarginLabel, marginLSpin); - form->addRow(rightMarginLabel, marginRSpin); - form->addRow(verticalOffsetLabel, verticalOffsetSpin); - form->addRow(zoomLabel, zoomSpin); - - controlsGroup = new QGroupBox; - controlsGroup->setLayout(form); - - preview = new PlaymatPreviewWidget; - - auto *previewLayout = new QVBoxLayout; - previewLayout->addWidget(preview); - previewGroup = new QGroupBox; - previewGroup->setLayout(previewLayout); - - auto *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - removeButton = new QPushButton; - buttons->addButton(removeButton, QDialogButtonBox::ResetRole); - - connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); - connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); - connect(removeButton, &QPushButton::clicked, this, [this]() { - currentCard = CardRef{}; // empty signals removal - accept(); - }); - - auto *root = new QVBoxLayout; - root->addWidget(controlsGroup); - root->addWidget(previewGroup); - root->addWidget(buttons); - setLayout(root); - - connect(marginLSpin, &QDoubleSpinBox::valueChanged, this, &PlaymatSettingsDialog::onParamChanged); - connect(marginRSpin, &QDoubleSpinBox::valueChanged, this, &PlaymatSettingsDialog::onParamChanged); - connect(verticalOffsetSpin, &QDoubleSpinBox::valueChanged, this, &PlaymatSettingsDialog::onParamChanged); - connect(zoomSpin, &QDoubleSpinBox::valueChanged, this, &PlaymatSettingsDialog::onParamChanged); -} - -void PlaymatSettingsDialog::populateProviderCombo(const QString &cardName) -{ - providerComboBox->clear(); - - auto card = CardDatabaseManager::query()->getCard({cardName}); - - const auto &sets = card.getInfo().getSets(); - - for (const auto &printings : sets) { - for (const auto &p : printings) { - QString setName = p.getSet()->getLongName(); - QString collector = p.getProperty("num"); - QString uuid = p.getUuid(); - - QString label = setName; - if (!collector.isEmpty()) { - label += " #" + collector; - } - - providerComboBox->addItem(label, uuid); - } - } -} - -void PlaymatSettingsDialog::onCardNameChanged(const QString &name) -{ - if (name.isEmpty()) { - currentPixmap = QPixmap(); - preview->setPixmap(currentPixmap); - return; - } - - const ExactCard card = CardDatabaseManager::query()->getCard({name}); - if (!card) { - currentPixmap = QPixmap(); - preview->setPixmap(currentPixmap); - providerComboBox->clear(); - return; - } - - currentCard.name = name; - - populateProviderCombo(name); - - if (providerComboBox->count() == 0) { - currentPixmap = QPixmap(); - preview->setPixmap(currentPixmap); - currentCard.providerId.clear(); - return; - } - - currentCard.providerId = providerComboBox->currentData().toString(); - reloadPreview(); -} - -void PlaymatSettingsDialog::reloadPreview() -{ - if (currentCard.name.isEmpty()) { - return; - } - - ExactCard card = CardDatabaseManager::query()->getCard({currentCard.name, currentCard.providerId}); - if (!card) { - return; - } - - disconnect(pixmapUpdatedConnection); - - QPixmap fullRes; - CardPictureLoader::getPixmap(fullRes, card, QSize(745, 1040)); - - if (fullRes.isNull()) { - CardInfo *cardInfo = card.getCardPtr().data(); - if (cardInfo) { - pixmapUpdatedConnection = connect(cardInfo, &CardInfo::pixmapUpdated, this, [this]() { reloadPreview(); }); - } - return; - } - - currentPixmap = fullRes; - preview->setPixmap(currentPixmap); - preview->setParams(currentParams); - preview->setAttribution(buildArtAttribution(card)); -} - -void PlaymatSettingsDialog::onParamChanged() -{ - currentParams.marginPctL = marginLSpin->value(); - currentParams.marginPctR = marginRSpin->value(); - currentParams.verticalOffset = verticalOffsetSpin->value(); - currentParams.zoom = zoomSpin->value(); - preview->setParams(currentParams); -} - -void PlaymatSettingsDialog::retranslateUi() -{ - setWindowTitle(tr("Playmat Settings")); - searchBar->setPlaceholderText(tr("Type a card name...")); - cardNameLabel->setText(tr("Card name:")); - printingLabel->setText(tr("Printing:")); - leftMarginLabel->setText(tr("Left margin (%):")); - rightMarginLabel->setText(tr("Right margin (%):")); - verticalOffsetLabel->setText(tr("Vertical offset:")); - zoomLabel->setText(tr("Zoom:")); - controlsGroup->setTitle(tr("Parameters")); - previewGroup->setTitle(tr("Preview")); - removeButton->setText(tr("Remove Playmat")); -} diff --git a/cockatrice/src/interface/widgets/playmat/playmat_settings_dialog.h b/cockatrice/src/interface/widgets/playmat/playmat_settings_dialog.h deleted file mode 100644 index 7ccd1569d..000000000 --- a/cockatrice/src/interface/widgets/playmat/playmat_settings_dialog.h +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef COCKATRICE_PLAYMAT_SETTINGS_DIALOG_H -#define COCKATRICE_PLAYMAT_SETTINGS_DIALOG_H - -#include -#include -#include - -class QComboBox; -class QCompleter; -class QDoubleSpinBox; -class QGroupBox; -class QLabel; -class QLineEdit; -class QPushButton; -class CardDatabaseModel; -class CardDatabaseDisplayModel; -class CardSearchModel; -class CardCompleterProxyModel; -class PlaymatPreviewWidget; - -/** - * @brief Dialog for configuring the playmat card art for a deck. - * - * Allows the user to select a card from the database and adjust - * positioning parameters (margins, zoom, vertical offset) for how - * the card art appears as a playmat background across the - * combined table + stack play area. - */ -class PlaymatSettingsDialog : public QDialog -{ - Q_OBJECT - -public: - explicit PlaymatSettingsDialog(const CardRef &initialCard = {}, - const PlaymatParams &initialParams = {}, - QWidget *parent = nullptr); - - CardRef card() const; - PlaymatParams params() const; - -private slots: - void onCardNameChanged(const QString &name); - void reloadPreview(); - void onParamChanged(); - -private: - void setupUi(); - void populateProviderCombo(const QString &cardName); - void initializeSearchBar(); - void retranslateUi(); - QDoubleSpinBox *makeSpinBox(double min, double max, double value, double step); - - QLineEdit *searchBar; - QCompleter *completer; - CardDatabaseModel *cardDatabaseModel; - CardDatabaseDisplayModel *cardDatabaseDisplayModel; - CardSearchModel *searchModel; - CardCompleterProxyModel *proxyModel; - - QComboBox *providerComboBox; - - QMetaObject::Connection pixmapUpdatedConnection; - - QLabel *cardNameLabel; - QLabel *printingLabel; - QLabel *leftMarginLabel; - QLabel *rightMarginLabel; - QLabel *verticalOffsetLabel; - QLabel *zoomLabel; - QGroupBox *controlsGroup; - QGroupBox *previewGroup; - QPushButton *removeButton; - - QDoubleSpinBox *marginLSpin; - QDoubleSpinBox *marginRSpin; - QDoubleSpinBox *verticalOffsetSpin; - QDoubleSpinBox *zoomSpin; - PlaymatPreviewWidget *preview; - - QPixmap currentPixmap; - CardRef currentCard; - PlaymatParams currentParams; -}; - -#endif // COCKATRICE_PLAYMAT_SETTINGS_DIALOG_H diff --git a/cockatrice/src/interface/widgets/playmat/playmat_utils.h b/cockatrice/src/interface/widgets/playmat/playmat_utils.h deleted file mode 100644 index 9ab8190b3..000000000 --- a/cockatrice/src/interface/widgets/playmat/playmat_utils.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef COCKATRICE_PLAYMAT_UTILS_H -#define COCKATRICE_PLAYMAT_UTILS_H - -#include -#include -#include -#include - -/** - * @brief Computes the source region of the full-resolution card image to use as a playmat. - * - * Parameters are relative to the full card image: horizontal margins trim the card - * borders, the vertical offset positions a square viewing window, and zoom scales - * into that window. The result is clamped to the card image bounds. - * - * @param fullCardSize Size of the full card image. - * @param params Positioning parameters. - * @return Source rectangle in full-card image pixel coordinates. - */ -inline QRectF computeArtSourceRect(const QSize &fullCardSize, const PlaymatParams ¶ms) -{ - const qreal srcW = fullCardSize.width(); - const qreal srcH = fullCardSize.height(); - - const qreal marginL = params.marginPctL * srcW; - const qreal marginR = params.marginPctR * srcW; - // Guard against margins summing to >= 1 (both are individually in range), - // which would otherwise make the viewing window negative or zero. - const qreal visibleW = qMax(0.0, srcW - marginL - marginR); - const qreal visibleH = visibleW; // square viewing window, keeps art unskewed - - const qreal vCenter = params.verticalOffset * srcH; - qreal srcY = vCenter - visibleH / 2.0; - srcY = qBound(0.0, srcY, srcH - visibleH); - - // Guard the zoom divisor; everything that produces params clamps zoom to - // [0.1, 4.0] already, this keeps the render path self-contained. - const qreal zoom = qBound(0.1, params.zoom, 4.0); - const qreal zoomedW = visibleW / zoom; - const qreal zoomedH = visibleH / zoom; - const qreal zoomedX = marginL + (visibleW - zoomedW) / 2.0; - const qreal zoomedY = srcY + (visibleH - zoomedH) / 2.0; - - return QRectF(zoomedX, zoomedY, zoomedW, zoomedH); -} - -/** - * @brief Returns the destination rectangle that fits a source of the given aspect - * ratio into dstArea using "cover" semantics (no distortion, overflows cropped). - * - * @param dstArea Area to fill. - * @param srcSize Size of the source; only its aspect ratio matters. - * @return Destination rectangle centered in dstArea. - */ -inline QRectF coverFitRect(const QRectF &dstArea, const QSizeF &srcSize) -{ - const qreal srcAspect = srcSize.width() / srcSize.height(); - const qreal dstAspect = dstArea.width() / dstArea.height(); - - if (srcAspect > dstAspect) { - const qreal dstW = dstArea.height() * srcAspect; - return QRectF(dstArea.left() + (dstArea.width() - dstW) / 2.0, dstArea.top(), dstW, dstArea.height()); - } - - const qreal dstH = dstArea.width() / srcAspect; - return QRectF(dstArea.left(), dstArea.top() + (dstArea.height() - dstH) / 2.0, dstArea.width(), dstH); -} - -#endif // COCKATRICE_PLAYMAT_UTILS_H diff --git a/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp b/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp index bebc2e3c4..e62195c2f 100644 --- a/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp +++ b/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp @@ -273,14 +273,6 @@ void ChatView::appendMessage(QString message, // messageType should be Event_RoomSay::UserMessage though we don't actually check bool isUserMessage = !(userName.toLower() == "servatrice" || userName.isEmpty()); bool sameSender = isUserMessage && userName == lastSender; - - if (isUserMessage) { - chatHistory.append({userName, message, QDateTime::currentDateTime()}); - while (chatHistory.size() > MAX_CHAT_HISTORY) { - chatHistory.removeFirst(); - } - } - QTextCursor cursor = prepareBlock(sameSender); lastSender = userName; @@ -658,19 +650,6 @@ void ChatView::clearChat() document()->clear(); lastSender = ""; evenNumber = true; - chatHistory.clear(); -} - -QString ChatView::getRecentChatLog(int maxMessages) const -{ - QStringList lines; - int start = qMax(0, chatHistory.size() - maxMessages); - for (int i = start; i < chatHistory.size(); ++i) { - const ChatLogEntry &entry = chatHistory.at(i); - lines.append( - QString("[%1] %2: %3").arg(entry.timestamp.toString("hh:mm:ss")).arg(entry.userName).arg(entry.message)); - } - return lines.join("\n"); } void ChatView::redactMessages(const QString &userName, int amount) diff --git a/cockatrice/src/interface/widgets/server/chat_view/chat_view.h b/cockatrice/src/interface/widgets/server/chat_view/chat_view.h index 671b35163..c58efa2c6 100644 --- a/cockatrice/src/interface/widgets/server/chat_view/chat_view.h +++ b/cockatrice/src/interface/widgets/server/chat_view/chat_view.h @@ -33,13 +33,6 @@ public: QTextBlock block; }; -struct ChatLogEntry -{ - QString userName; - QString message; - QDateTime timestamp; -}; - class ChatView : public QTextBrowser { Q_OBJECT @@ -72,8 +65,6 @@ private: QString hoveredContent; QAction *messageClicked; QMap> userMessagePositions; - QList chatHistory; - static constexpr int MAX_CHAT_HISTORY = 200; [[nodiscard]] QTextFragment getFragmentUnderMouse(const QPoint &pos) const; QTextCursor prepareBlock(bool same = false); @@ -116,7 +107,6 @@ public: bool playerBold = false); void clearChat(); void redactMessages(const QString &userName, int amount); - QString getRecentChatLog(int maxMessages = 50) const; protected: void enterEvent(QEnterEvent *event) override; diff --git a/cockatrice/src/interface/widgets/server/game_selector.cpp b/cockatrice/src/interface/widgets/server/game_selector.cpp index f41002247..28e2ae607 100644 --- a/cockatrice/src/interface/widgets/server/game_selector.cpp +++ b/cockatrice/src/interface/widgets/server/game_selector.cpp @@ -359,11 +359,7 @@ void GameSelector::joinGame(const bool asSpectator, const bool asJudge) return; } - joinGame(gameListModel->getGame(ind.data(Qt::UserRole).toInt()), asSpectator, asJudge); -} - -void GameSelector::joinGame(const ServerInfo_Game &game, const bool asSpectator, const bool asJudge) -{ + const ServerInfo_Game &game = gameListModel->getGame(ind.data(Qt::UserRole).toInt()); if (tabSupervisor->switchToGameTabIfAlreadyExists(game.game_id())) { return; } @@ -418,16 +414,18 @@ void GameSelector::joinGame(const ServerInfo_Game &game, const bool asSpectator, disableButtons(); } -bool GameSelector::joinGameById(const int gameId, const bool asSpectator) +bool GameSelector::joinGameById(int gameId) { - for (int row = 0; row < gameListModel->rowCount(); ++row) { - const ServerInfo_Game &game = gameListModel->getGame(row); - if (game.game_id() != gameId) { - continue; - } + auto *model = gameListView->model(); - joinGame(game, asSpectator); - return true; + for (int row = 0; row < model->rowCount(); ++row) { + QModelIndex idx = model->index(row, 0); + const ServerInfo_Game &game = gameListModel->getGame(idx.data(Qt::UserRole).toInt()); + if (game.game_id() == gameId) { + gameListView->setCurrentIndex(idx); + joinGame(); + return true; + } } qWarning() << "Game" << gameId << "not found"; diff --git a/cockatrice/src/interface/widgets/server/game_selector.h b/cockatrice/src/interface/widgets/server/game_selector.h index 9af39cf50..da34d5322 100644 --- a/cockatrice/src/interface/widgets/server/game_selector.h +++ b/cockatrice/src/interface/widgets/server/game_selector.h @@ -171,17 +171,6 @@ private: */ void joinGame(bool asSpectator = false, bool asJudge = false); - /** - * @brief Performs the join or spectate action for a specific game. - * @param game The game to join. - * @param asSpectator True to join as a spectator, false to join as a player. - * @param asJudge True to join as a judge, false to join as a player. - * - * Unlike the selection-based overload, this does not depend on the game being - * visible in the filtered game list. - */ - void joinGame(const ServerInfo_Game &game, bool asSpectator = false, bool asJudge = false); - public: /** * @brief Constructs a GameSelector widget. @@ -213,16 +202,7 @@ public: * @param info The ServerInfo_Game object containing information about the game to update. */ void processGameInfo(const ServerInfo_Game &info); - /** - * @brief Finds a game by ID and joins or spectates it. - * @param gameId The ID of the game to join. - * @param asSpectator True to join as a spectator, false to join as a player. - * @return True if the game was found and joined, false otherwise. - * - * Unlike the selection-based overload, this does not depend on the game - * being visible in the filtered game list. - */ - bool joinGameById(int gameId, bool asSpectator = false); + bool joinGameById(int gameId); }; #endif diff --git a/cockatrice/src/interface/widgets/server/user/user_context_menu.cpp b/cockatrice/src/interface/widgets/server/user/user_context_menu.cpp index 646f2ee33..372dbfc19 100644 --- a/cockatrice/src/interface/widgets/server/user/user_context_menu.cpp +++ b/cockatrice/src/interface/widgets/server/user/user_context_menu.cpp @@ -1,6 +1,5 @@ #include "user_context_menu.h" -#include "../../dialogs/dlg_report_user.h" #include "../../interface/widgets/tabs/tab_account.h" #include "../../interface/widgets/tabs/tab_game.h" #include "../../interface/widgets/tabs/tab_supervisor.h" @@ -42,7 +41,6 @@ UserContextMenu::UserContextMenu(TabSupervisor *_tabSupervisor, QWidget *parent, aAddToIgnoreList = new QAction(QString(), this); aRemoveFromIgnoreList = new QAction(QString(), this); aKick = new QAction(QString(), this); - aReport = new QAction(QString(), this); aWarnUser = new QAction(QString(), this); aWarnHistory = new QAction(QString(), this); aBan = new QAction(QString(), this); @@ -52,7 +50,6 @@ UserContextMenu::UserContextMenu(TabSupervisor *_tabSupervisor, QWidget *parent, aPromoteToJudge = new QAction(QString(), this); aDemoteFromJudge = new QAction(QString(), this); aGetAdminNotes = new QAction(QString(), this); - aInvestigateUser = new QAction(QString(), this); retranslateUi(); } @@ -67,7 +64,6 @@ void UserContextMenu::retranslateUi() aAddToIgnoreList->setText(tr("Add to &ignore list")); aRemoveFromIgnoreList->setText(tr("Remove from &ignore list")); aKick->setText(tr("Kick from &game")); - aReport->setText(tr("Report user")); aWarnUser->setText(tr("Warn user")); aWarnHistory->setText(tr("View user's war&n history")); aBan->setText(tr("Ban from &server")); @@ -77,7 +73,6 @@ void UserContextMenu::retranslateUi() aPromoteToJudge->setText(tr("Promote user to &judge")); aDemoteFromJudge->setText(tr("Demote user from judge")); aGetAdminNotes->setText(tr("View admin notes")); - aInvestigateUser->setText(tr("Investigate user")); } void UserContextMenu::gamesOfUserReceived(const Response &resp, const CommandContainer &commandContainer) @@ -149,8 +144,7 @@ void UserContextMenu::warnUser_processGetWarningsListResponse(const Response &r) if (response.warning_size() > 0) { for (int i = 0; i < response.warning_size(); ++i) { - int startingIl = i < response.warning_il_size() ? response.warning_il(i) : 1; - dlg->addWarningOption(QString::fromStdString(response.warning(i)).simplified(), startingIl); + dlg->addWarningOption(QString::fromStdString(response.warning(i)).simplified()); } } dlg->show(); @@ -401,9 +395,6 @@ void UserContextMenu::showContextMenu(const QPoint &pos, aRemoveMessages = new QAction(tr("Remove this user's messages"), this); menu->addAction(aRemoveMessages); } - if (userListProxy->isOwnUserRegistered()) { - menu->addAction(aReport); - } if (game && (game->isHost() || !tabSupervisor->getAdminLocked())) { menu->addSeparator(); menu->addAction(aKick); @@ -417,7 +408,6 @@ void UserContextMenu::showContextMenu(const QPoint &pos, menu->addAction(aBanHistory); menu->addSeparator(); menu->addAction(aGetAdminNotes); - menu->addAction(aInvestigateUser); menu->addSeparator(); if (userLevel.testFlag(ServerInfo_User::IsModerator) && @@ -441,7 +431,6 @@ void UserContextMenu::showContextMenu(const QPoint &pos, aDetails->setEnabled(true); aChat->setEnabled(anotherUser && online); aShowGames->setEnabled(online); - aReport->setEnabled(anotherUser); aAddToBuddyList->setEnabled(anotherUser); aRemoveFromBuddyList->setEnabled(anotherUser); aAddToIgnoreList->setEnabled(anotherUser); @@ -452,7 +441,6 @@ void UserContextMenu::showContextMenu(const QPoint &pos, aBan->setEnabled(anotherUser); aBanHistory->setEnabled(anotherUser); aGetAdminNotes->setEnabled(anotherUser); - aInvestigateUser->setEnabled(anotherUser); aPromoteToMod->setEnabled(anotherUser); aDemoteFromMod->setEnabled(anotherUser); @@ -474,15 +462,6 @@ void UserContextMenu::showContextMenu(const QPoint &pos, execRemoveFromIgnore(userName); } else if (actionClicked == aKick) { execKick(playerId); - } else if (actionClicked == aReport) { - int gameId = game ? game->getGameMetaInfo()->gameId() : -1; - QString autoChatLog; - if (chatView) { - autoChatLog = chatView->getRecentChatLog(50); - } - auto dlgReport = new DlgReportUser(client, userName, gameId, autoChatLog, static_cast(parent())); - dlgReport->setAttribute(Qt::WA_DeleteOnClose); - dlgReport->exec(); } else if (actionClicked == aBan) { execBan(userName); } else if (actionClicked == aPromoteToMod || actionClicked == aDemoteFromMod) { @@ -497,8 +476,6 @@ void UserContextMenu::showContextMenu(const QPoint &pos, execWarnHistory(userName); } else if (actionClicked == aGetAdminNotes) { execAdminNotes(userName); - } else if (actionClicked == aInvestigateUser) { - execInvestigateUser(userName); } else if (actionClicked == aCopyToClipBoard) { QClipboard *clipboard = QGuiApplication::clipboard(); clipboard->setText(deckHash); @@ -675,11 +652,6 @@ void UserContextMenu::execAdminNotes(const QString &userName) client->sendCommand(pend); } -void UserContextMenu::execInvestigateUser(const QString &userName) -{ - tabSupervisor->openTabModeration(userName); -} - void UserContextMenu::execAdjustMod(const QString &userName, bool shouldBeMod) { Command_AdjustMod cmd; diff --git a/cockatrice/src/interface/widgets/server/user/user_context_menu.h b/cockatrice/src/interface/widgets/server/user/user_context_menu.h index f1ce931f8..70bbff977 100644 --- a/cockatrice/src/interface/widgets/server/user/user_context_menu.h +++ b/cockatrice/src/interface/widgets/server/user/user_context_menu.h @@ -41,14 +41,12 @@ private: QAction *aAddToBuddyList, *aRemoveFromBuddyList; QAction *aAddToIgnoreList, *aRemoveFromIgnoreList; QAction *aKick; - QAction *aReport; QAction *aBan, *aBanHistory; QAction *aPromoteToMod, *aDemoteFromMod; QAction *aPromoteToJudge, *aDemoteFromJudge; QAction *aWarnUser, *aWarnHistory; QAction *aGetAdminNotes; std::function()> gameInviteLinkProvider; - QAction *aInvestigateUser; signals: void openMessageDialog(const QString &userName, bool focus); private slots: @@ -120,7 +118,6 @@ public: void execBanHistory(const QString &userName); void execWarnHistory(const QString &userName); void execAdminNotes(const QString &userName); - void execInvestigateUser(const QString &userName); void execAdjustMod(const QString &userName, bool shouldBeMod); void execAdjustJudge(const QString &userName, bool shouldBeJudge); diff --git a/cockatrice/src/interface/widgets/server/user/user_list_widget.cpp b/cockatrice/src/interface/widgets/server/user/user_list_widget.cpp index 2534ee62c..3dac7944d 100644 --- a/cockatrice/src/interface/widgets/server/user/user_list_widget.cpp +++ b/cockatrice/src/interface/widgets/server/user/user_list_widget.cpp @@ -151,7 +151,7 @@ WarningDialog::WarningDialog(const QString userName, const QString clientID, QWi warnClientID = new QLineEdit(clientID); warnClientID->setMaxLength(MAX_NAME_LENGTH); warningOption = new QComboBox(); - warningOption->addItem("", ""); + warningOption->addItem(""); deleteMessages = new QCheckBox(tr("Redact all messages from this user in all rooms")); @@ -184,7 +184,7 @@ void WarningDialog::okClicked() return; } - if (warningOption->currentData().toString().simplified().isEmpty()) { + if (warningOption->currentText().simplified().isEmpty()) { QMessageBox::critical(this, tr("Error"), tr("Warning to use can not be blank, please select a valid warning to send.")); return; @@ -205,7 +205,7 @@ QString WarningDialog::getWarnID() const QString WarningDialog::getReason() const { - return warningOption->currentData().toString().simplified(); + return warningOption->currentText().simplified(); } int WarningDialog::getDeleteMessages() const @@ -213,13 +213,9 @@ int WarningDialog::getDeleteMessages() const return deleteMessages->isChecked() ? -1 : 0; } -void WarningDialog::addWarningOption(const QString warning, int startingIl) +void WarningDialog::addWarningOption(const QString warning) { - if (startingIl > 1) { - warningOption->addItem(tr("%1 (IL %2)").arg(warning).arg(startingIl), warning); - } else { - warningOption->addItem(warning, warning); - } + warningOption->addItem(warning); } void BanDialog::okClicked() diff --git a/cockatrice/src/interface/widgets/server/user/user_list_widget.h b/cockatrice/src/interface/widgets/server/user/user_list_widget.h index e7a5116ef..0407ad8ca 100644 --- a/cockatrice/src/interface/widgets/server/user/user_list_widget.h +++ b/cockatrice/src/interface/widgets/server/user/user_list_widget.h @@ -85,7 +85,7 @@ public: [[nodiscard]] QString getWarnID() const; [[nodiscard]] QString getReason() const; [[nodiscard]] int getDeleteMessages() const; - void addWarningOption(const QString warning, int startingIl = 1); + void addWarningOption(const QString warning); }; class AdminNotesDialog : public QDialog diff --git a/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.cpp b/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.cpp index 149395194..c470e54fe 100644 --- a/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.cpp +++ b/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.cpp @@ -7,14 +7,11 @@ #include "../dialogs/override_printing_warning.h" #include "../interface/theme_manager.h" #include "../interface/widgets/general/background_sources.h" -#include "../playmat/playmat_collection_dialog.h" -#include "../playmat/playmat_settings_dialog.h" #include #include #include #include -#include #include #include #include @@ -328,52 +325,10 @@ AppearanceSettingsPage::AppearanceSettingsPage() tableGroupBox = new QGroupBox; tableGroupBox->setLayout(tableGrid); - // Playmat settings - playmatVisibilityCombo.addItem(tr("Show all playmats"), PlaymatVisibilityAll); - playmatVisibilityCombo.addItem(tr("Show own playmat only"), PlaymatVisibilityOwnOnly); - playmatVisibilityCombo.addItem(tr("Don't use playmats"), PlaymatVisibilityNone); - int visIdx = playmatVisibilityCombo.findData(settings.userInterface().getPlaymatVisibility()); - if (visIdx >= 0) { - playmatVisibilityCombo.setCurrentIndex(visIdx); - } - connect(&playmatVisibilityCombo, qOverload(&QComboBox::currentIndexChanged), this, [this](int index) { - SettingsCache::instance().userInterface().setPlaymatVisibility(playmatVisibilityCombo.itemData(index).toInt()); - }); - playmatVisibilityLabel.setBuddy(&playmatVisibilityCombo); - - // Playmat mode: Override / Fallback / Deck-only - playmatModeCombo.addItem(tr("Override deck playmat"), PlaymatModeOverrideDeck); - playmatModeCombo.addItem(tr("Fallback if deck has none"), PlaymatModeFallback); - playmatModeCombo.addItem(tr("Deck only, ignore collection"), PlaymatModeDeckOnly); - int modeIdx = playmatModeCombo.findData(settings.userInterface().getPlaymatMode()); - if (modeIdx >= 0) { - playmatModeCombo.setCurrentIndex(modeIdx); - } - connect(&playmatModeCombo, qOverload(&QComboBox::currentIndexChanged), this, [this](int index) { - SettingsCache::instance().userInterface().setPlaymatMode(playmatModeCombo.itemData(index).toInt()); - }); - playmatModeLabel.setBuddy(&playmatModeCombo); - - // User-level playmat settings: fallback collection. - connect(&playmatDefaultEditButton, &QPushButton::clicked, this, - &AppearanceSettingsPage::openPlaymatCollectionDialog); - - auto *playmatGrid = new QGridLayout; - playmatGrid->addWidget(&playmatVisibilityLabel, 0, 0, 1, 1); - playmatGrid->addWidget(&playmatVisibilityCombo, 0, 1, 1, 1); - playmatGrid->addWidget(&playmatModeLabel, 1, 0, 1, 1); - playmatGrid->addWidget(&playmatModeCombo, 1, 1, 1, 1); - playmatGrid->addWidget(&playmatDefaultLabel, 2, 0, 1, 1); - playmatGrid->addWidget(&playmatDefaultEditButton, 2, 1, 1, 1); - - playmatGroupBox = new QGroupBox; - playmatGroupBox->setLayout(playmatGrid); - // putting it all together auto *mainLayout = new QVBoxLayout; mainLayout->addWidget(themeGroupBox); mainLayout->addWidget(homeTabGroupBox); - mainLayout->addWidget(playmatGroupBox); mainLayout->addWidget(stylingGroupBox); mainLayout->addWidget(menuGroupBox); mainLayout->addWidget(printingsGroupBox); @@ -476,12 +431,6 @@ void AppearanceSettingsPage::cardViewExpandedRowsMaxChanged(int value) } } -void AppearanceSettingsPage::openPlaymatCollectionDialog() -{ - PlaymatCollectionDialog dialog(this); - dialog.exec(); -} - void AppearanceSettingsPage::retranslateUi() { themeGroupBox->setTitle(tr("Theme settings")); @@ -540,9 +489,4 @@ void AppearanceSettingsPage::retranslateUi() tableGroupBox->setTitle(tr("Table grid layout")); invertVerticalCoordinateCheckBox.setText(tr("Invert vertical coordinate")); minPlayersForMultiColumnLayoutLabel.setText(tr("Minimum player count for multi-column layout:")); - playmatGroupBox->setTitle(tr("Playmat settings")); - playmatVisibilityLabel.setText(tr("Playmat visibility:")); - playmatModeLabel.setText(tr("Default collection behavior:")); - playmatDefaultLabel.setText(tr("Default playmat collection:")); - playmatDefaultEditButton.setText(tr("Edit...")); -} +} \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.h b/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.h index 28abbd537..0b6b6832c 100644 --- a/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.h +++ b/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.h @@ -24,7 +24,6 @@ private slots: void cardViewInitialRowsMaxChanged(int value); void cardViewExpandedRowsMaxChanged(int value); - void openPlaymatCollectionDialog(); private: QLabel themeLabel; @@ -60,12 +59,6 @@ private: QCheckBox horizontalHandCheckBox; QCheckBox leftJustifiedHandCheckBox; QCheckBox invertVerticalCoordinateCheckBox; - QLabel playmatVisibilityLabel; - QComboBox playmatVisibilityCombo; - QLabel playmatModeLabel; - QComboBox playmatModeCombo; - QLabel playmatDefaultLabel; - QPushButton playmatDefaultEditButton; QGroupBox *themeGroupBox; QGroupBox *homeTabGroupBox; QGroupBox *stylingGroupBox; @@ -74,7 +67,6 @@ private: QGroupBox *cardsGroupBox; QGroupBox *cardLayoutGroupBox; QGroupBox *handGroupBox; - QGroupBox *playmatGroupBox; QGroupBox *tableGroupBox; QGroupBox *cardCountersGroupBox; QList cardCounterNames; diff --git a/cockatrice/src/interface/widgets/tabs/tab_account.cpp b/cockatrice/src/interface/widgets/tabs/tab_account.cpp index 410a48d40..2c30178f3 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_account.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_account.cpp @@ -1,7 +1,6 @@ #include "tab_account.h" #include "../client/sound_engine.h" -#include "../interface/widgets/dialogs/dlg_my_reports.h" #include "../interface/widgets/server/user/user_info_box.h" #include "../interface/widgets/server/user/user_list_manager.h" #include "../interface/widgets/server/user/user_list_widget.h" @@ -50,11 +49,6 @@ TabAccount::TabAccount(TabSupervisor *_tabSupervisor, AbstractClient *_client, c auto *vbox = new QVBoxLayout; vbox->addWidget(userInfoBox); - - myReportsButton = new QPushButton(tr("My Reports")); - connect(myReportsButton, &QPushButton::clicked, this, &TabAccount::openMyReports); - vbox->addWidget(myReportsButton); - vbox->addWidget(allUsersList); auto *addToBuddyList = new QHBoxLayout; @@ -132,7 +126,6 @@ void TabAccount::addToList(const std::string &listName, const QString &userName) void TabAccount::retranslateUi() { - myReportsButton->setText(tr("My Reports")); allUsersList->retranslateUi(); buddyList->retranslateUi(); ignoreList->retranslateUi(); @@ -247,10 +240,3 @@ void TabAccount::processRemoveFromListEvent(const Event_RemoveFromList &event) userList->deleteUser(user); } - -void TabAccount::openMyReports() -{ - auto *dlg = new DlgMyReports(client, this); - dlg->setAttribute(Qt::WA_DeleteOnClose); - dlg->exec(); -} diff --git a/cockatrice/src/interface/widgets/tabs/tab_account.h b/cockatrice/src/interface/widgets/tabs/tab_account.h index 68054c7a1..887038ebb 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_account.h +++ b/cockatrice/src/interface/widgets/tabs/tab_account.h @@ -18,7 +18,6 @@ class Event_RemoveFromList; class Event_UserJoined; class Event_UserLeft; class LineEditUnfocusable; -class QPushButton; class Response; class ServerInfo_User; class UserInfoBox; @@ -42,7 +41,6 @@ private slots: void processRemoveFromListEvent(const Event_RemoveFromList &event); void addToIgnoreList(); void addToBuddyList(); - void openMyReports(); private: AbstractClient *client; @@ -52,7 +50,6 @@ private: UserInfoBox *userInfoBox; LineEditUnfocusable *addBuddyEdit; LineEditUnfocusable *addIgnoreEdit; - QPushButton *myReportsButton; void addToList(const std::string &listName, const QString &userName); public: diff --git a/cockatrice/src/interface/widgets/tabs/tab_game.cpp b/cockatrice/src/interface/widgets/tabs/tab_game.cpp index 196ea4526..dbf4a5a4a 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_game.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_game.cpp @@ -911,7 +911,6 @@ void TabGame::stopGame() QMapIterator i(deckViewContainers); while (i.hasNext()) { i.next(); - i.value()->playerDeckView->advancePlaymatRotation(); i.value()->show(); } diff --git a/cockatrice/src/interface/widgets/tabs/tab_moderation.cpp b/cockatrice/src/interface/widgets/tabs/tab_moderation.cpp deleted file mode 100644 index 077b876d2..000000000 --- a/cockatrice/src/interface/widgets/tabs/tab_moderation.cpp +++ /dev/null @@ -1,462 +0,0 @@ -#include "tab_moderation.h" - -#include "abstract_client.h" -#include "tab_supervisor.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace -{ -constexpr int COL_ALTS_USER = 0; -constexpr int COL_ALTS_EMAIL = 1; -constexpr int COL_ALTS_CLIENTID = 2; -constexpr int COL_ALTS_REGISTERED = 3; -constexpr int COL_ALTS_LAST_LOGIN = 4; -constexpr int COL_ALTS_WARNS = 5; -constexpr int COL_ALTS_BANS = 6; -constexpr int COL_ALTS_ACTIVE = 7; -constexpr int COL_ALTS_COUNT = 8; - -constexpr int COL_SESSIONS_IP = 0; -constexpr int COL_SESSIONS_CLIENTID = 1; -constexpr int COL_SESSIONS_START = 2; -constexpr int COL_SESSIONS_END = 3; -constexpr int COL_SESSIONS_TYPE = 4; -constexpr int COL_SESSIONS_COUNT = 5; - -constexpr int COL_STAFF_USER = 0; -constexpr int COL_STAFF_LEVEL = 1; -constexpr int COL_STAFF_LAST_LOGIN = 2; -constexpr int COL_STAFF_COUNT = 3; -} // namespace - -TabModeration::TabModeration(TabSupervisor *_tabSupervisor, AbstractClient *_client, const QString &initialUser) - : Tab(_tabSupervisor), client(_client) -{ - auto *centralWidget = new QWidget(this); - setCentralWidget(centralWidget); - - searchEdit = new QLineEdit; - searchEdit->setClearButtonEnabled(true); - connect(searchEdit, &QLineEdit::returnPressed, this, &TabModeration::investigateUser); - - investigateButton = new QPushButton; - connect(investigateButton, &QPushButton::clicked, this, &TabModeration::investigateUser); - - resetPasswordButton = new QPushButton; - connect(resetPasswordButton, &QPushButton::clicked, this, &TabModeration::resetPassword); - - removeAvatarButton = new QPushButton; - connect(removeAvatarButton, &QPushButton::clicked, this, &TabModeration::removeAvatar); - - auto *topBar = new QHBoxLayout; - topBar->addWidget(searchEdit); - topBar->addWidget(investigateButton); - topBar->addStretch(); - topBar->addWidget(resetPasswordButton); - topBar->addWidget(removeAvatarButton); - - userInfoGroup = new QGroupBox; - userInfoNameLabel = new QLabel; - userInfoNameValue = new QLabel; - userInfoRegisteredLabel = new QLabel; - userInfoRegisteredValue = new QLabel; - userInfoLastLoginLabel = new QLabel; - userInfoLastLoginValue = new QLabel; - userInfoStatusLabel = new QLabel; - userInfoStatusValue = new QLabel; - userInfoCountsLabel = new QLabel; - userInfoCountsValue = new QLabel; - userInfoNotesLabel = new QLabel; - userInfoNotesEdit = new QTextEdit; - userInfoNotesEdit->setReadOnly(true); - - auto *infoGrid = new QGridLayout; - infoGrid->addWidget(userInfoNameLabel, 0, 0); - infoGrid->addWidget(userInfoNameValue, 0, 1); - infoGrid->addWidget(userInfoRegisteredLabel, 0, 2); - infoGrid->addWidget(userInfoRegisteredValue, 0, 3); - infoGrid->addWidget(userInfoLastLoginLabel, 1, 0); - infoGrid->addWidget(userInfoLastLoginValue, 1, 1); - infoGrid->addWidget(userInfoStatusLabel, 1, 2); - infoGrid->addWidget(userInfoStatusValue, 1, 3); - infoGrid->addWidget(userInfoCountsLabel, 2, 0); - infoGrid->addWidget(userInfoCountsValue, 2, 1, 1, 3); - infoGrid->addWidget(userInfoNotesLabel, 3, 0, Qt::AlignTop); - infoGrid->addWidget(userInfoNotesEdit, 3, 1, 1, 3); - infoGrid->setColumnStretch(1, 1); - infoGrid->setColumnStretch(3, 1); - - auto *infoLayout = new QVBoxLayout(userInfoGroup); - infoLayout->addLayout(infoGrid); - - auto configureTable = [](QTableWidget *table) { - table->setSelectionBehavior(QAbstractItemView::SelectRows); - table->setSelectionMode(QAbstractItemView::SingleSelection); - table->setEditTriggers(QAbstractItemView::NoEditTriggers); - table->verticalHeader()->setVisible(false); - table->setAlternatingRowColors(true); - table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); - table->horizontalHeader()->setStretchLastSection(true); - }; - - altsGroup = new QGroupBox; - altsTable = new QTableWidget(0, COL_ALTS_COUNT); - configureTable(altsTable); - auto *altsLayout = new QVBoxLayout(altsGroup); - altsLayout->addWidget(altsTable); - - sessionsGroup = new QGroupBox; - sessionsTable = new QTableWidget(0, COL_SESSIONS_COUNT); - configureTable(sessionsTable); - auto *sessionsLayout = new QVBoxLayout(sessionsGroup); - sessionsLayout->addWidget(sessionsTable); - - staffGroup = new QGroupBox; - staffTable = new QTableWidget(0, COL_STAFF_COUNT); - configureTable(staffTable); - refreshStaffButton = new QPushButton; - connect(refreshStaffButton, &QPushButton::clicked, this, &TabModeration::requestModeratorLogins); - auto *staffHeader = new QHBoxLayout; - staffHeader->addStretch(); - staffHeader->addWidget(refreshStaffButton); - auto *staffLayout = new QVBoxLayout(staffGroup); - staffLayout->addWidget(staffTable); - staffLayout->addLayout(staffHeader); - - auto *splitter = new QSplitter(Qt::Vertical); - splitter->addWidget(userInfoGroup); - splitter->addWidget(altsGroup); - splitter->addWidget(sessionsGroup); - splitter->addWidget(staffGroup); - splitter->setStretchFactor(0, 1); - splitter->setStretchFactor(1, 2); - splitter->setStretchFactor(2, 2); - splitter->setStretchFactor(3, 1); - - auto *mainLayout = new QVBoxLayout(centralWidget); - mainLayout->addLayout(topBar); - mainLayout->addWidget(splitter); - - retranslateUi(); - clearUserData(); - requestModeratorLogins(); - investigate(initialUser); -} - -void TabModeration::retranslateUi() -{ - searchEdit->setPlaceholderText(tr("User name")); - investigateButton->setText(tr("Investigate")); - resetPasswordButton->setText(tr("Reset Password")); - removeAvatarButton->setText(tr("Remove Avatar")); - refreshStaffButton->setText(tr("Refresh")); - - userInfoGroup->setTitle(tr("User Info")); - userInfoNameLabel->setText(tr("Name:")); - userInfoRegisteredLabel->setText(tr("Registered:")); - userInfoLastLoginLabel->setText(tr("Last login:")); - userInfoStatusLabel->setText(tr("Status:")); - userInfoCountsLabel->setText(tr("Counts:")); - userInfoNotesLabel->setText(tr("Admin notes:")); - - altsGroup->setTitle(tr("Alts")); - sessionsGroup->setTitle(tr("Sessions")); - staffGroup->setTitle(tr("Staff Last Logins")); - - altsTable->setHorizontalHeaderLabels({tr("User"), tr("eMail"), tr("Client ID"), tr("Registered"), tr("Last login"), - tr("Warns"), tr("Bans"), tr("Active")}); - sessionsTable->setHorizontalHeaderLabels({tr("IP"), tr("Client ID"), tr("Start"), tr("End"), tr("Type")}); - staffTable->setHorizontalHeaderLabels({tr("User"), tr("Level"), tr("Last login")}); -} - -QString TabModeration::formatEpoch(quint64 ts) const -{ - if (ts == 0) { - return tr("Unknown"); - } - return QDateTime::fromSecsSinceEpoch(ts).toLocalTime().toString("yyyy-MM-dd HH:mm"); -} - -void TabModeration::clearUserData() -{ - currentUser.clear(); - userInfoNameValue->clear(); - userInfoRegisteredValue->clear(); - userInfoLastLoginValue->clear(); - userInfoStatusValue->clear(); - userInfoCountsValue->clear(); - userInfoNotesEdit->clear(); - altsTable->setRowCount(0); - sessionsTable->setRowCount(0); - resetPasswordButton->setEnabled(false); - removeAvatarButton->setEnabled(false); -} - -void TabModeration::investigate(const QString &userName) -{ - if (userName.isEmpty()) { - return; - } - searchEdit->setText(userName); - investigateUser(); -} - -void TabModeration::investigateUser() -{ - const QString userName = searchEdit->text().simplified(); - if (userName.isEmpty()) { - return; - } - currentUser = userName; - resetPasswordButton->setEnabled(true); - removeAvatarButton->setEnabled(true); - altsTable->setRowCount(0); - sessionsTable->setRowCount(0); - requestUserInfo(userName); - requestSessions(userName); - requestAlts(userName); -} - -void TabModeration::requestUserInfo(const QString &userName) -{ - userInfoNameValue->setText(userName); - userInfoRegisteredValue->setText(tr("Loading...")); - userInfoLastLoginValue->setText(tr("Loading...")); - userInfoStatusValue->clear(); - userInfoCountsValue->clear(); - userInfoNotesEdit->clear(); - - Command_ReportUserInfo cmd; - cmd.set_user_name(userName.toStdString()); - PendingCommand *pend = client->prepareModeratorCommand(cmd); - connect(pend, &PendingCommand::finished, this, &TabModeration::userInfoResponse); - client->sendCommand(pend); -} - -void TabModeration::userInfoResponse(const Response &response) -{ - if (response.response_code() != Response::RespOk) { - userInfoRegisteredValue->clear(); - userInfoLastLoginValue->clear(); - userInfoStatusValue->setText(tr("Error loading user info.")); - return; - } - - const Response_ReportUserInfo &resp = response.GetExtension(Response_ReportUserInfo::ext); - if (resp.has_user_name() && QString::fromStdString(resp.user_name()) != currentUser) { - return; - } - - userInfoRegisteredValue->setText(formatEpoch(resp.registration_time())); - userInfoLastLoginValue->setText(formatEpoch(resp.last_login())); - - QStringList statusParts; - statusParts << (resp.is_active() ? tr("active") : tr("inactive")); - if (resp.has_is_admin() && resp.is_admin()) { - statusParts << tr("admin"); - } - userInfoStatusValue->setText(statusParts.join(", ")); - - userInfoCountsValue->setText(tr("Reports: %1 Bans: %2 Warnings: %3") - .arg(resp.total_reports()) - .arg(resp.total_bans()) - .arg(resp.total_warns())); - - if (resp.has_admin_notes() && !resp.admin_notes().empty()) { - userInfoNotesEdit->setPlainText(QString::fromStdString(resp.admin_notes())); - } else { - userInfoNotesEdit->setPlainText(tr("(none)")); - } -} - -void TabModeration::requestSessions(const QString &userName) -{ - Command_GetUserSessions cmd; - cmd.set_user_name(userName.toStdString()); - PendingCommand *pend = client->prepareModeratorCommand(cmd); - connect(pend, &PendingCommand::finished, this, &TabModeration::sessionsResponse); - client->sendCommand(pend); -} - -void TabModeration::sessionsResponse(const Response &response) -{ - sessionsTable->setRowCount(0); - if (response.response_code() != Response::RespOk) { - return; - } - - const Response_UserSessions &resp = response.GetExtension(Response_UserSessions::ext); - sessionsTable->setRowCount(resp.sessions_size()); - for (int i = 0; i < resp.sessions_size(); ++i) { - const ServerInfo_UserSession &session = resp.sessions(i); - sessionsTable->setItem(i, COL_SESSIONS_IP, new QTableWidgetItem(QString::fromStdString(session.ip_address()))); - sessionsTable->setItem(i, COL_SESSIONS_CLIENTID, - new QTableWidgetItem(QString::fromStdString(session.clientid()))); - sessionsTable->setItem(i, COL_SESSIONS_START, new QTableWidgetItem(formatEpoch(session.start_time()))); - sessionsTable->setItem( - i, COL_SESSIONS_END, - new QTableWidgetItem(session.end_time() == 0 ? tr("Active") : formatEpoch(session.end_time()))); - sessionsTable->setItem(i, COL_SESSIONS_TYPE, - new QTableWidgetItem(QString::fromStdString(session.connection_type()))); - } -} - -void TabModeration::requestAlts(const QString &userName) -{ - Command_GetUserAlts cmd; - cmd.set_user_name(userName.toStdString()); - PendingCommand *pend = client->prepareModeratorCommand(cmd); - connect(pend, &PendingCommand::finished, this, &TabModeration::altsResponse); - client->sendCommand(pend); -} - -void TabModeration::altsResponse(const Response &response) -{ - altsTable->setRowCount(0); - if (response.response_code() != Response::RespOk) { - return; - } - - const Response_UserAlts &resp = response.GetExtension(Response_UserAlts::ext); - altsTable->setRowCount(resp.alts_size()); - for (int i = 0; i < resp.alts_size(); ++i) { - const ServerInfo_UserAlt &alt = resp.alts(i); - altsTable->setItem(i, COL_ALTS_USER, new QTableWidgetItem(QString::fromStdString(alt.user_name()))); - altsTable->setItem(i, COL_ALTS_EMAIL, new QTableWidgetItem(QString::fromStdString(alt.email()))); - altsTable->setItem(i, COL_ALTS_CLIENTID, new QTableWidgetItem(QString::fromStdString(alt.clientid()))); - altsTable->setItem(i, COL_ALTS_REGISTERED, new QTableWidgetItem(formatEpoch(alt.registration_time()))); - altsTable->setItem(i, COL_ALTS_LAST_LOGIN, new QTableWidgetItem(formatEpoch(alt.last_login()))); - altsTable->setItem(i, COL_ALTS_WARNS, new QTableWidgetItem(QString::number(alt.warn_count()))); - altsTable->setItem(i, COL_ALTS_BANS, new QTableWidgetItem(QString::number(alt.ban_count()))); - altsTable->setItem(i, COL_ALTS_ACTIVE, new QTableWidgetItem(alt.is_active() ? tr("yes") : tr("no"))); - } -} - -void TabModeration::requestModeratorLogins() -{ - Command_GetModeratorLastLogins cmd; - PendingCommand *pend = client->prepareModeratorCommand(cmd); - connect(pend, &PendingCommand::finished, this, &TabModeration::moderatorLoginsResponse); - client->sendCommand(pend); -} - -void TabModeration::moderatorLoginsResponse(const Response &response) -{ - staffTable->setRowCount(0); - if (response.response_code() != Response::RespOk) { - return; - } - - const Response_ModeratorLastLogins &resp = response.GetExtension(Response_ModeratorLastLogins::ext); - staffTable->setRowCount(resp.logins_size()); - for (int i = 0; i < resp.logins_size(); ++i) { - const ServerInfo_ModeratorLogin &login = resp.logins(i); - staffTable->setItem(i, COL_STAFF_USER, new QTableWidgetItem(QString::fromStdString(login.user_name()))); - staffTable->setItem(i, COL_STAFF_LAST_LOGIN, new QTableWidgetItem(formatEpoch(login.last_login()))); - - QStringList levels; - if (login.user_level() & ServerInfo_User::IsAdmin) { - levels << tr("Admin"); - } - if (login.user_level() & ServerInfo_User::IsModerator) { - levels << tr("Moderator"); - } - if (login.user_level() & ServerInfo_User::IsJudge) { - levels << tr("Judge"); - } - staffTable->setItem(i, COL_STAFF_LEVEL, new QTableWidgetItem(levels.join(" / "))); - } -} - -void TabModeration::resetPassword() -{ - if (currentUser.isEmpty()) { - return; - } - - QMessageBox::StandardButton choice = - QMessageBox::warning(this, tr("Reset Password"), - tr("Reset the password of %1? A temporary password will be generated and shown to you. " - "The user must change it on their first login.") - .arg(currentUser), - QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); - if (choice != QMessageBox::Ok) { - return; - } - - Command_ResetUserPassword cmd; - cmd.set_user_name(currentUser.toStdString()); - PendingCommand *pend = client->prepareModeratorCommand(cmd); - connect(pend, &PendingCommand::finished, this, &TabModeration::resetPasswordResponse); - client->sendCommand(pend); -} - -void TabModeration::resetPasswordResponse(const Response &response) -{ - if (response.response_code() != Response::RespOk) { - QMessageBox::critical(this, tr("Error"), tr("Password reset failed.")); - return; - } - - const Response_ResetUserPassword &resp = response.GetExtension(Response_ResetUserPassword::ext); - QMessageBox::information(this, tr("Password reset"), - tr("Temporary password for %1:\n\n%2\n\nPass it to the user through a secure channel.") - .arg(QString::fromStdString(resp.user_name())) - .arg(QString::fromStdString(resp.temporary_password()))); -} - -void TabModeration::removeAvatar() -{ - if (currentUser.isEmpty()) { - return; - } - - QMessageBox::StandardButton choice = - QMessageBox::warning(this, tr("Remove Avatar"), - tr("Remove the avatar of %1? The user will have to upload a new one.").arg(currentUser), - QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); - if (choice != QMessageBox::Ok) { - return; - } - - Command_RemoveUserAvatar cmd; - cmd.set_user_name(currentUser.toStdString()); - PendingCommand *pend = client->prepareModeratorCommand(cmd); - connect(pend, &PendingCommand::finished, this, &TabModeration::removeAvatarResponse); - client->sendCommand(pend); -} - -void TabModeration::removeAvatarResponse(const Response &response) -{ - if (response.response_code() != Response::RespOk) { - QMessageBox::critical(this, tr("Error"), tr("Could not remove the avatar.")); - return; - } - - const Response_RemoveUserAvatar &resp = response.GetExtension(Response_RemoveUserAvatar::ext); - QMessageBox::information(this, tr("Avatar removed"), - tr("The avatar of %1 has been removed.").arg(QString::fromStdString(resp.user_name()))); -} diff --git a/cockatrice/src/interface/widgets/tabs/tab_moderation.h b/cockatrice/src/interface/widgets/tabs/tab_moderation.h deleted file mode 100644 index 0a534992b..000000000 --- a/cockatrice/src/interface/widgets/tabs/tab_moderation.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef TAB_MODERATION_H -#define TAB_MODERATION_H - -#include "tab.h" - -#include - -class AbstractClient; -class QGroupBox; -class QLabel; -class QLineEdit; -class QPushButton; -class QTableWidget; -class QTextEdit; - -/** - * Staff investigation tool. Lets moderators look up a user's account data, - * alternate accounts, login sessions, and staff login activity, and offers - * the password-reset and remove-avatar actions. - */ -class TabModeration : public Tab -{ - Q_OBJECT -public: - explicit TabModeration(TabSupervisor *_tabSupervisor, AbstractClient *_client, const QString &initialUser = {}); - void retranslateUi() override; - [[nodiscard]] QString getTabText() const override - { - return tr("Moderation"); - } - void investigate(const QString &userName); - -private slots: - void investigateUser(); - void userInfoResponse(const Response &response); - void sessionsResponse(const Response &response); - void altsResponse(const Response &response); - void moderatorLoginsResponse(const Response &response); - void resetPassword(); - void resetPasswordResponse(const Response &response); - void removeAvatar(); - void removeAvatarResponse(const Response &response); - -private: - void requestUserInfo(const QString &userName); - void requestSessions(const QString &userName); - void requestAlts(const QString &userName); - void requestModeratorLogins(); - void clearUserData(); - [[nodiscard]] QString formatEpoch(quint64 ts) const; - - AbstractClient *client; - QString currentUser; - - QLineEdit *searchEdit; - QPushButton *investigateButton; - QPushButton *resetPasswordButton; - QPushButton *removeAvatarButton; - - QGroupBox *userInfoGroup; - QLabel *userInfoNameLabel; - QLabel *userInfoNameValue; - QLabel *userInfoRegisteredLabel; - QLabel *userInfoRegisteredValue; - QLabel *userInfoLastLoginLabel; - QLabel *userInfoLastLoginValue; - QLabel *userInfoStatusLabel; - QLabel *userInfoStatusValue; - QLabel *userInfoCountsLabel; - QLabel *userInfoCountsValue; - QLabel *userInfoNotesLabel; - QTextEdit *userInfoNotesEdit; - - QGroupBox *altsGroup; - QTableWidget *altsTable; - QGroupBox *sessionsGroup; - QTableWidget *sessionsTable; - QGroupBox *staffGroup; - QTableWidget *staffTable; - QPushButton *refreshStaffButton; -}; - -#endif // TAB_MODERATION_H diff --git a/cockatrice/src/interface/widgets/tabs/tab_report.cpp b/cockatrice/src/interface/widgets/tabs/tab_report.cpp deleted file mode 100644 index 0b9108f9b..000000000 --- a/cockatrice/src/interface/widgets/tabs/tab_report.cpp +++ /dev/null @@ -1,927 +0,0 @@ -#include "tab_report.h" - -#include "../utility/report_utils.h" -#include "abstract_client.h" -#include "tab_supervisor.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace -{ -constexpr int COL_ID = 0; -constexpr int COL_TIME = 1; -constexpr int COL_REPORTER = 2; -constexpr int COL_REPORTED = 3; -constexpr int COL_CATEGORY = 4; -constexpr int COL_GAMEID = 5; -constexpr int COL_STATUS = 6; -constexpr int COL_ASSIGNED = 7; -constexpr int COL_REPLAY = 8; -constexpr int COL_ROOM = 9; -constexpr int COL_COUNT = 10; -constexpr int REFRESH_INTERVAL_MS = 300000; -} // namespace - -TabReport::TabReport(TabSupervisor *_tabSupervisor, AbstractClient *_client) : Tab(_tabSupervisor), client(_client) -{ - auto *centralWidget = new QWidget(this); - setCentralWidget(centralWidget); - - searchEdit = new QLineEdit; - searchEdit->setClearButtonEnabled(true); - connect(searchEdit, &QLineEdit::textChanged, this, &TabReport::applyFilters); - - statusFilter = new QComboBox; - connect(statusFilter, &QComboBox::currentIndexChanged, this, &TabReport::applyFilters); - - unresolvedOnlyBox = new QCheckBox; - unresolvedOnlyBox->setChecked(true); -#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) - connect(unresolvedOnlyBox, &QCheckBox::checkStateChanged, this, &TabReport::refreshList); -#else - connect(unresolvedOnlyBox, &QCheckBox::stateChanged, this, &TabReport::refreshList); -#endif - - refreshButton = new QPushButton; - connect(refreshButton, &QPushButton::clicked, this, &TabReport::refreshList); - - refreshTimer = new QTimer(this); - refreshTimer->setInterval(REFRESH_INTERVAL_MS); - connect(refreshTimer, &QTimer::timeout, this, [this]() { - if (tabSupervisor->currentWidget() == this) { - refreshList(); - } - }); - refreshTimer->start(); - - auto *topBar = new QHBoxLayout; - topBar->addWidget(searchEdit); - topBar->addWidget(statusFilter); - topBar->addWidget(unresolvedOnlyBox); - topBar->addStretch(); - topBar->addWidget(refreshButton); - - statsLabel = new QLabel; - - table = new QTableWidget(0, COL_COUNT); - table->setSelectionBehavior(QAbstractItemView::SelectRows); - table->setSelectionMode(QAbstractItemView::SingleSelection); - table->setEditTriggers(QAbstractItemView::NoEditTriggers); - table->setSortingEnabled(true); - table->verticalHeader()->setVisible(false); - table->setAlternatingRowColors(true); - table->horizontalHeader()->setSectionResizeMode(COL_TIME, QHeaderView::ResizeToContents); - table->horizontalHeader()->setSectionResizeMode(COL_REPORTED, QHeaderView::Stretch); - table->horizontalHeader()->setSectionResizeMode(COL_REPORTER, QHeaderView::ResizeToContents); - connect(table, &QTableWidget::itemSelectionChanged, this, &TabReport::onSelectionChanged); - - descGroup = new QGroupBox; - descriptionEdit = new QTextEdit; - descriptionEdit->setReadOnly(true); - auto *descLayout = new QVBoxLayout(descGroup); - descLayout->setContentsMargins(4, 4, 4, 4); - descLayout->addWidget(descriptionEdit); - - chatGroup = new QGroupBox; - chatLogEdit = new QTextEdit; - chatLogEdit->setReadOnly(true); - QFont monoFont("monospace"); - monoFont.setStyleHint(QFont::Monospace); - const int systemPointSize = QFontDatabase::systemFont(QFontDatabase::GeneralFont).pointSize(); - if (systemPointSize > 0) { - monoFont.setPointSize(systemPointSize); - } - chatLogEdit->setFont(monoFont); - auto *chatLayout = new QVBoxLayout(chatGroup); - chatLayout->setContentsMargins(4, 4, 4, 4); - chatLayout->addWidget(chatLogEdit); - - commentsGroup = new QGroupBox; - commentsEdit = new QTextEdit; - commentsEdit->setReadOnly(true); - auto *commentInputRow = new QHBoxLayout; - commentInput = new QLineEdit; - commentButton = new QPushButton; - commentButton->setEnabled(false); - connect(commentButton, &QPushButton::clicked, this, &TabReport::addComment); - connect(commentInput, &QLineEdit::returnPressed, this, &TabReport::addComment); - commentInputRow->addWidget(commentInput); - commentInputRow->addWidget(commentButton); - auto *commentsLayout = new QVBoxLayout(commentsGroup); - commentsLayout->setContentsMargins(4, 4, 4, 4); - commentsLayout->addWidget(commentsEdit); - commentsLayout->addLayout(commentInputRow); - - userContextGroup = new QGroupBox; - userContextName = new QLabel; - userContextAccountAge = new QLabel; - userContextReports = new QLabel; - userContextBans = new QLabel; - userContextWarns = new QLabel; - userContextNotes = new QTextEdit; - userContextNotes->setReadOnly(true); - userContextNotes->setMaximumHeight(80); - userContextRecentReports = new QTextEdit; - userContextRecentReports->setReadOnly(true); - userContextRecentReports->setMaximumHeight(120); - userContextUserLabel = new QLabel; - userContextAgeLabel = new QLabel; - userContextReportsLabel = new QLabel; - userContextBansLabel = new QLabel; - userContextWarnsLabel = new QLabel; - userContextNotesLabel = new QLabel; - userContextRecentReportsLabel = new QLabel; - auto *ucLayout = new QGridLayout(userContextGroup); - ucLayout->setContentsMargins(8, 8, 8, 8); - ucLayout->addWidget(userContextUserLabel, 0, 0); - ucLayout->addWidget(userContextName, 0, 1, 1, 3); - ucLayout->addWidget(userContextAgeLabel, 1, 0); - ucLayout->addWidget(userContextAccountAge, 1, 1); - ucLayout->addWidget(userContextReportsLabel, 1, 2); - ucLayout->addWidget(userContextReports, 1, 3); - ucLayout->addWidget(userContextBansLabel, 2, 0); - ucLayout->addWidget(userContextBans, 2, 1); - ucLayout->addWidget(userContextWarnsLabel, 2, 2); - ucLayout->addWidget(userContextWarns, 2, 3); - ucLayout->addWidget(userContextNotesLabel, 3, 0, Qt::AlignTop); - ucLayout->addWidget(userContextNotes, 3, 1, 1, 3); - ucLayout->addWidget(userContextRecentReportsLabel, 4, 0, 1, 4); - ucLayout->addWidget(userContextRecentReports, 5, 0, 1, 4); - userContextGroup->setVisible(false); - - statsGroup = new QGroupBox; - statsGroup->setCheckable(true); - statsTotalLabel = new QLabel; - statsTrendLabel = new QLabel; - statsCategoriesLabel = new QLabel; - statsDetailText = new QTextEdit; - statsDetailText->setReadOnly(true); - statsDetailText->setMaximumHeight(150); - auto *statsContent = new QWidget; - auto *sgLayout = new QVBoxLayout(statsContent); - sgLayout->setContentsMargins(8, 8, 8, 8); - sgLayout->addWidget(statsTotalLabel); - sgLayout->addWidget(statsTrendLabel); - sgLayout->addWidget(statsCategoriesLabel); - sgLayout->addWidget(statsDetailText); - auto *statsGroupLayout = new QVBoxLayout(statsGroup); - statsGroupLayout->setContentsMargins(0, 0, 0, 0); - statsGroupLayout->addWidget(statsContent); - statsGroup->setChecked(true); - connect(statsGroup, &QGroupBox::toggled, this, [this, statsContent](bool checked) { - statsContent->setVisible(checked); - if (checked) { - requestStats(); - } - }); - - detailSplitter = new QSplitter(Qt::Vertical); - detailSplitter->addWidget(descGroup); - detailSplitter->addWidget(chatGroup); - detailSplitter->addWidget(commentsGroup); - detailSplitter->setStretchFactor(0, 1); - detailSplitter->setStretchFactor(1, 1); - detailSplitter->setStretchFactor(2, 2); - - assignButton = new QPushButton; - resolveButton = new QPushButton; - resolveWithNoteButton = new QPushButton; - dismissButton = new QPushButton; - viewReplayButton = new QPushButton; - joinGameButton = new QPushButton; - connect(assignButton, &QPushButton::clicked, this, &TabReport::assignReport); - connect(resolveButton, &QPushButton::clicked, this, [this]() { resolveReport(false, false); }); - connect(resolveWithNoteButton, &QPushButton::clicked, this, [this]() { resolveReport(false, true); }); - connect(dismissButton, &QPushButton::clicked, this, [this]() { resolveReport(true, true); }); - connect(viewReplayButton, &QPushButton::clicked, this, &TabReport::viewReplay); - connect(joinGameButton, &QPushButton::clicked, this, &TabReport::joinGame); - - statusLabel = new QLabel; - - auto *actionBar = new QHBoxLayout; - actionBar->addWidget(assignButton); - actionBar->addWidget(resolveButton); - actionBar->addWidget(resolveWithNoteButton); - actionBar->addWidget(dismissButton); - actionBar->addSpacing(20); - actionBar->addWidget(viewReplayButton); - actionBar->addWidget(joinGameButton); - actionBar->addStretch(); - actionBar->addWidget(statusLabel); - - auto *layout = new QVBoxLayout(centralWidget); - layout->addLayout(topBar); - layout->addWidget(statsLabel); - layout->addWidget(table, 2); - layout->addWidget(detailSplitter, 1); - layout->addWidget(userContextGroup); - layout->addWidget(statsGroup); - layout->addLayout(actionBar); - - retranslateUi(); - - setActionsEnabled(false); - refreshList(); -} - -void TabReport::retranslateUi() -{ - searchEdit->setPlaceholderText(tr("Search by username, category...")); - statusFilter->clear(); - statusFilter->addItem(tr("All Statuses"), ""); - statusFilter->addItem(tr("Open"), "open"); - statusFilter->addItem(tr("Assigned"), "assigned"); - statusFilter->addItem(tr("Resolved"), "resolved"); - statusFilter->addItem(tr("Dismissed"), "dismissed"); - unresolvedOnlyBox->setText(tr("Unresolved only")); - refreshButton->setText(tr("Refresh")); - - table->setHorizontalHeaderLabels({tr("#"), tr("Time"), tr("Reporter"), tr("Reported User"), tr("Category"), - tr("Game ID"), tr("Status"), tr("Assigned To"), tr("Replay"), tr("Room")}); - - descGroup->setTitle(tr("Description")); - chatGroup->setTitle(tr("Chat Log Context")); - chatGroup->setToolTip( - tr("Chat log attached by the reporter. It is captured from their client and may be incomplete or edited.")); - commentsGroup->setTitle(tr("Comments / Thread")); - descriptionEdit->setPlaceholderText(tr("No description.")); - chatLogEdit->setPlaceholderText(tr("No chat log attached.")); - commentsEdit->setPlaceholderText(tr("No comments yet.")); - commentInput->setPlaceholderText(tr("Type a reply...")); - commentButton->setText(tr("Send")); - - userContextGroup->setTitle(tr("Reported User Context")); - userContextUserLabel->setText(tr("User:")); - userContextAgeLabel->setText(tr("Account Age:")); - userContextReportsLabel->setText(tr("Reports:")); - userContextBansLabel->setText(tr("Bans:")); - userContextWarnsLabel->setText(tr("Warns:")); - userContextNotesLabel->setText(tr("Admin Notes:")); - userContextRecentReportsLabel->setText(tr("Recent Reports Against User:")); - userContextAccountAge->setText(QString()); - userContextReports->setText(QString()); - userContextBans->setText(QString()); - userContextWarns->setText(QString()); - - statsGroup->setTitle(tr("Report Statistics")); - - assignButton->setText(tr("Assign to Me")); - resolveButton->setText(tr("Resolve")); - resolveWithNoteButton->setText(tr("Resolve with note...")); - dismissButton->setText(tr("Dismiss...")); - viewReplayButton->setText(tr("View Replay")); - joinGameButton->setText(tr("Join Game")); -} - -void TabReport::refreshList() -{ - selectedReportIdBeforeRefresh = selectedReportId(); - commentDraftBeforeRefresh = commentInput->text(); - previousSelectedReportValid = selectedReportInfo(previousSelectedReport); - - refreshButton->setEnabled(false); - statusLabel->setText(tr("Loading...")); - table->setRowCount(0); - allReports.clear(); - filteredReports.clear(); - commentButton->setEnabled(false); - setActionsEnabled(false); - - Command_ReportList cmd; - cmd.set_unresolved_only(unresolvedOnlyBox->isChecked()); - - PendingCommand *pend = client->prepareModeratorCommand(cmd); - connect(pend, &PendingCommand::finished, this, &TabReport::reportListResponse); - client->sendCommand(pend); -} - -void TabReport::reportListResponse(const Response &response) -{ - refreshButton->setEnabled(true); - - if (response.response_code() != Response::RespOk) { - statusLabel->setText(tr("Failed to load reports.")); - return; - } - - const Response_ReportList &resp = response.GetExtension(Response_ReportList::ext); - allReports.clear(); - for (int i = 0; i < resp.reports_size(); ++i) { - allReports.append(resp.reports(i)); - } - - applyFilters(); - updateStats(); - - if (selectedReportIdBeforeRefresh >= 0) { - bool found = false; - for (int row = 0; row < table->rowCount(); ++row) { - if (table->item(row, COL_ID) && - table->item(row, COL_ID)->data(Qt::UserRole).toInt() == selectedReportIdBeforeRefresh) { - found = true; - { - QSignalBlocker blocker(table); - table->setCurrentCell(row, 0); - } - break; - } - } - - if (found) { - bool dataChanged = false; - ServerInfo_Report newReport; - if (!selectedReportInfo(newReport) || !previousSelectedReportValid || - previousSelectedReport.description() != newReport.description() || - previousSelectedReport.status() != newReport.status() || - previousSelectedReport.resolution_note() != newReport.resolution_note() || - previousSelectedReport.assigned_mod_name() != newReport.assigned_mod_name()) { - dataChanged = true; - } - - if (dataChanged || detailsRequestedReportId != selectedReportIdBeforeRefresh) { - loadReportDetails(selectedReportIdBeforeRefresh); - } - updateActionStates(); - } else { - descriptionEdit->clear(); - chatLogEdit->clear(); - commentsEdit->clear(); - setActionsEnabled(false); - userContextGroup->setVisible(false); - } - } - - if (commentInput->text().isEmpty()) { - commentInput->setText(commentDraftBeforeRefresh); - } - - if (statsGroup->isChecked()) { - requestStats(); - } -} - -void TabReport::applyFilters() -{ - filteredReports.clear(); - - const QString searchText = searchEdit->text().toLower(); - const QString statusFilterValue = statusFilter->currentData().toString(); - - for (const ServerInfo_Report &r : allReports) { - bool matchesSearch = searchText.isEmpty() || - QString::fromStdString(r.reporter_name()).toLower().contains(searchText) || - QString::fromStdString(r.reported_user_name()).toLower().contains(searchText) || - QString::fromStdString(r.category()).toLower().contains(searchText) || - QString::fromStdString(r.description()).toLower().contains(searchText); - - bool matchesStatus = statusFilterValue.isEmpty() || QString::fromStdString(r.status()) == statusFilterValue; - - if (matchesSearch && matchesStatus) { - filteredReports.append(r); - } - } - - table->setSortingEnabled(false); - table->setRowCount(filteredReports.size()); - - for (int row = 0; row < filteredReports.size(); ++row) { - const ServerInfo_Report &r = filteredReports[row]; - - report_utils::fillReportTableRow(table, row, r, COL_ID, COL_TIME, COL_REPORTED, COL_CATEGORY, COL_GAMEID, - COL_STATUS, COL_ASSIGNED); - - table->setItem(row, COL_REPORTER, new QTableWidgetItem(QString::fromStdString(r.reporter_name()))); - - table->setItem(row, COL_REPLAY, - new QTableWidgetItem(r.has_replay_id() && r.replay_id() > 0 ? tr("Yes") : tr("No"))); - - table->setItem(row, COL_ROOM, - new QTableWidgetItem(r.has_room_id() && r.room_id() > 0 ? QString::number(r.room_id()) : "")); - } - - table->setSortingEnabled(true); - table->resizeColumnsToContents(); - table->horizontalHeader()->setSectionResizeMode(COL_REPORTED, QHeaderView::Stretch); - - statusLabel->setText(tr("%1 report(s)").arg(filteredReports.size())); -} - -void TabReport::updateStats() -{ - int open = 0, assigned = 0, resolved = 0, dismissed = 0; - for (const ServerInfo_Report &r : allReports) { - QString status = QString::fromStdString(r.status()); - if (status == "open") { - open++; - } else if (status == "assigned") { - assigned++; - } else if (status == "resolved") { - resolved++; - } else if (status == "dismissed") { - dismissed++; - } - } - - statsLabel->setText(tr("%1 open | %2 assigned | %3 resolved | %4 dismissed") - .arg(open) - .arg(assigned) - .arg(resolved) - .arg(dismissed)); -} - -void TabReport::onSelectionChanged() -{ - const int row = table->currentRow(); - if (row < 0 || !table->item(row, COL_ID)) { - descriptionEdit->clear(); - chatLogEdit->clear(); - commentsEdit->clear(); - commentInput->clear(); - commentButton->setEnabled(false); - setActionsEnabled(false); - userContextGroup->setVisible(false); - return; - } - - const int reportId = table->item(row, COL_ID)->data(Qt::UserRole).toInt(); - loadReportDetails(reportId); - updateActionStates(); -} - -void TabReport::loadReportDetails(int reportId) -{ - detailsRequestedReportId = reportId; - - for (const ServerInfo_Report &r : filteredReports) { - if (r.report_id() == reportId) { - descriptionEdit->setPlainText(QString::fromStdString(r.description())); - - QString reportedUser = QString::fromStdString(r.reported_user_name()); - if (!reportedUser.isEmpty()) { - requestUserInfo(reportedUser); - } else { - userContextGroup->setVisible(false); - } - - QString status = QString::fromStdString(r.status()); - bool canComment = (status == "open" || status == "assigned"); - commentButton->setEnabled(canComment); - commentInput->setEnabled(canComment); - if (!canComment) { - commentInput->setPlaceholderText(tr("This report is closed.")); - } else { - commentInput->setPlaceholderText(tr("Type a reply...")); - } - break; - } - } - - chatLogEdit->setPlainText(tr("Loading...")); - commentsEdit->setPlainText(tr("Loading...")); - - Command_ReportDetails cmd; - cmd.set_report_id(reportId); - - const int seq = ++detailsRequestSeq; - PendingCommand *pend = client->prepareSessionCommand(cmd); - connect(pend, &PendingCommand::finished, this, [this, seq](const Response &response) { - if (seq != detailsRequestSeq) { - return; - } - reportDetailsResponse(response); - }); - client->sendCommand(pend); -} - -void TabReport::reportDetailsResponse(const Response &response) -{ - if (response.response_code() != Response::RespOk) { - if (selectedReportId() == detailsRequestedReportId) { - chatLogEdit->clear(); - commentsEdit->setPlainText(tr("Failed to load report details.")); - } - return; - } - - const Response_ReportDetails &resp = response.GetExtension(Response_ReportDetails::ext); - const ServerInfo_Report &r = resp.report(); - - if (selectedReportId() != r.report_id()) { - return; - } - - report_utils::renderReportDetails(chatLogEdit, commentsEdit, r, tr("No comments yet."), tr("[Moderator]"), - tr("[Reporter]")); -} - -void TabReport::updateActionStates() -{ - const int row = table->currentRow(); - if (row < 0 || !table->item(row, COL_STATUS)) { - setActionsEnabled(false); - return; - } - - ServerInfo_Report report; - if (!selectedReportInfo(report)) { - setActionsEnabled(false); - return; - } - - const QString status = QString::fromStdString(report.status()); - assignButton->setEnabled(status == "open"); - resolveButton->setEnabled(status == "open" || status == "assigned"); - resolveWithNoteButton->setEnabled(status == "open" || status == "assigned"); - dismissButton->setEnabled(status == "open" || status == "assigned"); - - const bool hasGameId = report.game_id() > 0; - const bool hasReplay = hasGameId && report.has_replay_id() && report.replay_id() > 0; - viewReplayButton->setEnabled(hasReplay); - joinGameButton->setEnabled(hasGameId && report.has_room_id() && report.room_id() > 0); -} - -void TabReport::setActionsEnabled(bool enabled) -{ - assignButton->setEnabled(enabled); - resolveButton->setEnabled(enabled); - resolveWithNoteButton->setEnabled(enabled); - dismissButton->setEnabled(enabled); - viewReplayButton->setEnabled(false); - joinGameButton->setEnabled(false); - commentButton->setEnabled(enabled); - commentInput->setEnabled(enabled); -} - -int TabReport::selectedReportId() const -{ - const int row = table->currentRow(); - if (row < 0 || !table->item(row, COL_ID)) { - return -1; - } - return table->item(row, COL_ID)->data(Qt::UserRole).toInt(); -} - -bool TabReport::selectedReportInfo(ServerInfo_Report &info) const -{ - const int reportId = selectedReportId(); - if (reportId < 0) { - return false; - } - - for (const ServerInfo_Report &r : filteredReports) { - if (r.report_id() == reportId) { - info = r; - return true; - } - } - - return false; -} - -void TabReport::assignReport() -{ - const int reportId = selectedReportId(); - if (reportId < 0) { - return; - } - - setActionsEnabled(false); - statusLabel->setText(tr("Assigning...")); - - Command_ReportAssign cmd; - cmd.set_report_id(reportId); - - PendingCommand *pend = client->prepareModeratorCommand(cmd); - connect(pend, &PendingCommand::finished, this, &TabReport::assignResponse); - client->sendCommand(pend); -} - -void TabReport::assignResponse(const Response &response) -{ - if (response.response_code() == Response::RespOk) { - statusLabel->setText(tr("Assigned.")); - refreshList(); - } else { - statusLabel->setText(tr("Assignment failed.")); - updateActionStates(); - } -} - -void TabReport::resolveReport(bool dismissed, bool promptNote) -{ - const int reportId = selectedReportId(); - if (reportId < 0) { - return; - } - - QString note; - if (promptNote) { - bool ok; - note = QInputDialog::getText(this, dismissed ? tr("Dismiss Report") : tr("Resolve Report"), - dismissed ? tr("Optional note:") : tr("Resolution note (optional):"), - QLineEdit::Normal, QString(), &ok); - if (!ok) { - return; - } - } - - setActionsEnabled(false); - statusLabel->setText(dismissed ? tr("Dismissing...") : tr("Resolving...")); - - Command_ReportResolve cmd; - cmd.set_report_id(reportId); - cmd.set_dismissed(dismissed); - if (!note.isEmpty()) { - cmd.set_resolution_note(note.toStdString()); - } - - PendingCommand *pend = client->prepareModeratorCommand(cmd); - connect(pend, &PendingCommand::finished, this, &TabReport::resolveResponse); - client->sendCommand(pend); -} - -void TabReport::resolveResponse(const Response &response) -{ - if (response.response_code() == Response::RespOk) { - statusLabel->setText(tr("Done.")); - refreshList(); - } else { - statusLabel->setText(tr("Action failed.")); - updateActionStates(); - } -} - -void TabReport::viewReplay() -{ - ServerInfo_Report report; - if (!selectedReportInfo(report) || report.game_id() <= 0) { - return; - } - - setActionsEnabled(false); - statusLabel->setText(tr("Loading replay...")); - - Command_ReplayDownloadByGameId cmd; - cmd.set_game_id(report.game_id()); - - PendingCommand *pend = client->prepareModeratorCommand(cmd); - connect(pend, &PendingCommand::finished, this, &TabReport::viewReplayResponse); - client->sendCommand(pend); -} - -void TabReport::viewReplayResponse(const Response &response) -{ - if (response.response_code() != Response::RespOk) { - statusLabel->setText(tr("No replay available for this game.")); - updateActionStates(); - return; - } - - const Response_ReplayDownloadByGameId &resp = response.GetExtension(Response_ReplayDownloadByGameId::ext); - GameReplay *replay = new GameReplay; - if (replay->ParseFromString(resp.replay_data())) { - emit openReplay(replay); - statusLabel->setText(tr("Replay opened.")); - } else { - delete replay; - statusLabel->setText(tr("Failed to parse replay.")); - } - - updateActionStates(); -} - -void TabReport::joinGame() -{ - ServerInfo_Report report; - if (!selectedReportInfo(report) || report.game_id() <= 0) { - return; - } - - const int roomId = report.has_room_id() ? report.room_id() : -1; - if (roomId <= 0) { - statusLabel->setText(tr("No room recorded for this report, use the replay instead.")); - return; - } - - emit requestJoinGame(report.game_id(), roomId); - statusLabel->setText(tr("Attempting to join game...")); -} - -void TabReport::addComment() -{ - const int reportId = selectedReportId(); - if (reportId < 0) { - return; - } - - QString text = commentInput->text().trimmed(); - if (text.isEmpty()) { - return; - } - - commentButton->setEnabled(false); - - Command_ReportAddComment cmd; - cmd.set_report_id(reportId); - cmd.set_comment(text.toStdString()); - - PendingCommand *pend = client->prepareSessionCommand(cmd); - connect(pend, &PendingCommand::finished, this, &TabReport::addCommentResponse); - client->sendCommand(pend); -} - -void TabReport::addCommentResponse(const Response &response) -{ - if (response.response_code() == Response::RespOk) { - commentInput->clear(); - refreshList(); - } else { - commentButton->setEnabled(true); - statusLabel->setText(tr("Failed to send comment.")); - } -} - -void TabReport::requestUserInfo(const QString &userName) -{ - if (userName.isEmpty()) { - userContextGroup->setVisible(false); - return; - } - - lastRequestedUser = userName; - userContextGroup->setVisible(true); - userContextName->setText(userName); - userContextAccountAge->setText(tr("Loading...")); - userContextReports->clear(); - userContextBans->clear(); - userContextWarns->clear(); - userContextNotes->clear(); - userContextRecentReports->clear(); - - Command_ReportUserInfo cmd; - cmd.set_user_name(userName.toStdString()); - - PendingCommand *pend = client->prepareModeratorCommand(cmd); - connect(pend, &PendingCommand::finished, this, &TabReport::userInfoResponse); - client->sendCommand(pend); -} - -void TabReport::userInfoResponse(const Response &response) -{ - if (response.response_code() != Response::RespOk) { - userContextAccountAge->setText(tr("Error loading user info.")); - return; - } - - const Response_ReportUserInfo &resp = response.GetExtension(Response_ReportUserInfo::ext); - - if (resp.has_user_name() && QString::fromStdString(resp.user_name()) != lastRequestedUser) { - return; - } - - QDateTime regTime = QDateTime::fromSecsSinceEpoch(resp.registration_time()); - qint64 daysSinceReg = regTime.daysTo(QDateTime::currentDateTime()); - userContextAccountAge->setText(tr("%1 days (since %2)").arg(daysSinceReg).arg(regTime.toString("yyyy-MM-dd"))); - - userContextReports->setText(QString::number(resp.total_reports())); - userContextBans->setText(QString::number(resp.total_bans())); - userContextWarns->setText(QString::number(resp.total_warns())); - - if (resp.has_admin_notes() && !resp.admin_notes().empty()) { - userContextNotes->setPlainText(QString::fromStdString(resp.admin_notes())); - } else { - userContextNotes->setPlainText(tr("(none)")); - } - - userContextRecentReports->clear(); - if (resp.recent_reports_size() == 0) { - userContextRecentReports->setPlainText(tr("No previous reports against this user.")); - } else { - for (int i = 0; i < resp.recent_reports_size(); ++i) { - const ServerInfo_Report &r = resp.recent_reports(i); - QDateTime dt = QDateTime::fromSecsSinceEpoch(r.report_time()); - userContextRecentReports->moveCursor(QTextCursor::End); - userContextRecentReports->insertPlainText(QString("[%1] #%2 by %3 [%4]: %5\n") - .arg(dt.toString("yyyy-MM-dd")) - .arg(r.report_id()) - .arg(QString::fromStdString(r.reporter_name())) - .arg(QString::fromStdString(r.status())) - .arg(QString::fromStdString(r.category()))); - } - } -} - -void TabReport::requestStats() -{ - statsTotalLabel->setText(tr("Loading statistics...")); - statsTrendLabel->clear(); - statsCategoriesLabel->clear(); - statsDetailText->clear(); - - Command_ReportStats cmd; - - PendingCommand *pend = client->prepareModeratorCommand(cmd); - connect(pend, &PendingCommand::finished, this, &TabReport::statsResponse); - client->sendCommand(pend); -} - -void TabReport::statsResponse(const Response &response) -{ - if (response.response_code() != Response::RespOk) { - statsTotalLabel->setText(tr("Error loading statistics.")); - return; - } - - const Response_ReportStats &resp = response.GetExtension(Response_ReportStats::ext); - - statsTotalLabel->setText(tr("Total: %1 reports (%2 open, %3 assigned, %4 resolved/dismissed)") - .arg(resp.total_reports()) - .arg(resp.total_pending()) - .arg(resp.total_assigned()) - .arg(resp.total_resolved())); - - statsTrendLabel->setText(tr("Last 24h: %1 | Last 7d: %2 | Last 30d: %3 | Avg resolution: %4h") - .arg(resp.reports_last_24h()) - .arg(resp.reports_last_7d()) - .arg(resp.reports_last_30d()) - .arg(resp.avg_resolution_hours(), 0, 'f', 1)); - - QString weekCompare = - tr("This week: %1 vs last week: %2 (%3%)") - .arg(resp.reports_this_week()) - .arg(resp.reports_last_week()) - .arg(resp.reports_last_week() > 0 ? QString::number(((resp.reports_this_week() - resp.reports_last_week()) * - 100.0 / resp.reports_last_week()), - 'f', 0) - : resp.reports_this_week() > 0 ? "new" - : "0"); - statsTrendLabel->setText(statsTrendLabel->text() + " | " + weekCompare); - - statsCategoriesLabel->setText(tr("By category:")); - statsDetailText->clear(); - - statsDetailText->moveCursor(QTextCursor::End); - statsDetailText->insertPlainText(tr("=== Top Categories ===") + "\n"); - for (int i = 0; i < resp.category_counts_size(); ++i) { - const ReportCategoryCount &cc = resp.category_counts(i); - QString cat = QString::fromStdString(cc.category()); - cat.replace('_', ' '); - cat = cat.left(1).toUpper() + cat.mid(1); - statsDetailText->moveCursor(QTextCursor::End); - statsDetailText->insertPlainText(QString(" %1: %2\n").arg(cat).arg(cc.count())); - } - - statsDetailText->moveCursor(QTextCursor::End); - statsDetailText->insertPlainText("\n" + tr("=== Most Reported Users ===") + "\n"); - for (int i = 0; i < resp.top_reported_users_size(); ++i) { - const ReportTopUser &tu = resp.top_reported_users(i); - statsDetailText->moveCursor(QTextCursor::End); - statsDetailText->insertPlainText( - QString(" %1: %2 reports\n").arg(QString::fromStdString(tu.user_name())).arg(tu.count())); - } - - statsDetailText->moveCursor(QTextCursor::End); - statsDetailText->insertPlainText("\n" + tr("=== Top Reporters ===") + "\n"); - for (int i = 0; i < resp.top_reporters_size(); ++i) { - const ReportTopUser &tu = resp.top_reporters(i); - statsDetailText->moveCursor(QTextCursor::End); - statsDetailText->insertPlainText( - QString(" %1: %2 reports filed\n").arg(QString::fromStdString(tu.user_name())).arg(tu.count())); - } -} diff --git a/cockatrice/src/interface/widgets/tabs/tab_report.h b/cockatrice/src/interface/widgets/tabs/tab_report.h deleted file mode 100644 index b62a99ce2..000000000 --- a/cockatrice/src/interface/widgets/tabs/tab_report.h +++ /dev/null @@ -1,124 +0,0 @@ -#ifndef TAB_REPORT_H -#define TAB_REPORT_H - -#include "tab.h" - -#include -#include -#include - -class AbstractClient; -class QCheckBox; -class QComboBox; -class QGroupBox; -class QLabel; -class QLineEdit; -class QPushButton; -class QSplitter; -class QTableWidget; -class QTextEdit; -class QTimer; -class GameReplay; - -class TabReport : public Tab -{ - Q_OBJECT -public: - TabReport(TabSupervisor *_tabSupervisor, AbstractClient *_client); - void retranslateUi() override; - [[nodiscard]] QString getTabText() const override - { - return tr("Report Queue"); - } - -signals: - void openReplay(GameReplay *replay); - void requestJoinGame(int gameId, int roomId); - -private slots: - void refreshList(); - void reportListResponse(const Response &response); - void assignReport(); - void assignResponse(const Response &response); - void resolveReport(bool dismissed, bool promptNote); - void resolveResponse(const Response &response); - void onSelectionChanged(); - void viewReplay(); - void viewReplayResponse(const Response &response); - void joinGame(); - void addComment(); - void addCommentResponse(const Response &response); - void requestUserInfo(const QString &userName); - void userInfoResponse(const Response &response); - void reportDetailsResponse(const Response &response); - void requestStats(); - void statsResponse(const Response &response); - -private: - int selectedReportId() const; - bool selectedReportInfo(ServerInfo_Report &info) const; - void setActionsEnabled(bool enabled); - void updateActionStates(); - void applyFilters(); - void updateStats(); - void loadReportDetails(int reportId); - - AbstractClient *client; - - QLineEdit *searchEdit; - QComboBox *statusFilter; - QCheckBox *unresolvedOnlyBox; - QPushButton *refreshButton; - QLabel *statsLabel; - QTableWidget *table; - QSplitter *detailSplitter; - QGroupBox *descGroup; - QGroupBox *chatGroup; - QGroupBox *commentsGroup; - QTextEdit *descriptionEdit; - QTextEdit *chatLogEdit; - QTextEdit *commentsEdit; - QLineEdit *commentInput; - QPushButton *commentButton; - QPushButton *assignButton; - QPushButton *resolveButton; - QPushButton *resolveWithNoteButton; - QPushButton *dismissButton; - QPushButton *viewReplayButton; - QPushButton *joinGameButton; - QLabel *statusLabel; - QTimer *refreshTimer; - - QGroupBox *userContextGroup; - QLabel *userContextName; - QLabel *userContextAccountAge; - QLabel *userContextReports; - QLabel *userContextBans; - QLabel *userContextWarns; - QTextEdit *userContextNotes; - QTextEdit *userContextRecentReports; - QLabel *userContextUserLabel; - QLabel *userContextAgeLabel; - QLabel *userContextReportsLabel; - QLabel *userContextBansLabel; - QLabel *userContextWarnsLabel; - QLabel *userContextNotesLabel; - QLabel *userContextRecentReportsLabel; - QString lastRequestedUser; - QGroupBox *statsGroup; - QLabel *statsTotalLabel; - QLabel *statsTrendLabel; - QLabel *statsCategoriesLabel; - QTextEdit *statsDetailText; - - QList allReports; - QList filteredReports; - int detailsRequestedReportId = -1; - int detailsRequestSeq = 0; - int selectedReportIdBeforeRefresh = -1; - QString commentDraftBeforeRefresh; - ServerInfo_Report previousSelectedReport; - bool previousSelectedReportValid = false; -}; - -#endif // TAB_REPORT_H diff --git a/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp b/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp index f1b26da9d..016d96434 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp @@ -2,7 +2,6 @@ #include "../../../client/settings/cache_settings.h" #include "../../../client/settings/shortcuts_settings.h" -#include "../../intents/intent_join_server_game.h" #include "../interface/pixel_map_generator.h" #include "../interface/widgets/server/game_link.h" #include "../interface/widgets/server/user/user_list_manager.h" @@ -19,9 +18,7 @@ #include "tab_home.h" #include "tab_logs.h" #include "tab_message.h" -#include "tab_moderation.h" #include "tab_replays.h" -#include "tab_report.h" #include "tab_room.h" #include "tab_server.h" #include "tab_visual_database_display.h" @@ -34,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -43,7 +39,6 @@ #include #include #include -#include #include #include #include @@ -118,7 +113,7 @@ void CloseButton::paintEvent(QPaintEvent * /*event*/) TabSupervisor::TabSupervisor(AbstractClient *_client, QMenu *tabsMenu, QWidget *parent) : QTabWidget(parent), userInfo(nullptr), client(_client), tabsMenu(tabsMenu), tabVisualDeckStorage(nullptr), tabServer(nullptr), tabAccount(nullptr), tabDeckStorage(nullptr), tabReplays(nullptr), tabAdmin(nullptr), - tabLog(nullptr), tabReport(nullptr), tabModeration(nullptr), isLocalGame(false) + tabLog(nullptr), isLocalGame(false) { setElideMode(Qt::ElideRight); setMovable(true); @@ -195,14 +190,6 @@ TabSupervisor::TabSupervisor(AbstractClient *_client, QMenu *tabsMenu, QWidget * aTabLog->setCheckable(true); connect(aTabLog, &QAction::triggered, this, &TabSupervisor::actTabLog); - aTabReport = new QAction(this); - aTabReport->setCheckable(true); - connect(aTabReport, &QAction::triggered, this, &TabSupervisor::actTabReport); - - aTabModeration = new QAction(this); - aTabModeration->setCheckable(true); - connect(aTabModeration, &QAction::triggered, this, &TabSupervisor::actTabModeration); - connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this, &TabSupervisor::refreshShortcuts); refreshShortcuts(); @@ -242,8 +229,6 @@ void TabSupervisor::retranslateUi() aTabReplays->setText(tr("Game Replays")); aTabAdmin->setText(tr("Administration")); aTabLog->setText(tr("Logs")); - aTabReport->setText(tr("Report Queue")); - aTabModeration->setText(tr("Moderation")); // tabs QList tabs; @@ -253,8 +238,6 @@ void TabSupervisor::retranslateUi() tabs.append(tabAdmin); tabs.append(tabAccount); tabs.append(tabLog); - tabs.append(tabReport); - tabs.append(tabModeration); QMapIterator roomIterator(roomTabs); while (roomIterator.hasNext()) { tabs.append(roomIterator.next().value()); @@ -301,8 +284,6 @@ void TabSupervisor::refreshShortcuts() aTabReplays->setShortcuts(shortcuts.getShortcut("Tabs/aTabReplays")); aTabAdmin->setShortcuts(shortcuts.getShortcut("Tabs/aTabAdmin")); aTabLog->setShortcuts(shortcuts.getShortcut("Tabs/aTabLog")); - aTabReport->setShortcuts(shortcuts.getShortcut("Tabs/aTabReport")); - aTabModeration->setShortcuts(shortcuts.getShortcut("Tabs/aTabModeration")); } void TabSupervisor::closeEvent(QCloseEvent *event) @@ -504,8 +485,6 @@ void TabSupervisor::start(const ServerInfo_User &_userInfo) tabsMenu->addAction(aTabAdmin); tabsMenu->addAction(aTabLog); tabsMenu->addAction(aTabCardArtRules); - tabsMenu->addAction(aTabReport); - tabsMenu->addAction(aTabModeration); if (SettingsCache::instance().tabs().getTabAdminOpen()) { openTabAdmin(); @@ -513,12 +492,6 @@ void TabSupervisor::start(const ServerInfo_User &_userInfo) if (SettingsCache::instance().tabs().getTabLogOpen()) { openTabLog(); } - if (SettingsCache::instance().tabs().getTabReportOpen()) { - openTabReport(); - } - if (SettingsCache::instance().tabs().getTabModerationOpen()) { - openTabModeration(); - } openTabCardArtRules(); } @@ -532,8 +505,6 @@ void TabSupervisor::startLocal(const QList &_clients) tabAccount = nullptr; tabAdmin = nullptr; tabLog = nullptr; - tabReport = nullptr; - tabModeration = nullptr; isLocalGame = true; userInfo = new ServerInfo_User; localClients = _clients; @@ -575,12 +546,6 @@ void TabSupervisor::stop() if (tabLog) { tabLog->close(); } - if (tabReport) { - tabReport->close(); - } - if (tabModeration) { - tabModeration->close(); - } } QList tabsToDelete; @@ -818,59 +783,6 @@ void TabSupervisor::openTabLog() aTabLog->setChecked(true); } -void TabSupervisor::actTabReport(bool checked) -{ - SettingsCache::instance().tabs().setTabReportOpen(checked); - if (checked && !tabReport) { - openTabReport(); - setCurrentWidget(tabReport); - } else if (!checked && tabReport) { - tabReport->closeRequest(); - } -} - -void TabSupervisor::openTabReport() -{ - tabReport = new TabReport(this, client); - myAddTab(tabReport, aTabReport); - connect(tabReport, &TabReport::openReplay, this, &TabSupervisor::openReplay); - connect(tabReport, &TabReport::requestJoinGame, this, &TabSupervisor::joinReportGame); - connect(tabReport, &QObject::destroyed, this, [this] { - tabReport = nullptr; - aTabReport->setChecked(false); - }); - aTabReport->setChecked(true); -} - -void TabSupervisor::actTabModeration(bool checked) -{ - SettingsCache::instance().tabs().setTabModerationOpen(checked); - if (checked && !tabModeration) { - openTabModeration(); - setCurrentWidget(tabModeration); - } else if (!checked && tabModeration) { - tabModeration->closeRequest(); - } -} - -void TabSupervisor::openTabModeration(const QString &userName) -{ - if (tabModeration) { - setCurrentWidget(tabModeration); - if (!userName.isEmpty()) { - tabModeration->investigate(userName); - } - return; - } - tabModeration = new TabModeration(this, client, userName); - myAddTab(tabModeration, aTabModeration); - connect(tabModeration, &QObject::destroyed, this, [this] { - tabModeration = nullptr; - aTabModeration->setChecked(false); - }); - aTabModeration->setChecked(true); -} - void TabSupervisor::updatePingTime(int value, int max) { if (!tabServer) { @@ -988,30 +900,6 @@ void TabSupervisor::replayLeft(TabGame *tab) replayTabs.removeOne(tab); } -void TabSupervisor::joinReportGame(const int gameId, const int roomId) -{ - auto *remoteClient = qobject_cast(client); - if (!remoteClient) { - actShowPopup(tr("Report joins are only available on a remote server.")); - return; - } - - auto ctx = std::make_unique(); - ctx->roomContext.serverContext.hostname = remoteClient->peerName(); - ctx->roomContext.serverContext.port = QString::number(remoteClient->peerPort()); - ctx->roomContext.roomId = roomId; - ctx->gameId = gameId; - ctx->asSpectator = true; - - auto *joinGameIntent = new IntentJoinServerGame(this, remoteClient, std::move(ctx)); - joinGameIntent->setParent(this); - connect(joinGameIntent, &Intent::failed, this, [gameId](const QString &reason) { - actShowPopup(tr("Could not join game %1.\n%2").arg(gameId).arg(reason)); - }); - - joinGameIntent->execute(); -} - TabMessage *TabSupervisor::addMessageTab(const QString &receiverName, bool focus) { if (receiverName == QString::fromStdString(userInfo->name())) { @@ -1396,24 +1284,6 @@ void TabSupervisor::processNotifyUserEvent(const Event_NotifyUser &event) } break; } - case Event_NotifyUser::REPORT_RESOLVED: { - QString title = QString::fromStdString(event.custom_title()).simplified(); - QString content = QString::fromStdString(event.custom_content()).trimmed(); - if (!title.isEmpty() && !content.isEmpty()) { - actShowPopup(title + "\n" + content); - QApplication::alert(this); - } - break; - } - case Event_NotifyUser::REPORT_COMMENT: { - QString title = QString::fromStdString(event.custom_title()).simplified(); - QString content = QString::fromStdString(event.custom_content()).trimmed(); - if (!title.isEmpty() && !content.isEmpty()) { - actShowPopup(title + "\n" + content); - QApplication::alert(this); - } - break; - } default:; } } diff --git a/cockatrice/src/interface/widgets/tabs/tab_supervisor.h b/cockatrice/src/interface/widgets/tabs/tab_supervisor.h index 32ed14504..81ad22f54 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_supervisor.h +++ b/cockatrice/src/interface/widgets/tabs/tab_supervisor.h @@ -40,8 +40,6 @@ class TabDeckStorage; class TabReplays; class TabAdmin; class TabMessage; -class TabReport; -class TabModeration; class TabAccount; class TabDeckEditor; class TabLog; @@ -105,8 +103,6 @@ private: TabAdmin *tabAdmin; TabCardArtRules *tabCardArtRules; TabLog *tabLog; - TabReport *tabReport; - TabModeration *tabModeration; QMap roomTabs; QMap gameTabs; QList replayTabs; @@ -116,7 +112,7 @@ private: QAction *aTabHome, *aTabDeckEditor, *aTabVisualDeckEditor, *aTabEdhRec, *aTabArchidekt, *aTabVisualDeckStorage, *aTabVisualDatabaseDisplay, *aTabServer, *aTabAccount, *aTabDeckStorage, *aTabReplays, *aTabAdmin, - *aTabCardArtRules, *aTabLog, *aTabReport, *aTabModeration; + *aTabCardArtRules, *aTabLog; int myAddTab(Tab *tab, QAction *manager = nullptr); void addCloseButtonToTab(Tab *tab, int tabIndex, QAction *manager); @@ -187,8 +183,6 @@ public slots: TabArchidekt *addArchidektTab(); TabEdhRec *addEdhrecTab(const CardInfoPtr &cardToQuery, bool isCommander = false); void openReplay(GameReplay *replay); - void joinReportGame(int gameId, int roomId); - void openTabModeration(const QString &userName = {}); void switchToFirstAvailableNetworkTab(); void maximizeMainWindow(); void actTabVisualDeckStorage(bool checked); @@ -204,8 +198,6 @@ private slots: void actTabDeckStorage(bool checked); void actTabAdmin(bool checked); void actTabLog(bool checked); - void actTabReport(bool checked); - void actTabModeration(bool checked); void openTabVisualDeckStorage(); void openTabHome(); @@ -216,7 +208,6 @@ private slots: void actTabCardArtRules(bool checked); void openTabCardArtRules(); void openTabLog(); - void openTabReport(); void updateCurrent(int index); void updatePingTime(int value, int max); diff --git a/cockatrice/src/interface/widgets/utility/report_utils.cpp b/cockatrice/src/interface/widgets/utility/report_utils.cpp deleted file mode 100644 index aa4558e5c..000000000 --- a/cockatrice/src/interface/widgets/utility/report_utils.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "report_utils.h" - -#include -#include -#include -#include -#include -#include -#include - -namespace report_utils -{ - -namespace -{ -QColor reportStatusColor(const QString &status) -{ - const QColor text = qApp->palette().color(QPalette::Text); - const int luminance = (299 * text.red() + 587 * text.green() + 114 * text.blue()) / 1000; - const bool dark = luminance < 128; - - if (status == "open") { - return dark ? QColor("#e06060") : QColor("#c0392b"); - } - if (status == "assigned") { - return dark ? QColor("#e0a030") : QColor("#d68910"); - } - if (status == "resolved") { - return dark ? QColor("#50c878") : QColor("#1e8449"); - } - if (status == "dismissed") { - return qApp->palette().color(QPalette::PlaceholderText); - } - return QColor(); -} - -void setReportStatusItemColor(QTableWidgetItem *item, const QString &status) -{ - const QColor color = reportStatusColor(status); - if (color.isValid()) { - item->setForeground(QBrush(color)); - } -} -} // namespace - -QString formatReportCategory(const QString &category) -{ - QString result = category; - result.replace('_', ' '); - return result.left(1).toUpper() + result.mid(1); -} - -QString formatReportTime(qint64 secondsSinceEpoch) -{ - const QDateTime dt = QDateTime::fromSecsSinceEpoch(secondsSinceEpoch); - return dt.toString("yyyy-MM-dd hh:mm"); -} - -void fillReportTableRow(QTableWidget *table, - int row, - const ServerInfo_Report &report, - int idColumn, - int timeColumn, - int reportedColumn, - int categoryColumn, - int gameIdColumn, - int statusColumn, - int assignedColumn) -{ - auto *idItem = new QTableWidgetItem(QString::number(report.report_id())); - idItem->setData(Qt::UserRole, report.report_id()); - table->setItem(row, idColumn, idItem); - - table->setItem(row, timeColumn, new QTableWidgetItem(formatReportTime(report.report_time()))); - table->setItem(row, reportedColumn, new QTableWidgetItem(QString::fromStdString(report.reported_user_name()))); - table->setItem(row, categoryColumn, - new QTableWidgetItem(formatReportCategory(QString::fromStdString(report.category())))); - table->setItem(row, gameIdColumn, - new QTableWidgetItem(report.game_id() > 0 ? QString::number(report.game_id()) : QString())); - - auto *statusItem = new QTableWidgetItem(QString::fromStdString(report.status())); - setReportStatusItemColor(statusItem, QString::fromStdString(report.status())); - table->setItem(row, statusColumn, statusItem); - - table->setItem(row, assignedColumn, new QTableWidgetItem(QString::fromStdString(report.assigned_mod_name()))); -} - -void renderReportDetails(QTextEdit *chatLogEdit, - QTextEdit *commentsEdit, - const ServerInfo_Report &report, - const QString &commentsEmptyText, - const QString &moderatorPrefix, - const QString &nonModeratorPrefix) -{ - if (report.has_chat_log() && !report.chat_log().empty()) { - chatLogEdit->setPlainText(QString::fromStdString(report.chat_log())); - } else { - chatLogEdit->clear(); - } - - commentsEdit->clear(); - - if (report.comments_size() == 0) { - commentsEdit->setPlainText(commentsEmptyText); - return; - } - - for (int i = 0; i < report.comments_size(); ++i) { - const ServerInfo_ReportComment &c = report.comments(i); - const QString author = QString::fromStdString(c.author_name()); - const QString text = QString::fromStdString(c.comment_text()); - const QString prefix = c.is_moderator() ? moderatorPrefix : nonModeratorPrefix; - commentsEdit->moveCursor(QTextCursor::End); - commentsEdit->insertPlainText( - QString("[%1] %2 %3:\n%4\n\n").arg(formatReportTime(c.comment_time()), prefix, author, text)); - } -} - -} // namespace report_utils diff --git a/cockatrice/src/interface/widgets/utility/report_utils.h b/cockatrice/src/interface/widgets/utility/report_utils.h deleted file mode 100644 index c6cc96c18..000000000 --- a/cockatrice/src/interface/widgets/utility/report_utils.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef REPORT_UTILS_H -#define REPORT_UTILS_H - -#include -#include - -class QTableWidget; -class QTextEdit; - -namespace report_utils -{ - -QString formatReportCategory(const QString &category); -QString formatReportTime(qint64 secondsSinceEpoch); - -void fillReportTableRow(QTableWidget *table, - int row, - const ServerInfo_Report &report, - int idColumn, - int timeColumn, - int reportedColumn, - int categoryColumn, - int gameIdColumn, - int statusColumn, - int assignedColumn); - -void renderReportDetails(QTextEdit *chatLogEdit, - QTextEdit *commentsEdit, - const ServerInfo_Report &report, - const QString &commentsEmptyText, - const QString &moderatorPrefix, - const QString &nonModeratorPrefix); - -} // namespace report_utils - -#endif // REPORT_UTILS_H diff --git a/libcockatrice_deck_list/CMakeLists.txt b/libcockatrice_deck_list/CMakeLists.txt index c7a54a390..33494b9a9 100644 --- a/libcockatrice_deck_list/CMakeLists.txt +++ b/libcockatrice_deck_list/CMakeLists.txt @@ -11,7 +11,6 @@ set(HEADERS libcockatrice/deck_list/deck_list_history_manager.h libcockatrice/deck_list/deck_list_node_tree.h libcockatrice/deck_list/deck_list_memento.h - libcockatrice/deck_list/playmat_resolver.h libcockatrice/deck_list/sideboard_plan.h ) @@ -27,7 +26,6 @@ add_library( libcockatrice/deck_list/deck_list.cpp libcockatrice/deck_list/deck_list_history_manager.cpp libcockatrice/deck_list/deck_list_node_tree.cpp - libcockatrice/deck_list/playmat_resolver.cpp libcockatrice/deck_list/sideboard_plan.cpp ) @@ -35,7 +33,4 @@ add_dependencies(libcockatrice_deck_list libcockatrice_protocol) target_include_directories(libcockatrice_deck_list PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries( - libcockatrice_deck_list PUBLIC libcockatrice_interfaces libcockatrice_protocol libcockatrice_utility - ${QT_CORE_MODULE} -) +target_link_libraries(libcockatrice_deck_list PUBLIC libcockatrice_protocol libcockatrice_utility ${QT_CORE_MODULE}) diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp index 4ffc1bab7..d41713302 100644 --- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp +++ b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp @@ -25,7 +25,7 @@ static const QString CURRENT_SIDEBOARD_PLAN_KEY = ""; bool DeckList::Metadata::isEmpty() const { - return name.isEmpty() && comments.isEmpty() && bannerCard.isEmpty() && tags.isEmpty() && playmat.card.isEmpty(); + return name.isEmpty() && comments.isEmpty() && bannerCard.isEmpty() && tags.isEmpty(); } DeckList::DeckList() @@ -74,36 +74,6 @@ bool DeckList::readElement(QXmlStreamReader *xml) QString providerId = xml->attributes().value("providerId").toString(); QString cardName = xml->readElementText(); metadata.bannerCard = {cardName, providerId}; - } else if (childName == "playmatCard") { - QString providerId = xml->attributes().value("providerId").toString(); - bool ok; - QString marginLStr = xml->attributes().value("marginPctL").toString(); - QString marginRStr = xml->attributes().value("marginPctR").toString(); - QString vOffStr = xml->attributes().value("verticalOffset").toString(); - QString zoomStr = xml->attributes().value("zoom").toString(); - QString cardName = xml->readElementText(); - PlaymatInfo playmat; - playmat.card = {cardName, providerId}; - // Clamp to the same ranges as the settings dialog and the remote - // player-properties path so malformed deck files cannot produce - // degenerate art rectangles (e.g. a zoom of 0 dividing by zero). - playmat.params.marginPctL = qBound(0.0, marginLStr.toDouble(&ok), 0.95); - if (!ok) { - playmat.params.marginPctL = 0.07; - } - playmat.params.marginPctR = qBound(0.0, marginRStr.toDouble(&ok), 0.95); - if (!ok) { - playmat.params.marginPctR = 0.07; - } - playmat.params.verticalOffset = qBound(0.0, vOffStr.toDouble(&ok), 1.0); - if (!ok) { - playmat.params.verticalOffset = 0.33; - } - playmat.params.zoom = qBound(0.1, zoomStr.toDouble(&ok), 4.0); - if (!ok) { - playmat.params.zoom = 1.0; - } - metadata.playmat = playmat; } else if (childName == "tags") { metadata.tags.clear(); // Clear existing tags while (xml->readNextStartElement()) { @@ -134,16 +104,6 @@ static void writeMetadata(QXmlStreamWriter *xml, const DeckList::Metadata &metad xml->writeAttribute("providerId", metadata.bannerCard.providerId); xml->writeCharacters(metadata.bannerCard.name); xml->writeEndElement(); - if (!metadata.playmat.card.isEmpty()) { - xml->writeStartElement("playmatCard"); - xml->writeAttribute("providerId", metadata.playmat.card.providerId); - xml->writeAttribute("marginPctL", QString::number(metadata.playmat.params.marginPctL, 'f', 4)); - xml->writeAttribute("marginPctR", QString::number(metadata.playmat.params.marginPctR, 'f', 4)); - xml->writeAttribute("verticalOffset", QString::number(metadata.playmat.params.verticalOffset, 'f', 4)); - xml->writeAttribute("zoom", QString::number(metadata.playmat.params.zoom, 'f', 4)); - xml->writeCharacters(metadata.playmat.card.name); - xml->writeEndElement(); - } xml->writeTextElement("comments", metadata.comments); // Write tags diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h index 475d99560..e792c85dc 100644 --- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h +++ b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h @@ -18,7 +18,7 @@ #include #include #include -#include +#include class AbstractDecklistNode; class DecklistCardNode; @@ -70,7 +70,6 @@ public: CardRef bannerCard; ///< Optional representative card for the deck. QStringList tags; ///< User-defined tags for deck classification. QString lastLoadedTimestamp; ///< Timestamp string of last load. - PlaymatInfo playmat; ///< Optional playmat background for table+stack zones. /** * @brief Checks if all values (except for lastLoadedTimestamp) in the metadata is empty. @@ -116,10 +115,6 @@ public: { metadata.bannerCard = _bannerCard; } - void setPlaymat(const PlaymatInfo &_playmat = {}) - { - metadata.playmat = _playmat; - } void setLastLoadedTimestamp(const QString &_lastLoadedTimestamp = QString()) { metadata.lastLoadedTimestamp = _lastLoadedTimestamp; @@ -175,10 +170,6 @@ public: { return metadata.bannerCard; } - PlaymatInfo getPlaymat() const - { - return metadata.playmat; - } QString getLastLoadedTimestamp() const { return metadata.lastLoadedTimestamp; diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/playmat_resolver.cpp b/libcockatrice_deck_list/libcockatrice/deck_list/playmat_resolver.cpp deleted file mode 100644 index fd72fea0b..000000000 --- a/libcockatrice_deck_list/libcockatrice/deck_list/playmat_resolver.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "playmat_resolver.h" - -#include - -PlaymatInfo resolveEffectivePlaymat(const DeckList &deck, - const PlaymatInfo &force, - const QList &fallbackList, - PlaymatFallbackMode fallbackMode, - int rotationIndex) -{ - if (!force.card.isEmpty()) { - return force; - } - - const PlaymatInfo &deckPlaymat = deck.getPlaymat(); - if (!deckPlaymat.card.isEmpty()) { - return deckPlaymat; - } - - if (fallbackList.isEmpty()) { - return {}; - } - - switch (fallbackMode) { - case PlaymatFallbackModeFixed: - return fallbackList.first(); - case PlaymatFallbackModeRoundRobin: - return fallbackList.at(rotationIndex % fallbackList.size()); - case PlaymatFallbackModeRandom: - return fallbackList.at(QRandomGenerator::global()->bounded(fallbackList.size())); - } - - return {}; -} - -PlaymatInfo resolvePlaymatForDeck(const DeckList &deck, - const QList &fallbackList, - PlaymatMode mode, - PlaymatFallbackMode fallbackBehavior, - int rotationIndex) -{ - switch (mode) { - case PlaymatModeOverrideDeck: { - const DeckList emptyDeck; - return resolveEffectivePlaymat(emptyDeck, {}, fallbackList, fallbackBehavior, rotationIndex); - } - case PlaymatModeFallback: - return resolveEffectivePlaymat(deck, {}, fallbackList, fallbackBehavior, rotationIndex); - case PlaymatModeDeckOnly: - return deck.getPlaymat(); - } - - return {}; -} diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/playmat_resolver.h b/libcockatrice_deck_list/libcockatrice/deck_list/playmat_resolver.h deleted file mode 100644 index 59bc449c8..000000000 --- a/libcockatrice_deck_list/libcockatrice/deck_list/playmat_resolver.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef COCKATRICE_PLAYMAT_RESOLVER_H -#define COCKATRICE_PLAYMAT_RESOLVER_H - -#include "deck_list.h" - -#include - -/** - * @brief Resolves the effective playmat for a deck per the resolution chain: - * force override > deck-configured playmat > fallback list > none. - * - * @param deck The deck to resolve a playmat for. - * @param force An optional user-level override; wins over everything. Pass an - * empty @ref PlaymatInfo::card to skip it. - * @param fallbackList User-level fallback playmats, consulted only when the - * deck has no configured playmat. - * @param fallbackMode How @p fallbackList is consulted (ignored when empty). - * @param rotationIndex In/out cursor for @c PlaymatFallbackModeRoundRobin; - * advanced once per call. Unused for the other modes. - * @return The effective playmat; an empty @ref PlaymatInfo::card when - * nothing in the chain resolves. - */ -PlaymatInfo resolveEffectivePlaymat(const DeckList &deck, - const PlaymatInfo &force, - const QList &fallbackList, - PlaymatFallbackMode fallbackMode, - int rotationIndex); - -/** - * @brief Resolves the playmat to display for a deck according to the user's - * collection mode (@ref PlaymatMode), combining the deck with the - * given fallback list. - * - * @param deck The deck to resolve a playmat for. - * @param fallbackList User-level fallback playmats. - * @param mode How the collection interacts with the deck-configured playmat. - * @param fallbackBehavior How @p fallbackList is picked from. - * @param rotationIndex Cursor for @c PlaymatFallbackModeRoundRobin. - * @return The effective playmat; an empty @ref PlaymatInfo::card when nothing resolves. - */ -PlaymatInfo resolvePlaymatForDeck(const DeckList &deck, - const QList &fallbackList, - PlaymatMode mode, - PlaymatFallbackMode fallbackBehavior, - int rotationIndex); - -#endif // COCKATRICE_PLAYMAT_RESOLVER_H diff --git a/libcockatrice_interfaces/CMakeLists.txt b/libcockatrice_interfaces/CMakeLists.txt index f53c41807..f606f6207 100644 --- a/libcockatrice_interfaces/CMakeLists.txt +++ b/libcockatrice_interfaces/CMakeLists.txt @@ -30,4 +30,4 @@ add_library(libcockatrice_interfaces STATIC ${MOC_SOURCES}) target_include_directories(libcockatrice_interfaces PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(libcockatrice_interfaces PUBLIC libcockatrice_utility ${QT_CORE_MODULE}) +target_link_libraries(libcockatrice_interfaces PUBLIC ${QT_CORE_MODULE}) diff --git a/libcockatrice_interfaces/libcockatrice/interfaces/interface_interface_settings_provider.h b/libcockatrice_interfaces/libcockatrice/interfaces/interface_interface_settings_provider.h index bc2118cb3..b77c98357 100644 --- a/libcockatrice_interfaces/libcockatrice/interfaces/interface_interface_settings_provider.h +++ b/libcockatrice_interfaces/libcockatrice/interfaces/interface_interface_settings_provider.h @@ -1,40 +1,8 @@ #ifndef COCKATRICE_INTERFACE_INTERFACE_SETTINGS_PROVIDER_H #define COCKATRICE_INTERFACE_INTERFACE_SETTINGS_PROVIDER_H -#include #include #include -#include - -/** - * @brief Whether playmats are rendered in-game, and for whom. - */ -enum PlaymatVisibility -{ - PlaymatVisibilityNone = 0, ///< Don't use playmats. - PlaymatVisibilityOwnOnly = 1, ///< Show the local player's playmat only. - PlaymatVisibilityAll = 2 ///< Show playmats for all players. -}; - -/** - * @brief How the user-level playmat collection interacts with the deck-configured playmat. - */ -enum PlaymatMode -{ - PlaymatModeOverrideDeck = 0, ///< Always use the collection, ignoring any deck-configured playmat. - PlaymatModeFallback = 1, ///< Prefer the deck-configured playmat; fall back to the collection when absent. - PlaymatModeDeckOnly = 2 ///< Use only the deck-configured playmat, ignoring the collection. -}; - -/** - * @brief How the user-level fallback playmat list is consulted when a deck has no playmat configured. - */ -enum PlaymatFallbackMode -{ - PlaymatFallbackModeFixed = 0, ///< Always use the first entry of the fallback list. - PlaymatFallbackModeRoundRobin = 1, ///< Cycle through the list, advancing one entry per resolution. - PlaymatFallbackModeRandom = 2 ///< Pick a random entry per resolution. -}; class IInterfaceSettingsProvider { @@ -75,21 +43,6 @@ public: [[nodiscard]] virtual bool getLifeCounterAnimationsEnabled() const = 0; [[nodiscard]] virtual bool getBattlefieldFlashEnabled() const = 0; [[nodiscard]] virtual QStringList getUserListExpandedSections() const = 0; - - /** @brief Who gets playmats rendered: @ref PlaymatVisibility. */ - [[nodiscard]] virtual int getPlaymatVisibility() const = 0; - - /** @brief User-level playmat collection. Used either as a forced playmat - * (mode == @ref PlaymatModeOverrideDeck) or as a fallback when a deck has none - * (mode == @ref PlaymatModeFallback). */ - [[nodiscard]] virtual QList getPlaymatFallbackList() const = 0; - - /** @brief How the fallback list is applied: @ref PlaymatMode. */ - [[nodiscard]] virtual int getPlaymatMode() const = 0; - - /** @brief How the fallback list is picked from when mode is @ref PlaymatModeFallback: - * @ref PlaymatFallbackMode. */ - [[nodiscard]] virtual int getPlaymatFallbackBehavior() const = 0; }; #endif // COCKATRICE_INTERFACE_INTERFACE_SETTINGS_PROVIDER_H diff --git a/libcockatrice_interfaces/libcockatrice/interfaces/interface_tabs_settings_provider.h b/libcockatrice_interfaces/libcockatrice/interfaces/interface_tabs_settings_provider.h index a81616cb0..bbe475903 100644 --- a/libcockatrice_interfaces/libcockatrice/interfaces/interface_tabs_settings_provider.h +++ b/libcockatrice_interfaces/libcockatrice/interfaces/interface_tabs_settings_provider.h @@ -19,8 +19,6 @@ public: [[nodiscard]] virtual bool getTabReplaysOpen() const = 0; [[nodiscard]] virtual bool getTabAdminOpen() const = 0; [[nodiscard]] virtual bool getTabLogOpen() const = 0; - [[nodiscard]] virtual bool getTabReportOpen() const = 0; - [[nodiscard]] virtual bool getTabModerationOpen() const = 0; }; #endif // COCKATRICE_INTERFACE_TABS_SETTINGS_PROVIDER_H diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.cpp index a6280ffa1..5c0fdf944 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.cpp @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -145,13 +144,6 @@ Response::ResponseCode Server_AbstractParticipant::cmdSetSideboardLock(const Com return Response::RespFunctionNotAllowed; } -Response::ResponseCode Server_AbstractParticipant::cmdSetPlaymat(const Command_SetPlaymat & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - Response::ResponseCode Server_AbstractParticipant::cmdConcede(const Command_Concede & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage & /*ges*/) @@ -533,9 +525,6 @@ Server_AbstractParticipant::processGameCommand(const GameCommand &command, Respo case GameCommand::REVERSE_TURN: return cmdReverseTurn(command.GetExtension(Command_ReverseTurn::ext), rc, ges); break; - case GameCommand::SET_PLAYMAT: - return cmdSetPlaymat(command.GetExtension(Command_SetPlaymat::ext), rc, ges); - break; default: return Response::RespInvalidCommand; } diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.h index c78ae78c1..a24fa5799 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.h +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.h @@ -52,7 +52,6 @@ class Command_SetSideboardPlan; class Command_DeckSelect; class Command_SetSideboardLock; class Command_ChangeZoneProperties; -class Command_SetPlaymat; class Server_AbstractParticipant : public Server_ArrowTarget, public ServerInfo_User_Container { @@ -125,8 +124,6 @@ public: cmdSetSideboardPlan(const Command_SetSideboardPlan &cmd, ResponseContainer &rc, GameEventStorage &ges); virtual Response::ResponseCode cmdSetSideboardLock(const Command_SetSideboardLock &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdSetPlaymat(const Command_SetPlaymat &cmd, ResponseContainer &rc, GameEventStorage &ges); virtual Response::ResponseCode cmdGameSay(const Command_GameSay &cmd, ResponseContainer &rc, GameEventStorage &ges); virtual Response::ResponseCode cmdShuffle(const Command_Shuffle &cmd, ResponseContainer &rc, GameEventStorage &ges); virtual Response::ResponseCode diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp index 957a89792..4128c6c90 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp @@ -1653,13 +1653,5 @@ void Server_AbstractPlayer::getPlayerProperties(ServerInfo_PlayerProperties &res result.set_ready_start(readyStart); if (deck) { result.set_deck_hash(deck->getDeckHash().toStdString()); - const auto &playmat = deck->getPlaymat(); - auto *playmatParams = result.mutable_playmat_params(); - playmatParams->set_card_name(playmat.card.name.toStdString()); - playmatParams->set_card_provider_id(playmat.card.providerId.toStdString()); - playmatParams->set_margin_pct_l(playmat.params.marginPctL); - playmatParams->set_margin_pct_r(playmat.params.marginPctR); - playmatParams->set_vertical_offset(playmat.params.verticalOffset); - playmatParams->set_zoom(playmat.params.zoom); } } diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp index cafa33c07..d502fc7d6 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -251,14 +250,6 @@ Server_Player::cmdDeckSelect(const Command_DeckSelect &cmd, ResponseContainer &r Event_PlayerPropertiesChanged event; event.mutable_player_properties()->set_sideboard_locked(true); event.mutable_player_properties()->set_deck_hash(deck->getDeckHash().toStdString()); - const auto &playmat = deck->getPlaymat(); - auto *playmatParams = event.mutable_player_properties()->mutable_playmat_params(); - playmatParams->set_card_name(playmat.card.name.left(MAX_NAME_LENGTH).toStdString()); - playmatParams->set_card_provider_id(playmat.card.providerId.left(MAX_NAME_LENGTH).toStdString()); - playmatParams->set_margin_pct_l(playmat.params.marginPctL); - playmatParams->set_margin_pct_r(playmat.params.marginPctR); - playmatParams->set_vertical_offset(playmat.params.verticalOffset); - playmatParams->set_zoom(playmat.params.zoom); ges.enqueueGameEvent(event, playerId); Context_DeckSelect context; @@ -603,46 +594,6 @@ Server_Player::cmdReverseTurn(const Command_ReverseTurn &cmd, ResponseContainer return Server_AbstractParticipant::cmdReverseTurn(cmd, rc, ges); } -Response::ResponseCode -Server_Player::cmdSetPlaymat(const Command_SetPlaymat &cmd, ResponseContainer &rc, GameEventStorage &ges) -{ - Q_UNUSED(rc); - - if (!deck) { - return Response::RespContextError; - } - - const auto &pp = cmd.playmat_params(); - const auto rawName = QString::fromStdString(pp.card_name()); - const auto rawProviderId = QString::fromStdString(pp.card_provider_id()); - if (rawName.length() > MAX_NAME_LENGTH || rawProviderId.length() > MAX_NAME_LENGTH) { - return Response::RespInvalidData; - } - PlaymatInfo playmat; - playmat.card.name = rawName; - playmat.card.providerId = rawProviderId; - playmat.params.marginPctL = qBound(0.0, pp.margin_pct_l(), 0.95); - playmat.params.marginPctR = qBound(0.0, pp.margin_pct_r(), 0.95); - playmat.params.verticalOffset = qBound(0.0, pp.vertical_offset(), 1.0); - playmat.params.zoom = qBound(0.1, pp.zoom(), 4.0); - deck->setPlaymat(playmat); - - Event_PlayerPropertiesChanged event; - auto *props = event.mutable_player_properties(); - props->set_sideboard_locked(sideboardLocked); - props->set_deck_hash(deck->getDeckHash().toStdString()); - auto *playmatParams = props->mutable_playmat_params(); - playmatParams->set_card_name(playmat.card.name.toStdString()); - playmatParams->set_card_provider_id(playmat.card.providerId.toStdString()); - playmatParams->set_margin_pct_l(playmat.params.marginPctL); - playmatParams->set_margin_pct_r(playmat.params.marginPctR); - playmatParams->set_vertical_offset(playmat.params.verticalOffset); - playmatParams->set_zoom(playmat.params.zoom); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; -} - void Server_Player::getInfo(ServerInfo_Player *info, Server_AbstractParticipant *recipient, bool omniscient, diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.h index c35428480..5925ed3c2 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.h +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.h @@ -62,8 +62,6 @@ public: cmdSetActivePhase(const Command_SetActivePhase &cmd, ResponseContainer &rc, GameEventStorage &ges) override; Response::ResponseCode cmdReverseTurn(const Command_ReverseTurn & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage &ges) override; - Response::ResponseCode - cmdSetPlaymat(const Command_SetPlaymat &cmd, ResponseContainer &rc, GameEventStorage &ges) override; Response::ResponseCode cmdChangeZoneProperties(const Command_ChangeZoneProperties &cmd, ResponseContainer &rc, GameEventStorage &ges) override; diff --git a/libcockatrice_network/libcockatrice/network/server/remote/server.h b/libcockatrice_network/libcockatrice/network/server/remote/server.h index 0ded27afa..12e71ebff 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/server.h +++ b/libcockatrice_network/libcockatrice/network/server/remote/server.h @@ -38,8 +38,7 @@ enum AuthenticationResult UsernameInvalid, RegistrationRequired, UserIsInactive, - ClientIdRequired, - PasswordChangeRequired + ClientIdRequired }; class Server : public QObject diff --git a/libcockatrice_network/libcockatrice/network/server/remote/server_abstractuserinterface.cpp b/libcockatrice_network/libcockatrice/network/server/remote/server_abstractuserinterface.cpp index 31fa13d81..641be1eed 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/server_abstractuserinterface.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/server_abstractuserinterface.cpp @@ -69,10 +69,6 @@ void Server_AbstractUserInterface::sendResponseContainer(const ResponseContainer } } -void Server_AbstractUserInterface::onLogin(ResponseContainer &) -{ -} - void Server_AbstractUserInterface::playerRemovedFromGame(Server_Game *game) { qDebug() << "Server_AbstractUserInterface::playerRemovedFromGame(): gameId =" << game->getGameId(); diff --git a/libcockatrice_network/libcockatrice/network/server/remote/server_abstractuserinterface.h b/libcockatrice_network/libcockatrice/network/server/remote/server_abstractuserinterface.h index 2da72c01d..b11260003 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/server_abstractuserinterface.h +++ b/libcockatrice_network/libcockatrice/network/server/remote/server_abstractuserinterface.h @@ -42,7 +42,6 @@ public: void playerRemovedFromGame(Server_Game *game); void playerAddedToGame(int gameId, int roomId, int playerId); void joinPersistentGames(ResponseContainer &rc); - virtual void onLogin(ResponseContainer &rc); QMap> getGames() const { diff --git a/libcockatrice_network/libcockatrice/network/server/remote/server_database_interface.h b/libcockatrice_network/libcockatrice/network/server/remote/server_database_interface.h index 1e4fc990b..b43dbde42 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/server_database_interface.h +++ b/libcockatrice_network/libcockatrice/network/server/remote/server_database_interface.h @@ -172,9 +172,6 @@ public: { return false; } - virtual void setForcePasswordChange(const QString & /* user */, bool /* force */) - { - } }; #endif diff --git a/libcockatrice_network/libcockatrice/network/server/remote/server_protocolhandler.cpp b/libcockatrice_network/libcockatrice/network/server/remote/server_protocolhandler.cpp index 899df6529..561115084 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/server_protocolhandler.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/server_protocolhandler.cpp @@ -564,8 +564,6 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd return Response::RespClientIdRequired; case UserIsInactive: return Response::RespAccountNotActivated; - case PasswordChangeRequired: - return Response::RespPasswordChangeRequired; default: authState = res; usingRealPassword = needsHash; @@ -616,7 +614,6 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd joinPersistentGames(rc); databaseInterface->removeForgotPassword(userName); - onLogin(rc); rc.setResponseExtension(re); return Response::RespOk; } diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt b/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt index 3a193ae3c..20a4cb08d 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt @@ -35,20 +35,10 @@ set(PROTO_FILES command_ready_start.proto command_replay_delete_match.proto command_replay_download.proto - command_replay_download_by_game_id.proto command_replay_get_code.proto command_replay_list.proto command_replay_modify_match.proto command_replay_submit_code.proto - command_report.proto - command_report_add_comment.proto - command_report_assign.proto - command_report_details.proto - command_report_list.proto - command_report_my_list.proto - command_report_resolve.proto - command_report_stats.proto - command_report_user_info.proto command_reveal_cards.proto command_reverse_turn.proto command_roll_die.proto @@ -56,7 +46,6 @@ set(PROTO_FILES command_set_card_attr.proto command_set_card_counter.proto command_set_counter.proto - command_set_playmat.proto command_set_sideboard_lock.proto command_set_sideboard_plan.proto command_shuffle.proto @@ -148,19 +137,8 @@ set(PROTO_FILES response_password_salt.proto response_register.proto response_replay_download.proto - response_replay_download_by_game_id.proto response_replay_get_code.proto response_replay_list.proto - response_report_details.proto - response_report_list.proto - response_report_my_list.proto - response_report_stats.proto - response_report_user_info.proto - response_moderator_last_logins.proto - response_remove_user_avatar.proto - response_reset_user_password.proto - response_user_alts.proto - response_user_sessions.proto response_viewlog_history.proto response_warn_history.proto response_warn_list.proto @@ -176,17 +154,13 @@ set(PROTO_FILES serverinfo_deckstorage.proto serverinfo_game.proto serverinfo_gametype.proto - serverinfo_moderator_login.proto serverinfo_player.proto serverinfo_playerping.proto serverinfo_playerproperties.proto serverinfo_replay.proto serverinfo_replay_match.proto - serverinfo_report.proto serverinfo_room.proto serverinfo_user.proto - serverinfo_user_alt.proto - serverinfo_user_session.proto serverinfo_warning.proto serverinfo_zone.proto session_commands.proto diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/admin_commands.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/admin_commands.proto index f8b34b3f8..8faaec2d2 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/admin_commands.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/admin_commands.proto @@ -5,7 +5,6 @@ message AdminCommand { SHUTDOWN_SERVER = 1001; RELOAD_CONFIG = 1002; ADJUST_MOD = 1003; - RESET_USER_PASSWORD = 1016; } extensions 100 to max; } @@ -38,10 +37,3 @@ message Command_AdjustMod { optional bool should_be_mod = 2; optional bool should_be_judge = 3; } - -message Command_ResetUserPassword { - extend AdminCommand { - optional Command_ResetUserPassword ext = 1016; - } - optional string user_name = 1; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_replay_download_by_game_id.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_replay_download_by_game_id.proto deleted file mode 100644 index 8fa61a517..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/command_replay_download_by_game_id.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto2"; -import "moderator_commands.proto"; - -message Command_ReplayDownloadByGameId { - extend ModeratorCommand { - optional Command_ReplayDownloadByGameId ext = 1203; - } - required sint32 game_id = 1; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_report.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_report.proto deleted file mode 100644 index 3a8c1548e..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/command_report.proto +++ /dev/null @@ -1,13 +0,0 @@ -syntax = "proto2"; -import "session_commands.proto"; - -message Command_Report { - extend SessionCommand { - optional Command_Report ext = 1200; - } - optional string reported_user = 1; - optional int32 game_id = 2; - optional string category = 3; - optional string description = 4; - optional string chat_log = 5; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_add_comment.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_add_comment.proto deleted file mode 100644 index 1fc5696e5..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_add_comment.proto +++ /dev/null @@ -1,10 +0,0 @@ -syntax = "proto2"; -import "session_commands.proto"; - -message Command_ReportAddComment { - extend SessionCommand { - optional Command_ReportAddComment ext = 1205; - } - required int32 report_id = 1; - required string comment = 2; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_assign.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_assign.proto deleted file mode 100644 index 2b72205f8..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_assign.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto2"; -import "moderator_commands.proto"; - -message Command_ReportAssign { - extend ModeratorCommand { - optional Command_ReportAssign ext = 1201; - } - required int32 report_id = 1; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_details.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_details.proto deleted file mode 100644 index e3ae8cfb4..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_details.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto2"; -import "session_commands.proto"; - -message Command_ReportDetails { - extend SessionCommand { - optional Command_ReportDetails ext = 1206; - } - required int32 report_id = 1; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_list.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_list.proto deleted file mode 100644 index 6993de1a7..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_list.proto +++ /dev/null @@ -1,11 +0,0 @@ -syntax = "proto2"; -import "moderator_commands.proto"; - -message Command_ReportList { - extend ModeratorCommand { - optional Command_ReportList ext = 1200; - } - optional bool unresolved_only = 1; - optional uint32 offset = 2 [default = 0]; - optional uint32 limit = 3 [default = 100]; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_my_list.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_my_list.proto deleted file mode 100644 index 7ee18a65d..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_my_list.proto +++ /dev/null @@ -1,8 +0,0 @@ -syntax = "proto2"; -import "session_commands.proto"; - -message Command_ReportMyList { - extend SessionCommand { - optional Command_ReportMyList ext = 1204; - } -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_resolve.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_resolve.proto deleted file mode 100644 index 312ca0f9d..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_resolve.proto +++ /dev/null @@ -1,11 +0,0 @@ -syntax = "proto2"; -import "moderator_commands.proto"; - -message Command_ReportResolve { - extend ModeratorCommand { - optional Command_ReportResolve ext = 1202; - } - required int32 report_id = 1; - optional string resolution_note = 2; - optional bool dismissed = 3; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_stats.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_stats.proto deleted file mode 100644 index b3c6eb1a9..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_stats.proto +++ /dev/null @@ -1,8 +0,0 @@ -syntax = "proto2"; -import "moderator_commands.proto"; - -message Command_ReportStats { - extend ModeratorCommand { - optional Command_ReportStats ext = 1205; - } -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_user_info.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_user_info.proto deleted file mode 100644 index 8f83cb5a3..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/command_report_user_info.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto2"; -import "moderator_commands.proto"; - -message Command_ReportUserInfo { - extend ModeratorCommand { - optional Command_ReportUserInfo ext = 1204; - } - optional string user_name = 1; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_set_playmat.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_set_playmat.proto deleted file mode 100644 index ad9a29a44..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/command_set_playmat.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto2"; -import "game_commands.proto"; -import "serverinfo_playerproperties.proto"; -message Command_SetPlaymat { - extend GameCommand { - optional Command_SetPlaymat ext = 1035; - } - optional ServerInfo_PlayerProperties.PlaymatParams playmat_params = 1; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/event_notify_user.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/event_notify_user.proto index b722dfc71..3a90d278b 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/event_notify_user.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/event_notify_user.proto @@ -9,8 +9,6 @@ message Event_NotifyUser { WARNING = 2; IDLEWARNING = 3; CUSTOM = 4; - REPORT_RESOLVED = 5; - REPORT_COMMENT = 6; } extend SessionEvent { diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/game_commands.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/game_commands.proto index 2e5b88978..8ecf7ec9c 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/game_commands.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/game_commands.proto @@ -175,11 +175,6 @@ message GameCommand { /// Server: Server_Player::cmdReverseTurn /// Client: reflected via subsequent turn events REVERSE_TURN = 1034; - - /// Set the player's playmat independently of the deck. - /// Server: Server_Player::cmdSetPlaymat - /// Client: reflected via player properties changed event - SET_PLAYMAT = 1035; } extensions 100 to max; diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/moderator_commands.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/moderator_commands.proto index 685408830..ca46e4dd7 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/moderator_commands.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/moderator_commands.proto @@ -14,17 +14,6 @@ message ModeratorCommand { ADD_CARD_ART_RULE = 1010; REMOVE_CARD_ART_RULE = 1011; LIST_CARD_ART_RULES = 1012; - GET_USER_SESSIONS = 1013; - GET_USER_ALTS = 1014; - GET_MODERATOR_LAST_LOGINS = 1015; - RESET_USER_PASSWORD = 1016; - REMOVE_USER_AVATAR = 1017; - REPORT_LIST = 1200; - REPORT_ASSIGN = 1201; - REPORT_RESOLVE = 1202; - REPLAY_DOWNLOAD_BY_GAME_ID = 1203; - REPORT_USER_INFO = 1204; - REPORT_STATS = 1205; } extensions 100 to max; } @@ -146,31 +135,3 @@ message Command_ListCardArtRules { optional Command_ListCardArtRules ext = 1012; } } - -message Command_GetUserSessions { - extend ModeratorCommand { - optional Command_GetUserSessions ext = 1013; - } - optional string user_name = 1; - optional uint32 limit = 2 [default = 110]; -} - -message Command_GetUserAlts { - extend ModeratorCommand { - optional Command_GetUserAlts ext = 1014; - } - optional string user_name = 1; -} - -message Command_GetModeratorLastLogins { - extend ModeratorCommand { - optional Command_GetModeratorLastLogins ext = 1015; - } -} - -message Command_RemoveUserAvatar { - extend ModeratorCommand { - optional Command_RemoveUserAvatar ext = 1017; - } - optional string user_name = 1; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response.proto index 14ba737b5..e1f415ce6 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/response.proto @@ -53,7 +53,6 @@ message Response { RespClientUpdateRequired = 35; // Client is missing features that the server is requiring RespServerFull = 36; // Server user limit reached RespEmailBlackListed = 37; // Server has blocked the email address provided for registration for some reason - RespPasswordChangeRequired = 38; // Server requires the user to change their password before proceeding } // Type of response, used to route handling on the client diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response_moderator_last_logins.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response_moderator_last_logins.proto deleted file mode 100644 index 6288880c4..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response_moderator_last_logins.proto +++ /dev/null @@ -1,10 +0,0 @@ -syntax = "proto2"; -import "response.proto"; -import "serverinfo_moderator_login.proto"; - -message Response_ModeratorLastLogins { - extend Response { - optional Response_ModeratorLastLogins ext = 1217; - } - repeated ServerInfo_ModeratorLogin logins = 1; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response_remove_user_avatar.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response_remove_user_avatar.proto deleted file mode 100644 index e5697d4e0..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response_remove_user_avatar.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto2"; -import "response.proto"; - -message Response_RemoveUserAvatar { - extend Response { - optional Response_RemoveUserAvatar ext = 1219; - } - optional string user_name = 1; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response_replay_download_by_game_id.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response_replay_download_by_game_id.proto deleted file mode 100644 index 77a5feb81..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response_replay_download_by_game_id.proto +++ /dev/null @@ -1,10 +0,0 @@ -syntax = "proto2"; -import "response.proto"; - -message Response_ReplayDownloadByGameId { - extend Response { - optional Response_ReplayDownloadByGameId ext = 1203; - } - optional bytes replay_data = 1; - optional sint32 replay_id = 2; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response_report_details.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response_report_details.proto deleted file mode 100644 index 463beaefc..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response_report_details.proto +++ /dev/null @@ -1,10 +0,0 @@ -syntax = "proto2"; -import "response.proto"; -import "serverinfo_report.proto"; - -message Response_ReportDetails { - extend Response { - optional Response_ReportDetails ext = 1214; - } - optional ServerInfo_Report report = 1; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response_report_list.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response_report_list.proto deleted file mode 100644 index 73d9fbef8..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response_report_list.proto +++ /dev/null @@ -1,11 +0,0 @@ -syntax = "proto2"; -import "response.proto"; -import "serverinfo_report.proto"; - -message Response_ReportList { - extend Response { - optional Response_ReportList ext = 1210; - } - repeated ServerInfo_Report reports = 1; - optional uint32 total_count = 2; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response_report_my_list.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response_report_my_list.proto deleted file mode 100644 index fdd02a3f1..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response_report_my_list.proto +++ /dev/null @@ -1,10 +0,0 @@ -syntax = "proto2"; -import "response.proto"; -import "serverinfo_report.proto"; - -message Response_ReportMyList { - extend Response { - optional Response_ReportMyList ext = 1213; - } - repeated ServerInfo_Report reports = 1; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response_report_stats.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response_report_stats.proto deleted file mode 100644 index 8bd2cf766..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response_report_stats.proto +++ /dev/null @@ -1,36 +0,0 @@ -syntax = "proto2"; -import "response.proto"; - -message Response_ReportStats { - extend Response { - optional Response_ReportStats ext = 1212; - } - - optional int32 total_reports = 1; - optional int32 total_pending = 2; - optional int32 total_assigned = 3; - optional int32 total_resolved = 4; - - optional int32 reports_last_24h = 5; - optional int32 reports_last_7d = 6; - optional int32 reports_last_30d = 7; - - optional double avg_resolution_hours = 8; - - optional int32 reports_this_week = 9; - optional int32 reports_last_week = 10; - - repeated ReportCategoryCount category_counts = 11; - repeated ReportTopUser top_reported_users = 12; - repeated ReportTopUser top_reporters = 13; -} - -message ReportCategoryCount { - optional string category = 1; - optional int32 count = 2; -} - -message ReportTopUser { - optional string user_name = 1; - optional int32 count = 2; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response_report_user_info.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response_report_user_info.proto deleted file mode 100644 index 5f9e8ec38..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response_report_user_info.proto +++ /dev/null @@ -1,21 +0,0 @@ -syntax = "proto2"; -import "response.proto"; -import "serverinfo_report.proto"; - -message Response_ReportUserInfo { - extend Response { - optional Response_ReportUserInfo ext = 1211; - } - - optional string user_name = 1; - optional int32 total_reports = 2; - optional int32 total_bans = 3; - optional int32 total_warns = 4; - optional int64 registration_time = 5; - optional bool is_admin = 6; - optional bool is_active = 7; - optional string admin_notes = 8; - repeated ServerInfo_Report recent_reports = 9; - // Last known login of the user, epoch seconds; 0 = unknown. - optional int64 last_login = 10; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response_reset_user_password.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response_reset_user_password.proto deleted file mode 100644 index aa379e573..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response_reset_user_password.proto +++ /dev/null @@ -1,12 +0,0 @@ -syntax = "proto2"; -import "response.proto"; - -message Response_ResetUserPassword { - extend Response { - optional Response_ResetUserPassword ext = 1218; - } - optional string user_name = 1; - // The generated temporary password, shown to the moderator who - // requested the reset. The affected user must change it on first login. - optional string temporary_password = 2; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response_user_alts.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response_user_alts.proto deleted file mode 100644 index 6bc48d6cb..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response_user_alts.proto +++ /dev/null @@ -1,10 +0,0 @@ -syntax = "proto2"; -import "response.proto"; -import "serverinfo_user_alt.proto"; - -message Response_UserAlts { - extend Response { - optional Response_UserAlts ext = 1216; - } - repeated ServerInfo_UserAlt alts = 1; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response_user_sessions.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response_user_sessions.proto deleted file mode 100644 index 7174fd3f7..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response_user_sessions.proto +++ /dev/null @@ -1,10 +0,0 @@ -syntax = "proto2"; -import "response.proto"; -import "serverinfo_user_session.proto"; - -message Response_UserSessions { - extend Response { - optional Response_UserSessions ext = 1215; - } - repeated ServerInfo_UserSession sessions = 1; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response_warn_list.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response_warn_list.proto index cddd08a51..d48352529 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response_warn_list.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/response_warn_list.proto @@ -8,7 +8,4 @@ message Response_WarnList { repeated string warning = 1; optional string user_name = 2; optional string user_clientid = 3; - // Recommended starting intervention level per warning category, - // aligned by index with `warning`. Absent or shorter lists default to 1. - repeated uint32 warning_il = 4; } diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_moderator_login.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_moderator_login.proto deleted file mode 100644 index 21db2d3fa..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_moderator_login.proto +++ /dev/null @@ -1,11 +0,0 @@ -syntax = "proto2"; - -/** - * The last login date of a staff member (moderator/judge/admin). - * Used by the moderation "Moderator Last Logins" tool. - */ -message ServerInfo_ModeratorLogin { - optional string user_name = 1; // staff account name - optional uint64 last_login = 2; // last known login, epoch seconds; 0 = unknown - optional uint32 user_level = 3; // ServerInfo_User::UserLevelFlag mask (moderator/judge/admin) -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_playerproperties.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_playerproperties.proto index ae19e4018..cdd0ee42c 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_playerproperties.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_playerproperties.proto @@ -2,15 +2,6 @@ syntax = "proto2"; import "serverinfo_user.proto"; message ServerInfo_PlayerProperties { - message PlaymatParams { - optional string card_name = 1; - optional string card_provider_id = 2; - optional double margin_pct_l = 3 [default = 0.07]; - optional double margin_pct_r = 4 [default = 0.07]; - optional double vertical_offset = 5 [default = 0.33]; - optional double zoom = 6 [default = 1.0]; - } - optional sint32 player_id = 1; optional ServerInfo_User user_info = 2; optional bool spectator = 3; @@ -20,5 +11,4 @@ message ServerInfo_PlayerProperties { optional sint32 ping_seconds = 7; optional bool sideboard_locked = 8; optional bool judge = 9; - optional PlaymatParams playmat_params = 10; } diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_report.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_report.proto deleted file mode 100644 index a145fd855..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_report.proto +++ /dev/null @@ -1,36 +0,0 @@ -syntax = "proto2"; - -message ServerInfo_ReportComment { - optional string author_name = 1; - optional string comment_text = 2; - optional int64 comment_time = 3; - optional bool is_moderator = 4; -} - -message ServerInfo_Report { - optional int32 report_id = 1; - - optional string reporter_name = 2; - optional string reported_user_name = 3; - - optional int32 game_id = 4; - optional int32 replay_id = 5; - optional int32 room_id = 6; - - optional string category = 7; - optional string status = 8; - - optional string description = 9; - - optional int64 report_time = 10; - - optional string assigned_mod_name = 11; - - optional int64 resolution_time = 12; - - repeated ServerInfo_ReportComment comments = 13; - - optional string chat_log = 14; - - optional string resolution_note = 15; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_user_alt.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_user_alt.proto deleted file mode 100644 index 191dd5063..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_user_alt.proto +++ /dev/null @@ -1,16 +0,0 @@ -syntax = "proto2"; - -/** - * An account that shares an IP address, client id or eMail address with - * the account being investigated. Used by the moderation "Get User Alts" tool. - */ -message ServerInfo_UserAlt { - optional string user_name = 1; // account name - optional string email = 2; // registration eMail - optional string clientid = 3; // client id - optional uint64 registration_time = 4; // account registration, epoch seconds - optional uint64 last_login = 5; // last known login, epoch seconds; 0 = unknown - optional uint32 warn_count = 6; // number of warnings on record - optional uint32 ban_count = 7; // number of bans on record - optional bool is_active = 8; // account is not deactivated/banned -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_user_session.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_user_session.proto deleted file mode 100644 index 7ce240050..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_user_session.proto +++ /dev/null @@ -1,14 +0,0 @@ -syntax = "proto2"; - -/** - * A single login session of a user on the server, as stored in the - * sessions table. Used by the moderation "Get User Sessions" tool. - */ -message ServerInfo_UserSession { - optional string user_name = 1; // account that was logged in - optional string ip_address = 2; // IP address used for the session - optional string clientid = 3; // client id used for the session - optional uint64 start_time = 4; // session start, epoch seconds - optional uint64 end_time = 5; // session end, epoch seconds; 0 = still active - optional string connection_type = 6; // "tcp" or "websocket" -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/session_commands.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/session_commands.proto index fee8c36a8..9d207c711 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/session_commands.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/session_commands.proto @@ -34,11 +34,6 @@ message SessionCommand { REPLAY_DELETE_MATCH = 1103; REPLAY_GET_CODE = 1104; REPLAY_SUBMIT_CODE = 1105; - REPORT = 1200; - // 1201-1203 reserved: removed during squash - REPORT_MY_LIST = 1204; - REPORT_ADD_COMMENT = 1205; - REPORT_DETAILS = 1206; } extensions 100 to max; } diff --git a/libcockatrice_settings/libcockatrice/settings/interface_settings.cpp b/libcockatrice_settings/libcockatrice/settings/interface_settings.cpp index b92a5fdec..2f0718533 100644 --- a/libcockatrice_settings/libcockatrice/settings/interface_settings.cpp +++ b/libcockatrice_settings/libcockatrice/settings/interface_settings.cpp @@ -1,35 +1,5 @@ #include "interface_settings.h" -namespace -{ -const QChar PLAYMAT_FIELD_SEP = QChar(0x1F); ///< Separator between PlaymatInfo fields. - -QString encodePlaymatInfo(const PlaymatInfo &res) -{ - return res.card.name + PLAYMAT_FIELD_SEP + res.card.providerId + PLAYMAT_FIELD_SEP + - QString::number(res.params.marginPctL, 'f', 4) + PLAYMAT_FIELD_SEP + - QString::number(res.params.marginPctR, 'f', 4) + PLAYMAT_FIELD_SEP + - QString::number(res.params.verticalOffset, 'f', 4) + PLAYMAT_FIELD_SEP + - QString::number(res.params.zoom, 'f', 4); -} - -PlaymatInfo decodePlaymatInfo(const QString &encoded) -{ - const QStringList fields = encoded.split(PLAYMAT_FIELD_SEP); - if (fields.size() != 6) { - return {}; - } - PlaymatInfo res; - res.card.name = fields.at(0); - res.card.providerId = fields.at(1); - res.params.marginPctL = fields.at(2).toDouble(); - res.params.marginPctR = fields.at(3).toDouble(); - res.params.verticalOffset = fields.at(4).toDouble(); - res.params.zoom = fields.at(5).toDouble(); - return res; -} -} // namespace - InterfaceSettings::InterfaceSettings(const QString &settingPath, QObject *parent) : SettingsManager(settingPath + "interface.ini", "interface", QString(), parent) { @@ -190,35 +160,6 @@ bool InterfaceSettings::getShowGameSelectorFilterToolbar() const return getValue("showGameSelectorFilterToolbar", QString(), QString(), true).toBool(); } -int InterfaceSettings::getPlaymatVisibility() const -{ - return qBound(0, getValue("playmatvisibility", QString(), QString(), 2).toInt(), 2); -} - -QList InterfaceSettings::getPlaymatFallbackList() const -{ - const QStringList entries = getValue("playmatFallbackList", QString(), QString(), QStringList()).toStringList(); - QList result; - result.reserve(entries.size()); - for (const QString &entry : entries) { - const PlaymatInfo res = decodePlaymatInfo(entry); - if (!res.card.isEmpty()) { - result.append(res); - } - } - return result; -} - -int InterfaceSettings::getPlaymatMode() const -{ - return qBound(0, getValue("playmatMode", QString(), QString(), 1).toInt(), 2); -} - -int InterfaceSettings::getPlaymatFallbackBehavior() const -{ - return qBound(0, getValue("playmatFallbackBehavior", QString(), QString(), 0).toInt(), 2); -} - bool InterfaceSettings::getLifeCounterAnimationsEnabled() const { return getValue("lifeCounterAnimationsEnabled", QString(), QString(), true).toBool(); @@ -402,46 +343,6 @@ void InterfaceSettings::setShowGameSelectorFilterToolbar(bool _showGameSelectorF emit showGameSelectorFilterToolbarChanged(_showGameSelectorFilterToolbar); } -void InterfaceSettings::setPlaymatVisibility(int _visibility) -{ - if (getPlaymatVisibility() == _visibility) { - return; - } - setValue(_visibility, "playmatvisibility"); - emit playmatVisibilityChanged(_visibility); -} - -void InterfaceSettings::setPlaymatFallbackList(const QList &_fallbackList) -{ - QStringList entries; - entries.reserve(_fallbackList.size()); - for (const PlaymatInfo &res : _fallbackList) { - entries.append(encodePlaymatInfo(res)); - } - setValue(entries, "playmatFallbackList"); - emit playmatSettingsChanged(); -} - -void InterfaceSettings::setPlaymatMode(int _mode) -{ - const int mode = qBound(0, _mode, 2); - if (getPlaymatMode() == mode) { - return; - } - setValue(mode, "playmatMode"); - emit playmatSettingsChanged(); -} - -void InterfaceSettings::setPlaymatFallbackBehavior(int _behavior) -{ - const int behavior = qBound(0, _behavior, 2); - if (getPlaymatFallbackBehavior() == behavior) { - return; - } - setValue(behavior, "playmatFallbackBehavior"); - emit playmatSettingsChanged(); -} - void InterfaceSettings::setLifeCounterAnimationsEnabled(bool _lifeCounterAnimationsEnabled) { setValue(_lifeCounterAnimationsEnabled, "lifeCounterAnimationsEnabled"); diff --git a/libcockatrice_settings/libcockatrice/settings/interface_settings.h b/libcockatrice_settings/libcockatrice/settings/interface_settings.h index 6b226f307..981d28679 100644 --- a/libcockatrice_settings/libcockatrice/settings/interface_settings.h +++ b/libcockatrice_settings/libcockatrice/settings/interface_settings.h @@ -42,10 +42,6 @@ public: [[nodiscard]] bool getShowStatusBar() const override; [[nodiscard]] bool getShowShortcuts() const override; [[nodiscard]] bool getShowGameSelectorFilterToolbar() const override; - [[nodiscard]] int getPlaymatVisibility() const override; - [[nodiscard]] QList getPlaymatFallbackList() const override; - [[nodiscard]] int getPlaymatMode() const override; - [[nodiscard]] int getPlaymatFallbackBehavior() const override; [[nodiscard]] bool getLifeCounterAnimationsEnabled() const override; [[nodiscard]] bool getBattlefieldFlashEnabled() const override; [[nodiscard]] QStringList getUserListExpandedSections() const override; @@ -81,10 +77,6 @@ public: void setShowStatusBar(bool _showStatusBar); void setShowShortcuts(bool _showShortcuts); void setShowGameSelectorFilterToolbar(bool _showGameSelectorFilterToolbar); - void setPlaymatVisibility(int _visibility); - void setPlaymatFallbackList(const QList &_fallbackList); - void setPlaymatMode(int _mode); - void setPlaymatFallbackBehavior(int _behavior); void setLifeCounterAnimationsEnabled(bool _lifeCounterAnimationsEnabled); void setBattlefieldFlashEnabled(bool _battlefieldFlashEnabled); void setUserListExpandedSections(const QStringList §ions); @@ -99,8 +91,6 @@ signals: void tallyTypeChanged(int type); void showStatusBarChanged(bool state); void showGameSelectorFilterToolbarChanged(bool state); - void playmatVisibilityChanged(int visibility); - void playmatSettingsChanged(); void lifeCounterAnimationsEnabledChanged(bool state); void battlefieldFlashEnabledChanged(bool state); diff --git a/libcockatrice_settings/libcockatrice/settings/tabs_settings.cpp b/libcockatrice_settings/libcockatrice/settings/tabs_settings.cpp index 85a1424a6..78e48ed5b 100644 --- a/libcockatrice_settings/libcockatrice/settings/tabs_settings.cpp +++ b/libcockatrice_settings/libcockatrice/settings/tabs_settings.cpp @@ -96,16 +96,6 @@ void TabsSettings::setStartupRoomName(const QString &roomName) emit startupRoomNameChanged(roomName); } -bool TabsSettings::getTabReportOpen() const -{ - return getValue("report", QString(), QString(), false).toBool(); -} - -bool TabsSettings::getTabModerationOpen() const -{ - return getValue("moderation", QString(), QString(), false).toBool(); -} - void TabsSettings::setTabVisualDeckStorageOpen(bool value) { setValue(value, "visualDeckStorage"); @@ -140,13 +130,3 @@ void TabsSettings::setTabLogOpen(bool value) { setValue(value, "log"); } - -void TabsSettings::setTabReportOpen(bool value) -{ - setValue(value, "report"); -} - -void TabsSettings::setTabModerationOpen(bool value) -{ - setValue(value, "moderation"); -} diff --git a/libcockatrice_settings/libcockatrice/settings/tabs_settings.h b/libcockatrice_settings/libcockatrice/settings/tabs_settings.h index 365d91af7..0d5da80af 100644 --- a/libcockatrice_settings/libcockatrice/settings/tabs_settings.h +++ b/libcockatrice_settings/libcockatrice/settings/tabs_settings.h @@ -41,8 +41,6 @@ public: [[nodiscard]] bool getTabReplaysOpen() const override; [[nodiscard]] bool getTabAdminOpen() const override; [[nodiscard]] bool getTabLogOpen() const override; - [[nodiscard]] bool getTabReportOpen() const override; - [[nodiscard]] bool getTabModerationOpen() const override; void setStartupTabIndex(int value); void setStartupServerHost(const QString &host); @@ -55,8 +53,6 @@ public: void setTabReplaysOpen(bool value); void setTabAdminOpen(bool value); void setTabLogOpen(bool value); - void setTabReportOpen(bool value); - void setTabModerationOpen(bool value); signals: void startupTabIndexChanged(int index); diff --git a/libcockatrice_utility/CMakeLists.txt b/libcockatrice_utility/CMakeLists.txt index c6411ea76..79f5a11e4 100644 --- a/libcockatrice_utility/CMakeLists.txt +++ b/libcockatrice_utility/CMakeLists.txt @@ -5,19 +5,16 @@ set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) -set(UTILITY_SOURCES - libcockatrice/utility/expression.cpp libcockatrice/utility/levenshtein.cpp libcockatrice/utility/passwordhasher.cpp - libcockatrice/utility/server_rate_limiter.cpp libcockatrice/utility/warning_categories.cpp +set(UTILITY_SOURCES libcockatrice/utility/expression.cpp libcockatrice/utility/levenshtein.cpp + libcockatrice/utility/passwordhasher.cpp libcockatrice/utility/server_rate_limiter.cpp ) set(UTILITY_HEADERS - libcockatrice/utility/card_ref.h libcockatrice/utility/color.h libcockatrice/utility/expression.h libcockatrice/utility/levenshtein.h libcockatrice/utility/macros.h libcockatrice/utility/passwordhasher.h - libcockatrice/utility/playmat_params.h libcockatrice/utility/string_limits.h libcockatrice/utility/dice_limits.h libcockatrice/utility/counter_limits.h @@ -25,7 +22,6 @@ set(UTILITY_HEADERS libcockatrice/utility/zone_names.h libcockatrice/utility/days_years_between.h libcockatrice/utility/server_rate_limiter.h - libcockatrice/utility/warning_categories.h ) add_library(libcockatrice_utility STATIC ${UTILITY_SOURCES} ${UTILITY_HEADERS}) diff --git a/libcockatrice_utility/libcockatrice/utility/playmat_params.h b/libcockatrice_utility/libcockatrice/utility/playmat_params.h deleted file mode 100644 index c64a6f2ae..000000000 --- a/libcockatrice_utility/libcockatrice/utility/playmat_params.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef COCKATRICE_PLAYMAT_PARAMS_H -#define COCKATRICE_PLAYMAT_PARAMS_H - -#include "card_ref.h" - -#include - -/** - * @struct PlaymatParams - * @ingroup Decks - * @brief Positioning parameters for a playmat card image. - * - * Controls how the cropped card art is positioned within the - * combined table+stack play area. The coordinate system is - * relative to the cropped art source image. - */ -struct PlaymatParams -{ - double marginPctL = 0.07; ///< Left margin as fraction of card width (0.0–0.95). - double marginPctR = 0.07; ///< Right margin as fraction of card width (0.0–0.95). - double verticalOffset = 0.33; ///< Vertical position within card (0.0=top, 1.0=bottom). - double zoom = 1.0; ///< Scale factor (0.1–4.0). - - bool operator==(const PlaymatParams &other) const - { - return qFuzzyCompare(marginPctL, other.marginPctL) && qFuzzyCompare(marginPctR, other.marginPctR) && - qFuzzyCompare(verticalOffset, other.verticalOffset) && qFuzzyCompare(zoom, other.zoom); - } - - bool operator!=(const PlaymatParams &other) const - { - return !(*this == other); - } -}; - -/** - * @struct PlaymatInfo - * @ingroup Decks - * @brief A resolved playmat (card + positioning parameters). - */ -struct PlaymatInfo -{ - CardRef card; ///< The card whose art is used as playmat. - PlaymatParams params; ///< Positioning parameters for the playmat card image. - - bool operator==(const PlaymatInfo &other) const - { - return card == other.card && params == other.params; - } -}; - -#endif // COCKATRICE_PLAYMAT_PARAMS_H diff --git a/libcockatrice_utility/libcockatrice/utility/string_limits.h b/libcockatrice_utility/libcockatrice/utility/string_limits.h index 5079ec46d..cca804bf0 100644 --- a/libcockatrice_utility/libcockatrice/utility/string_limits.h +++ b/libcockatrice_utility/libcockatrice/utility/string_limits.h @@ -22,19 +22,6 @@ inline QString textFromStdString(const std::string &_string) { return QString::fromUtf8(_string.data(), std::min(int(_string.size()), MAX_TEXT_LENGTH)); } -/** @brief Returns a QString from a std::string, truncated to at most MAX_TEXT_LENGTH bytes, keeping the tail. */ -inline QString textTailFromStdString(const std::string &_string) -{ - if (int(_string.size()) <= MAX_TEXT_LENGTH) { - return QString::fromUtf8(_string.data(), int(_string.size())); - } - - int start = int(_string.size()) - MAX_TEXT_LENGTH; - while (start < int(_string.size()) && (static_cast(_string[start]) & 0xC0) == 0x80) { - ++start; - } - return QString::fromUtf8(_string.data() + start, int(_string.size()) - start); -} /** @brief Returns a QString from a std::string, truncated to at most MAX_FILE_LENGTH bytes. */ inline QString fileFromStdString(const std::string &_string) { diff --git a/libcockatrice_utility/libcockatrice/utility/warning_categories.cpp b/libcockatrice_utility/libcockatrice/utility/warning_categories.cpp deleted file mode 100644 index b5ca563ff..000000000 --- a/libcockatrice_utility/libcockatrice/utility/warning_categories.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "warning_categories.h" - -QList parseWarningCategories(const QString &value) -{ - QList categories; - const QStringList entries = value.split(',', Qt::SkipEmptyParts); - for (const QString &entry : entries) { - const QStringList parts = entry.split('|'); - WarningCategory category; - category.name = parts.first().trimmed(); - if (category.name.isEmpty()) { - continue; - } - if (parts.size() > 1) { - bool ok = false; - const int il = parts.at(1).trimmed().toInt(&ok); - if (ok && il > 0) { - category.startingIl = il; - } - } - categories.append(category); - } - return categories; -} diff --git a/libcockatrice_utility/libcockatrice/utility/warning_categories.h b/libcockatrice_utility/libcockatrice/utility/warning_categories.h deleted file mode 100644 index ce5308cf4..000000000 --- a/libcockatrice_utility/libcockatrice/utility/warning_categories.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef WARNING_CATEGORIES_H -#define WARNING_CATEGORIES_H - -#include -#include - -/** - * A warning category the server offers to moderators, optionally carrying a - * recommended starting intervention level (see the moderator guide). - */ -struct WarningCategory -{ - QString name; - int startingIl = 1; -}; - -/** - * Parses the `server/officialwarnings` setting value into warning categories. - * - * Entries are separated by commas. Each entry is a category name, optionally - * followed by "|" and the recommended starting intervention level: - * "Abusive Language|1,Cheating|2,Spamming" - * Entries without an explicit level default to intervention level 1. - * Empty entries are skipped. - */ -QList parseWarningCategories(const QString &value); - -#endif // WARNING_CATEGORIES_H diff --git a/servatrice/migrations/servatrice_0035_to_0036.sql b/servatrice/migrations/servatrice_0035_to_0036.sql deleted file mode 100644 index 9727eeba5..000000000 --- a/servatrice/migrations/servatrice_0035_to_0036.sql +++ /dev/null @@ -1,53 +0,0 @@ --- Servatrice db migration from version 35 to version 36 - -CREATE TABLE IF NOT EXISTS `cockatrice_reports` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `reporter_id` int(7) unsigned NULL, - `reporter_name` varchar(35) NOT NULL, - `reported_user_id` int(7) unsigned NULL, - `reported_user_name` varchar(35) NOT NULL, - `game_id` int(7) unsigned NULL, - `room_id` int(7) unsigned NULL, - `category` varchar(255) NOT NULL, - `description` text NOT NULL, - `chat_log` mediumtext NULL, - `created_at` datetime NOT NULL, - `resolution_time` datetime NULL, - `status` enum('open','assigned','resolved','dismissed') NOT NULL DEFAULT 'open', - `assigned_to` int(7) unsigned NULL, - `resolved_by` int(7) unsigned NULL, - `resolution_note` text, - `notified` tinyint(1) NOT NULL DEFAULT 0, - PRIMARY KEY (`id`), - INDEX `idx_status` (`status`), - INDEX `idx_created` (`created_at`), - INDEX `idx_reporter_id_created` (`reporter_id`, `created_at`), - INDEX `idx_reported_user_name` (`reported_user_name`), - INDEX `idx_status_created` (`status`, `created_at`), - FOREIGN KEY (`reporter_id`) REFERENCES `cockatrice_users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE, - FOREIGN KEY (`reported_user_id`) REFERENCES `cockatrice_users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE, - FOREIGN KEY (`assigned_to`) REFERENCES `cockatrice_users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE, - FOREIGN KEY (`resolved_by`) REFERENCES `cockatrice_users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `cockatrice_report_comments` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `report_id` int(11) unsigned NOT NULL, - `author_name` varchar(35) NOT NULL, - `author_id` int(7) unsigned NULL, - `comment_text` text NOT NULL, - `created_at` datetime NOT NULL, - `is_moderator` tinyint(1) NOT NULL DEFAULT 0, - `notified` tinyint(1) NOT NULL DEFAULT 0, - PRIMARY KEY (`id`), - INDEX `idx_report_id` (`report_id`), - INDEX `idx_notified` (`notified`), - FOREIGN KEY (`report_id`) REFERENCES `cockatrice_reports`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, - FOREIGN KEY (`author_id`) REFERENCES `cockatrice_users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -ALTER TABLE `cockatrice_users` - ADD COLUMN `force_password_change` tinyint(1) NOT NULL DEFAULT 0 - AFTER `passwordLastChangedDate`; - -UPDATE cockatrice_schema_version SET version=36 WHERE version=35; diff --git a/servatrice/servatrice.ini.example b/servatrice/servatrice.ini.example index c1940c22f..fac743c39 100644 --- a/servatrice/servatrice.ini.example +++ b/servatrice/servatrice.ini.example @@ -79,10 +79,8 @@ requiredfeatures="" ; You can define custom warnings that users are sent when the moderation staff uses the right client warn user ; menu option. This list is comma seperated that each item will appear in the drop down list for staff members -; to choose from. Each entry may optionally carry a recommended starting intervention level (see the moderator -; guide) by appending "|" and the level number. Entries without an explicit level default to intervention -; level 1. Example: "Flaming,Foul Language" -officialwarnings="Abusive Language|1,Calling Out User|1,Causing Drama|1,Cheating|2,Disrespecting Staff|1,Disrupting a Draft|1,Inappropriate Avatar|3,Inappropriate Game Name|1,Kicking Without Valid Reason|1,Spamming|1,Targeted Harassment|2" +; to choose from. Example: "Flaming,Foul Language" +officialwarnings="Flaming,Spamming,Causing Drama,Abusive Language" ; Maximum time in seconds a player can stay connected but idle. Default is 3600 (0 = disabled) ; Clients will be notified at the 90% time period of pending disconnection if they do not take action. @@ -375,13 +373,6 @@ command_counting_interval=10 ; Maximum number of game commands in an interval before new commands gets dropped; default is 20 max_command_count_per_interval=20 -[reporting] -; Maximum number of user reports a single user can file per day; default is 10; set to 0 to disable the limit -max_reports_per_day=10 - -; Maximum number of report comments a single user can post per hour; default is 30; set to 0 to disable the limit -max_comments_per_hour=30 - [logging] ; Admin/Moderators can query the stored logs for information when looking up reports by various players. This ; option can allow or disallow them from doing so. diff --git a/servatrice/servatrice.sql b/servatrice/servatrice.sql index 5dbf69cbc..7f530063c 100644 --- a/servatrice/servatrice.sql +++ b/servatrice/servatrice.sql @@ -20,7 +20,7 @@ CREATE TABLE IF NOT EXISTS `cockatrice_schema_version` ( PRIMARY KEY (`version`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; -INSERT INTO cockatrice_schema_version VALUES(36); +INSERT INTO cockatrice_schema_version VALUES(35); -- users and user data tables CREATE TABLE IF NOT EXISTS `cockatrice_users` ( @@ -41,7 +41,6 @@ CREATE TABLE IF NOT EXISTS `cockatrice_users` ( `privlevelStartDate` datetime NOT NULL, `privlevelEndDate` datetime NOT NULL, `passwordLastChangedDate` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', - `force_password_change` tinyint(1) NOT NULL DEFAULT 0, `leftPawnColorOverride` varchar(255), `rightPawnColorOverride` varchar(255), `card_art_params` TEXT DEFAULT NULL, @@ -234,52 +233,6 @@ CREATE TABLE IF NOT EXISTS `cockatrice_warnings` ( INDEX `idx_user_name` (`user_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; -CREATE TABLE IF NOT EXISTS `cockatrice_reports` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `reporter_id` int(7) unsigned NULL, - `reporter_name` varchar(35) NOT NULL, - `reported_user_id` int(7) unsigned NULL, - `reported_user_name` varchar(35) NOT NULL, - `game_id` int(7) unsigned NULL, - `room_id` int(7) unsigned NULL, - `category` varchar(255) NOT NULL, - `description` text NOT NULL, - `chat_log` mediumtext NULL, - `created_at` datetime NOT NULL, - `resolution_time` datetime NULL, - `status` enum('open','assigned','resolved','dismissed') NOT NULL DEFAULT 'open', - `assigned_to` int(7) unsigned NULL, - `resolved_by` int(7) unsigned NULL, - `resolution_note` text, - `notified` tinyint(1) NOT NULL DEFAULT 0, - PRIMARY KEY (`id`), - INDEX `idx_status` (`status`), - INDEX `idx_created` (`created_at`), - INDEX `idx_reporter_id_created` (`reporter_id`, `created_at`), - INDEX `idx_reported_user_name` (`reported_user_name`), - INDEX `idx_status_created` (`status`, `created_at`), - FOREIGN KEY (`reporter_id`) REFERENCES `cockatrice_users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE, - FOREIGN KEY (`reported_user_id`) REFERENCES `cockatrice_users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE, - FOREIGN KEY (`assigned_to`) REFERENCES `cockatrice_users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE, - FOREIGN KEY (`resolved_by`) REFERENCES `cockatrice_users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `cockatrice_report_comments` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `report_id` int(11) unsigned NOT NULL, - `author_name` varchar(35) NOT NULL, - `author_id` int(7) unsigned NULL, - `comment_text` text NOT NULL, - `created_at` datetime NOT NULL, - `is_moderator` tinyint(1) NOT NULL DEFAULT 0, - `notified` tinyint(1) NOT NULL DEFAULT 0, - PRIMARY KEY (`id`), - INDEX `idx_report_id` (`report_id`), - INDEX `idx_notified` (`notified`), - FOREIGN KEY (`report_id`) REFERENCES `cockatrice_reports`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, - FOREIGN KEY (`author_id`) REFERENCES `cockatrice_users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - CREATE TABLE IF NOT EXISTS `cockatrice_log` ( `log_time` datetime NOT NULL, `sender_id` int(7) unsigned NULL, diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index db8751658..4305f6882 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -514,23 +514,6 @@ QList Servatrice::getServerList() const return result; } -std::shared_ptr Servatrice::getCachedReportStats() const -{ - QMutexLocker locker(&reportStatsMutex); - if (!reportStatsTimestamp.isValid() || - reportStatsTimestamp.secsTo(QDateTime::currentDateTime()) >= reportStatsCacheTtlSeconds) { - return nullptr; - } - return reportStatsCache; -} - -void Servatrice::cacheReportStats(const Response_ReportStats &stats) -{ - QMutexLocker locker(&reportStatsMutex); - reportStatsCache = std::make_shared(stats); - reportStatsTimestamp = QDateTime::currentDateTime(); -} - int Servatrice::getUsersWithAddress(const QHostAddress &address) const { int result = 0; diff --git a/servatrice/src/servatrice.h b/servatrice/src/servatrice.h index 8b0f5ad60..6eb00c165 100644 --- a/servatrice/src/servatrice.h +++ b/servatrice/src/servatrice.h @@ -20,7 +20,6 @@ #ifndef SERVATRICE_H #define SERVATRICE_H -#include #include #include #include @@ -30,8 +29,6 @@ #include #include #include -#include -#include #include #include @@ -176,11 +173,6 @@ private: int nextShutdownMessageMinutes; QTimer *shutdownTimer; - mutable QMutex reportStatsMutex; - QDateTime reportStatsTimestamp; - std::shared_ptr reportStatsCache; - static constexpr int reportStatsCacheTtlSeconds = 60; - mutable QMutex serverListMutex; QList serverList; void updateServerList(); @@ -291,11 +283,6 @@ public: void removeIslInterface(int _serverId); QReadWriteLock islLock; - // The moderation queue statistics are shared between all connected moderators and - // cached briefly to avoid re-running several full-table queries on every refresh. - std::shared_ptr getCachedReportStats() const; - void cacheReportStats(const Response_ReportStats &stats); - QList getServerList() const; }; diff --git a/servatrice/src/servatrice_database_interface.cpp b/servatrice/src/servatrice_database_interface.cpp index 847be61da..d5e1f13ef 100644 --- a/servatrice/src/servatrice_database_interface.cpp +++ b/servatrice/src/servatrice_database_interface.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include inline Q_LOGGING_CATEGORY(DatabaseInterfaceLog, "database_interface"); @@ -340,8 +339,8 @@ AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_Prot return UserIsBanned; } - QSqlQuery *passwordQuery = prepareQuery( - "select password_sha512, active, force_password_change from {prefix}_users where name = :name"); + QSqlQuery *passwordQuery = + prepareQuery("select password_sha512, active from {prefix}_users where name = :name"); passwordQuery->bindValue(":name", user); if (!execSqlQuery(passwordQuery)) { qCWarning(DatabaseInterfaceLog) << "Login denied: SQL error"; @@ -351,7 +350,6 @@ AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_Prot if (passwordQuery->next()) { const QString correctPasswordSha512 = passwordQuery->value(0).toString(); const bool userIsActive = passwordQuery->value(1).toBool(); - const bool forceChange = passwordQuery->value(2).toBool(); if (!userIsActive) { qCWarning(DatabaseInterfaceLog) << "Login denied: user not active"; return UserIsInactive; @@ -363,10 +361,6 @@ AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_Prot hashedPassword = password; } if (correctPasswordSha512 == hashedPassword) { - if (forceChange) { - qCDebug(DatabaseInterfaceLog) << "Login accepted but password change required"; - return PasswordChangeRequired; - } qCDebug(DatabaseInterfaceLog) << "Login accepted: password right"; return PasswordRight; } else { @@ -1090,22 +1084,11 @@ bool Servatrice_DatabaseInterface::changeUserPassword(const QString &user, "passwordLastChangedDate = NOW() where name = :name"); passwordQuery->bindValue(":password", passwordSha512); passwordQuery->bindValue(":name", user); - if (!execSqlQuery(passwordQuery)) { - return false; - } - return passwordQuery->numRowsAffected() > 0; -} - -void Servatrice_DatabaseInterface::setForcePasswordChange(const QString &user, bool force) -{ - if (!checkSql()) { - return; + if (execSqlQuery(passwordQuery)) { + return true; } - QSqlQuery *query = prepareQuery("UPDATE {prefix}_users SET force_password_change = :force WHERE name = :name"); - query->bindValue(":force", force ? 1 : 0); - query->bindValue(":name", user); - execSqlQuery(query); + return false; } bool Servatrice_DatabaseInterface::changeUserPassword(const QString &user, @@ -1331,169 +1314,6 @@ QList Servatrice_DatabaseInterface::getUserWarnHistory(const return results; } -QList Servatrice_DatabaseInterface::getUserSessions(const QString &userName, int limit) -{ - QList results; - - if (!checkSql()) { - return results; - } - - QSqlQuery *query = prepareQuery("SELECT user_name, ip_address, clientid, " - "UNIX_TIMESTAMP(start_time), UNIX_TIMESTAMP(end_time), connection_type " - "FROM {prefix}_sessions WHERE user_name = :user_name " - "ORDER BY start_time DESC LIMIT :limit"); - query->bindValue(":user_name", userName); - query->bindValue(":limit", limit); - - if (!execSqlQuery(query)) { - qCWarning(DatabaseInterfaceLog) << "Failed to collect session history information: SQL Error"; - return results; - } - - while (query->next()) { - ServerInfo_UserSession sessionDetails; - sessionDetails.set_user_name(query->value(0).toString().toStdString()); - sessionDetails.set_ip_address(query->value(1).toString().toStdString()); - sessionDetails.set_clientid(query->value(2).toString().toStdString()); - sessionDetails.set_start_time(query->value(3).toLongLong()); - if (!query->value(4).isNull()) { - sessionDetails.set_end_time(query->value(4).toLongLong()); - } - sessionDetails.set_connection_type(query->value(5).toString().toStdString()); - results << sessionDetails; - } - - return results; -} - -QList Servatrice_DatabaseInterface::getUserAlts(const QString &userName) -{ - QList results; - - if (!checkSql()) { - return results; - } - - // Seed account identifiers used to find related accounts - QSqlQuery *seedQuery = prepareQuery("SELECT email, clientid FROM {prefix}_users WHERE name = :user_name"); - seedQuery->bindValue(":user_name", userName); - if (!execSqlQuery(seedQuery) || !seedQuery->next()) { - return results; - } - const QString seedEmail = seedQuery->value(0).toString(); - const QString seedClientId = seedQuery->value(1).toString(); - - QString queryString = "SELECT u.name, u.email, u.clientid, UNIX_TIMESTAMP(u.registrationDate), " - "UNIX_TIMESTAMP(a.last_login), " - "(SELECT COUNT(*) FROM {prefix}_warnings w WHERE w.user_id = u.id), " - "(SELECT COUNT(*) FROM {prefix}_bans b WHERE b.user_name = u.name), " - "u.active " - "FROM {prefix}_users u " - "LEFT JOIN {prefix}_user_analytics a ON a.id = u.id " - "WHERE u.name = :user_name"; - if (!seedEmail.isEmpty()) { - queryString.append(" OR u.email = :seed_email"); - } - if (!seedClientId.isEmpty()) { - queryString.append(" OR u.clientid = :seed_clientid"); - } - queryString.append(" OR u.name IN (SELECT DISTINCT s.user_name FROM {prefix}_sessions s " - "WHERE s.ip_address IN (SELECT DISTINCT s2.ip_address FROM {prefix}_sessions s2 " - "WHERE s2.user_name = :user_name" - " AND s2.start_time >= DATE_SUB(NOW(), INTERVAL 6 MONTH))" - " AND s.start_time >= DATE_SUB(NOW(), INTERVAL 6 MONTH)) " - "ORDER BY u.name LIMIT 200"); - - QSqlQuery *query = prepareQuery(queryString); - query->bindValue(":user_name", userName); - if (!seedEmail.isEmpty()) { - query->bindValue(":seed_email", seedEmail); - } - if (!seedClientId.isEmpty()) { - query->bindValue(":seed_clientid", seedClientId); - } - - if (!execSqlQuery(query)) { - qCWarning(DatabaseInterfaceLog) << "Failed to collect user alt information: SQL Error"; - return results; - } - - while (query->next()) { - ServerInfo_UserAlt altDetails; - altDetails.set_user_name(query->value(0).toString().toStdString()); - altDetails.set_email(query->value(1).toString().toStdString()); - altDetails.set_clientid(query->value(2).toString().toStdString()); - altDetails.set_registration_time(query->value(3).toLongLong()); - if (!query->value(4).isNull()) { - altDetails.set_last_login(query->value(4).toLongLong()); - } - altDetails.set_warn_count(query->value(5).toInt()); - altDetails.set_ban_count(query->value(6).toInt()); - altDetails.set_is_active(query->value(7).toBool()); - results << altDetails; - } - - return results; -} - -QList Servatrice_DatabaseInterface::getModeratorLastLogins() -{ - QList results; - - if (!checkSql()) { - return results; - } - - QSqlQuery *query = prepareQuery("SELECT u.name, u.admin, UNIX_TIMESTAMP(a.last_login) " - "FROM {prefix}_users u " - "LEFT JOIN {prefix}_user_analytics a ON a.id = u.id " - "WHERE (u.admin & 7) <> 0 ORDER BY u.name"); - - if (!execSqlQuery(query)) { - qCWarning(DatabaseInterfaceLog) << "Failed to collect moderator login information: SQL Error"; - return results; - } - - while (query->next()) { - ServerInfo_ModeratorLogin loginDetails; - loginDetails.set_user_name(query->value(0).toString().toStdString()); - - const int isAdmin = query->value(1).toInt(); - int userLevel = ServerInfo_User::IsUser | ServerInfo_User::IsRegistered; - if (isAdmin & 1) { - userLevel |= ServerInfo_User::IsAdmin | ServerInfo_User::IsModerator; - } else if (isAdmin & 2) { - userLevel |= ServerInfo_User::IsModerator; - } - if (isAdmin & 4) { - userLevel |= ServerInfo_User::IsJudge; - } - loginDetails.set_user_level(userLevel); - - if (!query->value(2).isNull()) { - loginDetails.set_last_login(query->value(2).toLongLong()); - } - results << loginDetails; - } - - return results; -} - -bool Servatrice_DatabaseInterface::removeUserAvatar(const QString &userName) -{ - if (!checkSql()) { - return false; - } - - QSqlQuery *query = prepareQuery("UPDATE {prefix}_users SET avatar_bmp = '' WHERE name = :user_name"); - query->bindValue(":user_name", userName); - if (!execSqlQuery(query)) { - return false; - } - return query->numRowsAffected() > 0; -} - QList Servatrice_DatabaseInterface::getMessageLogHistory(const QString &user, const QString &ipaddress, const QString &gamename, diff --git a/servatrice/src/servatrice_database_interface.h b/servatrice/src/servatrice_database_interface.h index cd76ae288..1e3501ec7 100644 --- a/servatrice/src/servatrice_database_interface.h +++ b/servatrice/src/servatrice_database_interface.h @@ -6,14 +6,11 @@ #include #include #include -#include -#include -#include #include #include #include -#define DATABASE_SCHEMA_VERSION 36 +#define DATABASE_SCHEMA_VERSION 35 class Servatrice; @@ -122,7 +119,6 @@ public: bool oldPasswordNeedsHash, const QString &newPassword, bool newPasswordNeedsHash) override; - void setForcePasswordChange(const QString &user, bool force) override; QList getUserBanHistory(const QString userName); bool addWarning(const QString userName, const QString adminName, const QString warningReason, const QString clientID); @@ -137,10 +133,6 @@ public: bool &room, int &range, int &maxresults); - QList getUserSessions(const QString &userName, int limit); - QList getUserAlts(const QString &userName); - QList getModeratorLastLogins(); - bool removeUserAvatar(const QString &userName); bool addForgotPassword(const QString &user); bool removeForgotPassword(const QString &user) override; bool doesForgotPasswordExist(const QString &user); diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index 2a8b5f0a4..d4a1b9217 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -49,20 +49,10 @@ #include #include #include -#include #include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include @@ -80,36 +70,20 @@ #include #include #include -#include #include #include -#include #include -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include #include #include #include -#include #include #include -#include -#include -#include #include -#include #include #include #include @@ -249,14 +223,6 @@ Response::ResponseCode AbstractServerSocketInterface::processExtendedSessionComm case SessionCommand::REQUEST_PASSWORD_SALT: return cmdRequestPasswordSalt(cmd.GetExtension(Command_RequestPasswordSalt::ext), rc); break; - case SessionCommand::REPORT: - return cmdReport(cmd.GetExtension(Command_Report::ext), rc); - case SessionCommand::REPORT_MY_LIST: - return cmdReportMyList(cmd.GetExtension(Command_ReportMyList::ext), rc); - case SessionCommand::REPORT_ADD_COMMENT: - return cmdReportAddComment(cmd.GetExtension(Command_ReportAddComment::ext), rc); - case SessionCommand::REPORT_DETAILS: - return cmdReportDetails(cmd.GetExtension(Command_ReportDetails::ext), rc); default: return Response::RespFunctionNotAllowed; } @@ -277,18 +243,10 @@ Response::ResponseCode AbstractServerSocketInterface::processExtendedModeratorCo return cmdGetWarnHistory(cmd.GetExtension(Command_GetWarnHistory::ext), rc); case ModeratorCommand::WARN_LIST: return cmdGetWarnList(cmd.GetExtension(Command_GetWarnList::ext), rc); - case ModeratorCommand::REPORT_LIST: - return cmdReportList(cmd.GetExtension(Command_ReportList::ext), rc); - case ModeratorCommand::REPORT_ASSIGN: - return cmdReportAssign(cmd.GetExtension(Command_ReportAssign::ext), rc); - case ModeratorCommand::REPORT_RESOLVE: - return cmdReportResolve(cmd.GetExtension(Command_ReportResolve::ext), rc); case ModeratorCommand::VIEWLOG_HISTORY: return cmdGetLogHistory(cmd.GetExtension(Command_ViewLogHistory::ext), rc); case ModeratorCommand::GRANT_REPLAY_ACCESS: return cmdGrantReplayAccess(cmd.GetExtension(Command_GrantReplayAccess::ext), rc); - case ModeratorCommand::REPLAY_DOWNLOAD_BY_GAME_ID: - return cmdReplayDownloadByGameId(cmd.GetExtension(Command_ReplayDownloadByGameId::ext), rc); case ModeratorCommand::FORCE_ACTIVATE_USER: return cmdForceActivateUser(cmd.GetExtension(Command_ForceActivateUser::ext), rc); case ModeratorCommand::GET_ADMIN_NOTES: @@ -301,18 +259,6 @@ Response::ResponseCode AbstractServerSocketInterface::processExtendedModeratorCo return cmdRemoveCardArtRule(cmd.GetExtension((Command_RemoveCardArtRule::ext)), rc); case ModeratorCommand::LIST_CARD_ART_RULES: return cmdListCardArtRules(cmd.GetExtension((Command_ListCardArtRules::ext)), rc); - case ModeratorCommand::REPORT_USER_INFO: - return cmdReportUserInfo(cmd.GetExtension(Command_ReportUserInfo::ext), rc); - case ModeratorCommand::REPORT_STATS: - return cmdReportStats(cmd.GetExtension(Command_ReportStats::ext), rc); - case ModeratorCommand::GET_USER_SESSIONS: - return cmdGetUserSessions(cmd.GetExtension(Command_GetUserSessions::ext), rc); - case ModeratorCommand::GET_USER_ALTS: - return cmdGetUserAlts(cmd.GetExtension(Command_GetUserAlts::ext), rc); - case ModeratorCommand::GET_MODERATOR_LAST_LOGINS: - return cmdGetModeratorLastLogins(cmd.GetExtension(Command_GetModeratorLastLogins::ext), rc); - case ModeratorCommand::REMOVE_USER_AVATAR: - return cmdRemoveUserAvatar(cmd.GetExtension(Command_RemoveUserAvatar::ext), rc); default: return Response::RespFunctionNotAllowed; } @@ -330,8 +276,6 @@ AbstractServerSocketInterface::processExtendedAdminCommand(int cmdType, const Ad return cmdReloadConfig(cmd.GetExtension(Command_ReloadConfig::ext), rc); case AdminCommand::ADJUST_MOD: return cmdAdjustMod(cmd.GetExtension(Command_AdjustMod::ext), rc); - case AdminCommand::RESET_USER_PASSWORD: - return cmdResetUserPassword(cmd.GetExtension(Command_ResetUserPassword::ext), rc); default: return Response::RespFunctionNotAllowed; } @@ -1123,10 +1067,9 @@ Response::ResponseCode AbstractServerSocketInterface::cmdGetWarnList(const Comma Response_WarnList *re = new Response_WarnList; QString officialWarnings = settingsCache->value("server/officialwarnings").toString(); - const QList categories = parseWarningCategories(officialWarnings); - for (const WarningCategory &category : categories) { - re->add_warning(category.name.toStdString()); - re->add_warning_il(category.startingIl); + QStringList warningsList = officialWarnings.split(",", Qt::SkipEmptyParts); + for (const QString &warning : warningsList) { + re->add_warning(warning.toStdString()); } re->set_user_name(nameFromStdString(cmd.user_name()).toStdString()); re->set_user_clientid(nameFromStdString(cmd.user_clientid()).toStdString()); @@ -1310,1010 +1253,6 @@ Response::ResponseCode AbstractServerSocketInterface::cmdBanFromServer(const Com return Response::RespOk; } -Response::ResponseCode AbstractServerSocketInterface::cmdReportList(const Command_ReportList &cmd, - ResponseContainer &rc) -{ - if (!sqlInterface->checkSql()) { - return Response::RespInternalError; - } - - const bool unresolvedOnly = cmd.unresolved_only(); - const int offset = static_cast(cmd.offset()); - const int limit = qMin(static_cast(cmd.limit()), 1000); - - // Columns: 0=id, 1=reporter_name, 2=reported_user_name, 3=game_id, - // 4=category, 5=description, 6=created_at, 7=status, - // 8=resolution_note, 9=assigned_mod_name, - // 10=room_id, 11=replay_id - QString whereClause; - if (unresolvedOnly) { - whereClause = "WHERE r.status = 'open' OR r.status = 'assigned' "; - } - - // Total count query - QSqlQuery *countQuery = sqlInterface->prepareQuery("SELECT COUNT(*) FROM {prefix}_reports r " + whereClause); - int totalCount = 0; - if (sqlInterface->execSqlQuery(countQuery) && countQuery->next()) { - totalCount = countQuery->value(0).toInt(); - } - - QString queryStr = "SELECT r.id, r.reporter_name, r.reported_user_name, r.game_id, " - "r.category, r.description, r.created_at, r.status, " - "r.resolution_note, u.name AS assigned_mod_name, r.room_id, " - "(SELECT id FROM {prefix}_replays WHERE id_game = r.game_id ORDER BY id DESC LIMIT 1) " - "AS replay_id " - "FROM {prefix}_reports r " - "LEFT JOIN {prefix}_users u ON r.assigned_to = u.id "; - - if (unresolvedOnly) { - queryStr += "WHERE r.status = 'open' OR r.status = 'assigned' "; - } - - queryStr += "ORDER BY r.created_at DESC LIMIT :limit OFFSET :offset"; - - QSqlQuery *query = sqlInterface->prepareQuery(queryStr); - query->bindValue(":limit", limit); - query->bindValue(":offset", offset); - if (!sqlInterface->execSqlQuery(query)) { - return Response::RespInternalError; - } - - Response_ReportList *re = new Response_ReportList; - re->set_total_count(totalCount); - - while (query->next()) { - ServerInfo_Report *info = re->add_reports(); - - info->set_report_id(query->value(0).toInt()); - info->set_reporter_name(query->value(1).toString().toStdString()); - info->set_reported_user_name(query->value(2).toString().toStdString()); - - if (!query->value(3).isNull()) { - info->set_game_id(query->value(3).toInt()); - } - - info->set_category(query->value(4).toString().toStdString()); - info->set_description(query->value(5).toString().toStdString()); - info->set_report_time(query->value(6).toDateTime().toSecsSinceEpoch()); - info->set_status(query->value(7).toString().toStdString()); - - if (!query->value(8).isNull()) { - info->set_resolution_note(query->value(8).toString().toStdString()); - } - - if (!query->value(9).isNull()) { - info->set_assigned_mod_name(query->value(9).toString().toStdString()); - } - - if (!query->value(10).isNull()) { - info->set_room_id(query->value(10).toInt()); - } - - if (!query->value(11).isNull()) { - info->set_replay_id(query->value(11).toInt()); - } - } - - rc.setResponseExtension(re); - return Response::RespOk; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdReportAssign(const Command_ReportAssign &cmd, - ResponseContainer & /*rc*/) -{ - if (!sqlInterface->checkSql()) { - return Response::RespInternalError; - } - - int reportId = cmd.report_id(); - int modId = userInfo->id(); - - QSqlQuery *lookupQuery = sqlInterface->prepareQuery("SELECT status FROM {prefix}_reports WHERE id = :id"); - lookupQuery->bindValue(":id", reportId); - - if (!sqlInterface->execSqlQuery(lookupQuery)) { - return Response::RespInternalError; - } - - if (!lookupQuery->next()) { - return Response::RespNameNotFound; - } - - if (lookupQuery->value(0).toString() != "open") { - return Response::RespInvalidData; - } - - QSqlQuery *query = sqlInterface->prepareQuery("UPDATE {prefix}_reports " - "SET status = 'assigned', assigned_to = :mod_id " - "WHERE id = :id AND status = 'open'"); - - query->bindValue(":mod_id", modId); - query->bindValue(":id", reportId); - - if (!sqlInterface->execSqlQuery(query)) { - return Response::RespInternalError; - } - - if (query->numRowsAffected() == 0) { - // The report was taken by another moderator between the lookup and this update. - return Response::RespInvalidData; - } - - return Response::RespOk; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdReportResolve(const Command_ReportResolve &cmd, - ResponseContainer & /*rc*/) -{ - if (!sqlInterface->checkSql()) { - return Response::RespInternalError; - } - - int reportId = cmd.report_id(); - QString note = textFromStdString(cmd.resolution_note()); - bool dismissed = cmd.dismissed(); - - QString newStatus = dismissed ? "dismissed" : "resolved"; - - QSqlQuery *query = sqlInterface->prepareQuery("UPDATE {prefix}_reports " - "SET status = :status, resolution_note = :note, " - "resolution_time = NOW(), resolved_by = :mod_id " - "WHERE id = :id AND status IN ('open', 'assigned')"); - - query->bindValue(":status", newStatus); - query->bindValue(":note", note); - query->bindValue(":mod_id", userInfo->id()); - query->bindValue(":id", reportId); - - if (!sqlInterface->execSqlQuery(query)) { - return Response::RespInternalError; - } - - if (query->numRowsAffected() == 0) { - return Response::RespInvalidData; - } - - sqlInterface->addAuditRecord(QString::number(reportId), this->getAddress(), - QString::fromStdString(userInfo->clientid()), - dismissed ? "REPORT_DISMISSED" : "REPORT_RESOLVED", - QString("Report #%1 %2").arg(reportId).arg(newStatus), true); - - // Notifying the reporter is best-effort: the resolve already succeeded. If any of the lookup - // queries below fail, the report is simply left with notified = 0 and the notification is - // delivered later via sendPendingReportNotifications. - QSqlQuery *lookupQuery = - sqlInterface->prepareQuery("SELECT reporter_id, reported_user_name FROM {prefix}_reports WHERE id = :id"); - lookupQuery->bindValue(":id", reportId); - - QString reporterName; - QString reportedUser; - if (sqlInterface->execSqlQuery(lookupQuery) && lookupQuery->next()) { - int reporterId = lookupQuery->value(0).toInt(); - reportedUser = lookupQuery->value(1).toString(); - - QSqlQuery *nameQuery = sqlInterface->prepareQuery("SELECT name FROM {prefix}_users WHERE id = :id"); - nameQuery->bindValue(":id", reporterId); - if (sqlInterface->execSqlQuery(nameQuery) && nameQuery->next()) { - reporterName = nameQuery->value(0).toString(); - } - } - - const QString ownName = QString::fromStdString(userInfo->name()); - if (!reporterName.isEmpty()) { - if (reporterName == ownName) { - // Resolving your own report: no self-notification needed, but mark it as notified - // so it is not delivered on the next login via sendPendingReportNotifications. - QSqlQuery *notifiedQuery = - sqlInterface->prepareQuery("UPDATE {prefix}_reports SET notified = 1 WHERE id = :id"); - notifiedQuery->bindValue(":id", reportId); - sqlInterface->execSqlQuery(notifiedQuery); - } else { - QReadLocker clientsLocker(&servatrice->clientsLock); - AbstractServerSocketInterface *reporter = - static_cast(server->getUsers().value(reporterName)); - if (reporter) { - Event_NotifyUser event; - event.set_type(Event_NotifyUser::REPORT_RESOLVED); - event.set_custom_title(dismissed ? tr("Report Dismissed").toStdString() - : tr("Report Resolved").toStdString()); - - QString content = dismissed ? tr("Your report about %1 has been dismissed.").arg(reportedUser) - : tr("Your report about %1 has been resolved.").arg(reportedUser); - if (!note.isEmpty()) { - content += "\n" + tr("Note: %1").arg(note); - } - event.set_custom_content(content.toStdString()); - - SessionEvent *se = reporter->prepareSessionEvent(event); - reporter->sendProtocolItem(*se); - delete se; - - // Only mark the report as notified if the reporter is still connected; otherwise the - // notification is delivered on their next login via sendPendingReportNotifications. - QSqlQuery *notifiedQuery = - sqlInterface->prepareQuery("UPDATE {prefix}_reports SET notified = 1 WHERE id = :id"); - notifiedQuery->bindValue(":id", reportId); - sqlInterface->execSqlQuery(notifiedQuery); - } - } - } - - return Response::RespOk; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdReportUserInfo(const Command_ReportUserInfo &cmd, - ResponseContainer &rc) -{ - if (!sqlInterface->checkSql()) { - return Response::RespInternalError; - } - - QString userName = nameFromStdString(cmd.user_name()); - - QSqlQuery *userQuery = sqlInterface->prepareQuery("SELECT u.admin, u.active, u.registrationDate, u.adminnotes " - "FROM {prefix}_users u " - "WHERE u.name = :name"); - userQuery->bindValue(":name", userName); - - if (!sqlInterface->execSqlQuery(userQuery)) { - return Response::RespInternalError; - } - - if (!userQuery->next()) { - return Response::RespNameNotFound; - } - - Response_ReportUserInfo *re = new Response_ReportUserInfo; - re->set_user_name(cmd.user_name()); - - re->set_is_admin(userQuery->value(0).toBool()); - re->set_is_active(userQuery->value(1).toBool()); - re->set_registration_time(userQuery->value(2).toDateTime().toSecsSinceEpoch()); - - if (!userQuery->value(3).isNull()) { - re->set_admin_notes(userQuery->value(3).toString().toStdString()); - } - - QSqlQuery *reportCountQuery = - sqlInterface->prepareQuery("SELECT COUNT(*) FROM {prefix}_reports WHERE reported_user_name = :name"); - reportCountQuery->bindValue(":name", userName); - if (sqlInterface->execSqlQuery(reportCountQuery) && reportCountQuery->next()) { - re->set_total_reports(reportCountQuery->value(0).toInt()); - } - - QSqlQuery *banCountQuery = sqlInterface->prepareQuery("SELECT COUNT(*) FROM {prefix}_bans WHERE user_name = :name"); - banCountQuery->bindValue(":name", userName); - if (sqlInterface->execSqlQuery(banCountQuery) && banCountQuery->next()) { - re->set_total_bans(banCountQuery->value(0).toInt()); - } - - QSqlQuery *warnCountQuery = - sqlInterface->prepareQuery("SELECT COUNT(*) FROM {prefix}_warnings WHERE user_name = :name"); - warnCountQuery->bindValue(":name", userName); - if (sqlInterface->execSqlQuery(warnCountQuery) && warnCountQuery->next()) { - re->set_total_warns(warnCountQuery->value(0).toInt()); - } - - QSqlQuery *lastLoginQuery = sqlInterface->prepareQuery("SELECT UNIX_TIMESTAMP(a.last_login) " - "FROM {prefix}_user_analytics a " - "JOIN {prefix}_users u ON u.id = a.id " - "WHERE u.name = :name"); - lastLoginQuery->bindValue(":name", userName); - if (sqlInterface->execSqlQuery(lastLoginQuery) && lastLoginQuery->next() && !lastLoginQuery->value(0).isNull()) { - re->set_last_login(lastLoginQuery->value(0).toLongLong()); - } - - QSqlQuery *recentQuery = - sqlInterface->prepareQuery("SELECT r.id, r.reporter_name, r.reported_user_name, r.game_id, " - "r.category, r.description, r.created_at, r.status, " - "r.resolution_note, ru.name AS assigned_mod_name " - "FROM {prefix}_reports r " - "LEFT JOIN {prefix}_users ru ON r.assigned_to = ru.id " - "WHERE r.reported_user_name = :name " - "ORDER BY r.created_at DESC LIMIT 10"); - recentQuery->bindValue(":name", userName); - - if (sqlInterface->execSqlQuery(recentQuery)) { - while (recentQuery->next()) { - ServerInfo_Report *info = re->add_recent_reports(); - info->set_report_id(recentQuery->value(0).toInt()); - info->set_reporter_name(recentQuery->value(1).toString().toStdString()); - info->set_reported_user_name(recentQuery->value(2).toString().toStdString()); - - if (!recentQuery->value(3).isNull()) { - info->set_game_id(recentQuery->value(3).toInt()); - } - - info->set_category(recentQuery->value(4).toString().toStdString()); - info->set_description(recentQuery->value(5).toString().toStdString()); - info->set_report_time(recentQuery->value(6).toDateTime().toSecsSinceEpoch()); - info->set_status(recentQuery->value(7).toString().toStdString()); - - if (!recentQuery->value(8).isNull()) { - info->set_resolution_note(recentQuery->value(8).toString().toStdString()); - } - - if (!recentQuery->value(9).isNull()) { - info->set_assigned_mod_name(recentQuery->value(9).toString().toStdString()); - } - } - } - - rc.setResponseExtension(re); - return Response::RespOk; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdReportStats(const Command_ReportStats & /*cmd */, - ResponseContainer &rc) -{ - if (!sqlInterface->checkSql()) { - return Response::RespInternalError; - } - - if (const auto cached = servatrice->getCachedReportStats()) { - rc.setResponseExtension(new Response_ReportStats(*cached)); - return Response::RespOk; - } - - Response_ReportStats *re = new Response_ReportStats; - - QSqlQuery *overviewQuery = sqlInterface->prepareQuery( - "SELECT COUNT(*) AS total, " - "SUM(status IN ('open', 'assigned')) AS pending, " - "SUM(status = 'assigned') AS assigned, " - "SUM(status IN ('resolved', 'dismissed')) AS resolved, " - "SUM(created_at >= DATE_SUB(NOW(), INTERVAL 1 DAY)) AS last24h, " - "SUM(created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)) AS last7d, " - "SUM(created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)) AS last30d, " - "SUM(created_at >= DATE_SUB(CURDATE(), INTERVAL WEEKDAY(CURDATE()) DAY)) AS this_week " - "FROM {prefix}_reports"); - if (!sqlInterface->execSqlQuery(overviewQuery) || !overviewQuery->next()) { - delete re; - return Response::RespInternalError; - } - - re->set_total_reports(overviewQuery->value(0).toInt()); - re->set_total_pending(overviewQuery->value(1).toInt()); - re->set_total_assigned(overviewQuery->value(2).toInt()); - re->set_total_resolved(overviewQuery->value(3).toInt()); - re->set_reports_last_24h(overviewQuery->value(4).toInt()); - re->set_reports_last_7d(overviewQuery->value(5).toInt()); - re->set_reports_last_30d(overviewQuery->value(6).toInt()); - re->set_reports_this_week(overviewQuery->value(7).toInt()); - - bool allQueriesOk = true; - - QSqlQuery *lastWeekQuery = - sqlInterface->prepareQuery("SELECT COUNT(*) FROM {prefix}_reports WHERE created_at >= DATE_SUB(CURDATE(), " - "INTERVAL WEEKDAY(CURDATE()) + 7 DAY) " - "AND created_at < DATE_SUB(CURDATE(), INTERVAL WEEKDAY(CURDATE()) DAY)"); - if (sqlInterface->execSqlQuery(lastWeekQuery) && lastWeekQuery->next()) { - re->set_reports_last_week(lastWeekQuery->value(0).toInt()); - } else { - allQueriesOk = false; - } - - QSqlQuery *avgResQuery = sqlInterface->prepareQuery( - "SELECT AVG(TIMESTAMPDIFF(HOUR, created_at, resolution_time)) " - "FROM {prefix}_reports WHERE status IN ('resolved','dismissed') AND resolution_time IS NOT NULL"); - if (sqlInterface->execSqlQuery(avgResQuery) && avgResQuery->next()) { - if (!avgResQuery->value(0).isNull()) { - re->set_avg_resolution_hours(avgResQuery->value(0).toDouble()); - } - } else { - allQueriesOk = false; - } - - QSqlQuery *catQuery = sqlInterface->prepareQuery( - "SELECT category, COUNT(*) AS cnt FROM {prefix}_reports GROUP BY category ORDER BY cnt DESC LIMIT 10"); - if (sqlInterface->execSqlQuery(catQuery)) { - while (catQuery->next()) { - ReportCategoryCount *cc = re->add_category_counts(); - cc->set_category(catQuery->value(0).toString().toStdString()); - cc->set_count(catQuery->value(1).toInt()); - } - } else { - allQueriesOk = false; - } - - QSqlQuery *topReportedQuery = - sqlInterface->prepareQuery("SELECT reported_user_name, COUNT(*) AS cnt FROM {prefix}_reports " - "GROUP BY reported_user_name ORDER BY cnt DESC LIMIT 10"); - if (sqlInterface->execSqlQuery(topReportedQuery)) { - while (topReportedQuery->next()) { - ReportTopUser *tu = re->add_top_reported_users(); - tu->set_user_name(topReportedQuery->value(0).toString().toStdString()); - tu->set_count(topReportedQuery->value(1).toInt()); - } - } else { - allQueriesOk = false; - } - - QSqlQuery *topReporterQuery = - sqlInterface->prepareQuery("SELECT reporter_name, COUNT(*) AS cnt FROM {prefix}_reports " - "GROUP BY reporter_name ORDER BY cnt DESC LIMIT 10"); - if (sqlInterface->execSqlQuery(topReporterQuery)) { - while (topReporterQuery->next()) { - ReportTopUser *tu = re->add_top_reporters(); - tu->set_user_name(topReporterQuery->value(0).toString().toStdString()); - tu->set_count(topReporterQuery->value(1).toInt()); - } - } else { - allQueriesOk = false; - } - - if (allQueriesOk) { - servatrice->cacheReportStats(*re); - } - - rc.setResponseExtension(re); - return Response::RespOk; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdGetUserSessions(const Command_GetUserSessions &cmd, - ResponseContainer &rc) -{ - if (!sqlInterface->checkSql()) { - return Response::RespInternalError; - } - - const QString userName = nameFromStdString(cmd.user_name()); - if (userName.isEmpty()) { - return Response::RespContextError; - } - - Response_UserSessions *re = new Response_UserSessions; - const int limit = qMin(static_cast(cmd.limit()), 500); - const QList sessions = sqlInterface->getUserSessions(userName, limit); - for (const ServerInfo_UserSession &session : sessions) { - re->add_sessions()->CopyFrom(session); - } - rc.setResponseExtension(re); - return Response::RespOk; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdGetUserAlts(const Command_GetUserAlts &cmd, - ResponseContainer &rc) -{ - if (!sqlInterface->checkSql()) { - return Response::RespInternalError; - } - - const QString userName = nameFromStdString(cmd.user_name()); - if (userName.isEmpty()) { - return Response::RespContextError; - } - - Response_UserAlts *re = new Response_UserAlts; - const QList alts = sqlInterface->getUserAlts(userName); - for (const ServerInfo_UserAlt &alt : alts) { - re->add_alts()->CopyFrom(alt); - } - rc.setResponseExtension(re); - return Response::RespOk; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdGetModeratorLastLogins(const Command_GetModeratorLastLogins &, - ResponseContainer &rc) -{ - if (!sqlInterface->checkSql()) { - return Response::RespInternalError; - } - - Response_ModeratorLastLogins *re = new Response_ModeratorLastLogins; - const QList logins = sqlInterface->getModeratorLastLogins(); - for (const ServerInfo_ModeratorLogin &login : logins) { - re->add_logins()->CopyFrom(login); - } - rc.setResponseExtension(re); - return Response::RespOk; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdResetUserPassword(const Command_ResetUserPassword &cmd, - ResponseContainer &rc) -{ - if (!sqlInterface->checkSql()) { - return Response::RespInternalError; - } - - const QString userName = nameFromStdString(cmd.user_name()).simplified(); - if (userName.isEmpty()) { - return Response::RespContextError; - } - - // Look up the target user's privilege level to prevent escalation. - QSqlQuery *privQuery = sqlInterface->prepareQuery("SELECT admin FROM {prefix}_users WHERE name = :name"); - privQuery->bindValue(":name", userName); - if (!sqlInterface->execSqlQuery(privQuery) || !privQuery->next()) { - return Response::RespNameNotFound; - } - const int targetAdmin = privQuery->value(0).toInt(); - const bool targetIsAdmin = targetAdmin & 1; - const bool targetIsMod = targetAdmin & 2; - - const bool callerIsAdmin = userInfo->user_level() & ServerInfo_User::IsAdmin; - - // A moderator must not reset the password of an admin or another moderator. - if (!callerIsAdmin && (targetIsAdmin || targetIsMod)) { - return Response::RespAccessDenied; - } - - const QString tempPassword = PasswordHasher::generateRandomSalt(); - if (!sqlInterface->changeUserPassword(userName, tempPassword, true)) { - return Response::RespInternalError; - } - sqlInterface->setForcePasswordChange(userName, true); - - sqlInterface->addAuditRecord(userName, this->getAddress(), QString::fromStdString(userInfo->clientid()), - "PASSWORD_RESET", "Admin password reset", true); - - // Notify the affected user if they are currently online. - QReadLocker clientsLocker(&servatrice->clientsLock); - AbstractServerSocketInterface *targetSession = - static_cast(server->getUsers().value(userName)); - if (targetSession) { - Event_NotifyUser event; - event.set_type(Event_NotifyUser::CUSTOM); - event.set_custom_title(tr("Password Reset").toStdString()); - event.set_custom_content(tr("An administrator has reset your password. Please log in with the new " - "password provided to you and change it immediately.") - .toStdString()); - SessionEvent *se = targetSession->prepareSessionEvent(event); - targetSession->sendProtocolItem(*se); - delete se; - } - - Response_ResetUserPassword *re = new Response_ResetUserPassword; - re->set_user_name(userName.toStdString()); - re->set_temporary_password(tempPassword.toStdString()); - rc.setResponseExtension(re); - return Response::RespOk; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdRemoveUserAvatar(const Command_RemoveUserAvatar &cmd, - ResponseContainer &rc) -{ - if (!sqlInterface->checkSql()) { - return Response::RespInternalError; - } - - const QString userName = nameFromStdString(cmd.user_name()).simplified(); - if (userName.isEmpty()) { - return Response::RespContextError; - } - - if (!sqlInterface->removeUserAvatar(userName)) { - return Response::RespInternalError; - } - - sqlInterface->addAuditRecord(userName, this->getAddress(), QString::fromStdString(userInfo->clientid()), - "REMOVE_USER_AVATAR", "Moderator removed user avatar", true); - - Response_RemoveUserAvatar *re = new Response_RemoveUserAvatar; - re->set_user_name(userName.toStdString()); - rc.setResponseExtension(re); - return Response::RespOk; -} - -void AbstractServerSocketInterface::sendPendingReportNotifications(ResponseContainer &rc) -{ - if (!sqlInterface->checkSql()) { - return; - } - - QSqlQuery *query = sqlInterface->prepareQuery("SELECT id, reported_user_name, status, resolution_note " - "FROM {prefix}_reports " - "WHERE reporter_id = :reporter_id AND notified = 0 " - "AND status IN ('resolved', 'dismissed')"); - query->bindValue(":reporter_id", userInfo->id()); - - if (!sqlInterface->execSqlQuery(query)) { - return; - } - - while (query->next()) { - const int reportId = query->value(0).toInt(); - const QString reportedUser = query->value(1).toString(); - const bool dismissed = query->value(2).toString() == "dismissed"; - const QString note = query->value(3).toString(); - - Event_NotifyUser event; - event.set_type(Event_NotifyUser::REPORT_RESOLVED); - event.set_custom_title(dismissed ? tr("Report Dismissed").toStdString() : tr("Report Resolved").toStdString()); - - QString content = dismissed ? tr("Your report about %1 has been dismissed.").arg(reportedUser) - : tr("Your report about %1 has been resolved.").arg(reportedUser); - if (!note.isEmpty()) { - content += "\n" + tr("Note: %1").arg(note); - } - event.set_custom_content(content.toStdString()); - - rc.enqueuePreResponseItem(ServerMessage::SESSION_EVENT, prepareSessionEvent(event)); - - QSqlQuery *notifiedQuery = - sqlInterface->prepareQuery("UPDATE {prefix}_reports SET notified = 1 WHERE id = :id"); - notifiedQuery->bindValue(":id", reportId); - sqlInterface->execSqlQuery(notifiedQuery); - } - - QSqlQuery *commentQuery = sqlInterface->prepareQuery("SELECT c.id, c.report_id, c.author_name, c.comment_text " - "FROM {prefix}_report_comments c " - "JOIN {prefix}_reports r ON r.id = c.report_id " - "WHERE c.notified = 0 " - "AND c.author_id != :user_id " - "AND ((c.is_moderator = 1 AND r.reporter_id = :user_id) " - "OR (c.is_moderator = 0 AND r.assigned_to = :user_id) " - "OR (c.is_moderator = 1 AND r.assigned_to = :user_id)) " - "ORDER BY c.created_at ASC"); - commentQuery->bindValue(":user_id", userInfo->id()); - - if (!sqlInterface->execSqlQuery(commentQuery)) { - return; - } - - while (commentQuery->next()) { - const qlonglong commentId = commentQuery->value(0).toLongLong(); - const int reportId = commentQuery->value(1).toInt(); - const QString authorName = commentQuery->value(2).toString(); - const QString commentText = commentQuery->value(3).toString(); - - Event_NotifyUser event; - event.set_type(Event_NotifyUser::REPORT_COMMENT); - event.set_custom_title(tr("New Comment on Report #%1").arg(reportId).toStdString()); - event.set_custom_content(tr("%1 commented:\n%2").arg(authorName, commentText).toStdString()); - - rc.enqueuePreResponseItem(ServerMessage::SESSION_EVENT, prepareSessionEvent(event)); - - QSqlQuery *notifiedQuery = - sqlInterface->prepareQuery("UPDATE {prefix}_report_comments SET notified = 1 WHERE id = :id"); - notifiedQuery->bindValue(":id", commentId); - sqlInterface->execSqlQuery(notifiedQuery); - } -} - -void AbstractServerSocketInterface::onLogin(ResponseContainer &rc) -{ - sendPendingReportNotifications(rc); -} - -Response::ResponseCode AbstractServerSocketInterface::cmdReportMyList(const Command_ReportMyList & /*cmd */, - ResponseContainer &rc) -{ - if (authState != PasswordRight) { - return Response::RespFunctionNotAllowed; - } - - if (!sqlInterface->checkSql()) { - return Response::RespInternalError; - } - - sendPendingReportNotifications(rc); - - QString queryStr = "SELECT r.id, r.reporter_name, r.reported_user_name, r.game_id, " - "r.category, r.description, r.created_at, r.status, " - "r.resolution_note, u.name AS assigned_mod_name, r.room_id, " - "(SELECT id FROM {prefix}_replays WHERE id_game = r.game_id ORDER BY id DESC LIMIT 1) " - "AS replay_id " - "FROM {prefix}_reports r " - "LEFT JOIN {prefix}_users u ON r.assigned_to = u.id " - "WHERE r.reporter_id = :reporter_id " - "ORDER BY r.created_at DESC LIMIT 200"; - - QSqlQuery *query = sqlInterface->prepareQuery(queryStr); - query->bindValue(":reporter_id", userInfo->id()); - - if (!sqlInterface->execSqlQuery(query)) { - return Response::RespInternalError; - } - - Response_ReportMyList *re = new Response_ReportMyList; - - while (query->next()) { - ServerInfo_Report *info = re->add_reports(); - - info->set_report_id(query->value(0).toInt()); - info->set_reporter_name(query->value(1).toString().toStdString()); - info->set_reported_user_name(query->value(2).toString().toStdString()); - - if (!query->value(3).isNull()) { - info->set_game_id(query->value(3).toInt()); - } - - info->set_category(query->value(4).toString().toStdString()); - info->set_description(query->value(5).toString().toStdString()); - info->set_report_time(query->value(6).toDateTime().toSecsSinceEpoch()); - info->set_status(query->value(7).toString().toStdString()); - - if (!query->value(8).isNull()) { - info->set_resolution_note(query->value(8).toString().toStdString()); - } - - if (!query->value(9).isNull()) { - info->set_assigned_mod_name(query->value(9).toString().toStdString()); - } - - if (!query->value(10).isNull()) { - info->set_room_id(query->value(10).toInt()); - } - - if (!query->value(11).isNull()) { - info->set_replay_id(query->value(11).toInt()); - } - } - - rc.setResponseExtension(re); - return Response::RespOk; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdReportDetails(const Command_ReportDetails &cmd, - ResponseContainer &rc) -{ - if (authState != PasswordRight) { - return Response::RespFunctionNotAllowed; - } - - if (!sqlInterface->checkSql()) { - return Response::RespInternalError; - } - - const int reportId = cmd.report_id(); - - // Columns: 0=id, 1=reporter_id, 2=reporter_name, 3=reported_user_name, 4=game_id, - // 5=category, 6=description, 7=created_at, 8=status, - // 9=resolution_note, 10=assigned_mod_name, 11=chat_log, - // 12=room_id, 13=resolution_time, 14=replay_id - QString queryStr = "SELECT r.id, r.reporter_id, r.reporter_name, r.reported_user_name, r.game_id, " - "r.category, r.description, r.created_at, r.status, " - "r.resolution_note, u.name AS assigned_mod_name, r.chat_log, r.room_id, " - "r.resolution_time, " - "(SELECT id FROM {prefix}_replays WHERE id_game = r.game_id ORDER BY id DESC LIMIT 1) " - "AS replay_id " - "FROM {prefix}_reports r " - "LEFT JOIN {prefix}_users u ON r.assigned_to = u.id " - "WHERE r.id = :id"; - QSqlQuery *query = sqlInterface->prepareQuery(queryStr); - query->bindValue(":id", reportId); - - if (!sqlInterface->execSqlQuery(query)) { - return Response::RespInternalError; - } - - if (!query->next()) { - return Response::RespNameNotFound; - } - - const bool isMod = userInfo->user_level() & ServerInfo_User::IsModerator; - const int reporterId = query->value(1).toInt(); - if (reporterId != userInfo->id() && !isMod) { - return Response::RespAccessDenied; - } - - ServerInfo_Report *info = new ServerInfo_Report; - info->set_report_id(query->value(0).toInt()); - info->set_reporter_name(query->value(2).toString().toStdString()); - info->set_reported_user_name(query->value(3).toString().toStdString()); - - if (!query->value(4).isNull()) { - info->set_game_id(query->value(4).toInt()); - } - - info->set_category(query->value(5).toString().toStdString()); - info->set_description(query->value(6).toString().toStdString()); - info->set_report_time(query->value(7).toDateTime().toSecsSinceEpoch()); - info->set_status(query->value(8).toString().toStdString()); - - if (!query->value(9).isNull()) { - info->set_resolution_note(query->value(9).toString().toStdString()); - } - - if (!query->value(10).isNull()) { - info->set_assigned_mod_name(query->value(10).toString().toStdString()); - } - - if (!query->value(11).isNull()) { - info->set_chat_log(query->value(11).toString().toStdString()); - } - - if (!query->value(12).isNull()) { - info->set_room_id(query->value(12).toInt()); - } - - if (!query->value(13).isNull()) { - info->set_resolution_time(query->value(13).toDateTime().toSecsSinceEpoch()); - } - - if (!query->value(14).isNull()) { - info->set_replay_id(query->value(14).toInt()); - } - - QSqlQuery *commentQuery = - sqlInterface->prepareQuery("SELECT c.author_name, c.comment_text, c.created_at, c.is_moderator " - "FROM {prefix}_report_comments c " - "WHERE c.report_id = :report_id ORDER BY c.created_at ASC"); - commentQuery->bindValue(":report_id", reportId); - - if (sqlInterface->execSqlQuery(commentQuery)) { - while (commentQuery->next()) { - ServerInfo_ReportComment *comment = info->add_comments(); - comment->set_author_name(commentQuery->value(0).toString().toStdString()); - comment->set_comment_text(commentQuery->value(1).toString().toStdString()); - comment->set_comment_time(commentQuery->value(2).toDateTime().toSecsSinceEpoch()); - comment->set_is_moderator(commentQuery->value(3).toBool()); - } - } - - Response_ReportDetails *re = new Response_ReportDetails; - re->set_allocated_report(info); - rc.setResponseExtension(re); - return Response::RespOk; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdReportAddComment(const Command_ReportAddComment &cmd, - ResponseContainer & /*rc*/) -{ - if (authState != PasswordRight) { - return Response::RespFunctionNotAllowed; - } - - if (!sqlInterface->checkSql()) { - return Response::RespInternalError; - } - - int reportId = cmd.report_id(); - QString commentText = textFromStdString(cmd.comment()).trimmed(); - - if (commentText.isEmpty()) { - return Response::RespInvalidData; - } - - const int maxCommentsPerHour = settingsCache->value("reporting/max_comments_per_hour", 30).toInt(); - if (maxCommentsPerHour > 0) { - QSqlQuery *countQuery = - sqlInterface->prepareQuery("SELECT COUNT(*) FROM {prefix}_report_comments WHERE author_id = :id " - "AND created_at >= DATE_SUB(NOW(), INTERVAL 1 HOUR)"); - countQuery->bindValue(":id", userInfo->id()); - if (sqlInterface->execSqlQuery(countQuery) && countQuery->next() && - countQuery->value(0).toInt() >= maxCommentsPerHour) { - return Response::RespTooManyRequests; - } - } - - QSqlQuery *checkQuery = sqlInterface->prepareQuery( - "SELECT reporter_id, reporter_name, assigned_to, status FROM {prefix}_reports WHERE id = :id"); - checkQuery->bindValue(":id", reportId); - - if (!sqlInterface->execSqlQuery(checkQuery)) { - return Response::RespInternalError; - } - - if (!checkQuery->next()) { - return Response::RespNameNotFound; - } - - int reporterId = checkQuery->value(0).toInt(); - QString reporterName = checkQuery->value(1).toString(); - int assignedToId = checkQuery->value(2).toInt(); - - bool isMod = userInfo->user_level() & ServerInfo_User::IsModerator; - - // Only the reporter (by id) or a moderator may comment on a report. - if (reporterId != userInfo->id() && !isMod) { - return Response::RespAccessDenied; - } - - QString reportStatus = checkQuery->value(3).toString(); - if (reportStatus == "resolved" || reportStatus == "dismissed") { - return Response::RespInvalidData; - } - - QSqlQuery *insertQuery = sqlInterface->prepareQuery( - "INSERT INTO {prefix}_report_comments (report_id, author_name, author_id, comment_text, created_at, " - "is_moderator) " - "VALUES (:report_id, :author_name, :author_id, :comment_text, NOW(), :is_moderator)"); - insertQuery->bindValue(":report_id", reportId); - insertQuery->bindValue(":author_name", QString::fromStdString(userInfo->name())); - insertQuery->bindValue(":author_id", userInfo->id()); - insertQuery->bindValue(":comment_text", commentText); - insertQuery->bindValue(":is_moderator", isMod); - - if (!sqlInterface->execSqlQuery(insertQuery)) { - return Response::RespInternalError; - } - - const qlonglong commentId = insertQuery->lastInsertId().toLongLong(); - - QStringList recipients; - if (isMod) { - // A moderator comment reaches both the reporter and (if assigned) the assigned moderator. - recipients.append(reporterName); - if (assignedToId > 0) { - QSqlQuery *modNameQuery = sqlInterface->prepareQuery("SELECT name FROM {prefix}_users WHERE id = :id"); - modNameQuery->bindValue(":id", assignedToId); - if (sqlInterface->execSqlQuery(modNameQuery) && modNameQuery->next()) { - recipients.append(modNameQuery->value(0).toString()); - } - } - } else if (assignedToId > 0) { - QSqlQuery *modNameQuery = sqlInterface->prepareQuery("SELECT name FROM {prefix}_users WHERE id = :id"); - modNameQuery->bindValue(":id", assignedToId); - if (sqlInterface->execSqlQuery(modNameQuery) && modNameQuery->next()) { - recipients.append(modNameQuery->value(0).toString()); - } - } - - const QString ownName = QString::fromStdString(userInfo->name()); - bool allNotified = !recipients.isEmpty(); - QReadLocker clientsLocker(&servatrice->clientsLock); - for (const QString ¬ifyName : recipients) { - if (notifyName == ownName) { - continue; - } - - AbstractServerSocketInterface *notify = - static_cast(server->getUsers().value(notifyName)); - if (!notify) { - // The recipient is offline; leave `notified` = 0 so the notification is - // delivered on their next login via sendPendingReportNotifications. - allNotified = false; - continue; - } - - Event_NotifyUser event; - event.set_type(Event_NotifyUser::REPORT_COMMENT); - event.set_custom_title(tr("New Comment on Report #%1").arg(reportId).toStdString()); - event.set_custom_content( - tr("%1 commented:\n%2").arg(QString::fromStdString(userInfo->name()), commentText).toStdString()); - - SessionEvent *se = notify->prepareSessionEvent(event); - notify->sendProtocolItem(*se); - delete se; - } - - if (allNotified) { - QSqlQuery *notifiedQuery = - sqlInterface->prepareQuery("UPDATE {prefix}_report_comments SET notified = 1 WHERE id = :id"); - notifiedQuery->bindValue(":id", commentId); - sqlInterface->execSqlQuery(notifiedQuery); - } - - return Response::RespOk; -} - -Response::ResponseCode -AbstractServerSocketInterface::cmdReplayDownloadByGameId(const Command_ReplayDownloadByGameId &cmd, - ResponseContainer &rc) -{ - if (authState != PasswordRight) { - return Response::RespFunctionNotAllowed; - } - - if (!sqlInterface->checkSql()) { - return Response::RespInternalError; - } - - QSqlQuery *query = sqlInterface->prepareQuery("SELECT r.id, r.replay FROM {prefix}_replays r " - "WHERE r.id_game = :game_id ORDER BY r.id DESC LIMIT 1"); - query->bindValue(":game_id", cmd.game_id()); - - if (!sqlInterface->execSqlQuery(query)) { - return Response::RespInternalError; - } - - if (!query->next()) { - return Response::RespNameNotFound; - } - - int replayId = query->value(0).toInt(); - QByteArray data = query->value(1).toByteArray(); - - Response_ReplayDownloadByGameId *re = new Response_ReplayDownloadByGameId; - re->set_replay_data(data.data(), data.size()); - re->set_replay_id(replayId); - rc.setResponseExtension(re); - - return Response::RespOk; -} - Response::ResponseCode AbstractServerSocketInterface::cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc) { @@ -2846,8 +1785,6 @@ Response::ResponseCode AbstractServerSocketInterface::cmdAccountPassword(const C return Response::RespWrongPassword; } - databaseInterface->setForcePasswordChange(userName, false); - return Response::RespOk; } @@ -2984,7 +1921,6 @@ Response::ResponseCode AbstractServerSocketInterface::cmdForgotPasswordReset(con "PASSWORD_RESET", "", true); } - sqlInterface->setForcePasswordChange(nameFromStdString(cmd.user_name()), false); sqlInterface->removeForgotPassword(nameFromStdString(cmd.user_name())); return Response::RespOk; } @@ -3053,100 +1989,6 @@ Response::ResponseCode AbstractServerSocketInterface::cmdRequestPasswordSalt(con return Response::RespOk; } -Response::ResponseCode AbstractServerSocketInterface::cmdReport(const Command_Report &cmd, ResponseContainer & /*rc*/) -{ - if (authState != PasswordRight) { - return Response::RespFunctionNotAllowed; - } - - if (!sqlInterface->checkSql()) { - return Response::RespInternalError; - } - - if (!cmd.has_reported_user() || !cmd.has_category() || !cmd.has_description()) { - return Response::RespInvalidData; - } - - QString reporterName = QString::fromStdString(userInfo->name()); - QString reportedUser = nameFromStdString(cmd.reported_user()); - QString category = nameFromStdString(cmd.category()); - QString description = textFromStdString(cmd.description()); - QString chatLog = textTailFromStdString(cmd.chat_log()); - - if (reportedUser.isEmpty() || category.isEmpty() || description.isEmpty()) { - return Response::RespInvalidData; - } - - static const QStringList validCategories = {"cheating", "bug_abuse", "verbal_abuse", "other"}; - if (!validCategories.contains(category.toLower())) { - return Response::RespInvalidData; - } - - const int maxReportsPerDay = settingsCache->value("reporting/max_reports_per_day", 10).toInt(); - if (maxReportsPerDay > 0) { - QSqlQuery *countQuery = - sqlInterface->prepareQuery("SELECT COUNT(*) FROM {prefix}_reports WHERE reporter_id = :id " - "AND created_at >= DATE_SUB(NOW(), INTERVAL 1 DAY)"); - countQuery->bindValue(":id", userInfo->id()); - if (sqlInterface->execSqlQuery(countQuery) && countQuery->next() && - countQuery->value(0).toInt() >= maxReportsPerDay) { - return Response::RespTooManyRequests; - } - } - - int reportedUserId = -1; - QSqlQuery *lookupQuery = sqlInterface->prepareQuery("SELECT id FROM {prefix}_users WHERE name = :name"); - lookupQuery->bindValue(":name", reportedUser); - if (sqlInterface->execSqlQuery(lookupQuery) && lookupQuery->next()) { - reportedUserId = lookupQuery->value(0).toInt(); - } - - if (reportedUserId == -1) { - return Response::RespNameNotFound; - } - - int roomId = 0; - const int gameId = cmd.game_id(); - if (gameId > 0) { - QReadLocker roomsLocker(&servatrice->roomsLock); - const QMap &rooms = servatrice->getRooms(); - for (auto it = rooms.constBegin(); it != rooms.constEnd(); ++it) { - QReadLocker gamesLocker(&it.value()->gamesLock); - if (it.value()->getGames().contains(gameId)) { - roomId = it.key(); - break; - } - } - } - - QSqlQuery *query = - sqlInterface->prepareQuery("insert into {prefix}_reports " - "(reporter_id, reporter_name, reported_user_id, reported_user_name, " - "game_id, room_id, category, description, chat_log, created_at, status) " - "values " - "(:reporter_id, :reporter_name, :reported_user_id, :reported_user_name, " - ":game_id, :room_id, :category, :description, :chat_log, NOW(), 'open')"); - - query->bindValue(":reporter_id", userInfo->id()); - query->bindValue(":reporter_name", reporterName); - query->bindValue(":reported_user_id", reportedUserId); - query->bindValue(":reported_user_name", reportedUser); - - query->bindValue(":game_id", gameId > 0 ? gameId : QVariant()); - - query->bindValue(":room_id", roomId > 0 ? roomId : QVariant()); - - query->bindValue(":category", category); - query->bindValue(":description", description); - query->bindValue(":chat_log", chatLog.isEmpty() ? QVariant() : chatLog); - - if (!sqlInterface->execSqlQuery(query)) { - return Response::RespInternalError; - } - - return Response::RespOk; -} - // ADMIN FUNCTIONS. // Permission is checked by the calling function. diff --git a/servatrice/src/serversocketinterface.h b/servatrice/src/serversocketinterface.h index 600796b5f..0d66ae78f 100644 --- a/servatrice/src/serversocketinterface.h +++ b/servatrice/src/serversocketinterface.h @@ -24,16 +24,6 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include class Servatrice; @@ -60,7 +50,6 @@ class Command_BanFromServer; class Command_UpdateServerMessage; class Command_ShutdownServer; class Command_ReloadConfig; -class Command_ReplayDownloadByGameId; class Command_AccountEdit; class Command_AccountImage; @@ -78,7 +67,7 @@ signals: void incTxBytes(qint64 amount); protected: - void logDebugMessage(const QString &message) override; + void logDebugMessage(const QString &message); bool tooManyRegistrationAttempts(const QString &ipAddress); virtual void writeToSocket(QByteArray &data) = 0; @@ -113,7 +102,6 @@ private: Response::ResponseCode cmdReplayGetCode(const Command_ReplayGetCode &cmd, ResponseContainer &rc); Response::ResponseCode cmdReplaySubmitCode(const Command_ReplaySubmitCode &cmd, ResponseContainer &rc); Response::ResponseCode cmdBanFromServer(const Command_BanFromServer &cmd, ResponseContainer &rc); - Response::ResponseCode cmdReportList(const Command_ReportList &cmd, ResponseContainer &rc); Response::ResponseCode cmdWarnUser(const Command_WarnUser &cmd, ResponseContainer &rc); Response::ResponseCode cmdGetLogHistory(const Command_ViewLogHistory &cmd, ResponseContainer &rc); Response::ResponseCode cmdGetBanHistory(const Command_GetBanHistory &cmd, ResponseContainer &rc); @@ -121,10 +109,6 @@ private: Response::ResponseCode cmdGetWarnHistory(const Command_GetWarnHistory &cmd, ResponseContainer &rc); Response::ResponseCode cmdShutdownServer(const Command_ShutdownServer &cmd, ResponseContainer &rc); Response::ResponseCode cmdUpdateServerMessage(const Command_UpdateServerMessage &cmd, ResponseContainer &rc); - Response::ResponseCode cmdReportAssign(const Command_ReportAssign &cmd, ResponseContainer &); - Response::ResponseCode cmdReportResolve(const Command_ReportResolve &cmd, ResponseContainer &); - Response::ResponseCode cmdReportUserInfo(const Command_ReportUserInfo &cmd, ResponseContainer &rc); - Response::ResponseCode cmdReportStats(const Command_ReportStats &cmd, ResponseContainer &rc); Response::ResponseCode cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc); Response::ResponseCode cmdActivateAccount(const Command_Activate &cmd, ResponseContainer & /* rc */); Response::ResponseCode cmdReloadConfig(const Command_ReloadConfig & /* cmd */, ResponseContainer & /*rc*/); @@ -138,19 +122,10 @@ private: Response::ResponseCode cmdForgotPasswordChallenge(const Command_ForgotPasswordChallenge &cmd, ResponseContainer &rc); Response::ResponseCode cmdRequestPasswordSalt(const Command_RequestPasswordSalt &cmd, ResponseContainer &rc); - Response::ResponseCode cmdReport(const Command_Report &cmd, ResponseContainer &); - Response::ResponseCode cmdReportMyList(const Command_ReportMyList &cmd, ResponseContainer &rc); - void sendPendingReportNotifications(ResponseContainer &rc); - void onLogin(ResponseContainer &rc) override; - Response::ResponseCode cmdReportDetails(const Command_ReportDetails &cmd, ResponseContainer &rc); - Response::ResponseCode cmdReportAddComment(const Command_ReportAddComment &cmd, ResponseContainer &rc); - Response::ResponseCode cmdReplayDownloadByGameId(const Command_ReplayDownloadByGameId &cmd, ResponseContainer &rc); + Response::ResponseCode processExtendedSessionCommand(int cmdType, const SessionCommand &cmd, ResponseContainer &rc); Response::ResponseCode - processExtendedSessionCommand(int cmdType, const SessionCommand &cmd, ResponseContainer &rc) override; - Response::ResponseCode - processExtendedModeratorCommand(int cmdType, const ModeratorCommand &cmd, ResponseContainer &rc) override; - Response::ResponseCode - processExtendedAdminCommand(int cmdType, const AdminCommand &cmd, ResponseContainer &rc) override; + processExtendedModeratorCommand(int cmdType, const ModeratorCommand &cmd, ResponseContainer &rc); + Response::ResponseCode processExtendedAdminCommand(int cmdType, const AdminCommand &cmd, ResponseContainer &rc); Response::ResponseCode cmdAccountEdit(const Command_AccountEdit &cmd, ResponseContainer &rc); Response::ResponseCode cmdAccountImage(const Command_AccountImage &cmd, ResponseContainer &rc); @@ -166,12 +141,6 @@ private: Response::ResponseCode cmdGetAdminNotes(const Command_GetAdminNotes &cmd, ResponseContainer &rc); Response::ResponseCode cmdUpdateAdminNotes(const Command_UpdateAdminNotes &cmd, ResponseContainer &rc); - Response::ResponseCode cmdGetUserSessions(const Command_GetUserSessions &cmd, ResponseContainer &rc); - Response::ResponseCode cmdGetUserAlts(const Command_GetUserAlts &cmd, ResponseContainer &rc); - Response::ResponseCode cmdGetModeratorLastLogins(const Command_GetModeratorLastLogins &cmd, ResponseContainer &rc); - Response::ResponseCode cmdResetUserPassword(const Command_ResetUserPassword &cmd, ResponseContainer &rc); - Response::ResponseCode cmdRemoveUserAvatar(const Command_RemoveUserAvatar &cmd, ResponseContainer &rc); - bool addAdminFlagToUser(const QString &user, int flag); bool removeAdminFlagFromUser(const QString &user, int flag); @@ -188,9 +157,9 @@ public: bool initSession(); virtual QHostAddress getPeerAddress() const = 0; - QString getAddress() const override = 0; + virtual QString getAddress() const = 0; - void transmitProtocolItem(const ServerMessage &item) override; + void transmitProtocolItem(const ServerMessage &item); }; class TcpServerSocketInterface : public AbstractServerSocketInterface diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 804293784..1c3f4c2c6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -7,11 +7,9 @@ add_test(NAME expression_test COMMAND expression_test) add_test(NAME clamped_arithmetic_test COMMAND clamped_arithmetic_test) add_test(NAME test_age_formatting COMMAND test_age_formatting) add_test(NAME password_hash_test COMMAND password_hash_test) -add_test(NAME playmat_resolver_test COMMAND playmat_resolver_test) add_test(NAME server_card_counter_test COMMAND server_card_counter_test) add_test(NAME server_counter_test COMMAND server_counter_test) add_test(NAME server_rate_limiter_test COMMAND server_rate_limiter_test) -add_test(NAME warning_categories_test COMMAND warning_categories_test) add_test(NAME deck_hash_performance_test COMMAND deck_hash_performance_test) set_tests_properties(deck_hash_performance_test PROPERTIES TIMEOUT 5) @@ -23,12 +21,10 @@ add_executable(expression_test expression_test.cpp) add_executable(clamped_arithmetic_test clamped_arithmetic_test.cpp) add_executable(test_age_formatting test_age_formatting.cpp) add_executable(password_hash_test password_hash_test.cpp) -add_executable(playmat_resolver_test playmat_resolver_test.cpp) add_executable(deck_hash_performance_test deck_hash_performance_test.cpp) add_executable(server_card_counter_test server_card_counter_test.cpp) add_executable(server_counter_test server_counter_test.cpp) add_executable(server_rate_limiter_test server_rate_limiter_test.cpp) -add_executable(warning_categories_test warning_categories_test.cpp) find_package(GTest) @@ -60,12 +56,10 @@ if(NOT GTEST_FOUND) add_dependencies(clamped_arithmetic_test gtest) add_dependencies(test_age_formatting gtest) add_dependencies(password_hash_test gtest) - add_dependencies(playmat_resolver_test gtest) add_dependencies(deck_hash_performance_test gtest) add_dependencies(server_card_counter_test gtest) add_dependencies(server_counter_test gtest) add_dependencies(server_rate_limiter_test gtest) - add_dependencies(warning_categories_test gtest) endif() include_directories(${GTEST_INCLUDE_DIRS}) @@ -80,10 +74,6 @@ target_link_libraries( target_link_libraries( password_hash_test libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES} ) -target_link_libraries( - playmat_resolver_test libcockatrice_deck_list libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES} - ${TEST_QT_MODULES} -) target_link_libraries( deck_hash_performance_test libcockatrice_deck_list libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES} @@ -97,9 +87,6 @@ target_link_libraries( target_link_libraries( server_rate_limiter_test libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES} ) -target_link_libraries( - warning_categories_test libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES} -) add_subdirectory(card_zone_algorithms) add_subdirectory(carddatabase) diff --git a/tests/playmat_resolver_test.cpp b/tests/playmat_resolver_test.cpp deleted file mode 100644 index a931c0647..000000000 --- a/tests/playmat_resolver_test.cpp +++ /dev/null @@ -1,126 +0,0 @@ -#include -#include -#include - -namespace -{ - -PlaymatInfo makePlaymatInfo(const QString &name, const QString &providerId = QString()) -{ - PlaymatInfo info; - info.card = {name, providerId}; - return info; -} - -} // namespace - -TEST(PlaymatResolverTest, EmptyChainReturnsEmpty) -{ - DeckList deck; - const PlaymatInfo resolved = resolveEffectivePlaymat(deck, {}, {}, PlaymatFallbackModeFixed, 0); - EXPECT_TRUE(resolved.card.isEmpty()); -} - -TEST(PlaymatResolverTest, OverrideWinsOverDeckAndFallback) -{ - DeckList deck; - deck.setPlaymat({{QStringLiteral("Deck Mat"), QStringLiteral("deck-provider")}, {}}); - - const PlaymatInfo force = makePlaymatInfo(QStringLiteral("Force Mat"), QStringLiteral("force-provider")); - const QList fallback = {makePlaymatInfo(QStringLiteral("Fallback Mat"))}; - - const PlaymatInfo resolved = resolveEffectivePlaymat(deck, force, fallback, PlaymatFallbackModeFixed, 0); - EXPECT_EQ(resolved.card.name, QStringLiteral("Force Mat")); - EXPECT_EQ(resolved.card.providerId, QStringLiteral("force-provider")); -} - -TEST(PlaymatResolverTest, DeckWinsOverFallback) -{ - DeckList deck; - deck.setPlaymat({{QStringLiteral("Deck Mat"), QStringLiteral("deck-provider")}, {0.1, 0.2, 0.3, 1.5}}); - - const QList fallback = {makePlaymatInfo(QStringLiteral("Fallback Mat"))}; - - const PlaymatInfo resolved = resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackModeFixed, 0); - EXPECT_EQ(resolved.card.name, QStringLiteral("Deck Mat")); - EXPECT_EQ(resolved.card.providerId, QStringLiteral("deck-provider")); - EXPECT_DOUBLE_EQ(resolved.params.marginPctL, 0.1); - EXPECT_DOUBLE_EQ(resolved.params.verticalOffset, 0.3); -} - -TEST(PlaymatResolverTest, FallbackUsedWhenDeckHasNone) -{ - DeckList deck; - const QList fallback = {makePlaymatInfo(QStringLiteral("Fallback Mat"))}; - - const PlaymatInfo resolved = resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackModeFixed, 0); - EXPECT_EQ(resolved.card.name, QStringLiteral("Fallback Mat")); -} - -TEST(PlaymatResolverTest, FixedAlwaysUsesFirst) -{ - DeckList deck; - const QList fallback = {makePlaymatInfo(QStringLiteral("First")), - makePlaymatInfo(QStringLiteral("Second"))}; - - for (int i = 0; i < 5; ++i) { - const PlaymatInfo resolved = resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackModeFixed, i); - EXPECT_EQ(resolved.card.name, QStringLiteral("First")); - } -} - -TEST(PlaymatResolverTest, RoundRobinCyclesAndWraps) -{ - DeckList deck; - const QList fallback = {makePlaymatInfo(QStringLiteral("First")), - makePlaymatInfo(QStringLiteral("Second")), - makePlaymatInfo(QStringLiteral("Third"))}; - - const QStringList expected = {QStringLiteral("First"), QStringLiteral("Second"), QStringLiteral("Third"), - QStringLiteral("First"), QStringLiteral("Second"), QStringLiteral("Third")}; - for (int i = 0; i < expected.size(); ++i) { - const PlaymatInfo resolved = resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackModeRoundRobin, i); - EXPECT_EQ(resolved.card.name, expected.at(i)); - } -} - -TEST(PlaymatResolverTest, RoundRobinRespectsCursor) -{ - DeckList deck; - const QList fallback = {makePlaymatInfo(QStringLiteral("First")), - makePlaymatInfo(QStringLiteral("Second"))}; - - const PlaymatInfo resolved = resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackModeRoundRobin, 5); - EXPECT_EQ(resolved.card.name, QStringLiteral("Second")); // 5 % 2 == 1 -} - -TEST(PlaymatResolverTest, RandomStaysWithinList) -{ - DeckList deck; - const QList fallback = {makePlaymatInfo(QStringLiteral("First")), - makePlaymatInfo(QStringLiteral("Second")), - makePlaymatInfo(QStringLiteral("Third"))}; - - for (int i = 0; i < 50; ++i) { - const PlaymatInfo resolved = resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackModeRandom, i); - ASSERT_FALSE(resolved.card.name.isEmpty()); - EXPECT_TRUE(fallback.contains(resolved)); - } -} - -TEST(PlaymatResolverTest, ForceWithEmptyCardIgnoresFallbackParamsButNotFallback) -{ - DeckList deck; - deck.setPlaymat({{QStringLiteral("Deck Mat")}, {}}); - - // An empty force entry must not mask the deck-configured playmat. - const PlaymatInfo emptyForce; - const PlaymatInfo resolved = resolveEffectivePlaymat(deck, emptyForce, {}, PlaymatFallbackModeFixed, 0); - EXPECT_EQ(resolved.card.name, QStringLiteral("Deck Mat")); -} - -int main(int argc, char **argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/tests/settings/settings_defaults_test.cpp b/tests/settings/settings_defaults_test.cpp index 6c79d5227..1a2fc1176 100644 --- a/tests/settings/settings_defaults_test.cpp +++ b/tests/settings/settings_defaults_test.cpp @@ -232,12 +232,6 @@ TEST_F(SettingsDefaultsTest, Tabs_AllTabsOpen_Default) ASSERT_EQ(s.getTabLogOpen(), true); } -TEST_F(SettingsDefaultsTest, Tabs_ModerationOpen_Default) -{ - TabsSettings s(settingsPath, nullptr); - ASSERT_EQ(s.getTabModerationOpen(), false); -} - // --- ChatSettings --- TEST_F(SettingsDefaultsTest, Chat_Mention_Default) diff --git a/tests/warning_categories_test.cpp b/tests/warning_categories_test.cpp deleted file mode 100644 index 432885dd9..000000000 --- a/tests/warning_categories_test.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include "gtest/gtest.h" -#include -#include - -TEST(WarningCategoriesTest, EmptyValueYieldsNoCategories) -{ - EXPECT_TRUE(parseWarningCategories(QString()).isEmpty()); - EXPECT_TRUE(parseWarningCategories(QString("")).isEmpty()); -} - -TEST(WarningCategoriesTest, PlainNamesDefaultToInterventionLevelOne) -{ - const QList categories = parseWarningCategories("Flaming,Spamming,Causing Drama"); - - ASSERT_EQ(3, categories.size()); - EXPECT_EQ("Flaming", categories.at(0).name); - EXPECT_EQ(1, categories.at(0).startingIl); - EXPECT_EQ("Spamming", categories.at(1).name); - EXPECT_EQ(1, categories.at(1).startingIl); - EXPECT_EQ("Causing Drama", categories.at(2).name); - EXPECT_EQ(1, categories.at(2).startingIl); -} - -TEST(WarningCategoriesTest, ExplicitInterventionLevelsAreParsed) -{ - const QList categories = parseWarningCategories("Cheating|2,Inappropriate Avatar|3"); - - ASSERT_EQ(2, categories.size()); - EXPECT_EQ("Cheating", categories.at(0).name); - EXPECT_EQ(2, categories.at(0).startingIl); - EXPECT_EQ("Inappropriate Avatar", categories.at(1).name); - EXPECT_EQ(3, categories.at(1).startingIl); -} - -TEST(WarningCategoriesTest, MixedEntriesKeepDefaultsForThoseWithoutLevels) -{ - const QList categories = parseWarningCategories("Abusive Language|1,Cheating|2,Spamming"); - - ASSERT_EQ(3, categories.size()); - EXPECT_EQ(1, categories.at(0).startingIl); - EXPECT_EQ(2, categories.at(1).startingIl); - EXPECT_EQ("Spamming", categories.at(2).name); - EXPECT_EQ(1, categories.at(2).startingIl); -} - -TEST(WarningCategoriesTest, EmptyEntriesAreSkipped) -{ - const QList categories = parseWarningCategories("Spamming,,Cheating|2,"); - - ASSERT_EQ(2, categories.size()); - EXPECT_EQ("Spamming", categories.at(0).name); - EXPECT_EQ("Cheating", categories.at(1).name); -} - -TEST(WarningCategoriesTest, WhitespaceIsTrimmed) -{ - const QList categories = parseWarningCategories(" Abusive Language , Cheating | 2 "); - - ASSERT_EQ(2, categories.size()); - EXPECT_EQ("Abusive Language", categories.at(0).name); - EXPECT_EQ(1, categories.at(0).startingIl); - EXPECT_EQ("Cheating", categories.at(1).name); - EXPECT_EQ(2, categories.at(1).startingIl); -} - -TEST(WarningCategoriesTest, InvalidInterventionLevelsFallBackToOne) -{ - const QList categories = parseWarningCategories("Spamming|abc,Cheating|0,Targeted Harassment|-3"); - - ASSERT_EQ(3, categories.size()); - EXPECT_EQ(1, categories.at(0).startingIl); - EXPECT_EQ(1, categories.at(1).startingIl); - EXPECT_EQ(1, categories.at(2).startingIl); -} - -TEST(WarningCategoriesTest, EntryWithOnlyLevelIsSkipped) -{ - const QList categories = parseWarningCategories("|2,Spamming|2"); - - ASSERT_EQ(1, categories.size()); - EXPECT_EQ("Spamming", categories.at(0).name); - EXPECT_EQ(2, categories.at(0).startingIl); -} - -int main(int argc, char **argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -}