summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/common/hex_util.h3
-rw-r--r--src/core/hle/service/filesystem/fsp/fsp_srv.cpp9
-rw-r--r--src/hid_core/resource_manager.cpp88
-rw-r--r--src/hid_core/resource_manager.h9
-rw-r--r--src/hid_core/resources/digitizer/digitizer.cpp4
-rw-r--r--src/hid_core/resources/digitizer/digitizer.h3
-rw-r--r--src/hid_core/resources/npad/npad.cpp18
-rw-r--r--src/hid_core/resources/npad/npad.h1
-rw-r--r--src/hid_core/resources/unique_pad/unique_pad.cpp4
-rw-r--r--src/hid_core/resources/unique_pad/unique_pad.h3
-rw-r--r--src/yuzu/main.cpp7
-rw-r--r--src/yuzu/multiplayer/lobby.cpp13
-rw-r--r--src/yuzu/multiplayer/lobby_p.h25
-rw-r--r--src/yuzu/uisettings.h14
14 files changed, 129 insertions, 72 deletions
diff --git a/src/common/hex_util.h b/src/common/hex_util.h
index a00904939..618f53152 100644
--- a/src/common/hex_util.h
+++ b/src/common/hex_util.h
@@ -9,6 +9,7 @@
#include <string>
#include <vector>
#include <fmt/format.h>
+#include "common/assert.h"
#include "common/common_types.h"
namespace Common {
@@ -29,6 +30,8 @@ namespace Common {
template <std::size_t Size, bool le = false>
[[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
+ ASSERT_MSG(Size * 2 <= str.size(), "Invalid string size");
+
std::array<u8, Size> out{};
if constexpr (le) {
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
diff --git a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp
index 5fe534c73..63c2d3a58 100644
--- a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp
@@ -115,6 +115,11 @@ private:
if (type->GetName() == "save") {
for (const auto& save_id : type->GetSubdirectories()) {
for (const auto& user_id : save_id->GetSubdirectories()) {
+ // Skip non user id subdirectories
+ if (user_id->GetName().size() != 0x20) {
+ continue;
+ }
+
const auto save_id_numeric = stoull_be(save_id->GetName());
auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName());
std::reverse(user_id_numeric.begin(), user_id_numeric.end());
@@ -160,6 +165,10 @@ private:
} else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) {
// Temporary Storage
for (const auto& user_id : type->GetSubdirectories()) {
+ // Skip non user id subdirectories
+ if (user_id->GetName().size() != 0x20) {
+ continue;
+ }
for (const auto& title_id : user_id->GetSubdirectories()) {
if (!title_id->GetFiles().empty() ||
!title_id->GetSubdirectories().empty()) {
diff --git a/src/hid_core/resource_manager.cpp b/src/hid_core/resource_manager.cpp
index 68ce2c7ae..245da582e 100644
--- a/src/hid_core/resource_manager.cpp
+++ b/src/hid_core/resource_manager.cpp
@@ -52,9 +52,42 @@ ResourceManager::ResourceManager(Core::System& system_,
std::shared_ptr<HidFirmwareSettings> settings)
: firmware_settings{settings}, system{system_}, service_context{system_, "hid"} {
applet_resource = std::make_shared<AppletResource>(system);
+
+ // Register update callbacks
+ npad_update_event = Core::Timing::CreateEvent("HID::UpdatePadCallback",
+ [this](s64 time, std::chrono::nanoseconds ns_late)
+ -> std::optional<std::chrono::nanoseconds> {
+ UpdateNpad(ns_late);
+ return std::nullopt;
+ });
+ default_update_event = Core::Timing::CreateEvent(
+ "HID::UpdateDefaultCallback",
+ [this](s64 time,
+ std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
+ UpdateControllers(ns_late);
+ return std::nullopt;
+ });
+ mouse_keyboard_update_event = Core::Timing::CreateEvent(
+ "HID::UpdateMouseKeyboardCallback",
+ [this](s64 time,
+ std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
+ UpdateMouseKeyboard(ns_late);
+ return std::nullopt;
+ });
+ motion_update_event = Core::Timing::CreateEvent(
+ "HID::UpdateMotionCallback",
+ [this](s64 time,
+ std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
+ UpdateMotion(ns_late);
+ return std::nullopt;
+ });
}
ResourceManager::~ResourceManager() {
+ system.CoreTiming().UnscheduleEvent(npad_update_event);
+ system.CoreTiming().UnscheduleEvent(default_update_event);
+ system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event);
+ system.CoreTiming().UnscheduleEvent(motion_update_event);
system.CoreTiming().UnscheduleEvent(touch_update_event);
input_event->Finalize();
};
@@ -201,6 +234,7 @@ void ResourceManager::InitializeHidCommonSampler() {
debug_pad->SetAppletResource(applet_resource, &shared_mutex);
digitizer->SetAppletResource(applet_resource, &shared_mutex);
+ unique_pad->SetAppletResource(applet_resource, &shared_mutex);
keyboard->SetAppletResource(applet_resource, &shared_mutex);
const auto settings =
@@ -214,6 +248,14 @@ void ResourceManager::InitializeHidCommonSampler() {
home_button->SetAppletResource(applet_resource, &shared_mutex);
sleep_button->SetAppletResource(applet_resource, &shared_mutex);
capture_button->SetAppletResource(applet_resource, &shared_mutex);
+
+ system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event);
+ system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns,
+ default_update_event);
+ system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns,
+ mouse_keyboard_update_event);
+ system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns,
+ motion_update_event);
}
void ResourceManager::InitializeTouchScreenSampler() {
@@ -465,55 +507,9 @@ IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<Resource
{0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
};
RegisterHandlers(functions);
-
- // Register update callbacks
- npad_update_event = Core::Timing::CreateEvent(
- "HID::UpdatePadCallback",
- [this, resource](
- s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
- const auto guard = LockService();
- resource->UpdateNpad(ns_late);
- return std::nullopt;
- });
- default_update_event = Core::Timing::CreateEvent(
- "HID::UpdateDefaultCallback",
- [this, resource](
- s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
- const auto guard = LockService();
- resource->UpdateControllers(ns_late);
- return std::nullopt;
- });
- mouse_keyboard_update_event = Core::Timing::CreateEvent(
- "HID::UpdateMouseKeyboardCallback",
- [this, resource](
- s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
- const auto guard = LockService();
- resource->UpdateMouseKeyboard(ns_late);
- return std::nullopt;
- });
- motion_update_event = Core::Timing::CreateEvent(
- "HID::UpdateMotionCallback",
- [this, resource](
- s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
- const auto guard = LockService();
- resource->UpdateMotion(ns_late);
- return std::nullopt;
- });
-
- system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event);
- system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns,
- default_update_event);
- system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns,
- mouse_keyboard_update_event);
- system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns,
- motion_update_event);
}
IAppletResource::~IAppletResource() {
- system.CoreTiming().UnscheduleEvent(npad_update_event);
- system.CoreTiming().UnscheduleEvent(default_update_event);
- system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event);
- system.CoreTiming().UnscheduleEvent(motion_update_event);
resource_manager->FreeAppletResourceId(aruid);
}
diff --git a/src/hid_core/resource_manager.h b/src/hid_core/resource_manager.h
index 0bfe09511..dc3ff01f8 100644
--- a/src/hid_core/resource_manager.h
+++ b/src/hid_core/resource_manager.h
@@ -147,6 +147,10 @@ private:
std::shared_ptr<SixAxis> six_axis{nullptr};
std::shared_ptr<SleepButton> sleep_button{nullptr};
std::shared_ptr<UniquePad> unique_pad{nullptr};
+ std::shared_ptr<Core::Timing::EventType> npad_update_event;
+ std::shared_ptr<Core::Timing::EventType> default_update_event;
+ std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
+ std::shared_ptr<Core::Timing::EventType> motion_update_event;
// TODO: Create these resources
// std::shared_ptr<AudioControl> audio_control{nullptr};
@@ -179,11 +183,6 @@ public:
private:
void GetSharedMemoryHandle(HLERequestContext& ctx);
- std::shared_ptr<Core::Timing::EventType> npad_update_event{nullptr};
- std::shared_ptr<Core::Timing::EventType> default_update_event{nullptr};
- std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event{nullptr};
- std::shared_ptr<Core::Timing::EventType> motion_update_event{nullptr};
-
u64 aruid{};
std::shared_ptr<ResourceManager> resource_manager;
};
diff --git a/src/hid_core/resources/digitizer/digitizer.cpp b/src/hid_core/resources/digitizer/digitizer.cpp
index cd72fd6e5..5d7dcadfe 100644
--- a/src/hid_core/resources/digitizer/digitizer.cpp
+++ b/src/hid_core/resources/digitizer/digitizer.cpp
@@ -17,10 +17,6 @@ void Digitizer::OnInit() {}
void Digitizer::OnRelease() {}
void Digitizer::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
- if (!smart_update) {
- return;
- }
-
std::scoped_lock shared_lock{*shared_mutex};
const u64 aruid = applet_resource->GetActiveAruid();
auto* data = applet_resource->GetAruidData(aruid);
diff --git a/src/hid_core/resources/digitizer/digitizer.h b/src/hid_core/resources/digitizer/digitizer.h
index e031a16b0..68b03111c 100644
--- a/src/hid_core/resources/digitizer/digitizer.h
+++ b/src/hid_core/resources/digitizer/digitizer.h
@@ -20,8 +20,5 @@ public:
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
-
-private:
- bool smart_update{};
};
} // namespace Service::HID
diff --git a/src/hid_core/resources/npad/npad.cpp b/src/hid_core/resources/npad/npad.cpp
index 1a58eff4a..fe3fdc5cd 100644
--- a/src/hid_core/resources/npad/npad.cpp
+++ b/src/hid_core/resources/npad/npad.cpp
@@ -102,6 +102,8 @@ Result NPad::Activate(u64 aruid) {
for (std::size_t i = 0; i < 19; ++i) {
WriteEmptyEntry(npad);
}
+
+ controller.is_active = true;
}
return ResultSuccess;
@@ -467,6 +469,13 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
continue;
}
+ bool is_set{};
+ npad_resource.IsSupportedNpadStyleSet(is_set, aruid);
+ // Wait until style is defined
+ if (!is_set) {
+ continue;
+ }
+
for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
auto& controller = controller_data[aruid_index][i];
controller.shared_memory =
@@ -484,6 +493,10 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
continue;
}
+ if (!controller.is_active) {
+ continue;
+ }
+
RequestPadStateUpdate(aruid, controller.device->GetNpadIdType());
auto& pad_state = controller.npad_pad_state;
auto& libnx_state = controller.npad_libnx_state;
@@ -592,7 +605,9 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw;
libnx_state.l_stick = pad_state.l_stick;
libnx_state.r_stick = pad_state.r_stick;
- npad->system_ext_lifo.WriteNextEntry(pad_state);
+ libnx_state.sampling_number =
+ npad->system_ext_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->system_ext_lifo.WriteNextEntry(libnx_state);
press_state |= static_cast<u64>(pad_state.npad_buttons.raw);
}
@@ -1060,6 +1075,7 @@ void NPad::UnregisterAppletResourceUserId(u64 aruid) {
// TODO: Remove this once abstract pad is emulated properly
const auto aruid_index = npad_resource.GetIndexFromAruid(aruid);
for (auto& controller : controller_data[aruid_index]) {
+ controller.is_active = false;
controller.is_connected = false;
controller.shared_memory = nullptr;
}
diff --git a/src/hid_core/resources/npad/npad.h b/src/hid_core/resources/npad/npad.h
index 4e26ed2e8..c63488346 100644
--- a/src/hid_core/resources/npad/npad.h
+++ b/src/hid_core/resources/npad/npad.h
@@ -164,6 +164,7 @@ private:
NpadInternalState* shared_memory = nullptr;
Core::HID::EmulatedController* device = nullptr;
+ bool is_active{};
bool is_connected{};
// Dual joycons can have only one side connected
diff --git a/src/hid_core/resources/unique_pad/unique_pad.cpp b/src/hid_core/resources/unique_pad/unique_pad.cpp
index 89fc57269..b2db55c5a 100644
--- a/src/hid_core/resources/unique_pad/unique_pad.cpp
+++ b/src/hid_core/resources/unique_pad/unique_pad.cpp
@@ -17,10 +17,6 @@ void UniquePad::OnInit() {}
void UniquePad::OnRelease() {}
void UniquePad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
- if (!smart_update) {
- return;
- }
-
const u64 aruid = applet_resource->GetActiveAruid();
auto* data = applet_resource->GetAruidData(aruid);
diff --git a/src/hid_core/resources/unique_pad/unique_pad.h b/src/hid_core/resources/unique_pad/unique_pad.h
index 674ad1691..4873b7f7e 100644
--- a/src/hid_core/resources/unique_pad/unique_pad.h
+++ b/src/hid_core/resources/unique_pad/unique_pad.h
@@ -20,8 +20,5 @@ public:
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
-
-private:
- bool smart_update{};
};
} // namespace Service::HID
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 303d84a1f..13381fea8 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1353,6 +1353,13 @@ void GMainWindow::InitializeHotkeys() {
LinkActionShortcut(ui->action_TAS_Start, QStringLiteral("TAS Start/Stop"), true);
LinkActionShortcut(ui->action_TAS_Record, QStringLiteral("TAS Record"), true);
LinkActionShortcut(ui->action_TAS_Reset, QStringLiteral("TAS Reset"), true);
+ LinkActionShortcut(ui->action_View_Lobby,
+ QStringLiteral("Multiplayer Browse Public Game Lobby"));
+ LinkActionShortcut(ui->action_Start_Room, QStringLiteral("Multiplayer Create Room"));
+ LinkActionShortcut(ui->action_Connect_To_Room,
+ QStringLiteral("Multiplayer Direct Connect to Room"));
+ LinkActionShortcut(ui->action_Show_Room, QStringLiteral("Multiplayer Show Current Room"));
+ LinkActionShortcut(ui->action_Leave_Room, QStringLiteral("Multiplayer Leave Room"));
static const QString main_window = QStringLiteral("Main Window");
const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) {
diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp
index 41692c05b..77ac84295 100644
--- a/src/yuzu/multiplayer/lobby.cpp
+++ b/src/yuzu/multiplayer/lobby.cpp
@@ -77,16 +77,23 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
// UI Buttons
connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby);
+ connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch);
connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned);
connect(ui->hide_empty, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterEmpty);
connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull);
- connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch);
connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom);
connect(ui->room_list, &QTreeView::clicked, this, &Lobby::OnExpandRoom);
// Actions
connect(&room_list_watcher, &QFutureWatcher<AnnounceMultiplayerRoom::RoomList>::finished, this,
&Lobby::OnRefreshLobby);
+
+ // Load persistent filters after events are connected to make sure they apply
+ ui->search->setText(
+ QString::fromStdString(UISettings::values.multiplayer_filter_text.GetValue()));
+ ui->games_owned->setChecked(UISettings::values.multiplayer_filter_games_owned.GetValue());
+ ui->hide_empty->setChecked(UISettings::values.multiplayer_filter_hide_empty.GetValue());
+ ui->hide_full->setChecked(UISettings::values.multiplayer_filter_hide_full.GetValue());
}
Lobby::~Lobby() = default;
@@ -204,6 +211,10 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
// Save settings
UISettings::values.multiplayer_nickname = ui->nickname->text().toStdString();
+ UISettings::values.multiplayer_filter_text = ui->search->text().toStdString();
+ UISettings::values.multiplayer_filter_games_owned = ui->games_owned->isChecked();
+ UISettings::values.multiplayer_filter_hide_empty = ui->hide_empty->isChecked();
+ UISettings::values.multiplayer_filter_hide_full = ui->hide_full->isChecked();
UISettings::values.multiplayer_ip =
proxy->data(connection_index, LobbyItemHost::HostIPRole).value<QString>().toStdString();
UISettings::values.multiplayer_port =
diff --git a/src/yuzu/multiplayer/lobby_p.h b/src/yuzu/multiplayer/lobby_p.h
index 068c95aca..398833e7a 100644
--- a/src/yuzu/multiplayer/lobby_p.h
+++ b/src/yuzu/multiplayer/lobby_p.h
@@ -193,12 +193,29 @@ public:
}
QVariant data(int role) const override {
- if (role != Qt::DisplayRole) {
+ switch (role) {
+ case Qt::DisplayRole: {
+ auto members = data(MemberListRole).toList();
+ return QStringLiteral("%1 / %2 ")
+ .arg(QString::number(members.size()), data(MaxPlayerRole).toString());
+ }
+ case Qt::ForegroundRole: {
+ auto members = data(MemberListRole).toList();
+ auto max_players = data(MaxPlayerRole).toInt();
+ if (members.size() >= max_players) {
+ return QBrush(QColor(255, 48, 32));
+ } else if (members.size() == (max_players - 1)) {
+ return QBrush(QColor(255, 140, 32));
+ } else if (members.size() == 0) {
+ return QBrush(QColor(128, 128, 128));
+ }
+ // FIXME: How to return a value that tells Qt not to modify the
+ // text color from the default (as if Qt::ForegroundRole wasn't overridden)?
+ return QBrush(nullptr);
+ }
+ default:
return LobbyItem::data(role);
}
- auto members = data(MemberListRole).toList();
- return QStringLiteral("%1 / %2 ")
- .arg(QString::number(members.size()), data(MaxPlayerRole).toString());
}
bool operator<(const QStandardItem& other) const override {
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index f9906be33..03e42b930 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -169,6 +169,13 @@ struct Values {
// multiplayer settings
Setting<std::string> multiplayer_nickname{linkage, {}, "nickname", Category::Multiplayer};
+ Setting<std::string> multiplayer_filter_text{linkage, {}, "filter_text", Category::Multiplayer};
+ Setting<bool> multiplayer_filter_games_owned{linkage, false, "filter_games_owned",
+ Category::Multiplayer};
+ Setting<bool> multiplayer_filter_hide_empty{linkage, false, "filter_games_hide_empty",
+ Category::Multiplayer};
+ Setting<bool> multiplayer_filter_hide_full{linkage, false, "filter_games_hide_full",
+ Category::Multiplayer};
Setting<std::string> multiplayer_ip{linkage, {}, "ip", Category::Multiplayer};
Setting<u16, true> multiplayer_port{linkage, 24872, 0,
UINT16_MAX, "port", Category::Multiplayer};
@@ -222,7 +229,7 @@ void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig);
// This must be in alphabetical order according to action name as it must have the same order as
// UISetting::values.shortcuts, which is alphabetically ordered.
// clang-format off
-const std::array<Shortcut, 23> default_hotkeys{{
+const std::array<Shortcut, 28> default_hotkeys{{
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+M"), std::string("Home+Dpad_Right"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("-"), std::string("Home+Dpad_Down"), Qt::ApplicationShortcut, true}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("="), std::string("Home+Dpad_Up"), Qt::ApplicationShortcut, true}},
@@ -236,6 +243,11 @@ const std::array<Shortcut, 23> default_hotkeys{{
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F11"), std::string("Home+B"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+O"), std::string(""), Qt::WidgetWithChildrenShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F2"), std::string("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Browse Public Game Lobby")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+B"), std::string(""), Qt::ApplicationShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Create Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+N"), std::string(""), Qt::ApplicationShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Direct Connect to Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+C"), std::string(""), Qt::ApplicationShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Leave Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+L"), std::string(""), Qt::ApplicationShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Show Current Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+R"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F6"), std::string("R+Plus+Minus"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F5"), std::string("L+Plus+Minus"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F7"), std::string(""), Qt::ApplicationShortcut, false}},