| | |
| | |
| |
|
| | #include <future> |
| | #include <QColor> |
| | #include <QImage> |
| | #include <QList> |
| | #include <QLocale> |
| | #include <QMessageBox> |
| | #include <QMetaType> |
| | #include <QTime> |
| | #include <QtConcurrent/QtConcurrentRun> |
| | #include "common/logging/log.h" |
| | #include "common/settings.h" |
| | #include "core/core.h" |
| | #include "core/internal_network/network_interface.h" |
| | #include "network/announce_multiplayer_session.h" |
| | #include "ui_host_room.h" |
| | #include "yuzu/game_list_p.h" |
| | #include "yuzu/main.h" |
| | #include "yuzu/multiplayer/host_room.h" |
| | #include "yuzu/multiplayer/message.h" |
| | #include "yuzu/multiplayer/state.h" |
| | #include "yuzu/multiplayer/validation.h" |
| | #include "yuzu/uisettings.h" |
| | #ifdef ENABLE_WEB_SERVICE |
| | #include "web_service/verify_user_jwt.h" |
| | #endif |
| |
|
| | HostRoomWindow::HostRoomWindow(QWidget* parent, QStandardItemModel* list, |
| | std::shared_ptr<Core::AnnounceMultiplayerSession> session, |
| | Core::System& system_) |
| | : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), |
| | ui(std::make_unique<Ui::HostRoom>()), |
| | announce_multiplayer_session(session), system{system_}, room_network{ |
| | system.GetRoomNetwork()} { |
| | ui->setupUi(this); |
| |
|
| | |
| | ui->room_name->setValidator(validation.GetRoomName()); |
| | ui->username->setValidator(validation.GetNickname()); |
| | ui->port->setValidator(validation.GetPort()); |
| | ui->port->setPlaceholderText(QString::number(Network::DefaultRoomPort)); |
| |
|
| | |
| | game_list = new QStandardItemModel; |
| | UpdateGameList(list); |
| |
|
| | proxy = new ComboBoxProxyModel; |
| | proxy->setSourceModel(game_list); |
| | proxy->sort(0, Qt::AscendingOrder); |
| | ui->game_list->setModel(proxy); |
| |
|
| | |
| | connect(ui->host, &QPushButton::clicked, this, &HostRoomWindow::Host); |
| |
|
| | |
| | ui->username->setText( |
| | QString::fromStdString(UISettings::values.multiplayer_room_nickname.GetValue())); |
| | if (ui->username->text().isEmpty() && !Settings::values.yuzu_username.GetValue().empty()) { |
| | |
| | ui->username->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue())); |
| | } |
| | ui->room_name->setText( |
| | QString::fromStdString(UISettings::values.multiplayer_room_name.GetValue())); |
| | ui->port->setText(QString::number(UISettings::values.multiplayer_room_port.GetValue())); |
| | ui->max_player->setValue(UISettings::values.multiplayer_max_player.GetValue()); |
| | int index = UISettings::values.multiplayer_host_type.GetValue(); |
| | if (index < ui->host_type->count()) { |
| | ui->host_type->setCurrentIndex(index); |
| | } |
| | index = ui->game_list->findData(UISettings::values.multiplayer_game_id.GetValue(), |
| | GameListItemPath::ProgramIdRole); |
| | if (index != -1) { |
| | ui->game_list->setCurrentIndex(index); |
| | } |
| | ui->room_description->setText( |
| | QString::fromStdString(UISettings::values.multiplayer_room_description.GetValue())); |
| | } |
| |
|
| | HostRoomWindow::~HostRoomWindow() = default; |
| |
|
| | void HostRoomWindow::UpdateGameList(QStandardItemModel* list) { |
| | game_list->clear(); |
| | for (int i = 0; i < list->rowCount(); i++) { |
| | auto parent = list->item(i, 0); |
| | for (int j = 0; j < parent->rowCount(); j++) { |
| | game_list->appendRow(parent->child(j)->clone()); |
| | } |
| | } |
| | } |
| |
|
| | void HostRoomWindow::RetranslateUi() { |
| | ui->retranslateUi(this); |
| | } |
| |
|
| | std::unique_ptr<Network::VerifyUser::Backend> HostRoomWindow::CreateVerifyBackend( |
| | bool use_validation) const { |
| | std::unique_ptr<Network::VerifyUser::Backend> verify_backend; |
| | if (use_validation) { |
| | #ifdef ENABLE_WEB_SERVICE |
| | verify_backend = |
| | std::make_unique<WebService::VerifyUserJWT>(Settings::values.web_api_url.GetValue()); |
| | #else |
| | verify_backend = std::make_unique<Network::VerifyUser::NullBackend>(); |
| | #endif |
| | } else { |
| | verify_backend = std::make_unique<Network::VerifyUser::NullBackend>(); |
| | } |
| | return verify_backend; |
| | } |
| |
|
| | void HostRoomWindow::Host() { |
| | if (!Network::GetSelectedNetworkInterface()) { |
| | NetworkMessage::ErrorManager::ShowError( |
| | NetworkMessage::ErrorManager::NO_INTERFACE_SELECTED); |
| | return; |
| | } |
| | if (!ui->username->hasAcceptableInput()) { |
| | NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::USERNAME_NOT_VALID); |
| | return; |
| | } |
| | if (!ui->room_name->hasAcceptableInput()) { |
| | NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::ROOMNAME_NOT_VALID); |
| | return; |
| | } |
| | if (!ui->port->hasAcceptableInput()) { |
| | NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::PORT_NOT_VALID); |
| | return; |
| | } |
| | if (ui->game_list->currentIndex() == -1) { |
| | NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::GAME_NOT_SELECTED); |
| | return; |
| | } |
| | if (system.IsPoweredOn()) { |
| | if (!NetworkMessage::WarnGameRunning()) { |
| | return; |
| | } |
| | } |
| | if (auto member = room_network.GetRoomMember().lock()) { |
| | if (member->GetState() == Network::RoomMember::State::Joining) { |
| | return; |
| | } else if (member->IsConnected()) { |
| | auto parent = static_cast<MultiplayerState*>(parentWidget()); |
| | if (!parent->OnCloseRoom()) { |
| | close(); |
| | return; |
| | } |
| | } |
| | ui->host->setDisabled(true); |
| |
|
| | const AnnounceMultiplayerRoom::GameInfo game{ |
| | .name = ui->game_list->currentData(Qt::DisplayRole).toString().toStdString(), |
| | .id = ui->game_list->currentData(GameListItemPath::ProgramIdRole).toULongLong(), |
| | }; |
| | const auto port = |
| | ui->port->isModified() ? ui->port->text().toInt() : Network::DefaultRoomPort; |
| | const auto password = ui->password->text().toStdString(); |
| | const bool is_public = ui->host_type->currentIndex() == 0; |
| | Network::Room::BanList ban_list{}; |
| | if (ui->load_ban_list->isChecked()) { |
| | ban_list = UISettings::values.multiplayer_ban_list; |
| | } |
| | if (auto room = room_network.GetRoom().lock()) { |
| | const bool created = |
| | room->Create(ui->room_name->text().toStdString(), |
| | ui->room_description->toPlainText().toStdString(), "", port, password, |
| | ui->max_player->value(), Settings::values.yuzu_username.GetValue(), |
| | game, CreateVerifyBackend(is_public), ban_list); |
| | if (!created) { |
| | NetworkMessage::ErrorManager::ShowError( |
| | NetworkMessage::ErrorManager::COULD_NOT_CREATE_ROOM); |
| | LOG_ERROR(Network, "Could not create room!"); |
| | ui->host->setEnabled(true); |
| | return; |
| | } |
| | } |
| | |
| | if (is_public) { |
| | if (auto session = announce_multiplayer_session.lock()) { |
| | |
| | WebService::WebResult result = session->Register(); |
| | if (result.result_code != WebService::WebResult::Code::Success) { |
| | QMessageBox::warning( |
| | this, tr("Error"), |
| | tr("Failed to announce the room to the public lobby. In order to host a " |
| | "room publicly, you must have a valid yuzu account configured in " |
| | "Emulation -> Configure -> Web. If you do not want to publish a room in " |
| | "the public lobby, then select Unlisted instead.\nDebug Message: ") + |
| | QString::fromStdString(result.result_string), |
| | QMessageBox::Ok); |
| | ui->host->setEnabled(true); |
| | if (auto room = room_network.GetRoom().lock()) { |
| | room->Destroy(); |
| | } |
| | return; |
| | } |
| | session->Start(); |
| | } else { |
| | LOG_ERROR(Network, "Starting announce session failed"); |
| | } |
| | } |
| | std::string token; |
| | #ifdef ENABLE_WEB_SERVICE |
| | if (is_public) { |
| | WebService::Client client(Settings::values.web_api_url.GetValue(), |
| | Settings::values.yuzu_username.GetValue(), |
| | Settings::values.yuzu_token.GetValue()); |
| | if (auto room = room_network.GetRoom().lock()) { |
| | token = client.GetExternalJWT(room->GetVerifyUID()).returned_data; |
| | } |
| | if (token.empty()) { |
| | LOG_ERROR(WebService, "Could not get external JWT, verification may fail"); |
| | } else { |
| | LOG_INFO(WebService, "Successfully requested external JWT: size={}", token.size()); |
| | } |
| | } |
| | #endif |
| | |
| | member->Join(ui->username->text().toStdString(), "127.0.0.1", port, 0, |
| | Network::NoPreferredIP, password, token); |
| |
|
| | |
| | UISettings::values.multiplayer_room_nickname = ui->username->text().toStdString(); |
| | UISettings::values.multiplayer_room_name = ui->room_name->text().toStdString(); |
| | UISettings::values.multiplayer_game_id = |
| | ui->game_list->currentData(GameListItemPath::ProgramIdRole).toLongLong(); |
| | UISettings::values.multiplayer_max_player = ui->max_player->value(); |
| |
|
| | UISettings::values.multiplayer_host_type = ui->host_type->currentIndex(); |
| | if (ui->port->isModified() && !ui->port->text().isEmpty()) { |
| | UISettings::values.multiplayer_room_port = ui->port->text().toInt(); |
| | } else { |
| | UISettings::values.multiplayer_room_port = Network::DefaultRoomPort; |
| | } |
| | UISettings::values.multiplayer_room_description = |
| | ui->room_description->toPlainText().toStdString(); |
| | ui->host->setEnabled(true); |
| | emit SaveConfig(); |
| | close(); |
| | } |
| | } |
| |
|
| | QVariant ComboBoxProxyModel::data(const QModelIndex& idx, int role) const { |
| | if (role != Qt::DisplayRole) { |
| | auto val = QSortFilterProxyModel::data(idx, role); |
| | |
| | if (role == Qt::DecorationRole) |
| | val = val.value<QImage>().scaled(16, 16, Qt::KeepAspectRatio); |
| | return val; |
| | } |
| | std::string filename; |
| | Common::SplitPath( |
| | QSortFilterProxyModel::data(idx, GameListItemPath::FullPathRole).toString().toStdString(), |
| | nullptr, &filename, nullptr); |
| | QString title = QSortFilterProxyModel::data(idx, GameListItemPath::TitleRole).toString(); |
| | return title.isEmpty() ? QString::fromStdString(filename) : title; |
| | } |
| |
|
| | bool ComboBoxProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right) const { |
| | auto leftData = left.data(GameListItemPath::TitleRole).toString(); |
| | auto rightData = right.data(GameListItemPath::TitleRole).toString(); |
| | return leftData.compare(rightData) < 0; |
| | } |
| |
|