summaryrefslogtreecommitdiff
path: root/src/yuzu/multiplayer/chat_room.cpp
diff options
context:
space:
mode:
authorZephyron <zephyron@citron-emu.org>2024-12-31 16:19:25 +1000
committerZephyron <zephyron@citron-emu.org>2024-12-31 16:19:25 +1000
commit9427e27e24a7135880ee2881c3c44988e174b41a (patch)
tree83f0062a35be144f6b162eaa823c5b3c7620146e /src/yuzu/multiplayer/chat_room.cpp
parentb35ae725d20960411e8588b11c12a2d55f86c9d0 (diff)
chore: update project branding to citron
Diffstat (limited to 'src/yuzu/multiplayer/chat_room.cpp')
-rw-r--r--src/yuzu/multiplayer/chat_room.cpp508
1 files changed, 0 insertions, 508 deletions
diff --git a/src/yuzu/multiplayer/chat_room.cpp b/src/yuzu/multiplayer/chat_room.cpp
deleted file mode 100644
index 4463616b4..000000000
--- a/src/yuzu/multiplayer/chat_room.cpp
+++ /dev/null
@@ -1,508 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include <array>
-#include <future>
-#include <QColor>
-#include <QDesktopServices>
-#include <QFutureWatcher>
-#include <QImage>
-#include <QList>
-#include <QLocale>
-#include <QMenu>
-#include <QMessageBox>
-#include <QMetaType>
-#include <QTime>
-#include <QUrl>
-#include <QtConcurrent/QtConcurrentRun>
-#include "common/logging/log.h"
-#include "network/announce_multiplayer_session.h"
-#include "ui_chat_room.h"
-#include "yuzu/game_list_p.h"
-#include "yuzu/multiplayer/chat_room.h"
-#include "yuzu/multiplayer/message.h"
-#ifdef ENABLE_WEB_SERVICE
-#include "web_service/web_backend.h"
-#endif
-
-class ChatMessage {
-public:
- explicit ChatMessage(const Network::ChatEntry& chat, Network::RoomNetwork& room_network,
- QTime ts = {}) {
- /// Convert the time to their default locale defined format
- QLocale locale;
- timestamp = locale.toString(ts.isValid() ? ts : QTime::currentTime(), QLocale::ShortFormat);
- nickname = QString::fromStdString(chat.nickname);
- username = QString::fromStdString(chat.username);
- message = QString::fromStdString(chat.message);
-
- // Check for user pings
- QString cur_nickname, cur_username;
- if (auto room = room_network.GetRoomMember().lock()) {
- cur_nickname = QString::fromStdString(room->GetNickname());
- cur_username = QString::fromStdString(room->GetUsername());
- }
-
- // Handle pings at the beginning and end of message
- QString fixed_message = QStringLiteral(" %1 ").arg(message);
- if (fixed_message.contains(QStringLiteral(" @%1 ").arg(cur_nickname)) ||
- (!cur_username.isEmpty() &&
- fixed_message.contains(QStringLiteral(" @%1 ").arg(cur_username)))) {
-
- contains_ping = true;
- } else {
- contains_ping = false;
- }
- }
-
- bool ContainsPing() const {
- return contains_ping;
- }
-
- /// Format the message using the players color
- QString GetPlayerChatMessage(u16 player) const {
- const bool is_dark_theme = QIcon::themeName().contains(QStringLiteral("dark")) ||
- QIcon::themeName().contains(QStringLiteral("midnight"));
- auto color =
- is_dark_theme ? player_color_dark[player % 16] : player_color_default[player % 16];
- QString name;
- if (username.isEmpty() || username == nickname) {
- name = nickname;
- } else {
- name = QStringLiteral("%1 (%2)").arg(nickname, username);
- }
-
- QString style, text_color;
- if (ContainsPing()) {
- // Add a background color to these messages
- style = QStringLiteral("background-color: %1").arg(QString::fromStdString(ping_color));
- // Add a font color
- text_color = QStringLiteral("color='#000000'");
- }
-
- return QStringLiteral("[%1] <font color='%2'>&lt;%3&gt;</font> <font style='%4' "
- "%5>%6</font>")
- .arg(timestamp, QString::fromStdString(color), name.toHtmlEscaped(), style, text_color,
- message.toHtmlEscaped());
- }
-
-private:
- static constexpr std::array<const char*, 16> player_color_default = {
- {"#0000FF", "#FF0000", "#8A2BE2", "#FF69B4", "#1E90FF", "#008000", "#00FF7F", "#B22222",
- "#DAA520", "#FF4500", "#2E8B57", "#5F9EA0", "#D2691E", "#9ACD32", "#FF7F50", "#FFFF00"}};
- static constexpr std::array<const char*, 16> player_color_dark = {
- {"#559AD1", "#4EC9A8", "#D69D85", "#C6C923", "#B975B5", "#D81F1F", "#7EAE39", "#4F8733",
- "#F7CD8A", "#6FCACF", "#CE4897", "#8A2BE2", "#D2691E", "#9ACD32", "#FF7F50", "#152ccd"}};
- static constexpr char ping_color[] = "#FFFF00";
-
- QString timestamp;
- QString nickname;
- QString username;
- QString message;
- bool contains_ping;
-};
-
-class StatusMessage {
-public:
- explicit StatusMessage(const QString& msg, QTime ts = {}) {
- /// Convert the time to their default locale defined format
- QLocale locale;
- timestamp = locale.toString(ts.isValid() ? ts : QTime::currentTime(), QLocale::ShortFormat);
- message = msg;
- }
-
- QString GetSystemChatMessage() const {
- return QStringLiteral("[%1] <font color='%2'>* %3</font>")
- .arg(timestamp, QString::fromStdString(system_color), message);
- }
-
-private:
- static constexpr const char system_color[] = "#FF8C00";
- QString timestamp;
- QString message;
-};
-
-class PlayerListItem : public QStandardItem {
-public:
- static const int NicknameRole = Qt::UserRole + 1;
- static const int UsernameRole = Qt::UserRole + 2;
- static const int AvatarUrlRole = Qt::UserRole + 3;
- static const int GameNameRole = Qt::UserRole + 4;
- static const int GameVersionRole = Qt::UserRole + 5;
-
- PlayerListItem() = default;
- explicit PlayerListItem(const std::string& nickname, const std::string& username,
- const std::string& avatar_url,
- const AnnounceMultiplayerRoom::GameInfo& game_info) {
- setEditable(false);
- setData(QString::fromStdString(nickname), NicknameRole);
- setData(QString::fromStdString(username), UsernameRole);
- setData(QString::fromStdString(avatar_url), AvatarUrlRole);
- if (game_info.name.empty()) {
- setData(QObject::tr("Not playing a game"), GameNameRole);
- } else {
- setData(QString::fromStdString(game_info.name), GameNameRole);
- }
- setData(QString::fromStdString(game_info.version), GameVersionRole);
- }
-
- QVariant data(int role) const override {
- if (role != Qt::DisplayRole) {
- return QStandardItem::data(role);
- }
- QString name;
- const QString nickname = data(NicknameRole).toString();
- const QString username = data(UsernameRole).toString();
- if (username.isEmpty() || username == nickname) {
- name = nickname;
- } else {
- name = QStringLiteral("%1 (%2)").arg(nickname, username);
- }
- const QString version = data(GameVersionRole).toString();
- QString version_string;
- if (!version.isEmpty()) {
- version_string = QStringLiteral("(%1)").arg(version);
- }
- return QStringLiteral("%1\n %2 %3")
- .arg(name, data(GameNameRole).toString(), version_string);
- }
-};
-
-ChatRoom::ChatRoom(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::ChatRoom>()) {
- ui->setupUi(this);
-
- // set the item_model for player_view
-
- player_list = new QStandardItemModel(ui->player_view);
- ui->player_view->setModel(player_list);
- ui->player_view->setContextMenuPolicy(Qt::CustomContextMenu);
- // set a header to make it look better though there is only one column
- player_list->insertColumns(0, 1);
- player_list->setHeaderData(0, Qt::Horizontal, tr("Members"));
-
- ui->chat_history->document()->setMaximumBlockCount(max_chat_lines);
-
- auto font = ui->chat_history->font();
- font.setPointSizeF(10);
- ui->chat_history->setFont(font);
-
- // register the network structs to use in slots and signals
- qRegisterMetaType<Network::ChatEntry>();
- qRegisterMetaType<Network::StatusMessageEntry>();
- qRegisterMetaType<Network::RoomInformation>();
- qRegisterMetaType<Network::RoomMember::State>();
-
- // Connect all the widgets to the appropriate events
- connect(ui->player_view, &QTreeView::customContextMenuRequested, this,
- &ChatRoom::PopupContextMenu);
- connect(ui->chat_message, &QLineEdit::returnPressed, this, &ChatRoom::OnSendChat);
- connect(ui->chat_message, &QLineEdit::textChanged, this, &ChatRoom::OnChatTextChanged);
- connect(ui->send_message, &QPushButton::clicked, this, &ChatRoom::OnSendChat);
-}
-
-ChatRoom::~ChatRoom() = default;
-
-void ChatRoom::Initialize(Network::RoomNetwork* room_network_) {
- room_network = room_network_;
- // setup the callbacks for network updates
- if (auto member = room_network->GetRoomMember().lock()) {
- member->BindOnChatMessageReceived(
- [this](const Network::ChatEntry& chat) { emit ChatReceived(chat); });
- member->BindOnStatusMessageReceived(
- [this](const Network::StatusMessageEntry& status_message) {
- emit StatusMessageReceived(status_message);
- });
- connect(this, &ChatRoom::ChatReceived, this, &ChatRoom::OnChatReceive);
- connect(this, &ChatRoom::StatusMessageReceived, this, &ChatRoom::OnStatusMessageReceive);
- }
-}
-
-void ChatRoom::SetModPerms(bool is_mod) {
- has_mod_perms = is_mod;
-}
-
-void ChatRoom::RetranslateUi() {
- ui->retranslateUi(this);
-}
-
-void ChatRoom::Clear() {
- ui->chat_history->clear();
- block_list.clear();
-}
-
-void ChatRoom::AppendStatusMessage(const QString& msg) {
- ui->chat_history->append(StatusMessage(msg).GetSystemChatMessage());
-}
-
-void ChatRoom::AppendChatMessage(const QString& msg) {
- ui->chat_history->append(msg);
-}
-
-void ChatRoom::SendModerationRequest(Network::RoomMessageTypes type, const std::string& nickname) {
- if (auto room = room_network->GetRoomMember().lock()) {
- auto members = room->GetMemberInformation();
- auto it = std::find_if(members.begin(), members.end(),
- [&nickname](const Network::RoomMember::MemberInformation& member) {
- return member.nickname == nickname;
- });
- if (it == members.end()) {
- NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::NO_SUCH_USER);
- return;
- }
- room->SendModerationRequest(type, nickname);
- }
-}
-
-bool ChatRoom::ValidateMessage(const std::string& msg) {
- return !msg.empty();
-}
-
-void ChatRoom::OnRoomUpdate(const Network::RoomInformation& info) {
- // TODO(B3N30): change title
- if (auto room_member = room_network->GetRoomMember().lock()) {
- SetPlayerList(room_member->GetMemberInformation());
- }
-}
-
-void ChatRoom::Disable() {
- ui->send_message->setDisabled(true);
- ui->chat_message->setDisabled(true);
-}
-
-void ChatRoom::Enable() {
- ui->send_message->setEnabled(true);
- ui->chat_message->setEnabled(true);
-}
-
-void ChatRoom::OnChatReceive(const Network::ChatEntry& chat) {
- if (!ValidateMessage(chat.message)) {
- return;
- }
- if (auto room = room_network->GetRoomMember().lock()) {
- // get the id of the player
- auto members = room->GetMemberInformation();
- auto it = std::find_if(members.begin(), members.end(),
- [&chat](const Network::RoomMember::MemberInformation& member) {
- return member.nickname == chat.nickname &&
- member.username == chat.username;
- });
- if (it == members.end()) {
- LOG_INFO(Network, "Chat message received from unknown player. Ignoring it.");
- return;
- }
- if (block_list.count(chat.nickname)) {
- LOG_INFO(Network, "Chat message received from blocked player {}. Ignoring it.",
- chat.nickname);
- return;
- }
- auto player = std::distance(members.begin(), it);
- ChatMessage m(chat, *room_network);
- if (m.ContainsPing()) {
- emit UserPinged();
- }
- AppendChatMessage(m.GetPlayerChatMessage(player));
- }
-}
-
-void ChatRoom::OnStatusMessageReceive(const Network::StatusMessageEntry& status_message) {
- QString name;
- if (status_message.username.empty() || status_message.username == status_message.nickname) {
- name = QString::fromStdString(status_message.nickname);
- } else {
- name = QStringLiteral("%1 (%2)").arg(QString::fromStdString(status_message.nickname),
- QString::fromStdString(status_message.username));
- }
- QString message;
- switch (status_message.type) {
- case Network::IdMemberJoin:
- message = tr("%1 has joined").arg(name);
- break;
- case Network::IdMemberLeave:
- message = tr("%1 has left").arg(name);
- break;
- case Network::IdMemberKicked:
- message = tr("%1 has been kicked").arg(name);
- break;
- case Network::IdMemberBanned:
- message = tr("%1 has been banned").arg(name);
- break;
- case Network::IdAddressUnbanned:
- message = tr("%1 has been unbanned").arg(name);
- break;
- }
- if (!message.isEmpty())
- AppendStatusMessage(message);
-}
-
-void ChatRoom::OnSendChat() {
- if (auto room_member = room_network->GetRoomMember().lock()) {
- if (!room_member->IsConnected()) {
- return;
- }
- auto message = ui->chat_message->text().toStdString();
- if (!ValidateMessage(message)) {
- return;
- }
- auto nick = room_member->GetNickname();
- auto username = room_member->GetUsername();
- Network::ChatEntry chat{nick, username, message};
-
- auto members = room_member->GetMemberInformation();
- auto it = std::find_if(members.begin(), members.end(),
- [&chat](const Network::RoomMember::MemberInformation& member) {
- return member.nickname == chat.nickname &&
- member.username == chat.username;
- });
- if (it == members.end()) {
- LOG_INFO(Network, "Cannot find self in the player list when sending a message.");
- }
- auto player = std::distance(members.begin(), it);
- ChatMessage m(chat, *room_network);
- room_member->SendChatMessage(message);
- AppendChatMessage(m.GetPlayerChatMessage(player));
- ui->chat_message->clear();
- }
-}
-
-void ChatRoom::UpdateIconDisplay() {
- for (int row = 0; row < player_list->invisibleRootItem()->rowCount(); ++row) {
- QStandardItem* item = player_list->invisibleRootItem()->child(row);
- const std::string avatar_url =
- item->data(PlayerListItem::AvatarUrlRole).toString().toStdString();
- if (icon_cache.count(avatar_url)) {
- item->setData(icon_cache.at(avatar_url), Qt::DecorationRole);
- } else {
- item->setData(QIcon::fromTheme(QStringLiteral("no_avatar")).pixmap(48),
- Qt::DecorationRole);
- }
- }
-}
-
-void ChatRoom::SetPlayerList(const Network::RoomMember::MemberList& member_list) {
- // TODO(B3N30): Remember which row is selected
- player_list->removeRows(0, player_list->rowCount());
- for (const auto& member : member_list) {
- if (member.nickname.empty())
- continue;
- QStandardItem* name_item = new PlayerListItem(member.nickname, member.username,
- member.avatar_url, member.game_info);
-
-#ifdef ENABLE_WEB_SERVICE
- if (!icon_cache.count(member.avatar_url) && !member.avatar_url.empty()) {
- // Start a request to get the member's avatar
- const QUrl url(QString::fromStdString(member.avatar_url));
- QFuture<std::string> future = QtConcurrent::run([url] {
- WebService::Client client(
- QStringLiteral("%1://%2").arg(url.scheme(), url.host()).toStdString(), "", "");
- auto result = client.GetImage(url.path().toStdString(), true);
- if (result.returned_data.empty()) {
- LOG_ERROR(WebService, "Failed to get avatar");
- }
- return result.returned_data;
- });
- auto* future_watcher = new QFutureWatcher<std::string>(this);
- connect(future_watcher, &QFutureWatcher<std::string>::finished, this,
- [this, future_watcher, avatar_url = member.avatar_url] {
- const std::string result = future_watcher->result();
- if (result.empty())
- return;
- QPixmap pixmap;
- if (!pixmap.loadFromData(reinterpret_cast<const u8*>(result.data()),
- static_cast<uint>(result.size())))
- return;
- icon_cache[avatar_url] =
- pixmap.scaled(48, 48, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
- // Update all the displayed icons with the new icon_cache
- UpdateIconDisplay();
- });
- future_watcher->setFuture(future);
- }
-#endif
-
- player_list->invisibleRootItem()->appendRow(name_item);
- }
- UpdateIconDisplay();
- // TODO(B3N30): Restore row selection
-}
-
-void ChatRoom::OnChatTextChanged() {
- if (ui->chat_message->text().length() > static_cast<int>(Network::MaxMessageSize))
- ui->chat_message->setText(
- ui->chat_message->text().left(static_cast<int>(Network::MaxMessageSize)));
-}
-
-void ChatRoom::PopupContextMenu(const QPoint& menu_location) {
- QModelIndex item = ui->player_view->indexAt(menu_location);
- if (!item.isValid())
- return;
-
- std::string nickname =
- player_list->item(item.row())->data(PlayerListItem::NicknameRole).toString().toStdString();
-
- QMenu context_menu;
-
- QString username = player_list->item(item.row())->data(PlayerListItem::UsernameRole).toString();
- if (!username.isEmpty()) {
- QAction* view_profile_action = context_menu.addAction(tr("View Profile"));
- connect(view_profile_action, &QAction::triggered, [username] {
- QDesktopServices::openUrl(
- QUrl(QStringLiteral("https://community.citra-emu.org/u/%1").arg(username)));
- });
- }
-
- std::string cur_nickname;
- if (auto room = room_network->GetRoomMember().lock()) {
- cur_nickname = room->GetNickname();
- }
-
- if (nickname != cur_nickname) { // You can't block yourself
- QAction* block_action = context_menu.addAction(tr("Block Player"));
-
- block_action->setCheckable(true);
- block_action->setChecked(block_list.count(nickname) > 0);
-
- connect(block_action, &QAction::triggered, [this, nickname] {
- if (block_list.count(nickname)) {
- block_list.erase(nickname);
- } else {
- QMessageBox::StandardButton result = QMessageBox::question(
- this, tr("Block Player"),
- tr("When you block a player, you will no longer receive chat messages from "
- "them.<br><br>Are you sure you would like to block %1?")
- .arg(QString::fromStdString(nickname)),
- QMessageBox::Yes | QMessageBox::No);
- if (result == QMessageBox::Yes)
- block_list.emplace(nickname);
- }
- });
- }
-
- if (has_mod_perms && nickname != cur_nickname) { // You can't kick or ban yourself
- context_menu.addSeparator();
-
- QAction* kick_action = context_menu.addAction(tr("Kick"));
- QAction* ban_action = context_menu.addAction(tr("Ban"));
-
- connect(kick_action, &QAction::triggered, [this, nickname] {
- QMessageBox::StandardButton result =
- QMessageBox::question(this, tr("Kick Player"),
- tr("Are you sure you would like to <b>kick</b> %1?")
- .arg(QString::fromStdString(nickname)),
- QMessageBox::Yes | QMessageBox::No);
- if (result == QMessageBox::Yes)
- SendModerationRequest(Network::IdModKick, nickname);
- });
- connect(ban_action, &QAction::triggered, [this, nickname] {
- QMessageBox::StandardButton result = QMessageBox::question(
- this, tr("Ban Player"),
- tr("Are you sure you would like to <b>kick and ban</b> %1?\n\nThis would "
- "ban both their forum username and their IP address.")
- .arg(QString::fromStdString(nickname)),
- QMessageBox::Yes | QMessageBox::No);
- if (result == QMessageBox::Yes)
- SendModerationRequest(Network::IdModBan, nickname);
- });
- }
-
- context_menu.exec(ui->player_view->viewport()->mapToGlobal(menu_location));
-}